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}