Skip to main content

soma_mcp_proxy/
projection.rs

1//! Projection of proxy routes into concrete `rmcp::model` types.
2//!
3//! [`crate::McpToolRoute`], [`crate::McpResourceRoute`], and
4//! [`crate::McpPromptRoute`] carry the loose descriptor fields recovered from
5//! an upstream MCP server. Any caller that wants to hand these routes back to
6//! *its own* inbound MCP clients (as opposed to just tracking route names)
7//! eventually needs them as real `rmcp::model` objects. This module owns that
8//! conversion once, on top of `soma-mcp-server`'s generic descriptor
9//! projection, so callers do not each reimplement the JSON-schema-to-`Tool`
10//! shuffle.
11
12use rmcp::model::{Prompt, Resource, Tool};
13use soma_mcp_server::protocol::{
14    prompt_from_descriptor, resource_from_descriptor, tool_from_descriptor,
15};
16
17use crate::{McpPromptRoute, McpResourceRoute, McpToolRoute};
18
19/// Project a [`McpToolRoute`] into an [`rmcp::model::Tool`] ready for
20/// `tools/list`.
21pub fn rmcp_tool_from_route(route: &McpToolRoute) -> Tool {
22    tool_from_descriptor(
23        route.name.clone(),
24        route.descriptor.description.clone(),
25        route.descriptor.input_schema.clone(),
26        route.descriptor.output_schema.clone(),
27        route.descriptor.destructive,
28    )
29}
30
31/// Project a [`McpResourceRoute`] into an [`rmcp::model::Resource`] ready for
32/// `resources/list`.
33pub fn rmcp_resource_from_route(route: &McpResourceRoute) -> Resource {
34    let name = route
35        .descriptor
36        .name
37        .clone()
38        .unwrap_or_else(|| route.native_uri.clone());
39    resource_from_descriptor(route.uri.clone(), name)
40}
41
42/// Project a [`McpPromptRoute`] into an [`rmcp::model::Prompt`] ready for
43/// `prompts/list`.
44pub fn rmcp_prompt_from_route(route: &McpPromptRoute) -> Prompt {
45    prompt_from_descriptor(route.name.clone(), route.descriptor.description.as_deref())
46}
47
48#[cfg(test)]
49#[path = "projection_tests.rs"]
50mod tests;