MoriMori
Mori

MCP — plug anything in, plug Mori in anywhere

Mori speaks the Model Context Protocol in both directions: any MCP server's tools become hers, and she exposes a curated, token-guarded tool surface for other agents.

Two directions, one protocol

MCP support in Mori is symmetrical. As a client, Mori connects to any Model Context Protocol server — filesystem, GitHub, Slack, Postgres, web search — and merges its tools into her own dispatch, so they work identically on every model provider you bring keys for. As a server, Mori exposes a small, curated tool surface on localhost so other agents (Claude Code, IDEs, scripts) can read your machine through her, see her Kanban board, and file tasks for you to triage.

Both directions follow the same rules as everything else in Mori: local-first (configs and tokens live on your machine, never a cloud account), bring-your-own-keys, and approval-gated for anything risky. Everything is managed from Settings → Connections.

Connecting a server to Mori

In Settings → Connections, give the server a short name and paste its config as JSON. Mori uses the exact claude_desktop_config.json shape, so any published server config works verbatim — no translation. Local servers use command/args (plus optional env and cwd); remote servers use url (plus optional headers). A "disabled": true flag parks a server without deleting it.

On disk, the config is a single file — mcp.json in Mori's user-data directory — with the standard mcpServers wrapper. You can edit it directly and hit Reload all in the card, or add and remove servers one at a time from the UI; each edit reconnects only the server it touches. The card shows a live status dot per server (connecting, ready, error, disabled), the tool count, the discovered tool names, and the exact error message if a connection failed.

// Settings → Connections → Add a server
// Name: filesystem
{
  "command": "npx",
  "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"]
}

// The same server in mcp.json (userData) — paste-compatible with Claude Desktop:
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents"]
    }
  }
}

Transports: stdio, Streamable HTTP, SSE fallback

A config with command starts a stdio server as a child process. Mori resolves your login-shell PATH once at startup, because GUI apps launched from Finder do not inherit it — npx, uvx, nvm-managed node, and Homebrew binaries all work without configuration. Stdio servers receive a minimal environment: the resolved PATH, a safe baseline (HOME, USER, LANG, and similar), and whatever you put in the server's own env block. They never see Mori's full process environment, so your API keys cannot leak into a third-party server.

A config with url connects over Streamable HTTP first. If that connect fails — legacy SSE-only servers reject the Streamable POST with a 404 or 405 — Mori automatically retries the same URL with the legacy SSE transport. You do not declare the transport; the fallback is handled at connect time. Custom headers (for example an Authorization bearer) are sent on either transport.

// Remote server over Streamable HTTP (SSE fallback is automatic):
{
  "url": "https://example.com/mcp",
  "headers": { "Authorization": "Bearer YOUR_KEY" }
}

How foreign tools behave inside Mori

Discovered tools are namespaced mcp__<server>__<tool> and merged into Mori's tool dispatch, which means they flow into both her OpenAI-shaped and Anthropic-shaped schemas automatically — one config, every provider. Tool descriptions are prefixed with the server name so the model always knows where a capability comes from.

MCP tools sit inside Mori's permission system, not beside it:

  • Agent mode must be on for any MCP tool to be callable.
  • At the read-only Safe permission level, MCP tools are blocked entirely.
  • Destructive actions go through Mori's typed approval ladder like her native tools — nothing risky runs on a nod.
  • Tools are only executed in parallel when the server marks them readOnlyHint in its annotations; anything unannotated runs sequentially.

Results are normalized into Mori's tool-result convention: text blocks are joined, resource blocks are inlined with their URI, and an image block is converted into a real vision block so the model can actually see it.

Reliability: budgets, pagination, hot-reload

All configured servers connect in parallel after the window opens, best-effort — a dead or slow server never blocks the others or Mori's startup. Each connection gets a 15-second budget; each tool call gets 60 seconds. Timeouts and failures come back to the model as plain error: strings rather than crashes, so Mori can route around a flaky server mid-task.

  • Pagination: tool discovery follows the spec's nextCursor loop, so servers with large tool catalogs are listed completely (hard cap of 2,000 tools per server, so a broken cursor cannot spin forever).
  • Hot-reload: when a server announces notifications/tools/list_changed, Mori re-fetches its tool list on the spot — no reconnect, no restart.
  • Manual control: Reload all tears down every client and reconnects from the current config; removing a server closes its client and drops its tools from dispatch immediately.

Mori as a server: the curated surface

Flip on Mori as a server in Settings → Connections and Mori serves MCP over Streamable HTTP at http://127.0.0.1:4519/mcp. It is fail-closed by design: off by default, bound to localhost only, and every request must carry a bearer token that is generated once and shown (masked, with copy buttons) in the card. Requests are stateless — one fresh server-transport pair per POST, nothing shared between clients.

Version 1 exposes exactly seven tools, each mapped onto the same execution core Mori's own brain uses:

  • mori_status — app version, platform, agent availability
  • mori_read_file — read a text file (read-only)
  • mori_list_files — list a folder (read-only)
  • mori_search_files — find files by name under a folder (read-only)
  • mori_board — read the full Kanban board, every lane and card
  • mori_create_task — add a card to the board
  • mori_goals — read the persistent goals list

There is deliberately no terminal, no file write, no send, and no computer control on this surface. The only write, mori_create_task, lands cards in the board's Inbox lane — an external agent can ask for work, but nothing executes until you move the card through triage yourself.

Connecting Claude Code to Mori

Turn the server on, copy the token from the Settings card, and register Mori as an HTTP MCP server. From then on, Claude Code sessions can check what Mori is doing, read files through her, inspect her board and goals, and queue tasks into her Inbox.

# One-time setup — paste the token from Settings → Connections → Mori as a server
claude mcp add --transport http mori http://127.0.0.1:4519/mcp \
  --header "Authorization: Bearer <your-token>"

# Then, inside Claude Code:
#   mori_status          -> {"app":"Studio Mori","version":"…","platform":"darwin","tools":7}
#   mori_create_task     -> lands in Mori's Inbox for you to triage

Files and defaults at a glance

  • Client config: mcp.json in Mori's user-data directory, claude_desktop_config.json shape (mcpServers key; servers also accepted).
  • Server config: mcp-server.json in the same directory — enabled flag plus the generated token. Regenerate by deleting the file while the server is off.
  • Defaults: client has no servers configured; Mori-as-server is disabled; port 4519; endpoint accepts POST /mcp only and answers 401 without the exact bearer token.
  • Scope: both files stay on your machine. Nothing about your MCP setup is synced or sent anywhere.