Skip to main content

vtcode_mcp/
rmcp_transport.rs

1/// RMCP transport layer wrappers for VT Code
2///
3/// This module provides wrappers around rmcp's transport types to integrate
4/// with VT Code's configuration and error handling.
5use anyhow::{Context, Result};
6use rmcp::transport::StreamableHttpClientTransport;
7use rmcp::transport::TokioChildProcess;
8use rmcp_reqwest::header::HeaderMap;
9use std::ffi::OsString;
10use std::path::PathBuf;
11use tokio::process::Command;
12
13use vtcode_config::mcp::McpStdioServerConfig;
14
15/// Type alias for HTTP transport
16pub type HttpTransport = StreamableHttpClientTransport<rmcp_reqwest::Client>;
17
18/// Create a stdio-based transport from configuration
19///
20/// # Arguments
21/// * `stdio_config` - Stdio server configuration with command and args
22/// * `env` - Environment variables to pass to the process
23///
24/// # Returns
25/// A TokioChildProcess transport ready to use with RMCP client
26pub fn create_stdio_transport(
27    stdio_config: &McpStdioServerConfig,
28    env: &hashbrown::HashMap<OsString, OsString>,
29) -> Result<TokioChildProcess> {
30    let mut cmd = Command::new(&stdio_config.command);
31
32    // Add arguments
33    let _command = cmd.args(&stdio_config.args);
34
35    // Set working directory if specified
36    if let Some(working_dir) = &stdio_config.working_directory {
37        let _command = cmd.current_dir(working_dir);
38    }
39
40    // Configure environment variables
41    for (key, value) in env {
42        let _command = cmd.env(key, value);
43    }
44
45    // Create the child process transport
46    TokioChildProcess::new(cmd).context("Failed to create child process for MCP server")
47}
48
49/// Create a stdio transport from individual parameters (Phase 2 integration)
50///
51/// This is a convenience wrapper for use within RmcpClient where transport
52/// parameters come from different configuration sources.
53///
54/// # Arguments
55/// * `program` - Path to the executable
56/// * `args` - Command arguments
57/// * `working_dir` - Working directory (optional)
58/// * `env` - Environment variables to pass
59///
60/// # Returns
61/// A tuple of (TokioChildProcess transport, stderr reader)
62/// The stderr reader can be passed to async logging tasks
63pub fn create_stdio_transport_with_stderr(
64    program: &OsString,
65    args: &[OsString],
66    working_dir: Option<&PathBuf>,
67    env: &hashbrown::HashMap<OsString, OsString>,
68) -> Result<(TokioChildProcess, Option<tokio::process::ChildStderr>)> {
69    let mut cmd = Command::new(program);
70
71    let _command = cmd
72        .kill_on_drop(true)
73        .stdin(std::process::Stdio::piped())
74        .stdout(std::process::Stdio::piped())
75        .stderr(std::process::Stdio::piped())
76        .env_clear();
77
78    // Add all environment variables
79    for (key, value) in env {
80        let _command = cmd.env(key, value);
81    }
82
83    // Set working directory if provided
84    if let Some(dir) = working_dir {
85        let _command = cmd.current_dir(dir);
86    }
87
88    // Add command arguments
89    let _command = cmd.args(args);
90
91    // Create transport with stderr capture for logging
92    let builder = TokioChildProcess::builder(cmd);
93    builder
94        .stderr(std::process::Stdio::piped())
95        .spawn()
96        .context("Failed to create stdio transport with stderr capture")
97}
98
99/// Create an HTTP-based transport from endpoint URL (Phase 3.2)
100///
101/// # Arguments
102/// * `endpoint` - HTTP endpoint URL (e.g., `https://api.example.com/mcp`)
103/// * `bearer_token` - Optional bearer token for authentication
104/// * `headers` - Custom HTTP headers to include in requests
105///
106/// # Returns
107/// A StreamableHttpClientTransport ready to use with RMCP client
108///
109/// # Note
110/// This is a convenience wrapper for HTTP transport creation. The actual
111/// transport construction delegates to rmcp's StreamableHttpClientTransport
112/// following the pattern used in RmcpClient::new_streamable_http_client().
113///
114/// # Example
115/// ```ignore
116/// let transport = create_http_transport(
117///     "https://api.example.com/mcp",
118///     Some("auth_token"),
119///     &HeaderMap::new()
120/// )?;
121/// ```
122pub fn create_http_transport(
123    _endpoint: &str,
124    _bearer_token: Option<&str>,
125    _headers: &HeaderMap,
126) -> Result<HttpTransport> {
127    // Phase 3.2: HTTP transport wrapper
128    // NOTE: Full implementation requires direct use of rmcp APIs
129    // See RmcpClient::new_streamable_http_client() for reference implementation
130    // This function provides the interface; actual HTTP transport is created via:
131    // StreamableHttpClientTransport::with_client(http_client, config)
132
133    anyhow::bail!(
134        "HTTP transport creation requires rmcp's StreamableHttpClientTransport. \
135         Use RmcpClient::new_streamable_http_client() for full implementation."
136    )
137}
138
139#[cfg(test)]
140mod tests {
141    #[test]
142    fn test_transport_creation() {
143        // Test transport creation with configuration
144        // Detailed tests in integration tests
145    }
146}