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}
328
329#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
330#[serde(tag = "kind", rename_all = "kebab-case")]
331pub enum CapabilityGrant {
332    Filesystem {
333        read_roots: Vec<String>,
334        write_roots: Vec<String>,
335    },
336    Network {
337        allowed_hosts: Vec<String>,
338    },
339    Env {
340        allowed: Vec<String>,
341    },
342    Terminal {
343        working_dir: Option<String>,
344        allowlist: Vec<String>,
345    },
346    Browser {
347        allowed_origins: Vec<String>,
348    },
349    Github {
350        allowed_repos: Vec<String>,
351        read_only: bool,
352    },
353}
354
355#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
356#[serde(deny_unknown_fields)]
357pub struct FilesystemCapability {
358    #[serde(default)]
359    pub enabled: bool,
360    #[serde(default)]
361    pub read_roots: Vec<String>,
362    #[serde(default)]
363    pub write_roots: Vec<String>,
364}
365
366#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
367#[serde(deny_unknown_fields)]
368pub struct NetworkCapability {
369    #[serde(default)]
370    pub enabled: bool,
371    #[serde(default)]
372    pub allowed_hosts: Vec<String>,
373}
374
375#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
376#[serde(deny_unknown_fields)]
377pub struct EnvCapability {
378    #[serde(default)]
379    pub enabled: bool,
380    #[serde(default)]
381    pub allowed: Vec<String>,
382}
383
384#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
385#[serde(deny_unknown_fields)]
386pub struct TerminalCapability {
387    #[serde(default)]
388    pub enabled: bool,
389    #[serde(default)]
390    pub working_dir: Option<String>,
391    #[serde(default)]
392    pub allowlist: Vec<String>,
393}
394
395#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
396#[serde(deny_unknown_fields)]
397pub struct BrowserCapability {
398    #[serde(default)]
399    pub enabled: bool,
400    #[serde(default)]
401    pub allowed_origins: Vec<String>,
402}
403
404#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq, Eq)]
405#[serde(deny_unknown_fields)]
406pub struct GitHubCapability {
407    #[serde(default)]
408    pub enabled: bool,
409    #[serde(default)]
410    pub allowed_repos: Vec<String>,
411    #[serde(default = "default_true")]
412    pub read_only: bool,
413}
414
415#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
416#[serde(deny_unknown_fields)]
417pub struct McpOverlay {
418    #[serde(default = "default_true")]
419    pub enabled: bool,
420    #[serde(default)]
421    pub title: Option<String>,
422    #[serde(default)]
423    pub annotations: Value,
424}
425
426#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
427#[serde(deny_unknown_fields)]
428pub struct RestOverlay {
429    #[serde(default = "default_true")]
430    pub enabled: bool,
431    #[serde(default)]
432    pub method: Option<String>,
433    #[serde(default)]
434    pub path: Option<String>,
435    #[serde(default)]
436    pub tags: Vec<String>,
437    #[serde(default)]
438    pub summary: Option<String>,
439    #[serde(default)]
440    pub description: Option<String>,
441    #[serde(default)]
442    pub deprecated: bool,
443    #[serde(default)]
444    pub path_params: Value,
445    #[serde(default)]
446    pub query_params: Value,
447    #[serde(default)]
448    pub request_body_schema: Option<Value>,
449}
450
451#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
452#[serde(deny_unknown_fields)]
453pub struct CliOverlay {
454    #[serde(default = "default_true")]
455    pub enabled: bool,
456    #[serde(default)]
457    pub command: Option<String>,
458    #[serde(default)]
459    pub aliases: Vec<String>,
460    #[serde(default)]
461    pub about: Option<String>,
462    #[serde(default)]
463    pub long_about: Option<String>,
464    #[serde(default)]
465    pub hidden: bool,
466    #[serde(default)]
467    pub flags: Vec<Value>,
468    #[serde(default)]
469    pub default_output: Option<String>,
470    #[serde(default)]
471    pub interactive: bool,
472}
473
474#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
475#[serde(deny_unknown_fields)]
476pub struct PaletteOverlay {
477    #[serde(default = "default_true")]
478    pub enabled: bool,
479    #[serde(default)]
480    pub category: Option<String>,
481    #[serde(default)]
482    pub icon: Option<String>,
483    #[serde(default)]
484    pub tone: Option<String>,
485    #[serde(default)]
486    pub arg_mode: Option<String>,
487    #[serde(default)]
488    pub result_view: Option<String>,
489    #[serde(default)]
490    pub aurora_blocks: Vec<String>,
491}
492
493#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
494#[serde(deny_unknown_fields)]
495pub struct UiOverlay {
496    #[serde(default = "default_true")]
497    pub enabled: bool,
498    #[serde(default)]
499    pub aurora_registry_dependencies: Vec<String>,
500    #[serde(default)]
501    pub shadcn_items: Vec<String>,
502    #[serde(default)]
503    pub categories: Vec<String>,
504    #[serde(default)]
505    pub meta: Value,
506}
507
508#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
509#[serde(deny_unknown_fields)]
510pub struct DocsOverlay {
511    #[serde(default)]
512    pub when_to_use: Option<String>,
513    #[serde(default)]
514    pub examples: Vec<ProviderExample>,
515    #[serde(default)]
516    pub troubleshooting: Vec<String>,
517}
518
519#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
520#[serde(deny_unknown_fields)]
521pub struct PluginOverlay {
522    #[serde(default = "default_true")]
523    pub generate_skill: bool,
524    #[serde(default = "default_true")]
525    pub generate_claude: bool,
526    #[serde(default = "default_true")]
527    pub generate_codex: bool,
528    #[serde(default = "default_true")]
529    pub generate_gemini: bool,
530    #[serde(default = "default_true")]
531    pub generate_marketplace: bool,
532    #[serde(default)]
533    pub mcp_registration: Option<String>,
534}
535
536#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
537#[serde(deny_unknown_fields)]
538pub struct ProviderLimits {
539    #[serde(default)]
540    pub timeout_ms: Option<u64>,
541    #[serde(default)]
542    pub max_response_bytes: Option<usize>,
543    #[serde(default)]
544    pub max_input_bytes: Option<usize>,
545}
546
547#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
548#[serde(deny_unknown_fields)]
549pub struct ProviderExample {
550    #[serde(default)]
551    pub title: Option<String>,
552    #[serde(default)]
553    pub description: Option<String>,
554    #[serde(default)]
555    pub input: Option<Value>,
556    #[serde(default)]
557    pub output: Option<Value>,
558    #[serde(default)]
559    pub cli: Option<String>,
560    #[serde(default)]
561    pub rest: Option<Value>,
562    #[serde(default)]
563    pub mcp: Option<Value>,
564}
565
566fn default_true() -> bool {
567    true
568}