You built an MCP server in C#. It works great on your machine. Now you want to share it with colleagues, publish it to the community, or ship it as part of a product. The problem? Every time someone wants to use a local MCP server, they have to clone a repo, install runtimes, hand-edit a JSON config file, get the path wrong, edit it again... you know the drill.
MCP Bundles (.mcpb) solve exactly that. They're the .vsix of the MCP ecosystem: a single file that a supporting app (like Claude for Desktop) opens with one click to present a guided install dialog. No terminal, no JSON editing, no "works on my machine."
This post walks through taking a C# MCP server binary and packaging it into a distributable .mcpb file from scratch.
What's inside a .mcpb file?
Before touching anything, it helps to understand what you're building. A .mcpb file is just a ZIP archive with a specific structure:
my-server.mcpb (ZIP file)
├── manifest.json ← required: describes your server
├── server/
│ ├── MyMcpServer.exe ← Windows binary
│ └── MyMcpServer ← macOS/Linux binary (if targeting those)
└── icon.png ← optional but recommended
The manifest.json is the heart of the bundle. It tells the host application how to launch your server, what user configuration to collect, and what tools and prompts your server provides — all without the user ever needing to open a terminal.
Prerequisites
You'll need Node.js installed (for the mcpb CLI), plus your compiled C# MCP server binary.
Install the CLI globally:
npm install -g @anthropic-ai/mcpb
Verify it's working:
mcpb --version
Step 1: Prepare your binary
For a C# MCP server, you'll want a self-contained single-file publish so users don't need the .NET runtime installed. A typical publish command looks like:
# Windows, self-contained
dotnet publish
This gives you a single executable per platform with the runtime baked in — exactly what a Binary bundle needs.
Now organize your bundle directory:
my-mcp-bundle/
├── manifest.json ← we'll create this next
├── server/
│ ├── MyMcpServer.exe ← from publish/win-x64/
└── icon.png ← 128x128 PNG, your server's icon
Step 2: Create the manifest
Navigate into your bundle directory and run:
cd my-mcp-bundle
mcpb init
The interactive prompts will walk you through the basics. But since you're writing a binary bundle, you'll want to fine-tune the result.
Here's what a solid manifest.json looks like for a cross-platform C# server:
{
"manifest_version": "0.3",
"name": "my-mcp-server",
"display_name": "My MCP Server",
"version": "1.0.0",
"description": "Does something useful via MCP.",
"long_description": "A longer description of what the server does, what tools it exposes, and any relevant context for the user. Supports basic markdown.",
"author": {
"name": "Your Name",
"email": "you@example.com",
"url": "https://your-website.com"
},
"repository": {
"type": "git",
"url": "https://github.com/your-org/my-mcp-server.git"
},
"icon": "icon.png",
"license": "MIT",
"keywords": ["your", "relevant", "keywords"],
"server": {
"type": "binary",
"entry_point": "server/MyMcpServer",
"mcp_config": {
"command": "${__dirname}/server/MyMcpServer",
"args": [],
"env": {},
"platform_overrides": {
"win32": {
"command": "${__dirname}/server/MyMcpServer.exe"
}
}
}
},
"tools": [
{
"name": "my_tool",
"description": "Does something useful"
}
],
"compatibility": {
"platforms": ["win32"]
}
}
A few things worth highlighting:
${__dirname} is a magic variable the host app substitutes with the absolute path to the installed bundle directory. Always use this instead of hardcoding paths — your bundle will be installed to a user-specific location the host controls.
platform_overrides lets you specify different commands per OS. Since Windows adds .exe to executables, you can explicitly handle that here. The host app may also automatically append .exe on Windows, but being explicit avoids ambiguity.
tools is not required for the bundle to work, but it lets supporting apps display your server's capabilities before the user even installs it. Worth filling in.
Step 3: Handle user configuration
If your MCP server needs any input from the user (an API key, a directory path, a connection string), declare it in user_config. The host app will collect these values through its UI and inject them into your server at launch time.
"user_config": {
"api_key": {
"type": "string",
"title": "API Key",
"description": "Your API key. Find it at https://example.com/settings.",
"sensitive": true,
"required": true
},
"data_directory": {
"type": "directory",
"title": "Data Directory",
"description": "The folder your server will read from.",
"required": false,
"default": "${HOME}/Documents"
}
},
"server": {
"mcp_config": {
"command": "${__dirname}/server/MyMcpServer.exe",
"args": [],
"env": {
"API_KEY": "${user_config.api_key}",
"DATA_DIR": "${user_config.data_directory}"
}
}
}
In your C# server, you read these from environment variables as usual. No change needed to how your server works, just how it's launched.
Available user config types: string, number, boolean, directory, and file. Use "sensitive": true for anything you don't want shown in plain text or stored unencrypted.
Step 4: Validate
Before packing, validate your manifest:
mcpb validate manifest.json
This checks the manifest against the schema and catches issues like missing required fields or malformed version strings before they become mysterious install failures.
Step 5: Pack
mcpb pack .
This produces my-mcp-server.mcpb in the current directory. The CLI automatically excludes development noise (.git/, *.log, lock files, etc.). You can fine-tune exclusions with a .mcpbignore file at the root of your bundle directory — it works exactly like .gitignore.
To specify a custom output name:
mcpb pack . my-mcp-bundle-v1.0.0.mcpb
Step 6: Sign (Optional but recommended)
For development and internal sharing, you can self-sign:
mcpb sign my-mcp-bundle.mcpb --self-signed
For production distribution, use a proper code-signing certificate:
mcpb sign my-mcp-bundle.mcpb \
--cert cert.pem \
--key key.pem \
--intermediate intermediate-ca.pem
Signing uses PKCS#7 (CMS), stored as a block appended to the ZIP file. This means unsigned bundles are still valid ZIP files and stay backward compatible.
Verify the signature after signing:
mcpb verify my-mcp-bundle.mcpb
And inspect the bundle metadata:
mcpb info my-mcp-bundle.mcpb
Step 7: Test the install
The simplest way to test is to open the .mcpb file with Claude for Desktop on macOS or Windows. The app will show an install dialog, prompt for any user_config values you defined, and register the server. No JSON editing required.
If something goes wrong, the most common culprits are:
- Path issues: double-check you're using
${__dirname}for all paths inside the bundle - Missing binaries: the platform you're testing on needs its binary present
- Manifest validation errors: run
mcpb validateagain after any edits
Full workflow cheat sheet
# Install the CLI
npm install -g @anthropic-ai/mcpb
# Publish your C# server
dotnet publish -c Release -r win-x64 --self-contained true \
-p:PublishSingleFile=true -o ./publish/win-x64
# Set up bundle directory
mkdir my-mcp-bundle && cd my-mcp-bundle
mkdir server
cp ../publish/win-x64/MyMcpServer.exe server/
# Initialize manifest
mcpb init
# (Edit manifest.json to match the binary bundle pattern above)
# Validate
mcpb validate manifest.json
# Pack
mcpb pack . my-mcp-server-v1.0.0.mcpb
# Sign (dev)
mcpb sign my-mcp-server-v1.0.0.mcpb --self-signed
# Verify
mcpb verify my-mcp-server-v1.0.0.mcpb
# Open with Claude for Desktop to test the install dialog
Conclusion
The .mcpb format is open: the spec, toolchain, and host integration code are all in the modelcontextprotocol/mcpb repo. Claude for Desktop is the first app to support it natively, but the format is designed for any desktop AI app to adopt.
The goal is that one bundle works everywhere. Unfortunately, it doesn't work (yet) in VS Code or Visual Studio.