Vier nieuwe tools + propagateStatusUpwards uitbreiding:
T1 — verify_sprint_task (src/tools/verify-sprint-task.ts):
Execution-aware verify met frozen plan_snapshot. Input: execution_id +
worktree_path + optionele summary (voor PARTIAL/DIVERGENT-rationale).
Vult base_sha dynamisch voor task[1..N] op basis van vorige DONE-execution's
head_sha. Schrijft verify_result + verify_summary op execution-row.
Returns { result, reasoning, base_sha, allowed_for_done, reason? } —
allowed_for_done via standaard checkVerifyGate met snapshot-velden.
T2 — update_task_execution (src/tools/update-task-execution.ts):
Lifecycle-tool voor SprintTaskExecution: PENDING/RUNNING/DONE/FAILED/SKIPPED
+ base_sha/head_sha/skip_reason. Idempotent. Token-check via
execution.sprint_job.claimed_by_token_id. started_at/finished_at automatisch.
T3 — job_heartbeat (src/tools/job-heartbeat.ts):
Verlengt ClaudeJob.lease_until met 5 min via atomic conditional UPDATE
(token-check + status-check in WHERE). Voor SPRINT-jobs: response bevat
sprint_run_status + sprint_run_pause_reason zodat worker op UI-side cancel
of MERGE_CONFLICT-pause kan breken zonder extra query.
T4 — update_task_status sprint_run_id-arg + token-coupling
(src/tools/update-task-status.ts):
Optionele sprint_run_id-arg voor expliciete cascade. Validaties: SprintRun
bestaat + actief, task in deze sprint, current token heeft een actieve
ClaudeJob in deze run geclaimd (403 anders). Response uitgebreid met
sprint_run_status_change.
T5 — propagateStatusUpwards sprintRunId-param
(src/lib/tasks-status-update.ts):
Optionele sprintRunId-parameter. Resolve-volgorde: expliciete arg →
ClaudeJob.task_id-lookup → Story → Sprint → SprintRun.findFirst({active}).
De derde fallback dekt SPRINT_IMPLEMENTATION (geen task_id-koppeling) én
handmatige task-statuswijzigingen via UI. cancelExceptJobId voor
sibling-cancel; null voor SPRINT-job betekent geen siblings te cancellen.
src/index.ts: drie nieuwe tools geregistreerd.
Tests: 31 files, 243 passing (geen tests voor nieuwe tools nog — F5).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
124 lines
5.8 KiB
JavaScript
124 lines
5.8 KiB
JavaScript
#!/usr/bin/env node
|
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
import { registerHealthTool } from './tools/health.js'
|
|
import { registerListProductsTool } from './tools/list-products.js'
|
|
import { registerGetClaudeContextTool } from './tools/get-claude-context.js'
|
|
import { registerUpdateTaskStatusTool } from './tools/update-task-status.js'
|
|
import { registerUpdateTaskPlanTool } from './tools/update-task-plan.js'
|
|
import { registerLogImplementationTool } from './tools/log-implementation.js'
|
|
import { registerLogTestResultTool } from './tools/log-test-result.js'
|
|
import { registerLogCommitTool } from './tools/log-commit.js'
|
|
import { registerCreatePbiTool } from './tools/create-pbi.js'
|
|
import { registerCreateStoryTool } from './tools/create-story.js'
|
|
import { registerCreateTaskTool } from './tools/create-task.js'
|
|
import { registerAskUserQuestionTool } from './tools/ask-user-question.js'
|
|
import { registerGetQuestionAnswerTool } from './tools/get-question-answer.js'
|
|
import { registerListOpenQuestionsTool } from './tools/list-open-questions.js'
|
|
import { registerCancelQuestionTool } from './tools/cancel-question.js'
|
|
import { registerWaitForJobTool } from './tools/wait-for-job.js'
|
|
import { registerUpdateJobStatusTool } from './tools/update-job-status.js'
|
|
import { registerVerifyTaskAgainstPlanTool } from './tools/verify-task-against-plan.js'
|
|
import { registerCleanupMyWorktreesTool } from './tools/cleanup-my-worktrees.js'
|
|
import { registerCheckQueueEmptyTool } from './tools/check-queue-empty.js'
|
|
import { registerSetPbiPrTool } from './tools/set-pbi-pr.js'
|
|
import { registerMarkPbiPrMergedTool } from './tools/mark-pbi-pr-merged.js'
|
|
import { registerGetIdeaContextTool } from './tools/get-idea-context.js'
|
|
import { registerUpdateIdeaGrillMdTool } from './tools/update-idea-grill-md.js'
|
|
import { registerUpdateIdeaPlanMdTool } from './tools/update-idea-plan-md.js'
|
|
import { registerLogIdeaDecisionTool } from './tools/log-idea-decision.js'
|
|
import { registerGetWorkerSettingsTool } from './tools/get-worker-settings.js'
|
|
import { registerWorkerHeartbeatTool } from './tools/worker-heartbeat.js'
|
|
// PBI-50: SPRINT_IMPLEMENTATION-tools
|
|
import { registerVerifySprintTaskTool } from './tools/verify-sprint-task.js'
|
|
import { registerUpdateTaskExecutionTool } from './tools/update-task-execution.js'
|
|
import { registerJobHeartbeatTool } from './tools/job-heartbeat.js'
|
|
import { registerImplementNextStoryPrompt } from './prompts/implement-next-story.js'
|
|
import { getAuth } from './auth.js'
|
|
import { registerWorker } from './presence/worker.js'
|
|
import { startHeartbeat } from './presence/heartbeat.js'
|
|
import { registerShutdownHandlers } from './presence/shutdown.js'
|
|
|
|
import { readFileSync } from 'node:fs'
|
|
import { dirname, join } from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
// Read version dynamically from package.json — voorheen hardcoded en
|
|
// veroorzaakte sync-issues bij deployment. Lees op module-load.
|
|
function readPkgVersion(): string {
|
|
try {
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
const pkgPath = join(here, '..', 'package.json')
|
|
return (JSON.parse(readFileSync(pkgPath, 'utf8')) as { version?: string }).version ?? '0.0.0'
|
|
} catch {
|
|
return '0.0.0'
|
|
}
|
|
}
|
|
const VERSION = readPkgVersion()
|
|
|
|
async function main() {
|
|
const server = new McpServer(
|
|
{ name: 'scrum4me-mcp', version: VERSION },
|
|
{
|
|
instructions:
|
|
'Scrum4Me dev-flow tools: read product/sprint/story context, update tasks, log activity. ' +
|
|
'Always call get_claude_context before starting work to fetch the next story.',
|
|
},
|
|
)
|
|
|
|
registerHealthTool(server)
|
|
registerListProductsTool(server)
|
|
registerGetClaudeContextTool(server)
|
|
registerUpdateTaskStatusTool(server)
|
|
registerUpdateTaskPlanTool(server)
|
|
registerLogImplementationTool(server)
|
|
registerLogTestResultTool(server)
|
|
registerLogCommitTool(server)
|
|
registerCreatePbiTool(server)
|
|
registerCreateStoryTool(server)
|
|
registerCreateTaskTool(server)
|
|
registerAskUserQuestionTool(server)
|
|
registerGetQuestionAnswerTool(server)
|
|
registerListOpenQuestionsTool(server)
|
|
registerCancelQuestionTool(server)
|
|
registerWaitForJobTool(server)
|
|
registerUpdateJobStatusTool(server)
|
|
registerVerifyTaskAgainstPlanTool(server)
|
|
registerCleanupMyWorktreesTool(server)
|
|
registerCheckQueueEmptyTool(server)
|
|
registerSetPbiPrTool(server)
|
|
registerMarkPbiPrMergedTool(server)
|
|
// M12: idee-job tools
|
|
registerGetIdeaContextTool(server)
|
|
registerUpdateIdeaGrillMdTool(server)
|
|
registerUpdateIdeaPlanMdTool(server)
|
|
registerLogIdeaDecisionTool(server)
|
|
// M13: worker quota-gate tools
|
|
registerGetWorkerSettingsTool(server)
|
|
registerWorkerHeartbeatTool(server)
|
|
// PBI-50: SPRINT_IMPLEMENTATION-tools
|
|
registerVerifySprintTaskTool(server)
|
|
registerUpdateTaskExecutionTool(server)
|
|
registerJobHeartbeatTool(server)
|
|
registerImplementNextStoryPrompt(server)
|
|
|
|
// Presence bootstrap MUST run before server.connect — the stdio transport
|
|
// can stall the await on incoming messages, so anything after server.connect
|
|
// may never execute reliably. Registering the worker + starting the
|
|
// heartbeat first guarantees the UI sees the agent as soon as the process
|
|
// is up, regardless of when the MCP client sends its first request.
|
|
const auth = await getAuth()
|
|
await registerWorker({ userId: auth.userId, tokenId: auth.tokenId })
|
|
const { stop: stopHeartbeat } = startHeartbeat({ userId: auth.userId, tokenId: auth.tokenId })
|
|
registerShutdownHandlers({ userId: auth.userId, tokenId: auth.tokenId, stopHeartbeat })
|
|
|
|
const transport = new StdioServerTransport()
|
|
await server.connect(transport)
|
|
|
|
console.error(`scrum4me-mcp ${VERSION} running on stdio`)
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('Fatal error in scrum4me-mcp:', err)
|
|
process.exit(1)
|
|
})
|