- scripts/worker-quota-probe.sh: curl-probe die rate-limit-headers parset en { remaining, limit, pct, reset_at_iso } JSON retourneert
- mcp-integration.md: pre-flight sectie uitgebreid met bash-script voorbeeld, retry-strategie (sleep tot reset+5s), bekende beperking
- claude-question-channel.md: M13 quota-gate flow diagram + pre-flight loop uitleg
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
133 lines
8.2 KiB
Markdown
133 lines
8.2 KiB
Markdown
---
|
|
title: "MCP Integration — Scrum4Me Tools"
|
|
status: active
|
|
audience: [ai-agent]
|
|
language: nl
|
|
last_updated: 2026-05-03
|
|
when_to_read: "When using MCP tools to interact with the Scrum4Me backlog."
|
|
---
|
|
|
|
# MCP-integratie
|
|
|
|
Scrum4Me heeft een eigen MCP-server in repo [`madhura68/scrum4me-mcp`](https://github.com/madhura68/scrum4me-mcp) die de REST-API als native tools voor Claude Code aanbiedt. Schema's worden gedeeld via een git submodule (`vendor/scrum4me`), niet gedupliceerd.
|
|
|
|
## Tools beschikbaar in Claude Code (18)
|
|
|
|
**Read / context:**
|
|
- `mcp__scrum4me__health` — service + DB ping
|
|
- `mcp__scrum4me__list_products` — producten waar de tokengebruiker toegang tot heeft
|
|
- `mcp__scrum4me__get_claude_context` — bundled product / actieve sprint / next story (met tasks) / open todos
|
|
|
|
**Authoring (PBI/Story/Task aanmaken):**
|
|
- `mcp__scrum4me__create_pbi` — `{ product_id, title, description?, priority, sort_order? }`; auto sort_order = last+1 binnen prio-groep
|
|
- `mcp__scrum4me__create_story` — `{ pbi_id, title, description?, acceptance_criteria?, priority, sort_order? }`; product_id afgeleid uit PBI; status=OPEN
|
|
- `mcp__scrum4me__create_task` — `{ story_id, title, description?, implementation_plan?, priority, sort_order? }`; sprint_id geërfd van story; status=TO_DO
|
|
- `mcp__scrum4me__create_todo` — losse todo (optioneel product-scoped)
|
|
|
|
**Task / story writes:**
|
|
- `mcp__scrum4me__update_task_status`, `mcp__scrum4me__update_task_plan`
|
|
- `mcp__scrum4me__log_implementation`, `mcp__scrum4me__log_test_result`, `mcp__scrum4me__log_commit`
|
|
|
|
**Vraag-antwoord-kanaal (M11):**
|
|
- `mcp__scrum4me__ask_user_question` — post een vraag over een story; optionele `wait_seconds` (max 600) polt voor het antwoord
|
|
- `mcp__scrum4me__get_question_answer` — huidige status + antwoord (voor latere session-pickup)
|
|
- `mcp__scrum4me__list_open_questions` — eigen vragen, max 50, recente eerst
|
|
- `mcp__scrum4me__cancel_question` — asker-only annulering van een eigen open vraag
|
|
|
|
**Worker-instellingen + quota (M13):**
|
|
- `mcp__scrum4me__get_worker_settings` — geen args; retourneert `{ min_quota_pct }` afgeleid van het Bearer-token. Gebruik dit voor de pre-flight quota-check.
|
|
- `mcp__scrum4me__worker_heartbeat` — optionele args: `last_quota_pct` (0-100), `last_quota_check_at` (ISO-8601), `is_low` (boolean). Slaat quota-state op in `ClaudeWorker` en triggert een `worker_heartbeat` SSE-event zodat de NavBar een stand-by badge kan tonen.
|
|
|
|
**Job queue — agent worker mode (M13):**
|
|
- `mcp__scrum4me__wait_for_job` — blokkeert ≤600s, claimt atomisch een QUEUED-job via FOR UPDATE SKIP LOCKED. **Sinds M12** retourneert de payload een `kind`-discriminator:
|
|
- `kind: 'TASK_IMPLEMENTATION'` (default) — payload met `implementation_plan`, `story`, `pbi`, `sprint`, `repo_url`
|
|
- `kind: 'IDEA_GRILL'` of `'IDEA_MAKE_PLAN'` — payload met `idea`, `product`, `repo_url`, en `prompt_text` (de embedded prompt uit `lib/idea-prompts/`)
|
|
Stale CLAIMED-jobs (>30min) worden eerst terug naar QUEUED gezet. Lege queue na block-time = klaar.
|
|
- `mcp__scrum4me__update_job_status` — agent rapporteert `running|done|failed` + optionele branch/summary/error; triggert automatisch SSE-event. Bij `failed` voor `IDEA_GRILL`/`IDEA_MAKE_PLAN` wordt de idea-status automatisch op `GRILL_FAILED` resp. `PLAN_FAILED` gezet. Auth: Bearer-token moet matchen `claimed_by_token_id`. Optionele token-velden: `model_id` (string), `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_write_tokens` (alle non-negative int) — worden opgeslagen op de ClaudeJob-rij bij done/failed.
|
|
|
|
**Idea-jobs (M12) — agent gedrag per kind:**
|
|
|
|
| Kind | Werkwijze | Eind-call |
|
|
|---|---|---|
|
|
| `IDEA_GRILL` | Lees `prompt_text` (embedded grill-prompt) + `idea.grill_md` als startpunt; itereer met `ask_user_question(idea_id=...)`/`get_question_answer`; log onderweg `log_idea_decision`; eindig met `update_idea_grill_md(markdown)` | `update_job_status('done')` |
|
|
| `IDEA_MAKE_PLAN` | Lees `prompt_text` (embedded make-plan-prompt) + `idea.grill_md` + repo-context. **Stel GEEN vragen** — single-pass output. Bouw plan in strict yaml-frontmatter format en eindig met `update_idea_plan_md(markdown)`. Server-side parser kan parse-fail → `PLAN_FAILED` | `update_job_status('done')` |
|
|
|
|
**MCP-tools — Idea-laag (M12):**
|
|
- `mcp__scrum4me__get_idea_context(idea_id)` — `{ idea, product, repo_url, grill_md_so_far, open_questions, prompt_text }`
|
|
- `mcp__scrum4me__update_idea_grill_md(idea_id, markdown)` — schrijft veld; status → `GRILLED`; logt `IdeaLog{GRILL_RESULT}`
|
|
- `mcp__scrum4me__update_idea_plan_md(idea_id, markdown)` — server-side `parsePlanMd`; ok → `PLAN_READY` + `IdeaLog{PLAN_RESULT}`; parse-fail → `PLAN_FAILED` + `IdeaLog{JOB_EVENT, errors}`
|
|
- `mcp__scrum4me__log_idea_decision(idea_id, type, content, metadata?)` — `type ∈ {DECISION, NOTE}`
|
|
- `mcp__scrum4me__ask_user_question` — geüpgrade contract: exact één van `story_id` óf `idea_id` (xor); idea-vragen zijn user-private (geen productAccessFilter).
|
|
|
|
## Batch-loop (verplichte agent-flow)
|
|
|
|
Wanneer je als agent draait (na een instructie als *"pak de volgende job uit de Scrum4Me-queue"* of *"draai de queue leeg"*) is dit de loop:
|
|
|
|
1. `wait_for_job` aanroepen.
|
|
2. Switch op `kind`:
|
|
- `TASK_IMPLEMENTATION`: voer uit volgens het meegegeven `implementation_plan` (zoals altijd — branch, code, commit, push, verify_task_against_plan).
|
|
- `IDEA_GRILL`: laad `prompt_text` als gids; gebruik `ask_user_question` / `get_question_answer` voor de Q&A-loop; eindig met `update_idea_grill_md`.
|
|
- `IDEA_MAKE_PLAN`: laad `prompt_text` + `idea.grill_md`; **stel geen vragen**; produceer strict yaml-frontmatter; eindig met `update_idea_plan_md`.
|
|
3. `update_job_status('done'|'failed')` aanroepen.
|
|
4. **Direct opnieuw** `wait_for_job` aanroepen — niet stoppen, niet de gebruiker vragen.
|
|
5. Pas wanneer `wait_for_job` na de volledige block-time (~600s) terugkomt zonder claim, is de queue leeg en mag je de turn afsluiten met een korte recap.
|
|
|
|
Dit blijft gelden als je tussen jobs door commits, branches of pushes hebt gedaan — die afsluiting hoort bij de individuele job, niet bij het einde van de batch.
|
|
|
|
**Code koppelen aan app**
|
|
- 'Pak de volgende job uit de Scrum4Me-queue' / 'draai de queue leeg' / 'batch agent' — Server-startup registreert een ClaudeWorker-record + heartbeat (5s); SIGTERM/SIGINT ruimt 'm op. UI in NavBar telt actieve workers via `last_seen_at < now() - 15s`.
|
|
|
|
## Pre-flight quota-check
|
|
|
|
Vóór elke `wait_for_job` kan de worker controleren of er voldoende Claude-quota is.
|
|
|
|
**Helper-script:** `scripts/worker-quota-probe.sh` doet een minimale API-call (1 token) en geeft JSON terug:
|
|
|
|
```json
|
|
{ "remaining": 180000, "limit": 200000, "pct": 90, "reset_at_iso": "2026-05-06T13:00:00Z" }
|
|
```
|
|
|
|
**Stappenplan:**
|
|
|
|
1. Haal drempel op: `get_worker_settings` → `min_quota_pct` (bijv. 20).
|
|
2. Probe quota:
|
|
|
|
```bash
|
|
PROBE=$(bash scripts/worker-quota-probe.sh)
|
|
PCT=$(echo "$PROBE" | grep -o '"pct":[0-9]*' | cut -d: -f2)
|
|
RESET_AT=$(echo "$PROBE" | grep -o '"reset_at_iso":"[^"]*"' | cut -d'"' -f4)
|
|
```
|
|
|
|
3. Vergelijk en meld via `worker_heartbeat`:
|
|
|
|
```
|
|
worker_heartbeat(
|
|
last_quota_pct=<PCT>,
|
|
last_quota_check_at=<nu als ISO-8601>,
|
|
is_low=<PCT < min_quota_pct>
|
|
)
|
|
```
|
|
|
|
4. Bij low quota: retry-strategie — slaap tot reset + 5s:
|
|
|
|
```bash
|
|
if [ "$PCT" -lt "$MIN_QUOTA_PCT" ]; then
|
|
RESET_S=$(date -d "$RESET_AT" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$RESET_AT" +%s 2>/dev/null || echo 0)
|
|
NOW_S=$(date +%s)
|
|
SLEEP_S=$(( RESET_S - NOW_S + 5 ))
|
|
echo "Wachten tot quota-reset om $(date -d "$RESET_AT" '+%H:%M' 2>/dev/null || echo "$RESET_AT")"
|
|
[ "$SLEEP_S" -gt 0 ] && sleep "$SLEEP_S"
|
|
fi
|
|
```
|
|
|
|
5. Na de sleep: herhaal stap 2 (re-probe) voordat `wait_for_job` wordt aangeroepen.
|
|
|
|
**Bekende beperking:** elke probe kost ~1 token; bij 12x/uur is de overhead 12 tokens/uur — verwaarloosbaar ten opzichte van taak-gebruik.
|
|
|
|
## Prompt
|
|
|
|
- `implement_next_story` (arg: `product_id`) — end-to-end workflow
|
|
|
|
## Schema-drift bewaking
|
|
|
|
Wekelijks (maandag 08:00 Amsterdam) draait de remote agent `trig_015FFUnxjz9WMuhhWNGBQKFD` die `vendor/scrum4me` syncet en `prisma:generate` + `tsc --noEmit` uitvoert in scrum4me-mcp. Als die agent drift rapporteert, hoort dat **vóór** een Scrum4Me-PR met schema-wijziging gemerged kan worden — anders breekt de MCP-server stilletjes op runtime.
|