tim.lingo fdf8fb5cda feat: neuron-connectd — MCP connector bridge (Accessor sidecar)
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>
2026-06-13 18:45:55 -05:00

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

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.

{
  "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.
S
Description
No description provided
Readme
66 KiB
Languages
TypeScript 94.6%
JavaScript 5.4%