Decision. Ship a local stdio server when one user runs it on their own machine — publish it as an npm/PyPI package (
npx/uvx) or wrap it in a.mcpbbundle for one-click Claude Desktop install [1][7]. Go remote Streamable-HTTP the moment many users share one instance or you need auth, audit, and horizontal scale — host it on Cloudflare Workers, a container, or a serverless platform [1][2]. For discovery, publishserver.jsonto the official MCP Registry [4] — but it only stores metadata pointing at npm/PyPI/NuGet/Docker; your code still lives in a package registry.
Transport: where the server runs
Transport is the first deployment decision because it dictates everything downstream (hosting, auth, packaging).
| Axis | stdio (local) | Streamable HTTP (remote) |
|---|---|---|
| Where it runs | Subprocess on the client’s machine | A web service reachable over the network |
| Process model | One process per user — 50 devs × 8 servers ≈ 400 processes [1] | One process serves many clients concurrently [1] |
| Single-call latency | ~0.3–1 ms warm (p95 ~2–3 ms) [1] | ~5–10 ms same-DC (p95 ~15–25 ms) [1] |
| Auth | None at transport layer (env vars only) [1] | Authorization header, OAuth 2.1 + PKCE [1][2] |
| Best for | Local dev, single-user desktop tools [3] | Team sharing, SaaS, identity/audit/RBAC/scale [1] |
Streamable HTTP (MCP spec 2025-03-26) is JSON-RPC 2.0 over a single HTTP endpoint supporting POST and GET, with optional Server-Sent Events for streaming — it replaces the deprecated HTTP+SSE transport [3]. The migration from stdio is mechanical: with the TypeScript SDK you swap the stdio adapter for the HTTP server adapter; tools and handlers are unchanged [2][1]. The 5–10 ms HTTP overhead is rarely the dominant cost against an LLM call, so don’t pick stdio for latency alone [1].
Hosting a remote server
| Option | Notes |
|---|---|
| Cloudflare Workers ⭐ 3.8k | Official template; ~0ms cold start, global edge, free tier 100K req/day; OAuth built-in [2][5] |
| Vercel / serverless functions | Free tier; good fit if you already deploy there [5] |
| FastMCP Cloud / mcphosting.io | Managed MCP-specific hosting, free personal tiers [5] |
| Your own container (Docker) | Run the HTTP server behind a load balancer; full control, you own auth/scale [6] |
For public servers you can skip auth entirely; for private ones add an X-API-Key header or implement OAuth 2.1 with PKCE (the MCP spec standard) [2][5].
Packaging & distribution channels
A local server gets distributed as a runnable package; a remote server gets distributed as a URL. The channels:
| Channel | How users run it | Best for | One-click? |
|---|---|---|---|
npm (npx) |
npx -y your-mcp-server |
Node/TypeScript SDK servers [10] | ✗ (config) |
PyPI (uvx) |
uvx your-mcp-server |
Python servers [10] | ✗ (config) |
| Docker / OCI | docker run your/mcp-image |
Pinned deps, isolation, CI/CD [4] | ✗ (config) |
.mcpb bundle |
Double-click → Install [7] | End users on Claude Desktop, no runtime needed | ✓ |
| Remote URL | Paste https://…/mcp into client |
SaaS, zero-install, shared instance [2] | ~ (deeplink) |
npx -y and uvx are the default distribution for SDK-built servers: ship the package to npm/PyPI and the client’s config just references the command. The registry’s supported package types map directly to npm, PyPI, NuGet, and Docker Hub [4].
DXT / .mcpb — one-click desktop install
Desktop Extensions ⭐ 1.9k bundle an entire local MCP server — including all dependencies — into a single installable package, “spiritually similar to Chrome extensions (.crx) or VS Code extensions (.vsix)” [7]. Note the rename: the format moved from .dxt to .mcpb (MCP Bundle); existing .dxt files still work but use .mcpb for new extensions [8].
A .mcpb is a zip archive containing the server plus a manifest.json — the only required file — declaring name, tools/prompts, user configuration, and runtime requirements [8]. It eliminates the usual friction: Claude Desktop bundles Node.js, so users need no developer tools, and sensitive config like API keys is stored in the OS keychain [8]. Build it with the CLI [9]:
npm install -g @anthropic-ai/mcpb
mcpb init # scaffold manifest.json
mcpb pack # produce the .mcpb file
Publishing to the official MCP Registry
The official MCP Registry ⭐ 6.9k is the official centralized metadata repository for publicly accessible servers, backed by Anthropic, GitHub, PulseMCP, and Microsoft [4]. It is an “app store for MCP servers” but hosts metadata only, not code [12] — package registries (npm, PyPI, Docker Hub) host the binaries; the registry maps a server name + version to e.g. npm:weather-mcp [4]. It is still in preview (API freeze v0.1, GA pending) and is designed to be consumed by downstream aggregators/marketplaces — not directly by host apps [4].
Server names use reverse-DNS namespaces tied to verified GitHub accounts or domains, so only the legitimate owner can publish under io.github.<username>/* [4]. The publish flow [11]:
- Write a
server.json(name,version,packages[]withregistryType+transport,repository). - Reference the name in your package README as
<!-- mcp-name: io.github.you/server -->so the registry can verify ownership. - Publish the package to npm/PyPI/NuGet/Docker.
- Authenticate and publish the metadata:
./mcp-publisher login github # browser code flow → unlocks io.github.<you>/*
./mcp-publisher publish .mcp/server.json
The registry does not support private servers (private networks or private package registries) — self-host your own registry implementing the OpenAPI spec for those [4]. The GitHub MCP Registry will soon source its listings from the official registry [11].
Installing into clients
Every client speaks the same protocol, so a package is interchangeable — but each has its own config file and command [13].
| Client | Config file / command | stdio example | remote example |
|---|---|---|---|
| Claude Desktop | .mcpb install, or claude_desktop_config.json [8] |
manifest-driven, no JSON edit | — (use .mcpb / connectors) |
| Claude Code | claude mcp add → .mcp.json / ~/.claude.json [14] |
claude mcp add --transport stdio fs -- npx -y @some/mcp |
claude mcp add --transport http notion https://mcp.notion.com/mcp |
| VS Code | .vscode/mcp.json or code --add-mcp [10] |
{"command":"npx","args":["-y","@.../server"]} |
{"type":"http","url":"https://…/mcp"} |
| Cursor | .cursor/mcp.json or “Add to Cursor” deeplink [15][16] |
mcpServers block with command/args |
base64 deeplink: cursor://anysphere.cursor-deeplink/mcp/install?name=…&config=… |
Notes for distributors:
- Scope = sharing. In Claude Code,
--scope projectwrites.mcp.jsonat the repo root; commit it and every teammate’s machine gets the same config [14]. VS Code’s.vscode/mcp.jsonand Cursor’s.cursor/mcp.jsonwork the same way [10][15]. - Deeplinks are the closest thing to one-click for editors: ship an “Add to Cursor”/”Install in VS Code” button that encodes the server config so users skip JSON editing [16].
- SSE is deprecated as of early 2026 — emit HTTP config, not SSE, for new servers [14].
- Never hardcode secrets in shared config; use input variables / env files [10].
Practical recommendation
Build once with the TypeScript SDK (repo ⭐ 12.6k), then pick channels by audience: publish the npm package (works in every editor via npx), wrap a .mcpb for non-technical Claude Desktop users, and — when you outgrow per-user processes — stand up a Streamable-HTTP instance and hand out a URL plus an “Add to Cursor” deeplink. List server.json in the official registry so aggregators surface you [4].