Skip to content

Bridge credential timeout (5s) too short when macOS Keychain prompts for password #55

@chocholous

Description

@chocholous

Bug

mcpc connect and mcpc restart fail with ENOENT on the bridge socket when macOS Keychain access requires user password confirmation (interactive Keychain dialog).

Root cause

In dist/bridge/index.js:185-186, the bridge waits only 5 seconds for auth credentials:

const timeout = new Promise((_, reject) => {
    setTimeout(() => reject(new Error('Timeout waiting for auth credentials')), 5000);
});
await Promise.race([this.authCredentialsReceived, timeout]);

But credentials are read from macOS Keychain after the bridge starts (dist/lib/bridge-manager.js:61-72):

// 1. Bridge spawned, socket created
await waitForFile(socketPath, { timeoutMs: 5000 });  // ← bridge is ready

// 2. NOW read Keychain (triggers macOS password dialog!)
await sendAuthCredentialsToBridge(socketPath, ...);
//    └── readKeychainOAuthTokenInfo()  ← macOS prompt
//    └── readKeychainOAuthClientInfo() ← macOS prompt

Timeline:

  1. Bridge starts, creates socket, starts 5s credential timer
  2. Main process detects socket ✓
  3. Main process calls readKeychainOAuthTokenInfo()macOS dialog appears
  4. User reads dialog, types password → 5-15 seconds
  5. Bridge 5s timer fires → bridge shuts down → socket removed
  6. Main process gets Keychain value, tries connect(socket)ENOENT

Why "Always Allow" works

When user clicks "Always Allow" in macOS Keychain dialog, node is added to the Keychain entry ACL permanently. Subsequent getPassword() calls return instantly (~1ms) with no dialog → credentials reach bridge well within 5s.

Steps to reproduce

  1. Clear Keychain access for node to mcpc entries: Keychain Access.app → find mcpc entries → Get Info → Access Control → remove node
  2. Run mcpc mcp.apify.com connect @session
  3. When macOS prompts for password, wait a few seconds before entering → ENOENT

Suggested fixes (in order of preference)

Option A: Read credentials before starting bridge (best)

Read Keychain in the main process before spawning the bridge, then pass credentials immediately after socket is ready. Zero delay.

// In startBridge():
const credentials = await readCredentialsFromKeychain(...);  // ← prompts happen HERE
const bridgeProcess = spawn('node', [bridgeExecutable, ...args], ...);
await waitForFile(socketPath, { timeoutMs: 5000 });
await sendCredentialsToBridge(socketPath, credentials);  // ← instant, no Keychain

Option B: Increase credential timeout

Change 5000ms to 60000ms in bridge/index.js:186. Simple but doesn't address the architectural issue.

Option C: Bridge waits indefinitely

Remove the timeout — bridge waits for credentials until main process sends them or bridge is killed.

Environment

  • mcpc: 0.1.10
  • OS: macOS 26.3 (arm64)
  • Keychain: @napi-rs/keyring → macOS Keychain
  • Context: Claude Code plugin running mcpc commands

Updated: Original report incorrectly identified ensureBridgeReady as the cause. After deeper analysis, the actual issue is the 5s credential timeout in the bridge process vs macOS Keychain interactive prompts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions