Client consumes the same primitives no matter how it reached the server, so the only thing that changes between a server running across the network, a server running as a child process, and a server running inside your own process is the argument you hand to Client.connect(). You never name the transport explicitly — the client infers it from the shape of that argument and builds the right connection for you.
Connecting
Client.connect() inspects its first argument and resolves a transport from it. A string is a URL, so the client speaks Streamable HTTP. A StdioTransport describes a command to run, so the client spawns that subprocess and talks to it over stdin and stdout. A live FastMCP instance is already in memory, so the client wires up an in-process channel with no network or process boundary at all.
Transport inputs
Each accepted input maps to a concrete connection, and knowing the mapping tells you which one to reach for. A string URL speaks Streamable HTTP, the transport for servers exposed over the network. It is the only network transport the client reaches for on its own. The older SSE transport is deprecated in the MCP SDK, so the client never connects to it silently: a URL whose path looks like an SSE endpoint — one ending in/sse — throws a clear error that points you at Streamable HTTP and at the opt-in below. To connect to a legacy SSE server on purpose, set legacySSE: true, which permits the SSE path and logs a one-time deprecation warning.
StdioTransport instance spawns a subprocess and communicates over its standard streams. This is how you drive a server that ships as a command — a published package run through npx, or a local file run through tsx. The first argument is the binary and the rest are its arguments, kept as separate array elements so commands with spaces resolve correctly.
A FastMCP instance connects in process, covered below. The client recognizes it structurally — anything with a connect method qualifies — which avoids importing the server module into client code.
An mcpServers config describes one or more servers by name, the same shape MCP clients use in their configuration files. Each entry is either { url } for an HTTP server or { command, args } for a stdio server, and entries may also carry per-server auth.
Client, so a config file with one server behaves exactly like connecting to that server directly. A config with more than one entry returns a MultiServerClient that aggregates and namespaces every server behind one interface — the same code path resolves each entry’s transport, so a config can freely mix HTTP and stdio servers.
A raw SDK Transport object passes through untouched, for the rare case where you have constructed a transport from the underlying MCP SDK yourself.
In-process connections
Connecting to aFastMCP instance directly skips the network and the subprocess entirely. The client pairs an in-memory transport between the two halves and runs the full MCP protocol across it — every request, response, and notification behaves exactly as it would over HTTP, with none of the serialization or process overhead.
connect() — you only pass the instance.
The in-memory pairing is a 2025-era transport, so an in-process connection is legacy unless you pin the modern era. Pass versionNegotiation: { mode: { pin: '2026-07-28' } } and the client routes through the server’s modern stateless handler instead of the in-memory pair, which lets a test exercise modern-era behavior with no network. The default and the 'auto' and 'legacy' modes all use the in-memory pairing and connect legacy.
Browser builds
The client bundles for the browser, where the only server it can reach is one exposed over the network. A string URL works unchanged: Streamable HTTP rides the browser’s ownfetch. A legacy SSE endpoint still needs the explicit legacySSE opt-in described above, and rides EventSource when you enable it. The other inputs are inherently Node-only — a StdioTransport spawns a subprocess, and an in-process FastMCP instance needs a server living in the same runtime, neither of which a browser can offer. Those paths are loaded lazily, so they never enter a browser bundle, and a build pulls in only the HTTP transport it actually uses. Proving who you are from a browser is its own concern, covered in authentication.