chore: kanban template
This commit is contained in:
90
packages/functions/src/wirings/kanban.mcp.ts
Normal file
90
packages/functions/src/wirings/kanban.mcp.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { z } from 'zod'
|
||||
import {
|
||||
pikkuMCPToolFunc,
|
||||
pikkuMCPResourceFunc,
|
||||
pikkuMCPPromptFunc,
|
||||
wireMCPResource,
|
||||
wireMCPPrompt,
|
||||
} from '#pikku'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
|
||||
export const listCardsTool = pikkuMCPToolFunc({
|
||||
description: 'List all kanban cards on the board.',
|
||||
tags: ['kanban', 'cards'],
|
||||
func: async ({ kysely }) => {
|
||||
const cards = await kysely
|
||||
.selectFrom('kanbanCard')
|
||||
.selectAll()
|
||||
.orderBy('position', 'asc')
|
||||
.execute()
|
||||
return [{ type: 'text' as const, text: JSON.stringify(cards, null, 2) }]
|
||||
},
|
||||
})
|
||||
|
||||
export const CreateCardToolInput = z.object({
|
||||
title: z.string().min(1),
|
||||
status: z.enum(['todo', 'doing', 'done']).default('todo'),
|
||||
})
|
||||
|
||||
export const createCardTool = pikkuMCPToolFunc({
|
||||
input: CreateCardToolInput,
|
||||
description: 'Create a new kanban card on the board.',
|
||||
tags: ['kanban', 'cards'],
|
||||
func: async ({ kysely, queueService }, { title, status }) => {
|
||||
const cardId = randomUUID()
|
||||
const createdAt = new Date().toISOString()
|
||||
const max = await kysely
|
||||
.selectFrom('kanbanCard')
|
||||
.select(({ fn }) => fn.max<number>('position').as('max'))
|
||||
.where('status', '=', status)
|
||||
.executeTakeFirst()
|
||||
const position = (max?.max ?? 0) + 1
|
||||
await kysely
|
||||
.insertInto('kanbanCard')
|
||||
.values({ cardId, title, status, position, createdAt })
|
||||
.execute()
|
||||
await queueService!.add('process-card-event', { cardId, action: 'created', status, createdAt })
|
||||
return [{ type: 'text' as const, text: `Created card ${cardId}: "${title}" (${status})` }]
|
||||
},
|
||||
})
|
||||
|
||||
const kanbanCardsResource = pikkuMCPResourceFunc<void>(async ({ kysely }) => {
|
||||
const cards = await kysely
|
||||
.selectFrom('kanbanCard')
|
||||
.selectAll()
|
||||
.orderBy('position', 'asc')
|
||||
.execute()
|
||||
return [{ uri: 'kanban://cards', text: JSON.stringify(cards, null, 2) }]
|
||||
})
|
||||
|
||||
const kanbanStatusPrompt = pikkuMCPPromptFunc<void>(async ({ kysely }) => {
|
||||
const cards = await kysely.selectFrom('kanbanCard').selectAll().execute()
|
||||
const byStatus = cards.reduce<Record<string, number>>((acc, c) => {
|
||||
acc[c.status] = (acc[c.status] ?? 0) + 1
|
||||
return acc
|
||||
}, {})
|
||||
return [
|
||||
{
|
||||
role: 'user' as const,
|
||||
content: {
|
||||
type: 'text' as const,
|
||||
text: `Board has ${cards.length} cards: ${JSON.stringify(byStatus)}. Summarise the state.`,
|
||||
},
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
wireMCPResource({
|
||||
uri: 'kanban://cards',
|
||||
title: 'Kanban Board Cards',
|
||||
description: 'All cards currently on the kanban board',
|
||||
func: kanbanCardsResource,
|
||||
tags: ['kanban'],
|
||||
})
|
||||
|
||||
wireMCPPrompt({
|
||||
name: 'kanbanStatus',
|
||||
description: 'Get a prompt summarising the current kanban board state',
|
||||
func: kanbanStatusPrompt,
|
||||
tags: ['kanban'],
|
||||
})
|
||||
Reference in New Issue
Block a user