rmcp_openapi/tool/
metadata.rs

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