---
name: mcpb
description: Generate MCPB (MCP Bundle) packages for MCP server projects. Creates manifest.json, bundle directory structure, validates against the MCPB spec, and wires in CI/CD workflows.
user_invocable: true
---

# MCPB Bundle Generator

You are an expert at creating MCPB (MCP Bundle) packages following the official specification from https://github.com/anthropics/mcpb.

## Trigger Patterns

This skill activates when the user asks to:
- "create mcpb bundle"
- "package mcp server"
- "create mcp bundle"
- "mcpb init"
- "mcpb"

## Execution Flow

Follow these steps in order. Do NOT skip steps. Ask clarifying questions only when values truly cannot be inferred.

### Step 1: Detect MCP Server

Search the current project for an existing MCP server implementation.

**Detection signals by language:**

**Node.js:**
- `package.json` containing `@modelcontextprotocol/sdk` in dependencies
- Source files importing from `@modelcontextprotocol/sdk`
- Usage of `StdioServerTransport`
- Usage of `Server` class from `@modelcontextprotocol/sdk/server`

**Python:**
- `requirements.txt` or `pyproject.toml` containing `mcp` package
- Source files importing from `mcp.server`
- Usage of `stdio_server()` or `mcp.server.stdio`
- Decorators like `@server.list_tools()`, `@server.call_tool()`

**Python UV:**
- `pyproject.toml` with `mcp` in dependencies
- Source files using `mcp.server.stdio.stdio_server()`
- No `server/lib/` or `server/venv/` directories (deps managed by UV)

**Binary:**
- `Cargo.toml` with MCP-related dependencies
- `go.mod` with MCP SDK references
- Pre-compiled executables in `server/` directory

**Search strategy:**
1. Check `package.json` for `@modelcontextprotocol/sdk` in dependencies/devDependencies
2. Check `pyproject.toml` or `requirements.txt` for `mcp` package
3. Grep source files for MCP SDK imports: `@modelcontextprotocol/sdk`, `from mcp`, `mcp.server`
4. Look for `StdioServerTransport`, `stdio_server`, transport setup patterns
5. Identify all tool definitions (tool names, descriptions, input schemas)

**If NO MCP server is detected:**
Ask the user: "No MCP server found in this project. Would you like me to:
1. Scaffold a new MCP server with MCPB bundle structure
2. Point me to the server entry file

Which would you prefer?"

If they choose option 1, scaffold a minimal MCP server using Node.js with `@modelcontextprotocol/sdk` that includes a single example tool.

### Step 2: Gather Manifest Information

Extract as much as possible from the project automatically:

**Auto-inferred fields (DO NOT ask the user for these):**
- `name` → from `package.json` name field, or `pyproject.toml` project name, or directory name
- `version` → from `package.json` version, or `pyproject.toml` version, or default `"0.1.0"`
- `description` → from `package.json` description, or `pyproject.toml` description
- `author` → from `package.json` author field, or `pyproject.toml` authors, or git config
- `server.type` → detected language: `"node"`, `"python"`, `"uv"`, or `"binary"`
- `server.entry_point` → detected main file path relative to project root
- `tools` → extracted from tool handler registrations in source code
- `license` → from `package.json` license field, or LICENSE file detection
- `keywords` → from `package.json` keywords, or inferred from tool names
- `compatibility.platforms` → default `["darwin", "win32", "linux"]`
- `compatibility.runtimes` → inferred from detected language and version requirements

**Ask the user ONLY for fields that cannot be inferred:**
- `display_name` — suggest a default based on the name (e.g., "my-mcp-server" → "My MCP Server")
- `description` — only if not found in package.json/pyproject.toml
- `icon` — ask if they have an icon file, or skip (optional field)

**For `user_config`:**
- Scan source code for environment variable reads (`process.env.`, `os.environ`, `os.getenv`)
- Look for config file loading patterns
- For each env var found, create a corresponding `user_config` entry with:
  - `type: "string"` (or `"boolean"` / `"number"` if usage context is clear)
  - `sensitive: true` for vars named `*_KEY`, `*_SECRET`, `*_TOKEN`, `*_PASSWORD`
  - Appropriate `title` and `description` derived from the variable name
  - `required: true` if the code treats it as mandatory (no fallback/default)

### Step 3: Generate manifest.json

Create a valid `manifest.json` at the project root. Use the correct schema based on detected server type:

**For node / python / binary** (`manifest_version: "0.3"`):

```json
{
  "manifest_version": "0.3",
  "name": "<machine-readable-name>",
  "display_name": "<Human Friendly Name>",
  "version": "<semver>",
  "description": "<brief description>",
  "author": {
    "name": "<author name>",
    "email": "<optional email>",
    "url": "<optional url>"
  },
  "server": {
    "type": "<node|python|binary>",
    "entry_point": "<relative path to main file>",
    "mcp_config": {
      "command": "<node|python|binary-path>",
      "args": ["${__dirname}/<entry_point>"],
      "env": {}
    }
  },
  "tools": [
    {
      "name": "<tool_name>",
      "description": "<tool description>"
    }
  ],
  "keywords": [],
  "license": "MIT",
  "user_config": {},
  "compatibility": {
    "platforms": ["darwin", "win32", "linux"],
    "runtimes": {}
  }
}
```

**For uv** (`manifest_version: "0.4"` — omit `mcp_config`, UV runtime handles execution):

```json
{
  "manifest_version": "0.4",
  "name": "<machine-readable-name>",
  "display_name": "<Human Friendly Name>",
  "version": "<semver>",
  "description": "<brief description>",
  "author": {
    "name": "<author name>",
    "email": "<optional email>",
    "url": "<optional url>"
  },
  "server": {
    "type": "uv",
    "entry_point": "<relative path to main file>"
  },
  "tools": [
    {
      "name": "<tool_name>",
      "description": "<tool description>"
    }
  ],
  "keywords": [],
  "license": "MIT",
  "user_config": {},
  "compatibility": {
    "platforms": ["darwin", "win32", "linux"],
    "runtimes": {}
  }
}
```

**Schema rules:**
- `manifest_version`: Use `"0.3"` for node/python/binary, `"0.4"` for uv
- `name`: Lowercase, hyphenated, no spaces (machine-readable). Sanitize inferred names: lowercase, replace spaces and underscores with hyphens, strip non-alphanumeric characters except hyphens
- `version`: Must be valid semver
- `server.type`: One of `"node"`, `"python"`, `"binary"`, `"uv"`
- `server.entry_point`: Relative path from bundle root to the server main file
- For UV type: omit `mcp_config` entirely (the UV runtime handles execution)
- `args` should use `${__dirname}` variable for the entry point path
- Sensitive config values use `${user_config.KEY_NAME}` variable substitution in `env`
- `tools` array: list every tool the server exposes; each entry must have both `name` and `description`
- `compatibility.runtimes`: e.g., `{"node": ">=20.0.0"}` or `{"python": ">=3.10"}`

### Step 4: Create Bundle Directory Structure

Organize the project files into the MCPB bundle structure:

**For Node.js projects:**
```
<project-root>/
├── manifest.json
├── server/
│   └── index.js          (or the detected entry point)
├── node_modules/          (production deps only)
├── package.json
├── icon.png               (if provided)
└── .mcpbignore
```

**For Python projects:**
```
<project-root>/
├── manifest.json
├── server/
│   ├── main.py            (or the detected entry point)
│   └── lib/               (bundled dependencies)
├── requirements.txt
├── icon.png               (if provided)
└── .mcpbignore
```

**For UV runtime projects:**
```
<project-root>/
├── manifest.json
├── pyproject.toml
├── src/
│   └── server.py          (or the detected entry point)
├── icon.png               (if provided)
└── .mcpbignore
```

**For Binary projects:**
```
<project-root>/
├── manifest.json
├── server/
│   └── <binary-name>      (compiled executable)
├── icon.png               (if provided)
└── .mcpbignore
```

**Generate `.mcpbignore`** to exclude non-bundle files:
```
.git/
.github/
.vscode/
.idea/
*.md
tests/
test/
__tests__/
*.test.*
*.spec.*
.env
.env.*
.DS_Store
Thumbs.db
coverage/
.nyc_output/
tsconfig.json
.eslintrc*
.prettierrc*
__pycache__/
*.pyc
.pytest_cache/
.mypy_cache/
*.egg-info/
.venv/
```

**IMPORTANT:** Do NOT move or restructure existing source files unless absolutely necessary. If the server entry point is already at a reasonable location (e.g., `src/index.js`), update the manifest `entry_point` to reference that location rather than forcing files into `server/`.

### Step 5: Validate Manifest

Run these validation checks on the generated `manifest.json`:

1. **Required fields present:** `manifest_version`, `name`, `version`, `description`, `author.name`, `server.type`, `server.entry_point`
2. **Version is valid semver:** Match pattern `^\d+\.\d+\.\d+(-[\w.]+)?(\+[\w.]+)?$`
3. **Server type is valid:** One of `"node"`, `"python"`, `"binary"`, `"uv"`
4. **Entry point file exists:** The file referenced by `server.entry_point` must exist
5. **Manifest version matches server type:** `"uv"` type requires `manifest_version: "0.4"`
6. **Platform values valid:** Each entry in `compatibility.platforms` is one of `"darwin"`, `"win32"`, `"linux"`
7. **User config types valid:** Each `user_config` entry has `type` of `"string"`, `"number"`, `"boolean"`, `"directory"`, or `"file"`
8. **Variable substitution valid:** All `${user_config.*}` references in `mcp_config` have matching `user_config` entries
9. **Tool names present:** If `tools` array exists, each tool has `name` and `description`
10. **No duplicate tool names:** All tool names in the `tools` array are unique

**On validation failure:** Report the specific validation errors and fix them automatically. Do not ask the user to fix validation issues — resolve them.

### Step 6: Wire In CI/CD Workflow

Generate a caller workflow at `.github/workflows/mcpb-package.yml` that invokes the reusable workflow. Do NOT use the filename `mcp-bundle.yml` — that name is reserved for the reusable workflow itself in the `zircote/mcp-bundle` repo and would collide if users fork or vendor that file:

```yaml
name: Package MCPB Bundle
on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:
    branches: [main]

jobs:
  package:
    uses: zircote/mcp-bundle/.github/workflows/mcp-bundle.yml@v1
    with:
      source-files: "<detected-source-glob>"
      manifest-path: "manifest.json"
      node-version: "<detected-node-version-or-20>"
      build-command: "<detected-build-command-or-npm-run-build>"
      test-command: "<detected-test-command-or-npm-test>"
      upload-artifact: true
      create-release-asset: ${{ startsWith(github.ref, 'refs/tags/v') }}
```

**Customize the caller workflow based on detected project:**
- `source-files`: Infer from project structure (e.g., `"src/**"`, `"server/**"`)
- `config-files`: Include if config directory exists
- `additional-artifacts`: Include if assets directory exists
- `build-command`: From `package.json` scripts.build, or `pyproject.toml` build config
- `test-command`: From `package.json` scripts.test, or `pytest`/`python -m pytest`
- `node-version`: From `.nvmrc`, `package.json` engines, or default `"20"`

### Step 7: Summary Output

After completing all steps, print a summary:

```
## MCPB Bundle Created

### Files Generated:
- ✓ manifest.json — Bundle manifest (spec v0.3)
- ✓ .mcpbignore — Bundle exclusion rules
- ✓ .github/workflows/mcpb-package.yml — CI/CD caller workflow

### Manifest Details:
- Name: <name>
- Version: <version>
- Server Type: <type>
- Entry Point: <entry_point>
- Tools: <count> tools defined
- User Config: <count> configurable options

### Validation:
- ✓ All required fields present
- ✓ Semver valid
- ✓ Entry point exists
- ✓ Variable substitutions resolved

### Next Steps:
1. Review manifest.json and adjust any inferred values
2. Install mcpb CLI: `npm install -g @anthropic-ai/mcpb`
3. Test locally: `mcpb pack` to create the .mcpb bundle
4. Test the bundle: install it in Claude Desktop
5. Push to trigger CI/CD packaging
```

## Important Constraints

- **stdio transport ONLY** — All MCPB servers communicate over stdin/stdout. Never use HTTP/SSE transport.
- **stderr for logging** — stdout is reserved for MCP protocol messages. All logging goes to stderr.
- **No shell injection** — Never construct shell commands from user input. Validate all inputs.
- **Idempotent** — Running `/mcpb` again on a project that already has a manifest should update, not duplicate.
- **Preserve existing code** — Do not restructure the user's source code. Adapt the manifest to match the existing structure.
- **Validate before writing** — Always validate the manifest in memory before writing to disk.
