Revka as an MCP server
Expose Revka's tool surface to external CLIs via the in-process MCP server, discovery file, and auth model.
Revka runs a Model Context Protocol (MCP) server as a background task inside the main daemon, so any MCP-compatible client — Claude Code, Codex, OpenCode, Gemini CLI, Claude Desktop — can drive Revka’s entire built-in and integration tool surface without duplicating configuration. The server speaks JSON-RPC 2.0 over a Streamable HTTP transport, binds an ephemeral local port, and advertises that port through a discovery file that external tools read automatically.
Read this page if you want an external coding CLI to call Revka’s tools, or if you are building a client against the in-process server’s native endpoints. This is the server side: the daemon, its native HTTP endpoints, the tool registry tiers, the session-wide progress stream, and the two-header auth model. For the browser-facing reverse proxy the dashboard uses, see MCP proxy & discovery API. To make Revka a client of other MCP servers instead, see Connecting to external MCP servers.
How the in-process server works
Section titled “How the in-process server works”The MCP server starts automatically when the daemon boots — there is no separate process to launch. It binds 127.0.0.1 on an ephemeral port, then writes its URL to ~/.revka/mcp.json so callers never have to hardcode a port. The full boot sequence is:
-
You start the daemon (
revka daemon— see revka gateway, daemon & service). -
The daemon spawns the MCP server task, which binds
127.0.0.1:<ephemeral-port>. -
The server atomically writes
~/.revka/mcp.jsonwith{ url, pid, started_at }. -
An external CLI reads
~/.revka/mcp.json, mints a session, and starts calling tools.
The endpoint is always of the form:
http://127.0.0.1:<ephemeral>/mcpThe server advertises MCP protocol version 2024-11-05 and identifies itself as revka-mcp in the initialize response.
The discovery file (~/.revka/mcp.json)
Section titled “The discovery file (~/.revka/mcp.json)”The daemon writes ~/.revka/mcp.json at startup. External CLIs and the gateway both read it to learn where the MCP server is listening. The payload shape is a frozen contract — only these three fields appear:
{ "url": "http://127.0.0.1:54500/mcp", "pid": 12345, "started_at": "2026-06-18T00:00:00Z"}| Field | Type | Meaning |
|---|---|---|
url | string | Full MCP endpoint, e.g. http://127.0.0.1:<port>/mcp. Always present. |
pid | number | Daemon process id. May be omitted by older writers. |
started_at | string | RFC 3339 start timestamp (UTC). May be omitted by older writers. |
Behavior to rely on:
- Atomic write. The file is written to a temporary file in the same directory and then renamed over the target, so a concurrent reader never observes a half-written document.
- Absent until bound. The file does not exist until the MCP task has fully bound its port. Callers must handle absence gracefully rather than treating a missing file as an error.
- Cleaned up on shutdown. The daemon removes the file on graceful shutdown.
- mtime-cached by the gateway. The gateway caches the parsed file keyed by its modification time. Because the daemon may restart between requests (changing the ephemeral port), the next read after the mtime changes re-parses automatically — a restart never serves a stale port.
Native endpoints
Section titled “Native endpoints”The in-process server exposes four endpoints. External CLIs talk to these directly using the URL from the discovery file.
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET | /health | None | Liveness probe. |
POST | /session | None | Mint a session; returns { session_id, token, cwd }. |
POST | /mcp | Two headers (below) | JSON-RPC 2.0 endpoint: initialize, tools/list, tools/call. |
GET | /session/{session_id}/events | Authorization: Bearer | Per-session progress SSE stream. |
GET /health
Section titled “GET /health”A simple liveness probe. Returns 200 OK with:
{ "status": "ok", "pid": 12345, "uptime_seconds": 3600, "started_at": "2026-06-18T00:00:00Z", "protocol_version": "2024-11-05"}POST /session
Section titled “POST /session”Mints a session and returns the credentials needed for every subsequent /mcp call. The body is optional.
POST /sessionContent-Type: application/json
{ "cwd": "/home/user/project", "label": "code-cli" }| Field | Type | Meaning |
|---|---|---|
cwd | string (optional) | Working directory for the session. Tilde-expanded and canonicalized; defaults to the daemon’s current directory. |
label | string (optional) | Human-readable label for the session. |
Response:
{ "session_id": "<session-id>", "token": "<session-token>", "cwd": "/home/user/project"}Keep both session_id and token — they authenticate every /mcp call and the events stream.
POST /mcp
Section titled “POST /mcp”The JSON-RPC 2.0 endpoint. It requires the two-header auth described below and supports exactly three methods:
| Method | Response type | Notes |
|---|---|---|
initialize | Plain JSON | Returns protocolVersion, capabilities, and serverInfo ({ name: "revka-mcp", version }). |
tools/list | Plain JSON | Lists every advertised tool with its name, description, and inputSchema. The list is sorted alphabetically by name. |
tools/call | SSE stream | Executes a tool. See Tool-call response format. |
POST /mcpAuthorization: Bearer <session-token>X-Revka-Session: <session_id>Content-Type: application/json
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }The notifications/initialized and notifications/cancelled notifications are accepted and return 202 Accepted with no body.
GET /session/{session_id}/events
Section titled “GET /session/{session_id}/events”The session-wide progress SSE stream — covered in detail under Session-wide progress stream.
Two-header auth
Section titled “Two-header auth”POST /mcp is the only authenticated method-dispatch endpoint, and it requires two headers together:
Authorization: Bearer <session-token>X-Revka-Session: <session_id>Both values come from the POST /session response. The server looks up the session by X-Revka-Session and compares the supplied bearer against the issued token using a constant-time comparison. If either header is missing or empty, or the pair does not match a live session, the server returns 401 Unauthorized.
The events stream (GET /session/{session_id}/events) needs only Authorization: Bearer <session-token> — the session id is already in the URL, so X-Revka-Session is redundant there.
Tool-call response format
Section titled “Tool-call response format”Unlike initialize and tools/list, a tools/call request returns a Server-Sent Events stream, because tools can emit progress before they finish. For each call the server emits, in order:
- Zero or more
notifications/progressevents (forwarded from the tool’s own progress reporting). - Exactly one terminal JSON-RPC response event carrying the tool result (
{ content, isError }). - A final
event: done.
POST /mcpAuthorization: Bearer <session-token>X-Revka-Session: <session_id>Content-Type: application/json
{ "jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": { "name": "notion", "arguments": { "action": "search", "query": "roadmap" }, "_meta": { "progressToken": 7 } }}If you supply a progressToken in params._meta, the server reuses it on every notifications/progress event for that call so you can correlate progress to the originating request. If you omit it, the server mints one. A tool that does not exist returns a plain JSON-RPC error (unknown tool) rather than a stream.
Tool registry tiers
Section titled “Tool registry tiers”The server builds its advertised tool set in tiers. Which tier you get depends on how much state the daemon has wired in at boot.
Baseline (always on)
Section titled “Baseline (always on)”The baseline registry is a curated set of ~16 zero-dependency tools that need nothing beyond a security policy and the workspace path. It is always available, even if config loading fails:
shell file_read file_write file_editglob_search content_search calculator weathergit_operations http_request web_fetch web_search_toolpdf_read screenshot image_info browser_openExtended (credential-gated)
Section titled “Extended (credential-gated)”When the daemon loads a Config, it adds the skills meta-tools and any integration whose credentials are present:
- Skills meta-tools (always added with a config):
skills_list,skills_describe,skills_execute— see Skills system. - Integrations (added only when configured):
notion,jira,composio,google_workspace,linkedin,microsoft365.
Each integration that is enabled but missing credentials is skipped with a reason logged at boot (for example, notion — enabled but notion.api_key / NOTION_API_KEY missing), so operators can see exactly why a tool didn’t register. Configure these in the integration catalog and tool config reference.
Runtime-aware (needs live handles)
Section titled “Runtime-aware (needs live handles)”A third tier registers only when the gateway threads its live runtime handles into the server at boot — these tools need state the bare daemon does not own (schedulers, channel maps, provider credentials, workspace session stores):
cron_add / cron_list / cron_remove / cron_update / cron_run / cron_runsllm_task image_gen delegate swarmworkspace poll discord_searchsop_list / sop_execute / sop_advance / sop_approve / sop_statussessions_list / sessions_history / sessions_sendWhen a runtime-aware tool can’t register, the server records a skip reason (for example, cron_* — needs boot Config + scheduler, discord_search — needs discord.db Memory backend). The coding-CLI tools (claude_code, codex_cli, gemini_cli, opencode_cli) are intentionally never exposed — those are the clients connecting to this daemon.
Session-wide progress stream
Section titled “Session-wide progress stream”In addition to the per-call progress embedded in each tools/call SSE stream, the server publishes every progress event onto a per-session broadcast channel. Any subscriber holding the session token can tap it to watch all in-flight tool progress for that session — which is how a UI shows “what Revka is doing right now” while an external CLI drives the request.
GET /session/<session_id>/eventsAuthorization: Bearer <session-token>Accept: text/event-streamEach event’s data: payload is a JSON ProgressEvent. The stream terminates when the client disconnects.
Progress events
Section titled “Progress events”A ProgressEvent mirrors the per-call notifications/progress payload with two extra fields — tool and timestamp — so a subscriber can render “Notion — 4/10 at 10:20:33” without correlating progress tokens back to the originating request:
{ "token": 7, "progress": 4, "total": 10, "message": "notion: sending request", "tool": "notion", "timestamp": "2026-04-17T10:20:33+00:00"}| Field | Type | Meaning |
|---|---|---|
token | number | Progress token correlating the event to a tools/call. |
progress | number | Current progress count. |
total | number (optional) | Total expected steps, when known. |
message | string (optional) | Human-readable status line. |
tool | string (optional) | Name of the tool emitting progress (e.g. notion). |
timestamp | string | RFC 3339 timestamp (UTC). |
Progress is advisory, not load-bearing. The broadcast channel has a small fixed capacity; slow consumers silently drop lagged frames rather than blocking the tool call. A subscriber that falls behind simply misses frames and catches up on the next one.
End-to-end: connect an external CLI
Section titled “End-to-end: connect an external CLI”Putting it together, an external client follows this sequence:
-
Discover. Read
~/.revka/mcp.jsonand take itsurl(e.g.http://127.0.0.1:54500/mcp). If the file is absent, the daemon isn’t running. -
Open a session.
POST <host>/sessionand keep the returnedsession_idandtoken.Terminal window curl -s http://127.0.0.1:54500/session \-H 'Content-Type: application/json' \-d '{ "cwd": "/home/user/project", "label": "code-cli" }' -
Initialize and list tools.
POST <host>/mcpwith both auth headers.Terminal window curl -s http://127.0.0.1:54500/mcp \-H 'Authorization: Bearer <session-token>' \-H 'X-Revka-Session: <session_id>' \-H 'Content-Type: application/json' \-d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }' -
Call tools.
POST <host>/mcpwithmethod: "tools/call"and read the SSE stream untilevent: done. -
(Optional) Watch progress. Subscribe to
GET <host>/session/<session_id>/eventswith the bearer to follow every tool’s progress on the session.
The dashboard’s Code tab wires this automatically for the supported CLIs (Claude Code, Codex, OpenCode, Gemini, and more), injecting the discovery URL, session id, and token into each tool’s config — see MCP proxy & discovery API for the proxy and terminal injection details.