Deployment

Deploying FastMCP (Python) and TypeScript SDK Servers to Production

Package a FastMCP or @modelcontextprotocol/sdk server for Streamable HTTP, then ship it to MCPLambda via CLI or GitOps — with correct transport syntax for both frameworks.

11 min read Published August 10, 2026 Updated August 10, 2026

FastMCP abstracts protocol plumbing into Python decorators; the official @modelcontextprotocol/sdk does the same for TypeScript. Both get you a working local server fast. The gap is what comes next: picking a transport that’s actually current, packaging the result so a clean container can start it, and pointing it at a production host.

Three layers, not one

Keep these separate or the guidance below won’t make sense:

  • MCP protocol — the spec at modelcontextprotocol.io, currently version 2025-11-25. It defines transports, lifecycle, tools.
  • Framework — FastMCP (Python) and @modelcontextprotocol/sdk (TypeScript) are open-source implementations of that spec. Their APIs change between versions independent of the protocol itself.
  • Platform — MCPLambda hosts the container your framework code runs in. It has its own CLI flags and config file, which don’t always share vocabulary with the framework.

Option 1: FastMCP (Python)

server.py

from fastmcp import FastMCP

mcp = FastMCP("Database Tools")

@mcp.tool
def query_user_count(status: str = "active") -> str:
    """Fetch total user count filtered by account status."""
    # Custom database or API logic here
    return f"Total users with status '{status}': 1,420"

if __name__ == "__main__":
    mcp.run(transport="http", host="0.0.0.0", port=8000)

transport="http" starts FastMCP’s Streamable HTTP server (mounted at /mcp by default). Bind host="0.0.0.0" — the container needs to accept connections from outside its own network namespace, not just 127.0.0.1. This requires FastMCP 2.0+; earlier 0.x releases predate the transport="http" argument.

requirements.txt

fastmcp>=2.0

Option 2: TypeScript SDK

src/index.ts

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import express from "express";

const server = new Server(
  { name: "company-metrics-mcp", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "get_monthly_revenue",
      description: "Get monthly revenue stats",
      inputSchema: { type: "object", properties: {} },
    },
  ],
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === "get_monthly_revenue") {
    return { content: [{ type: "text", text: "Revenue: $128,400" }] };
  }
  throw new Error(`Unknown tool: ${request.params.name}`);
});

const app = express();
app.use(express.json());

app.post("/mcp", async (req, res) => {
  // Stateless: a fresh transport per request. Simple, and any container
  // replica can serve the request — no session affinity needed.
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  res.on("close", () => transport.close());
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.listen(8000, () => console.log("MCP server listening on port 8000"));

StreamableHTTPServerTransport lives at @modelcontextprotocol/sdk/server/streamableHttp.js. Passing sessionIdGenerator: undefined runs it stateless — the right default behind a load balancer or multiple replicas. If tools depend on server-side session state across calls, generate a session ID instead and route by the Mcp-Session-Id header (see the SDK’s session docs).

Transport: use Streamable HTTP, not legacy SSE

The 2025-11-25 transports spec defines stdio and Streamable HTTP as the standard transports. HTTP+SSE was the standard remote transport in the older 2024-11-05 spec; the current spec documents it only for backwards compatibility, not as an equal third option for new servers.

LayerHow you say “Streamable HTTP”
FastMCP Python codemcp.run(transport="http", ...)
TypeScript SDK codenew StreamableHTTPServerTransport({...})
MCPLambda CLI flag--transport streamable-http
mcplambda.yaml fieldtransport: streamable-http

Same protocol, four different spellings depending on which layer you’re touching. Set the wrong one and the framework starts correctly but the platform proxies traffic to a transport your process never opens.

Deploying to MCPLambda via CLI

curl -fsSL https://mcplambda.io/mcpl/install.sh | sh
mcpl login

Published package (PyPI/npm):

mcpl deploy uvx://my-fastmcp-server --name db-tools
mcpl deploy npx://@your-org/my-mcp-server --name company-metrics

Your own repo, while you’re still iterating on it — this is the flow most custom servers actually use, since uvx:///npx:// expect an already-published package:

mcpl deploy https://github.com/your-org/my-fastmcp-server --branch main --name db-tools --transport streamable-http

Full flag reference: CLI docs.

Deploying via GitOps (mcplambda.yaml)

Connect the repo in the dashboard and every push to the selected branch triggers a build and deploy. Commit mcplambda.yaml at the repo root so builds don’t depend on flags typed at deploy time.

FastMCP (Python, pip):

run: python server.py
build:
  strategy: pip
  install_command: pip install -r requirements.txt
port: 8000
transport: streamable-http

TypeScript SDK (npm):

run: node dist/index.js
build:
  strategy: npm
  install_command: npm ci
  build_command: npm run build
port: 8000
transport: streamable-http

Neither example needs a Dockerfile — pip and npm are auto-detected build strategies. Full field list and override rules: mcplambda.yaml docs.

Security checklist for Streamable HTTP

The transport spec is explicit here, not just a platform recommendation:

  • MUST validate the Origin header on incoming connections to mitigate DNS rebinding; reject with 403 on mismatch
  • SHOULD implement authentication for all connections — don’t ship an unauthenticated tool endpoint to the public internet
  • Validate tool inputs server-side; don’t trust the client’s declared schema alone
  • Keep secrets (DATABASE_URL, API keys) out of env_vars in mcplambda.yaml — use MCPLambda project secrets, injected at deploy time instead

See Securing remote MCP servers for the full checklist.

What breaks in production

SymptomLikely causeFix
Deploy succeeds, client can’t connectFastMCP host="127.0.0.1" (the default) inside a containerBind host="0.0.0.0"
Works locally, 403s once deployedNo Origin/host validation, or a proxy rewriting it unexpectedlyAdd host-header validation middleware; check MCPLambda’s Deployment URL, not localhost
TypeError: run() got unexpected keywordFastMCP before 2.0 with the new mcp.run(transport=..., host=..., port=...) call shapeUpgrade fastmcp
Deployment shows sse in the platform but framework serves /mcp--transport/yaml set to sse while code runs transport="http" / StreamableHTTPServerTransportMatch all four layers to streamable-http
One replica handles all traffic and others 404Stateful sessionIdGenerator without session-aware routing across replicasUse stateless mode, or route by Mcp-Session-Id

Packaging for production · mcplambda.yaml reference · Transports explained · Securing remote servers

Once it’s running, add it to a Gateway to group it with other servers behind one URL, and check MCP Server Registry if you’re looking for pre-built tools instead.

Sources

FAQs

Frequently Asked Questions

  • Should a new FastMCP or TypeScript SDK server use SSE or Streamable HTTP?

    Streamable HTTP. It is the current remote transport in the MCP specification (2025-11-25); HTTP+SSE was the transport in the 2024-11-05 spec and is now legacy, kept only for backwards compatibility with older clients and servers.

  • Do FastMCP's transport argument and MCPLambda's transport field use the same value?

    No, and mixing them up is the most common mistake. In FastMCP Python code, mcp.run(transport="http") starts a Streamable HTTP server. In MCPLambda's CLI flag and mcplambda.yaml field, the same protocol is named transport: streamable-http. Same wire protocol, different string at each layer.

  • Do I need a Dockerfile to deploy a FastMCP or TypeScript MCP server to MCPLambda?

    No. Git and package-URL deploys auto-build from requirements.txt/pyproject.toml or package.json using a detected strategy (pip, uv, poetry, npm, pnpm). Add a Dockerfile only when you need OS-level packages or a custom base image.