Build an MCP server using Docker containers for maximum flexibility. While this example uses Python, the Docker container principles and patterns shown here can be adapted to any programming language - Go, Rust, Java, C#, or any other language of your choice.
Port Requirement: Your server must listen on the configurable PORT environment variable. Smithery will set this to 8081 when deployed, but you can use any port for local development.
# main.pydef main(): print("Say Hello MCP Server starting...") # Setup Starlette app with CORS for cross-origin requests app = mcp.streamable_http_app() # IMPORTANT: add CORS middleware for browser based clients app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["GET", "POST", "OPTIONS"], allow_headers=["*"], expose_headers=["mcp-session-id", "mcp-protocol-version"], max_age=86400, ) # Get port from environment variable (Smithery sets this to 8081) port = int(os.environ.get("PORT", 8080)) print(f"Listening on port {port}") uvicorn.run(app, host="0.0.0.0", port=port, log_level="debug")if __name__ == "__main__": main()
You can create your own Dockerfile or use this recommended template:
Copy
Ask AI
# Dockerfile# Use a Python image with uv pre-installedFROM ghcr.io/astral-sh/uv:python3.12-alpine# Install the project into `/app`WORKDIR /app# Enable bytecode compilationENV UV_COMPILE_BYTECODE=1# Copy from the cache instead of linking since it's a mounted volumeENV UV_LINK_MODE=copy# Install the project's dependencies using the lockfile and settingsRUN --mount=type=cache,target=/root/.cache/uv \ --mount=type=bind,source=uv.lock,target=uv.lock \ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \ uv sync --locked --no-install-project --no-dev# Then, add the rest of the project source code and install it# Installing separately from its dependencies allows optimal layer cachingCOPY . /appRUN --mount=type=cache,target=/root/.cache/uv \ uv sync --locked --no-dev# Place executables in the environment at the front of the pathENV PATH="/app/.venv/bin:$PATH"# Reset the entrypoint, don't invoke `uv`ENTRYPOINT []# Run the application directly using the venv PythonCMD ["python", "main.py"]
uv Docker Best Practices: For more examples and best practices on using uv with Docker, including multistage builds and development workflows, check out the uv Docker example repository.
Local vs Deployed Ports: For local testing, you can use any port (like 8080 above). However, when deployed to Smithery, your server must listen on the PORT environment variable, which Smithery will set to 8081.
Test interactively: Once your server is running in HTTP mode, you can test it interactively using the Smithery playground:
Copy
Ask AI
# Using npx (no installation needed)npx -y @smithery/cli playground --port 8080
Or install globally:
Copy
Ask AI
# Install globallynpm install -g @smithery/cli# Then run playgroundsmithery playground --port 8080
This guide showed how to build a Python MCP server using custom Docker containers and FastMCP. We implemented a simple say_hello tool with proper CORS configuration and HTTP transport. This approach gives you full control over your Python environment and can be extended to work with any other programming language by adapting the Dockerfile and server implementation.Need help? Join our Discord or email support@smithery.ai for assistance.