MCP telemetry: per-call logging, aggregation route, and admin dashboard

Gadoes adds end-to-end observability for MCP tool calls: a 17-line `mcp_events` migration, telemetry logging inside `McpClientManager.callTool()`, a `GET /admin/mcp-telemetry` aggregation endpoint, and a Next.js admin page that auto-refreshes every 60 seconds.

analyticsintegration

Schema (migration 005). mcp_events stores user_id, source, tool, latency_ms, cache_hit, success, error_type, and created_at. Two indexes: one on created_at, one on source. The table is intentionally lightweight - no FK to chat_messages, no payload storage.

Logging. McpClientManager.callTool() gains an optional { userId, db } options argument. When provided, it inserts a row after each call with latency and success/error outcome. The chatTools.ts call site is updated to pass those values through. A source_unavailable SSE event is also emitted when the circuit is open, so the frontend can surface a per-source degradation message without waiting for a timeout.

Aggregation route. GET /admin/mcp-telemetry at routes/admin.ts (71 lines) queries mcp_events and returns per-source aggregates: total_calls, success_rate, avg_latency_ms, cache_hit_rate, errors_24h. Gated by the is_admin column added in Chunk 6.

Dashboard. The Next.js page at /admin/mcp-telemetry renders a table of those aggregates and re-fetches every 60 seconds via setInterval. Sample data in the test shows courtlistener at 340ms average latency and EUR-Lex at 820ms.

So what Worth a look if you're running external MCP sources in production - knowing which sources are slow, failing, or hitting cache frequently is genuinely useful. The `mcp_events` migration is tiny and the logging is optional (the callTool signature stays backward compatible). One dependency to check: the admin dashboard reads the `is_admin` column from the user profile table added in Chunk 6. If you pull telemetry without that chunk, you'll need to either add the column separately or swap the auth check.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

4 commits from Gadoes/dispumike, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
f7cb5d92 feat(chunk19): migration 005 - mcp_events telemetry table Gadoes 2026-05-02 ↗ GitHub
aabc7e28 feat(chunk19): telemetry logging in McpClientManager.callTool Gadoes 2026-05-02 ↗ GitHub
e37d7572 feat(chunk19): GET /admin/mcp-telemetry route with unit tests Gadoes 2026-05-02 ↗ GitHub
89fbb081 feat(chunk19): /admin/mcp-telemetry dashboard page with tests Gadoes 2026-05-02 ↗ GitHub

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-27.md from inside the repo you want the changes in.

⬇ Download capture-thread-27.md