mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
fix(mcp): lift Authorization headers into bearer credentials on import
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
177463e9b0
commit
0557a95259
2 changed files with 64 additions and 4 deletions
|
|
@ -119,7 +119,8 @@ def _convert_entry(name: str, entry: MCPConnectorEntry) -> ConvertedConnector |
|
|||
return ConnectorConversionError(name=name, error=f"Unsupported connector type '{entry.type}'.")
|
||||
|
||||
transport: Final = MCPTransport.sse if entry_type in _SSE_TYPES else MCPTransport.http
|
||||
credentials: Final = _bearer_credentials(entry.authorization_token)
|
||||
token: Final = entry.authorization_token or _header_bearer_token(entry.headers)
|
||||
static_headers: Final = entry.headers if entry.authorization_token else _non_auth_headers(entry.headers)
|
||||
try:
|
||||
remote_request: Final = NewMCPServerRequest(
|
||||
server_name=sanitized_name,
|
||||
|
|
@ -128,9 +129,9 @@ def _convert_entry(name: str, entry: MCPConnectorEntry) -> ConvertedConnector |
|
|||
approval_status=MCPApprovalStatus.active,
|
||||
transport=transport,
|
||||
url=entry.url,
|
||||
auth_type=MCPAuth.bearer_token if entry.authorization_token else MCPAuth.none,
|
||||
credentials=credentials,
|
||||
static_headers=dict(entry.headers) if entry.headers is not None else None,
|
||||
auth_type=MCPAuth.bearer_token if token else MCPAuth.none,
|
||||
credentials=_bearer_credentials(token),
|
||||
static_headers=dict(static_headers) if static_headers is not None else None,
|
||||
)
|
||||
except ValidationError as e:
|
||||
return ConnectorConversionError(name=name, error=_first_validation_message(e))
|
||||
|
|
@ -144,6 +145,24 @@ def _bearer_credentials(token: str | None) -> MCPCredentials | None:
|
|||
return credentials
|
||||
|
||||
|
||||
_BEARER_PREFIX: Final = "bearer "
|
||||
|
||||
|
||||
def _header_bearer_token(headers: Mapping[str, str] | None) -> str | None:
|
||||
values: Final = tuple(value for key, value in (headers or {}).items() if key.lower() == "authorization")
|
||||
if not values:
|
||||
return None
|
||||
value: Final = values[0]
|
||||
return value[len(_BEARER_PREFIX) :] if value.lower().startswith(_BEARER_PREFIX) else value
|
||||
|
||||
|
||||
def _non_auth_headers(headers: Mapping[str, str] | None) -> Mapping[str, str] | None:
|
||||
if headers is None:
|
||||
return None
|
||||
remaining: Final = {key: value for key, value in headers.items() if key.lower() != "authorization"}
|
||||
return remaining or None
|
||||
|
||||
|
||||
def _first_validation_message(error: ValidationError) -> str:
|
||||
messages: Final = tuple(str(detail.get("msg", "")) for detail in error.errors())
|
||||
return messages[0] if messages else str(error)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,47 @@ class TestConvertMcpServersMapping:
|
|||
assert result.request.static_headers == {"X-Env": "prod"}
|
||||
assert result.request.description == "GitHub connector"
|
||||
|
||||
def test_authorization_header_becomes_bearer_credentials(self):
|
||||
result = _single(
|
||||
{
|
||||
"mcpServers": {
|
||||
"srv": {
|
||||
"url": "https://x.example/mcp",
|
||||
"headers": {"Authorization": "Bearer header-token", "X-Env": "prod"},
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
assert isinstance(result, ConvertedConnector)
|
||||
assert result.request.auth_type == MCPAuth.bearer_token
|
||||
assert result.request.credentials == {"auth_value": "header-token"}
|
||||
assert result.request.static_headers == {"X-Env": "prod"}
|
||||
|
||||
def test_authorization_header_without_bearer_prefix(self):
|
||||
result = _single(
|
||||
{"mcpServers": {"srv": {"url": "https://x.example/mcp", "headers": {"authorization": "raw-token"}}}}
|
||||
)
|
||||
assert isinstance(result, ConvertedConnector)
|
||||
assert result.request.auth_type == MCPAuth.bearer_token
|
||||
assert result.request.credentials == {"auth_value": "raw-token"}
|
||||
assert result.request.static_headers is None
|
||||
|
||||
def test_authorization_token_wins_over_authorization_header(self):
|
||||
result = _single(
|
||||
{
|
||||
"mcpServers": {
|
||||
"srv": {
|
||||
"url": "https://x.example/mcp",
|
||||
"authorization_token": "explicit-token",
|
||||
"headers": {"Authorization": "Bearer header-token"},
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
assert isinstance(result, ConvertedConnector)
|
||||
assert result.request.credentials == {"auth_value": "explicit-token"}
|
||||
assert result.request.static_headers == {"Authorization": "Bearer header-token"}
|
||||
|
||||
def test_camel_case_authorization_token_alias(self):
|
||||
result = _single(
|
||||
{"mcpServers": {"srv": {"url": "https://x.example/mcp", "authorizationToken": "tok"}}}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue