MCP introspection & observability
Modules:
apps/mcp/src/resources.ts— read-only resource templates.apps/mcp/src/prompts.ts— best-practice prompt catalogue.apps/mcp/src/metrics.ts— in-memory Prometheus registry.apps/mcp/src/routes/introspection.ts— sidecar HTTP routes.
Resources
Four URI templates served via the MCP resources/list + resources/read standard endpoints:
| URI | Returns |
|---|---|
drobek://plans/{id} | plan row + tasks + edges |
drobek://tasks/{id} | full task entity |
drobek://knowledge/{id} | full knowledge body (vs the search snippet) |
drobek://orgs/{id}/stats | entitlements + current-period usage |
Cross-org reads (token from org A → resource of org B) → 403 auth.cross_org. Auth flows through an AsyncLocalStorage populated by the MCP request middleware in server.ts — resources cannot bypass it.
Prompts
Three canned templates served via prompts/list + prompts/get:
start-work-session— the canonical agent loop.summarize-knowledge— guidance forlearningsincomplete_task.review-needs-review— workflow for clearingneeds_reviewtasks.
Placeholders {{agent_name}}, {{org_slug}}, {{task_id}} are substituted server-side from the request's auth context before the body is shipped to the client.
Sidecar routes
| Route | Auth | Returns |
|---|---|---|
GET /metrics | scope audit:read | Prometheus text |
GET /audit?limit=&cursor= | scope audit:read | { items, nextCursor } |
GET /mcp/usage | any valid token | { rateLimit, entitlements, usage } |
Metric names:
drobek_mcp_tool_calls_total{tool, status}— counter.drobek_mcp_tool_duration_ms_bucket{tool, le}+_sum+_count—
histogram, buckets [5, 10, 25, 50, 100, 250, 500, 1000, 2500].
Audit cursor format: base64url-encoded {at: ISO, id: uuid} — descending order by (createdAt, id). The +1 overflow row is used to determine nextCursor without a second round-trip.