Vier nieuwe tools voor het Claude vraag-antwoord-kanaal: - ask_user_question (write): post een gestructureerde vraag aan de actieve Scrum4Me-gebruiker over een story; default async (returnt direct met question_id + status='open'); optionele wait_seconds (max 600) polt elke 2s tot het antwoord er is of timeout — daarna status='pending' zodat Claude met get_question_answer later kan ophalen - get_question_answer (read): huidige status + antwoord van een eerder gestelde vraag - list_open_questions (read): eigen vragen met status open/answered, max 50, meest recente eerst - cancel_question (write, asker-only): atomic UPDATE WHERE asked_by + status= 'open' zodat alleen eigen open vragen geannuleerd worden Allemaal achter access-check via userCanAccessStory/Product en demo-blok via requireWriteAccess (volgt patroon van create-todo en bestaande log-tools). Submodule vendor/scrum4me bumpt naar Scrum4Me commit 79367dd (M11 ST-1101) — bevat het ClaudeQuestion-model en notify_question_change-trigger waar deze tools tegen werken. scripts/smoke-test.ts: 13 tools verwacht (was 9); list_open_questions toegevoegd als read-tool-coverage. Build + tools/list groen — verdere e2e via MCP Inspector na PR-merge omdat de seed een nieuwe API-token heeft gegenereerd en .env een nieuwe waarde nodig heeft. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
65 lines
2.2 KiB
TypeScript
65 lines
2.2 KiB
TypeScript
// ST-1102: get_question_answer — vraag de huidige status + antwoord op van een
|
|
// eerder gestelde vraag. Bedoeld voor Claude om in een latere sessie het
|
|
// resultaat op te pikken (voor wanneer ask_user_question async werd gebruikt).
|
|
|
|
import { z } from 'zod'
|
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
import { prisma } from '../prisma.js'
|
|
import { getAuth } from '../auth.js'
|
|
import { userCanAccessProduct } from '../access.js'
|
|
import { toolError, toolJson, withToolErrors } from '../errors.js'
|
|
|
|
const inputSchema = z.object({
|
|
question_id: z.string().min(1),
|
|
})
|
|
|
|
export function registerGetQuestionAnswerTool(server: McpServer) {
|
|
server.registerTool(
|
|
'get_question_answer',
|
|
{
|
|
title: 'Get question answer',
|
|
description:
|
|
'Fetch the current status and (if available) answer for a previously-asked Claude question. ' +
|
|
'Use this when ask_user_question was called with wait_seconds=0 (or timed out) and you want ' +
|
|
'to check whether the user has answered.',
|
|
inputSchema,
|
|
},
|
|
async ({ question_id }) =>
|
|
withToolErrors(async () => {
|
|
const auth = await getAuth()
|
|
const q = await prisma.claudeQuestion.findUnique({
|
|
where: { id: question_id },
|
|
select: {
|
|
id: true,
|
|
product_id: true,
|
|
story_id: true,
|
|
task_id: true,
|
|
status: true,
|
|
question: true,
|
|
options: true,
|
|
answer: true,
|
|
answered_by: true,
|
|
answered_at: true,
|
|
expires_at: true,
|
|
},
|
|
})
|
|
if (!q) return toolError(`Question ${question_id} not found`)
|
|
if (!(await userCanAccessProduct(q.product_id, auth.userId))) {
|
|
return toolError(`Question ${question_id} not accessible`)
|
|
}
|
|
|
|
return toolJson({
|
|
question_id: q.id,
|
|
status: q.status,
|
|
story_id: q.story_id,
|
|
task_id: q.task_id,
|
|
question: q.question,
|
|
options: q.options,
|
|
answer: q.answer,
|
|
answered_by: q.answered_by,
|
|
answered_at: q.answered_at?.toISOString() ?? null,
|
|
expires_at: q.expires_at.toISOString(),
|
|
})
|
|
}),
|
|
)
|
|
}
|