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 => );
}
}