Skip to content

MCP server

Khiip ships a Model Context Protocol server (khiip-mcp-server) that exposes the substrate’s core operations as MCP tools. Claude Desktop, Cursor, or any other MCP-aware client can connect over stdio and capture/recall through Khiip without writing any HTTP code.

Tools

ToolMaps to
capture_url(url, force_new=False)POST /api/v1/captures?mode=async + polls GET /api/v1/jobs/{id}
recall(query, limit=10, min_score=None)GET /api/v1/recall?view=lean
list_captures(source=None, limit=50, offset=0)GET /api/v1/captures?view=lean
get_capture(capture_id, format="json")GET /api/v1/captures/{id}
refetch_capture(capture_id, dimension="extraction", force=False)POST /api/v1/captures/{id}/refetch
daemon_status()GET /health + GET /api/v1/meta

recall and list_captures return lean rows — a flat, token-frugal projection (id, url, source, title, author, recorded_at, vault_path, description, excerpt, excerpt_from, payload_chars, and — on recallscore) rather than the full nested payload, which would blow past an MCP client’s tool-result budget. excerpt is the head of the source’s primary text (capped at 500 characters, with a trailing only when it was actually truncated); payload_chars is the exact character count of the format="json" drill-down body, so an agent can size get_capture(id) before calling it. There is no full/verbose flag on these two tools by design — get_capture(id) is the drill-down.

recall’s optional min_score is a cosine floor in [-1, 1] — see score interpretation. refetch_capture’s dimension takes extraction (default) / re-extract / re-render / media / wayback, and force=True overrides the daemon’s hand-edit guard on the in-place dimensions — see the refetch guide.

capture_url runs asynchronously

capture_url submits the capture as a daemon-side job and polls it to completion, forwarding per-stage progress (extract → enrich → media → preserve → wayback → render → write → embed; the media stage reports item counts) as MCP progress notifications when your client asks for them. The returned value is the finished capture record — the same shape a synchronous capture returns — so nothing about the result changes; you just get a progress bar on long captures.

The poll loop has a client-side wall-clock deadline (300 seconds). If a capture job is still running after that — a stuck worker, not a slow one — the tool returns a structured capture-job-timeout result (with the job_id) instead of hanging; the job keeps running on the daemon and can be polled directly at GET /api/v1/jobs/{id}.

Every tool reports failures as a structured {"error": ..., "detail": ...} dict rather than raising, so the agent always has something readable to act on (for example a daemon-unreachable error when the daemon is down, or daemon-409 when a refetch would clobber a hand-edited note).

How it works

The MCP server is a thin HTTP proxy that connects to your running Khiip daemon — by default at 127.0.0.1:8478, or wherever KHIIP_DAEMON_URL, else [daemon] host/port in ~/.config/khiip/config.toml, points it (in that precedence; prefer config.toml, since GUI-launched MCP servers don’t reliably see shell env vars). It reads the Bearer auth token from ~/.config/khiip/auth.toml — the same file the CLI and Obsidian plugin use. Since 0.2.3, the MCP initialize handshake reports khiip’s own package version, so your client shows which khiip it launched. The daemon must be running before MCP tool calls land. To start it automatically at login — so a reboot never leaves your MCP client pointing at a dead daemon — use the ready-made launchd/systemd templates on the Autostart (launchd / systemd) page.

Claude Desktop config

On macOS, edit ~/Library/Application Support/Claude/claude_desktop_config.json (analogous paths on other platforms):

{
"mcpServers": {
"khiip": {
"command": "khiip-mcp-server"
}
}
}

If khiip-mcp-server isn’t on Claude Desktop’s PATH (common when you installed with pip install -e inside a venv), point at the full path:

{
"mcpServers": {
"khiip": {
"command": "/Users/you/projects/khiip/.venv/bin/khiip-mcp-server"
}
}
}

Verify the wiring

Terminal window
# Start the daemon in one terminal
khiipd serve
# In another terminal, send an initialize ping to the MCP server
python -c "
import asyncio
from mcp.client.session import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters
async def main():
params = StdioServerParameters(command='khiip-mcp-server', args=[])
async with stdio_client(params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
tools = await session.list_tools()
for t in tools.tools:
print(t.name)
asyncio.run(main())
"

You should see all six tool names listed.