Modified 9Router's callback page to call the exchange endpoint directly
instead of relying on postMessage/BroadcastChannel relay (which fails
in Electron where window.opener is null).
Flow: click Connect → sign in → callback page fetches pending state
from our backend → calls 9Router exchange → connection saved → done.
Backend:
- Added GET /api/subscriptions/pending/{state} with CORS for callback
- Added GET /api/subscriptions/callback as fallback
- Store pending OAuth state (codeVerifier, redirectUri) keyed by state
- redirect_uri stays on localhost:20128 (required by Anthropic)
Frontend:
- Popup window for auth URL + postMessage listener + status polling
- Works in both Electron and regular browser
Performance fix:
- MCP servers no longer spawned upfront for every message
- Tool schemas loaded from cached metadata (instant, no subprocess)
- MCP servers only connected when their tools are first called (lazy)
- Browser agent MCP server still connects upfront (lightweight)
- Reduces startup from 30+ seconds to <1 second for simple messages
Browser agent 9Router support:
- Browser agent now works through 9Router when no API key set
- Uses OpenAI client format when base_url provided
- Model ID mapping for 9Router (cc/ prefix)
- Passes auth_token and base_url through run_browser_agents
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>