mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-25 01:02:15 +00:00
fix(mcp): preserve scopes through admin server edits
This commit is contained in:
parent
9cff026bae
commit
f59cd303c6
4 changed files with 38 additions and 12 deletions
|
|
@ -7088,6 +7088,7 @@ class MCPServerManager:
|
|||
spec_path=server.spec_path,
|
||||
transport=server.transport,
|
||||
auth_type=server.auth_type,
|
||||
credentials={"scopes": server.scopes} if server.scopes else None,
|
||||
created_at=server.created_at,
|
||||
updated_at=server.updated_at,
|
||||
teams=[],
|
||||
|
|
|
|||
|
|
@ -849,7 +849,9 @@ if MCP_AVAILABLE:
|
|||
if not credentials:
|
||||
return False
|
||||
as_dict: Final[dict[str, object]] = dict(credentials)
|
||||
return any(value for key, value in as_dict.items() if key not in MCP_ADMIN_CONFIG_CREDENTIAL_KEYS)
|
||||
return any(
|
||||
value for key, value in as_dict.items() if key not in MCP_ADMIN_CONFIG_CREDENTIAL_KEYS and key != "scopes"
|
||||
)
|
||||
|
||||
def _inherit_credentials_from_existing_server(
|
||||
payload: NewMCPServerRequest,
|
||||
|
|
|
|||
|
|
@ -10424,11 +10424,15 @@ def test_build_mcp_server_table_carries_oauth2_flow():
|
|||
transport=MCPTransport.http,
|
||||
auth_type=MCPAuth.oauth2,
|
||||
oauth2_flow="client_credentials",
|
||||
client_id="client-123",
|
||||
client_secret="secret-xyz",
|
||||
scopes=["scope:a", "scope:b"],
|
||||
)
|
||||
|
||||
table = manager._build_mcp_server_table(server)
|
||||
|
||||
assert table.oauth2_flow == "client_credentials"
|
||||
assert table.credentials == {"scopes": ["scope:a", "scope:b"]}
|
||||
|
||||
|
||||
def test_build_mcp_server_table_carries_null_oauth2_flow():
|
||||
|
|
|
|||
|
|
@ -1704,14 +1704,26 @@ class TestTemporaryMCPSessionEndpoints:
|
|||
|
||||
return _inherit_credentials_from_existing_server(payload)
|
||||
|
||||
def test_admin_config_alone_does_not_suppress_credential_inheritance(self):
|
||||
"""The edit form round-trips upstream_resource, which is admin config rather than a credential.
|
||||
Treating the blob as "credentials supplied" left the Authorize session with no declared app on
|
||||
the exact path where this knob is configured."""
|
||||
updated = self._inherit_with({"upstream_resource": "api://audience"})
|
||||
@pytest.mark.parametrize(
|
||||
"credentials",
|
||||
[
|
||||
{"upstream_resource": "api://audience"},
|
||||
{"scopes": ["scope:a", "scope:b"]},
|
||||
{"scopes": ["scope:edited"], "upstream_resource": "api://audience"},
|
||||
{"scopes": ["scope:edited"], "upstream_token_header": "esb-oauth"},
|
||||
{"scopes": []},
|
||||
{"scopes": None},
|
||||
],
|
||||
)
|
||||
def test_admin_config_alone_does_not_suppress_credential_inheritance(self, credentials: MCPCredentials):
|
||||
updated = self._inherit_with(credentials, scopes=["scope:stored"])
|
||||
|
||||
assert updated.credentials["client_id"] == "client-123"
|
||||
assert updated.credentials["client_secret"] == "secret-xyz"
|
||||
assert updated.credentials == {
|
||||
"client_id": "client-123",
|
||||
"client_secret": "secret-xyz",
|
||||
"scopes": ["scope:stored"],
|
||||
**credentials,
|
||||
}
|
||||
|
||||
def test_upstream_token_header_is_inherited_like_other_admin_config(self):
|
||||
"""It is admin config rather than a credential, so a session server derived from an existing
|
||||
|
|
@ -1730,11 +1742,18 @@ class TestTemporaryMCPSessionEndpoints:
|
|||
assert updated.credentials["client_secret"] == "secret-xyz"
|
||||
assert updated.credentials["upstream_token_header"] == "esb-oauth"
|
||||
|
||||
def test_supplied_credential_still_wins_over_inheritance(self):
|
||||
"""A caller that supplies a real credential keeps it; inheritance must not overwrite it."""
|
||||
updated = self._inherit_with({"auth_value": "caller-token"})
|
||||
@pytest.mark.parametrize(
|
||||
"credentials",
|
||||
[
|
||||
{"auth_value": "caller-token"},
|
||||
{"client_id": "caller-client", "scopes": ["scope:edited"]},
|
||||
{"client_secret": "caller-secret", "scopes": ["scope:edited"]},
|
||||
],
|
||||
)
|
||||
def test_supplied_credential_still_wins_over_inheritance(self, credentials: MCPCredentials):
|
||||
updated = self._inherit_with(credentials)
|
||||
|
||||
assert updated.credentials == {"auth_value": "caller-token"}
|
||||
assert updated.credentials == credentials
|
||||
|
||||
def test_inheritance_carries_upstream_resource_to_the_session_server(self):
|
||||
"""Without this the temporary server omits the resource indicator and the Authorize leg it
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue