Migrate Your MCP Server
You have an MCP server on GitHub. Users clone it, install dependencies, and configure paths manually. Let’s fix that in 10 minutes.
Before You Start
Section titled “Before You Start”You need:
- A working MCP server in a GitHub repository
- The server must run via Python, Node.js, or a compiled binary
- Admin access to the repo (to add a workflow and create releases)
No mpak account is needed. Your GitHub identity is your mpak identity.
Step 1: Add manifest.json
Section titled “Step 1: Add manifest.json”Create a manifest.json at the root of your repository:
{ "name": "@yourorg/your-server", "version": "1.0.0", "description": "What your server does", "server": { "type": "python", "entry_point": "your_package.server", "mcp_config": { "command": "python", "args": ["-m", "your_package.server"] } }}Key requirements for Python:
- Use module execution (
-m) to handle relative imports correctly - Your server must have a
if __name__ == "__main__"block:if __name__ == "__main__":mcp.run()
{ "name": "@yourorg/your-server", "version": "1.0.0", "description": "What your server does", "server": { "type": "node", "entry_point": "dist/index.js", "mcp_config": { "command": "node", "args": ["${__dirname}/dist/index.js"] } }}Key requirements for Node.js:
- Reference the compiled JavaScript output, not TypeScript source
- Use
${__dirname}to reference the bundle extraction directory
{ "name": "@yourorg/your-server", "version": "1.0.0", "description": "What your server does", "server": { "type": "binary", "entry_point": "bin/server", "mcp_config": { "command": "${__dirname}/bin/server", "args": [] } }}Replace @yourorg with your GitHub org or username (lowercased). See Naming Conventions.
Step 2: Add the GitHub Action
Section titled “Step 2: Add the GitHub Action”Create .github/workflows/release.yml:
name: Releaseon: release: types: [published]
permissions: contents: write id-token: write
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: NimbleBrainInc/mcpb-pack@v2That’s it. The action handles:
- Installing dependencies
- Vendoring them into the bundle
- Building the
.mcpbfile - Uploading it to your GitHub Release
- Announcing it to the mpak registry (with OIDC provenance)
Step 3: Test Locally
Section titled “Step 3: Test Locally”Before publishing, verify your server works correctly:
# Make sure your server starts and responds to MCP messages# For Python:python -m your_package.server
# For Node.js:node dist/index.jsStep 4: Create Your First Release
Section titled “Step 4: Create Your First Release”# Tag the releasegit tag v1.0.0git push origin v1.0.0Then go to your GitHub repository and create a release from the v1.0.0 tag. The GitHub Action will trigger automatically.
Watch the Actions tab to see the build progress. When it completes:
- The
.mcpbfile appears as a release asset - The bundle is registered on mpak.dev
Step 5: Verify
Section titled “Step 5: Verify”# Search for your packagempak bundle search your-server
# View full detailsmpak bundle show @yourorg/your-server
# Run itmpak bundle run @yourorg/your-serverYour server is now installable with a single command by anyone, anywhere.
What Changed in Your Repo
Section titled “What Changed in Your Repo”You added 2 files:
| File | Purpose |
|---|---|
manifest.json | Describes your server and how to run it |
.github/workflows/release.yml | Builds and publishes on each release |
No changes to your server code. No new runtime dependencies. No configuration files beyond what you already had.
Common Migration Issues
Section titled “Common Migration Issues”Python import errors
Section titled “Python import errors”If you see ModuleNotFoundError when running the bundle, the issue is usually module execution vs script execution:
// ✗ Wrong: script execution doesn't handle relative imports{ "args": ["src/server.py"] }
// ✓ Correct: module execution handles imports properly{ "args": ["-m", "your_package.server"] }Stdout pollution
Section titled “Stdout pollution”MCP uses JSON-RPC over stdio. Any non-JSON-RPC output on stdout breaks the connection. Common culprits:
print()statements (Python)console.log()(Node.js)- Library banners or startup messages
- Logging frameworks defaulting to stdout
Fix: redirect all logging to stderr:
import logginglogging.basicConfig(stream=sys.stderr)Missing if __name__ == "__main__" block
Section titled “Missing if __name__ == "__main__" block”Python servers must include this guard:
if __name__ == "__main__": mcp.run()Without it, mpak can’t start your server as a module.
Optional: Security Scanning
Section titled “Optional: Security Scanning”Every bundle published to mpak is automatically scanned. Run the scanner locally to catch issues before publishing:
pip install mpak-scannermpak-scanner scan .See Scanning Your Bundle for details.
Optional: User Configuration
Section titled “Optional: User Configuration”If your server requires API keys or other user-provided values, add user_config to your manifest:
{ "user_config": { "api_key": { "type": "string", "title": "API Key", "description": "Your API key from example.com", "sensitive": true, "required": true } }}See User Configuration for the full guide.