1use std::collections::HashMap;
12
13use anyhow::{Result, anyhow};
14use serde::{Deserialize, Serialize};
15
16static REQUEST_ID: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(1);
21
22pub(crate) fn next_request_id() -> usize {
23 REQUEST_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
24}
25
26pub type McpServerConfig = McpServer;
32
33#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct McpServer {
36 pub name: String,
38 pub command: String,
40 pub args: Vec<String>,
42 pub env: HashMap<String, String>,
44 pub enabled: bool,
46}
47
48impl McpServer {
49 pub fn new(name: &str, command: &str) -> Self {
51 Self {
52 name: name.to_string(),
53 command: command.to_string(),
54 args: Vec::new(),
55 env: HashMap::new(),
56 enabled: true,
57 }
58 }
59
60 pub fn with_args(mut self, args: Vec<String>) -> Self {
62 self.args = args;
63 self
64 }
65
66 pub fn with_env(mut self, key: &str, value: &str) -> Self {
68 self.env.insert(key.to_string(), value.to_string());
69 self
70 }
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct McpRequest {
80 pub jsonrpc: String,
82 #[serde(
87 skip_serializing_if = "serde_json::Value::is_null",
88 default = "serde_json::Value::default"
89 )]
90 pub id: serde_json::Value,
91 pub method: String,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub params: Option<serde_json::Value>,
96}
97
98impl McpRequest {
99 pub fn new(method: &str) -> Self {
101 Self::with_id(next_request_id(), method)
102 }
103
104 pub fn with_id(id: usize, method: &str) -> Self {
106 Self {
107 jsonrpc: "2.0".to_string(),
108 id: serde_json::json!(id),
109 method: method.to_string(),
110 params: None,
111 }
112 }
113
114 pub fn notification(method: &str) -> Self {
119 Self {
120 jsonrpc: "2.0".to_string(),
121 id: serde_json::Value::Null,
122 method: method.to_string(),
123 params: None,
124 }
125 }
126
127 pub fn with_params(mut self, params: serde_json::Value) -> Self {
129 self.params = Some(params);
130 self
131 }
132
133 pub fn to_jsonl(&self) -> Result<Vec<u8>> {
135 let mut buf = serde_json::to_vec(self)?;
136 buf.push(b'\n');
137 Ok(buf)
138 }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct McpResponse {
144 pub jsonrpc: String,
146 pub id: serde_json::Value,
148 #[serde(skip_serializing_if = "Option::is_none")]
150 pub result: Option<serde_json::Value>,
151 #[serde(skip_serializing_if = "Option::is_none")]
153 pub error: Option<McpError>,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct McpError {
159 pub code: i32,
161 pub message: String,
163 #[serde(skip_serializing_if = "Option::is_none")]
165 pub data: Option<serde_json::Value>,
166}
167
168impl std::fmt::Display for McpError {
169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170 let error_type = match self.code {
171 -32700 => "parse error",
172 -32600 => "invalid request",
173 -32601 => "method not found",
174 -32602 => "invalid params",
175 -32603 => "internal error",
176 -32099..=-32000 => "server error",
177 _ => "unknown error",
178 };
179 write!(f, "{} (code {}): {}", error_type, self.code, self.message)
180 }
181}
182
183impl McpError {
184 pub fn new(code: i32, message: &str) -> Self {
186 Self {
187 code,
188 message: message.to_string(),
189 data: None,
190 }
191 }
192
193 pub fn parse_error() -> Self {
195 Self::new(-32700, "Parse error")
196 }
197
198 pub fn invalid_request(msg: &str) -> Self {
200 Self::new(-32600, msg)
201 }
202
203 pub fn method_not_found() -> Self {
205 Self::new(-32601, "Method not found")
206 }
207
208 pub fn invalid_params() -> Self {
210 Self::new(-32602, "Invalid params")
211 }
212
213 pub fn internal_error(msg: &str) -> Self {
215 Self::new(-32603, msg)
216 }
217
218 pub fn server_error(msg: &str) -> Self {
220 Self::new(-32000, msg)
221 }
222}
223
224impl McpResponse {
225 pub fn is_error(&self) -> bool {
227 self.error.is_some()
228 }
229
230 pub fn into_result(self) -> Result<serde_json::Value> {
232 if let Some(err) = self.error {
233 return Err(anyhow!("{err}"));
234 }
235 Ok(self.result.unwrap_or(serde_json::Value::Null))
236 }
237}
238
239#[derive(Debug, Clone, Serialize, Deserialize, Default)]
245pub struct McpCapabilities {
246 pub tools: bool,
248 pub resources: bool,
250 pub prompts: bool,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct InitializeParams {
257 pub protocol_version: String,
259 pub capabilities: McpCapabilities,
261 pub client_info: ClientInfo,
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct ClientInfo {
268 pub name: String,
270 pub version: String,
272}
273
274impl Default for InitializeParams {
275 fn default() -> Self {
276 Self {
277 protocol_version: "2024-11-05".to_string(),
278 capabilities: McpCapabilities::default(),
279 client_info: ClientInfo {
280 name: "oxios".to_string(),
281 version: env!("CARGO_PKG_VERSION").to_string(),
282 },
283 }
284 }
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct InitializeResult {
290 pub protocol_version: String,
292 pub capabilities: McpCapabilities,
294 pub server_info: ServerInfo,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct ServerInfo {
301 pub name: String,
303 pub version: String,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct McpTool {
314 pub name: String,
316 pub description: String,
318 pub input_schema: serde_json::Value,
320}
321
322impl McpTool {
323 pub fn name(&self) -> &str {
325 &self.name
326 }
327
328 pub fn description(&self) -> &str {
330 &self.description
331 }
332
333 pub fn input_schema(&self) -> &serde_json::Value {
335 &self.input_schema
336 }
337}
338
339#[derive(Debug, Clone, Serialize, Deserialize)]
341pub struct McpToolsResult {
342 pub tools: Vec<McpTool>,
344}
345
346#[derive(Debug, Clone, Serialize, Deserialize)]
348pub struct McpToolCallResult {
349 pub content: Vec<McpContentBlock>,
351 pub is_error: Option<bool>,
353}
354
355#[derive(Debug, Clone, Serialize, Deserialize)]
357#[serde(tag = "type")]
358pub enum McpContentBlock {
359 #[serde(rename = "text")]
361 Text {
362 text: String,
364 },
365 #[serde(rename = "image")]
367 Image {
368 data: String,
370 mime_type: Option<String>,
372 },
373 #[serde(rename = "resource")]
375 Resource {
376 resource: MappedResource,
378 },
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct MappedResource {
384 pub uri: String,
386 pub mime_type: Option<String>,
388}