Skip to content

Commit a930a9f

Browse files
authored
feat: Add environment variable support for HTTP MCP headers (#3075)
* feat: Add environment variable support for HTTP MCP headers - Enable ${env:VAR_NAME} syntax in HTTP MCP server headers - Extends existing env var processing from stdio to http transport - Improves security by avoiding hardcoded tokens in config files Example usage: "headers": { "Authorization": "Bearer ${env:GITHUB_TOKEN}" } * test: Add unit test for HTTP headers environment variable processing - Tests that env vars in HTTP headers are properly substituted - Covers Authorization header with Bearer token pattern - Verifies multiple headers and mixed content scenarios * revert: Remove unnecessary formatting change in oauth_util.rs Keep PR focused only on the core environment variable feature
1 parent e501b5a commit a930a9f

File tree

1 file changed

+33
-2
lines changed

1 file changed

+33
-2
lines changed

crates/chat-cli/src/mcp_client/client.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,13 @@ impl McpClientService {
458458
..
459459
} = &self.config;
460460

461-
let http_service_builder =
462-
HttpServiceBuilder::new(url, os, url, *timeout, scopes, headers, oauth, messenger);
461+
// Process environment variables in headers
462+
let mut processed_headers = headers.clone();
463+
for (_, value) in processed_headers.iter_mut() {
464+
*value = substitute_env_vars(value, &os.env);
465+
}
466+
467+
let http_service_builder = HttpServiceBuilder::new(url, os, url, *timeout, scopes, &processed_headers, messenger);
463468

464469
let (service, auth_client_wrapper) = http_service_builder.try_build(&self).await?;
465470

@@ -673,4 +678,30 @@ mod tests {
673678
assert_eq!(env_vars.get("KEY1").unwrap(), "Value is test_value");
674679
assert_eq!(env_vars.get("KEY2").unwrap(), "No substitution");
675680
}
681+
682+
#[tokio::test]
683+
async fn test_http_headers_env_var_processing() {
684+
let os = Os::new().await.unwrap();
685+
unsafe {
686+
os.env.set_var("GITHUB_TOKEN", "github_pat_test123");
687+
os.env.set_var("API_KEY", "secret_key_456");
688+
}
689+
690+
// Simulate HTTP headers with environment variables
691+
let mut headers = HashMap::new();
692+
headers.insert("Authorization".to_string(), "Bearer ${env:GITHUB_TOKEN}".to_string());
693+
headers.insert("X-API-Key".to_string(), "${env:API_KEY}".to_string());
694+
headers.insert("Content-Type".to_string(), "application/json".to_string());
695+
696+
// Process headers (same logic as in HTTP transport)
697+
let mut processed_headers = headers.clone();
698+
for (_, value) in processed_headers.iter_mut() {
699+
*value = substitute_env_vars(value, &os.env);
700+
}
701+
702+
// Verify environment variables were substituted
703+
assert_eq!(processed_headers.get("Authorization").unwrap(), "Bearer github_pat_test123");
704+
assert_eq!(processed_headers.get("X-API-Key").unwrap(), "secret_key_456");
705+
assert_eq!(processed_headers.get("Content-Type").unwrap(), "application/json");
706+
}
676707
}

0 commit comments

Comments
 (0)