Skip to content

Let AI agents query Argus data via a local MCP endpoint #299

Description

@mando

Description

Argus indexes local agent transcripts, but agents can't see any of it. Let AI agents (Claude Code, Codex, Gemini CLI) query Argus — "what did I work on last week", session history, usage/cost, task outcomes — so agents can use the user's own work history as context.

Approach

A local MCP server, served as streamable HTTP on the existing serve app at /mcp — reachable at http://127.0.0.1:4242/mcp for both desktop-app users (via the front-door proxy: no PATH dependency, survives sidecar restarts) and CLI users. No stdio subcommand in v1: desktop users' sidecar binary lives at an unstable bundle path, and "works while Argus isn't running" isn't a real use case when the daemon is the product. Claude Code (claude mcp add --transport http), Codex ([mcp_servers.argus] in config.toml), and Gemini CLI all speak streamable HTTP. The zero-code alternative (document the HTTP API + argus search --json) becomes a short fallback section on the docs page.

Privacy defaults: agentAccess.enabled default ON (UI toggle — MCP adds discoverability, not new exposure, since the read API is already open to local processes; matches the interpret default-on precedent). agentAccess.includeTranscripts default OFF (UI toggle — the genuinely new risk is transcript text flowing into a remote model's context). Both resolve per request so Settings toggles apply live; disabled → /mcp returns 404. The transcript tool stays listed when off, with a description explaining how to enable it.

Six read-only tools, all reusing readers startServer already assembles (zero new store queries):

Tool Reuses
search_sessions (query/file/since/until/source/project/sort, limit ≤50) sessionList reader → store.searchSessions + readSessionAggregates + buildSessionList; strip FTS snippet sentinels like search-ops.ts does
get_session (+optional task metrics) sessionDetail + sessionTaskMetrics readers
get_session_transcript (paginated; gated by includeTranscripts + retainText) sessionInteractions reader (buildSessionInteractions)
usage_summary (group_by day|model|source|project) views.usageDaily/usageByModel/usageBySource/usageByProject
tool_usage (group_by tool|category|mcp_server|skill) views.toolsByTool/toolsByCategory/toolsByMcpServer/skills
health_summary views.health + views.recommendations

Server instructions teach date semantics (local YYYY-MM-DD; the agent computes "last week" itself) and valid sources (ALL_SOURCES). Tools only — no MCP resources/prompts in v1.

Security: guard /mcp with the existing rejectUnsafeHost (the MCP spec requires Origin validation against DNS rebinding; it tolerates absent Origin, which MCP clients satisfy). Do NOT require the x-argus-app CSRF header (MCP clients can't send it; the endpoint is read-only). No auth token — loopback-only bind stays the boundary, consistent with the rest of the API. Mount among read routes so it survives readOnly mode.

Implementation steps

  1. Dependencies (root package.json): @modelcontextprotocol/sdk (import only server/mcp.js + types subpaths — never its node/express transports), @hono/mcp (StreamableHTTPTransport, Bun-supported, takes a Hono Context), zod@^3. Risk to retire early: verify bun run build:compile bundles the SDK, then smoke-test dist/argus serve + a JSON-RPC initialize POST.
  2. Config (src/config.ts): new AGENT_ACCESS_SETTINGS registry — enabled (path agentAccess.enabled, env ARGUS_AGENT_ACCESS_ENABLED, default true, toggle UI) and includeTranscripts (path agentAccess.includeTranscripts, env ARGUS_AGENT_ACCESS_INCLUDE_TRANSCRIPTS, default false, toggle UI with text-leaves-machine warning); resolveAgentAccess() modeled on resolveRetainText; add to ALL_SETTINGS.
  3. Tool registry (src/api/mcp.ts, new): createArgusMcpServer(deps, { includeTranscripts }) registering the 6 tools (zod schemas; JSON in content text + structuredContent); createMcpHandler(deps) → per-request: resolve gates, 404 if disabled, fresh McpServer + StreamableHTTPTransport (stateless), transport.handleRequest(c). McpDeps = the reader types from serve.ts (export the ones not yet exported).
  4. Serve wiring (src/api/serve.ts): AppOptions.mcp?: (c: Context) => Promise<Response> (injected like views, keeps createApp unit-testable); mount app.all("/mcp", ...) among read routes behind rejectUnsafeHost; in startServer, pass createMcpHandler({ views, sessionList, sessionDetail, sessionInteractions, sessionTaskMetrics }). No run.ts/cli.ts changes — it rides the serve leg.
  5. Settings surface (src/api/settings.ts): new layout category "Agent access" with the two toggles; the web Settings screen renders from describeSettings(), so no web code changes required (connect-snippet helper copy in web/src/routes/Settings.tsx is a nice-to-have).
  6. Tests: test/mcp.test.ts (new) following test/serve.test.ts's fake-reader pattern — drive createApp(null, { mcp: createMcpHandler(fakeDeps) }) with raw JSON-RPC POSTs (initialize, tools/list, tools/call per tool); assert filter pass-through, sentinel stripping, limit clamping, gating (enabled=false → 404; transcripts off → refusal; non-loopback Host → 403). Config resolution tests mirror the resolveRetainText ones.
  7. Docs: docs/connect-your-agent.md (new, under "Using Argus" in the VitePress sidebar; contributing voice): per-client connect snippets, what agents can ask, the transcript toggle, HTTP-API fallback. docs/internals/agent-access.md (new, styled like web-app.md): transport decision, tool→reader map, threat model. Update docs/privacy.md and docs/settings-reference.md. Flag: Claude Cowork's sandboxed VM may not reach host loopback — document Claude Code/Codex/Gemini first, verify Cowork separately.

v1 non-goals

No stdio argus mcp subcommand (phase 2: extract reader assembly into a shared factory, ~50-line wrapper); no auth token; no write tools; no MCP resources/prompts; no one-click client-config install from the desktop app; no Hub/org-level access; no per-agent audit log; no transcript redaction.

Phase 2 candidates

stdio subcommand · one-click connect from tray/Settings (writes client configs) · optional bearer token for multi-user machines · MCP prompts ("weekly review", "cost report") · Hub-side org endpoint with real auth. Free bonus: agents querying Argus show up in Argus's own tool analytics as mcp__argus__*.

Verification

  • bun test (new mcp/config/settings tests + existing suites) and bun run typecheck.
  • Compile smoke: bun run build:compile, run dist/argus serve, POST JSON-RPC initialize + tools/list + a tools/call to http://127.0.0.1:4242/mcp.
  • Live end-to-end: claude mcp add --transport http argus http://127.0.0.1:4242/mcp, ask Claude Code "what did I work on last week?", confirm sensible search_sessions/usage_summary calls; toggle transcripts and enabled in Settings and confirm gates apply without restart.
  • Desktop path: confirm /mcp is reachable through the front-door proxy on 4242.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions