feat(mcp): expose get_competitor_comparison tool (with share of voice) + REST endpoint#116
Merged
Merged
Conversation
…) + REST endpoint Adds the third A-group MCP read tool from the assistant-readiness list, after the content-opportunity tools shipped in #74 / #109 / #110. Combines two existing RPCs (competitor_aggregates and share_of_voice_aggregates, both DB-side aggregated since #114) so the tool answers "how do I compare to my competitors?" and "who's gaining share of voice?" in a single round trip. Ownership-check first via supabaseAdmin (wrong-org brand → null → 404, no RPC fire). Snapshot shape — deltas are out of scope for V1 and consistent with the existing get_visibility_summary / list_topics / list_prompts shape; a caller that wants a delta issues a second call with an earlier window. - web/src/lib/mcp/data.ts: getCompetitorComparisonFor(auth, params) — parallel RPC calls, computes brand + per-competitor averages (when-present semantics matching the UI), and per-(model_used, platform) SoV split. Uses the same competitor display-name fallback pattern as the existing competitor logic (name ?? competitor_id) so unnamed competitors don't collide. - web/src/lib/mcp/server.ts: registers the get_competitor_comparison tool with a description that covers both questions the tool answers and the snapshot-vs-delta caveat. - web/src/app/api/mcp/competitor-comparison/route.ts: GET handler sharing the same data fn; query params mirror the MCP tool args. Verified end-to-end against the largest brand in the cloud project (5054 prompt result appearances, 7 competitors, 10 distinct model/ platform combos) — returned shape matches the documented interface, SoV totals + per-platform breakdown reconcile with the existing UI. Closes #98.
This was referenced May 28, 2026
Closed
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #98. Third A-group MCP read tool — combines the competitor benchmark and share-of-voice surfaces into a single tool call.
What
Why
The assistant epic (#94) explicitly needs this tool to answer "who's gaining share of voice?" and "how do I compare to competitor X?" — both currently dashboard-only. Bundling competitor benchmark and SoV in one call also saves the assistant an extra round trip vs. two separate tools.
Shape of the response
```json
{
"brand": {
"id": "...", "name": "...",
"avg_visibility_score": 76,
"total_mentions": 26913, "total_citations": 20847,
"appearance_count": 5054
},
"competitors": [
{
"competitor_id": "...", "name": "...",
"avg_visibility_score": 81,
"total_mentions": 21788, "total_citations": 14201,
"appearance_count": 5054
}
],
"share_of_voice": {
"overall_sov_pct": 18.2,
"total_brand_mentions": 26913,
"total_competitor_mentions": 121335,
"by_platform": [
{ "model_used": "...", "platform": "...",
"brand_mentions": ..., "competitor_mentions": ..., "sov_pct": ... }
]
}
}
```
Auth + safety
Ownership check first via `supabaseAdmin` (same brand-belongs-to-org pattern as the other MCP data fns). Wrong-org or missing `brand_id` returns null (→ 404) before either RPC fires — no data leakage, no wasted DB work. Both RPCs (`competitor_aggregates`, `share_of_voice_aggregates`) are SECURITY DEFINER per the existing project pattern; the membership-enforcement question is tracked in #115 and applies to every aggregate RPC uniformly, not just this new tool.
Why a snapshot, not a delta
V1 returns a single-window snapshot — same shape contract as the existing `get_visibility_summary` / `list_topics` / `list_prompts` MCP tools. A caller that wants a delta (e.g. last 30 days vs the 30 before) makes two tool calls with different `date_from`/`date_to` and diffs in its own reducer. Building delta into the tool surface adds an opinionated 7-day default that wouldn't generalize cleanly across MCP clients.
Performance
Two existing DB-side aggregate RPCs in parallel (`Promise.all`). No JS-side row-by-row reduction; no `select('*')` from `prompt_results`. Brand + per-competitor / per-platform aggregates come back as JSONB. Same shape the dashboard already consumes since #114 — re-uses that work end-to-end.
Files
How verified
Local e2e against the cloud project:
Out of scope (per #98)
Up next
After this lands, #97 (`list_citations`) and #96 (`get_visibility_trend`) are the remaining A-group tools the assistant epic #94 needs.