1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::io::{BufRead, BufReader, Write};
6
7use crate::error::{EngramError, Result};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct McpRequest {
12 pub jsonrpc: String,
13 pub id: Option<Value>,
14 pub method: String,
15 #[serde(default)]
16 pub params: Value,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct McpResponse {
22 pub jsonrpc: String,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub id: Option<Value>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 pub result: Option<Value>,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 pub error: Option<McpError>,
29}
30
31#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct McpError {
34 pub code: i64,
35 pub message: String,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub data: Option<Value>,
38}
39
40impl McpResponse {
41 pub fn success(id: Option<Value>, result: Value) -> Self {
43 Self {
44 jsonrpc: "2.0".to_string(),
45 id,
46 result: Some(result),
47 error: None,
48 }
49 }
50
51 pub fn error(id: Option<Value>, code: i64, message: String) -> Self {
53 Self {
54 jsonrpc: "2.0".to_string(),
55 id,
56 result: None,
57 error: Some(McpError {
58 code,
59 message,
60 data: None,
61 }),
62 }
63 }
64
65 pub fn from_error(id: Option<Value>, err: EngramError) -> Self {
67 Self::error(id, err.code(), err.to_string())
68 }
69}
70
71pub struct McpServer<H>
73where
74 H: McpHandler,
75{
76 handler: H,
77}
78
79pub trait McpHandler: Send + Sync {
81 fn handle_request(&self, request: McpRequest) -> McpResponse;
82}
83
84impl<H: McpHandler> McpServer<H> {
85 pub fn new(handler: H) -> Self {
87 Self { handler }
88 }
89
90 pub fn run(&self) -> Result<()> {
92 let stdin = std::io::stdin();
93 let stdout = std::io::stdout();
94 let mut reader = BufReader::new(stdin.lock());
95 let mut writer = stdout.lock();
96
97 let mut line = String::new();
98
99 loop {
100 line.clear();
101 match reader.read_line(&mut line) {
102 Ok(0) => break, Ok(_) => {
104 let trimmed = line.trim();
105 if trimmed.is_empty() {
106 continue;
107 }
108
109 match serde_json::from_str::<McpRequest>(trimmed) {
110 Ok(request) => {
111 let response = self.handler.handle_request(request);
112 let response_json = serde_json::to_string(&response)?;
113 writeln!(writer, "{}", response_json)?;
114 writer.flush()?;
115 }
116 Err(e) => {
117 let response =
118 McpResponse::error(None, -32700, format!("Parse error: {}", e));
119 let response_json = serde_json::to_string(&response)?;
120 writeln!(writer, "{}", response_json)?;
121 writer.flush()?;
122 }
123 }
124 }
125 Err(e) => {
126 tracing::error!("Error reading stdin: {}", e);
127 break;
128 }
129 }
130 }
131
132 Ok(())
133 }
134}
135
136pub mod methods {
138 pub const INITIALIZE: &str = "initialize";
139 pub const INITIALIZED: &str = "notifications/initialized";
140 pub const LIST_TOOLS: &str = "tools/list";
141 pub const CALL_TOOL: &str = "tools/call";
142 pub const LIST_RESOURCES: &str = "resources/list";
143 pub const READ_RESOURCE: &str = "resources/read";
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct ToolDefinition {
149 pub name: String,
150 pub description: String,
151 #[serde(rename = "inputSchema")]
152 pub input_schema: Value,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct InitializeResult {
158 #[serde(rename = "protocolVersion")]
159 pub protocol_version: String,
160 pub capabilities: ServerCapabilities,
161 #[serde(rename = "serverInfo")]
162 pub server_info: ServerInfo,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct ServerCapabilities {
168 pub tools: Option<ToolsCapability>,
169 pub resources: Option<ResourcesCapability>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct ToolsCapability {
174 #[serde(rename = "listChanged")]
175 pub list_changed: bool,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct ResourcesCapability {
180 pub subscribe: bool,
181 #[serde(rename = "listChanged")]
182 pub list_changed: bool,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize)]
187pub struct ServerInfo {
188 pub name: String,
189 pub version: String,
190}
191
192impl Default for InitializeResult {
193 fn default() -> Self {
194 Self {
195 protocol_version: "2024-11-05".to_string(),
196 capabilities: ServerCapabilities {
197 tools: Some(ToolsCapability {
198 list_changed: false,
199 }),
200 resources: Some(ResourcesCapability {
201 subscribe: false,
202 list_changed: false,
203 }),
204 },
205 server_info: ServerInfo {
206 name: "engram".to_string(),
207 version: env!("CARGO_PKG_VERSION").to_string(),
208 },
209 }
210 }
211}
212
213#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct ToolCallResult {
216 pub content: Vec<ToolContent>,
217 #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
218 pub is_error: Option<bool>,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
222#[serde(tag = "type")]
223pub enum ToolContent {
224 #[serde(rename = "text")]
225 Text { text: String },
226 #[serde(rename = "image")]
227 Image { data: String, mime_type: String },
228 #[serde(rename = "resource")]
229 Resource { resource: ResourceContent },
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
233pub struct ResourceContent {
234 pub uri: String,
235 pub text: Option<String>,
236 pub blob: Option<String>,
237 #[serde(rename = "mimeType")]
238 pub mime_type: Option<String>,
239}
240
241impl ToolCallResult {
242 pub fn text(text: impl Into<String>) -> Self {
244 Self {
245 content: vec![ToolContent::Text { text: text.into() }],
246 is_error: None,
247 }
248 }
249
250 pub fn json(value: &impl Serialize) -> Self {
252 let text = serde_json::to_string_pretty(value).unwrap_or_default();
253 Self::text(text)
254 }
255
256 pub fn error(message: impl Into<String>) -> Self {
258 Self {
259 content: vec![ToolContent::Text {
260 text: message.into(),
261 }],
262 is_error: Some(true),
263 }
264 }
265}