Skip to content
Back to skills

Mcpb

ASecurity

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.

  • 3 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 26, 2026
toolspythongoshellnodegitci/cd

Works with

  • claude desktop
  • vscode
  • cli
  • mcp

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 2 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add zircote/mcp-bundle --skill mcpb --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Mcpb?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Mcpb
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/zircote-mcpb/badge)](https://www.skillsdirectory.com/skills/zircote-mcpb)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
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.

Files in this skill

  • SKILL.md12.6 KB
  • evals/evals.json21.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.