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 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:

  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

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.