Repository navigation
kcap mcp analytics: governed SQL analytics tools for coding agents - #344
Conversation
|
/review |
Code Review by Qodo
1.
|
New stdio MCP server with two read-only tools wrapping the kcap-server /api/analytics endpoints: get_analytics_schema (the governed schema document) and query_analytics (one governed Postgres SELECT, defaulting to the cwd repo scope, fail-closed when the repo is unresolvable, with scope 'global' and max_rows options). Success bodies pass through as raw JSON plus an explicit truncation trailer; 400s surface the server validator's reason as 'REJECTED: ...' so the agent self-repairs; 404 maps to an upgrade-kcap-server hint. Structure cloned from McpMemoryServer (deferred authed client, refresh retry, guarded dispatch). Registration v1 targets Claude Code + Codex only: kcap-analytics joins KcapMcpServers.All (ReadOnly, so Codex gets per-server trust) and both bundled manifests, and is name-filtered out of ForCursor (the kcap-workitems pattern) pending the wider harness rollout. Registry descriptor is StartsFlows: false and NOT review-flow auto-approvable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A parenthetical trailer is easy for a model to skim past. State the consequence (statistics over a truncated listing are unreliable) and prescribe the fix (aggregate in SQL, which runs over all rows server-side) so an agent can't mistake a capped sample for the population. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The "Codex MCP servers registered: kcap-review, kcap-sessions" line was a hardcoded two-name list that predated kcap-memory and now kcap-analytics. After this PR ForCodex registers four servers, so the success message under-reported the actual MCP surface and contradicted the analytics rollout docs. Build the list from KcapMcpServers.ForCodex so it can never drift again, and de-enumerate the matching doc-comments in CodexConfigToml. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The per-server trust paragraph listed only kcap-review and kcap-sessions as auto-approved, but kcap-analytics is ReadOnly and in ForCodex, so CodexConfigToml now stamps default_tools_approval_mode="approve" on it too. Split the Gemini vs Codex lists so the public docs state that Codex runs the analytics read tool without prompting (Gemini isn't offered analytics in v1). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR adds kcap-analytics to both bundled plugin descriptors (.mcp.json for Claude, .codex-mcp.json for Codex), but the plugin README documented no such server and still claimed "Two stdio servers" while already listing three. Add a kcap-analytics section and drop the hardcoded count so the plugin docs match what the plugin actually auto-installs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CLAUDE.md forbids Linear issue numbers in comments and CI enforces it (scripts/check-linear-ids.sh). The analytics MCP work introduced six AI-xxxx references in comments across src and test; rewrite them to keep the explanatory intent without the external tracker id. Guard now passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ling Two Qodo findings: Reliability: sql/scope/name and the JSON-RPC method were read with JsonNode.GetValue<T>(), which throws InvalidOperationException on a wrong-typed value (common from LLM clients). Those escaped the local catch(ArgumentException) and were masked by the outer catch-all as a generic "internal error", denying the agent a field-specific message to self-repair from. Read them tolerantly (TryGetValue, mirroring McpReviewServer.TryGetStringArg), and translate any residual InvalidOperationException/FormatException/JsonException into a tool error rather than the internal-error fallback. Maintainability: BuildQueryBody called McpMemoryServer.TryReadInt, coupling two independent servers at compile time. Give this server its own private TryReadInt, matching the established per-server convention (-sessions, -memory, -workitems each carry their own copy). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Condense the class summary, deferred-client note, and MapResponse doc per the concise-comments convention, keeping the load-bearing rationale (the nullable-field-not-Lazy pointer, why a bare truncated flag is model-missable, and the McpMemoryServer clone provenance). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adding kcap-analytics grew KcapMcpServers.All from five to six, but this writer test still hard-asserted Count == 5, so it failed once the server was added. Derive the expected count from KcapMcpServers.All.Count (as the sibling test already does) so it tracks the set instead of drifting again. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…spatch The defensive arg-parsing pass still read params/arguments via AsObject(), which throws InvalidOperationException on a wrong-SHAPED value (e.g. an array where an object is expected) — and those conversions ran before the guarding try, so the throw fell through to the outer catch-all as the generic "internal error", the very dead-end the pass set out to remove. Use the non-throwing `as JsonObject` cast so a malformed params/arguments degrades to null and becomes an actionable protocol/tool error. Adds a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The default status branch dumped the raw response body, so any status outside the documented 400/401/404/408 set (e.g. a 403 or a 429 from a rate limiter or intermediary) gave the agent a raw RFC-7807 JSON envelope instead of the clean, actionable `detail` — the same noise the 400 path exists to avoid. Run ExtractProblemDetail in the default branch too; it falls back to the raw body when the response isn't a problem document (e.g. an HTML 502 from a proxy). Adds coverage for both paths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit 93b65b4 |
HandleToolCallAsync caught ArgumentException/HttpRequestException and the JSON type errors, but not OperationCanceledException — so an HttpClient client-side timeout (TaskCanceledException) on a slow/hung query fell through to the outer catch-all as the generic "internal error", never reaching the server-408 "narrow the date range or aggregate" hint (which only fires when the server itself responds 408). Add a catch that returns the same hint, and hoist that message into a shared TimedOutMessage constant so the client and 408 paths can't drift. Adds a regression test with a timing-out handler. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit 13b6695 |
Both timeout paths (client-side OperationCanceledException and server 408) returned the query-specific "narrow the date range or aggregate" hint even for get_analytics_schema, which has no query to narrow — misleading recovery guidance. Branch on the tool via a small TimeoutHintFor helper: query hint for query_analytics, a generic "Schema fetch timed out — retry." for the schema fetch. Covers both the 408 mapping and the client-timeout catch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
/review |
|
Code review by qodo was updated up to the latest commit f9fceb6 |
Drop kcap-analytics from the ForCursor name-filter exclusion so it registers for every non-Claude JSON harness (Cursor, Copilot, OpenCode, Kiro, Gemini, Antigravity) — the same CWD-resolved writer path already used for kcap-sessions. Only kcap-workitems stays excluded (its session-id default rides the Claude hook env). Gemini auto-approves read-only servers via "trust": true, so analytics now inherits that on Gemini alongside kcap-review/kcap-sessions. Flip the exact-set / contract / per-harness registration tests and update the README, plugin README, and help-mcp.txt to drop the "Claude Code + Codex only" language. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The property already states it "omits only kcap-workitems", so spelling out kcap-analytics's inclusion rationale was redundant. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ForCursor and ForCodex now select the same set (both exclude only kcap-workitems), so "Unlike Codex, these still get kcap-flows" asserted a difference that no longer exists — Codex receives flows too (#346). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
kcap-workitems resolves its default session id from the generic KCAP_SESSION_ID (with a CODEX_THREAD_ID fallback), not a Claude-specific hook env — so "rides the Claude hook env" was wrong and contradicted the real policy (workitems is Claude-plugin-only, excluded from ForCodex too). State the policy, not a false mechanism. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…kcap-analytics-mcp-to-remaining-harnesses [AI-1475] Roll out kcap-analytics MCP to remaining JSON harnesses
|
Code review by qodo was updated up to the latest commit 056174f |
PR Summary by QodoAdd kcap-analytics MCP server for governed SQL analytics queries
AI Description
Diagram
High-Level Assessment
Files changed (25)
|
Closes #343. Linear: AI-1477 (parent AI-1468). Server side: kurrent-io/kcap-server#1157 + kurrent-io/kcap-server#1158 (merge those first — against an older server the tools return a clear "upgrade kcap-server" message).
What
New
kcap mcp analyticsstdio server with two read-only tools wrapping the bearer-authed/api/analyticsendpoints:get_analytics_schema— fetches the governed schema document (views/columns, glossary, SQL rules, worked examples) and unwraps thetextenvelope. Description steers agents to call it once before writing SQL.query_analytics(sql, scope? "repo"|"global", max_rows?)— one governed Postgres SELECT. Defaults to the cwd-derived repo hash; fail-closed when the repo is unresolvable (error suggestsscope: "global", mirroringMcpMemoryServer.BuildSaveBody). Success bodies pass through as raw JSON with an explicit truncation trailer; 400 →REJECTED: {validator reason}for agent self-repair; 408 → narrow-the-query hint; 401 → not-logged-in; 404 → upgrade hint.Structure cloned from
McpMemoryServer(deferred authenticated client, 401 refresh retry, guarded dispatch, AOT-safe JsonNode bodies).Registration (v1: Claude Code + Codex only)
KcapMcpServers.Allgainskcap-analytics(ReadOnly: true→ Codex per-server trust;NeedsProjectCwd: true) — the contract tests derive both bundled manifests from this, and both are updated.ForCursorname-filters it out (thekcap-workitemspattern), so Cursor/Copilot/OpenCode/Kiro/Gemini/Antigravity don't get it yet — widening is AI-1475.KcapMcpRegistry:StartsFlows: false, deliberately not added to the review-flow auto-approvable sets.help-mcp.txt,help-usage.txt) + README (overview + new "Analytics MCP server" section) in this PR.Tests
REJECTED:detail, status→message table), tools-list schema.KcapMcpServersTests(6 canonical servers, ForCodex/ForCursor sets, ReadOnly pin),McpCanonicalContractTests(Codex includes / Cursor excludes analytics),PluginCommandCursorTests(cursor mcp.json excludes analytics),AcpHostedAgentRuntimeFactoryTests(+kcap-analyticsas non-auto-approvable).vswhere.exemissing), unrelated to this change — CI runs the real publish.🤖 Generated with Claude Code