mcp_runner/transport/
mod.rs

1//! Transport module for communication with MCP servers.
2//!
3//! This module provides transport layer implementations for communicating with
4//! Model Context Protocol (MCP) servers. It defines a common `Transport` trait
5//! that abstracts the communication details and provides implementations for
6//! different transport mechanisms.
7//!
8//! Currently supported transport mechanisms:
9//! - `StdioTransport`: Communicates with MCP servers via standard input/output
10
11pub mod json_rpc;
12mod stdio;
13
14use crate::error::Result;
15use async_trait::async_trait;
16pub use json_rpc::{JsonRpcMessage, JsonRpcRequest, JsonRpcResponse};
17use serde_json::Value;
18pub use stdio::StdioTransport;
19
20/// Transport is the core trait for communication with MCP servers.
21///
22/// This trait defines the interface for interacting with Model Context Protocol (MCP)
23/// servers through various transport mechanisms. Implementations of this trait handle
24/// the low-level communication details, allowing clients to focus on high-level interactions.
25///
26/// # Examples
27///
28/// Using a transport to list available tools:
29///
30/// ```no_run
31/// use mcp_runner::transport::Transport;
32/// use mcp_runner::error::Result;
33/// use serde_json::Value;
34///
35/// async fn example<T: Transport>(transport: &T) -> Result<()> {
36///     // Initialize the transport
37///     transport.initialize().await?;
38///     
39///     // List available tools
40///     let tools = transport.list_tools().await?;
41///     println!("Available tools: {:?}", tools);
42///     
43///     Ok(())
44/// }
45/// ```
46#[async_trait]
47pub trait Transport: Send + Sync {
48    /// Initializes the transport connection to the MCP server.
49    ///
50    /// This method should be called before any other methods to ensure
51    /// the transport is ready for communication.
52    ///
53    /// # Returns
54    ///
55    /// A `Result<()>` that is:
56    /// - `Ok(())` if initialization was successful
57    /// - `Err(Error)` if initialization failed
58    async fn initialize(&self) -> Result<()>;
59
60    /// Lists all available tools provided by the MCP server.
61    ///
62    /// # Returns
63    ///
64    /// A `Result<Vec<Value>>` that is:
65    /// - `Ok(Vec<Value>)` containing a list of tool definitions if successful
66    /// - `Err(Error)` if the request failed
67    ///
68    /// # Tool Definition Format
69    ///
70    /// Each tool definition is a JSON object with at least:
71    /// - `name`: A string identifier for the tool
72    /// - `description`: A human-readable description of the tool
73    /// - Additional fields as specified by the MCP server
74    async fn list_tools(&self) -> Result<Vec<Value>>;
75
76    /// Calls a specific tool on the MCP server with the provided arguments.
77    ///
78    /// # Arguments
79    ///
80    /// * `name` - The name of the tool to call
81    /// * `args` - Arguments to pass to the tool as a JSON value
82    ///
83    /// # Returns
84    ///
85    /// A `Result<Value>` that is:
86    /// - `Ok(Value)` containing the tool's response if successful
87    /// - `Err(Error)` if the tool call failed
88    async fn call_tool(&self, name: &str, args: Value) -> Result<Value>;
89
90    /// Lists all available resources provided by the MCP server.
91    ///
92    /// Resources can include model metadata, usage information, or other
93    /// static or dynamic data exposed by the server.
94    ///
95    /// # Returns
96    ///
97    /// A `Result<Vec<Value>>` that is:
98    /// - `Ok(Vec<Value>)` containing a list of resource definitions if successful
99    /// - `Err(Error)` if the request failed
100    async fn list_resources(&self) -> Result<Vec<Value>>;
101
102    /// Retrieves a specific resource from the MCP server.
103    ///
104    /// # Arguments
105    ///
106    /// * `uri` - The URI identifying the resource to retrieve
107    ///
108    /// # Returns
109    ///
110    /// A `Result<Value>` that is:
111    /// - `Ok(Value)` containing the resource data if successful
112    /// - `Err(Error)` if the resource retrieval failed
113    async fn get_resource(&self, uri: &str) -> Result<Value>;
114}
115
116/// Creates a new transport for a server using the provided configuration
117///
118/// This function creates an appropriate transport based on the server configuration.
119/// Currently, it only supports stdio transport.
120///
121/// # Arguments
122///
123/// * `server_name` - Name of the server for which to create transport
124/// * `server_config` - Server configuration
125///
126/// # Returns
127///
128/// A `Result<StdioTransport>` containing a transport implementation
129pub fn create_transport_for_config(
130    server_name: &str,
131    server_config: &crate::config::ServerConfig,
132) -> Result<StdioTransport> {
133    use crate::error::Error;
134    use async_process::{Command, Stdio as AsyncStdio};
135    use tracing;
136
137    tracing::debug!(server = %server_name, "Creating transport for server");
138
139    // Currently we only support launching a new process for connection
140    let mut cmd = Command::new(&server_config.command);
141
142    // Add arguments
143    cmd.args(&server_config.args);
144
145    // Set environment variables
146    for (key, value) in &server_config.env {
147        cmd.env(key, value);
148    }
149
150    // Configure stdio
151    cmd.stdin(AsyncStdio::piped())
152        .stdout(AsyncStdio::piped())
153        .stderr(AsyncStdio::inherit());
154
155    // Spawn the process
156    let child = cmd.spawn().map_err(|e| {
157        tracing::error!(error = %e, "Failed to spawn server process");
158        Error::Process(format!("Failed to spawn server process: {}", e))
159    })?;
160
161    let stdin = child.stdin.ok_or_else(|| {
162        tracing::error!("Failed to open stdin for server process");
163        Error::Process("Failed to open stdin for server process".into())
164    })?;
165
166    let stdout = child.stdout.ok_or_else(|| {
167        tracing::error!("Failed to open stdout from server process");
168        Error::Process("Failed to open stdout from server process".into())
169    })?;
170
171    // Create the StdioTransport
172    let transport = StdioTransport::new(server_name.to_string(), stdin, stdout);
173
174    // Return the transport
175    Ok(transport)
176}