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.
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.
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"]
}
}
}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" }
}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:
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.
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.
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).notifications/tools/list_changed, Mori re-fetches its tool list on the spot — no reconnect, no restart.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 availabilitymori_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 cardmori_create_task — add a card to the boardmori_goals — read the persistent goals listThere 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.
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 triagemcp.json in Mori's user-data directory, claude_desktop_config.json shape (mcpServers key; servers also accepted).mcp-server.json in the same directory — enabled flag plus the generated token. Regenerate by deleting the file while the server is off.4519; endpoint accepts POST /mcp only and answers 401 without the exact bearer token.