Four MCP server deployment paths to choose between
MCP server development guidance distinguishes four deployment paths: (1) Remote Streamable HTTP — the default for anything wrapping a cloud API, since there is zero install friction, one deployment serves all users, and OAuth flows work properly because the server can handle redirects and token storage; (2) MCP apps — a server extended with interactive widgets rendered in chat (searchable pickers, charts, live dashboards); (3) MCP Bundles (MCPB) — a local server packaged with its runtime as a single .mcpb archive so users install it without setting up Node or Python; (4) Local stdio — kept for prototyping, with an upgrade path to MCPB when you are ready to distribute.
Remote servers differ from local servers only in hosting, not in primitives
Remote MCP servers function like local ones but are hosted on the internet instead of the user's machine. They expose the same primitives — tools, prompts, and resources — that the client can use. Remote hosting suits web-based AI applications, easy-install integrations, and services that need server-side processing or authentication; local (stdio) servers require installation and configuration on each device.
Inspector CLI server selection: stdio vs HTTP vs config file
The Inspector CLI accepts three ways to pick a server: a positional command for stdio (`mcp-inspector --cli node build/index.js --method tools/list`), a URL with `--transport http` (`mcp-inspector --cli https://api.example.com/mcp --transport http --method tools/list`), or a named server from a catalog/config file (`mcp-inspector --cli --config ./mcp.json --server myserver --method tools/list`).
Inspector ad-hoc server targets: positional stdio command or --server-url
Instead of a catalog/config file you can name one server directly on the command line. For stdio use a positional command, e.g. `mcp-inspector node build/index.js`. For remote servers use a URL, e.g. `mcp-inspector --server-url https://api.example.com/mcp --transport http`. `--transport` accepts `stdio`, `sse`, or `http`, and both `--transport` and `--server-url` apply to ad-hoc targets only.
Authorization applies to HTTP transports, not stdio
MCP authorization via OAuth 2.1 is optional but recommended when the server accesses user-specific data, needs auditing, requires user consent, targets enterprise access controls, or does per-user rate limiting. For servers using the STDIO transport, OAuth is not the right tool: because the server runs locally, use environment-based credentials or credentials from embedded third-party libraries instead. OAuth flows are designed for HTTP-based transports where the server is remotely hosted.
MCP Apps transport is postMessage, not stdio or HTTP
MCP Apps use their own dialect of MCP built on JSON-RPC, but the transport is the browser postMessage API rather than stdio or Streamable HTTP. Some messages are shared with core MCP (e.g. `tools/call`), some are similar (e.g. `ui/initialize`), and most new ones use a `ui/` method-name prefix. Because it is standard web primitives, any frontend framework or none can be used.
Why Tasks instead of blocking a request
Blocking a request until work finishes ties up a connection and many clients and transport intermediaries impose timeouts that make blocking impractical beyond a few seconds. Tasks avoid long-lived connections, give crash resilience (a task ID is a durable handle that survives client disconnect/restart), give progress visibility via status metadata, allow mid-flight interaction without unsolicited server-to-client messages, and are server-directed (the server decides per request whether to create a task; no per-tool warmup or per-request flag).
Task status notifications via notifications/tasks
Servers can push task status updates via `notifications/tasks`, which clients opt into through the `subscriptions/listen` mechanism. Each notification carries the full task state, removing the need for an extra `tasks/get` round-trip. Polling is the default; only if a server supports notifications can clients rely on them instead of polling.
MCP two transports: stdio vs Streamable HTTP
MCP supports exactly two transport mechanisms. Stdio transport uses standard input/output streams for direct process communication between local processes on the same machine, giving optimal performance with no network overhead. Streamable HTTP transport uses HTTP POST for client-to-server messages with optional Server-Sent Events for streaming, enabling remote server communication and supporting standard HTTP authentication such as bearer tokens, API keys, and custom headers; MCP recommends OAuth to obtain authentication tokens.
When to choose stdio vs Streamable HTTP (client cardinality)
Local MCP servers that use the STDIO transport typically serve a single MCP client, whereas remote MCP servers that use the Streamable HTTP transport typically serve many MCP clients concurrently. A server launched by the host process on the same machine (e.g. the filesystem server launched by Claude Desktop) is a 'local' server using STDIO; a server hosted on a vendor platform (e.g. the Sentry MCP server) is a 'remote' server using Streamable HTTP.
Transport layer is interchangeable: same JSON-RPC 2.0 messages
MCP consists of a data layer (inner) and a transport layer (outer). The transport layer handles connection establishment, message framing, and authorization, and abstracts communication details from the protocol layer so the exact same JSON-RPC 2.0 message format is used across all transport mechanisms. Server logic therefore does not need to change between stdio and Streamable HTTP.
server.json package entry declares registryType, identifier and transport
A registry server.json entry lists `packages`, where each package object contains `registryType` (e.g. "npm"), `identifier` (e.g. "@username/email-integration-mcp"), `version`, and a `transport` object whose `type` is for example "stdio". This is how a published server declares which transport a client should use to launch it.
Remote server entry uses remotes[] with type streamable-http and a URL
A remote MCP server is declared in server.json via a `remotes` array, e.g. {"version": "2.1.0", "remotes": [{"type": "streamable-http", "url": "https://api.myservice.com/mcp/v2.1"}]}. The transport type value for remote HTTP servers is `streamable-http`, versus `stdio` for local package-based servers.
Registry package entry declares the transport type
Each entry in the `packages` array of server.json declares how the server is launched via a `transport` object, e.g. {"type": "stdio"} for a locally executed stdio server. Remote servers are supported through separate remote-server registry documentation.
Ping is excepted from request association requirement
ping is an MCP-level liveness check and MAY be sent by either party at any time on an established session/connection. Request-association requirements for roots/list, sampling/createMessage, and elicitation/create do not apply to ping. In Streamable HTTP, implementations SHOULD prefer transport-level SSE keepalive mechanisms for idle-connection maintenance; ping remains available for protocol-level responsiveness checks.
Benefits of enforcing request association
Enforcing request association simplifies transport implementations by removing need to support arbitrary server-initiated request/response flows. It clarifies user experience so users understand sampling/elicitation happens because they initiated an action. It reduces security surface by ensuring clients have context for what scope additional requested information will be used for, allowing better informed decisions. It aligns with practice, as all existing implementations already follow this pattern.
Example of prohibited standalone server request pattern
The following pattern is now explicitly prohibited: a background task that sleeps in a loop and attempts to initiate sampling without any client request context. This was never explicitly documented or recommended but is now explicitly disallowed.
What is an MCP transport binding
A transport is a binding that defines how MCP messages are framed and delivered, how request metadata is carried, and how cancellation and termination are signaled. The binding does not define what the messages mean; protocol semantics are identical on every transport. Message patterns are part of the core protocol and are the same on every binding.
Standard MCP transports
The two standard MCP transports are: (1) stdio: newline-delimited messages over the standard streams of a client-launched subprocess; (2) Streamable HTTP: each message is an HTTP POST to a single MCP endpoint, with replies arriving as a JSON object or a request-scoped SSE stream.
JSON-RPC encoding in MCP
MCP uses JSON-RPC to encode messages. JSON-RPC messages MUST be UTF-8 encoded.
Message direction in MCP transports
A binding MUST deliver client-sent requests and notifications to the server, and server-sent responses and notifications to the client. No other message direction exists: servers do not initiate JSON-RPC requests and clients do not send JSON-RPC responses.
Request metadata location in MCP
All protocol metadata travels in the message body: every request carries its protocol version and client capabilities in _meta.io.modelcontextprotocol/* fields. A binding MAY additionally mirror selected body fields into envelope metadata.
Custom transports MUST preserve JSON-RPC format and message patterns
Implementers who support custom transports MUST preserve the JSON-RPC message format, the message patterns, and the per-request metadata model. Custom transports SHOULD document their connection establishment, message framing, and cancellation patterns to aid interoperability.
Custom transports over byte streams SHOULD reuse stdio framing
Custom transports that run over a reliable bidirectional byte stream such as Unix domain sockets or TCP SHOULD reuse the stdio framing rather than defining a new one. The stdio binding is just newline-delimited JSON-RPC over a byte stream, and only its process-lifecycle rules are specific to standard streams.
Cancellation is binding-specific
Each binding defines how a client abandons an in-flight request. On stdio the client sends a notifications/cancelled notification; on Streamable HTTP it closes the request's response stream. The protocol-level rules are the same everywhere.
Custom transports are allowed in MCP
Clients and servers MAY implement additional custom transport mechanisms to suit their specific needs. The protocol is transport-agnostic and can be implemented over any communication channel that supports bidirectional message exchange.