Back to Blog
Tutorial12 min read2026-06-30

How to Deploy an MCP Server to Production

Take a Model Context Protocol server from stdio prototype to a production HTTP deployment with streamable transport, auth, and observability that AI clients can reliably connect to.

Ajay Kumar
Ajay Kumar
Founder & DevOps, PandaStack

# How to Deploy an MCP Server to Production

The Model Context Protocol (MCP) is the emerging standard for connecting AI assistants to tools and data. Most MCP tutorials run the server over stdio on your laptop — great for local development with Claude Desktop, useless for letting a remote client connect. This guide takes an MCP server from stdio prototype to a publicly reachable HTTP deployment.

stdio vs. HTTP transport

MCP defines multiple transports. The key distinction for deployment:

TransportUse caseReachable remotely?
stdioLocal desktop clients launching the server as a subprocessNo
Streamable HTTPRemote/hosted servers, multiple clientsYes

For production you want Streamable HTTP, the current recommended remote transport (it superseded the older HTTP+SSE approach). It runs over a normal HTTPS endpoint, which is exactly what a container platform serves.

A minimal HTTP MCP server

Using the official TypeScript SDK:

// server.ts
import express from 'express';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { z } from 'zod';

const mcp = new McpServer({ name: 'weather-mcp', version: '1.0.0' });

mcp.tool(
  'get_forecast',
  { city: z.string() },
  async ({ city }) => {
    const data = await fetchForecast(city); // your logic
    return { content: [{ type: 'text', text: JSON.stringify(data) }] };
  }
);

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

app.post('/mcp', async (req, res) => {
  const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
  res.on('close', () => transport.close());
  await mcp.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

app.get('/healthz', (_req, res) => res.send('ok'));
app.listen(process.env.PORT || 3000);

This exposes a single /mcp endpoint that AI clients POST to. (Exact API surface varies by SDK version — check the SDK docs for your version; the deployment shape stays the same.)

Add authentication

An open MCP server is an open door to whatever tools it exposes. At minimum, require a bearer token:

app.use('/mcp', (req, res, next) => {
  const token = (req.headers.authorization || '').replace('Bearer ', '');
  if (token !== process.env.MCP_API_KEY) return res.status(401).end();
  next();
});

The MCP spec also defines an OAuth 2.1-based authorization flow for richer scenarios; for internal tools a static key behind HTTPS is a reasonable start. Whatever you choose, never ship an unauthenticated server that can read databases or call paid APIs.

Containerize

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/server.js"]

Python MCP servers (FastMCP and similar) follow the same pattern — expose HTTP, containerize, deploy.

Deploy on PandaStack

  1. 1Push the repo to GitHub.
  2. 2Create a container app in the [dashboard](https://dashboard.pandastack.io). It builds with rootless BuildKit and serves an HTTPS URL with automatic SSL — non-negotiable, since MCP clients require secure transport for remote servers.
  3. 3Set MCP_API_KEY and any tool credentials (API keys for the services your tools call) as encrypted env vars.
  4. 4If your tools need persistence — caching, rate-limit counters, stored context — provision a managed PostgreSQL or Redis; DATABASE_URL is auto-wired.

Long-lived connections and scale-to-zero

Streamable HTTP can hold streaming responses open. That interacts poorly with aggressive scale-to-zero: an idle server scaling down mid-session drops the connection. For an MCP server that real clients connect to, run on a paid compute tier so at least one instance stays warm. Use the free tier for experiments where occasional cold starts are acceptable.

Pick a compute tier by what your tools do:

Tool workloadSuggested tier family
Light API proxyingSmall shared compute
CPU-heavy parsingc1/c2 compute-optimized
Large in-memory cachesm1/m2 memory-optimized

Connecting a client

Point a remote-capable MCP client at your URL. For clients that read a JSON config, it looks roughly like:

{
  "mcpServers": {
    "weather": {
      "url": "https://<your-app>/mcp",
      "headers": { "Authorization": "Bearer <MCP_API_KEY>" }
    }
  }
}

Use the MCP Inspector during development to call tools manually and confirm schemas before wiring up a real assistant.

Observability

MCP servers fail in confusing ways — a tool throws, a schema mismatches, a downstream API rate-limits. Log every tool invocation with its arguments (scrub secrets) and outcome. PandaStack's live, Elasticsearch-backed logs let you tail tool calls in real time, and server-side metrics show request rates and latency without adding an SDK to your server.

Common pitfalls

  • Using stdio in production — it can't accept remote connections; switch to Streamable HTTP.
  • No auth — anyone who finds the URL can run your tools.
  • Scale-to-zero dropping streams — keep an instance warm for real traffic.
  • Returning non-spec responses — tool results must follow the content-block shape the SDK expects.

References

  • [Model Context Protocol — specification](https://modelcontextprotocol.io/specification)
  • [MCP Transports](https://modelcontextprotocol.io/docs/concepts/transports)
  • [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk)
  • [MCP Inspector](https://github.com/modelcontextprotocol/inspector)

MCP is moving fast, and hosting a remote server is the difference between a local toy and a shared capability. PandaStack gives you HTTPS, encrypted secrets, warm compute, and live logs — deploy yours from [dashboard.pandastack.io](https://dashboard.pandastack.io).

Ready to deploy?

Start free on PandaStack.

Start free on PandaStack

More in Tutorial

Browse all Tutorial articles →

See also