1pub mod auth;
4pub mod registry;
5pub mod servers;
6mod transport;
7
8pub use auth::{
9 ApiKeyAuth, AuthConfig, AuthMethod, AuthenticatedHttpTransport, BasicAuth, BearerAuth,
10 CredentialStore, CustomHeaderAuth,
11};
12pub use registry::{
13 LoadBalanceConfig, LoadBalanceStrategy, McpRegistry, RegistryStats, ServerEntry, ServerHealth,
14 ServerMetrics,
15};
16pub use servers::{
17 DatabaseConfig, DatabaseServer, DatabaseType, ExecuteResult, FilesystemServer, GitServer,
18 QueryResult, ShellServer, StatementResult, TransactionResult, WebServer, WorkflowExecutor,
19 WorkflowServer, WorkflowServerConfig,
20};
21pub use transport::{HttpTransport, McpTransport, StdioTransport};
22
23use async_trait::async_trait;
24use serde::{Deserialize, Serialize};
25use serde_json::json;
26use thiserror::Error;
27
28pub type Result<T> = std::result::Result<T, McpError>;
29
30#[derive(Error, Debug)]
31pub enum McpError {
32 #[error("Protocol error: {0}")]
33 ProtocolError(String),
34
35 #[error("Server error: {0}")]
36 ServerError(String),
37
38 #[error("Tool not found: {0}")]
39 ToolNotFound(String),
40
41 #[error("Invalid request: {0}")]
42 InvalidRequest(String),
43
44 #[error("Invalid argument: {0}")]
45 InvalidArgument(String),
46
47 #[error("Tool execution error: {0}")]
48 ToolExecutionError(String),
49
50 #[error("IO error: {0}")]
51 IoError(#[from] std::io::Error),
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct McpRequest {
56 pub server_id: String,
57 pub tool_name: String,
58 pub parameters: serde_json::Value,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct McpResponse {
63 pub result: serde_json::Value,
64}
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct ToolSchema {
68 pub name: String,
69 pub description: Option<String>,
70 #[serde(rename = "inputSchema")]
71 pub input_schema: serde_json::Value,
72}
73
74#[async_trait]
76pub trait McpClient: Send + Sync {
77 async fn invoke_tool(&mut self, request: McpRequest) -> Result<McpResponse>;
78 async fn list_tools(&mut self, server_id: &str) -> Result<Vec<ToolSchema>>;
79}
80
81#[async_trait]
83pub trait McpServer: Send + Sync {
84 async fn call_tool(
86 &self,
87 name: &str,
88 arguments: serde_json::Value,
89 ) -> Result<serde_json::Value>;
90
91 async fn list_tools(&self) -> Result<Vec<serde_json::Value>>;
93}
94
95pub struct DefaultMcpClient<T: McpTransport> {
97 transport: T,
98 #[allow(dead_code)]
99 server_id: String,
100}
101
102impl<T: McpTransport> DefaultMcpClient<T> {
103 pub fn new(transport: T, server_id: String) -> Self {
104 Self {
105 transport,
106 server_id,
107 }
108 }
109
110 pub async fn initialize(&mut self) -> Result<()> {
111 let request = json!({
112 "method": "initialize",
113 "params": {
114 "protocolVersion": "1.0",
115 "clientInfo": {
116 "name": "oxify",
117 "version": env!("CARGO_PKG_VERSION")
118 }
119 }
120 });
121
122 let _response = self.transport.send_request(request).await?;
123 Ok(())
124 }
125
126 pub async fn close(&mut self) -> Result<()> {
127 self.transport.close().await
128 }
129}
130
131#[async_trait]
132impl<T: McpTransport> McpClient for DefaultMcpClient<T> {
133 async fn invoke_tool(&mut self, request: McpRequest) -> Result<McpResponse> {
134 let rpc_request = json!({
135 "method": "tools/call",
136 "params": {
137 "name": request.tool_name,
138 "arguments": request.parameters
139 }
140 });
141
142 let response = self.transport.send_request(rpc_request).await?;
143
144 if let Some(error) = response.get("error") {
145 return Err(McpError::ServerError(format!(
146 "Tool invocation failed: {}",
147 error
148 )));
149 }
150
151 let result = response
152 .get("result")
153 .ok_or_else(|| McpError::ProtocolError("Missing result field".to_string()))?
154 .clone();
155
156 Ok(McpResponse { result })
157 }
158
159 async fn list_tools(&mut self, _server_id: &str) -> Result<Vec<ToolSchema>> {
160 let request = json!({
161 "method": "tools/list",
162 "params": {}
163 });
164
165 let response = self.transport.send_request(request).await?;
166
167 if let Some(error) = response.get("error") {
168 return Err(McpError::ServerError(format!(
169 "Failed to list tools: {}",
170 error
171 )));
172 }
173
174 let result = response
175 .get("result")
176 .and_then(|r| r.get("tools"))
177 .ok_or_else(|| McpError::ProtocolError("Missing tools in response".to_string()))?;
178
179 let tools: Vec<ToolSchema> = serde_json::from_value(result.clone())
180 .map_err(|e| McpError::ProtocolError(format!("Failed to parse tools: {}", e)))?;
181
182 Ok(tools)
183 }
184}