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 { registerGetClaudeContextTool } from './tools/get-claude-context.js'
|
||||||
import { registerUpdateTaskStatusTool } from './tools/update-task-status.js'
|
import { registerUpdateTaskStatusTool } from './tools/update-task-status.js'
|
||||||
import { registerUpdateTaskPlanTool } from './tools/update-task-plan.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'
|
const VERSION = '0.1.0'
|
||||||
|
|
||||||
|
|
@ -24,7 +27,10 @@ async function main() {
|
||||||
registerGetClaudeContextTool(server)
|
registerGetClaudeContextTool(server)
|
||||||
registerUpdateTaskStatusTool(server)
|
registerUpdateTaskStatusTool(server)
|
||||||
registerUpdateTaskPlanTool(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()
|
const transport = new StdioServerTransport()
|
||||||
await server.connect(transport)
|
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