diff --git a/src/__tests__/bugfixes.test.ts b/src/__tests__/bugfixes.test.ts index edaa2715..863f3639 100644 --- a/src/__tests__/bugfixes.test.ts +++ b/src/__tests__/bugfixes.test.ts @@ -288,3 +288,30 @@ describe('Context overflow 500 fix', () => { expect(content).toContain('automatic compaction has failed') }) }) + +// --------------------------------------------------------------------------- +// Fix N: Project-scope MCP servers from .mcp.json not detected for 3P providers (issue #696) +// --------------------------------------------------------------------------- +describe('Project-scope MCP approval — third-party providers (issue #696)', () => { + test('handleMcpjsonServerApprovals is NOT gated behind usesAnthropicSetup', async () => { + const content = await file('interactiveHelpers.tsx').text() + + // The call site for handleMcpjsonServerApprovals must not sit inside an + // `if (usesAnthropicSetup) { ... }` block, or third-party providers will + // never get the dialog and project-scope .mcp.json servers will be silently + // dropped from /mcp listings (issue #696). + const approvalCallIdx = content.indexOf('await handleMcpjsonServerApprovals(root)') + expect(approvalCallIdx).toBeGreaterThan(-1) + + // Look at the 800 chars BEFORE the call site for any `if (usesAnthropicSetup)` + // block that would still be open. Pick a window that's definitely inside the + // showSetupScreens function but not in earlier dialogs. + const before = content.slice(Math.max(0, approvalCallIdx - 800), approvalCallIdx) + expect(before).not.toMatch(/if\s*\(\s*usesAnthropicSetup\s*\)\s*{[^}]*$/) + }) + + test('issue #696 is referenced from the comment so future readers can find context', async () => { + const content = await file('interactiveHelpers.tsx').text() + expect(content).toContain('#696') + }) +}) diff --git a/src/interactiveHelpers.tsx b/src/interactiveHelpers.tsx index 60ee7c16..c655f38e 100644 --- a/src/interactiveHelpers.tsx +++ b/src/interactiveHelpers.tsx @@ -158,24 +158,25 @@ export async function showSetupScreens(root: Root, permissionMode: PermissionMod // Now that trust is established, prefetch system context if it wasn't already void getSystemContext(); - // Skip MCP approval dialogs for third-party providers (no interactive auth prompts) - if (usesAnthropicSetup) { - // If settings are valid, check for any mcp.json servers that need approval - const { - errors: allErrors - } = getSettingsWithAllErrors(); - if (allErrors.length === 0) { - await handleMcpjsonServerApprovals(root); - } + // MCP approval and external-includes warnings are about workspace + // trust, not about Anthropic auth. They must run for all providers + // — including third-party — otherwise project-scoped .mcp.json + // servers never get the approval that writes + // enableAllProjectMcpServers / enabledMcpjsonServers into + // settings.local.json, and the servers are silently dropped from + // /mcp and `mcp list` (issue #696). + const { errors: allErrors } = getSettingsWithAllErrors(); + if (allErrors.length === 0) { + await handleMcpjsonServerApprovals(root); + } - // Check for claude.md includes that need approval - if (await shouldShowClaudeMdExternalIncludesWarning()) { - const externalIncludes = getExternalClaudeMdIncludes(await getMemoryFiles(true)); - const { - ClaudeMdExternalIncludesDialog - } = await import('./components/ClaudeMdExternalIncludesDialog.js'); - await showSetupDialog(root, done => ); - } + // Check for claude.md includes that need approval + if (await shouldShowClaudeMdExternalIncludesWarning()) { + const externalIncludes = getExternalClaudeMdIncludes(await getMemoryFiles(true)); + const { + ClaudeMdExternalIncludesDialog + } = await import('./components/ClaudeMdExternalIncludesDialog.js'); + await showSetupDialog(root, done => ); } }