Summary
The current MCP integration is a minimal HTTP-only gateway: users manually add an MCP server URL + optional bearer token, and the agent calls it via JSON-RPC over HTTP. This works for the rare remote/hosted MCP server, but most real-world MCP servers are stdio-based (Gmail, Slack, Granola, Brave Search, filesystem, etc.) and unreachable from the Excel WebView.
For non-technical AI power users — the target audience — the current UX is a dead end. They can't use the MCPs they know from Claude Desktop or Claude Code.
Goal: make MCP a "just works" experience where enabling a popular MCP server requires minimal setup (ideally just an API key or OAuth), no extra processes beyond the CORS proxy, and no restart.
Current architecture
Excel WebView (browser)
│
│ POST JSON-RPC to server URL
│
├── direct ──────────────→ Remote HTTP MCP server (rare)
│
└── via CORS proxy ──────→ localhost:3003 ──→ Remote HTTP MCP server
(proxy.enabled=true) cors-proxy-server.mjs
Limitations:
- HTTP-only transport — can't reach stdio MCP servers (the vast majority)
- CORS proxy host allowlist —
DEFAULT_ALLOWED_TARGET_HOSTS only includes LLM provider domains; arbitrary MCP server hosts are blocked
- No hot-reload — adding a server mid-session requires config + proxy restart
- Manual-only setup — user must know the exact server URL and token format
Proposed architecture: MCP Hub in the CORS proxy
Extend cors-proxy-server.mjs into an MCP hub that manages stdio server lifecycles:
Excel WebView
│
│ POST /mcp/{server-id}/rpc (tool calls)
│ GET /mcp/servers (discovery)
│
▼
┌────────────────────────────────────────┐
│ localhost:3003 (existing proxy) │
│ │
│ Existing: /?url=... (CORS proxy) │
│ │
│ New: MCP Hub layer │
│ ┌──────────┐ ┌──────────┐ │
│ │ gmail │ │ slack │ ... │
│ │ (stdio) │ │ (stdio) │ │
│ └──────────┘ └──────────┘ │
│ │
│ Config: mcp-servers.json │
│ (file-watched for hot-reload) │
└────────────────────────────────────────┘
Config format (mcp-servers.json)
{
"gmail": {
"command": "npx",
"args": ["-y", "@anthropic/gmail-mcp"],
"env": { "GMAIL_OAUTH_CREDENTIALS": "~/.config/gmail-oauth.json" }
},
"slack": {
"command": "npx",
"args": ["-y", "@anthropic/slack-mcp"],
"env": { "SLACK_BOT_TOKEN": "xoxb-..." }
},
"brave-search": {
"command": "npx",
"args": ["-y", "@anthropic/brave-search-mcp"],
"env": { "BRAVE_API_KEY": "..." }
}
}
Key design decisions
Lazy spawn: Don't start all servers on boot. Spawn a child process on first connect or tool call. This keeps startup fast and avoids wasted processes.
File-watch hot-reload: fs.watch("mcp-servers.json") detects changes → diffs config → spawns new servers, kills removed ones. No proxy restart needed to add/remove servers mid-session.
Stdio ↔ HTTP translation: The hub receives HTTP JSON-RPC from the WebView, writes it as newline-delimited JSON to the child's stdin, reads the response from stdout, and returns it as HTTP.
CORS is a non-issue: All /mcp/ routes are loopback through the proxy the user already trusts. No host allowlist changes needed.
Management API (stretch): POST /mcp/servers / DELETE /mcp/servers/{id} so the Excel UI can add/remove servers without the user touching JSON.
Ship a curated mcp-servers.example.json
Pre-configure ~8 popular servers (commented out / disabled by default) so users just uncomment and add credentials:
| Category |
Server |
Package |
Auth |
| Email |
Gmail |
@anthropic/gmail-mcp |
OAuth |
| Files |
Google Drive |
@anthropic/gdrive-mcp |
OAuth |
| Calendar |
Google Calendar |
@anthropic/gcalendar-mcp |
OAuth |
| Chat |
Slack |
@anthropic/slack-mcp |
Bot token |
| Search |
Brave Search |
@anthropic/brave-search-mcp |
API key |
| Notes |
Notion |
@anthropic/notion-mcp |
Integration token |
| Files |
Filesystem |
@anthropic/filesystem-mcp |
None |
| Data |
SQLite |
@anthropic/sqlite-mcp |
None |
(Exact package names TBD — verify against the MCP server registry before implementation.)
Phases
Phase 1 — Stdio bridge in the proxy
/mcp/{server-id}/rpc route in cors-proxy-server.mjs
- Child process spawn/lifecycle management (lazy start, graceful shutdown)
mcp-servers.json config loading
fs.watch hot-reload
GET /mcp/servers discovery endpoint
- Ship
mcp-servers.example.json with curated popular servers
Phase 2 — Wire Excel client
mcp tool auto-discovers servers from GET /mcp/servers (bridge-managed servers appear alongside manually-added HTTP servers)
/integrations UI shows bridge-managed servers with status (running/stopped/error)
- "Test connection" triggers lazy spawn + MCP handshake
Phase 3 — Management API + zero-JSON UX
POST /mcp/servers / DELETE /mcp/servers/{id} for CRUD from Excel UI
- Browse/search a curated server catalog from the
/integrations overlay
- One-click enable with inline credential prompts
- Auto-install npm packages on first use
Out of scope (for now)
- OAuth broker in the proxy (users handle OAuth setup per-server for now)
- SSE / Streamable HTTP transport for remote servers (existing HTTP path covers this)
- Sandboxing / capability restrictions on stdio servers
Context
- Current MCP tool:
src/tools/mcp.ts (HTTP JSON-RPC only)
- Config store:
src/tools/mcp-config.ts (mcp.servers.v1 settings key)
- CORS proxy:
scripts/cors-proxy-server.mjs
- Proxy target policy:
scripts/proxy-target-policy.mjs
- Integration catalog:
src/integrations/catalog.ts
- UI overlay:
src/commands/builtins/integrations-overlay.ts + integrations-overlay-elements.ts
- MCP probe (test connection):
src/commands/builtins/integrations-overlay-mcp-probe.ts
- Agent Skill:
skills/mcp-gateway/SKILL.md
- Docs:
docs/integrations-external-tools.md, docs/agent-skills-interop.md
Summary
The current MCP integration is a minimal HTTP-only gateway: users manually add an MCP server URL + optional bearer token, and the agent calls it via JSON-RPC over HTTP. This works for the rare remote/hosted MCP server, but most real-world MCP servers are stdio-based (Gmail, Slack, Granola, Brave Search, filesystem, etc.) and unreachable from the Excel WebView.
For non-technical AI power users — the target audience — the current UX is a dead end. They can't use the MCPs they know from Claude Desktop or Claude Code.
Goal: make MCP a "just works" experience where enabling a popular MCP server requires minimal setup (ideally just an API key or OAuth), no extra processes beyond the CORS proxy, and no restart.
Current architecture
Limitations:
DEFAULT_ALLOWED_TARGET_HOSTSonly includes LLM provider domains; arbitrary MCP server hosts are blockedProposed architecture: MCP Hub in the CORS proxy
Extend
cors-proxy-server.mjsinto an MCP hub that manages stdio server lifecycles:Config format (
mcp-servers.json){ "gmail": { "command": "npx", "args": ["-y", "@anthropic/gmail-mcp"], "env": { "GMAIL_OAUTH_CREDENTIALS": "~/.config/gmail-oauth.json" } }, "slack": { "command": "npx", "args": ["-y", "@anthropic/slack-mcp"], "env": { "SLACK_BOT_TOKEN": "xoxb-..." } }, "brave-search": { "command": "npx", "args": ["-y", "@anthropic/brave-search-mcp"], "env": { "BRAVE_API_KEY": "..." } } }Key design decisions
Lazy spawn: Don't start all servers on boot. Spawn a child process on first
connectortoolcall. This keeps startup fast and avoids wasted processes.File-watch hot-reload:
fs.watch("mcp-servers.json")detects changes → diffs config → spawns new servers, kills removed ones. No proxy restart needed to add/remove servers mid-session.Stdio ↔ HTTP translation: The hub receives HTTP JSON-RPC from the WebView, writes it as newline-delimited JSON to the child's stdin, reads the response from stdout, and returns it as HTTP.
CORS is a non-issue: All
/mcp/routes are loopback through the proxy the user already trusts. No host allowlist changes needed.Management API (stretch):
POST /mcp/servers/DELETE /mcp/servers/{id}so the Excel UI can add/remove servers without the user touching JSON.Ship a curated
mcp-servers.example.jsonPre-configure ~8 popular servers (commented out / disabled by default) so users just uncomment and add credentials:
@anthropic/gmail-mcp@anthropic/gdrive-mcp@anthropic/gcalendar-mcp@anthropic/slack-mcp@anthropic/brave-search-mcp@anthropic/notion-mcp@anthropic/filesystem-mcp@anthropic/sqlite-mcp(Exact package names TBD — verify against the MCP server registry before implementation.)
Phases
Phase 1 — Stdio bridge in the proxy
/mcp/{server-id}/rpcroute incors-proxy-server.mjsmcp-servers.jsonconfig loadingfs.watchhot-reloadGET /mcp/serversdiscovery endpointmcp-servers.example.jsonwith curated popular serversPhase 2 — Wire Excel client
mcptool auto-discovers servers fromGET /mcp/servers(bridge-managed servers appear alongside manually-added HTTP servers)/integrationsUI shows bridge-managed servers with status (running/stopped/error)Phase 3 — Management API + zero-JSON UX
POST /mcp/servers/DELETE /mcp/servers/{id}for CRUD from Excel UI/integrationsoverlayOut of scope (for now)
Context
src/tools/mcp.ts(HTTP JSON-RPC only)src/tools/mcp-config.ts(mcp.servers.v1settings key)scripts/cors-proxy-server.mjsscripts/proxy-target-policy.mjssrc/integrations/catalog.tssrc/commands/builtins/integrations-overlay.ts+integrations-overlay-elements.tssrc/commands/builtins/integrations-overlay-mcp-probe.tsskills/mcp-gateway/SKILL.mddocs/integrations-external-tools.md,docs/agent-skills-interop.md