use crate::McpError;
use crate::jsonrpc::{JsonRpcError, JsonRpcRequest, JsonRpcResponse, JsonRpcResponsePayload};
use crate::types::RequestId;
use turbomcp_core::rkyv_types::{
InternalError, InternalId, InternalMessage, InternalResponse, RoutingHints,
};
pub fn to_internal(request: &JsonRpcRequest) -> Result<InternalMessage, McpError> {
let id = match &request.id {
RequestId::Number(n) => InternalId::Number(*n),
RequestId::String(s) => InternalId::String(s.clone()),
RequestId::Uuid(u) => InternalId::String(u.to_string()),
};
let params_raw = if let Some(ref params) = request.params {
serde_json::to_vec(params)
.map_err(|e| McpError::serialization(format!("Failed to serialize params: {e}")))?
} else {
Vec::new()
};
Ok(InternalMessage::new()
.with_id(id)
.with_method(&request.method)
.with_params_raw(params_raw))
}
pub fn to_internal_with_hints(
request: &JsonRpcRequest,
) -> Result<(InternalMessage, RoutingHints), McpError> {
let msg = to_internal(request)?;
let hints = extract_routing_hints(&request.method, request.params.as_ref())?;
Ok((msg, hints))
}
fn extract_routing_hints(
method: &str,
params: Option<&serde_json::Value>,
) -> Result<RoutingHints, McpError> {
let mut hints = RoutingHints::new();
if let Some(params) = params {
match method {
"tools/call" => {
if let Some(name) = params.get("name").and_then(|v| v.as_str()) {
hints = hints.with_tool_name(name);
}
}
"resources/read" => {
if let Some(uri) = params.get("uri").and_then(|v| v.as_str()) {
hints = hints.with_resource_uri(uri);
}
}
"prompts/get" => {
if let Some(name) = params.get("name").and_then(|v| v.as_str()) {
hints = hints.with_prompt_name(name);
}
}
_ => {}
}
}
Ok(hints)
}
pub fn from_internal_response(response: &InternalResponse) -> Result<JsonRpcResponse, McpError> {
let id = match &response.id {
InternalId::Number(n) => RequestId::Number(*n),
InternalId::String(s) => RequestId::String(s.clone()),
};
let payload = if let Some(ref result_raw) = response.result_raw {
let result: serde_json::Value = serde_json::from_slice(result_raw)
.map_err(|e| McpError::serialization(format!("Failed to parse result: {e}")))?;
JsonRpcResponsePayload::Success { result }
} else if let Some(ref error) = response.error {
JsonRpcResponsePayload::Error {
error: JsonRpcError {
code: error.code,
message: error.message.clone(),
data: error
.data_raw
.as_ref()
.and_then(|raw| serde_json::from_slice(raw).ok()),
},
}
} else {
JsonRpcResponsePayload::Success {
result: serde_json::Value::Null,
}
};
Ok(JsonRpcResponse {
jsonrpc: crate::jsonrpc::JsonRpcVersion,
payload,
id: crate::jsonrpc::ResponseId::from_request(id),
})
}
pub fn from_internal_message(msg: &InternalMessage) -> Result<JsonRpcRequest, McpError> {
let id = match &msg.id {
Some(InternalId::Number(n)) => RequestId::Number(*n),
Some(InternalId::String(s)) => RequestId::String(s.clone()),
None => {
return Err(McpError::invalid_params(
"Cannot convert notification to request",
));
}
};
let params = if msg.params_raw.is_empty() {
None
} else {
Some(
serde_json::from_slice(&msg.params_raw)
.map_err(|e| McpError::serialization(format!("Failed to parse params: {e}")))?,
)
};
Ok(JsonRpcRequest {
jsonrpc: crate::jsonrpc::JsonRpcVersion,
method: msg.method.clone(),
params,
id,
})
}
pub fn success_response(
id: InternalId,
result: &serde_json::Value,
) -> Result<InternalResponse, McpError> {
let result_raw = serde_json::to_vec(result)
.map_err(|e| McpError::serialization(format!("Failed to serialize result: {e}")))?;
Ok(InternalResponse::success(id, result_raw))
}
pub fn error_response(id: InternalId, error: &McpError) -> InternalResponse {
let internal_error = InternalError::new(error.jsonrpc_code(), error.to_string());
InternalResponse::error(id, internal_error)
}
pub trait ToInternal {
type Internal;
fn to_internal(&self) -> Result<Self::Internal, McpError>;
}
impl ToInternal for JsonRpcRequest {
type Internal = InternalMessage;
fn to_internal(&self) -> Result<Self::Internal, McpError> {
to_internal(self)
}
}
pub trait FromInternal: Sized {
type Internal;
fn from_internal(internal: &Self::Internal) -> Result<Self, McpError>;
}
impl FromInternal for JsonRpcResponse {
type Internal = InternalResponse;
fn from_internal(internal: &Self::Internal) -> Result<Self, McpError> {
from_internal_response(internal)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jsonrpc::JsonRpcVersion;
use rancor_crate;
use rkyv_crate;
#[test]
fn test_to_internal_simple() {
let request = JsonRpcRequest {
jsonrpc: JsonRpcVersion,
method: "tools/list".to_string(),
params: None,
id: RequestId::Number(1),
};
let internal = to_internal(&request).unwrap();
assert_eq!(internal.method, "tools/list");
assert!(internal.is_request());
assert!(internal.params_raw.is_empty());
}
#[test]
fn test_to_internal_with_params() {
let request = JsonRpcRequest {
jsonrpc: JsonRpcVersion,
method: "tools/call".to_string(),
params: Some(serde_json::json!({"name": "calculator", "args": {"a": 1, "b": 2}})),
id: RequestId::String("req-123".to_string()),
};
let internal = to_internal(&request).unwrap();
assert_eq!(internal.method, "tools/call");
assert!(!internal.params_raw.is_empty());
let params: serde_json::Value = serde_json::from_slice(&internal.params_raw).unwrap();
assert_eq!(params["name"], "calculator");
}
#[test]
fn test_to_internal_with_hints() {
let request = JsonRpcRequest {
jsonrpc: JsonRpcVersion,
method: "tools/call".to_string(),
params: Some(serde_json::json!({"name": "hello_world"})),
id: RequestId::Number(42),
};
let (msg, hints) = to_internal_with_hints(&request).unwrap();
assert_eq!(msg.method, "tools/call");
assert_eq!(hints.tool_name, Some("hello_world".to_string()));
}
#[test]
fn test_from_internal_response_success() {
let response =
InternalResponse::success(InternalId::Number(1), br#"{"content":"Hello!"}"#.to_vec());
let json_rpc = from_internal_response(&response).unwrap();
match json_rpc.payload {
JsonRpcResponsePayload::Success { result } => {
assert_eq!(result["content"], "Hello!");
}
_ => panic!("Expected success response"),
}
}
#[test]
fn test_from_internal_response_error() {
let error = InternalError::new(InternalError::METHOD_NOT_FOUND, "Method not found");
let response = InternalResponse::error(InternalId::Number(1), error);
let json_rpc = from_internal_response(&response).unwrap();
match json_rpc.payload {
JsonRpcResponsePayload::Error { error } => {
assert_eq!(error.code, -32601); assert_eq!(error.message, "Method not found");
}
_ => panic!("Expected error response"),
}
}
#[test]
fn test_roundtrip() {
let original = JsonRpcRequest {
jsonrpc: JsonRpcVersion,
method: "prompts/get".to_string(),
params: Some(serde_json::json!({"name": "greeting", "arguments": {"user": "Alice"}})),
id: RequestId::Number(99),
};
let internal = to_internal(&original).unwrap();
let restored = from_internal_message(&internal).unwrap();
assert_eq!(original.method, restored.method);
assert_eq!(original.id, restored.id);
assert_eq!(original.params, restored.params);
}
#[test]
fn test_rkyv_serialization() {
let request = JsonRpcRequest {
jsonrpc: JsonRpcVersion,
method: "resources/read".to_string(),
params: Some(serde_json::json!({"uri": "file:///test.txt"})),
id: RequestId::Number(1),
};
let internal = to_internal(&request).unwrap();
let bytes = rkyv_crate::to_bytes::<rancor_crate::Error>(&internal).expect("serialize");
let archived = rkyv_crate::access::<
turbomcp_core::rkyv_types::ArchivedInternalMessage,
rancor_crate::Error,
>(&bytes)
.expect("access");
assert_eq!(archived.method_str(), "resources/read");
assert!(archived.is_request());
}
#[test]
fn test_success_response_helper() {
let result = serde_json::json!({"tools": [{"name": "test"}]});
let response = success_response(InternalId::Number(1), &result).unwrap();
assert!(response.is_success());
let restored: serde_json::Value =
serde_json::from_slice(&response.result_raw.unwrap()).unwrap();
assert_eq!(restored, result);
}
#[test]
fn test_error_response_helper() {
let error = McpError::method_not_found("unknown_method");
let response = error_response(InternalId::Number(1), &error);
assert!(response.is_error());
assert_eq!(response.error.as_ref().unwrap().code, -32601);
}
}