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 dependency-free stdio JSON-RPC server:
modules/conversation/agent/conversation_agent/mcp_server.py, dispatched by
agent.py serve-mcp. No extra Python packages, no HTTP port — just
newline-delimited JSON-RPC over stdin/stdout.
.mcp.json¶
The repo root ships a committed .mcp.json that registers the server for
any MCP-aware client that reads it (Claude Code included):
{
"mcpServers": {
"jarvis-conversation": {
"command": "python3",
"args": ["-m", "conversation_agent.agent", "serve-mcp"],
"env": {
"PYTHONPATH": "agent-runtime/src:modules/conversation/agent"
}
}
}
}
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. |
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 four 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¶
A client that reads .mcp.json (e.g. Claude Code) picks the server up
automatically once you're working in this repo — no separate registration
step. If you're wiring up a different MCP client by hand, point it at the
same command/args/env from .mcp.json above.
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.