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('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(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(async ({ kysely }) => { const cards = await kysely.selectFrom('kanbanCard').selectAll().execute() const byStatus = cards.reduce>((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'], })