vtcode_core/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 cmd.args(&stdio_config.args);
34
35 // Set working directory if specified
36 if let Some(working_dir) = &stdio_config.working_directory {
37 cmd.current_dir(working_dir);
38 }
39
40 // Configure environment variables
41 for (key, value) in env {
42 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 cmd.kill_on_drop(true)
72 .stdin(std::process::Stdio::piped())
73 .stdout(std::process::Stdio::piped())
74 .stderr(std::process::Stdio::piped())
75 .env_clear();
76
77 // Add all environment variables
78 for (key, value) in env {
79 cmd.env(key, value);
80 }
81
82 // Set working directory if provided
83 if let Some(dir) = working_dir {
84 cmd.current_dir(dir);
85 }
86
87 // Add command arguments
88 cmd.args(args);
89
90 // Create transport with stderr capture for logging
91 let builder = TokioChildProcess::builder(cmd);
92 builder
93 .stderr(std::process::Stdio::piped())
94 .spawn()
95 .context("Failed to create stdio transport with stderr capture")
96}
97
98/// Create an HTTP-based transport from endpoint URL (Phase 3.2)
99///
100/// # Arguments
101/// * `endpoint` - HTTP endpoint URL (e.g., `https://api.example.com/mcp`)
102/// * `bearer_token` - Optional bearer token for authentication
103/// * `headers` - Custom HTTP headers to include in requests
104///
105/// # Returns
106/// A StreamableHttpClientTransport ready to use with RMCP client
107///
108/// # Note
109/// This is a convenience wrapper for HTTP transport creation. The actual
110/// transport construction delegates to rmcp's StreamableHttpClientTransport
111/// following the pattern used in RmcpClient::new_streamable_http_client().
112///
113/// # Example
114/// ```ignore
115/// let transport = create_http_transport(
116/// "https://api.example.com/mcp",
117/// Some("auth_token"),
118/// &HeaderMap::new()
119/// )?;
120/// ```
121pub fn create_http_transport(
122 _endpoint: &str,
123 _bearer_token: Option<&str>,
124 _headers: &HeaderMap,
125) -> Result<HttpTransport> {
126 // Phase 3.2: HTTP transport wrapper
127 // NOTE: Full implementation requires direct use of rmcp APIs
128 // See RmcpClient::new_streamable_http_client() for reference implementation
129 // This function provides the interface; actual HTTP transport is created via:
130 // StreamableHttpClientTransport::with_client(http_client, config)
131
132 anyhow::bail!(
133 "HTTP transport creation requires rmcp's StreamableHttpClientTransport. \
134 Use RmcpClient::new_streamable_http_client() for full implementation."
135 )
136}
137
138#[cfg(test)]
139mod tests {
140 #[test]
141 fn test_transport_creation() {
142 // Test transport creation with configuration
143 // Detailed tests in integration tests
144 }
145}