litellm/tests/e2e/mcp/test_mcp_access_group_e2e.py
mubashir1osmani c274cf321c
test(e2e): poll MCP tools across multi-worker lag (#35047)
* fix(mcp): resolve call_tool by registry without requiring tool map

Multi-worker reloads put MCP servers in the registry from the DB but do
not re-run tools/list on every process. Gating call_tool on
tool_name_to_mcp_server_name_mapping made cold workers 500 with Tool not
found after another worker had already listed the tool. Treat a registry
match on server id/name/alias as enough; upstream rejects unknown tools

* test(e2e): poll MCP register, tools/list, and tools/call across multi-worker lag

Stage multi-worker gateways only load MCP servers and tool maps on the
process that handled the request. Poll until the server is listed, the
tool appears on tools/list, and tools/call is not a cold-worker 500 so
key-access and Datadog MCP e2e stop racing the LB

* Revert "fix(mcp): resolve call_tool by registry without requiring tool map"

This reverts commit 8b56e51e39.

* test(e2e): tighten MCP multi-worker lag classifier

Only retry tools/call on gateway shapes Tool <name> not found and
server_not_found, not any 500 that mentions tool/server not found, so
upstream failures are not retried until the poll deadline

* test(e2e): drop unit file for MCP lag classifier

The live await_call_tool polls already cover multi-worker lag; a separate
string-match unit module is not worth keeping
2026-07-28 22:15:21 -07:00

54 lines
2.1 KiB
Python

"""Live e2e: MCP tool selection via access group at key creation.
An admin registers the Datadog remote MCP server tagged with a server-side
access group (`mcp_access_groups`). A key minted with that access group
(`object_permission.mcp_access_groups`) sees the server's tools; a key minted
with a different group does not. This exercises access-group-scoped tool
selection, the enterprise MCP surface where keys are granted tool access groups
rather than explicit server ids.
A tools/list that leaks the server across the access-group boundary fails hard.
Requires DD_API_KEY + DD_APP_KEY (the suite's real MCP upstream).
"""
import pytest
from datadog_mcp import SEARCH_LOGS_TOOL, register_datadog_mcp
from e2e_config import unique_marker
from e2e_http import unwrap
from lifecycle import ResourceManager
from mcp_client import McpClient
pytestmark = pytest.mark.e2e
class TestMcpAccessGroupToolSelection:
@pytest.mark.covers("mcp.list_tools.api_key.access_group_scoped")
def test_access_group_scopes_tool_selection(
self, client: McpClient, resources: ResourceManager
) -> None:
group = f"e2e-mcp-grp-{unique_marker()}"
server_id = register_datadog_mcp(client, resources, mcp_access_groups=[group])
client.await_registered(server_id)
granted = client.generate_key(
user_id=f"e2e-mcp-ag-granted-{unique_marker()}",
mcp_servers=None,
mcp_access_groups=[group],
)
resources.defer(lambda: client.proxy.delete_key(granted))
other = client.generate_key(
user_id=f"e2e-mcp-ag-other-{unique_marker()}",
mcp_servers=None,
mcp_access_groups=[f"e2e-mcp-grp-absent-{unique_marker()}"],
)
resources.defer(lambda: client.proxy.delete_key(other))
_ = client.await_tool(granted, server_id, SEARCH_LOGS_TOOL)
other_tools = unwrap(client.list_tools(other)).tool_names_for_server(server_id)
assert other_tools == frozenset(), (
f"key with a different access group saw the server's tools; access-group tool "
f"selection leaked across the boundary: {other_tools}"
)