fdf8fb5cda
The sidecar that isolates all MCP wire complexity from the soul. Binds
loopback 127.0.0.1:7771 only. The soul reaches it over flat HTTP; the bridge
owns stdio/streamable-HTTP transports, OAuth (PKCE), Keychain secrets, server
lifecycle, config, and a tool-schema-hash poisoning guard.
HTTP contract: GET /mcp/tools, /mcp/servers, /mcp/auto-approved, /healthz;
POST /mcp/call, /mcp/oauth/start, /mcp/servers/{add,toggle,auto-approve,
remove,secret}; GET /mcp/oauth/callback.
Config: ~/.neuron/connectors.json (servers, no secrets). Secrets in macOS
Keychain (service ai.neuron.connect, account = serverId). Spec:
docs/research/mcp-connectors-adoption-spec.md.
Phases 1-3 verified end to end (stdio + HTTP transport, Keychain token auth,
OAuth round-trip); Phase 4/5 (CRUD + auto-approve + schema-hash) added for the
ConnectorsView UI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
87 lines
3.5 KiB
Markdown
87 lines
3.5 KiB
Markdown
# 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__<srv>__<tool>", 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__<serverId>__<toolName>`; `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 `<serverId>`. 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 (`<serverId>: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.
|