rmcp_openapi/tool/
metadata.rs

1use rmcp::model::Tool;
2use serde_json::Value;
3use std::{collections::HashMap, sync::Arc};
4
5/// Parameter mapping information for converting between MCP and OpenAPI parameters
6#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
7pub struct ParameterMapping {
8    /// The sanitized parameter name used in MCP
9    pub sanitized_name: String,
10    /// The original parameter name from OpenAPI
11    pub original_name: String,
12    /// The location of the parameter (query, header, path, cookie, body)
13    pub location: String,
14    /// Whether the parameter should be exploded (for arrays/objects)
15    pub explode: bool,
16}
17
18/// Internal metadata for tools generated from OpenAPI operations.
19///
20/// This struct contains all the information needed to execute HTTP requests
21/// and is used internally by the OpenAPI server. It includes fields that are
22/// not part of the MCP specification but are necessary for HTTP execution.
23///
24/// For MCP compliance, this struct is converted to `rmcp::model::Tool` using
25/// the `From` trait implementation, which only includes MCP-compliant fields.
26#[derive(Debug, Clone, serde::Serialize)]
27pub struct ToolMetadata {
28    /// Tool name - exposed to MCP clients
29    pub name: String,
30    /// Tool title - human-readable display name exposed to MCP clients
31    pub title: Option<String>,
32    /// Tool description - exposed to MCP clients
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub description: Option<String>,
35    /// Input parameters schema - exposed to MCP clients as `inputSchema`
36    pub parameters: Value,
37    /// Output schema - exposed to MCP clients as `outputSchema`
38    pub output_schema: Option<Value>,
39    /// HTTP method (GET, POST, etc.) - internal only, not exposed to MCP
40    pub method: String,
41    /// URL path for the API endpoint - internal only, not exposed to MCP
42    pub path: String,
43    /// Security requirements from OpenAPI spec - internal only, not exposed to MCP
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub security: Option<Vec<String>>,
46    /// Parameter mappings for converting between MCP and OpenAPI parameters - internal only, not exposed to MCP
47    #[serde(skip_serializing_if = "HashMap::is_empty")]
48    pub parameter_mappings: HashMap<String, ParameterMapping>,
49}
50
51impl ToolMetadata {
52    /// Check if this tool requires authentication based on OpenAPI security definitions
53    pub fn requires_auth(&self) -> bool {
54        self.security.as_ref().is_some_and(|s| !s.is_empty())
55    }
56}
57
58/// Converts internal `ToolMetadata` to MCP-compliant `Tool`.
59///
60/// This implementation ensures that only MCP-compliant fields are exposed to clients.
61/// Internal fields like `method` and `path` are not included in the conversion.
62impl From<&ToolMetadata> for Tool {
63    fn from(metadata: &ToolMetadata) -> Self {
64        // Convert parameters to the expected Arc<Map> format
65        let input_schema = if let Value::Object(obj) = &metadata.parameters {
66            Arc::new(obj.clone())
67        } else {
68            Arc::new(serde_json::Map::new())
69        };
70
71        // Convert output_schema to the expected Arc<Map> format if present
72        let output_schema = metadata.output_schema.as_ref().and_then(|schema| {
73            if let Value::Object(obj) = schema {
74                Some(Arc::new(obj.clone()))
75            } else {
76                None
77            }
78        });
79
80        Tool {
81            name: metadata.name.clone().into(),
82            description: metadata.description.clone().map(|d| d.into()),
83            input_schema,
84            output_schema,
85            annotations: None,
86            title: metadata.title.clone(),
87            icons: None,
88        }
89    }
90}