.claude/skills/pikku-mcp/SKILL.md
Use when exposing Pikku functions as MCP tools, resources, or prompts for AI assistants. Covers mcp: true flag, pikkuMCPResourceFunc, pikkuMCPPromptFunc, and MCP wire object. TRIGGER when: code uses mcp: true or pikkuMCPResourceFunc/pikkuMCPPromptFunc, user asks about MCP, Model Context Protocol, AI tool integration, or exposing functions to Claude/ChatGPT. DO NOT TRIGGER when: user asks about AI agents (use pikku-ai-agent) or general function definitions (use pikku-concepts).
npx skillsauth add pikkujs/pikku pikku-mcpInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Expose Pikku functions as Model Context Protocol (MCP) tools, resources, and prompts for AI assistants like Claude, ChatGPT, and others.
pikku info functions --verbose # See existing functions that could become MCP tools
pikku info tags --verbose # Understand project organization
See pikku-concepts for the core mental model.
Add mcp: true to any existing pikkuFunc to expose it as an MCP tool:
const myFunc = pikkuFunc({
description: string, // Used as MCP tool description
input: ZodSchema, // Becomes MCP tool input schema
output: ZodSchema, // Return type
mcp: true, // ← Expose as MCP tool
func: async (services, data) => { ... },
})
pikkuMCPResourceFunc)import { pikkuMCPResourceFunc } from '#pikku'
const resource = pikkuMCPResourceFunc({
uri: string, // URI template, e.g. 'todos/{id}'
title: string, // Human-readable title
description?: string,
func: async (services, data, { mcp }) => {
// Must return array of { uri, text } or { uri, blob, mimeType }
return [{ uri: mcp.uri!, text: JSON.stringify(result) }]
},
})
pikkuMCPPromptFunc)import { pikkuMCPPromptFunc } from '#pikku'
const prompt = pikkuMCPPromptFunc({
name: string,
description: string,
func: async (services, data) => {
// Must return array of MCP messages
return [
{
role: 'user',
content: { type: 'text', text: '...' },
},
]
},
})
Inside MCP-enabled functions, wire.mcp provides:
mcp.uri // Current resource URI (for resources)
mcp.sendResourceUpdated(uri) // Notify clients a resource changed
mcp.enableTools({ toolName: true }) // Dynamically enable/disable tools
The simplest path — add mcp: true to any function:
export const createTodo = pikkuFunc({
description: 'Create a new todo item',
input: CreateTodoInput,
output: CreateTodoOutput,
mcp: true,
func: async ({ db }, { text, priority }) => {
return await db.createTodo({ text, priority })
},
})
export const getTodo = pikkuMCPResourceFunc({
uri: 'todos/{id}',
title: 'Todo Details',
description: 'Get a todo by ID',
func: async ({ db }, { id }, { mcp }) => {
const todo = await db.getTodo(id)
return [{ uri: mcp.uri!, text: JSON.stringify(todo) }]
},
})
export const codeReview = pikkuMCPPromptFunc({
name: 'codeReview',
description: 'Generate a code review prompt',
func: async ({}, { filePath, context }) => {
return [
{
role: 'user',
content: {
type: 'text',
text: `Review ${filePath}. Context: ${context}`,
},
},
]
},
})
export const manageTodos = pikkuFunc({
description: 'Manage todo items',
input: ManageTodosInput,
output: ManageTodosOutput,
mcp: true,
func: async ({ db }, { action, id }, { mcp }) => {
if (action === 'delete') {
await db.deleteTodo(id)
mcp.sendResourceUpdated(`todos/${id}`)
await mcp.enableTools({ archiveTodos: true })
return { deleted: true }
}
},
})
// start.ts
import { PikkuMCPServer } from '@pikku/modelcontextprotocol'
const server = new PikkuMCPServer(config, singletonServices, createWireServices)
await server.init()
await server.start()
// functions/todos.functions.ts
export const listTodos = pikkuSessionlessFunc({
description: 'List all todo items',
input: ListTodosInput,
output: ListTodosOutput,
mcp: true,
func: async ({ db }, { status }) => {
return { todos: await db.listTodos(status) }
},
})
export const createTodo = pikkuFunc({
description: 'Create a new todo item',
input: CreateTodoInput,
output: CreateTodoOutput,
mcp: true,
func: async ({ db }, { text, priority }) => {
return await db.createTodo({ text, priority })
},
})
export const completeTodo = pikkuFunc({
description: 'Mark a todo as complete',
input: CompleteTodoInput,
output: CompleteTodoOutput,
mcp: true,
func: async ({ db }, { todoId }) => {
return await db.completeTodo(todoId)
},
})
// functions/todos.mcp.ts
export const getTodoResource = pikkuMCPResourceFunc({
uri: 'todos/{id}',
title: 'Todo Details',
description: 'Get details of a specific todo',
func: async ({ db }, { id }, { mcp }) => {
const todo = await db.getTodo(id)
return [{ uri: mcp.uri!, text: JSON.stringify(todo) }]
},
})
export const planDayPrompt = pikkuMCPPromptFunc({
name: 'planDay',
description: 'Create a daily plan based on pending todos',
func: async ({ db }, {}) => {
const { todos } = await db.listTodos('pending')
return [
{
role: 'user',
content: {
type: 'text',
text: `Plan my day. Here are my pending todos:\n${todos.map((t) => `- ${t.text} (${t.priority})`).join('\n')}`,
},
},
]
},
})
documentation
Deprecated — use pikku-middleware instead. Tag middleware (addTagMiddleware) is now documented as a section within the pikku-middleware skill, alongside global HTTP middleware, execution order, and the service-to-service bearer auth pattern.
testing
Use when adding authorization checks to Pikku functions or routes — pikkuPermission, pikkuAuth, per-function permissions, pattern-based permissions, or understanding OR/AND permission logic. TRIGGER when: user wants to restrict who can call a function, check resource ownership, add role-based access, or understand where permission checks belong. DO NOT TRIGGER when: user asks about middleware or request interception (use pikku-middleware), authentication strategies (use pikku-security), or session management.
testing
Use when adding any middleware to a Pikku app — global HTTP middleware, tag-scoped middleware (including service-to-service bearer auth), per-route middleware, session-setting middleware, or understanding middleware execution order and priority. TRIGGER when: user wants middleware on some or all routes, machine-to-machine auth, tag-scoped cross-cutting concerns, global interceptors, or middleware priority/order questions. DO NOT TRIGGER when: user asks about permissions/authorization checks (use pikku-permissions), auth strategies like authBearer/authCookie (use pikku-security), or deployment.
documentation
Standard cleanup to run right after a Pikku template is cloned or scaffolded into a new project. TRIGGER when: a Pikku template was just cloned/scaffolded (via `pikku create`, `git clone <template>`, or the user says "I cloned the kanban template / starter / template"), or the working tree still looks like an untouched template (template README, placeholder `@project/*` name in package.json). DO NOT TRIGGER when: working in an established project mid-feature, or editing the template repo itself.