Skip to content

MCP server

jarvis-conversation exposes your Jarvis corpus to MCP clients like Claude Code — a natural-language ask tool plus four structured read primitives, every one of them egress-guarded before anything leaves the box.

It's a stdio JSON-RPC server: modules/conversation/agent/conversation_agent/mcp_server.py, dispatched by agent.py serve-mcp. No MCP SDK and no HTTP port — just newline-delimited JSON-RPC over stdin/stdout. It does need the packages in agent-runtime/requirements.txt installed in the python3 it runs under: serve-mcp imports sqlite_vec at startup, so without it the process exits before answering initialize and the client reports "failed to connect".

The same server over HTTPS, with the write tools added, is brain-mcp (the jarvis-brain server). That is the one the cloud routines use.

Registering it in Claude Code

jarvis-conversation is not in the committed .mcp.json. That file is loaded by every Claude Code session opened on this repo, including the cloud routines, and a stdio server cannot start there: the cloud sandbox has neither the repo's Python requirements nor a reachable PocketBase, vault or vector store. From 2026-09-01 to 2026-10-07 every routine run opened with "jarvis-conversation failed to connect" for that reason (DER-58). The committed file now holds only jarvis-brain, and tools/checks/test_routine_prompts.py keeps it remote-only.

For local use, register it in your local scope (stored in your own ~/.claude.json for this project, never committed), from the repo root, in a python3 that has agent-runtime/requirements.txt installed:

claude mcp add --scope local jarvis-conversation \
  -e PYTHONPATH=agent-runtime/src:modules/conversation/agent \
  -- python3 -m conversation_agent.agent serve-mcp
claude mcp list   # jarvis-conversation should show ✓ Connected

Add the live-answer variables below with more -e flags, or export them in the shell you start claude from.

Start it manually

Useful for testing the server directly, outside a client:

PYTHONPATH="agent-runtime/src:modules/conversation/agent" python -m conversation_agent.agent serve-mcp

The five tools

Tool Args What it returns
ask question A cited natural-language answer, via the agentic RAG loop (Chat & RAG).
search_vault query, k Semantic search over indexed vault notes — cited chunks.
query_records collection, filter, sort, limit A structured read over one allowlisted collection: cap_items, entities, obligations, inbox_items, cap_reply_drafts.
person_timeline entity_id, limit A cross-module contact timeline for one entity.
list_inbox limit Pending platform inbox items.

query_records's collection argument only accepts the five allowlisted names above — anything else is refused.

The egress guard

Every structured tool result passes through the same guard before it's returned:

  1. Sensitivity filter — items flagged sensitive are dropped, not sent.
  2. Wikilink citation — each item is resolved against the vault so results carry a real [[note]]-style reference.
  3. Redaction — the resolved title/text is scanned for secret-shaped spans.
  4. Transparency log — the (already-redacted) result is logged to conv_llm_log with purpose=rag_read.

ask doesn't go through this same per-item guard — it egresses only via its own two audited hops inside the agentic RAG loop (unchanged from the SP3 ask command), which are logged the same way.

Requirements for live answers

Without any of these, the server still starts and correctly answers initialize and tools/list — a client can connect and discover the tool schemas, because the live dependencies are built lazily on the first tool call.

Missing core env vars crash the server on first tool call

POCKETBASE_URL, DEV_USER_EMAIL, and DEV_USER_PASSWORD are hard requirements: Config.from_env() raises SystemExit when one is unset, and the tools/call handler only catches Exception — so a missing one is not caught and terminates the server process on the first tool call (rather than returning a per-call error). Set all three before serving. The remaining requirements below degrade more gracefully — a call that needs an unbuilt index or an unset API key surfaces as an error result.

To get real answers, the server needs:

  • DEEPSEEK_API_KEY — the DeepSeek hops inside ask.
  • POCKETBASE_URL — the PocketBase URL.
  • PocketBase dev credentials (DEV_USER_EMAIL / DEV_USER_PASSWORD).
  • JARVIS_VECTOR_DB (or JARVIS_PB_DATA, which it's derived from) — the sidecar vector store.
  • JARVIS_VAULT_DIR — to resolve citations back to vault notes.
  • The 2_conv_llm_log_read_purpose.js migration applied (adds the rag_read purpose to conv_llm_log).

See Configuration for where each of these is set.

Connecting a client

Claude Code: use the claude mcp add --scope local command above. Any other MCP client: point it at the same command, args and PYTHONPATH, with the repo root as working directory.

stdout is protocol-only

stdout carries only JSON-RPC frames — nothing else may ever be printed there, or it corrupts the protocol stream. All diagnostics, warnings, and errors go to stderr instead.