1use rmcp::{
2 RoleServer, ServerHandler,
3 model::{
4 CallToolRequestParam, CallToolResult, Content, ErrorData, Implementation, InitializeResult,
5 ListToolsResult, PaginatedRequestParam, ProtocolVersion, ServerCapabilities, Tool,
6 ToolsCapability,
7 },
8 service::RequestContext,
9};
10use serde_json::Value;
11use std::sync::Arc;
12
13use crate::error::OpenApiError;
14use crate::http_client::HttpClient;
15use crate::openapi_spec::OpenApiSpec;
16use crate::tool_registry::ToolRegistry;
17
18pub struct OpenApiServer {
19 pub spec_url: String,
20 pub registry: ToolRegistry,
21 pub http_client: HttpClient,
22 pub base_url: Option<String>,
23}
24
25#[derive(Debug, Clone, serde::Serialize)]
26pub struct ToolMetadata {
27 pub name: String,
28 pub description: String,
29 pub parameters: Value,
30 pub method: String,
31 pub path: String,
32}
33
34impl OpenApiServer {
35 pub fn new(spec_url: String) -> Self {
36 Self {
37 spec_url,
38 registry: ToolRegistry::new(),
39 http_client: HttpClient::new(),
40 base_url: None,
41 }
42 }
43
44 pub fn with_base_url(spec_url: String, base_url: String) -> Self {
46 let http_client = HttpClient::new().with_base_url(base_url.clone());
47 Self {
48 spec_url,
49 registry: ToolRegistry::new(),
50 http_client,
51 base_url: Some(base_url),
52 }
53 }
54
55 pub async fn load_openapi_spec(&mut self) -> Result<(), OpenApiError> {
56 let spec = if self.spec_url.starts_with("http") {
58 OpenApiSpec::from_url(&self.spec_url).await?
59 } else {
60 OpenApiSpec::from_file(&self.spec_url).await?
61 };
62
63 let registered_count = self.registry.register_from_spec(spec)?;
65
66 println!("Loaded {registered_count} tools from OpenAPI spec");
67 println!("Registry stats: {}", self.registry.get_stats().summary());
68
69 Ok(())
70 }
71
72 pub fn tool_count(&self) -> usize {
74 self.registry.tool_count()
75 }
76
77 pub fn get_tool_names(&self) -> Vec<String> {
79 self.registry.get_tool_names()
80 }
81
82 pub fn has_tool(&self, name: &str) -> bool {
84 self.registry.has_tool(name)
85 }
86
87 pub fn get_registry_stats(&self) -> crate::tool_registry::ToolRegistryStats {
89 self.registry.get_stats()
90 }
91
92 pub fn validate_registry(&self) -> Result<(), OpenApiError> {
94 self.registry.validate_registry()
95 }
96}
97
98impl ServerHandler for OpenApiServer {
99 fn get_info(&self) -> InitializeResult {
100 InitializeResult {
101 protocol_version: ProtocolVersion::V_2024_11_05,
102 server_info: Implementation {
103 name: "OpenAPI MCP Server".to_string(),
104 version: "0.1.0".to_string(),
105 },
106 capabilities: ServerCapabilities {
107 tools: Some(ToolsCapability {
108 list_changed: Some(false),
109 }),
110 ..Default::default()
111 },
112 instructions: Some("Exposes OpenAPI endpoints as MCP tools".to_string()),
113 }
114 }
115
116 async fn list_tools(
117 &self,
118 _request: Option<PaginatedRequestParam>,
119 _context: RequestContext<RoleServer>,
120 ) -> Result<ListToolsResult, ErrorData> {
121 let mut tools = Vec::new();
122
123 for tool_metadata in self.registry.get_all_tools() {
125 let input_schema = if let Value::Object(obj) = &tool_metadata.parameters {
127 Arc::new(obj.clone())
128 } else {
129 Arc::new(serde_json::Map::new())
130 };
131
132 let tool = Tool {
133 name: tool_metadata.name.clone().into(),
134 description: Some(tool_metadata.description.clone().into()),
135 input_schema,
136 annotations: None,
137 };
138 tools.push(tool);
139 }
140
141 Ok(ListToolsResult {
142 tools,
143 next_cursor: None,
144 })
145 }
146
147 async fn call_tool(
148 &self,
149 request: CallToolRequestParam,
150 _context: RequestContext<RoleServer>,
151 ) -> Result<CallToolResult, ErrorData> {
152 if let Some(tool_metadata) = self.registry.get_tool(&request.name) {
154 let arguments = request.arguments.unwrap_or_default();
155 let arguments_value = Value::Object(arguments.clone());
156
157 match self
159 .http_client
160 .execute_tool_call(tool_metadata, &arguments_value)
161 .await
162 {
163 Ok(response) => {
164 Ok(CallToolResult {
166 content: vec![Content::text(response.to_mcp_content())],
167 is_error: Some(!response.is_success),
168 })
169 }
170 Err(e) => {
171 Ok(CallToolResult {
173 content: vec![Content::text(format!(
174 "❌ Error executing tool '{}'\n\nError: {}\n\nTool details:\n- Method: {}\n- Path: {}\n- Arguments: {}",
175 request.name,
176 e,
177 tool_metadata.method.to_uppercase(),
178 tool_metadata.path,
179 serde_json::to_string_pretty(&arguments_value)
180 .unwrap_or_else(|_| "Invalid JSON".to_string())
181 ))],
182 is_error: Some(true),
183 })
184 }
185 }
186 } else {
187 Err(OpenApiError::ToolNotFound(request.name.to_string()).into())
188 }
189 }
190}