Build the image around the transport your clients need. Use stdio when a local host launches your MCP process; use Streamable HTTP when clients connect to a deployed service. Package an official MCP SDK server, its locked dependencies and only the required source files in a small, reproducible image. For HTTP, serve the SDK’s /mcp endpoint behind TLS and configure host/origin allowlists before exposing it publicly.
Choose the transport before writing the Dockerfile
The transport determines whether the container needs a listening port and how it is tested.
| Transport | Use it when | Container consequence |
|---|---|---|
| stdio | A local application (such as an IDE or desktop agent) starts the server process | No listening port. Keep standard output exclusively for JSON-RPC messages; send logs to standard error. |
| Streamable HTTP | Remote clients, several clients, or a managed deployment need an endpoint | Expose an HTTP port, normally mount the MCP application at /mcp, and configure host/origin security. |
| HTTP+SSE | You must support an older client | Use only for compatibility. The current TypeScript SDK recommends Streamable HTTP for new remote servers. |
A single source tree can support both modes, but run each mode with an explicit command. Do not make a production container guess from an environment variable unless you have tested both startup paths.
Create a minimal Python MCP server
Python SDK v2 requires Python 3.10 or newer. The following example registers one tool and defaults to stdio, which is the safest starting point for a locally spawned container.
#1 Best Overall
server.py for stdio
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("docker-demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b
if __name__ == "__main__":
mcp.run(transport="stdio")
Anything written to stdout can be interpreted as protocol data. Use print(..., file=sys.stderr) or the logging module configured for stderr for diagnostics. One accidental stdout log line can break the client handshake.
HTTP entrypoint
The Python SDK exposes a Starlette ASGI application through streamable_http_app(). Put the server object and the HTTP launcher in a separate entrypoint so the image command is unambiguous.
from mcp.server.fastmcp import FastMCP
import uvicorn
mcp = FastMCP("docker-demo")
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two integers."""
return a + b
app = mcp.streamable_http_app()
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
This application normally answers at http://host:8000/mcp. The process manager, TLS termination, worker count and load balancer are deployment concerns rather than MCP protocol features.
TypeScript option
The current MCP TypeScript first-server guide requires Node.js 20 or newer and ES modules. A stdio server can be kept very small:
Recommended Free Tools
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "docker-demo", version: "1.0.0" });
server.tool(
"add",
"Add two integers",
{ a: z.number().int(), b: z.number().int() },
async ({ a, b }) => ({ content: [{ type: "text", text: String(a + b) }] })
);
const transport = new StdioServerTransport();
await server.connect(transport);
Set "type": "module" in package.json and compile with your chosen TypeScript configuration. The protocol channel is stdout: use console.error() for logs and never console.log() in a stdio server. For a remote TypeScript deployment, use the SDK’s Streamable HTTP transport with an HTTP framework and mount the endpoint at the path expected by your client.
Write a reproducible Dockerfile
Python stdio image
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1
PYTHONUNBUFFERED=1
WORKDIR /app
# requirements.txt should contain the SDK and your application dependencies.
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY server.py .
RUN useradd --create-home --uid 10001 mcpuser
USER mcpuser
ENTRYPOINT ["python", "server.py"]
For production, generate a lock file or a hash-checked requirements file and pin the base image by digest. Copy manifests before source code so Docker can reuse the dependency layer. Do not copy local virtual environments, test data, credentials or .env files; use a .dockerignore.
Python HTTP image
FROM python:3.12-slim
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY http_server.py .
RUN useradd --create-home --uid 10001 mcpuser
USER mcpuser
EXPOSE 8000
CMD ["python", "http_server.py"]
Only the HTTP variant needs EXPOSE; that instruction documents the port and does not publish it by itself.
TypeScript image
FROM node:20-bookworm-slim AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
FROM node:20-bookworm-slim
WORKDIR /app
ENV NODE_ENV=production
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
RUN useradd --create-home --uid 10001 mcpuser
USER mcpuser
CMD ["node", "dist/server.js"]
Keep package-lock.json in the build context so npm ci is deterministic. If your server listens for HTTP, add EXPOSE and an HTTP command instead of the stdio command.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Build, run and verify the image
- Create a narrow build context. Add a
.dockerignorecontaining.git,node_modules, virtual environments, caches, local secrets and test output. - Build a versioned tag.
docker build --pull -t docker-demo-mcp:1.0.0 . - Run a stdio server interactively.
docker run --rm -i docker-demo-mcp:1.0.0Keep
-i; without an attached stdin, a stdio client cannot communicate with the process. Your MCP client should launch this exact command and exchange JSON-RPC over the container’s stdin/stdout. - Run the HTTP server locally.
docker run --rm --name docker-demo-mcp -p 8000:8000 -e API_TOKEN docker-demo-mcp:1.0.0Do not put the token in the Dockerfile or image history.
- Test with the same shape as production. Point an MCP client or MCP Inspector at
http://localhost:8000/mcp. Check startup logs, tool discovery and one real tool call. A generic HTTP health probe is useful, but it does not prove that MCP initialization and session handling work. - Record the immutable artifact. Push the tested tag to your registry and deploy by image digest where your platform supports it.
Expose /mcp safely
Localhost defaults are intentionally restrictive. In a deployed Python HTTP server, configure the SDK’s allowed_hosts and allowed_origins for the exact public hostname and origins that should connect. A missing host entry can produce 421 Misdirected Request; an origin rejected before MCP handling can produce 403 Forbidden.
Recommended boundary controls
- Terminate TLS at a managed ingress or reverse proxy and forward only to the private container port.
- Allow the canonical hostname, not a wildcard, unless you have a documented reason.
- Use platform identity, a gateway-issued credential or another authenticated boundary. Treat MCP tools as privileged actions.
- Pass API keys and cookies at runtime through your orchestrator or Docker MCP secret mechanisms; never bake them into layers or source control.
- Restrict each client to the tools and downstream credentials it actually needs.
- Keep the container non-root and make its filesystem read-only when the SDK and application permit it.
For stateful Streamable HTTP sessions, ensure your load balancer keeps a client on a compatible worker or that your server stores session state in a shared backend. A stateless design is easier to scale horizontally, but only if your tools do not depend on in-memory conversation or connection state.
Use Docker MCP Toolkit and Gateway
Docker MCP Toolkit organizes servers and clients with profiles. Docker MCP Gateway centralizes routing, credentials, access control and server lifecycle. The Gateway starts a server container when a requested tool is not already running, so clients do not each need to manage separate container commands.
The Docker MCP Catalog lists more than 300 verified servers packaged as container images with versioning, provenance and security updates. The documented Toolkit interface applies to Docker Desktop 4.62 and later. A practical flow is:
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Rank #3
- Create a profile for the client or team.
- Add the server image (from the Catalog or your registry) and declare required secrets.
- Connect the MCP client to the Gateway rather than directly to every container.
- Invoke a harmless discovery call and then a representative tool call.
- Review which credentials and tools the profile can access before sharing it.
Gateway management does not remove the need for a correct image: the container still needs a deterministic startup command, clean stdio or a reachable HTTP endpoint, and useful diagnostics.
Production checklist
- Use Python 3.10+ or Node.js 20+ as required by the current SDK guides.
- Pin application dependencies and preferably the base-image digest; rebuild deliberately for security updates.
- Run as a non-root user and drop unnecessary Linux capabilities.
- Keep stdout protocol-clean for stdio; send all logs to stderr.
- Expose only the HTTP port required by Streamable HTTP and keep it private behind authenticated ingress.
- Configure exact host and origin allowlists; do not disable SDK protections as a shortcut.
- Define startup and liveness diagnostics outside the MCP protocol stream.
- Set timeouts for slow tools and cap concurrency according to downstream service limits.
- Test the built image, not merely the source tree, with the same transport and endpoint path production clients use.
- Capture image digests, dependency versions and configuration in deployment records so a failing release can be rolled back.
Performance, reliability and cost decisions
Image and startup performance
Multi-stage builds keep compilers and development dependencies out of the runtime image. Layer dependency installation before source copying to maximize cache reuse. A smaller image downloads faster, but do not remove certificates, fonts or OS libraries your tools actually require.
HTTP scaling
Streamable HTTP supports remote and multi-client use, but each connection and tool call consumes server, proxy and downstream capacity. Set explicit proxy timeouts, cap request body sizes, and load-test the slowest tool. If sessions are in memory, use connection affinity or shared state before adding replicas.
Failure and billing boundaries
Container cost is determined by your Docker host or cloud platform, not by MCP itself. Budget for registry storage, egress, CPU, memory and any downstream API calls. A managed platform can simplify TLS and identity; direct Docker runs give more control but leave patching, monitoring, failover and secret rotation to you.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Troubleshooting
The client reports an invalid JSON-RPC response
In stdio mode, a log line or framework banner is probably on stdout. Move every diagnostic to stderr, disable colorful startup output, rebuild the image and retry with an interactive stdin.
The container exits immediately
Inspect docker logs and verify the entrypoint, working directory, executable permissions and required environment variables. For stdio, an absent or closed stdin can make a correctly behaving server terminate.
HTTP requests return 404 at the root path
The MCP application is normally mounted at /mcp, not /. Point the client and reverse proxy at the complete path and preserve it when forwarding.
HTTP returns 421 or 403 before a tool runs
Check the requested host and the browser/client origin against allowed_hosts and allowed_origins. Include the public hostname used by the proxy, not only the container’s internal name.
Free tools Windows power users keep installed
One-click scans. No signup required.
The client connects but discovery hangs
Confirm that the proxy supports the streaming behavior required by Streamable HTTP, that idle timeouts exceed the longest expected operation, and that the server’s worker is not blocked by synchronous code.
Works locally but fails in Gateway
Compare the Gateway profile’s image tag, command, environment variables, mounted secrets, network policy and endpoint path with the command you tested manually. Verify that the Gateway can resolve and reach the registry and downstream services.
New replicas lose sessions
Your deployment is stateful. Add shared session storage or connection affinity, or redesign the server so each request carries everything needed and workers can remain stateless.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your MCP project also needs website captures for documentation, visual checks or agent workflows, ScreenshotNeo provides a one-call screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; bot checks, blank pages, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. AI clients can use its MCP tools take_screenshot, get_page_info and capture_pdf.
Windows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOutdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchStart with the ScreenshotNeo API documentation and call:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
There is a free allowance of 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 screenshots, and every feature is included on every plan. Create a free ScreenshotNeo account.
Best Value
- Docker, Docker Swarm, Docker Compose, Programmer, Developer, Coding, Programming, Software Engineer, Code, DevOps, Deploy, Deployment, Kubernetes, Salt, Puppet, Chef, Terraform, Container, AWS, Azure, Cloud, Geek, Funny, Computer, Software, Tech, IT
- Integration, Scrum, Compile, Compilation, Science, Bug, Debug, Python, Linux, Java, Javascript, Scala, Dotnet, Kotlin
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
FAQ
Can one image support both stdio and Streamable HTTP?
Yes. Keep shared tool registration in one module and provide separate stdio and HTTP entrypoints or commands. Deploy them as distinct workloads so each has clear health checks and security settings.
Do I need Kubernetes to run an MCP image?
No. Docker Engine, a registry and a suitable host are enough. Kubernetes or a managed container service becomes useful when you need automated rollouts, replicas, ingress and centralized secrets.
Should I expose the container port directly to the internet?
Generally no. Put the HTTP server behind TLS, authentication and an ingress or gateway that limits hosts, origins, methods and network access.
Is HTTP+SSE unusable?
No. It remains relevant for older clients. For a new remote implementation, Streamable HTTP is the recommended direction in the TypeScript SDK documentation.
What belongs in the image versus the deployment configuration?
Put application code and runtime dependencies in the image. Keep secrets, hostnames, origins, credentials, replica counts and environment-specific limits in the deployment system.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

