use super::types::*;
use crate::jsonrpc::{JsonRpcError, JsonRpcRequest, JsonRpcResponse};
use serde_json::json;
use tracing::{error, info};
pub async fn handle_initialize(
request: JsonRpcRequest,
oauth_config: &OAuthConfig,
) -> Option<JsonRpcResponse> {
request.id.as_ref()?;
let requested_version = request
.params
.as_ref()
.and_then(|params| params.get("protocolVersion").and_then(|v| v.as_str()));
let protocol_version = match requested_version {
Some(version) => {
if SUPPORTED_VERSIONS.contains(&version) {
version.to_string()
} else {
info!(
"Client requested unsupported protocol version '{}', using latest '{}'",
version, SUPPORTED_VERSIONS[0]
);
SUPPORTED_VERSIONS[0].to_string()
}
}
None => {
SUPPORTED_VERSIONS[0].to_string()
}
};
info!("Negotiated protocol version: {}", protocol_version);
let mut capabilities = json!({
"tools": {
"listChanged": false
},
"completions": {},
"elicitation": {},
"tasks": {
"list": {},
"create": {},
"update": {}
}
});
if oauth_config.enabled {
capabilities["authorization"] = json!({
"enabled": true,
"issuer": oauth_config.issuer.clone().unwrap_or_default(),
"audience": oauth_config.audience.clone().unwrap_or_default(),
"scopes": oauth_config.scopes
});
}
let result = InitializeResult {
protocol_version,
capabilities,
server_info: json!({
"name": "do-memory-mcp-server",
"version": env!("CARGO_PKG_VERSION")
}),
};
match serde_json::to_value(result) {
Ok(value) => Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(value),
error: None,
}),
Err(e) => {
error!("Failed to serialize initialize response: {}", e);
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32603,
message: "Internal error".to_string(),
data: Some(json!({"details": format!("Response serialization failed: {}", e)})),
}),
})
}
}
}
pub async fn handle_list_tools(
request: JsonRpcRequest,
tools: Vec<McpTool>,
) -> Option<JsonRpcResponse> {
request.id.as_ref()?;
info!("Handling tools/list request");
let result = ListToolsResult { tools };
match serde_json::to_value(result) {
Ok(value) => Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(value),
error: None,
}),
Err(e) => {
error!("Failed to serialize list_tools response: {}", e);
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32603,
message: "Internal error".to_string(),
data: Some(json!({"details": format!("Response serialization failed: {}", e)})),
}),
})
}
}
}
pub async fn handle_shutdown(request: JsonRpcRequest) -> Option<JsonRpcResponse> {
request.id.as_ref()?;
info!("Handling shutdown request");
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(json!(null)),
error: None,
})
}
pub fn handle_list_tools_with_lazy(
request: JsonRpcRequest,
tools: Vec<crate::types::Tool>,
) -> Option<JsonRpcResponse> {
request.id.as_ref()?;
info!("Handling tools/list request");
let lazy = request
.params
.as_ref()
.and_then(|p| p.get("lazy"))
.and_then(|v| v.as_bool())
.unwrap_or(false);
if lazy {
let tool_stubs: Vec<ToolStub> = tools
.into_iter()
.map(|tool| ToolStub {
name: tool.name,
title: None,
description: tool.description,
})
.collect();
let result = ListToolStubsResult { tools: tool_stubs };
match serde_json::to_value(result) {
Ok(value) => Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(value),
error: None,
}),
Err(e) => {
error!("Failed to serialize list_tools response: {}", e);
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32603,
message: "Internal error".to_string(),
data: Some(
json!({"details": format!("Response serialization failed: {}", e)}),
),
}),
})
}
}
} else {
let mcp_tools: Vec<McpTool> = tools
.into_iter()
.map(|tool| McpTool {
name: tool.name,
title: None,
description: tool.description,
input_schema: tool.input_schema,
})
.collect();
let result = ListToolsResult { tools: mcp_tools };
match serde_json::to_value(result) {
Ok(value) => Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(value),
error: None,
}),
Err(e) => {
error!("Failed to serialize list_tools response: {}", e);
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32603,
message: "Internal error".to_string(),
data: Some(
json!({"details": format!("Response serialization failed: {}", e)}),
),
}),
})
}
}
}
}
pub fn handle_describe_tool<F>(request: JsonRpcRequest, get_tool: F) -> Option<JsonRpcResponse>
where
F: FnOnce(&str) -> Option<crate::types::Tool>,
{
request.id.as_ref()?;
info!("Handling tools/describe request");
let tool_name = request
.params
.as_ref()
.and_then(|p| p.get("name"))
.and_then(|v| v.as_str());
let tool_name = match tool_name {
Some(name) => name,
None => {
return Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32602,
message: "Invalid params".to_string(),
data: Some(json!({"details": "Missing required parameter: name"})),
}),
});
}
};
let tool = get_tool(tool_name);
match tool {
Some(tool) => {
let mcp_tool = McpTool {
name: tool.name,
title: None,
description: tool.description,
input_schema: tool.input_schema,
};
let result = DescribeToolResult { tool: mcp_tool };
match serde_json::to_value(result) {
Ok(value) => Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(value),
error: None,
}),
Err(e) => {
error!("Failed to serialize describe_tool response: {}", e);
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32603,
message: "Internal error".to_string(),
data: Some(
json!({"details": format!("Response serialization failed: {}", e)}),
),
}),
})
}
}
}
None => {
info!("Tool not found: {}", tool_name);
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32602,
message: "Tool not found".to_string(),
data: Some(json!({"tool_name": tool_name})),
}),
})
}
}
}
pub fn handle_describe_tools<F>(request: JsonRpcRequest, get_tool: F) -> Option<JsonRpcResponse>
where
F: Fn(&str) -> Option<crate::types::Tool>,
{
request.id.as_ref()?;
info!("Handling tools/describe_batch request");
let tool_names = request
.params
.as_ref()
.and_then(|p| p.get("names"))
.and_then(|v| v.as_array());
let tool_names = match tool_names {
Some(names) => names
.iter()
.filter_map(|v| v.as_str())
.map(String::from)
.collect::<Vec<_>>(),
None => {
return Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32602,
message: "Invalid params".to_string(),
data: Some(json!({"details": "Missing required parameter: names (array)"})),
}),
});
}
};
let mut mcp_tools = Vec::new();
for tool_name in &tool_names {
if let Some(tool) = get_tool(tool_name) {
mcp_tools.push(McpTool {
name: tool.name,
title: None,
description: tool.description,
input_schema: tool.input_schema,
});
}
}
let result = DescribeToolsResult { tools: mcp_tools };
match serde_json::to_value(result) {
Ok(value) => Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: Some(value),
error: None,
}),
Err(e) => {
error!("Failed to serialize describe_tools response: {}", e);
Some(JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id: request.id,
result: None,
error: Some(JsonRpcError {
code: -32603,
message: "Internal error".to_string(),
data: Some(json!({"details": format!("Response serialization failed: {}", e)})),
}),
})
}
}
}