Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 7 additions & 18 deletions src/adapters/cursor/tool-definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,29 +91,18 @@ export function cursorToolChoiceAliases(tool: Pick<OcxTool, "namespace" | "name"
return [...aliases];
}

function catalogHasBareCodexShellBridge(
catalog: readonly Pick<OcxTool, "namespace" | "name">[],
): boolean {
return catalog.some(isBareCodexShellBridgeTool);
}

/**
* Catalog-aware tool_choice matching for Cursor.
* When a bare Codex shell bridge is in the catalog, raw `shell_command` / `exec_command`
* choices select only that bridge (never a namespaced remote with the same raw name).
* When no bare bridge exists, raw bridge names may select a namespaced tool by raw name.
* Tool-choice matching for Cursor.
* Raw `shell_command` / `exec_command` choices select only a bare Codex shell bridge
* (never a namespaced remote with the same raw name), and fail closed when absent.
* Explicit wire names (`mcp__remote__exec_command`) always match the namespaced tool.
*/
function cursorToolChoiceMatches(
tool: Pick<OcxTool, "namespace" | "name">,
choiceName: string,
catalog: readonly Pick<OcxTool, "namespace" | "name">[],
): boolean {
if (isCodexShellBridgeToolName(choiceName)) {
if (catalogHasBareCodexShellBridge(catalog)) {
return isBareCodexShellBridgeTool(tool);
}
return tool.name === choiceName || cursorToolWireName(tool) === choiceName;
return isBareCodexShellBridgeTool(tool);
}
if (tool.name === choiceName || cursorToolWireName(tool) === choiceName) return true;
return cursorToolChoiceAliases(tool).includes(choiceName);
Expand Down Expand Up @@ -371,14 +360,14 @@ export function cursorShellBridgeArgsValid(
export function cursorToolAllowedByChoice(
tool: Pick<OcxTool, "namespace" | "name">,
toolChoice: OcxRequestOptions["toolChoice"] | undefined,
catalog: readonly Pick<OcxTool, "namespace" | "name">[] = [tool],
_catalog: readonly Pick<OcxTool, "namespace" | "name">[] = [tool],
): boolean {
if (!toolChoice || toolChoice === "auto" || toolChoice === "required") return true;
if (toolChoice === "none") return false;
if ("allowedTools" in toolChoice) {
return toolChoice.allowedTools.some(choiceName => cursorToolChoiceMatches(tool, choiceName, catalog));
return toolChoice.allowedTools.some(choiceName => cursorToolChoiceMatches(tool, choiceName));
}
return cursorToolChoiceMatches(tool, toolChoice.name, catalog);
return cursorToolChoiceMatches(tool, toolChoice.name);
}

function quotedNames(names: readonly string[]): string {
Expand Down
13 changes: 7 additions & 6 deletions tests/cursor-tool-choice.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,21 +44,22 @@ describe("Cursor Responses tool_choice support", () => {
expect(buildCursorToolDefinitions(toolsWithRemote, { name: "mcp__remote__exec_command" }).map(tool => tool.toolName)).toEqual(["mcp__remote__exec_command"]);
});

test("raw exec_command selects namespaced remote when no bare shell bridge exists", () => {
test("raw exec_command fails closed when no bare shell bridge exists", () => {
const remoteOnly: OcxTool[] = [
{ name: "exec_command", namespace: "mcp__remote", description: "Remote exec", parameters: {} },
];
expect(buildCursorToolDefinitions(remoteOnly, { name: "exec_command" }).map(tool => tool.toolName)).toEqual(["mcp__remote__exec_command"]);
expect(buildCursorToolDefinitions(remoteOnly, { mode: "required", allowedTools: ["exec_command"] }).map(tool => tool.toolName)).toEqual(["mcp__remote__exec_command"]);
expect(buildCursorToolDefinitions(remoteOnly, { name: "exec_command" }).map(tool => tool.toolName)).toEqual([]);
expect(buildCursorToolDefinitions(remoteOnly, { mode: "required", allowedTools: ["exec_command"] }).map(tool => tool.toolName)).toEqual([]);
expect(buildCursorToolDefinitions(remoteOnly, { name: "mcp__remote__exec_command" }).map(tool => tool.toolName)).toEqual(["mcp__remote__exec_command"]);
});

test("raw shell_command selects namespaced remote when no bare shell bridge exists", () => {
test("raw shell_command fails closed when no bare shell bridge exists", () => {
const remoteOnly: OcxTool[] = [
{ name: "shell_command", namespace: "mcp__remote", description: "Remote shell", parameters: {} },
];
expect(buildCursorToolDefinitions(remoteOnly, { name: "shell_command" }).map(tool => tool.toolName)).toEqual(["mcp__remote__shell_command"]);
expect(buildCursorToolDefinitions(remoteOnly, { mode: "required", allowedTools: ["shell_command"] }).map(tool => tool.toolName)).toEqual(["mcp__remote__shell_command"]);
expect(buildCursorToolDefinitions(remoteOnly, { name: "shell_command" }).map(tool => tool.toolName)).toEqual([]);
expect(buildCursorToolDefinitions(remoteOnly, { mode: "required", allowedTools: ["shell_command"] }).map(tool => tool.toolName)).toEqual([]);
expect(buildCursorToolDefinitions(remoteOnly, { name: "mcp__remote__shell_command" }).map(tool => tool.toolName)).toEqual(["mcp__remote__shell_command"]);
});

test("parser preserves parallel_tool_calls false for Cursor request enforcement", () => {
Expand Down
Loading