feat(ST-707): log tools — implementation, test_result, commit
Three write tools that append StoryLog entries: - log_implementation: type=IMPLEMENTATION_PLAN - log_test_result: type=TEST_RESULT, status PASSED|FAILED - log_commit: type=COMMIT with hash and message All accept optional metadata in input but skip writing it for now — the StoryLog.metadata JSONB column lands with Scrum4Me PR #2. After that PR merges, run sync-schema and add `metadata` to each prisma.create's data field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e3f9476568
commit
c39b337fcc
4 changed files with 138 additions and 1 deletions
|
|
@ -6,6 +6,9 @@ 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'
|
||||
|
||||
const VERSION = '0.1.0'
|
||||
|
||||
|
|
@ -24,7 +27,10 @@ async function main() {
|
|||
registerGetClaudeContextTool(server)
|
||||
registerUpdateTaskStatusTool(server)
|
||||
registerUpdateTaskPlanTool(server)
|
||||
// Log tools, create_todo and prompts in ST-707..ST-709.
|
||||
registerLogImplementationTool(server)
|
||||
registerLogTestResultTool(server)
|
||||
registerLogCommitTool(server)
|
||||
// create_todo and prompts in ST-708..ST-709.
|
||||
|
||||
const transport = new StdioServerTransport()
|
||||
await server.connect(transport)
|
||||
|
|
|
|||
45
src/tools/log-commit.ts
Normal file
45
src/tools/log-commit.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import { z } from 'zod'
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||
import { prisma } from '../prisma.js'
|
||||
import { requireWriteAccess } from '../auth.js'
|
||||
import { userCanAccessStory } from '../access.js'
|
||||
import { toolError, toolJson, withToolErrors } from '../errors.js'
|
||||
|
||||
const inputSchema = z.object({
|
||||
story_id: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
commit_hash: z.string().min(1),
|
||||
commit_message: z.string().min(1),
|
||||
metadata: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
|
||||
export function registerLogCommitTool(server: McpServer) {
|
||||
server.registerTool(
|
||||
'log_commit',
|
||||
{
|
||||
title: 'Log commit',
|
||||
description:
|
||||
'Append a COMMIT entry to a story log with hash and message. ' +
|
||||
'Forbidden for demo accounts.',
|
||||
inputSchema,
|
||||
},
|
||||
async ({ story_id, content, commit_hash, commit_message }) =>
|
||||
withToolErrors(async () => {
|
||||
const auth = await requireWriteAccess()
|
||||
if (!(await userCanAccessStory(story_id, auth.userId))) {
|
||||
return toolError(`Story ${story_id} not found or not accessible`)
|
||||
}
|
||||
const log = await prisma.storyLog.create({
|
||||
data: {
|
||||
story_id,
|
||||
type: 'COMMIT',
|
||||
content,
|
||||
commit_hash,
|
||||
commit_message,
|
||||
},
|
||||
select: { id: true, created_at: true },
|
||||
})
|
||||
return toolJson({ id: log.id, created_at: log.created_at })
|
||||
}),
|
||||
)
|
||||
}
|
||||
43
src/tools/log-implementation.ts
Normal file
43
src/tools/log-implementation.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { z } from 'zod'
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||
import { prisma } from '../prisma.js'
|
||||
import { requireWriteAccess } from '../auth.js'
|
||||
import { userCanAccessStory } from '../access.js'
|
||||
import { toolError, toolJson, withToolErrors } from '../errors.js'
|
||||
|
||||
// metadata is accepted on input but not yet written — schema sync after
|
||||
// Scrum4Me PR #2 lands adds the StoryLog.metadata JSONB column.
|
||||
const inputSchema = z.object({
|
||||
story_id: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
metadata: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
|
||||
export function registerLogImplementationTool(server: McpServer) {
|
||||
server.registerTool(
|
||||
'log_implementation',
|
||||
{
|
||||
title: 'Log implementation plan',
|
||||
description:
|
||||
'Append an IMPLEMENTATION_PLAN entry to a story log. ' +
|
||||
'Forbidden for demo accounts.',
|
||||
inputSchema,
|
||||
},
|
||||
async ({ story_id, content }) =>
|
||||
withToolErrors(async () => {
|
||||
const auth = await requireWriteAccess()
|
||||
if (!(await userCanAccessStory(story_id, auth.userId))) {
|
||||
return toolError(`Story ${story_id} not found or not accessible`)
|
||||
}
|
||||
const log = await prisma.storyLog.create({
|
||||
data: {
|
||||
story_id,
|
||||
type: 'IMPLEMENTATION_PLAN',
|
||||
content,
|
||||
},
|
||||
select: { id: true, created_at: true },
|
||||
})
|
||||
return toolJson({ id: log.id, created_at: log.created_at })
|
||||
}),
|
||||
)
|
||||
}
|
||||
43
src/tools/log-test-result.ts
Normal file
43
src/tools/log-test-result.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { z } from 'zod'
|
||||
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
||||
import { prisma } from '../prisma.js'
|
||||
import { requireWriteAccess } from '../auth.js'
|
||||
import { userCanAccessStory } from '../access.js'
|
||||
import { toolError, toolJson, withToolErrors } from '../errors.js'
|
||||
|
||||
const inputSchema = z.object({
|
||||
story_id: z.string().min(1),
|
||||
content: z.string().min(1),
|
||||
status: z.enum(['PASSED', 'FAILED']),
|
||||
metadata: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
|
||||
export function registerLogTestResultTool(server: McpServer) {
|
||||
server.registerTool(
|
||||
'log_test_result',
|
||||
{
|
||||
title: 'Log test result',
|
||||
description:
|
||||
'Append a TEST_RESULT entry (PASSED or FAILED) to a story log. ' +
|
||||
'Forbidden for demo accounts.',
|
||||
inputSchema,
|
||||
},
|
||||
async ({ story_id, content, status }) =>
|
||||
withToolErrors(async () => {
|
||||
const auth = await requireWriteAccess()
|
||||
if (!(await userCanAccessStory(story_id, auth.userId))) {
|
||||
return toolError(`Story ${story_id} not found or not accessible`)
|
||||
}
|
||||
const log = await prisma.storyLog.create({
|
||||
data: {
|
||||
story_id,
|
||||
type: 'TEST_RESULT',
|
||||
content,
|
||||
status,
|
||||
},
|
||||
select: { id: true, created_at: true },
|
||||
})
|
||||
return toolJson({ id: log.id, created_at: log.created_at })
|
||||
}),
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue