MCPB Creator
This skill provides comprehensive guidance for creating production-ready Model Context Protocol Bundles (MCPB).
About MCPB
Model Context Protocol Bundles (MCPB) are standardized packages for distributing MCP servers. Each bundle contains:
- manifest.json: Bundle metadata, tool definitions, and server configuration
- Server implementation: MCP protocol server (Node.js, Python, etc.)
- Dependencies: Package manager files (package.json, requirements.txt, etc.)
- Documentation: README with usage examples
MCPB bundles run locally, communicate via stdio (JSON-RPC), and provide tools that AI agents can call to perform actions or access resources.
Core Principles
- Production-ready code: Implement proper error handling, validation, and security
- Clear tool schemas: Define precise input/output types with JSON Schema
- Defensive programming: Validate all inputs, handle edge cases, return informative errors
- MCPB compliance: Follow exact specifications for manifest structure and protocol communication
- Local execution: Bundles run on the user's machine with file system access
- Stdio transport: Server reads JSON-RPC from stdin, writes responses to stdout, logs to stderr
Workflow
Step 1: Understand Requirements
Gather detailed information about what the bundle should do:
- What tools should the bundle provide? (e.g., file operations, API calls, data processing)
- What inputs do tools accept? (parameters, types, validation rules)
- What outputs do tools return? (data structures, formats)
- What resources does it need? (file system access, network, external APIs)
- What dependencies are required? (npm packages, system commands)
Step 2: Initialize Bundle Structure
Use the initialization script to create the bundle:
scripts/init_mcpb.py <bundle-name> --path <output-directory>
This creates:
bundle-name/
├── manifest.json # Bundle metadata and tool definitions
├── package.json # Node.js dependencies
├── server/
│ └── index.js # MCP server implementation
├── README.md # Documentation
└── .gitignore # Git ignore rules
Step 3: Define Tools in Manifest
Edit manifest.json to define your tools:
Update bundle metadata:
name: kebab-case identifier (must be unique)version: Semantic versioning (0.1.0, 1.0.0, etc.)description: Clear explanation of bundle purposeauthor: Your name and emaillicense: Software license (MIT, Apache-2.0, etc.)
Define tools array: Each tool needs:
name: snake_case identifier (e.g.,read_file,search_data)description: What the tool does (for AI to understand when to use it)inputSchema: JSON Schema defining parameters (must have"type": "object")
Example tool definition:
{
"name": "process_data",
"description": "Process input data and return structured results",
"inputSchema": {
"type": "object",
"properties": {
"data": {
"type": "string",
"description": "Raw data to process"
},
"format": {
"type": "string",
"enum": ["json", "csv", "xml"],
"description": "Output format"
}
},
"required": ["data"]
}
}
Step 4: Implement Server
Edit server/index.js to implement the MCP protocol:
- Update tool definitions: Copy tool schemas from manifest to the
TOOLSarray - Implement tool handlers: Create async function for each tool
- Add tool routing: Add case to switch statement linking tool name to handler
- Implement validation: Validate inputs before processing
- Implement error handling: Use try-catch and return informative errors
- Add logging: Use
console.error()for debugging (neverconsole.log())
Handler pattern:
async function handleToolName(args) {
console.error("[INFO] tool_name called with:", args);
// Validate inputs
if (!args.requiredParam || typeof args.requiredParam !== 'string') {
throw new Error("Invalid parameter: requiredParam must be a string");
}
try {
// Implement tool logic
const result = await performOperation(args);
// Return structured response
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2)
}
]
};
} catch (error) {
console.error("[ERROR] Operation failed:", error);
throw new Error(`Operation failed: ${error.message}`);
}
}
Step 5: Add Security Measures
Implement security best practices:
- Input validation: Check all parameters for type, format, and constraints
- Path validation: Prevent directory traversal attacks (sanitize file paths)
- Resource limits: Add timeouts for long-running operations
- Error sanitization: Don't expose sensitive information in error messages
- Environment variables: Use env vars for credentials, never hardcode secrets
Path validation example:
import path from 'path';
const WORKSPACE_DIR = process.env.WORKSPACE_DIR || process.cwd();
function validatePath(userPath) {
// Prevent directory traversal
if (userPath.includes('..')) {
throw new Error("Invalid path: directory traversal not allowed");
}
// Resolve to absolute path within workspace
const resolvedPath = path.resolve(WORKSPACE_DIR, userPath);
// Ensure resolved path is within workspace
if (!resolvedPath.startsWith(WORKSPACE_DIR)) {
throw new Error("Access denied: path outside workspace");
}
return resolvedPath;
}
Timeout example:
function withTimeout(promise, timeoutMs) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Operation timeout')), timeoutMs)
)
]);
}
async function handleLongOperation(args) {
const result = await withTimeout(
performOperation(args),
30000 // 30 second timeout
);
return {
content: [{ type: "text", text: JSON.stringify(result) }]
};
}
Step 6: Update Documentation
Edit README.md to document:
- Bundle description: What it does and why it's useful
- Installation steps: How to install dependencies
- Tool documentation: For each tool:
- What it does
- Input parameters (types, constraints, defaults)
- Output format
- Usage examples
- Configuration: Environment variables, settings
- Development guide: How to add new tools, run tests
Step 7: Validate and Test
- Validate manifest:
scripts/validate_mcpb.py <bundle-directory>
- Install dependencies:
cd <bundle-directory>
npm install
- Test server startup:
npm start
The server should start without errors and log "[INFO] Server running and ready for requests"
- Test tool calls using an MCP host or manual JSON-RPC:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/list"
}
Expected response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [...]
}
}
- Test error handling: Call tools with invalid inputs and verify errors are clear
Step 8: Finalize
Review checklist:
- Manifest validates successfully
- All tools have clear descriptions and schemas
- Input validation implemented for all parameters
- Error messages are informative and safe
- Timeouts for long operations
- No
console.log()in code (onlyconsole.error()) - Security measures in place
- Dependencies pinned in package.json
- README documents all tools with examples
- Server starts and responds to tool calls
Package for distribution: Add to version control, publish to registry, or share directory
Reference Materials
- references/mcpb-overview.md: MCPB architecture, capabilities, and integration patterns
- references/manifest-spec.md: Complete manifest.json structure and field definitions
- references/implementation-guide.md: Best practices, security, testing, and common patterns
Common Tool Patterns
File Operations
async function handleReadFile(args) {
const safePath = validatePath(args.path);
const content = await fs.readFile(safePath, 'utf-8');
return {
content: [{
type: "text",
text: content
}]
};
}
Data Processing
async function handleProcessData(args) {
const parsed = JSON.parse(args.data);
const processed = transformData(parsed);
return {
content: [{
type: "text",
text: JSON.stringify(processed, null, 2)
}]
};
}
External API Calls
async function handleApiCall(args) {
const response = await withTimeout(
fetch(args.url, {
method: args.method || 'GET',
headers: { 'Authorization': `Bearer ${process.env.API_KEY}` }
}),
10000
);
const data = await response.json();
return {
content: [{
type: "text",
text: JSON.stringify(data, null, 2)
}]
};
}
List/Search Operations
async function handleSearch(args) {
const results = await searchOperation(args.query);
return {
content: [{
type: "text",
text: JSON.stringify({
query: args.query,
count: results.length,
results: results
}, null, 2)
}]
};
}
Scripts
Initialize Bundle
scripts/init_mcpb.py <bundle-name> [--path <directory>]
Creates new bundle with template structure.
Validate Bundle
scripts/validate_mcpb.py <bundle-directory>
Validates manifest.json structure and required fields.
Best Practices Summary
- Tool naming: Use snake_case for tool names, kebab-case for bundle name
- Descriptions: Write clear descriptions for AI to understand tool purpose
- Schemas: Define precise inputSchema with types, descriptions, and required fields
- Validation: Check all inputs before processing
- Errors: Return structured errors with helpful context
- Logging: Use stderr for logs, stdout only for JSON-RPC
- Security: Validate paths, sanitize inputs, use timeouts
- Testing: Test with valid/invalid inputs, edge cases
- Documentation: Include examples for every tool
- Dependencies: Pin versions in package.json for reproducibility
Troubleshooting
Server won't start
- Check manifest.json syntax (valid JSON)
- Verify server.command points to valid executable
- Verify server.args[0] points to existing file
- Check for syntax errors in server/index.js
- Run
npm installto install dependencies
Tool calls fail
- Verify tool name matches between manifest and server
- Check inputSchema validation in handler
- Look for errors in stderr output
- Test with exact schema-compliant inputs
Validation errors
- Run
scripts/validate_mcpb.pyfor specific issues - Check manifest field names and types
- Verify semantic versioning format
- Ensure tool names use snake_case
Response format errors
- Ensure responses follow MCP schema
- Must return object with
contentarray - Each content item needs
typeandtextfields - Only write JSON-RPC to stdout, logs to stderr