Skip to content

Commit bb86fef

Browse files
authored
added auth (#308)
1 parent 5adf5f2 commit bb86fef

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

eval_protocol/mcp/mcp_multi_client.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,38 @@ def _validate_environment_variables(self, server_name: str, required_env: List[s
7070
f"Please set these variables in your environment or .env file."
7171
)
7272

73+
def _process_headers(self, headers: Dict[str, str]) -> Dict[str, str]:
74+
"""Process headers by substituting environment variables.
75+
76+
Supports environment variable substitution in the format:
77+
- ${ENV_VAR} or $ENV_VAR for environment variables
78+
- Raw strings are passed through unchanged
79+
80+
Example:
81+
{"Authorization": "Bearer ${API_KEY}"}
82+
-> {"Authorization": "Bearer abc123"} (if API_KEY=abc123)
83+
"""
84+
import re
85+
86+
processed_headers = {}
87+
for key, value in headers.items():
88+
# Match ${VAR} or $VAR patterns
89+
def replace_env_var(match):
90+
var_name = match.group(1) or match.group(2)
91+
env_value = os.environ.get(var_name)
92+
if env_value is None:
93+
raise ValueError(
94+
f"Environment variable '{var_name}' referenced in header '{key}' "
95+
f"is not set. Please set it in your environment or .env file."
96+
)
97+
return env_value
98+
99+
# Replace ${VAR} or $VAR with environment variable value
100+
processed_value = re.sub(r"\$\{([^}]+)\}|\$([A-Za-z_][A-Za-z0-9_]*)", replace_env_var, value)
101+
processed_headers[key] = processed_value
102+
103+
return processed_headers
104+
73105
async def connect_to_servers(self):
74106
"""Connect to all configured MCP servers"""
75107
if not self.config.mcpServers:
@@ -111,8 +143,17 @@ async def _connect_to_server(
111143
if not url:
112144
raise ValueError(f"Server '{server_name}' must have a 'url' specified")
113145

114-
# Connect using streamable HTTP client - manage resources manually
115-
http_transport = await self.exit_stack.enter_async_context(streamablehttp_client(url))
146+
# Build headers only from the authorization field (Responses-style)
147+
processed_headers: Dict[str, str] = {}
148+
auth_token = getattr(server_config, "authorization", None)
149+
if auth_token:
150+
# Support env substitution in the authorization value as well
151+
processed_headers = self._process_headers({"Authorization": auth_token})
152+
153+
# Connect using streamable HTTP client with auth headers
154+
http_transport = await self.exit_stack.enter_async_context(
155+
streamablehttp_client(url, headers=processed_headers)
156+
)
116157
read_stream, write_stream, get_session_id = http_transport
117158
session = await self.exit_stack.enter_async_context(ClientSession(read_stream, write_stream))
118159
else:

eval_protocol/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ class MCPConfigurationServerUrl(BaseModel):
11191119
"""Represents a Remote MCP configuration server."""
11201120

11211121
url: str # url to the MCP server
1122+
authorization: Optional[str] = None
11221123

11231124

11241125
class MCPMultiClientConfiguration(BaseModel):

0 commit comments

Comments
 (0)