feat: add coaching MCP tool for personalized engineering guidance#64
Merged
feat: add coaching MCP tool for personalized engineering guidance#64
Conversation
New `coaching` MCP tool that synthesizes multiple data sources into a single, prioritized coaching brief — what's working, what's not, and what to try next. Backend: - New coaching_service.py orchestrates calls to overview, maturity, friction, personalized_tips, and config_optimization services - Returns CoachingBrief with 3 sections: friction hotspots, skill opportunities, and top recommendations - Friction section includes specific fix suggestions per friction type - Skills section detects model diversity gaps, orchestration gaps, and cache efficiency issues - GET /api/v1/analytics/coaching endpoint (engineer-scoped) MCP: - New `coaching` tool in sidecar (6th tool) - Formats API response as readable markdown - Usage: @primer coaching or @primer coaching days=7 Tests: - test_coaching_requires_engineer_key (admin rejected) - test_coaching_empty (no sessions) - test_coaching_with_sessions (populated sections) - test_coaching_days_param Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Truthiness check on leverage_score skips zero values
- Changed
if profile and profile.leverage_score:toif profile and profile.leverage_score is not None:so a valid score of 0.0 is no longer skipped.
- Changed
Or push these changes by commenting:
@cursor push 54a29252c3
Preview (54a29252c3)
diff --git a/src/primer/server/services/coaching_service.py b/src/primer/server/services/coaching_service.py
--- a/src/primer/server/services/coaching_service.py
+++ b/src/primer/server/services/coaching_service.py
@@ -23,7 +23,7 @@
parts = [f"{overview.total_sessions} sessions"]
if overview.success_rate is not None:
parts.append(f"{_fmt_pct(overview.success_rate)} success rate")
- if profile and profile.leverage_score:
+ if profile and profile.leverage_score is not None:
parts.append(f"Leverage: {profile.leverage_score:.1f}")
if profile and profile.effectiveness_score is not None:
parts.append(f"Effectiveness: {profile.effectiveness_score:.1f}")Truthiness check skipped valid score of 0.0. Now uses `is not None` consistently for both leverage and effectiveness scores. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Ambiguous percentage formatting produces wrong output near boundary
- Removed the ambiguous
value <= 1.0heuristic and now always multiply by 100, since all callers pass 0–1 ratios.
- Removed the ambiguous
Or push these changes by commenting:
@cursor push 3b493d2927
Preview (3b493d2927)
diff --git a/src/primer/server/services/coaching_service.py b/src/primer/server/services/coaching_service.py
--- a/src/primer/server/services/coaching_service.py
+++ b/src/primer/server/services/coaching_service.py
@@ -16,7 +16,7 @@
def _fmt_pct(value: float | None) -> str:
if value is None:
return "N/A"
- return f"{value * 100:.0f}%" if value <= 1.0 else f"{value:.0f}%"
+ return f"{value * 100:.0f}%"
def _build_status(overview, profile) -> str:Always multiply by 100 since success_rate is always a 0-1 ratio. The <= 1.0 heuristic was fragile with floating-point edge cases. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix prepared fixes for both issues found in the latest run.
- ✅ Fixed: Misleading fallback message when profile data is absent
- Added a separate fallback branch when
profile is Nonethat shows an insufficient-data message instead of the misleading 'well-diversified' congratulation.
- Added a separate fallback branch when
- ✅ Fixed: Grammar error when model count is zero
- Changed the condition from
model_count <= 1tomodel_count == 1so that the zero case is excluded, avoiding both the grammar error and the misleading advice.
- Changed the condition from
Or push these changes by commenting:
@cursor push 551178f7c1
Preview (551178f7c1)
diff --git a/src/primer/server/services/coaching_service.py b/src/primer/server/services/coaching_service.py
--- a/src/primer/server/services/coaching_service.py
+++ b/src/primer/server/services/coaching_service.py
@@ -63,9 +63,9 @@
def _build_skills_section(profile, tips) -> CoachingSection:
items = []
if profile:
- if profile.model_count <= 1:
+ if profile.model_count == 1:
items.append(
- f"You use {profile.model_count} model. "
+ "You use 1 model. "
"Try cheaper models for simple tasks — "
"Haiku is 3-5x cheaper for lookups and quick edits."
)
@@ -88,10 +88,16 @@
items.append(tip.description)
if not items:
- items.append(
- "Your tool usage is well-diversified. "
- "Look at the Growth page for shared patterns from top performers."
- )
+ if profile is None:
+ items.append(
+ "Not enough session data to assess tool usage yet. "
+ "Keep using AI tools and check back later."
+ )
+ else:
+ items.append(
+ "Your tool usage is well-diversified. "
+ "Look at the Growth page for shared patterns from top performers."
+ )
return CoachingSection(title="Where you could level up", items=items)- model_count == 1 (not <= 1) to avoid "You use 0 model" message - Distinct fallback when profile is None (insufficient data) vs when profile exists but no gaps found (well-diversified) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

Summary
New
coachingMCP tool that synthesizes multiple analytics into a single, prioritized coaching brief. Engineers call@primer coachingin Claude Code and get a personalized report: what's working, what's slowing them down, and what to try next.What it returns
Architecture
coaching_service.py(new) — orchestrates calls to 5 existing services (overview, maturity, friction, personalized_tips, config_optimization), then synthesizes into aCoachingBriefGET /api/v1/analytics/coaching— API endpoint, engineer-scoped (rejects admin keys)coachingMCP tool — 6th tool in the sidecar, formats API response as markdownThe coaching service is a pure synthesis layer — no new analytics computation, just smart combination of existing data with friction-specific fix suggestions and skill gap detection.
Tests (4 new)
daysparameterTest plan
ruff checkandruff formatclean🤖 Generated with Claude Code
Note
Medium Risk
Adds a new engineer-scoped analytics endpoint and synthesis service that aggregates multiple existing analytics calls; main risk is correctness/performance of the combined queries and ensuring auth scoping stays strict (admin keys are explicitly rejected).
Overview
Adds a new personalized coaching brief surfaced end-to-end: a
CoachingBrief/CoachingSectionresponse schema, a newGET /api/v1/analytics/coachingendpoint (engineer-scoped; returns 400 for admin keys), and a newcoaching_service.get_coaching_briefthat synthesizes overview, maturity, friction, personalized tips, and config optimization into three prioritized sections.Exposes this via the sidecar as a new MCP
coachingtool (primer_coaching) that calls the new endpoint and renders the response as markdown, and adds backend tests covering auth rejection, empty-state, populated sessions, and thedaysquery param.Written by Cursor Bugbot for commit 4608385. This will update automatically on new commits. Configure here.