by dawsonlp
A minimal containerized base for MCP servers using the MCP Python SDK.
BaseMcpServer provides a standardized Docker base image for building Model Context Protocol (MCP) servers. It is:
This image provides all the common dependencies and configuration needed for MCP servers, so that derived projects can focus solely on implementing their specific tools and resources.
For local development without Docker, each MCP server includes setup scripts to create and manage Python virtual environments with the necessary dependencies.
Each MCP server includes two shell scripts for easy setup and execution:
The setup.sh
script creates a virtual environment and installs all dependencies:
# Navigate to the desired MCP server directory cd example/ # Run the setup script ./setup.sh
This will:
.venv
directory with a Python virtual environmentrequirements.txt
The run.sh
script activates the virtual environment and runs the MCP server:
# Start with SSE protocol (for Claude/Cline integration) ./run.sh sse # Or start with stdio protocol (for direct stdin/stdout communication) ./run.sh stdio
If the scripts don't work on your system, you can manually set up the environment:
# Navigate to the desired MCP server directory cd example/ # Create a virtual environment python3.11 -m venv .venv # Activate the virtual environment source .venv/bin/activate # On Windows: .venv\Scripts\activate # Install dependencies pip install -r requirements.txt # Set PYTHONPATH and run the server export PYTHONPATH="$PWD/src:$PYTHONPATH" # On Windows: set PYTHONPATH=%CD%\src;%PYTHONPATH% cd src python main.py sse # Or: python main.py stdio
The virtual environment approach:
When creating custom MCP servers based on this template, follow these best practices to avoid common issues:
Use the Correct Ports: The base image exposes port 7501
. Always align your configuration with this port:
.env
file, set PORT=7501
docker run -p EXTERNAL_PORT:7501
"url": "http://localhost:EXTERNAL_PORT/sse"
Consistent Port Usage: Be consistent with your port numbering. If you choose external port 7777:
docker run -p 7777:7501
"url": "http://localhost:7777/sse"
Port Conflicts: If you get connection errors, check for port conflicts with lsof -i :PORT_NUMBER
Mounting vs. Copying: For development, you can copy the .env
file into the container:
COPY ./.env ./.env
For production, mount it at runtime:
docker run -p 7777:7501 --env-file .env your-image
Robust Config Loading: Implement robust environment variable loading with logging and fallbacks:
# Add logging of loaded configuration values logger.info(f"JIRA_URL: {settings.JIRA_URL}") logger.info(f"Using port: {settings.port}")
Verify Environment: Use explicit validation of required environment variables with clear error messages
The project now includes an mcp-manager
utility for easy installation and management of MCP servers. This tool simplifies the process of setting up, configuring, and running MCP servers.
# install pipx brew install pipx # Install directly from the repository pipx install git+https://github.com/dawsonlp/BaseMcpServer.git#subdirectory=utils/mcp_manager
# Install a local MCP server mcp-manager install local example-server --source ./example # Install from a Git repository mcp-manager install git jira-server --repo https://github.com/example/jira-mcp-server.git # List installed servers mcp-manager list # Configure VS Code integration mcp-manager configure vscode # Run a server manually (if needed) mcp-manager run server-name --transport stdio
To connect your MCP server to Claude Desktop or Cline in VS Code:
For locally installed servers using mcp-manager:
For manually running servers:
./run.sh sse # For local development with HTTP+SSE transport
or
docker run -p 7501:7501 your-image # For Docker
For Cline in VS Code:
With mcp-manager, you can simply run:
mcp-manager configure vscode
Or manually edit the settings file:
Path: ~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json
Example configuration:
*Configuration content*bash
./build.sh base-mcp-server latest 7501
Parameters:
- `base-mcp-server`: Image name (default)
- `latest`: Tag (default)
- `7501`: Port to expose (default)
- `<your-docker-username>`: **Required** - Your Docker Hub username
This will:
1. Build the base image
1. Tag it for Docker Hub
1. Push it to Docker Hub if you're logged in
### Using the Base Image in Derived Projects
In your Dockerfile:
```dockerfile
# Define build argument for Docker Hub username
ARG DOCKER_USERNAME
# Use the base MCP server image from Docker Hub
FROM docker.io/${DOCKER_USERNAME}/base-mcp-server:latest
# Copy your application code
COPY ./src ./src
# Set PYTHONPATH to include src as a sources root
ENV PYTHONPATH="/app/src:${PYTHONPATH}"
# Set working directory to src
WORKDIR /app/src
# Command to run your MCP server with sse transport
CMD ["python", "main", "sse"]
BaseMcpServer now supports both HTTP+SSE and stdio protocols:
HTTP+SSE (Server-Sent Events) is one of the standard transports supported by the MCP protocol:
HTTP+SSE is designed for networked environments, making it ideal for:
The stdio transport uses standard input/output streams for communication:
This transport is primarily used for:
This base image uses:
The implementation is provided by the MCP Python SDK through:
.sse_app()
method for HTTP+SSErun("stdio")
support for stdio modeWhen extending this base image, your MCP server is automatically served via HTTP+SSE. For more advanced scenarios where you need to integrate with existing web services, you can mount the MCP server to an existing ASGI application:
from starlette.applications import Starlette from starlette.routing import Mount, Host from mcp.server.fastmcp import FastMCP mcp = FastMCP("My App") # Mount the MCP server to an existing ASGI application app = Starlette( routes=[ Mount('/mcp', app=mcp.sse_app()), ] ) # Or mount it as a subdomain app.router.routes.append(Host('mcp.example.com', app=mcp.sse_app()))
When using HTTP+SSE in production:
The image directly uses the MCP Python SDK with minimal abstraction:
The base image supports configuration through environment variables, which can be passed to derived images in several ways:
HOST
: Interface to bind to (default: 0.0.0.0)PORT
: Port to listen on (default: 7501)API_KEY
: Required for authentication (must be provided)SERVER_NAME
: Unique identifier for your MCP serverdocker run -p 7501:7501 \ -e API_KEY=your_api_key \ -e SERVER_NAME=your-mcp-server \ yourdockerusername/your-mcp-server:latest
Create a .env
file with your configuration:
API_KEY=your_api_key
SERVER_NAME=your-mcp-server
HOST=0.0.0.0
PORT=7501
Then run with:
docker run -p 7501:7501 --env-file .env yourdockerusername/your-mcp-server:latest
For production deployments using Docker Swarm:
echo "your_api_key" | docker secret create api_key - echo "your_server_name" | docker secret create server_name - docker service create \ --name your-mcp-server \ --secret api_key \ --secret server_name \ --publish 7501:7501 \ yourdockerusername/your-mcp-server:latest
For development or testing purposes, you can build configuration directly into derived images:
FROM docker.io/dawsonlp/base-mcp-server:latest # Configure environment variables (non-sensitive only!) ENV HOST=0.0.0.0 ENV PORT=7501 ENV SERVER_NAME=example-mcp-server # Copy application code COPY ./src ./src CMD ["python", "main", "sse"]
This repository contains:
docker/Dockerfile
: Multi-stage Dockerfile for the base imagebuild.sh
: Build script with Docker Hub integrationrequirements-base.txt
: Base Python dependenciesNo version information available
0 contributors