Meet Prefect Horizon, the enterprise MCP gateway built by the team behind FastMCP

v2.14.5

These are the docs for FastMCP 2.0. FastMCP 3.0 is now available.

Choosing the Right Transport

  • Use STDIO Transport when you need to run local MCP servers with full control over their environment and lifecycle
  • Use Remote Transports when connecting to production services or shared MCP servers running independently
  • Use In-Memory Transport for testing FastMCP servers without subprocess or network overhead
  • Use MCP JSON Configuration when you need to connect to multiple servers defined in configuration files

STDIO Transport

STDIO (Standard Input/Output) transport communicates with MCP servers through subprocess pipes. This is the standard mechanism used by desktop clients like Claude Desktop and is the primary way to run local MCP servers.

The Client Runs the Server

Critical Concept: When using STDIO transport, your client actually launches and manages the server process. This is fundamentally different from network transports where you connect to an already-running server. Understanding this relationship is key to using STDIO effectively.

With STDIO transport, your client:

  • Starts the server as a subprocess when you connect
  • Manages the server’s lifecycle (start, stop, restart)
  • Controls the server’s environment and configuration
  • Communicates through stdin/stdout pipes

Environment Isolation

STDIO servers run in isolated environments by default. This is a security feature enforced by the MCP protocol to prevent accidental exposure of sensitive data. When your client launches an MCP server:

  • The server does NOT inherit your shell’s environment variables
  • API keys, paths, and other configuration must be explicitly passed
  • The working directory and system paths may differ from your shell

To pass environment variables to your server, use the env parameter:

from fastmcp import Client

# If your server needs environment variables (like API keys),
# you must explicitly pass them:
client = Client(
    "my_server.py",
    env={"API_KEY": "secret", "DEBUG": "true"}
)

# This won't work - the server runs in isolation:
# export API_KEY="secret"  # in your shell
# client = Client("my_server.py")  # server can't see API_KEY

Basic Usage

To use STDIO transport, you create a transport instance with the command and arguments needed to run your server:

from fastmcp.client.transports import StdioTransport

transport = StdioTransport(
    command="python",
    args=["my_server.py"]
)
client = Client(transport)

You can configure additional settings like environment variables, working directory, or command arguments:

transport = StdioTransport(
    command="python",
    args=["my_server.py", "--verbose"],
    env={"LOG_LEVEL": "DEBUG"},
    cwd="/path/to/server"
)
client = Client(transport)

Environment Variables

Since STDIO servers don’t inherit your environment, you need strategies for passing configuration. Here are two common approaches:

Selective forwarding passes only the variables your server actually needs:

import os
from fastmcp.client.transports import StdioTransport

required_vars = ["API_KEY", "DATABASE_URL", "REDIS_HOST"]
env = {
    var: os.environ[var]
    for var in required_vars
    if var in os.environ
}

transport = StdioTransport(
    command="python",
    args=["server.py"],
    env=env
)
client = Client(transport)

Loading from .env files keeps configuration separate from code:

from dotenv import dotenv_values
from fastmcp.client.transports import StdioTransport

env = dotenv_values(".env")
transport = StdioTransport(
    command="python",
    args=["server.py"],
    env=env
)
client = Client(transport)

Session Persistence

STDIO transports maintain sessions across multiple client contexts by default (keep_alive=True). This improves performance by reusing the same subprocess for multiple connections, but can be controlled when you need isolation. By default, the subprocess persists between connections:

from fastmcp.client.transports import StdioTransport

transport = StdioTransport(
    command="python",
    args=["server.py"]
)
client = Client(transport)

async def efficient_multiple_operations():
    async with client:
        await client.ping()

async with client:  # Reuses the same subprocess
        await client.call_tool("process_data", {"file": "data.csv"})

For complete isolation between connections, disable session persistence:

transport = StdioTransport(
    command="python",
    args=["server.py"],
    keep_alive=False
)
client = Client(transport)

Use keep_alive=False when you need complete isolation (e.g., in test suites) or when server state could cause issues between connections.

Specialized STDIO Transports

FastMCP provides convenience transports that are thin wrappers around StdioTransport with pre-configured commands:

  • PythonStdioTransport - Uses python command for .py files
  • NodeStdioTransport - Uses node command for .js files
  • UvStdioTransport - Uses uv for Python packages (uses env_vars parameter)
  • UvxStdioTransport - Uses uvx for Python packages (uses env_vars parameter)
  • NpxStdioTransport - Uses npx for Node packages (uses env_vars parameter)

For most use cases, instantiate StdioTransport directly with your desired command. These specialized transports are primarily useful for client inference shortcuts.

Remote Transports

Remote transports connect to MCP servers running as web services. This is a fundamentally different model from STDIO transports—instead of your client launching and managing a server process, you connect to an already-running service that manages its own environment and lifecycle.

Streamable HTTP Transport

New in version 2.3.0 Streamable HTTP is the recommended transport for production deployments, providing efficient bidirectional streaming over HTTP connections.

  • Class: StreamableHttpTransport
  • Server compatibility: FastMCP servers running with mcp run --transport http

The transport requires a URL and optionally supports custom headers for authentication and configuration:

from fastmcp.client.transports import StreamableHttpTransport

# Basic connection
transport = StreamableHttpTransport(url="https://api.example.com/mcp")
client = Client(transport)

# With custom headers for authentication
transport = StreamableHttpTransport(
    url="https://api.example.com/mcp",
    headers={
        "Authorization": "Bearer your-token-here",
        "X-Custom-Header": "value"
    }
)
client = Client(transport)

For convenience, FastMCP also provides authentication helpers:

from fastmcp.client.auth import BearerAuth

client = Client(
    "https://api.example.com/mcp",
    auth=BearerAuth("your-token-here")
)

In-Memory Transport

In-memory transport connects directly to a FastMCP server instance within the same Python process. This eliminates both subprocess management and network overhead, making it ideal for testing and development.

from fastmcp import FastMCP, Client
import os

mcp = FastMCP("TestServer")

@mcp.tool
def greet(name: str) -> str:
    prefix = os.environ.get("GREETING_PREFIX", "Hello")
    return f"{prefix}, {name}!"

client = Client(mcp)

async with client:
    result = await client.call_tool("greet", {"name": "World"})

MCP JSON Configuration Transport

New in version 2.4.0 This transport supports the emerging MCP JSON configuration standard for defining multiple servers:

config = {
    "mcpServers": {
        "weather": {
            "url": "https://weather.example.com/mcp",
            "transport": "http"
        },
        "assistant": {
            "command": "python",
            "args": ["./assistant.py"],
            "env": {"LOG_LEVEL": "INFO"}
        }
    }
}

client = Client(config)

async with client:
    # Tools are namespaced by server
    weather = await client.call_tool("weather_get_forecast", {"city": "NYC"})
    answer = await client.call_tool("assistant_ask", {"question": "What?"})