rmcp_openapi/tool/metadata.rs
1use rmcp::model::{Tool, ToolAnnotations};
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 pub description: String,
21 /// Input parameters schema - exposed to MCP clients as `inputSchema`
22 pub parameters: Value,
23 /// Output schema - exposed to MCP clients as `outputSchema`
24 pub output_schema: Option<Value>,
25 /// HTTP method (GET, POST, etc.) - internal only, not exposed to MCP
26 pub method: String,
27 /// URL path for the API endpoint - internal only, not exposed to MCP
28 pub path: String,
29 /// Security requirements from OpenAPI spec - internal only, not exposed to MCP
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub security: Option<Vec<String>>,
32}
33
34impl ToolMetadata {
35 /// Check if this tool requires authentication based on OpenAPI security definitions
36 pub fn requires_auth(&self) -> bool {
37 self.security.as_ref().is_some_and(|s| !s.is_empty())
38 }
39}
40
41/// Converts internal `ToolMetadata` to MCP-compliant `Tool`.
42///
43/// This implementation ensures that only MCP-compliant fields are exposed to clients.
44/// Internal fields like `method` and `path` are not included in the conversion.
45impl From<&ToolMetadata> for Tool {
46 fn from(metadata: &ToolMetadata) -> Self {
47 // Convert parameters to the expected Arc<Map> format
48 let input_schema = if let Value::Object(obj) = &metadata.parameters {
49 Arc::new(obj.clone())
50 } else {
51 Arc::new(serde_json::Map::new())
52 };
53
54 // Convert output_schema to the expected Arc<Map> format if present
55 let output_schema = metadata.output_schema.as_ref().and_then(|schema| {
56 if let Value::Object(obj) = schema {
57 Some(Arc::new(obj.clone()))
58 } else {
59 None
60 }
61 });
62
63 // Create annotations with title if present
64 let annotations = metadata.title.as_ref().map(|title| ToolAnnotations {
65 title: Some(title.clone()),
66 ..Default::default()
67 });
68
69 Tool {
70 name: metadata.name.clone().into(),
71 description: Some(metadata.description.clone().into()),
72 input_schema,
73 output_schema,
74 annotations,
75 // TODO: Consider migration to Tool.title when rmcp supports MCP 2025-06-18 (see issue #26)
76 }
77 }
78}