Ria: MCP knowledge server and cost accounting - design reference

Researched 2026-10-12. Spec version cited: MCP 2025-11-25. Anything not confirmed on a primary page is marked [unverified].

Part A. MCP server that serves Ria's knowledge

A1. Transport: streamable HTTP, one endpoint

A2. Auth options (pick by consumer)

The authorization section is OPTIONAL in the spec, and for HTTP transports it says implementations SHOULD follow it. Source: https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization

OptionWhenNotes
Static bearer token per consumer app (Authorization: Bearer <token>)Byron's own apps and agents calling Ria over Tailscale/HTTPS. Recommended starting point.The spec requires bearer format on every HTTP request and forbids tokens in the query string. A static key is not the full OAuth flow, so third-party clients that auto-discover OAuth will not work, but any client that lets you set headers (Claude Code --mcp-config, Agent SDK, custom code) will. Issue one token per app, store only hashes, log token id per call, support revocation.
OAuth 2.1 resource serverIf you want claude.ai connectors, Claude Desktop, or any third-party client to connect without hand-set headers.Server MUST publish RFC 9728 Protected Resource Metadata (via WWW-Authenticate: Bearer resource_metadata=... on 401 or /.well-known/oauth-protected-resource), validate tokens' audience (RFC 8707 resource), accept PKCE S256 clients, never pass tokens through to upstream APIs. Client registration: Client ID Metadata Documents preferred, pre-registration, or Dynamic Client Registration as fallback. Use an external IdP (Auth0/Clerk/Keycloak) rather than writing an authorization server.
Network-level only (Tailscale ACL, no app auth)Strictly single-user, tailnet-only.Still add a bearer token; the spec says servers SHOULD authenticate.

FastMCP supports bearer verifiers (StaticTokenVerifier for dev/test, per the docs) and OAuth providers (GitHub etc.), with a production OAuth proxy needing an explicit jwt_signing_key and encrypted persistent storage (docs). For a production bearer scheme, write a small custom TokenVerifier that checks hashed tokens against a table instead of using the dev verifier. API details beyond the doc summary: read the FastMCP auth docs before coding [unverified].

Recommendation: Streamable HTTP, stateless, single /mcp endpoint, bearer token per consuming app now; add OAuth 2.1 resource-server metadata later if claude.ai/third-party connectors are wanted. Put it behind Cloudflare/Tailscale Funnel or an arnao.ai host with TLS.

A3. Tool, resource, pagination conventions

From the tools spec (https://modelcontextprotocol.io/specification/2025-11-25/server/tools):

Pagination (https://modelcontextprotocol.io/specification/2025-11-25/server/utilities/pagination): opaque cursor / nextCursor; server chooses page size; applies to resources/list, resources/templates/list, prompts/list, tools/list; bad cursor returns -32602. For your own data tools (which are not covered by that mechanism) copy the same convention: limit (cap 50), cursor in, next_cursor out, base64 of (sort_key, id); results sorted by a stable key such as (published_at DESC, id).

Proposed surface

Tools (read-only unless noted):

Resources (URI templates, so clients can attach context):

Prompts: ria.explain_incident, ria.weekly_rollup.

A4. Caching

A5. Knowledge-base MCP servers in the wild (to study)

A6. SDK choice


Part B. Cost accounting for an autonomous agent

B1. Where each provider reports cost

Anthropic API

OpenRouter

Local Ollama

LiteLLM as a single meter

B2. Claude Code headless (claude -p)

Docs: https://code.claude.com/docs/en/headless and https://code.claude.com/docs/en/agent-sdk/cost-tracking

B3. Ledger schema (SQLite)

CREATE TABLE ledger (
  id            INTEGER PRIMARY KEY,
  run_id        TEXT NOT NULL,         -- uuid per scheduled run
  parent_run_id TEXT,                  -- for subtasks / retries
  task          TEXT NOT NULL,         -- 'daily_brief','moltbook_probe','ingest_feed',...
  provider      TEXT NOT NULL,         -- 'anthropic' | 'openrouter' | 'ollama'
  model         TEXT NOT NULL,
  input_tokens  INTEGER NOT NULL DEFAULT 0,
  output_tokens INTEGER NOT NULL DEFAULT 0,
  cache_read_tokens   INTEGER NOT NULL DEFAULT 0,
  cache_write_tokens  INTEGER NOT NULL DEFAULT 0,
  usd           REAL NOT NULL,         -- estimate at write time
  usd_source    TEXT NOT NULL,         -- 'claude_cli_estimate'|'openrouter_cost'|'price_table'|'zero_local'
  wall_ms       INTEGER NOT NULL,
  status        TEXT NOT NULL,         -- ok|error|killed|capped
  external_id   TEXT,                  -- claude session_id / openrouter gen-id
  reconciled_usd REAL,                 -- filled by nightly reconcile from admin APIs
  started_at    TEXT NOT NULL,         -- UTC ISO8601
  UNIQUE(provider, external_id)
);
CREATE INDEX ledger_day ON ledger(substr(started_at,1,10));

Write one row per model call (or per claude -p run using modelUsage rows), including failures. A nightly job fills reconciled_usd from the Anthropic cost report and OpenRouter, and alerts when drift exceeds e.g. 15%.

B4. Daily cap and kill switch pattern

  1. Pre-flight gate (before every model call and before each claude -p): SELECT SUM(usd) FROM ledger WHERE day=today plus an in-flight reservation (worst-case estimate for this call). If spent + reservation > DAILY_CAP_USD (say 80% soft, 100% hard), refuse and log status=capped. Mirrors the "blocking guardrail runs before spend" idea in the OpenAI Agents SDK guardrails (docs).
  2. Per-run cap: pass --max-budget-usd to claude -p, set provider max_tokens, and a wall-clock timeout; budget per task type, not just per day.
  3. Kill switch file: a path such as ~/ria/KILL checked by the gate and by a Claude Code PreToolUse hook (hooks run deterministically and can block tool calls: https://code.claude.com/docs/en/hooks-guide). If present, every run exits immediately. Also expose it as an MCP admin tool, not on the public surface.
  4. Circuit breaker: if hourly spend exceeds N times the trailing average, or 3 consecutive runs error, auto-create the kill file and alert Byron (his usual Signal/slog path).
  5. Provider-side backstops: a separate Anthropic workspace with its own spend limit if available in Console, OpenRouter key credit limit, and LiteLLM per-key max_budget. These catch bugs in your own ledger. Confirm the exact settings in each console [unverified].
  6. Cheap-first cascade: screen with Ollama or Haiku-class, escalate only top items to a large model (the pattern NewsUpdate uses: gpt-4o-mini screening, gpt-4o analysis).
  7. Never trust a single estimate: the Claude CLI total is client-side; reconcile.

B5. Caveats