BasicMcpServer
dawsonlp
README
BasicMcpServer
A minimal, production-ready MCP (Model Context Protocol) server implementation exposed via HTTP+SSE in a Docker container.
Overview
This project implements the simplest possible Model Context Protocol (MCP) server that can be deployed in a Docker container and exposed via HTTP with Server-Sent Events (SSE). It follows best practices for environment variable management to ensure credentials are never committed to version control.
The server is designed to be:
- Simple: Minimal boilerplate and dependencies
- Secure: Proper credential management out of the box
- Containerized: Ready for Docker deployment
- Performant: Using FastMCP and Uvicorn for high-performance HTTP serving
Architecture
Python Packages
The project uses the following key packages:
-
MCP SDK: The Python implementation of the Model Context Protocol
mcp: Core MCP functionalitymcp.server.fastmcp: High-level FastMCP framework
-
Web Framework: FastMCP's built-in ASGI support
- Leverages Starlette under the hood
- Simplifies server setup and routing
- Excellent performance characteristics
-
ASGI Server: Uvicorn
- High-performance ASGI server
- Works seamlessly with FastMCP
- Well-suited for containerized environments
-
SSE Implementation: MCP SDK's built-in SSE transport
- Integrated with FastMCP
- Provides Server-Sent Events functionality
-
Configuration Management: pydantic-settings
- Type-safe environment variable handling
- Validation at startup
-
HTTP Client (if needed): httpx
- Async-compatible HTTP client for outbound requests
Dependencies
These will be specified in pyproject.toml:
[project]
name = "basic-mcp-server"
version = "0.1.0"
description = "A minimal MCP server with HTTP+SSE in a Docker container"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"mcp>=1.6.0",
"uvicorn>=0.34.1",
"pydantic-settings>=2.8.1",
"httpx>=0.28.1",
]
Credential Management & Security
To ensure credentials are never committed to version control:
Local Development
-
Create a
.envfile locally with your credentials:API_KEY=your_api_key_here OTHER_SECRET=other_secret_here -
This file is automatically excluded from Git via the
.gitignorefile. -
Use the included
.env.exampleas a template for required variables.
Docker Deployment
-
Never mount your
.envfile directly into the container. -
Pass environment variables at runtime:
docker run -e API_KEY=your_api_key_here -e OTHER_SECRET=other_secret_here basic-mcp-server -
Or use Docker Compose with environment variables:
services: mcp-server: build: . environment: - API_KEY=${API_KEY} - OTHER_SECRET=${OTHER_SECRET} -
For production, consider using Docker Swarm secrets or Kubernetes secrets.
Project Structure
basic-mcp-server/
├── .gitignore # Includes .env* patterns
├── .env.example # Template with dummy values
├── pyproject.toml # Dependencies and metadata
├── README.md # This file
├── readme_fastmcp.md # Documentation on FastMCP vs low-level Server
├── design_decisions.md # Project design decisions log
├── docker/ # Docker containerization
│ ├── Dockerfile # Multi-stage build with Python 3.13
│ ├── .dockerignore # Files excluded from build context
│ ├── README.md # Docker-specific documentation
│ └── scripts/ # Helper scripts
│ ├── entrypoint.sh # Container startup script
│ └── run.sh # Script to replace docker-compose
└── src/
├── __init__.py
├── main.py # Entry point
├── config.py # Environment & configuration management
├── server.py # MCP server implementation using FastMCP
└── tools/ # Tool implementations (alternative organization)
├── __init__.py
└── example.py # Example tool
Implementation Details
Configuration (src/config.py)
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
# Server settings
host: str = "0.0.0.0"
port: int = 7500
# Add your API keys and credentials here
api_key: str
other_secret: str = "" # Optional, has default empty value
# Configure settings to load from .env file
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False
)
# Create a settings instance for importing in other modules
settings = Settings()
Server Implementation (src/server.py)
from mcp.server.fastmcp import FastMCP
def create_mcp_server():
"""
Create and configure an MCP server instance using FastMCP.
Returns:
FastMCP: A configured FastMCP server instance
"""
# Create a FastMCP server instance with a unique name
mcp = FastMCP("basic-mcp-server")
# Add an example tool using the simpler decorator syntax
@mcp.tool()
def example(input: str) -> str:
"""An example tool that processes input text"""
return f"Processed: {input}"
# You can add more tools, resources, and prompts here
return mcp
Main Entry Point (src/main.py)
import logging
import sys
from .config import settings
from .server import create_mcp_server
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[logging.StreamHandler(sys.stdout)]
)
logger = logging.getLogger(__name__)
def start():
"""Start the MCP server using FastMCP's built-in functionality."""
logger.info(f"Starting MCP server on {settings.host}:{settings.port}")
# Create the MCP server
mcp_server = create_mcp_server()
# Configure server settings
mcp_server.settings.host = settings.host
mcp_server.settings.port = settings.port
mcp_server.settings.debug = True
mcp_server.settings.log_level = "INFO"
# Run the server with SSE transport
mcp_server.run("sse")
# Alternative approach using the FastMCP ASGI application
def create_app():
"""Create an ASGI application for use with an external ASGI server."""
mcp_server = create_mcp_server()
mcp_server.settings.debug = True
return mcp_server.sse_app()
if __name__ == "__main__":
start()
Docker Implementation
We've separated Docker containerization concerns from application functionality to improve maintainability and follow best practices:
Directory Structure
docker/
├── Dockerfile # Optimized multi-stage Dockerfile
├── .dockerignore # Files excluded from build context
└── scripts/ # Container helper scripts
├── entrypoint.sh # Container startup script
└── run.sh # Script to replace docker-compose functionality
Multi-stage Dockerfile
The Docker implementation uses a multi-stage build approach with Python 3.13:
# Stage 1: Builder
FROM python:3.13-slim AS builder
# Set build-time environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# Create app directory
WORKDIR /build
# Copy only what's needed for installation
COPY pyproject.toml README.md ./
# Install build dependencies
RUN apt-get update \
&& apt-get install -y --no-install-recommends gcc python3-dev \
&& pip install --upgrade pip \
&& pip install build wheel \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# Build wheel package
RUN pip wheel --no-deps --wheel-dir /wheels -e .
# Stage 2: Runtime
FROM python:3.13-slim AS runtime
# Set runtime environment variables
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
# Set working directory
WORKDIR /app
# Copy wheel from builder stage
COPY --from=builder /wheels /wheels
# Copy application code
COPY ./src ./src
# Install dependencies and app
RUN pip install --no-cache-dir --upgrade pip \
&& pip install --no-cache-dir /wheels/* \
&& rm -rf /wheels
# Create non-root user for security
RUN adduser --disabled-password --gecos "" appuser \
&& chown -R appuser:appuser /app
USER appuser
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import http.client; conn = http.client.HTTPConnection('localhost:7501'); conn.request('GET', '/health'); response = conn.getresponse(); exit(0 if response.status == 200 else 1)"
# Expose the port
EXPOSE 7501
# Command to run the application
CMD ["python", "-m", "src.main"]
Helper Scripts Instead of Docker Compose
For a single-container application, we've replaced Docker Compose with helper scripts that provide similar functionality with less complexity:
# Build the Docker image
./docker/scripts/run.sh build
# Run the server
./docker/scripts/run.sh run
# Run with hot reload for development
./docker/scripts/run.sh dev
# Run tests in container
./docker/scripts/run.sh test
# Clean up resources
./docker/scripts/run.sh clean
For more details, see docker/README.md.
Getting Started
Setup
- Clone this repository
- Copy
.env.exampleto.envand update with your credentials - Install dependencies:
pip install uv uv pip install -e .
Run Locally
python -m src.main
Build and Run with Docker
# Build the Docker image
./docker/scripts/run.sh build
# Run the server
./docker/scripts/run.sh run
For development with hot-reloading:
./docker/scripts/run.sh dev
Other useful commands:
# Run tests in container
./docker/scripts/run.sh test
# Clean up resources
./docker/scripts/run.sh clean
Testing Your MCP Server
Manual Testing
Using the MCP SDK CLI
If you have the MCP SDK CLI installed, you can use it to test your server:
mcp dev http://localhost:7500/sse
Using Claude
To test with Claude:
-
Update Claude's MCP settings file with:
{ "mcpServers": { "basic-mcp-server": { "url": "http://localhost:7500/sse", "disabled": false, "autoApprove": ["example"] } } } -
In Claude, use the tool:
<use_mcp_tool> <server_name>basic-mcp-server</server_name> <tool_name>example</tool_name> <arguments> { "input": "Hello MCP World!" } </arguments> </use_mcp_tool> -
You should receive: "Processed: Hello MCP World!"
Using HTTP Requests
You can also test your server with HTTP requests:
-
Connect to the SSE endpoint:
curl -N http://localhost:7500/sse -
In another terminal, send a message to the server:
curl -X POST -H "Content-Type: application/json" -d '{"type":"initialize","requestId":"test-123","content":{"clientInfo":{"clientType":"test","clientVersion":"1.0.0"},"capabilities":{"receiveText":true,"receiveImage":false}}}' http://localhost:7500/messages/?session_id=test-session-id
Automated End-to-End Testing
We provide automated tests that verify the MCP server works correctly when deployed as a container:
-
Install test dependencies:
cd tests/e2e pip install -r requirements.txt -
Run the test:
python mcp_container_test.py
The test script will:
- Build and start a Docker container with the MCP server
- Test HTTP connectivity to the server
- Test the MCP protocol (initialization, tool listing)
- Test the example tool functionality
- Clean up all resources when done
See tests/e2e/README.md for more details on the end-to-end tests.
Troubleshooting
If you encounter issues:
-
Connection refused:
- Ensure the container is running:
docker ps | grep mcp-server - Verify port mapping:
docker port [container-id]
- Ensure the container is running:
-
"Not connected" error in Claude:
- Ensure the URL includes the
/ssepath:http://localhost:7500/sse - Check container logs:
docker logs [container-id] - Try restarting the VSCode window
- Ensure the URL includes the
-
Timeout or no response:
- Examine server logs for errors
- Check if another service is using port 7500
Reference Implementation
This project is based on the Model Context Protocol (MCP) Python SDK. For more details on the MCP specification and SDK, refer to the original project README.
FastMCP vs Low-Level Server
This project has been migrated from using the low-level Server class to using the more ergonomic FastMCP approach. For details on the differences and benefits, see readme_fastmcp.md.
Next Steps
- Add more tools to make your MCP server useful
- Implement authentication for the HTTP endpoints
- Add monitoring and logging
- Deploy to your preferred cloud provider
This implementation provides a minimal but production-ready MCP server. You can extend it by adding more tools, resources, or custom functionality as needed.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
MCP Package Docs Server
Facilitates LLMs to efficiently access and fetch structured documentation for packages in Go, Python, and NPM, enhancing software development with multi-language support and performance optimization.
Claude Code MCP
An implementation of Claude Code as a Model Context Protocol server that enables using Claude's software engineering capabilities (code generation, editing, reviewing, and file operations) through the standardized MCP interface.
@kazuph/mcp-taskmanager
Model Context Protocol server for Task Management. This allows Claude Desktop (or any MCP client) to manage and execute tasks in a queue-based system.
Linear MCP Server
Enables interaction with Linear's API for managing issues, teams, and projects programmatically through the Model Context Protocol.
mermaid-mcp-server
A Model Context Protocol (MCP) server that converts Mermaid diagrams to PNG images.
Jira-Context-MCP
MCP server to provide Jira Tickets information to AI coding agents like Cursor
Linear MCP Server
A Model Context Protocol server that integrates with Linear's issue tracking system, allowing LLMs to create, update, search, and comment on Linear issues through natural language interactions.
Sequential Thinking MCP Server
This server facilitates structured problem-solving by breaking down complex issues into sequential steps, supporting revisions, and enabling multiple solution paths through full MCP integration.