* feat(ST-mhj9f2la): add set_pbi_pr MCP tool - Add pr_url and pr_merged_at fields to Pbi model in schema - Implement set_pbi_pr tool: writes pr_url, clears pr_merged_at (idempotent) - AuthZ via requireWriteAccess + userCanAccessProduct through pbi.product_id - 10 tests: happy path, not-found, no-access, demo-denied, schema validation - Update README tools table and bump version to 0.2.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(ST-mhj9f2la): add mark_pbi_pr_merged MCP tool - Implement mark_pbi_pr_merged: sets pr_merged_at = now() on a PBI - Requires pr_url to be set; returns error if not (geen gekoppelde PR) - Idempotent: re-calling overwrites the timestamp - AuthZ via requireWriteAccess + userCanAccessProduct through pbi.product_id - 6 tests: happy path, no-pr_url, idempotent, no-access, not-found, demo-denied - Update README tools table with mark_pbi_pr_merged entry Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs(ST-mhj9f2la): expand README with set_pbi_pr + mark_pbi_pr_merged docs Add full signature/input/output/error documentation sections for both new tools, following the verify_task_against_plan pattern. Version already bumped to 0.2.0 in earlier commit. Tag + MCP_GIT_REF pin in scrum4me-docker to be done by maintainer after merge. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
48 lines
1.6 KiB
TypeScript
48 lines
1.6 KiB
TypeScript
import { z } from 'zod'
|
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
import { prisma } from '../prisma.js'
|
|
import { requireWriteAccess } from '../auth.js'
|
|
import { userCanAccessProduct } from '../access.js'
|
|
import { toolError, toolJson, withToolErrors } from '../errors.js'
|
|
|
|
const inputSchema = z.object({
|
|
pbi_id: z.string().min(1),
|
|
})
|
|
|
|
export async function handleMarkPbiPrMerged({ pbi_id }: z.infer<typeof inputSchema>) {
|
|
return withToolErrors(async () => {
|
|
const auth = await requireWriteAccess()
|
|
|
|
const pbi = await prisma.pbi.findUnique({
|
|
where: { id: pbi_id },
|
|
select: { product_id: true, pr_url: true },
|
|
})
|
|
if (!pbi || !(await userCanAccessProduct(pbi.product_id, auth.userId))) {
|
|
return toolError(`PBI ${pbi_id} not found or not accessible`)
|
|
}
|
|
if (!pbi.pr_url) {
|
|
return toolError(`PBI ${pbi_id} heeft geen gekoppelde PR`)
|
|
}
|
|
|
|
const updated = await prisma.pbi.update({
|
|
where: { id: pbi_id },
|
|
data: { pr_merged_at: new Date() },
|
|
select: { id: true, pr_url: true, pr_merged_at: true },
|
|
})
|
|
|
|
return toolJson({ ok: true, pbi_id, pr_url: updated.pr_url, pr_merged_at: updated.pr_merged_at })
|
|
})
|
|
}
|
|
|
|
export function registerMarkPbiPrMergedTool(server: McpServer) {
|
|
server.registerTool(
|
|
'mark_pbi_pr_merged',
|
|
{
|
|
title: 'Mark PBI PR Merged',
|
|
description:
|
|
'Set pr_merged_at = now() on a PBI, signalling the PR has been merged. Requires pr_url to already be set. Idempotent: re-calling overwrites the timestamp. Forbidden for demo accounts.',
|
|
inputSchema,
|
|
},
|
|
handleMarkPbiPrMerged,
|
|
)
|
|
}
|