diff --git a/open-sse/services/claudeCodeToolRemapper.ts b/open-sse/services/claudeCodeToolRemapper.ts index 9ab697a100..754ecc978a 100644 --- a/open-sse/services/claudeCodeToolRemapper.ts +++ b/open-sse/services/claudeCodeToolRemapper.ts @@ -194,6 +194,12 @@ function toPascalCaseToolName(name: string): string { export function needsThirdPartyCloak(name: string): boolean { if (!name) return false; if (CLAUDE_BUILTIN_TOOL_NAMES.has(name)) return false; + // `mcp____` names are genuine Claude Code MCP tool names that + // Anthropic accepts natively. Cloaking them to PascalCase is unnecessary and, + // via round-trip asymmetry (a history tool_use keeping the original name while + // tools[] is cloaked), produces "Tool reference 'mcp__…' not found in available + // tools" 400s on the native claude OAuth path. Leave the MCP namespace alone. + if (name.startsWith("mcp__")) return false; return /[a-z]/.test(name.charAt(0)) || name.includes("_") || name.includes("-"); } diff --git a/src/sse/handlers/chat.ts b/src/sse/handlers/chat.ts index 58f732adc3..03ef7f5d05 100644 --- a/src/sse/handlers/chat.ts +++ b/src/sse/handlers/chat.ts @@ -1013,7 +1013,7 @@ async function handleSingleModelChat( ); preselectedCredentials = null; - if (!credentials || "allRateLimited" in credentials) { + if (!credentials || "allRateLimited" in credentials || !credentials.connectionId) { if (credentials?.allRateLimited) { const retryDecision = getCooldownAwareRetryDecision({ retryAfter: credentials.retryAfter, diff --git a/tests/unit/claude-oauth-tool-cloak.test.ts b/tests/unit/claude-oauth-tool-cloak.test.ts index 2fdb3c71b5..46bffc1d90 100644 --- a/tests/unit/claude-oauth-tool-cloak.test.ts +++ b/tests/unit/claude-oauth-tool-cloak.test.ts @@ -109,6 +109,14 @@ describe("cloakThirdPartyToolNames", () => { assert.equal(needsThirdPartyCloak("mixture_of_agents"), true); }); + it("needsThirdPartyCloak leaves mcp__ namespace untouched (#4861)", () => { + // Genuine Claude Code MCP names Anthropic accepts natively; cloaking them + // caused round-trip "Tool reference 'mcp__…' not found" 400s on claude OAuth. + assert.equal(needsThirdPartyCloak("mcp__filesystem__read_file"), false); + assert.equal(needsThirdPartyCloak("mcp__github__create_issue"), false); + assert.equal(needsThirdPartyCloak("mcp__server"), false); + }); + it("preserves the reserved name of a versioned Anthropic server tool", () => { const body: AnyRecord = { tools: [{ type: "web_search_20250305", name: "web_search", max_uses: 5 }],