Skip to main content

mcp_context_server/handlers/
mod.rs

1pub mod health;
2pub mod inspect_cache;
3pub mod list_caches;
4pub mod resolve_context;
5
6use crate::config::ServerConfig;
7use crate::protocol::{
8    InspectCacheParams, JsonRpcError, JsonRpcRequest, JsonRpcResponse, ResolveContextParams,
9    ToolCallParams, ToolResult,
10};
11
12/// Dispatch a JSON-RPC request to the appropriate handler.
13///
14/// Returns `None` for notifications (no response required).
15pub async fn dispatch(
16    req: &JsonRpcRequest,
17    config: &ServerConfig,
18) -> Option<JsonRpcResponse> {
19    match req.method.as_str() {
20        "initialize" => {
21            let result = serde_json::json!({
22                "protocolVersion": "2024-11-05",
23                "capabilities": {
24                    "tools": {}
25                },
26                "serverInfo": {
27                    "name": "mcp-context-server",
28                    "version": env!("CARGO_PKG_VERSION")
29                }
30            });
31            Some(JsonRpcResponse::success(req.id.clone(), result))
32        }
33
34        "notifications/initialized" => None,
35
36        "ping" => Some(JsonRpcResponse::success(req.id.clone(), serde_json::json!({}))),
37
38        "tools/list" => {
39            let result = serde_json::json!({
40                "tools": [
41                    {
42                        "name": "context.resolve",
43                        "description": "Resolve context from a cache using a query and token budget",
44                        "inputSchema": {
45                            "type": "object",
46                            "required": ["cache", "query", "budget"],
47                            "properties": {
48                                "cache": {
49                                    "type": "string",
50                                    "description": "Cache directory name (relative to CONTEXT_CACHE_ROOT)"
51                                },
52                                "query": {
53                                    "type": "string",
54                                    "description": "Search query for context selection"
55                                },
56                                "budget": {
57                                    "type": "integer",
58                                    "description": "Maximum token budget for selected context",
59                                    "minimum": 0
60                                }
61                            }
62                        }
63                    },
64                    {
65                        "name": "context.list_caches",
66                        "description": "List available context caches under the server's cache root",
67                        "inputSchema": {
68                            "type": "object",
69                            "properties": {}
70                        }
71                    },
72                    {
73                        "name": "context.inspect_cache",
74                        "description": "Inspect cache structure, metadata, and validity",
75                        "inputSchema": {
76                            "type": "object",
77                            "required": ["cache"],
78                            "properties": {
79                                "cache": {
80                                    "type": "string",
81                                    "description": "Cache directory name (relative to CONTEXT_CACHE_ROOT)"
82                                }
83                            }
84                        }
85                    }
86                ]
87            });
88            Some(JsonRpcResponse::success(req.id.clone(), result))
89        }
90
91        "tools/call" => {
92            let params: ToolCallParams = match &req.params {
93                Some(v) => match serde_json::from_value(v.clone()) {
94                    Ok(p) => p,
95                    Err(e) => {
96                        return Some(JsonRpcResponse::error(
97                            req.id.clone(),
98                            JsonRpcError::invalid_params(format!(
99                                "Invalid tools/call params: {e}"
100                            )),
101                        ));
102                    }
103                },
104                None => {
105                    return Some(JsonRpcResponse::error(
106                        req.id.clone(),
107                        JsonRpcError::invalid_params("Missing params for tools/call"),
108                    ));
109                }
110            };
111
112            let tool_result = dispatch_tool_call(&params, config).await;
113            let result_json = serde_json::to_value(&tool_result).expect("ToolResult must serialize to JSON Value");
114            Some(JsonRpcResponse::success(req.id.clone(), result_json))
115        }
116
117        _ => Some(JsonRpcResponse::error(
118            req.id.clone(),
119            JsonRpcError::method_not_found(&req.method),
120        )),
121    }
122}
123
124async fn dispatch_tool_call(params: &ToolCallParams, config: &ServerConfig) -> ToolResult {
125    match params.name.as_str() {
126        "context.resolve" => {
127            let resolve_params: ResolveContextParams = match &params.arguments {
128                Some(v) => match serde_json::from_value(v.clone()) {
129                    Ok(p) => p,
130                    Err(e) => {
131                        return ToolResult::error(format!(
132                            "Invalid arguments for context.resolve: {e}"
133                        ));
134                    }
135                },
136                None => {
137                    return ToolResult::error(
138                        "Missing arguments for context.resolve",
139                    );
140                }
141            };
142            resolve_context::handle(resolve_params, config).await
143        }
144
145        "context.list_caches" => list_caches::handle(config).await,
146
147        "context.inspect_cache" => {
148            let inspect_params: InspectCacheParams = match &params.arguments {
149                Some(v) => match serde_json::from_value(v.clone()) {
150                    Ok(p) => p,
151                    Err(e) => {
152                        return ToolResult::error(format!(
153                            "Invalid arguments for context.inspect_cache: {e}"
154                        ));
155                    }
156                },
157                None => {
158                    return ToolResult::error(
159                        "Missing arguments for context.inspect_cache",
160                    );
161                }
162            };
163            inspect_cache::handle(inspect_params, config).await
164        }
165
166        "health" => health::handle().await,
167
168        _ => ToolResult::error(format!("Unknown tool: {}", params.name)),
169    }
170}