1use super::convert::{prompt_value_to_messages, vm_value_to_content, vm_value_to_json};
2use super::defs::{
3 McpCompletionSource, McpPromptDef, McpResourceDef, McpResourceTemplateDef, McpServerMetadata,
4 McpToolDef,
5};
6use super::tools_schema::McpToolSet;
7use super::uri::{match_uri_template, uri_template_variables};
8use crate::mcp_progress::{
9 active_bus as active_progress_bus, install_active_bus as install_active_progress_bus,
10 is_valid_progress_token, scope_context, ProgressBus, ProgressContext,
11};
12use crate::mcp_protocol::{
13 self, apply_result_envelope, parse_request_metadata, server_discover_result, McpCacheHint,
14};
15use crate::stdlib::json_to_vm_value;
16use crate::value::VmError;
17use crate::vm::Vm;
18use std::sync::Mutex;
19use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
20use tokio::sync::mpsc;
21pub struct McpServer {
23 server_name: String,
24 server_version: String,
25 tools: McpToolSet,
26 resources: Vec<McpResourceDef>,
27 resource_templates: Vec<McpResourceTemplateDef>,
28 prompts: Vec<McpPromptDef>,
29 server_card: Option<serde_json::Value>,
34 instructions: Option<String>,
35 list_changes: bool,
36 connection: Mutex<mcp_protocol::McpServerSession>,
37 tasks: crate::mcp_tasks::McpTaskStore,
41}
42
43pub struct McpServerReload<A> {
48 server: McpServer,
49 vm: Vm,
50 anchor: A,
51}
52
53impl<A> McpServerReload<A> {
54 pub fn new(server: McpServer, vm: Vm, anchor: A) -> Self {
55 Self { server, vm, anchor }
56 }
57}
58
59impl McpServer {
60 pub fn new(
61 server_name: String,
62 tools: McpToolSet,
63 resources: Vec<McpResourceDef>,
64 resource_templates: Vec<McpResourceTemplateDef>,
65 prompts: Vec<McpPromptDef>,
66 ) -> Self {
67 Self {
68 server_name,
69 server_version: env!("CARGO_PKG_VERSION").to_string(),
70 tools,
71 resources,
72 resource_templates,
73 prompts,
74 server_card: None,
75 instructions: None,
76 list_changes: false,
77 connection: Mutex::new(mcp_protocol::McpServerSession::default()),
78 tasks: crate::mcp_tasks::McpTaskStore::new(),
79 }
80 }
81
82 pub fn with_metadata(mut self, metadata: McpServerMetadata) -> Self {
84 if let Some(name) = metadata.name {
85 self.server_name = name;
86 }
87 if let Some(version) = metadata.version {
88 self.server_version = version;
89 }
90 self.instructions = metadata.instructions;
91 self
92 }
93
94 pub fn with_server_card(mut self, card: serde_json::Value) -> Self {
98 self.server_card = Some(card);
99 self
100 }
101
102 pub fn with_list_changes(mut self, enabled: bool) -> Self {
104 self.list_changes = enabled;
105 self
106 }
107
108 pub async fn run(&mut self, vm: &mut Vm) -> Result<(), VmError> {
112 let (_reload_tx, mut reload_rx) = mpsc::unbounded_channel();
113 let mut anchor = ();
114 self.run_loop(vm, &mut anchor, &mut reload_rx, false).await
115 }
116
117 pub async fn run_reloadable<A>(
120 mut self,
121 mut vm: Vm,
122 mut anchor: A,
123 mut reload_rx: mpsc::UnboundedReceiver<Result<McpServerReload<A>, String>>,
124 ) -> Result<(), VmError> {
125 let result = self
126 .run_loop(&mut vm, &mut anchor, &mut reload_rx, true)
127 .await;
128 drop(vm);
131 drop(anchor);
132 result
133 }
134
135 async fn run_loop<A>(
136 &mut self,
137 vm: &mut Vm,
138 anchor: &mut A,
139 reload_rx: &mut mpsc::UnboundedReceiver<Result<McpServerReload<A>, String>>,
140 mut reload_enabled: bool,
141 ) -> Result<(), VmError> {
142 let (out_tx, mut out_rx) = mpsc::unbounded_channel::<serde_json::Value>();
143 let progress_bus = ProgressBus::from_mpsc(out_tx.clone());
144
145 let writer = tokio::spawn(async move {
146 let mut stdout = tokio::io::stdout();
147 while let Some(msg) = out_rx.recv().await {
148 let mut line = match serde_json::to_string(&msg) {
149 Ok(value) => value,
150 Err(_) => continue,
151 };
152 line.push('\n');
153 if stdout.write_all(line.as_bytes()).await.is_err() {
154 break;
155 }
156 if stdout.flush().await.is_err() {
157 break;
158 }
159 }
160 });
161
162 let previous_progress = install_active_progress_bus(Some(progress_bus));
163 let stdin = BufReader::new(tokio::io::stdin());
164 let mut lines = stdin.lines();
165 loop {
166 tokio::select! {
167 line = lines.next_line() => {
168 let Ok(Some(line)) = line else {
169 break;
170 };
171 let trimmed = line.trim();
172 if trimmed.is_empty() {
173 continue;
174 }
175 let Ok(msg) = serde_json::from_str::<serde_json::Value>(trimmed) else {
176 continue;
177 };
178 if msg.get("method").is_none() {
181 continue;
182 }
183 if let Some(response) = self.handle_json_rpc(msg, vm).await {
184 if out_tx.send(response).is_err() {
185 break;
186 }
187 }
188 }
189 replacement = reload_rx.recv(), if reload_enabled => {
190 match replacement {
191 Some(Ok(replacement)) => {
192 let McpServerReload {
193 server,
194 vm: next_vm,
195 anchor: next_anchor,
196 } = replacement;
197 self.apply_reload(server);
198 let previous_vm = std::mem::replace(vm, next_vm);
199 let previous_anchor = std::mem::replace(anchor, next_anchor);
200 drop(previous_vm);
201 drop(previous_anchor);
202 if self
203 .connection
204 .lock()
205 .expect("MCP session lock poisoned")
206 .is_ready_for_notifications()
207 {
208 let _ = out_tx.send(serde_json::json!({
209 "jsonrpc": "2.0",
210 "method": "notifications/tools/list_changed",
211 "params": {},
212 }));
213 let _ = out_tx.send(serde_json::json!({
214 "jsonrpc": "2.0",
215 "method": "notifications/resources/list_changed",
216 "params": {},
217 }));
218 let _ = out_tx.send(serde_json::json!({
219 "jsonrpc": "2.0",
220 "method": "notifications/prompts/list_changed",
221 "params": {},
222 }));
223 }
224 }
225 Some(Err(error)) => {
226 eprintln!(
227 "[harn] serve mcp: reload failed; keeping previous registry: {error}"
228 );
229 }
230 None => {
231 eprintln!(
232 "[harn] serve mcp: source reload task stopped; keeping current registry"
233 );
234 reload_enabled = false;
235 }
236 }
237 }
238 }
239 }
240
241 drop(out_tx);
243 install_active_progress_bus(previous_progress);
244
245 let _ = writer.await;
248 Ok(())
249 }
250
251 fn apply_reload(&mut self, replacement: McpServer) {
252 self.server_name = replacement.server_name;
253 self.server_version = replacement.server_version;
254 self.tools = replacement.tools;
255 self.resources = replacement.resources;
256 self.resource_templates = replacement.resource_templates;
257 self.prompts = replacement.prompts;
258 self.server_card = replacement.server_card;
259 self.instructions = replacement.instructions;
260 }
261
262 pub async fn handle_json_rpc(
264 &self,
265 msg: serde_json::Value,
266 vm: &mut Vm,
267 ) -> Option<serde_json::Value> {
268 let method = msg.get("method").and_then(|m| m.as_str()).unwrap_or("");
269 if method == "notifications/initialized" {
270 self.connection
271 .lock()
272 .expect("MCP session lock poisoned")
273 .accept_initialized_notification();
274 return None;
275 }
276 let id = msg.get("id").cloned()?;
277 let params = msg.get("params").cloned().unwrap_or(serde_json::json!({}));
278
279 if method == "initialize" {
280 let result = self
281 .connection
282 .lock()
283 .expect("MCP session lock poisoned")
284 .initialize(
285 ¶ms,
286 serde_json::Value::Object(self.server_capabilities()),
287 self.server_info_value(),
288 self.instructions.as_deref(),
289 );
290 return Some(match result {
291 Ok(result) => crate::jsonrpc::response(id, result),
292 Err(error) => crate::jsonrpc::error_response(id, -32602, &error),
293 });
294 }
295
296 let request_profile = match self
297 .connection
298 .lock()
299 .expect("MCP session lock poisoned")
300 .accept_request(&id, method, ¶ms)
301 {
302 Ok(profile) => profile,
303 Err(response) => return Some(response),
304 };
305
306 if let Some(response) =
307 mcp_protocol::explicit_unsupported_method_response(id.clone(), method)
308 {
309 return Some(response);
310 }
311 if request_profile.uses_result_envelope()
312 && mcp_protocol::is_task_method(method)
313 && !mcp_protocol::client_supports_tasks(¶ms)
314 {
315 return Some(mcp_protocol::missing_tasks_capability_response(id.clone()));
316 }
317
318 let response = match method {
319 mcp_protocol::METHOD_SERVER_DISCOVER => self.handle_server_discover(&id),
320 "ping" => crate::jsonrpc::response(id.clone(), serde_json::json!({})),
321 "harn.hitl.respond" => self.handle_hitl_respond(&id, ¶ms).await,
322 "tools/list" => self.handle_tools_list(&id, ¶ms),
323 "tools/call" => {
324 self.handle_tools_call(&id, ¶ms, vm, request_profile.uses_result_envelope())
325 .await
326 }
327 mcp_protocol::METHOD_TASKS_GET => self.tasks.handle_get(
328 &crate::mcp_tasks::McpTaskAccess::unscoped(),
329 id.clone(),
330 ¶ms,
331 ),
332 mcp_protocol::METHOD_TASKS_UPDATE => self.tasks.handle_update(
333 &crate::mcp_tasks::McpTaskAccess::unscoped(),
334 id.clone(),
335 ¶ms,
336 ),
337 mcp_protocol::METHOD_TASKS_CANCEL => self.tasks.handle_cancel(
338 &crate::mcp_tasks::McpTaskAccess::unscoped(),
339 id.clone(),
340 ¶ms,
341 ),
342 "resources/list" => self.handle_resources_list(&id, ¶ms),
343 "resources/read" => self.handle_resources_read(&id, ¶ms, vm).await,
344 "resources/templates/list" => self.handle_resource_templates_list(&id, ¶ms),
345 "prompts/list" => self.handle_prompts_list(&id, ¶ms),
346 "prompts/get" => self.handle_prompts_get(&id, ¶ms, vm).await,
347 mcp_protocol::METHOD_COMPLETION_COMPLETE => {
348 self.handle_completion_complete(&id, ¶ms, vm).await
349 }
350 _ => serde_json::json!({
351 "jsonrpc": "2.0",
352 "id": id,
353 "error": {
354 "code": -32601,
355 "message": format!("Method not found: {method}")
356 }
357 }),
358 };
359 Some(if request_profile.uses_result_envelope() {
360 apply_envelope(response, cache_hint_for_method(method))
361 } else {
362 response
363 })
364 }
365
366 fn server_capabilities(&self) -> serde_json::Map<String, serde_json::Value> {
367 let mut capabilities = serde_json::Map::new();
368 if !self.tools.is_empty() || self.list_changes {
369 capabilities.insert(
370 "tools".into(),
371 if self.list_changes {
372 serde_json::json!({"listChanged": true})
373 } else {
374 serde_json::json!({})
375 },
376 );
377 }
378 if !self.resources.is_empty()
379 || !self.resource_templates.is_empty()
380 || self.server_card.is_some()
381 || self.list_changes
382 {
383 capabilities.insert(
384 "resources".into(),
385 if self.list_changes {
386 serde_json::json!({"listChanged": true})
387 } else {
388 serde_json::json!({})
389 },
390 );
391 }
392 if !self.prompts.is_empty() || self.list_changes {
393 capabilities.insert(
394 "prompts".into(),
395 if self.list_changes {
396 serde_json::json!({"listChanged": true})
397 } else {
398 serde_json::json!({})
399 },
400 );
401 }
402 let mut extensions = mcp_protocol::tasks_capability();
403 capabilities.insert("completions".into(), mcp_protocol::completions_capability());
404 if self.resources.iter().any(|resource| {
405 resource.uri.starts_with("ui://")
406 && resource.mime_type.as_deref() == Some("text/html;profile=mcp-app")
407 }) {
408 extensions["io.modelcontextprotocol/ui"] = serde_json::json!({
409 "mimeTypes": ["text/html;profile=mcp-app"]
410 });
411 }
412 capabilities.insert("extensions".into(), extensions);
413 capabilities
414 }
415
416 fn server_info_value(&self) -> serde_json::Value {
417 let mut server_info = serde_json::json!({
418 "name": self.server_name,
419 "version": self.server_version
420 });
421 if let Some(ref card) = self.server_card {
422 server_info["card"] = card.clone();
423 }
424 server_info
425 }
426
427 fn handle_server_discover(&self, id: &serde_json::Value) -> serde_json::Value {
428 let capabilities = serde_json::Value::Object(self.server_capabilities());
429 let result = server_discover_result(
430 capabilities,
431 self.server_info_value(),
432 self.instructions.as_deref(),
433 );
434 crate::jsonrpc::response(id.clone(), result)
435 }
436
437 fn handle_tools_list(
438 &self,
439 id: &serde_json::Value,
440 params: &serde_json::Value,
441 ) -> serde_json::Value {
442 let prepared_tools = self.tools.prepared().mcp_tools();
443 let page = match mcp_protocol::mcp_list_page(params, prepared_tools.len(), "tools/list") {
444 Ok(page) => page,
445 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
446 };
447 let tools = prepared_tools[page.start..page.end].to_vec();
448
449 let mut result = serde_json::json!({ "tools": tools });
450 if let Some(next_cursor) = page.next_cursor {
451 result["nextCursor"] = serde_json::json!(next_cursor);
452 }
453
454 serde_json::json!({
455 "jsonrpc": "2.0",
456 "id": id,
457 "result": result
458 })
459 }
460
461 async fn handle_tools_call(
462 &self,
463 id: &serde_json::Value,
464 params: &serde_json::Value,
465 vm: &mut Vm,
466 uses_result_envelope: bool,
467 ) -> serde_json::Value {
468 let tool_name = params.get("name").and_then(|n| n.as_str()).unwrap_or("");
469 let tool = match self
470 .tools
471 .iter()
472 .find(|tool| tool.catalog.name == tool_name)
473 {
474 Some(t) => t,
475 None => {
476 return serde_json::json!({
477 "jsonrpc": "2.0",
478 "id": id,
479 "error": { "code": -32602, "message": format!("Unknown tool: {tool_name}") }
480 });
481 }
482 };
483
484 let wants_task = mcp_protocol::client_supports_tasks(params);
487 let task_support = tool
488 .catalog
489 .execution
490 .as_ref()
491 .map(|execution| execution.task_support)
492 .unwrap_or_default();
493 let as_task = task_support.allows_task() && wants_task;
494 if task_support == crate::mcp_tasks::McpTaskSupport::Required && !wants_task {
495 return mcp_protocol::missing_tasks_capability_response(id.clone());
496 }
497
498 let arguments = params
499 .get("arguments")
500 .cloned()
501 .unwrap_or(serde_json::json!({}));
502 if let Err(error) = self.tools.prepared().validate_input(tool_name, &arguments) {
503 return crate::jsonrpc::error_response(id.clone(), -32602, &error.to_string());
504 }
505 let args_vm = json_to_vm_value(&arguments);
506
507 let progress_token = params
516 .pointer("/_meta/progressToken")
517 .cloned()
518 .filter(is_valid_progress_token);
519 let progress_ctx = progress_token
520 .and_then(|token| active_progress_bus().map(|bus| ProgressContext::new(bus, token)));
521
522 let result = match crate::mcp_input::scope_input_context(
523 params,
524 client_capabilities(params),
525 scope_context(
526 progress_ctx,
527 crate::tool_handler_scope::scope(vm.call_closure_pub(&tool.handler, &[args_vm])),
528 ),
529 )
530 .await
531 {
532 Ok(result) => result,
533 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
534 };
535
536 if as_task {
544 let lease = match self.tasks.begin(
545 crate::mcp_tasks::McpTaskAccess::unscoped(),
546 Some(crate::mcp_tasks::DEFAULT_TASK_TTL_MS),
547 ) {
548 Ok(lease) => lease,
549 Err(error) => {
550 return crate::jsonrpc::error_response(id.clone(), -32000, &error.to_string());
551 }
552 };
553 let task = lease.task().clone();
554 match crate::tool_registry::classify_tool_result(
555 self.tools.prepared(),
556 &tool.catalog.name,
557 result,
558 ) {
559 Ok(crate::tool_registry::ToolInvocationOutcome::Success { value, json }) => {
560 match successful_tool_call_result(&self.tools, tool, &value, json) {
561 Ok(result) => lease.complete_with_tool_result(result, uses_result_envelope),
562 Err(error) => lease.complete(Err(error), uses_result_envelope),
563 }
564 }
565 Ok(crate::tool_registry::ToolInvocationOutcome::ApplicationError(error)) => lease
566 .complete_with_tool_result(
567 crate::tool_registry::application_error_mcp_result(&error),
568 uses_result_envelope,
569 ),
570 Err(crate::tool_registry::ToolInvocationError::Runtime(
571 VmError::McpInputRequired(_),
572 )) => lease.complete(
573 Err("Tool requested client input, which a task cannot carry".to_string()),
574 uses_result_envelope,
575 ),
576 Err(error) => lease.complete_with_tool_result(
577 serde_json::json!({
578 "content": [{"type": "text", "text": error.to_string()}],
579 "isError": true,
580 }),
581 uses_result_envelope,
582 ),
583 }
584 return crate::mcp_tasks::task_created_response(
585 id.clone(),
586 &task,
587 "The requested Harn tool ran and its result is available via tasks/get.",
588 );
589 }
590
591 match crate::tool_registry::classify_tool_result(
592 self.tools.prepared(),
593 &tool.catalog.name,
594 result,
595 ) {
596 Ok(crate::tool_registry::ToolInvocationOutcome::Success { value, json }) => {
597 match successful_tool_call_result(&self.tools, tool, &value, json) {
598 Ok(result) => serde_json::json!({
599 "jsonrpc": "2.0",
600 "id": id,
601 "result": result
602 }),
603 Err(error) => crate::jsonrpc::response(
604 id.clone(),
605 serde_json::json!({
606 "content": [{"type": "text", "text": error}],
607 "isError": true,
608 }),
609 ),
610 }
611 }
612 Ok(crate::tool_registry::ToolInvocationOutcome::ApplicationError(error)) => {
613 crate::jsonrpc::response(
614 id.clone(),
615 crate::tool_registry::application_error_mcp_result(&error),
616 )
617 }
618 Err(crate::tool_registry::ToolInvocationError::Runtime(VmError::McpInputRequired(
619 required,
620 ))) => crate::jsonrpc::response(id.clone(), crate::mcp_input::input_result(*required)),
621 Err(error) => serde_json::json!({
622 "jsonrpc": "2.0",
623 "id": id,
624 "result": {
625 "content": [{ "type": "text", "text": error.to_string() }],
626 "isError": true,
627 },
628 }),
629 }
630 }
631
632 async fn handle_hitl_respond(
633 &self,
634 id: &serde_json::Value,
635 params: &serde_json::Value,
636 ) -> serde_json::Value {
637 let response: crate::stdlib::hitl::HitlHostResponse =
638 match serde_json::from_value(params.clone()) {
639 Ok(response) => response,
640 Err(error) => {
641 return serde_json::json!({
642 "jsonrpc": "2.0",
643 "id": id,
644 "error": {
645 "code": -32602,
646 "message": format!("invalid harn.hitl.respond params: {error}"),
647 }
648 });
649 }
650 };
651 let cwd = std::env::current_dir().ok();
652 match crate::stdlib::hitl::append_hitl_response(cwd.as_deref(), response).await {
653 Ok(_) => serde_json::json!({
654 "jsonrpc": "2.0",
655 "id": id,
656 "result": { "ok": true }
657 }),
658 Err(error) => serde_json::json!({
659 "jsonrpc": "2.0",
660 "id": id,
661 "error": {
662 "code": -32000,
663 "message": error
664 }
665 }),
666 }
667 }
668
669 fn handle_resources_list(
670 &self,
671 id: &serde_json::Value,
672 params: &serde_json::Value,
673 ) -> serde_json::Value {
674 let mut all_resources = Vec::with_capacity(self.resources.len() + 1);
675 if self.server_card.is_some() {
676 all_resources.push(serde_json::json!({
677 "uri": "well-known://mcp-card",
678 "name": "Server Card",
679 "description": "MCP v2.1 Server Card advertising this server's identity and capabilities",
680 "mimeType": "application/json",
681 }));
682 }
683 all_resources.extend(self.resources.iter().map(|r| {
684 let mut entry = serde_json::json!({ "uri": r.uri, "name": r.name });
685 if let Some(ref title) = r.title {
686 entry["title"] = serde_json::json!(title);
687 }
688 if let Some(ref desc) = r.description {
689 entry["description"] = serde_json::json!(desc);
690 }
691 if let Some(ref mime) = r.mime_type {
692 entry["mimeType"] = serde_json::json!(mime);
693 }
694 if let Some(ref meta) = r.meta {
695 entry["_meta"] = meta.clone();
696 }
697 entry
698 }));
699
700 let page = match mcp_protocol::mcp_list_page(params, all_resources.len(), "resources/list")
701 {
702 Ok(page) => page,
703 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
704 };
705 let resources = all_resources[page.start..page.end].to_vec();
706
707 let mut result = serde_json::json!({ "resources": resources });
708 if let Some(next_cursor) = page.next_cursor {
709 result["nextCursor"] = serde_json::json!(next_cursor);
710 }
711
712 serde_json::json!({
713 "jsonrpc": "2.0",
714 "id": id,
715 "result": result
716 })
717 }
718
719 async fn handle_resources_read(
720 &self,
721 id: &serde_json::Value,
722 params: &serde_json::Value,
723 vm: &mut Vm,
724 ) -> serde_json::Value {
725 let uri = params.get("uri").and_then(|u| u.as_str()).unwrap_or("");
726
727 if uri == "well-known://mcp-card" {
731 if let Some(ref card) = self.server_card {
732 let content = serde_json::json!({
733 "uri": uri,
734 "text": serde_json::to_string(card).unwrap_or_else(|_| "{}".to_string()),
735 "mimeType": "application/json",
736 });
737 return serde_json::json!({
738 "jsonrpc": "2.0",
739 "id": id,
740 "result": { "contents": [content] }
741 });
742 }
743 }
744
745 if let Some(resource) = self.resources.iter().find(|r| r.uri == uri) {
747 let mut content = serde_json::json!({ "uri": resource.uri, "text": resource.text });
748 if let Some(ref mime) = resource.mime_type {
749 content["mimeType"] = serde_json::json!(mime);
750 }
751 if let Some(ref meta) = resource.meta {
752 content["_meta"] = meta.clone();
753 }
754 return serde_json::json!({
755 "jsonrpc": "2.0",
756 "id": id,
757 "result": { "contents": [content] }
758 });
759 }
760
761 for tmpl in &self.resource_templates {
762 if let Some(args) = match_uri_template(&tmpl.uri_template, uri) {
763 let args_vm = json_to_vm_value(&serde_json::json!(args));
764 let result = match crate::mcp_input::scope_input_context(
765 params,
766 client_capabilities(params),
767 vm.call_closure_pub(&tmpl.handler, &[args_vm]),
768 )
769 .await
770 {
771 Ok(result) => result,
772 Err(error) => {
773 return crate::jsonrpc::error_response(id.clone(), -32602, &error)
774 }
775 };
776 return match result {
777 Ok(value) => {
778 let mut content = serde_json::json!({
779 "uri": uri,
780 "text": value.display(),
781 });
782 if let Some(ref mime) = tmpl.mime_type {
783 content["mimeType"] = serde_json::json!(mime);
784 }
785 serde_json::json!({
786 "jsonrpc": "2.0",
787 "id": id,
788 "result": { "contents": [content] }
789 })
790 }
791 Err(VmError::McpInputRequired(required)) => crate::jsonrpc::response(
792 id.clone(),
793 crate::mcp_input::input_result(*required),
794 ),
795 Err(e) => serde_json::json!({
796 "jsonrpc": "2.0",
797 "id": id,
798 "error": { "code": -32603, "message": format!("{e}") }
799 }),
800 };
801 }
802 }
803
804 serde_json::json!({
805 "jsonrpc": "2.0",
806 "id": id,
807 "error": { "code": -32002, "message": format!("Resource not found: {uri}") }
808 })
809 }
810
811 fn handle_resource_templates_list(
812 &self,
813 id: &serde_json::Value,
814 params: &serde_json::Value,
815 ) -> serde_json::Value {
816 let page = match mcp_protocol::mcp_list_page(
817 params,
818 self.resource_templates.len(),
819 "resources/templates/list",
820 ) {
821 Ok(page) => page,
822 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
823 };
824 let templates: Vec<serde_json::Value> = self.resource_templates[page.start..page.end]
825 .iter()
826 .map(|t| {
827 let mut entry =
828 serde_json::json!({ "uriTemplate": t.uri_template, "name": t.name });
829 if let Some(ref title) = t.title {
830 entry["title"] = serde_json::json!(title);
831 }
832 if let Some(ref desc) = t.description {
833 entry["description"] = serde_json::json!(desc);
834 }
835 if let Some(ref mime) = t.mime_type {
836 entry["mimeType"] = serde_json::json!(mime);
837 }
838 entry
839 })
840 .collect();
841
842 let mut result = serde_json::json!({ "resourceTemplates": templates });
843 if let Some(next_cursor) = page.next_cursor {
844 result["nextCursor"] = serde_json::json!(next_cursor);
845 }
846
847 serde_json::json!({
848 "jsonrpc": "2.0",
849 "id": id,
850 "result": result
851 })
852 }
853
854 fn handle_prompts_list(
855 &self,
856 id: &serde_json::Value,
857 params: &serde_json::Value,
858 ) -> serde_json::Value {
859 let page = match mcp_protocol::mcp_list_page(params, self.prompts.len(), "prompts/list") {
860 Ok(page) => page,
861 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
862 };
863 let prompts: Vec<serde_json::Value> = self.prompts[page.start..page.end]
864 .iter()
865 .map(|p| {
866 let mut entry = serde_json::json!({ "name": p.name });
867 if let Some(ref title) = p.title {
868 entry["title"] = serde_json::json!(title);
869 }
870 if let Some(ref desc) = p.description {
871 entry["description"] = serde_json::json!(desc);
872 }
873 if let Some(ref args) = p.arguments {
874 let args_json: Vec<serde_json::Value> = args
875 .iter()
876 .map(|a| {
877 let mut arg =
878 serde_json::json!({ "name": a.name, "required": a.required });
879 if let Some(ref desc) = a.description {
880 arg["description"] = serde_json::json!(desc);
881 }
882 arg
883 })
884 .collect();
885 entry["arguments"] = serde_json::json!(args_json);
886 }
887 entry
888 })
889 .collect();
890
891 let mut result = serde_json::json!({ "prompts": prompts });
892 if let Some(next_cursor) = page.next_cursor {
893 result["nextCursor"] = serde_json::json!(next_cursor);
894 }
895
896 serde_json::json!({
897 "jsonrpc": "2.0",
898 "id": id,
899 "result": result
900 })
901 }
902
903 async fn handle_prompts_get(
904 &self,
905 id: &serde_json::Value,
906 params: &serde_json::Value,
907 vm: &mut Vm,
908 ) -> serde_json::Value {
909 let name = params.get("name").and_then(|n| n.as_str()).unwrap_or("");
910
911 let prompt = match self.prompts.iter().find(|p| p.name == name) {
912 Some(p) => p,
913 None => {
914 return serde_json::json!({
915 "jsonrpc": "2.0",
916 "id": id,
917 "error": { "code": -32602, "message": format!("Unknown prompt: {name}") }
918 });
919 }
920 };
921
922 let arguments = params
923 .get("arguments")
924 .cloned()
925 .unwrap_or(serde_json::json!({}));
926 let args_vm = json_to_vm_value(&arguments);
927
928 let result = match crate::mcp_input::scope_input_context(
929 params,
930 client_capabilities(params),
931 vm.call_closure_pub(&prompt.handler, &[args_vm]),
932 )
933 .await
934 {
935 Ok(result) => result,
936 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
937 };
938
939 match result {
940 Ok(value) => {
941 let messages = prompt_value_to_messages(&value);
942 serde_json::json!({
943 "jsonrpc": "2.0",
944 "id": id,
945 "result": { "messages": messages }
946 })
947 }
948 Err(VmError::McpInputRequired(required)) => {
949 crate::jsonrpc::response(id.clone(), crate::mcp_input::input_result(*required))
950 }
951 Err(e) => serde_json::json!({
952 "jsonrpc": "2.0",
953 "id": id,
954 "error": { "code": -32603, "message": format!("{e}") }
955 }),
956 }
957 }
958
959 async fn handle_completion_complete(
960 &self,
961 id: &serde_json::Value,
962 params: &serde_json::Value,
963 vm: &mut Vm,
964 ) -> serde_json::Value {
965 let Some(ref_type) = params.pointer("/ref/type").and_then(|value| value.as_str()) else {
966 return crate::jsonrpc::error_response(
967 id.clone(),
968 -32602,
969 "completion ref.type is required",
970 );
971 };
972 match ref_type {
973 "ref/prompt" => self.complete_prompt_argument(id, params, vm).await,
974 "ref/resource" => {
975 self.complete_resource_template_argument(id, params, vm)
976 .await
977 }
978 other => crate::jsonrpc::error_response(
979 id.clone(),
980 -32602,
981 &format!("Unsupported completion ref.type: {other}"),
982 ),
983 }
984 }
985
986 async fn complete_prompt_argument(
987 &self,
988 id: &serde_json::Value,
989 params: &serde_json::Value,
990 vm: &mut Vm,
991 ) -> serde_json::Value {
992 let name = params
993 .pointer("/ref/name")
994 .and_then(|value| value.as_str())
995 .unwrap_or("");
996 let argument_name = match completion_argument_name(params) {
997 Ok(name) => name,
998 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
999 };
1000 let value = completion_argument_value(params);
1001 let prompt = match self.prompts.iter().find(|prompt| prompt.name == name) {
1002 Some(prompt) => prompt,
1003 None => {
1004 return crate::jsonrpc::error_response(
1005 id.clone(),
1006 -32602,
1007 &format!("Unknown prompt: {name}"),
1008 );
1009 }
1010 };
1011 let Some(argument) = prompt
1012 .arguments
1013 .as_deref()
1014 .unwrap_or_default()
1015 .iter()
1016 .find(|argument| argument.name == argument_name)
1017 else {
1018 return crate::jsonrpc::error_response(
1019 id.clone(),
1020 -32602,
1021 &format!("Unknown prompt argument: {argument_name}"),
1022 );
1023 };
1024 let candidates =
1025 match completion_source_candidates(argument.completion.as_ref(), params, vm).await {
1026 Ok(candidates) => candidates,
1027 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32603, &error),
1028 };
1029 crate::mcp_protocol::completion_result(id.clone(), candidates, value)
1030 }
1031
1032 async fn complete_resource_template_argument(
1033 &self,
1034 id: &serde_json::Value,
1035 params: &serde_json::Value,
1036 vm: &mut Vm,
1037 ) -> serde_json::Value {
1038 let uri = params
1039 .pointer("/ref/uri")
1040 .and_then(|value| value.as_str())
1041 .unwrap_or("");
1042 let argument_name = match completion_argument_name(params) {
1043 Ok(name) => name,
1044 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32602, &error),
1045 };
1046 let value = completion_argument_value(params);
1047 let template = match self
1048 .resource_templates
1049 .iter()
1050 .find(|template| template.uri_template == uri)
1051 {
1052 Some(template) => template,
1053 None => {
1054 return crate::jsonrpc::error_response(
1055 id.clone(),
1056 -32602,
1057 &format!("Unknown resource template: {uri}"),
1058 );
1059 }
1060 };
1061 if !uri_template_variables(&template.uri_template)
1062 .iter()
1063 .any(|name| name == argument_name)
1064 {
1065 return crate::jsonrpc::error_response(
1066 id.clone(),
1067 -32602,
1068 &format!("Unknown resource template argument: {argument_name}"),
1069 );
1070 }
1071 let candidates =
1072 match completion_source_candidates(template.completions.get(argument_name), params, vm)
1073 .await
1074 {
1075 Ok(candidates) => candidates,
1076 Err(error) => return crate::jsonrpc::error_response(id.clone(), -32603, &error),
1077 };
1078 crate::mcp_protocol::completion_result(id.clone(), candidates, value)
1079 }
1080}
1081
1082fn successful_tool_call_result(
1083 tools: &McpToolSet,
1084 tool: &McpToolDef,
1085 value: &crate::value::VmValue,
1086 value_json: serde_json::Value,
1087) -> Result<serde_json::Value, String> {
1088 let mut result = serde_json::json!({
1089 "content": vm_value_to_content(value),
1090 "isError": false,
1091 });
1092 let entry = tools
1093 .prepared()
1094 .entry(&tool.catalog.name)
1095 .expect("prepared MCP catalog covers every executable handler");
1096 if let Some(structured) = tools
1097 .prepared()
1098 .catalog()
1099 .mcp_structured_content(entry, value_json)
1100 .map_err(|error| error.to_string())?
1101 {
1102 result["structuredContent"] = structured;
1103 }
1104 Ok(result)
1105}
1106
1107fn cache_hint_for_method(method: &str) -> Option<&'static McpCacheHint> {
1111 const LIST: McpCacheHint = McpCacheHint::list_default();
1112 const READ: McpCacheHint = McpCacheHint::read_default();
1113 match method {
1114 "tools/list" | "resources/list" | "resources/templates/list" | "prompts/list" => {
1115 Some(&LIST)
1116 }
1117 "resources/read" => Some(&READ),
1118 _ => None,
1119 }
1120}
1121
1122fn apply_envelope(
1123 mut response: serde_json::Value,
1124 hint: Option<&'static McpCacheHint>,
1125) -> serde_json::Value {
1126 if let Some(result) = response.get_mut("result") {
1127 apply_result_envelope(result, hint);
1128 }
1129 response
1130}
1131
1132fn client_capabilities(params: &serde_json::Value) -> serde_json::Value {
1133 serde_json::to_value(parse_request_metadata(params).client_capabilities())
1134 .unwrap_or_else(|_| serde_json::json!({}))
1135}
1136
1137fn completion_argument_name(params: &serde_json::Value) -> Result<&str, String> {
1138 params
1139 .pointer("/argument/name")
1140 .and_then(|value| value.as_str())
1141 .filter(|value| !value.is_empty())
1142 .ok_or_else(|| "completion argument.name is required".to_string())
1143}
1144
1145fn completion_argument_value(params: &serde_json::Value) -> &str {
1146 params
1147 .pointer("/argument/value")
1148 .and_then(|value| value.as_str())
1149 .unwrap_or_default()
1150}
1151
1152async fn completion_source_candidates(
1153 source: Option<&McpCompletionSource>,
1154 params: &serde_json::Value,
1155 vm: &mut Vm,
1156) -> Result<Vec<String>, String> {
1157 let Some(source) = source else {
1158 return Ok(Vec::new());
1159 };
1160 let mut candidates = source.values.clone();
1161 if let Some(handler) = source.handler.as_ref() {
1162 let request = json_to_vm_value(params);
1163 let value = vm
1164 .call_closure_pub(handler, &[request])
1165 .await
1166 .map_err(|error| format!("{error}"))?;
1167 candidates.extend(completion_candidates_from_json(&vm_value_to_json(&value)));
1168 }
1169 Ok(candidates)
1170}
1171
1172fn completion_candidates_from_json(value: &serde_json::Value) -> Vec<String> {
1173 match value {
1174 serde_json::Value::Array(items) => {
1175 items.iter().filter_map(json_completion_string).collect()
1176 }
1177 serde_json::Value::Object(map) => map
1178 .get("values")
1179 .or_else(|| map.get("completion").and_then(|value| value.get("values")))
1180 .map(completion_candidates_from_json)
1181 .unwrap_or_default(),
1182 _ => json_completion_string(value).into_iter().collect(),
1183 }
1184}
1185
1186fn json_completion_string(value: &serde_json::Value) -> Option<String> {
1187 match value {
1188 serde_json::Value::String(value) => Some(value.clone()),
1189 serde_json::Value::Number(_) | serde_json::Value::Bool(_) => Some(value.to_string()),
1190 _ => None,
1191 }
1192}