Skip to main content

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}