Skip to main content

mcp_context_server/protocol/
response.rs

1use serde::{Deserialize, Serialize};
2
3use super::request::RpcId;
4
5// ---------------------------------------------------------------------------
6// JSON-RPC 2.0 response layer
7// ---------------------------------------------------------------------------
8
9/// JSON-RPC 2.0 response envelope.
10#[derive(Debug, Clone, Serialize)]
11pub struct JsonRpcResponse {
12    pub jsonrpc: String,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub id: Option<RpcId>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub result: Option<serde_json::Value>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub error: Option<JsonRpcError>,
19}
20
21impl JsonRpcResponse {
22    pub fn success(id: Option<RpcId>, result: serde_json::Value) -> Self {
23        Self {
24            jsonrpc: "2.0".into(),
25            id,
26            result: Some(result),
27            error: None,
28        }
29    }
30
31    pub fn error(id: Option<RpcId>, error: JsonRpcError) -> Self {
32        Self {
33            jsonrpc: "2.0".into(),
34            id,
35            result: None,
36            error: Some(error),
37        }
38    }
39}
40
41/// JSON-RPC 2.0 error object (protocol-level errors).
42#[derive(Debug, Clone, Serialize)]
43pub struct JsonRpcError {
44    pub code: i32,
45    pub message: String,
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub data: Option<serde_json::Value>,
48}
49
50impl JsonRpcError {
51    pub fn parse_error() -> Self {
52        Self { code: -32700, message: "Parse error".into(), data: None }
53    }
54
55    pub fn invalid_request() -> Self {
56        Self { code: -32600, message: "Invalid Request".into(), data: None }
57    }
58
59    pub fn invalid_request_with(detail: impl Into<String>) -> Self {
60        Self { code: -32600, message: detail.into(), data: None }
61    }
62
63    pub fn method_not_found(method: &str) -> Self {
64        Self {
65            code: -32601,
66            message: format!("Method not found: {method}"),
67            data: None,
68        }
69    }
70
71    pub fn invalid_params(detail: impl Into<String>) -> Self {
72        Self { code: -32602, message: detail.into(), data: None }
73    }
74
75    pub fn internal_error(detail: impl Into<String>) -> Self {
76        Self { code: -32603, message: detail.into(), data: None }
77    }
78}
79
80// ---------------------------------------------------------------------------
81// MCP tool result layer (returned inside a *successful* JSON-RPC response)
82// ---------------------------------------------------------------------------
83
84/// MCP tool call result wrapper.
85#[derive(Debug, Clone, Serialize)]
86pub struct ToolResult {
87    pub content: Vec<ToolResultContent>,
88    #[serde(rename = "isError", skip_serializing_if = "std::ops::Not::not")]
89    pub is_error: bool,
90}
91
92/// A single content block inside a tool result.
93#[derive(Debug, Clone, Serialize)]
94pub struct ToolResultContent {
95    #[serde(rename = "type")]
96    pub content_type: String,
97    pub text: String,
98}
99
100impl ToolResult {
101    pub fn text(text: impl Into<String>) -> Self {
102        Self {
103            content: vec![ToolResultContent {
104                content_type: "text".into(),
105                text: text.into(),
106            }],
107            is_error: false,
108        }
109    }
110
111    pub fn error(text: impl Into<String>) -> Self {
112        Self {
113            content: vec![ToolResultContent {
114                content_type: "text".into(),
115                text: text.into(),
116            }],
117            is_error: true,
118        }
119    }
120}
121
122// ---------------------------------------------------------------------------
123// MCP domain-level error types (migrated from mcp/error.rs)
124// ---------------------------------------------------------------------------
125
126/// MCP error code (v0)
127#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
128#[serde(rename_all = "snake_case")]
129pub enum McpErrorCode {
130    CacheMissing,
131    CacheInvalid,
132    InvalidQuery,
133    InvalidBudget,
134    IoError,
135    InternalError,
136}
137
138impl McpErrorCode {
139    /// Map to the corresponding JSON-RPC 2.0 error code.
140    ///
141    /// Input validation failures → -32602 (Invalid params)
142    /// Server-side failures     → -32603 (Internal error)
143    pub fn json_rpc_code(&self) -> i32 {
144        match self {
145            Self::CacheMissing | Self::CacheInvalid => -32602,
146            Self::InvalidQuery | Self::InvalidBudget => -32602,
147            Self::IoError | Self::InternalError => -32603,
148        }
149    }
150}
151
152/// MCP error object
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct McpError {
155    pub code: McpErrorCode,
156    pub message: String,
157}
158
159/// MCP error response (top-level)
160#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
161pub struct McpErrorResponse {
162    pub error: McpError,
163}
164
165impl McpErrorResponse {
166    pub fn new(code: McpErrorCode, message: impl Into<String>) -> Self {
167        Self {
168            error: McpError {
169                code,
170                message: message.into(),
171            },
172        }
173    }
174
175    /// Construct with the spec-recommended canonical message for a given code.
176    ///
177    /// Messages match error_schema.md "Recommended canonical messages" exactly.
178    pub fn canonical(code: McpErrorCode) -> Self {
179        let message = match &code {
180            McpErrorCode::CacheMissing => "Cache does not exist",
181            McpErrorCode::CacheInvalid => "Cache exists but is invalid",
182            McpErrorCode::InvalidQuery => "Query is invalid",
183            McpErrorCode::InvalidBudget => "Budget is invalid",
184            McpErrorCode::IoError => "I/O error occurred",
185            McpErrorCode::InternalError => "Internal error",
186        };
187        Self::new(code, message)
188    }
189}
190
191/// Convert an MCP domain error into a JSON-RPC error.
192///
193/// The JSON-RPC `code` is derived from the MCP error code.
194/// The JSON-RPC `message` is the human-readable MCP message.
195/// The full MCP error object is carried in `data` for structured clients.
196impl From<McpErrorResponse> for JsonRpcError {
197    fn from(mcp: McpErrorResponse) -> Self {
198        Self {
199            code: mcp.error.code.json_rpc_code(),
200            message: mcp.error.message.clone(),
201            data: Some(serde_json::to_value(&mcp).expect("McpErrorResponse must serialize to JSON Value")),
202        }
203    }
204}
205
206/// Convert an MCP domain error into a tool result with `isError: true`.
207///
208/// The text content is the JSON-serialized `McpErrorResponse`, preserving
209/// the structured error for clients that inspect tool output.
210impl From<McpErrorResponse> for ToolResult {
211    fn from(mcp: McpErrorResponse) -> Self {
212        let json = serde_json::to_string(&mcp).expect("McpErrorResponse must serialize to JSON string");
213        Self::error(format!("{json}\n"))
214    }
215}