{server.description}
} + {grantsAll && ( ++ All tools allowed, including tools added to this server later +
+ )} {entry.ambiguousKeys.length > 0 && ({`Also granted by ${entry.ambiguousKeys.map((key) => `"${key}"`).join(", ")}, which names another server too. Those tools stay allowed here until the servers no longer share that name`} diff --git a/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.test.ts b/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.test.ts index 487c6f9f55e..07f2b0e2508 100644 --- a/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.test.ts +++ b/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.test.ts @@ -4,6 +4,7 @@ import { applyToolPermissionWrite, emptyMcpAccessGroups, mcpAllowedToolsFor, + mcpGrantsAllTools, mcpServersForIdentifier, mcpToolPermissionKeyFor, resolveEffectiveMcpServers, @@ -66,6 +67,16 @@ describe("mcpServersForIdentifier", () => { }); }); +describe("mcpGrantsAllTools", () => { + it("is true only when the union carries the wildcard, never for an absent grant", () => { + expect(mcpGrantsAllTools(["*"])).toBe(true); + expect(mcpGrantsAllTools(["read_file", "*"])).toBe(true); + expect(mcpGrantsAllTools(["read_file"])).toBe(false); + expect(mcpGrantsAllTools([])).toBe(false); + expect(mcpGrantsAllTools(undefined)).toBe(false); + }); +}); + describe("mcpToolPermissionKeyFor", () => { const target = server({ server_id: "uuid-1", server_name: "github_mcp", alias: "GitHub" }); diff --git a/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts b/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts index b3ba24f3c59..c9e85fef31b 100644 --- a/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts +++ b/ui/litellm-dashboard/src/components/mcp_server_management/effectiveMcpServers.ts @@ -1,5 +1,6 @@ import { z } from "zod/v4"; import { MCPServer, MCPToolset } from "../mcp_tools/types"; +import { MCP_ALL_TOOLS_WILDCARD } from "../mcp_tools/constants"; // Mirrors the backend resolver's union (direct + access_group + tool_perm + toolset), so the // editor shows exactly the servers this permission level entitles. @@ -121,6 +122,12 @@ export const mcpAllowedToolsFor = ( return [...new Set(keys.flatMap((key) => toolPermissions[key] ?? []))]; }; +// An allowed-tools union carrying the wildcard grants every current and future tool on the +// server; `undefined` (no entry at all) is unrestricted for a different reason and is not a +// wildcard grant the editor should expand. +export const mcpGrantsAllTools = (allowed: readonly string[] | undefined): boolean => + allowed !== undefined && allowed.includes(MCP_ALL_TOOLS_WILDCARD); + // Tool names the given toolsets grant on this server, `undefined` when they grant none. const mcpToolsetToolsFor = ( server: MCPServer, diff --git a/ui/litellm-dashboard/src/components/mcp_tools/constants.ts b/ui/litellm-dashboard/src/components/mcp_tools/constants.ts index 66ab1a352f4..eef98383d2a 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/constants.ts +++ b/ui/litellm-dashboard/src/components/mcp_tools/constants.ts @@ -3,5 +3,8 @@ export const NO_MCP_SERVERS_SENTINEL = "no-mcp-servers"; export const ALL_PROXY_MCP_SERVERS_SENTINEL = "all-proxy-mcpservers"; +// Must match the backend MCP_ALL_TOOLS_WILDCARD constant in litellm/constants.py. +export const MCP_ALL_TOOLS_WILDCARD = "*"; + export const MCP_TOOLS_PREVIEW_FORBIDDEN_MESSAGE = "Tool preview is not available for submissions. Tools will be verified by an admin during review.";