Skip to main content

soma_provider_core/
manifest.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::ProviderId;
5
6pub type ProviderCatalog = ProviderManifest;
7
8#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
9#[serde(deny_unknown_fields)]
10pub struct ProviderManifest {
11    pub schema_version: u32,
12    pub provider: ProviderIdentity,
13    #[serde(default)]
14    pub tools: Vec<ProviderTool>,
15    #[serde(default)]
16    pub prompts: Vec<ProviderPrompt>,
17    #[serde(default)]
18    pub resources: Vec<ProviderResource>,
19    #[serde(default)]
20    pub tasks: Vec<ProviderTask>,
21    #[serde(default)]
22    pub elicitation: Vec<ProviderElicitation>,
23    #[serde(default)]
24    pub env: Vec<EnvRequirement>,
25    #[serde(default)]
26    pub capabilities: HostCapabilities,
27    #[serde(default)]
28    pub docs: Option<DocsOverlay>,
29    #[serde(default)]
30    pub plugin: Option<PluginOverlay>,
31    #[serde(default)]
32    pub ui: Option<UiOverlay>,
33    #[serde(default)]
34    pub meta: Value,
35}
36
37impl ProviderManifest {
38    pub fn new(id: ProviderId, title: impl Into<String>, version: impl Into<String>) -> Self {
39        Self {
40            schema_version: 1,
41            provider: ProviderIdentity {
42                name: id.into_string(),
43                kind: ProviderKind::StaticRust,
44                title: Some(title.into()),
45                description: None,
46                homepage: None,
47                source: None,
48                version: Some(version.into()),
49                enabled: None,
50            },
51            tools: Vec::new(),
52            prompts: Vec::new(),
53            resources: Vec::new(),
54            tasks: Vec::new(),
55            elicitation: Vec::new(),
56            env: Vec::new(),
57            capabilities: HostCapabilities::default(),
58            docs: None,
59            plugin: None,
60            ui: None,
61            meta: Value::Null,
62        }
63    }
64
65    #[must_use]
66    pub fn with_tool(mut self, tool: ToolSpec) -> Self {
67        self.tools.push(tool);
68        self
69    }
70}
71
72#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
73#[serde(deny_unknown_fields)]
74pub struct ProviderIdentity {
75    pub name: String,
76    pub kind: ProviderKind,
77    #[serde(default)]
78    pub title: Option<String>,
79    #[serde(default)]
80    pub description: Option<String>,
81    #[serde(default)]
82    pub homepage: Option<String>,
83    #[serde(default)]
84    pub source: Option<String>,
85    #[serde(default)]
86    pub version: Option<String>,
87    #[serde(default)]
88    pub enabled: Option<bool>,
89}
90
91#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq)]
92#[serde(rename_all = "kebab-case")]
93pub enum ProviderKind {
94    StaticRust,
95    Openapi,
96    AiSdk,
97    Wasm,
98    Mcp,
99    Python,
100    Langchain,
101    Llamaindex,
102}
103
104impl ProviderKind {
105    pub fn as_str(self) -> &'static str {
106        match self {
107            Self::StaticRust => "static-rust",
108            Self::Openapi => "openapi",
109            Self::AiSdk => "ai-sdk",
110            Self::Wasm => "wasm",
111            Self::Mcp => "mcp",
112            Self::Python => "python",
113            Self::Langchain => "langchain",
114            Self::Llamaindex => "llamaindex",
115        }
116    }
117}
118
119#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
120#[serde(deny_unknown_fields)]
121pub struct ToolSpec {
122    pub name: String,
123    pub description: String,
124    #[serde(default)]
125    pub title: Option<String>,
126    pub input_schema: Value,
127    #[serde(default)]
128    pub output_schema: Option<Value>,
129    #[serde(default)]
130    pub scope: Option<String>,
131    #[serde(default)]
132    pub destructive: bool,
133    #[serde(default)]
134    pub requires_admin: bool,
135    #[serde(default)]
136    pub cost: Option<String>,
137    #[serde(default)]
138    pub env: Vec<EnvRequirement>,
139    #[serde(default)]
140    pub limits: Option<ProviderLimits>,
141    #[serde(default)]
142    pub mcp: Option<McpOverlay>,
143    #[serde(default)]
144    pub rest: Option<RestOverlay>,
145    #[serde(default)]
146    pub cli: Option<CliOverlay>,
147    #[serde(default)]
148    pub palette: Option<PaletteOverlay>,
149    #[serde(default)]
150    pub ui: Option<UiOverlay>,
151    #[serde(default)]
152    pub examples: Vec<ProviderExample>,
153    #[serde(default)]
154    pub meta: Value,
155}
156
157impl ToolSpec {
158    pub fn new(
159        name: impl Into<String>,
160        description: impl Into<String>,
161        input_schema: Value,
162    ) -> Self {
163        Self {
164            name: name.into(),
165            description: description.into(),
166            title: None,
167            input_schema,
168            output_schema: None,
169            scope: None,
170            destructive: false,
171            requires_admin: false,
172            cost: None,
173            env: Vec::new(),
174            limits: None,
175            mcp: None,
176            rest: None,
177            cli: None,
178            palette: None,
179            ui: None,
180            examples: Vec::new(),
181            meta: Value::Null,
182        }
183    }
184
185    pub fn exposed_on(&self, surface: crate::ProviderSurface) -> bool {
186        match surface {
187            crate::ProviderSurface::Internal => true,
188            crate::ProviderSurface::Mcp => self
189                .mcp
190                .as_ref()
191                .map(|overlay| overlay.enabled)
192                .unwrap_or(true),
193            crate::ProviderSurface::Rest => self
194                .rest
195                .as_ref()
196                .map(|overlay| overlay.enabled)
197                .unwrap_or(true),
198            crate::ProviderSurface::Cli => self
199                .cli
200                .as_ref()
201                .map(|overlay| overlay.enabled)
202                .unwrap_or(false),
203            crate::ProviderSurface::Palette => self
204                .palette
205                .as_ref()
206                .map(|overlay| overlay.enabled)
207                .unwrap_or(true),
208            crate::ProviderSurface::Ui => self
209                .ui
210                .as_ref()
211                .map(|overlay| overlay.enabled)
212                .unwrap_or(true),
213        }
214    }
215}
216
217pub type ProviderTool = ToolSpec;
218
219#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
220#[serde(deny_unknown_fields)]
221pub struct ProviderPrompt {
222    pub name: String,
223    pub description: String,
224    #[serde(default)]
225    pub template: Option<String>,
226    #[serde(default)]
227    pub arguments_schema: Option<Value>,
228    #[serde(default)]
229    pub scope: Option<String>,
230    #[serde(default)]
231    pub mcp: Option<McpOverlay>,
232    #[serde(default)]
233    pub examples: Vec<ProviderExample>,
234}
235
236#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
237#[serde(deny_unknown_fields)]
238pub struct ProviderResource {
239    pub uri_template: String,
240    pub name: String,
241    pub description: String,
242    #[serde(default)]
243    pub mime_type: Option<String>,
244    #[serde(default)]
245    pub scope: Option<String>,
246    #[serde(default)]
247    pub mcp: Option<McpOverlay>,
248    #[serde(default)]
249    pub annotations: Value,
250}
251
252#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
253#[serde(deny_unknown_fields)]
254pub struct ProviderTask {
255    pub name: String,
256    pub description: String,
257    pub input_schema: Value,
258    #[serde(default)]
259    pub output_schema: Option<Value>,
260    #[serde(default)]
261    pub scope: Option<String>,
262    #[serde(default)]
263    pub mcp: Option<McpOverlay>,
264    #[serde(default)]
265    pub limits: Option<ProviderLimits>,
266}
267
268#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
269#[serde(deny_unknown_fields)]
270pub struct ProviderElicitation {
271    pub name: String,
272    pub description: String,
273    pub schema: Value,
274    #[serde(default)]
275    pub scope: Option<String>,
276    #[serde(default)]
277    pub mcp: Option<McpOverlay>,
278}
279
280#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
281#[serde(deny_unknown_fields)]
282pub struct EnvRequirement {
283    pub name: String,
284    #[serde(default)]
285    pub description: Option<String>,
286    #[serde(default = "default_true")]
287    pub required: bool,
288    #[serde(default = "default_true")]
289    pub sensitive: bool,
290    #[serde(default = "default_true")]
291    pub server_prefixed: bool,
292    #[serde(default)]
293    pub allow_unprefixed: bool,
294    #[serde(default)]
295    pub default: Option<Value>,
296}
297
298impl EnvRequirement {
299    pub fn runtime_name(&self, server_prefix: &str) -> String {
300        if self.server_prefixed {
301            format!(
302                "{}_{}",
303                server_prefix.trim_matches('_').to_ascii_uppercase(),
304                self.name
305            )
306        } else {
307            self.name.clone()
308        }
309    }
310}
311
312#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
313#[serde(deny_unknown_fields)]
314pub struct HostCapabilities {
315    #[serde(default)]
316    pub filesystem: Option<FilesystemCapability>,
317    #[serde(default)]
318    pub network: Option<NetworkCapability>,
319    #[serde(default)]
320    pub env: Option<EnvCapability>,
321    #[serde(default)]
322    pub terminal: Option<TerminalCapability>,
323    #[serde(default)]
324    pub browser: Option<BrowserCapability>,
325    #[serde(default)]
326    pub github: Option<GitHubCapability>,
327    /// Brokered services available to isolated provider runtimes.
328    #[serde(default, skip_serializing_if = "Option::is_none")]
329    pub broker: Option<BrokerCapability>,
330}
331
332#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
333#[serde(tag = "kind", rename_all = "kebab-case")]
334pub enum CapabilityGrant {
335    Filesystem {
336        read_roots: Vec<String>,
337        write_roots: Vec<String>,
338    },
339    Network {
340        allowed_hosts: Vec<String>,
341    },
342    Env {
343        allowed: Vec<String>,
344    },
345    Terminal {
346        working_dir: Option<String>,
347        allowlist: Vec<String>,
348    },
349    Browser {
350        allowed_origins: Vec<String>,
351    },
352    Github {
353        allowed_repos: Vec<String>,
354        read_only: bool,
355    },
356    Broker {
357        secret_names: Vec<String>,
358        #[serde(default, alias = "state_namespaces")]
359        state_read_namespaces: Vec<String>,
360        #[serde(default)]
361        state_write_namespaces: Vec<String>,
362        allow_logging: bool,
363        allow_metrics: bool,
364        allow_progress: bool,
365    },
366}
367
368/// Fine-grained host services requested by a provider.
369#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
370#[serde(deny_unknown_fields)]
371pub struct BrokerCapability {
372    #[serde(default)]
373    pub enabled: bool,
374    #[serde(default)]
375    pub secret_names: Vec<String>,
376    #[serde(default)]
377    pub state_namespace: Option<String>,
378    #[serde(default)]
379    pub state_write: bool,
380    #[serde(default)]
381    pub logging: bool,
382    #[serde(default)]
383    pub metrics: bool,
384    #[serde(default)]
385    pub progress: bool,
386}
387
388#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
389#[serde(deny_unknown_fields)]
390pub struct FilesystemCapability {
391    #[serde(default)]
392    pub enabled: bool,
393    #[serde(default)]
394    pub read_roots: Vec<String>,
395    #[serde(default)]
396    pub write_roots: Vec<String>,
397}
398
399#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
400#[serde(deny_unknown_fields)]
401pub struct NetworkCapability {
402    #[serde(default)]
403    pub enabled: bool,
404    #[serde(default)]
405    pub allowed_hosts: Vec<String>,
406}
407
408#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
409#[serde(deny_unknown_fields)]
410pub struct EnvCapability {
411    #[serde(default)]
412    pub enabled: bool,
413    #[serde(default)]
414    pub allowed: Vec<String>,
415}
416
417#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
418#[serde(deny_unknown_fields)]
419pub struct TerminalCapability {
420    #[serde(default)]
421    pub enabled: bool,
422    #[serde(default)]
423    pub working_dir: Option<String>,
424    #[serde(default)]
425    pub allowlist: Vec<String>,
426}
427
428#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
429#[serde(deny_unknown_fields)]
430pub struct BrowserCapability {
431    #[serde(default)]
432    pub enabled: bool,
433    #[serde(default)]
434    pub allowed_origins: Vec<String>,
435}
436
437#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
438#[serde(deny_unknown_fields)]
439pub struct GitHubCapability {
440    #[serde(default)]
441    pub enabled: bool,
442    #[serde(default)]
443    pub allowed_repos: Vec<String>,
444    #[serde(default = "default_true")]
445    pub read_only: bool,
446}
447
448#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
449#[serde(deny_unknown_fields)]
450pub struct McpOverlay {
451    #[serde(default = "default_true")]
452    pub enabled: bool,
453    #[serde(default)]
454    pub title: Option<String>,
455    #[serde(default)]
456    pub annotations: Value,
457}
458
459#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
460#[serde(deny_unknown_fields)]
461pub struct RestOverlay {
462    #[serde(default = "default_true")]
463    pub enabled: bool,
464    #[serde(default)]
465    pub method: Option<String>,
466    #[serde(default)]
467    pub path: Option<String>,
468    #[serde(default)]
469    pub tags: Vec<String>,
470    #[serde(default)]
471    pub summary: Option<String>,
472    #[serde(default)]
473    pub description: Option<String>,
474    #[serde(default)]
475    pub deprecated: bool,
476    #[serde(default)]
477    pub path_params: Value,
478    #[serde(default)]
479    pub query_params: Value,
480    #[serde(default)]
481    pub request_body_schema: Option<Value>,
482}
483
484#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
485#[serde(deny_unknown_fields)]
486pub struct CliOverlay {
487    #[serde(default = "default_true")]
488    pub enabled: bool,
489    #[serde(default)]
490    pub command: Option<String>,
491    #[serde(default)]
492    pub aliases: Vec<String>,
493    #[serde(default)]
494    pub about: Option<String>,
495    #[serde(default)]
496    pub long_about: Option<String>,
497    #[serde(default)]
498    pub hidden: bool,
499    #[serde(default)]
500    pub flags: Vec<Value>,
501    #[serde(default)]
502    pub default_output: Option<String>,
503    #[serde(default)]
504    pub interactive: bool,
505}
506
507#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
508#[serde(deny_unknown_fields)]
509pub struct PaletteOverlay {
510    #[serde(default = "default_true")]
511    pub enabled: bool,
512    #[serde(default)]
513    pub category: Option<String>,
514    #[serde(default)]
515    pub icon: Option<String>,
516    #[serde(default)]
517    pub tone: Option<String>,
518    #[serde(default)]
519    pub arg_mode: Option<String>,
520    #[serde(default)]
521    pub result_view: Option<String>,
522    #[serde(default)]
523    pub aurora_blocks: Vec<String>,
524}
525
526#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
527#[serde(deny_unknown_fields)]
528pub struct UiOverlay {
529    #[serde(default = "default_true")]
530    pub enabled: bool,
531    #[serde(default)]
532    pub aurora_registry_dependencies: Vec<String>,
533    #[serde(default)]
534    pub shadcn_items: Vec<String>,
535    #[serde(default)]
536    pub categories: Vec<String>,
537    #[serde(default)]
538    pub meta: Value,
539}
540
541#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
542#[serde(deny_unknown_fields)]
543pub struct DocsOverlay {
544    #[serde(default)]
545    pub when_to_use: Option<String>,
546    #[serde(default)]
547    pub examples: Vec<ProviderExample>,
548    #[serde(default)]
549    pub troubleshooting: Vec<String>,
550}
551
552#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
553#[serde(deny_unknown_fields)]
554pub struct PluginOverlay {
555    #[serde(default = "default_true")]
556    pub generate_skill: bool,
557    #[serde(default = "default_true")]
558    pub generate_claude: bool,
559    #[serde(default = "default_true")]
560    pub generate_codex: bool,
561    #[serde(default = "default_true")]
562    pub generate_gemini: bool,
563    #[serde(default = "default_true")]
564    pub generate_marketplace: bool,
565    #[serde(default)]
566    pub mcp_registration: Option<String>,
567}
568
569#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
570#[serde(deny_unknown_fields)]
571pub struct ProviderLimits {
572    #[serde(default)]
573    pub timeout_ms: Option<u64>,
574    #[serde(default)]
575    pub max_response_bytes: Option<usize>,
576    #[serde(default)]
577    pub max_input_bytes: Option<usize>,
578}
579
580#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
581#[serde(deny_unknown_fields)]
582pub struct ProviderExample {
583    #[serde(default)]
584    pub title: Option<String>,
585    #[serde(default)]
586    pub description: Option<String>,
587    #[serde(default)]
588    pub input: Option<Value>,
589    #[serde(default)]
590    pub output: Option<Value>,
591    #[serde(default)]
592    pub cli: Option<String>,
593    #[serde(default)]
594    pub rest: Option<Value>,
595    #[serde(default)]
596    pub mcp: Option<Value>,
597}
598
599fn default_true() -> bool {
600    true
601}