feat: migrate JsonRpcMessage/Request/Response/Error/Notification from internal mcp crates to rmcp versions (#3564)

This commit is contained in:
Alex Hancock
2025-07-22 13:45:00 -04:00
committed by GitHub
parent 246ba19256
commit 3e5c9b8505
27 changed files with 256 additions and 246 deletions
+27 -34
View File
@@ -4,8 +4,10 @@ use std::{
};
use futures::{Future, Stream};
use mcp_core::protocol::{JsonRpcError, JsonRpcMessage, JsonRpcResponse};
use pin_project::pin_project;
use rmcp::model::{
ErrorData, JsonRpcError, JsonRpcMessage, JsonRpcResponse, JsonRpcVersion2_0, RequestId,
};
use router::McpRequest;
use tokio::{
io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader},
@@ -151,14 +153,11 @@ where
Ok(msg) => {
match msg {
JsonRpcMessage::Request(request) => {
// Serialize request for logging
let id = request.id;
let request_json = serde_json::to_string(&request)
.unwrap_or_else(|_| "Failed to serialize request".to_string());
tracing::info!(
request_id = ?id,
method = ?request.method,
method = ?request.request.method,
json = %request_json,
"Received request"
);
@@ -184,16 +183,11 @@ where
Err(e) => {
let error_msg = e.into().to_string();
tracing::error!(error = %error_msg, "Request processing failed");
JsonRpcResponse {
jsonrpc: "2.0".to_string(),
id,
result: None,
error: Some(mcp_core::protocol::ErrorData {
code: mcp_core::protocol::INTERNAL_ERROR,
message: error_msg,
data: None,
}),
}
// Return an error response instead of a regular response
return Err(ServerError::Transport(TransportError::Protocol(
error_msg,
)));
}
};
@@ -226,39 +220,38 @@ where
}
JsonRpcMessage::Response(_)
| JsonRpcMessage::Notification(_)
| JsonRpcMessage::Nil
| JsonRpcMessage::BatchRequest(_)
| JsonRpcMessage::BatchResponse(_)
| JsonRpcMessage::Error(_) => {
// Ignore responses, notifications and nil messages for now
// Ignore responses, notifications, batch messages and error messages for now
continue;
}
}
}
Err(e) => {
// Convert transport error to JSON-RPC error response
let error = match e {
TransportError::Json(_) | TransportError::InvalidMessage(_) => {
mcp_core::protocol::ErrorData {
code: mcp_core::protocol::PARSE_ERROR,
message: e.to_string(),
data: None,
}
}
TransportError::Protocol(_) => mcp_core::protocol::ErrorData {
code: mcp_core::protocol::INVALID_REQUEST,
message: e.to_string(),
let error_data = match e {
TransportError::Json(_) | TransportError::InvalidMessage(_) => ErrorData {
code: rmcp::model::ErrorCode::PARSE_ERROR,
message: e.to_string().into(),
data: None,
},
_ => mcp_core::protocol::ErrorData {
code: mcp_core::protocol::INTERNAL_ERROR,
message: e.to_string(),
TransportError::Protocol(_) => ErrorData {
code: rmcp::model::ErrorCode::INVALID_REQUEST,
message: e.to_string().into(),
data: None,
},
_ => ErrorData {
code: rmcp::model::ErrorCode::INTERNAL_ERROR,
message: e.to_string().into(),
data: None,
},
};
let error_response = JsonRpcMessage::Error(JsonRpcError {
jsonrpc: "2.0".to_string(),
id: None,
error,
jsonrpc: JsonRpcVersion2_0,
id: RequestId::Number(0), // Use a default ID for transport errors
error: error_data,
});
if let Err(e) = transport.write_message(error_response).await {
+1 -2
View File
@@ -1,11 +1,10 @@
use anyhow::Result;
use mcp_core::handler::{PromptError, ResourceError};
use mcp_core::protocol::JsonRpcMessage;
use mcp_core::tool::ToolAnnotations;
use mcp_core::{handler::ToolError, protocol::ServerCapabilities, tool::Tool};
use mcp_server::router::{CapabilitiesBuilder, RouterService};
use mcp_server::{ByteTransport, Router, Server};
use rmcp::model::{Content, Prompt, PromptArgument, RawResource, Resource};
use rmcp::model::{Content, JsonRpcMessage, Prompt, PromptArgument, RawResource, Resource};
use serde_json::Value;
use std::{future::Future, pin::Pin, sync::Arc};
use tokio::sync::mpsc;
+46 -60
View File
@@ -5,17 +5,18 @@ use std::{
};
type PromptFuture = Pin<Box<dyn Future<Output = Result<String, PromptError>> + Send + 'static>>;
use mcp_core::{
handler::{PromptError, ResourceError, ToolError},
protocol::{
CallToolResult, GetPromptResult, Implementation, InitializeResult, JsonRpcMessage,
JsonRpcRequest, JsonRpcResponse, ListPromptsResult, ListResourcesResult, ListToolsResult,
PromptsCapability, ReadResourceResult, ResourcesCapability, ServerCapabilities,
ToolsCapability,
CallToolResult, GetPromptResult, Implementation, InitializeResult, ListPromptsResult,
ListResourcesResult, ListToolsResult, PromptsCapability, ReadResourceResult,
ResourcesCapability, ServerCapabilities, ToolsCapability,
},
};
use rmcp::model::{Content, Prompt, PromptMessage, PromptMessageRole, Resource, ResourceContents};
use rmcp::model::{
Content, JsonRpcMessage, JsonRpcRequest, JsonRpcResponse, JsonRpcVersion2_0, Prompt,
PromptMessage, PromptMessageRole, RequestId, Resource, ResourceContents,
};
use serde_json::Value;
use tokio::sync::mpsc;
use tower_service::Service;
@@ -101,15 +102,32 @@ pub trait Router: Send + Sync + 'static {
fn get_prompt(&self, prompt_name: &str) -> PromptFuture;
// Helper method to create base response
fn create_response(&self, id: Option<u64>) -> JsonRpcResponse {
fn create_response(&self, id: RequestId) -> JsonRpcResponse {
JsonRpcResponse {
jsonrpc: "2.0".to_string(),
jsonrpc: JsonRpcVersion2_0,
id,
result: None,
error: None,
result: serde_json::Map::new(),
}
}
// Helper method to set result on response
fn set_result<T: serde::Serialize>(
&self,
response: &mut JsonRpcResponse,
result: T,
) -> Result<(), RouterError> {
let value = serde_json::to_value(result)
.map_err(|e| RouterError::Internal(format!("JSON serialization error: {}", e)))?;
if let Some(obj) = value.as_object() {
response.result = obj.clone();
} else {
return Err(RouterError::Internal("Result must be a JSON object".into()));
}
Ok(())
}
fn handle_initialize(
&self,
req: JsonRpcRequest,
@@ -126,11 +144,7 @@ pub trait Router: Send + Sync + 'static {
};
let mut response = self.create_response(req.id);
response.result =
Some(serde_json::to_value(result).map_err(|e| {
RouterError::Internal(format!("JSON serialization error: {}", e))
})?);
self.set_result(&mut response, result)?;
Ok(response)
}
}
@@ -147,11 +161,7 @@ pub trait Router: Send + Sync + 'static {
next_cursor: None,
};
let mut response = self.create_response(req.id);
response.result =
Some(serde_json::to_value(result).map_err(|e| {
RouterError::Internal(format!("JSON serialization error: {}", e))
})?);
self.set_result(&mut response, result)?;
Ok(response)
}
}
@@ -162,9 +172,7 @@ pub trait Router: Send + Sync + 'static {
notifier: mpsc::Sender<JsonRpcMessage>,
) -> impl Future<Output = Result<JsonRpcResponse, RouterError>> + Send {
async move {
let params = req
.params
.ok_or_else(|| RouterError::InvalidParams("Missing parameters".into()))?;
let params = &req.request.params;
let name = params
.get("name")
@@ -185,11 +193,7 @@ pub trait Router: Send + Sync + 'static {
};
let mut response = self.create_response(req.id);
response.result =
Some(serde_json::to_value(result).map_err(|e| {
RouterError::Internal(format!("JSON serialization error: {}", e))
})?);
self.set_result(&mut response, result)?;
Ok(response)
}
}
@@ -206,11 +210,7 @@ pub trait Router: Send + Sync + 'static {
next_cursor: None,
};
let mut response = self.create_response(req.id);
response.result =
Some(serde_json::to_value(result).map_err(|e| {
RouterError::Internal(format!("JSON serialization error: {}", e))
})?);
self.set_result(&mut response, result)?;
Ok(response)
}
}
@@ -220,9 +220,7 @@ pub trait Router: Send + Sync + 'static {
req: JsonRpcRequest,
) -> impl Future<Output = Result<JsonRpcResponse, RouterError>> + Send {
async move {
let params = req
.params
.ok_or_else(|| RouterError::InvalidParams("Missing parameters".into()))?;
let params = &req.request.params;
let uri = params
.get("uri")
@@ -240,11 +238,7 @@ pub trait Router: Send + Sync + 'static {
};
let mut response = self.create_response(req.id);
response.result =
Some(serde_json::to_value(result).map_err(|e| {
RouterError::Internal(format!("JSON serialization error: {}", e))
})?);
self.set_result(&mut response, result)?;
Ok(response)
}
}
@@ -259,11 +253,7 @@ pub trait Router: Send + Sync + 'static {
let result = ListPromptsResult { prompts };
let mut response = self.create_response(req.id);
response.result =
Some(serde_json::to_value(result).map_err(|e| {
RouterError::Internal(format!("JSON serialization error: {}", e))
})?);
self.set_result(&mut response, result)?;
Ok(response)
}
}
@@ -274,9 +264,7 @@ pub trait Router: Send + Sync + 'static {
) -> impl Future<Output = Result<JsonRpcResponse, RouterError>> + Send {
async move {
// Validate and extract parameters
let params = req
.params
.ok_or_else(|| RouterError::InvalidParams("Missing parameters".into()))?;
let params = &req.request.params;
// Extract "name" field
let prompt_name = params
@@ -381,13 +369,11 @@ pub trait Router: Send + Sync + 'static {
// Build the final response
let mut response = self.create_response(req.id);
response.result = Some(
serde_json::to_value(GetPromptResult {
description: Some(description_filled),
messages,
})
.map_err(|e| RouterError::Internal(format!("JSON serialization error: {}", e)))?,
);
let result = GetPromptResult {
description: Some(description_filled),
messages,
};
self.set_result(&mut response, result)?;
Ok(response)
}
}
@@ -416,7 +402,7 @@ where
let this = self.0.clone();
Box::pin(async move {
let result = match req.request.method.as_str() {
let result = match req.request.request.method.as_str() {
"initialize" => this.handle_initialize(req.request).await,
"tools/list" => this.handle_tools_list(req.request).await,
"tools/call" => this.handle_tools_call(req.request, req.notifier).await,
@@ -425,9 +411,9 @@ where
"prompts/list" => this.handle_prompts_list(req.request).await,
"prompts/get" => this.handle_prompts_get(req.request).await,
_ => {
let mut response = this.create_response(req.request.id);
response.error = Some(RouterError::MethodNotFound(req.request.method).into());
Ok(response)
return Err(
RouterError::MethodNotFound(req.request.request.method.clone()).into(),
);
}
};