pikku-mcp

$npx mdskill add pikkujs/pikku/pikku-mcp

Expose Pikku functions as MCP tools for AI assistants.

  • Enables Claude and ChatGPT to access project functions directly.
  • Integrates with Model Context Protocol for standardized tooling.
  • Decides usage by detecting mcp: true flags or specific function decorators.
  • Delivers structured schemas and descriptions to external agents.

SKILL.md

.github/skills/pikku-mcpView on GitHub ↗
---
name: pikku-mcp
description: '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).'
---

# Pikku MCP Wiring

Expose Pikku functions as Model Context Protocol (MCP) tools, resources, and prompts for AI assistants like Claude, ChatGPT, and others.

## Before You Start

```bash
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.

## API Reference

### MCP Tools (simplest approach)

Add `mcp: true` to any existing `pikkuFunc` to expose it as an MCP tool:

```typescript
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) => { ... },
})
```

### MCP Resources (`pikkuMCPResourceFunc`)

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

### MCP Prompts (`pikkuMCPPromptFunc`)

```typescript
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: '...' },
      },
    ]
  },
})
```

### MCP Wire Object

Inside MCP-enabled functions, `wire.mcp` provides:

```typescript
mcp.uri // Current resource URI (for resources)
mcp.sendResourceUpdated(uri) // Notify clients a resource changed
mcp.enableTools({ toolName: true }) // Dynamically enable/disable tools
```

## Usage Patterns

### Expose Existing Functions as MCP Tools

The simplest path — add `mcp: true` to any function:

```typescript
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 })
  },
})
```

### MCP Resources with URI Templates

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

### MCP Prompts

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

### Dynamic Tool Control

```typescript
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 }
    }
  },
})
```

### MCP Server Setup

```typescript
// start.ts
import { PikkuMCPServer } from '@pikku/modelcontextprotocol'

const server = new PikkuMCPServer(config, singletonServices, createWireServices)
await server.init()
await server.start()
```

## Complete Example

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

More from pikkujs/pikku

SkillDescription
pikku-addon'Use when creating or consuming reusable function packages (addons) in Pikku. Covers wireAddon, addon(), pikkuAddonServices, pikkuAddonWireServices, addon package structure, and cross-project function sharing.
pikku-ai-agent'Use when building AI agents, chatbots, or LLM-powered assistants with Pikku. Covers pikkuAIAgent, tool registration, memory, streaming, and agent invocation.
pikku-ai-vercel'Use when setting up AI agent execution with the Vercel AI SDK in a Pikku app. Covers VercelAIAgentRunner for streaming and non-streaming AI agent steps.
pikku-ai-voice'Use when adding voice input (speech-to-text) or voice output (text-to-speech) to AI agents in a Pikku app. Covers voiceInput/voiceOutput middleware hooks and STT/TTS service interfaces.
pikku-auth-js'Use when integrating Auth.js (NextAuth) with a Pikku app. Covers createAuthHandler, createAuthRoutes, and Auth.js configuration.
pikku-aws'Use when setting up AWS services (S3, SQS, Secrets Manager) in a Pikku app. Covers S3Content for file storage, SQSQueueService for queues, and AWSSecrets for secret management.
pikku-backblaze'Use when setting up Backblaze B2 file storage in a Pikku app. Covers B2Content for file uploads, downloads, and signed URLs.
pikku-cli'Use when building CLI commands with Pikku. Covers wireCLI, pikkuCLICommand, subcommands, options, parameters, custom renderers, and nested command groups.
pikku-concepts'Foundational guide to Pikku framework concepts. Use this skill when working with any Pikku codebase, starting a new Pikku project, or migrating a backend to Pikku. Covers the core mental model, function types, project structure, code generation, testing, and how Pikku maps to traditional backend patterns.
pikku-config'Use when managing secrets, environment variables, config, or OAuth2 credentials in a Pikku app. Covers wireSecret, wireVariable, wireOAuth2Credential, and typed config access.