Files
cli-e2e-mqo21uyk/packages/functions/src/wirings/kanban.mcp.ts
2026-06-21 19:23:05 +02:00

91 lines
2.7 KiB
TypeScript

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'],
})