# neuron-connectd The Neuron **MCP-connector bridge** — the Accessor that isolates the MCP wire protocol (stdio framing, SSE, OAuth, server lifecycle) behind a flat loopback HTTP contract the El soul consumes. The soul stays El-simple; this process absorbs the ecosystem. Spec: `../docs/research/mcp-connectors-adoption-spec.md`. ## Run ```bash npm install npm run dev # tsx src/index.ts (dev) # or npm run build && npm start ``` Binds **loopback only**: `http://127.0.0.1:7771` (override with `NEURON_CONNECTD_PORT`). ## Config Reads `~/.neuron/connectors.json`. If absent, falls back to a Phase-1 default: one zero-auth filesystem MCP server scoped to `~/neuron-connectd-sandbox`. ```json { "servers": { "filesystem": { "transport": "stdio", "enabled": true, "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"] } } } ``` Secrets never live in this file — Phase 3 puts OAuth/API tokens in the macOS Keychain (`ai.neuron.connect`). ## HTTP contract (Axon-shaped) | Method + path | Body | Returns | |----------------------|---------------------------|---------| | `GET /mcp/tools` | — | `{ tools: [{ name:"mcp____", description, input_schema }] }` | | `POST /mcp/call` | `{ name, input }` | `{ ok, content }` or `{ ok:false, error }` | | `GET /mcp/servers` | — | `{ servers: [{ id, status, tools }] }` | | `GET /healthz` | — | `{ ok:true }` | Tools are namespaced `mcp____`; `POST /mcp/call` routes back to the owning server by that prefix. `input_schema` is translated from MCP's `inputSchema` into Anthropic tool format so the soul can splice it straight into its tools array. ## Auth (Phase 3) Remote (`http`) connectors support two auth modes: - **`auth: "token"`** — a static bearer token stored in the Keychain under account ``. The bridge reads it and sends `Authorization: Bearer …`. Covers GitHub PATs and any API-key MCP server. - **`auth: "oauth"`** — SDK-driven PKCE. The bridge exposes: - `POST /mcp/oauth/start {id}` → `{ authUrl }` (open in the system browser) - `GET /mcp/oauth/callback?id=..&code=..` → completes the exchange, stores tokens in the Keychain, reconnects. Tokens, the dynamic-registration client info, and the in-flight PKCE verifier all live in the Keychain (`:oauth` / `:client` / `:verifier`), so a sign-in survives bridge restarts. ## Status - **Phase 1 (done):** stdio servers, tool merge + namespacing, call routing, graceful error paths. Proven against `server-filesystem` via curl. - **Phase 2 (done):** soul integration — `chat.el` merges + dispatches `mcp__*` tools; the model calls them through the approval loop. Proven on a rebuilt soul. - **Phase 3 (done):** `http` (Streamable HTTP) transport, Keychain-backed secrets, token auth, OAuth (PKCE) provider + endpoints. Token path proven end-to-end against a local token-gated MCP server (positive + negative); OAuth provider mechanics proven via Keychain round-trip. **Note:** the full live OAuth handshake (discovery → DCR → consent) is correct-by-construction on the SDK's flow but still needs a real OAuth MCP server to exercise end-to-end. - **Phase 4 (todo):** `ConnectorsView.kt` UI + `/api/connectors/*` soul routes. - **Phase 5 (todo):** server catalog, schema-hash pinning, per-connector auto-approve, config hot-reload, run `neuron-connectd` as a managed daemon.