mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-14 09:03:16 +00:00
fix(ui): keep provider default restrictions and mark toolset provider tools
Provider servers resolve their tools synchronously, so every default write in one pass read the same stale permissions ref and the last one silently dropped the others' delete-blocked defaults. Advance the ref before each write. The toolset editor now tags provider-native servers as permission only, and the usage guide says their tools are not served at the toolset URL.
This commit is contained in:
parent
d8185a5066
commit
81895d7bc1
5 changed files with 49 additions and 6 deletions
|
|
@ -458,6 +458,7 @@ it("creates a provider toolset from registration choices without MCP discovery",
|
|||
await openCreate(user);
|
||||
fireEvent.change(screen.getByPlaceholderText("e.g. github-linear-tools"), { target: { value: "native_research" } });
|
||||
await user.click(await screen.findByText("native_docs"));
|
||||
expect(screen.getByText("provider · permission only")).toBeInTheDocument();
|
||||
await user.click(await screen.findByRole("checkbox", { name: /^search/ }));
|
||||
await user.click(screen.getByRole("button", { name: "Create Toolset" }));
|
||||
await waitFor(() =>
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ const selectedToolIssue = (
|
|||
interface ServerToolGroupProps {
|
||||
serverId: string;
|
||||
serverName: string;
|
||||
providerNative: boolean;
|
||||
state: MCPServerToolsState;
|
||||
visibleTools: readonly MCPServerToolEntry[];
|
||||
expanded: boolean;
|
||||
|
|
@ -146,6 +147,7 @@ interface ServerToolGroupProps {
|
|||
function ServerToolGroup({
|
||||
serverId,
|
||||
serverName,
|
||||
providerNative,
|
||||
state,
|
||||
visibleTools,
|
||||
expanded,
|
||||
|
|
@ -235,6 +237,11 @@ function ServerToolGroup({
|
|||
>
|
||||
<span className="text-sm font-medium text-foreground flex items-center gap-2">
|
||||
{serverName}
|
||||
{providerNative && (
|
||||
<span className="text-xs font-normal text-muted-foreground">
|
||||
{t("mcpServers.toolsetsTab.serverGroup.providerPermissionOnly")}
|
||||
</span>
|
||||
)}
|
||||
{selectedCount > 0 && (
|
||||
<span className="ml-1 text-xs text-purple-600 font-semibold dark:text-purple-400">
|
||||
{t("mcpServers.toolsetsTab.serverGroup.selectedCount", { count: selectedCount })}
|
||||
|
|
@ -429,6 +436,7 @@ function CreateToolsetModal({ open, onClose, onSave, initialToolset }: CreateToo
|
|||
key={server.server_id}
|
||||
serverId={server.server_id}
|
||||
serverName={serverName}
|
||||
providerNative={server.execution_mode === "provider"}
|
||||
state={state}
|
||||
visibleTools={visibleTools}
|
||||
expanded={searchActive || expandedServerIds.has(server.server_id)}
|
||||
|
|
|
|||
|
|
@ -1154,3 +1154,35 @@ it("selects stored provider tools and reflects registration edits without discov
|
|||
expect(screen.getByRole("checkbox", { name: "fetch" })).toBeInTheDocument();
|
||||
expect(networking.listMCPTools).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps the delete-blocked default for every provider server that loads in the same pass", async () => {
|
||||
vi.clearAllMocks();
|
||||
testQueryClient.clear();
|
||||
vi.mocked(networking.fetchMCPAccessGroups).mockResolvedValue([]);
|
||||
vi.mocked(networking.fetchMCPToolsets).mockResolvedValue([]);
|
||||
const provider = (server_id: string, allowed_tools: string[]) => ({
|
||||
server_id,
|
||||
server_name: server_id,
|
||||
execution_mode: "provider" as const,
|
||||
allowed_tools,
|
||||
created_at: "",
|
||||
created_by: "",
|
||||
updated_at: "",
|
||||
updated_by: "",
|
||||
});
|
||||
vi.mocked(networking.fetchMCPServers).mockResolvedValue([
|
||||
provider("docs", ["search", "delete"]),
|
||||
provider("files", ["read", "purge"]),
|
||||
]);
|
||||
const changed = vi.fn();
|
||||
renderWithProviders(
|
||||
<MCPToolPermissions
|
||||
accessToken="test"
|
||||
selectedServers={["docs", "files"]}
|
||||
toolPermissions={{}}
|
||||
onChange={changed}
|
||||
/>,
|
||||
);
|
||||
await waitFor(() => expect(changed).toHaveBeenCalledTimes(2));
|
||||
expect(changed).toHaveBeenLastCalledWith({ docs: ["search"], files: ["read"] }, "default");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -172,10 +172,11 @@ const MCPToolPermissions: React.FC<MCPToolPermissionsProps> = ({
|
|||
const nonDeleteTools = fetchedTools
|
||||
.filter((t) => classifyToolOp(t.name, t.description || "") !== "delete")
|
||||
.map((t) => t.name);
|
||||
onChange(
|
||||
applyToolPermissionWrite({ toolPermissions: latestPermissions, entry, allowed: nonDeleteTools }),
|
||||
"default",
|
||||
);
|
||||
const next = applyToolPermissionWrite({ toolPermissions: latestPermissions, entry, allowed: nonDeleteTools });
|
||||
// Provider servers resolve synchronously in one pass, before the parent re-renders,
|
||||
// so advance the ref here or the next server's default overwrites this one.
|
||||
toolPermissionsRef.current = next;
|
||||
onChange(next, "default");
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -10259,7 +10259,8 @@
|
|||
"noTools": "No tools found for this server.",
|
||||
"selectMatching": "Select matching tools on {{serverName}}",
|
||||
"selectAll": "Select all tools on {{serverName}}",
|
||||
"selectedCount": "{{count}} selected"
|
||||
"selectedCount": "{{count}} selected",
|
||||
"providerPermissionOnly": "provider · permission only"
|
||||
},
|
||||
"modal": {
|
||||
"editTitle": "Edit Toolset",
|
||||
|
|
@ -10295,7 +10296,7 @@
|
|||
},
|
||||
"usageGuide": {
|
||||
"title": "How toolsets work",
|
||||
"body": "Create a toolset, assign it to a key via <menu>API Keys → Edit Key → MCP Servers</menu>, then point your MCP client at the toolset URL. The client only sees the tools you picked.",
|
||||
"body": "Create a toolset, assign it to a key via <menu>API Keys → Edit Key → MCP Servers</menu>, then point your MCP client at the toolset URL. The client only sees the tools you picked. Tools from provider-native registrations are permission grants for provider-native requests only and are not served at the toolset URL.",
|
||||
"configLabel": "Claude Code / Cursor config",
|
||||
"copy": "copy"
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue