Reusable Docker Compose deployment for two read-only MCP servers over Streamable HTTP:
- Filesystem MCP for directory browsing, file metadata, and text or media reads.
- DuckDB MCP for read-only SQL over files such as CSV, JSON, and Parquet.
Both services mount a configurable host data directory as read-only at /data inside the containers.
mcp-servers/
├── compose.yaml
├── Dockerfile.filesystem
├── Dockerfile.duckdb
├── scripts/
│ └── start-duckdb-mcp.sh
├── .env.example
├── .gitignore
├── .dockerignore
└── README.md
| Service | Endpoint | Purpose |
|---|---|---|
filesystem |
http://SERVER_IP:8000/mcp |
Browse directories and read files |
duckdb |
http://SERVER_IP:8001/mcp |
Query structured data with read-only SQL |
The host ports are configured through .env and listen on all host interfaces. This configuration is intended for a trusted private network. Add authentication and TLS before exposing it to an untrusted network.
cp .env.example .env
docker compose config
docker compose build
docker compose up -d
docker compose psDefault .env values:
HOST_DATA_PATH=/data
FILESYSTEM_MCP_PORT=8000
DUCKDB_MCP_PORT=8001Set HOST_DATA_PATH to the host directory that contains the shared data. Both containers always see that directory as /data. Change the port values when the defaults are already in use; the container-side MCP port remains 8000 for both services.
To start only one service:
docker compose up -d filesystem
docker compose up -d duckdbReplace SERVER_IP with the server address:
codex mcp add server-filesystem --url http://SERVER_IP:8000/mcp
codex mcp add server-duckdb --url http://SERVER_IP:8001/mcpConfirm the entries:
codex mcp list/datais mounted read-only in both containers.- Linux capabilities are dropped.
no-new-privilegesis enabled.- DuckDB external access, persistent secrets, export, and execute tools are disabled.
- The deployment does not provide authentication or TLS by default.