soma_application/types.rs
1use std::collections::BTreeMap;
2
3use crate::ApplicationError;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use soma_provider_core::ProviderCatalog;
7
8/// Request to execute a named action with free-form JSON parameters.
9#[derive(Debug, Clone, Deserialize, Serialize)]
10pub struct ExecuteActionRequest {
11 /// Name of the action to execute.
12 pub action: String,
13 /// JSON parameters passed to the action (defaults to `null` when absent).
14 #[serde(default)]
15 pub params: Value,
16}
17
18/// Result of executing an action, pairing its output with a correlation id.
19#[derive(Debug, Clone, PartialEq, Serialize)]
20pub struct ExecuteActionResponse {
21 /// JSON output produced by the action.
22 pub output: Value,
23 /// Unique id correlating this response with its originating request.
24 pub request_id: String,
25 /// Bounded provider progress events emitted during this invocation.
26 #[serde(default)]
27 pub progress: Vec<soma_provider_core::ProviderProgressEvent>,
28}
29
30impl ExecuteActionResponse {
31 pub(crate) fn enforce_serialized_limit(&self, limit: usize) -> Result<(), ApplicationError> {
32 let actual = serde_json::to_vec(self)
33 .map_err(|error| ApplicationError::legacy("response serialization", error))?
34 .len();
35 if actual > limit {
36 return Err(ApplicationError::new(
37 "response_too_large",
38 format!("response envelope exceeded {limit} bytes"),
39 false,
40 "Reduce the requested output or progress volume and retry.",
41 ));
42 }
43 Ok(())
44 }
45
46 /// Returns the stable success envelope shared by REST, CLI, and MCP.
47 #[must_use]
48 pub fn into_surface_value(self) -> Value {
49 serde_json::json!({
50 "output": self.output,
51 "request_id": self.request_id,
52 "progress": self.progress,
53 })
54 }
55}
56
57#[cfg(test)]
58mod response_tests {
59 use super::*;
60
61 #[test]
62 fn response_shape_is_stable_with_or_without_progress() {
63 let legacy = ExecuteActionResponse {
64 output: serde_json::json!({"ok": true}),
65 request_id: "request-1".to_owned(),
66 progress: Vec::new(),
67 };
68 assert_eq!(
69 legacy.into_surface_value(),
70 serde_json::json!({
71 "output": {"ok": true},
72 "request_id": "request-1",
73 "progress": [],
74 })
75 );
76
77 let reported = ExecuteActionResponse {
78 output: serde_json::json!({"ok": true}),
79 request_id: "request-2".to_owned(),
80 progress: vec![soma_provider_core::ProviderProgressEvent {
81 current: 1,
82 total: Some(2),
83 message: Some("halfway".to_owned()),
84 }],
85 };
86 assert_eq!(reported.into_surface_value()["progress"][0]["current"], 1);
87 }
88
89 #[test]
90 fn serialized_limit_includes_progress_and_envelope_fields() {
91 let response = ExecuteActionResponse {
92 output: serde_json::json!(null),
93 request_id: "request-1".to_owned(),
94 progress: vec![soma_provider_core::ProviderProgressEvent {
95 current: 1,
96 total: None,
97 message: Some("x".repeat(64)),
98 }],
99 };
100 assert!(response.enforce_serialized_limit(32).is_err());
101 }
102}
103
104/// Outcome of an MCP elicitation prompt asking the client for a name.
105#[derive(Debug, Clone, PartialEq, Eq)]
106pub enum ElicitedName {
107 /// The user supplied a name.
108 Accepted(String),
109 /// The user accepted the prompt but left the name empty.
110 NoInput,
111 /// The user declined to provide a name.
112 Declined,
113 /// The user cancelled the elicitation.
114 Cancelled,
115 /// The client does not support elicitation.
116 Unsupported,
117}
118
119/// Parameters collected by the scaffold-intent wizard to generate a new server.
120#[derive(Debug, Clone, Deserialize, Serialize)]
121pub struct ScaffoldIntentRequest {
122 /// Human-readable display name for the scaffolded server.
123 pub display_name: String,
124 /// Cargo crate name for the generated project.
125 pub crate_name: String,
126 /// Name of the produced binary.
127 pub binary_name: String,
128 /// Category classifying the kind of server.
129 pub server_category: String,
130 /// Environment variable prefix for the server's configuration.
131 pub env_prefix: String,
132 /// Authentication scheme to wire in (e.g. bearer, oauth).
133 pub auth_kind: String,
134 /// Default bind host.
135 pub host: String,
136 /// Default bind port.
137 pub port: u16,
138 /// MCP transport(s) to enable (e.g. http, stdio).
139 pub mcp_transport: String,
140 /// MCP primitives to include (tools, resources, prompts).
141 pub mcp_primitives: String,
142 /// Deployment target/model for the scaffolded server.
143 pub deployment: String,
144 /// Plugin surfaces to generate (Claude/Codex/Gemini).
145 pub plugins: String,
146 /// Whether to publish MCP registry metadata.
147 pub publish_mcp: bool,
148 /// Seed URLs to crawl for the knowledge base.
149 pub crawl_urls: String,
150 /// Seed repositories to ingest for the knowledge base.
151 pub crawl_repos: String,
152 /// Seed search topics for automated web crawling.
153 pub crawl_search_topics: String,
154}
155
156/// Request to reload the gateway with an optional replacement config.
157#[derive(Debug, Clone, Deserialize, Serialize)]
158pub struct GatewayReloadRequest {
159 /// New gateway configuration to apply (defaults to `null` when absent).
160 #[serde(default)]
161 pub config: Value,
162}
163
164/// Request to execute a gateway action with free-form JSON parameters.
165#[derive(Debug, Clone, Deserialize, Serialize)]
166pub struct GatewayExecuteRequest {
167 /// Name of the gateway action to execute.
168 pub action: String,
169 /// JSON parameters passed to the action (defaults to `null` when absent).
170 #[serde(default)]
171 pub params: Value,
172}
173
174/// Scope constraining which gateway upstreams and services a route may reach.
175#[derive(Debug, Clone, Default, PartialEq, Eq)]
176pub struct GatewayRouteScope {
177 /// Allowed upstream identifiers.
178 pub upstreams: Vec<String>,
179 /// Allowed service identifiers.
180 pub services: Vec<String>,
181 /// Whether the code-mode surface is exposed to this route.
182 pub expose_code_mode: bool,
183}
184
185/// Protocol-neutral representation of an MCP request outcome routed through Soma.
186#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
187pub enum GatewayMcpOutcome {
188 /// The operation completed normally.
189 Complete(Value),
190 /// The operation needs client-supplied input before it can continue.
191 InputRequired(Value),
192 /// The operation created or returned an asynchronous task.
193 Task(Value),
194}
195
196/// Follow-up fields supplied by a client during an MCP multi-round trip.
197#[derive(Debug, Clone, Default, PartialEq, Eq, Deserialize, Serialize)]
198pub struct GatewayMcpRoundTrip {
199 /// Responses keyed by the input request identifier from the prior result.
200 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub input_responses: Option<BTreeMap<String, Value>>,
202 /// Opaque request state echoed exactly from the prior result.
203 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub request_state: Option<String>,
205 /// Complete per-request MCP metadata forwarded without SDK coupling.
206 #[serde(default, skip_serializing_if = "Option::is_none")]
207 pub request_meta: Option<Value>,
208}
209
210/// A tool advertised through the gateway routing layer.
211#[derive(Debug, Clone, PartialEq, Serialize)]
212pub struct GatewayToolRoute {
213 /// Tool name as exposed by the gateway.
214 pub name: String,
215 /// Optional human-readable description.
216 pub description: Option<String>,
217 /// Optional JSON schema for the tool's input.
218 pub input_schema: Option<Value>,
219 /// Optional JSON schema for the tool's output.
220 pub output_schema: Option<Value>,
221 /// Whether invoking the tool may have destructive effects.
222 pub destructive: bool,
223}
224
225/// A resource advertised through the gateway routing layer.
226#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
227pub struct GatewayResourceRoute {
228 /// Gateway-facing URI for the resource.
229 pub uri: String,
230 /// Native upstream URI the gateway URI maps to.
231 pub native_uri: String,
232 /// Optional human-readable resource name.
233 pub name: Option<String>,
234}
235
236/// A prompt advertised through the gateway routing layer.
237#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
238pub struct GatewayPromptRoute {
239 /// Prompt name as exposed by the gateway.
240 pub name: String,
241 /// Optional human-readable description.
242 pub description: Option<String>,
243}
244
245/// Request to execute a code-mode script with optional JSON input.
246#[derive(Debug, Clone, Deserialize, Serialize)]
247pub struct CodeModeExecuteRequest {
248 /// Source code to execute in the code-mode sandbox.
249 pub source: String,
250 /// JSON input made available to the script (defaults to `null` when absent).
251 #[serde(default)]
252 pub input: Value,
253}
254
255/// Request to invoke an OpenAPI operation with free-form JSON parameters.
256#[derive(Debug, Clone, Deserialize, Serialize)]
257pub struct OpenApiExecuteRequest {
258 /// Identifier of the OpenAPI operation to invoke.
259 pub operation: String,
260 /// JSON parameters passed to the operation (defaults to `null` when absent).
261 #[serde(default)]
262 pub params: Value,
263}
264
265/// Result of an operation, pairing its output with a correlation id.
266#[derive(Debug, Clone, PartialEq, Serialize)]
267pub struct OperationResponse {
268 /// JSON output produced by the operation.
269 pub output: Value,
270 /// Unique id correlating this response with its originating request.
271 pub request_id: String,
272}
273
274/// Request to read a resource identified by URI.
275#[derive(Debug, Clone, Deserialize, Serialize)]
276pub struct ReadResourceRequest {
277 /// URI of the resource to read.
278 pub uri: String,
279}
280
281/// Content returned when reading a resource, either text or binary blob.
282#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
283#[serde(tag = "kind", rename_all = "kebab-case")]
284pub enum ResourceContent {
285 /// Textual resource content.
286 Text {
287 /// The resource text.
288 text: String,
289 /// Optional MIME type of the text.
290 mime_type: Option<String>,
291 },
292 /// Binary resource content encoded as base64.
293 Blob {
294 /// Base64-encoded resource bytes.
295 blob_base64: String,
296 /// Optional MIME type of the blob.
297 mime_type: Option<String>,
298 },
299}
300
301/// Specification of a templated resource with a parameterized URI.
302#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
303pub struct ResourceTemplateSpec {
304 /// RFC 6570 URI template describing the resource address.
305 pub uri_template: String,
306 /// Human-readable name for the resource template.
307 pub name: String,
308 /// Human-readable description of the resource template.
309 pub description: String,
310 /// Optional MIME type of resources produced by the template.
311 pub mime_type: Option<String>,
312}
313
314/// Snapshot of the provider catalogs at a point in time.
315#[derive(Debug, Clone, PartialEq, Serialize)]
316pub struct CatalogSnapshot {
317 /// Identifier of this snapshot.
318 pub id: String,
319 /// Fingerprint of the catalog contents used for change detection.
320 pub fingerprint: String,
321 /// Provider catalogs captured in the snapshot.
322 pub catalogs: Vec<ProviderCatalog>,
323}
324
325/// Result of the `doctor` pre-flight readiness check.
326#[derive(Debug, Clone, PartialEq, Serialize)]
327pub struct DoctorReport {
328 /// Whether the service is ready to serve requests.
329 pub ready: bool,
330 /// Optional status detail captured during the check.
331 pub status: Option<Value>,
332 /// List of problems detected during the check.
333 pub problems: Vec<String>,
334}