1use std::sync::Arc;
2
3use sim_kernel::{
4 Args, Callable, Cx, Error, Export, Expr, Object, ObjectCompat, Result, Symbol, Value,
5};
6use sim_shape::{AnyShape, ListShape, Shape, shape_value};
7
8use crate::McpRouter;
9use crate::methods::{
10 core, prompts as prompt_methods, resources as resource_methods, tools as tool_methods,
11};
12
13#[derive(Clone, Copy)]
15pub enum McpFunctionKind {
16 Handle,
18 Initialize,
20 Tools,
22 Call,
24 Resources,
26 Read,
28 Prompts,
30 GetPrompt,
32 #[cfg(feature = "sampling")]
34 SamplingRunner,
35 Health,
37}
38
39impl McpFunctionKind {
40 pub fn symbol(self) -> Symbol {
42 match self {
43 Self::Handle => handle_symbol(),
44 Self::Initialize => initialize_symbol(),
45 Self::Tools => tools_symbol(),
46 Self::Call => call_symbol(),
47 Self::Resources => resources_symbol(),
48 Self::Read => read_symbol(),
49 Self::Prompts => prompts_symbol(),
50 Self::GetPrompt => get_prompt_symbol(),
51 #[cfg(feature = "sampling")]
52 Self::SamplingRunner => crate::sampling::mcp_sampling_runner_symbol(),
53 Self::Health => health_symbol(),
54 }
55 }
56}
57
58#[derive(Clone)]
60pub struct McpFunction {
61 kind: McpFunctionKind,
62}
63
64impl McpFunction {
65 pub fn new(kind: McpFunctionKind) -> Self {
67 Self { kind }
68 }
69
70 pub fn symbol(&self) -> Symbol {
72 self.kind.symbol()
73 }
74
75 pub fn value(kind: McpFunctionKind) -> Arc<Self> {
77 Arc::new(Self::new(kind))
78 }
79}
80
81impl Object for McpFunction {
82 fn display(&self, _cx: &mut Cx) -> Result<String> {
83 Ok(format!("#<function {}>", self.symbol()))
84 }
85
86 fn as_any(&self) -> &dyn std::any::Any {
87 self
88 }
89}
90
91impl ObjectCompat for McpFunction {
92 fn as_callable(&self) -> Option<&dyn Callable> {
93 Some(self)
94 }
95}
96
97impl Callable for McpFunction {
98 fn call(&self, cx: &mut Cx, args: Args) -> Result<Value> {
99 match self.kind {
100 McpFunctionKind::Handle => handle(cx, args),
101 McpFunctionKind::Initialize => initialize(cx, args),
102 McpFunctionKind::Tools => tools(cx, args),
103 McpFunctionKind::Call => call(cx, args),
104 McpFunctionKind::Resources => resources(cx, args),
105 McpFunctionKind::Read => read(cx, args),
106 McpFunctionKind::Prompts => prompts(cx, args),
107 McpFunctionKind::GetPrompt => get_prompt(cx, args),
108 #[cfg(feature = "sampling")]
109 McpFunctionKind::SamplingRunner => sampling_runner(cx, args),
110 McpFunctionKind::Health => health(cx, args),
111 }
112 }
113
114 fn browse_args_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
115 let shape: Arc<dyn Shape> = match self.kind {
116 McpFunctionKind::Handle
117 | McpFunctionKind::Call
118 | McpFunctionKind::Read
119 | McpFunctionKind::GetPrompt => Arc::new(ListShape::new(vec![Arc::new(AnyShape)])),
120 McpFunctionKind::Initialize
121 | McpFunctionKind::Tools
122 | McpFunctionKind::Resources
123 | McpFunctionKind::Prompts
124 | McpFunctionKind::Health => Arc::new(ListShape::new(Vec::new())),
125 #[cfg(feature = "sampling")]
126 McpFunctionKind::SamplingRunner => Arc::new(ListShape::new(Vec::new())),
127 };
128 Ok(Some(shape_value(
129 Symbol::qualified(self.symbol().to_string(), "args"),
130 shape,
131 )))
132 }
133
134 fn browse_result_shape(&self, _cx: &mut Cx) -> Result<Option<sim_kernel::ShapeRef>> {
135 Ok(Some(shape_value(
136 Symbol::qualified(self.symbol().to_string(), "result"),
137 Arc::new(AnyShape),
138 )))
139 }
140}
141
142pub fn mcp_exports() -> Vec<Export> {
144 [
145 handle_symbol(),
146 initialize_symbol(),
147 tools_symbol(),
148 call_symbol(),
149 resources_symbol(),
150 read_symbol(),
151 prompts_symbol(),
152 get_prompt_symbol(),
153 #[cfg(feature = "sampling")]
154 crate::sampling::mcp_sampling_runner_symbol(),
155 health_symbol(),
156 ]
157 .into_iter()
158 .map(|symbol| Export::Function {
159 symbol,
160 function_id: None,
161 })
162 .collect()
163}
164
165fn handle(cx: &mut Cx, args: Args) -> Result<Value> {
166 let expr = one_expr_arg(cx, args, "mcp/handle expects one decoded MCP envelope Expr")?;
167 let mut router = McpRouter::fixture();
168 match router.handle_expr(cx, expr)? {
169 Some(reply) => cx.factory().expr(reply),
170 None => cx.factory().nil(),
171 }
172}
173
174fn initialize(cx: &mut Cx, args: Args) -> Result<Value> {
175 no_args(args, "mcp/initialize expects no arguments")?;
176 let mut session = crate::McpSession::fixture();
177 cx.factory()
178 .expr(core::initialize(&mut session, Expr::Nil)?)
179}
180
181fn tools(cx: &mut Cx, args: Args) -> Result<Value> {
182 no_args(args, "mcp/tools expects no arguments")?;
183 let session = crate::McpSession::fixture();
184 let result = tool_methods::list(cx, &session)?;
185 cx.factory().expr(result)
186}
187
188fn call(cx: &mut Cx, args: Args) -> Result<Value> {
189 let params = one_expr_arg(cx, args, "mcp/call expects one tools/call params Expr")?;
190 let session = crate::McpSession::fixture();
191 let result = tool_methods::call(cx, &session, params)?;
192 cx.factory().expr(result)
193}
194
195fn resources(cx: &mut Cx, args: Args) -> Result<Value> {
196 no_args(args, "mcp/resources expects no arguments")?;
197 let session = crate::McpSession::fixture();
198 let result = resource_methods::list(cx, &session)?;
199 cx.factory().expr(result)
200}
201
202fn read(cx: &mut Cx, args: Args) -> Result<Value> {
203 let params = one_expr_arg(cx, args, "mcp/read expects one resources/read params Expr")?;
204 let session = crate::McpSession::fixture();
205 let result = resource_methods::read(cx, &session, params)?;
206 cx.factory().expr(result)
207}
208
209fn prompts(cx: &mut Cx, args: Args) -> Result<Value> {
210 no_args(args, "mcp/prompts expects no arguments")?;
211 let session = crate::McpSession::fixture();
212 let result = prompt_methods::list(cx, &session)?;
213 cx.factory().expr(result)
214}
215
216fn get_prompt(cx: &mut Cx, args: Args) -> Result<Value> {
217 let params = one_expr_arg(
218 cx,
219 args,
220 "mcp/get-prompt expects one prompts/get params Expr",
221 )?;
222 let session = crate::McpSession::fixture();
223 let result = prompt_methods::get(cx, &session, params)?;
224 cx.factory().expr(result)
225}
226
227#[cfg(feature = "sampling")]
228fn sampling_runner(cx: &mut Cx, args: Args) -> Result<Value> {
229 no_args(args, "mcp/sampling-runner expects no arguments")?;
230 crate::sampling::sampling_runner_value(
231 cx,
232 Arc::new(crate::sampling::McpSamplingRunner::fixture()),
233 )
234}
235
236fn health(cx: &mut Cx, args: Args) -> Result<Value> {
237 no_args(args, "mcp/health expects no arguments")?;
238 let session = crate::McpSession::fixture();
239 cx.factory().expr(core::health(&session))
240}
241
242fn one_expr_arg(cx: &mut Cx, args: Args, message: &'static str) -> Result<Expr> {
243 let mut values = args.into_vec();
244 if values.len() != 1 {
245 return Err(Error::Eval(message.to_owned()));
246 }
247 values.remove(0).object().as_expr(cx)
248}
249
250fn no_args(args: Args, message: &'static str) -> Result<()> {
251 if args.values().is_empty() {
252 Ok(())
253 } else {
254 Err(Error::Eval(message.to_owned()))
255 }
256}
257
258pub fn handle_symbol() -> Symbol {
260 Symbol::qualified("mcp", "handle")
261}
262
263pub fn initialize_symbol() -> Symbol {
265 Symbol::qualified("mcp", "initialize")
266}
267
268pub fn tools_symbol() -> Symbol {
270 Symbol::qualified("mcp", "tools")
271}
272
273pub fn call_symbol() -> Symbol {
275 Symbol::qualified("mcp", "call")
276}
277
278pub fn resources_symbol() -> Symbol {
280 Symbol::qualified("mcp", "resources")
281}
282
283pub fn read_symbol() -> Symbol {
285 Symbol::qualified("mcp", "read")
286}
287
288pub fn prompts_symbol() -> Symbol {
290 Symbol::qualified("mcp", "prompts")
291}
292
293pub fn get_prompt_symbol() -> Symbol {
295 Symbol::qualified("mcp", "get-prompt")
296}
297
298pub fn health_symbol() -> Symbol {
300 Symbol::qualified("mcp", "health")
301}