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:
- Sensitivity filter — items flagged sensitive are dropped, not sent.
- Wikilink citation — each item is resolved against the vault so results
carry a real
[[note]]-style reference. - Redaction — the resolved title/text is scanned for secret-shaped spans.
- Transparency log — the (already-redacted) result is logged to
conv_llm_logwithpurpose=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 insideask.POCKETBASE_URL— the PocketBase URL.- PocketBase dev credentials (
DEV_USER_EMAIL/DEV_USER_PASSWORD). JARVIS_VECTOR_DB(orJARVIS_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.jsmigration applied (adds therag_readpurpose toconv_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.