mcp server
mongodb-mcp-server
A FastMCP-powered MongoDB MCP server written in Python. This repository exposes MongoDB operations as MCP tools, allowing MCP-compatible agents and clients to connect to MongoDB, inspect databases and
Set up
Runs on your computer. Pick your agent and operating system.
This project does not publish an install method we can turn into a config. Follow the README instead.
README
synced Oct 23, 2026MongoDB-FastMCP-Server
A Python-based MongoDB MCP server built with FastMCP.
This repository exposes MongoDB operations as MCP tools, allowing MCP-compatible clients and AI agents to connect to MongoDB, inspect databases and collections, and execute controlled database operations through a clean tool interface.
Overview
mongodb-mcp-server is designed to provide a structured bridge between MongoDB and the Model Context Protocol.
Instead of letting an agent interact with MongoDB directly, this server exposes selected database capabilities as well-defined MCP tools. This makes database access easier to control, validate, extend, and maintain.
Features
- Connect to MongoDB from a Python MCP server
- Expose MongoDB operations as FastMCP tools
- List databases and collections
- Inspect collection metadata (schema, indexes, storage size, stats, logs)
- Run controlled queries (find, aggregate, count) against collections
- Full CRUD support (insert, update, delete)
- Destructive operation confirmation (2-step pattern)
- Read-only mode support
- Separate MongoDB client logic from MCP tool definitions
- Designed for agentic workflows and MCP-compatible clients
Project Structure
mongodb-mcp-server/
├── pyproject.toml
├── pytest.ini
├── README.md
├── .env
├── src/
│ └── mongodb_mcp/
│ ├── __init__.py
│ ├── server.py # FastMCP instance, tool registration
│ ├── configs.py # Config via pydantic-settings (env vars)
│ │
│ ├── clients/
│ │ ├── __init__.py
│ │ └── mongodb/ # MongoDB client (pymongo)
│ │ ├── __init__.py # MongoDBClient (mixin composition)
│ │ ├── base.py # BaseMongoClient (connection, ping, close)
│ │ ├── metadata.py # list_databases, list_collections, indexes, stats, explain, logs
│ │ ├── read.py # find, aggregate, aggregate_db, count_documents
│ │ ├── create.py # insert_one, insert_many, create_collection, create_index
│ │ ├── update.py # update_one, update_many, rename_collection
│ │ └── delete.py # delete_one, delete_many, drop_collection, drop_database, drop_index
│ │
│ ├── services/
│ │ ├── __init__.py
│ │ ├── connection_manager.py # Connection state machine (singleton)
│ │ └── mongodb/ # MongoDB service layer (mixin-based)
│ │ ├── __init__.py # MongoDBService (composed) + singleton
│ │ ├── base.py # BaseMongoDBService (ensure_connected, resolve_name, validation)
│ │ ├── metadata.py # MetadataService (list, schema, indexes, stats, explain, logs)
│ │ ├── read.py # ReadService (find, aggregate, count_documents, distinct)
│ │ ├── create.py # CreateService (insert, create_collection, create_index)
│ │ ├── update.py # UpdateService (update_one, update_many, rename_collection)
│ │ └── delete.py # DeleteService (delete_one, delete_many, drop_collection, drop_database, drop_index)
│ │
│ └── tools/ # Tool definitions (thin layer, delegates to services)
│ ├── __init__.py # Logging setup
│ ├── connection.py # connect, disconnect
│ └── mongodb/ # MongoDB tools package
│ ├── __init__.py # Re-exports all tools + side-effect imports
│ ├── _utils.py # _format_not_found helper
│ ├── metadata.py # list_databases, list_collections, collection_schema, collection_indexes, db_stats, explain_query, get_logs, collection_stats
│ ├── create.py # insert_one, insert_many, create_collection, create_index
│ ├── read.py # find, aggregate, count_documents, distinct
│ ├── update.py # update_one, update_many, rename_collection
│ └── delete.py # delete_one, delete_many, drop_collection, drop_database, drop_index
│
└── test/
├── conftest.py # Shared fixtures, env setup
└── units/
├── services/
│ ├── test_base_service.py
│ ├── test_connection_manager.py
│ ├── test_metadata_service.py
│ ├── test_create_service.py
│ ├── test_read_service.py
│ ├── test_update_service.py
│ └── test_delete_service.py
└── tools/
├── test_metadata_tools.py
├── test_create_tools.py
├── test_read_tools.py
├── test_update_tools.py
└── test_delete_tools.py
Architecture
┌─────────────────────────────────────────────────────────┐
│ Tools Layer (tools/) │
│ - Defines MCP tool name, description, schema │
│ - Formats output for LLM consumption │
│ - Try/except top-level, returns user-friendly messages │
├─────────────────────────────────────────────────────────┤
│ Services Layer (services/mongodb/) │
│ - Auto-connect (ensure_connected) │
│ - Input validation (_validate_name) │
│ - Lazy name resolution with fuzzy suggestions │
│ - Read-only mode enforcement │
│ - Error classification and logging │
├─────────────────────────────────────────────────────────┤
│ Clients Layer (clients/mongodb/) │
│ - Pure pymongo async calls │
│ - No business logic, no error handling │
│ - Translates Python params → pymongo operations │
└─────────────────────────────────────────────────────────┘
│
▼
MongoDB (via AsyncMongoClient)
Key Design Decisions
- Lazy Name Resolution: Tools call MongoDB directly first. Only when results are empty/error does the service layer resolve names and suggest alternatives. Zero overhead on happy path.
- Mixin Composition: Both client and service layers use the same pattern — base class + mixins → composed class. Easy to extend with new operation types.
- Singleton Services:
connection_managerandmongodb_serviceare module-level singletons shared across all tools. - Structured Logging: Every layer uses Python
loggingwith prefix tags ([connection],[resolve],[tool:name]) for easy grep-based debugging.
Implementation Plan
| Phase | Status | Description |
|---|---|---|
| 1 | ✅ Done | Project setup: pyproject.toml, FastMCP server skeleton, config |
| 2 | ✅ Done | Client layer: MongoDBClient (mixin-based) + ConnectionManager |
| 3 | ✅ Done | Service layer + Metadata tools: list-databases, list-collections, collection-schema, db-stats, collection-indexes, collection-stats, explain, logs |
| 4 | ✅ Done | CRUD tools: ✅ create (insert, create_collection, create_index) · ✅ read (find, aggregate, count_documents, distinct) · ✅ update (update_one, update_many, rename_collection) · ✅ delete (delete_one, delete_many, drop_collection, drop_database, drop_index) |
Quick Start
# Install in editable mode
pip install -e ".[dev]"
# Set connection string
export MONGODB_CONNECTION_STRING="mongodb://localhost:27017"
# Run server (stdio transport)
fastmcp run src/mongodb_mcp/server.py:mcp
# Or use FastMCP dev UI
fastmcp dev src/mongodb_mcp/server.py:mcp
MCP Inspector
MCP Inspector is a browser-based tool for interactively testing MCP servers and their tools.
# Run MCP Inspector against this server (npx, no install required)
npx @modelcontextprotocol/inspector fastmcp run src/mongodb_mcp/server.py:mcp
Then open http://localhost:6274 in your browser. From there you can:
- Browse all registered tools and their input schemas
- Call tools manually and inspect structured responses
- Debug tool outputs without needing a full MCP client
Note: Set
MONGODB_CONNECTION_STRINGin your environment or.envfile before running.
Install Node.js (required for npx)
macOS
brew install node
Ubuntu / Debian
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
Windows
Download and run the installer from nodejs.org.
Verify installation:
node --version # should be ≥ 18
npx --version
Testing
# Install test dependencies
pip install -e ".[dev]"
# Run all unit tests
python -m pytest test/units/ -v
# Run specific test file
python -m pytest test/units/services/test_base_service.py -v
# Run with short traceback
python -m pytest test/units/ --tb=short
# Run with coverage (if pytest-cov installed)
python -m pytest test/units/ --cov=mongodb_mcp --cov-report=term-missing
Note: Tests mock all MongoDB interactions — no running MongoDB instance required for unit tests.
Configuration
Environment variables (or .env file):
| Variable | Default | Description |
|---|---|---|
MONGODB_CONNECTION_STRING | (required) | MongoDB connection URI |
READ_ONLY | true | Only allow read/metadata operations |
ALLOW_DESTRUCTIVE | false | Allow destructive operations (delete, drop). Requires READ_ONLY=false |
DEFAULT_TIMEOUT_MS | 30000 | Default timeout for MongoDB operations |
WRITE_ALLOWLIST | (unset) | Comma-separated db.collection patterns allowed for writes (see below) |
Write Allowlist
When READ_ONLY=false, you can further restrict which databases/collections accept writes:
# Allow specific targets
WRITE_ALLOWLIST=mydb.users,mydb.orders,testdb.*
# Allow all collections in a database
WRITE_ALLOWLIST=prod.*
# Explicit allow-all
WRITE_ALLOWLIST=*
# Not set or empty → allow all (backward compatible)
| Pattern | Meaning |
|---|---|
db.col | Exact match only |
db.* | All collections in that database |
* | Allow all writes |
| (empty/unset) | Allow all writes (backward compatible) |
Security Model
Write operations go through 3 policy checks:
_check_write_allowed() → READ_ONLY=false?
_check_destructive_allowed() → ALLOW_DESTRUCTIVE=true? (delete/drop only)
_check_write_target() → WRITE_ALLOWLIST match?
READ_ONLY | ALLOW_DESTRUCTIVE | Allowed operations |
|---|---|---|
true (default) | (ignored) | Read + metadata only |
false | false (default) | insert, update, create_index, create_collection, rename_collection |
false | true | All of above + delete_one, delete_many, drop_collection, drop_database, drop_index |
rename_collectionwithdrop_target=truealso requiresALLOW_DESTRUCTIVE=truesince it destroys the target collection.
Tech Stack
- FastMCP — MCP server framework (handles transport, protocol, tool registration)
- PyMongo — MongoDB driver (
AsyncMongoClient) - Pydantic Settings — Configuration management from env vars
- pytest + pytest-asyncio — Async-aware testing framework
Available Tools
| Tool | Description | Params | Resolves names? |
|---|---|---|---|
connect | Connect to a MongoDB instance | connection_string — MongoDB URI. Default: from env | — |
disconnect | Disconnect from MongoDB | (none) | — |
list_databases | List all databases with size | (none) | No |
list_collections | List collections in a database | database — Database name | No |
collection_schema | Sample documents to infer schema | database — Database namecollection — Collection namesample_size — Number of docs to sample. Default: 20 | Yes (lazy) |
collection_indexes | List indexes for a collection | database — Database namecollection — Collection name | Yes (lazy) |
db_stats | Get database statistics | database — Database name | Yes (lazy) |
explain_query | Explain a query plan (find/aggregate/count) | database — Database namecollection — Collection namemethod — One of: find, aggregate, countargs — JSON string of method argumentsverbosity — Detail level. Default: "queryPlanner" | Yes (lazy) |
get_logs | Get MongoDB server log entries | log_type — Log category. Default: "global"limit — Max lines to return. Default: 50 | No |
insert_one | Insert a single document | database — Database namecollection — Collection namedocument — JSON string of document | No |
insert_many | Insert multiple documents | database — Database namecollection — Collection namedocuments — JSON array of documentsordered — Ordered insert. Default: false | No |
create_collection | Create a new collection | database — Database namecollection — Collection name | No |
create_index | Create an index on a collection | database — Database namecollection — Collection namekeys — JSON array of [field, direction] pairsunique — Unique index. Default: falsename — Custom index name (optional) | Yes (lazy) |
find | Query documents from a collection | database — Database namecollection — Collection namefilter — JSON filter. Default: "{}"projection — JSON projection (optional)sort — JSON array of [field, direction] pairs (optional)skip — Documents to skip. Default: 0limit — Max documents. Default: 10 | Yes (lazy) |
aggregate | Run an aggregation pipeline | database — Database namecollection — Collection namepipeline — JSON array of stage objectslimit — Max results. Default: 1000 | Yes (lazy) |
count_documents | Count documents matching a filter | database — Database namecollection — Collection namefilter — JSON filter. Default: "{}" | Yes (lazy) |
distinct | Get distinct values of a field | database — Database namecollection — Collection namefield — Field namefilter — JSON filter. Default: "{}" | Yes (lazy) |
collection_stats | Get storage statistics for a collection | database — Database namecollection — Collection name | Yes (lazy) |
update_one | Update a single document matching a filter | database — Database namecollection — Collection namefilter — JSON filter stringupdate — JSON update with operators ($set, $inc, etc.)upsert — Insert if no match. Default: false | Yes (lazy) |
update_many | Update all documents matching a filter | database — Database namecollection — Collection namefilter — JSON filter stringupdate — JSON update with operators ($set, $inc, etc.)upsert — Insert if no match. Default: false | Yes (lazy) |
rename_collection | Rename a collection | database — Database namecollection — Current collection namenew_name — New collection namedrop_target — Overwrite if target exists. Default: false | Yes (lazy) |
delete_one | Delete a single document matching a filter | database — Database namecollection — Collection namefilter — JSON filter string | Yes (lazy) |
delete_many | Delete all documents matching a filter | database — Database namecollection — Collection namefilter — JSON filter string. Use '{}' to delete all | Yes (lazy) |
drop_collection | Drop a collection and all its data (irreversible) | database — Database namecollection — Collection name | Yes (lazy) |
drop_database | Drop an entire database (irreversible) | database — Database name | Yes (lazy) |
drop_index | Drop an index by name | database — Database namecollection — Collection nameindex_name — Index name (use collection_indexes to find names) | Yes (lazy) |
Lazy resolve: On happy path (correct name, data exists), no extra queries. Only when results are empty/error does the service resolve names and suggest fuzzy alternatives for user confirmation.
Write tools require
READ_ONLY=falsein configuration.
License
Apache-2.0