Skip to main content

hedl_mcp/
protocol.rs

1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License in the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! MCP Protocol types and JSON-RPC message handling.
19
20use serde::{Deserialize, Serialize};
21use serde_json::Value;
22
23/// JSON-RPC request.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct JsonRpcRequest {
26    /// JSON-RPC version string, must be "2.0".
27    pub jsonrpc: String,
28    /// Request identifier (string, number, or null).
29    pub id: Option<Value>,
30    /// Method name to invoke.
31    pub method: String,
32    /// Optional method parameters.
33    #[serde(default)]
34    pub params: Option<Value>,
35}
36
37impl JsonRpcRequest {
38    /// Validate JSON-RPC 2.0 protocol compliance.
39    ///
40    /// # Validation Rules
41    ///
42    /// 1. JSON-RPC version must be exactly "2.0"
43    /// 2. Request ID must be string, number, or null (if present)
44    /// 3. Method name must be non-empty and contain no whitespace
45    /// 4. Params must be object or array (if present)
46    ///
47    /// # Returns
48    ///
49    /// - `Ok(())` if the request is valid
50    /// - `Err(String)` with a descriptive error message if validation fails
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use hedl_mcp::JsonRpcRequest;
56    /// use serde_json::json;
57    ///
58    /// let request = JsonRpcRequest {
59    ///     jsonrpc: "2.0".to_string(),
60    ///     id: Some(json!(1)),
61    ///     method: "initialize".to_string(),
62    ///     params: Some(json!({"key": "value"})),
63    /// };
64    ///
65    /// assert!(request.validate().is_ok());
66    /// ```
67    pub fn validate(&self) -> Result<(), String> {
68        // Validate jsonrpc version
69        if self.jsonrpc != "2.0" {
70            return Err(format!(
71                "Invalid JSON-RPC version: expected '2.0', got '{}'",
72                self.jsonrpc
73            ));
74        }
75
76        // Validate id type (must be string, number, or null)
77        if let Some(id) = &self.id {
78            match id {
79                Value::String(_) | Value::Number(_) | Value::Null => {}
80                _ => return Err("Invalid request id: must be string, number, or null".to_string()),
81            }
82        }
83
84        // Validate method name
85        if self.method.is_empty() {
86            return Err("Method name cannot be empty".to_string());
87        }
88        if self.method.contains(char::is_whitespace) {
89            return Err("Method name cannot contain whitespace".to_string());
90        }
91
92        // Validate params type (must be object or array if present)
93        if let Some(params) = &self.params {
94            if !params.is_object() && !params.is_array() {
95                return Err("Params must be an object or array".to_string());
96            }
97        }
98
99        Ok(())
100    }
101}
102
103/// JSON-RPC response.
104#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct JsonRpcResponse {
106    /// JSON-RPC version string, always "2.0".
107    pub jsonrpc: String,
108    /// Request identifier this response corresponds to.
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub id: Option<Value>,
111    /// Result value on success.
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub result: Option<Value>,
114    /// Error object on failure.
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub error: Option<JsonRpcError>,
117}
118
119impl JsonRpcResponse {
120    /// Create a success response.
121    #[must_use]
122    pub fn success(id: Option<Value>, result: Value) -> Self {
123        Self {
124            jsonrpc: "2.0".to_string(),
125            id,
126            result: Some(result),
127            error: None,
128        }
129    }
130
131    /// Create an error response.
132    #[must_use]
133    pub fn error(id: Option<Value>, code: i32, message: String, data: Option<Value>) -> Self {
134        Self {
135            jsonrpc: "2.0".to_string(),
136            id,
137            result: None,
138            error: Some(JsonRpcError {
139                code,
140                message,
141                data,
142            }),
143        }
144    }
145}
146
147/// JSON-RPC error.
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct JsonRpcError {
150    /// Error code indicating the error type.
151    pub code: i32,
152    /// Human-readable error message.
153    pub message: String,
154    /// Additional error data.
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub data: Option<Value>,
157}
158
159// --- MCP Protocol Types ---
160
161/// Server capabilities.
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct ServerCapabilities {
164    /// Tools capability configuration.
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub tools: Option<ToolsCapability>,
167    /// Resources capability configuration.
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub resources: Option<ResourcesCapability>,
170    /// Prompts capability configuration.
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub prompts: Option<PromptsCapability>,
173}
174
175/// Tools capability configuration.
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct ToolsCapability {
178    /// Whether to notify clients when the tools list changes.
179    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
180    pub list_changed: Option<bool>,
181}
182
183/// Resources capability configuration.
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct ResourcesCapability {
186    /// Whether clients can subscribe to resource updates.
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub subscribe: Option<bool>,
189    /// Whether to notify clients when the resources list changes.
190    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
191    pub list_changed: Option<bool>,
192}
193
194/// Prompts capability configuration.
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct PromptsCapability {
197    /// Whether to notify clients when the prompts list changes.
198    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
199    pub list_changed: Option<bool>,
200}
201
202/// Initialize request params.
203#[derive(Debug, Clone, Serialize, Deserialize)]
204pub struct InitializeParams {
205    /// MCP protocol version.
206    #[serde(rename = "protocolVersion")]
207    pub protocol_version: String,
208    /// Client capability declarations.
209    pub capabilities: ClientCapabilities,
210    /// Client identification information.
211    #[serde(rename = "clientInfo")]
212    pub client_info: ClientInfo,
213}
214
215/// Client capabilities.
216#[derive(Debug, Clone, Serialize, Deserialize, Default)]
217pub struct ClientCapabilities {
218    /// Root URI capability configuration.
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub roots: Option<RootsCapability>,
221    /// Sampling capability configuration.
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub sampling: Option<Value>,
224}
225
226/// Root URI capability configuration.
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct RootsCapability {
229    /// Whether to notify clients when the roots list changes.
230    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
231    pub list_changed: Option<bool>,
232}
233
234/// Client info.
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ClientInfo {
237    /// Client application name.
238    pub name: String,
239    /// Client application version.
240    pub version: String,
241}
242
243/// Initialize result.
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct InitializeResult {
246    /// MCP protocol version supported by server.
247    #[serde(rename = "protocolVersion")]
248    pub protocol_version: String,
249    /// Server capability declarations.
250    pub capabilities: ServerCapabilities,
251    /// Server identification information.
252    #[serde(rename = "serverInfo")]
253    pub server_info: ServerInfo,
254}
255
256/// Server info.
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct ServerInfo {
259    /// Server application name.
260    pub name: String,
261    /// Server application version.
262    pub version: String,
263}
264
265/// Tool definition.
266#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct Tool {
268    /// Tool name identifier.
269    pub name: String,
270    /// Human-readable tool description.
271    pub description: String,
272    /// JSON Schema for tool input parameters.
273    #[serde(rename = "inputSchema")]
274    pub input_schema: Value,
275}
276
277/// Tool call request.
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct CallToolParams {
280    /// Name of the tool to call.
281    pub name: String,
282    /// Arguments to pass to the tool.
283    #[serde(default)]
284    pub arguments: Option<Value>,
285}
286
287/// Tool call result.
288#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct CallToolResult {
290    /// Content items returned by the tool.
291    pub content: Vec<Content>,
292    /// Whether the tool call resulted in an error.
293    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
294    pub is_error: Option<bool>,
295}
296
297/// Content item.
298#[derive(Debug, Clone, Serialize, Deserialize)]
299#[serde(tag = "type")]
300pub enum Content {
301    /// Text content.
302    #[serde(rename = "text")]
303    Text {
304        /// The text content.
305        text: String,
306    },
307    /// Resource content.
308    #[serde(rename = "resource")]
309    Resource {
310        /// The resource content.
311        resource: ResourceContent,
312    },
313}
314
315/// Resource content.
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct ResourceContent {
318    /// Resource URI identifier.
319    pub uri: String,
320    /// MIME type of the resource content.
321    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
322    pub mime_type: Option<String>,
323    /// Text content of the resource.
324    #[serde(skip_serializing_if = "Option::is_none")]
325    pub text: Option<String>,
326}
327
328/// Resource definition.
329#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct Resource {
331    /// Resource URI identifier.
332    pub uri: String,
333    /// Human-readable resource name.
334    pub name: String,
335    /// Optional resource description.
336    #[serde(skip_serializing_if = "Option::is_none")]
337    pub description: Option<String>,
338    /// MIME type of the resource content.
339    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
340    pub mime_type: Option<String>,
341}
342
343/// List tools result.
344#[derive(Debug, Clone, Serialize, Deserialize)]
345pub struct ListToolsResult {
346    /// Available tools.
347    pub tools: Vec<Tool>,
348}
349
350/// List resources result.
351#[derive(Debug, Clone, Serialize, Deserialize)]
352pub struct ListResourcesResult {
353    /// Available resources.
354    pub resources: Vec<Resource>,
355}
356
357/// Read resource params.
358#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct ReadResourceParams {
360    /// URI of the resource to read.
361    pub uri: String,
362}
363
364/// Read resource result.
365#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct ReadResourceResult {
367    /// Resource content items.
368    pub contents: Vec<ResourceContent>,
369}