crates_docs/server/handler/
standard.rs1use async_trait::async_trait;
4use rust_mcp_sdk::{
5 mcp_server::ServerHandler,
6 schema::{
7 CallToolError, CallToolRequestParams, CallToolResult, GetPromptRequestParams,
8 GetPromptResult, ListPromptsResult, ListResourcesResult, ListToolsResult,
9 PaginatedRequestParams, ReadResourceRequestParams, ReadResourceResult, RpcError,
10 },
11 McpServer,
12};
13use std::sync::Arc;
14use tracing::{info_span, Instrument};
15use uuid::Uuid;
16
17use super::config::HandlerConfig;
18use super::core::HandlerCore;
19use crate::metrics::ServerMetrics;
20use crate::server::CratesDocsServer;
21
22pub struct CratesDocsHandler {
31 core: HandlerCore,
32}
33
34impl CratesDocsHandler {
35 #[must_use]
53 pub fn new(server: Arc<CratesDocsServer>) -> Self {
54 Self {
55 core: HandlerCore::new(server),
56 }
57 }
58
59 #[must_use]
61 pub fn with_config(server: Arc<CratesDocsServer>, config: HandlerConfig) -> Self {
62 Self {
63 core: HandlerCore::with_config(server, config),
64 }
65 }
66
67 #[must_use]
69 pub fn with_merged_config(
70 server: Arc<CratesDocsServer>,
71 base_config: HandlerConfig,
72 override_config: Option<HandlerConfig>,
73 ) -> Self {
74 Self {
75 core: HandlerCore::with_merged_config(server, base_config, override_config),
76 }
77 }
78
79 #[must_use]
81 pub fn with_metrics(self, metrics: Arc<ServerMetrics>) -> Self {
82 Self {
83 core: self.core.with_metrics(metrics),
84 }
85 }
86
87 #[must_use]
89 pub fn core(&self) -> &HandlerCore {
90 &self.core
91 }
92
93 #[must_use]
95 pub fn server(&self) -> &Arc<CratesDocsServer> {
96 self.core.server()
97 }
98}
99
100#[async_trait]
101impl ServerHandler for CratesDocsHandler {
102 async fn handle_list_tools_request(
104 &self,
105 _request: Option<PaginatedRequestParams>,
106 _runtime: Arc<dyn McpServer>,
107 ) -> std::result::Result<ListToolsResult, RpcError> {
108 let trace_id = Uuid::new_v4().to_string();
109 let span = info_span!("list_tools", trace_id = %trace_id);
110
111 async {
112 tracing::debug!("Listing available tools");
113 let result = self.core.list_tools();
114 tracing::debug!("Found {} tools", result.tools.len());
115 Ok(result)
116 }
117 .instrument(span)
118 .await
119 }
120
121 async fn handle_call_tool_request(
123 &self,
124 params: CallToolRequestParams,
125 _runtime: Arc<dyn McpServer>,
126 ) -> std::result::Result<CallToolResult, CallToolError> {
127 self.core.execute_tool(params).await.into_call_tool_result()
128 }
129
130 async fn handle_list_resources_request(
132 &self,
133 _request: Option<PaginatedRequestParams>,
134 _runtime: Arc<dyn McpServer>,
135 ) -> std::result::Result<ListResourcesResult, RpcError> {
136 Ok(self.core.list_resources())
137 }
138
139 async fn handle_read_resource_request(
141 &self,
142 _params: ReadResourceRequestParams,
143 _runtime: Arc<dyn McpServer>,
144 ) -> std::result::Result<ReadResourceResult, RpcError> {
145 Err(RpcError::invalid_request().with_message("Resource not found".to_string()))
146 }
147
148 async fn handle_list_prompts_request(
150 &self,
151 _request: Option<PaginatedRequestParams>,
152 _runtime: Arc<dyn McpServer>,
153 ) -> std::result::Result<ListPromptsResult, RpcError> {
154 Ok(self.core.list_prompts())
155 }
156
157 async fn handle_get_prompt_request(
159 &self,
160 _params: GetPromptRequestParams,
161 _runtime: Arc<dyn McpServer>,
162 ) -> std::result::Result<GetPromptResult, RpcError> {
163 Err(RpcError::invalid_request().with_message("Prompt not found".to_string()))
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::AppConfig;
171
172 #[tokio::test]
173 async fn test_crates_docs_handler_delegation() {
174 let server = Arc::new(CratesDocsServer::new(AppConfig::default()).unwrap());
175 let handler = CratesDocsHandler::new(server);
176
177 let result = handler
178 .core()
179 .execute_tool(rust_mcp_sdk::schema::CallToolRequestParams {
180 arguments: Some(serde_json::Map::new()),
181 meta: None,
182 name: "health_check".to_string(),
183 task: None,
184 })
185 .await;
186
187 assert!(result.success);
188 }
189
190 #[tokio::test]
191 async fn test_handler_with_merged_config() {
192 let server = Arc::new(CratesDocsServer::new(AppConfig::default()).unwrap());
193 let base_config = HandlerConfig::default();
194 let override_config = HandlerConfig::new().with_verbose_logging();
195
196 let handler =
197 CratesDocsHandler::with_merged_config(server, base_config, Some(override_config));
198
199 assert!(handler.core().config().verbose_logging);
200 assert!(!handler.core().config().enable_metrics);
201 }
202}