1use sim_kernel::{CapabilityName, Expr, Result, ShapeRef, Symbol};
2
3use crate::surface::{public_annotations, stable_mcp_name_text};
4use crate::{
5 McpAnnotation, McpStreamPolicy, McpSurfaceCard, McpSurfaceRole, McpSurfaceSource,
6 stable_mcp_name,
7};
8
9#[derive(Clone)]
11pub struct McpNativeCard {
12 pub subject: Symbol,
14 pub description: String,
16 pub input_shape: Option<ShapeRef>,
18 pub output_shape: Option<ShapeRef>,
20 pub capabilities: Vec<CapabilityName>,
22 pub facets: Vec<NativeFacet>,
24}
25
26impl McpNativeCard {
27 pub fn new(subject: Symbol, description: impl Into<String>) -> Self {
29 Self {
30 subject,
31 description: description.into(),
32 input_shape: None,
33 output_shape: None,
34 capabilities: Vec::new(),
35 facets: Vec::new(),
36 }
37 }
38
39 pub fn with_shapes(mut self, input: ShapeRef, output: ShapeRef) -> Self {
41 self.input_shape = Some(input);
42 self.output_shape = Some(output);
43 self
44 }
45
46 pub fn with_capability(mut self, capability: CapabilityName) -> Self {
48 self.capabilities.push(capability);
49 self
50 }
51
52 pub fn with_facet(mut self, facet: NativeFacet) -> Self {
54 self.facets.push(facet);
55 self
56 }
57
58 pub fn exported(mut self, export: McpExportFacet) -> Self {
60 self.facets.push(NativeFacet::McpExport(export));
61 self
62 }
63}
64
65#[derive(Clone)]
67pub enum NativeFacet {
68 McpExport(McpExportFacet),
70 Other {
72 name: Symbol,
74 value: Expr,
76 private: bool,
78 },
79}
80
81#[derive(Clone)]
83pub struct McpExportFacet {
84 pub role: McpSurfaceRole,
86 pub name: Option<String>,
88 pub symbol: Option<Symbol>,
90 pub uri: Option<String>,
92 pub description: Option<String>,
94 pub input_shape: Option<ShapeRef>,
96 pub output_shape: Option<ShapeRef>,
98 pub annotations: Vec<McpAnnotation>,
100 pub capabilities: Vec<CapabilityName>,
102 pub stream_policy: McpStreamPolicy,
104}
105
106impl McpExportFacet {
107 pub fn tool() -> Self {
109 Self::new(McpSurfaceRole::Tool)
110 }
111
112 pub fn resource() -> Self {
114 Self::new(McpSurfaceRole::Resource)
115 }
116
117 pub fn prompt() -> Self {
119 Self::new(McpSurfaceRole::Prompt)
120 }
121
122 pub fn model() -> Self {
124 Self::new(McpSurfaceRole::Model)
125 }
126
127 pub fn new(role: McpSurfaceRole) -> Self {
129 Self {
130 role,
131 name: None,
132 symbol: None,
133 uri: None,
134 description: None,
135 input_shape: None,
136 output_shape: None,
137 annotations: Vec::new(),
138 capabilities: Vec::new(),
139 stream_policy: McpStreamPolicy::None,
140 }
141 }
142
143 pub fn with_name(mut self, name: impl Into<String>) -> Self {
145 self.name = Some(name.into());
146 self
147 }
148
149 pub fn with_symbol(mut self, symbol: Symbol) -> Self {
151 self.symbol = Some(symbol);
152 self
153 }
154
155 pub fn with_uri(mut self, uri: impl Into<String>) -> Self {
157 self.uri = Some(uri.into());
158 self
159 }
160
161 pub fn with_description(mut self, description: impl Into<String>) -> Self {
163 self.description = Some(description.into());
164 self
165 }
166
167 pub fn with_annotation(mut self, annotation: McpAnnotation) -> Self {
169 self.annotations.push(annotation);
170 self
171 }
172
173 pub fn with_capability(mut self, capability: CapabilityName) -> Self {
175 self.capabilities.push(capability);
176 self
177 }
178
179 pub fn with_stream_policy(mut self, policy: McpStreamPolicy) -> Self {
181 self.stream_policy = policy;
182 self
183 }
184}
185
186pub fn mcp_export_facet_name() -> Symbol {
188 Symbol::new("mcp-export")
189}
190
191pub fn mcp_export_operation_symbol() -> Symbol {
193 Symbol::qualified("mcp", "export")
194}
195
196pub fn native_surface_rows(cards: &[McpNativeCard]) -> Result<Vec<McpSurfaceCard>> {
198 let mut rows = Vec::new();
199 for card in cards {
200 for facet in &card.facets {
201 if let NativeFacet::McpExport(export) = facet {
202 rows.push(row_from_export(card, export)?);
203 }
204 }
205 }
206 Ok(rows)
207}
208
209fn row_from_export(card: &McpNativeCard, export: &McpExportFacet) -> Result<McpSurfaceCard> {
210 let symbol = export
211 .symbol
212 .clone()
213 .unwrap_or_else(|| card.subject.clone());
214 let name = match &export.name {
215 Some(name) => stable_mcp_name_text(name)?,
216 None => stable_mcp_name(&symbol)?,
217 };
218 let mut capabilities = card.capabilities.clone();
219 capabilities.extend(export.capabilities.clone());
220 capabilities.sort();
221 capabilities.dedup();
222
223 Ok(McpSurfaceCard {
224 id: format!("native:{}:{}", export.role.as_symbol(), card.subject),
225 source: McpSurfaceSource::NativeCard,
226 role: export.role.clone(),
227 name,
228 symbol: Some(symbol),
229 uri: Some(
230 export
231 .uri
232 .clone()
233 .unwrap_or_else(|| format!("sim://{}", card.subject)),
234 ),
235 description: export
236 .description
237 .clone()
238 .unwrap_or_else(|| card.description.clone()),
239 input_shape: export
240 .input_shape
241 .clone()
242 .or_else(|| card.input_shape.clone()),
243 output_shape: export
244 .output_shape
245 .clone()
246 .or_else(|| card.output_shape.clone()),
247 annotations: public_annotations(&export.annotations),
248 capabilities,
249 stream_policy: export.stream_policy.clone(),
250 })
251}