Production-grade MCP server for safe MongoDB inspection. It lets AI agents list collections, infer schemas from live documents, and run strictly read-only queries with bounded output.
Most agents need database context, but raw database access is too dangerous. This server narrows the surface area:
list_collectionsgives discovery without mutation.get_schemainfers structure from real sample data.run_safe_queryexposes a small, allowlisted read API.
The implementation is intentionally educational. The code uses strict TypeScript, runtime validation, recursive schema inference, cache-aware reads, and MCP tool wiring that is ready for real clients.
src/
├── index.ts
├── config/
│ └── env.ts
├── constants/
│ └── allowedOperations.ts
├── db/
│ └── mongo.ts
├── tools/
│ ├── getSchema.ts
│ ├── listCollections.ts
│ └── runSafeQuery.ts
├── types/
│ └── schema.ts
└── utils/
├── safeOperations.ts
├── schemaInferer.ts
├── typeHelpers.ts
└── validators.ts
scripts/
└── test-mcp-server.ts
This codebase intentionally demonstrates advanced TypeScript patterns in production context:
- interfaces for public response contracts
- literal union types for read-only operation allowlists
- branded types for validated collection names
- discriminated control flow by operation type
- generics for reusable schema/query helpers
- recursive types for schema trees and
ReadonlyDeep - conditional types through
ArrayElement<T> - type guards for
ObjectId,Date, and plain objects - strict null checks with
exactOptionalPropertyTypes
The compiler is configured with:
strict: truenoImplicitAny: trueexactOptionalPropertyTypes: truenoUncheckedIndexedAccess: true
- Install dependencies:
npm install- Copy environment variables:
cp .env.example .env- Fill in:
MONGODB_URI=mongodb://127.0.0.1:27017
DATABASE_NAME=sample_mcp
PORT=3001
SCHEMA_SAMPLE_SIZE=10
SAFE_QUERY_MAX_LIMIT=50
ENABLE_SCHEMA_CACHE=true
SCHEMA_CACHE_TTL_MS=300000
LOG_LEVEL=info- Run in development:
npm run mcp:dev- Build for production:
npm run mcp:build
npm run mcp:startInput:
{
"includeSample": true
}Response shape:
{
"collections": [
{
"name": "users",
"count": 123,
"sampleDocument": {
"_id": "66465e4db15c0708d8f13c20",
"name": "Dhruv"
}
}
]
}Input:
{
"collection": "users",
"sampleSize": 10,
"refresh": false
}Response includes:
- recursive field tree
- merged union types like
number | string - nullable detection
- array item inference including mixed arrays
- collection stats
- index summaries
- generated TypeScript interface
- generated Mongoose schema
- cache metadata and confidence scores
Allowed operations:
findfindOneaggregatecountDocuments
Blocked operations:
deleteManydeleteOneupdateManyupdateOnereplaceOneinsertOnedropcreateIndexbulkWritefindOneAndDeletefindOneAndUpdate
Example:
{
"operation": "find",
"collection": "users",
"query": {
"age": {
"$gt": 18
}
},
"limit": 10,
"sort": {
"age": -1
}
}The core logic lives in src/utils/schemaInferer.ts.
- Sample real MongoDB documents with a bounded
$sample. - Walk each document recursively.
- Infer each runtime value via
inferFieldType(value). - Merge observations into a tree of schema nodes.
- Mark fields as nullable when they are missing or explicitly
null. - Collapse inconsistent observations into unions such as
string | number. - Export the final structure as:
- machine-readable JSON
- TypeScript interface
- Mongoose schema draft
Type detection rules:
Dateinstances becomedateObjectIdbecomesobjectId- ISO date strings stay
string - arrays are represented as
array<...> - empty arrays default to
array<unknown> - nested objects produce
children
The server is read-only by construction.
- Operations are allowlisted in src/constants/allowedOperations.ts.
- Tool input is validated with Zod before execution.
- Collection names are restricted to safe characters.
- Query payloads are recursively sanitized.
$where,$function,$accumulator, and eval-like strings are rejected.- Aggregation stages are filtered by allowlist.
- Results are hard-limited to at most 50 documents.
- Logs go to
stderr, preservingstdoutfor MCP protocol traffic.
Implemented bonus features:
- schema caching with TTL
- query execution timing
- structured logging middleware
- collection statistics
- index inspection
- schema confidence scoring
- TypeScript export generation
- Mongoose export generation
- CLI smoke test script
- Docker support
Build the server first:
npm run mcp:buildRun the local smoke script:
node dist-mcp/scripts/test-mcp-server.jsOr use MCP Inspector:
npm run mcp:inspectorAdd this server to your Claude Desktop MCP config:
{
"mcpServers": {
"mongo-schema-explainer-mcp": {
"command": "node",
"args": ["/absolute/path/to/mern-mcp-web/dist-mcp/src/index.js"],
"env": {
"MONGODB_URI": "mongodb://127.0.0.1:27017",
"DATABASE_NAME": "sample_mcp",
"SCHEMA_SAMPLE_SIZE": "10",
"SAFE_QUERY_MAX_LIMIT": "50",
"ENABLE_SCHEMA_CACHE": "true",
"SCHEMA_CACHE_TTL_MS": "300000",
"LOG_LEVEL": "info"
}
}
}
}For development, you can point Claude Desktop at tsx instead:
{
"mcpServers": {
"mongo-schema-explainer-mcp": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/mern-mcp-web/src/index.ts"],
"env": {
"MONGODB_URI": "mongodb://127.0.0.1:27017",
"DATABASE_NAME": "sample_mcp"
}
}
}
}Build:
docker build -t mongo-schema-explainer-mcp .Run:
docker run --rm \
-e MONGODB_URI=mongodb://host.docker.internal:27017 \
-e DATABASE_NAME=sample_mcp \
mongo-schema-explainer-mcp- configurable BSON-aware query parsing for extended operators
- richer schema confidence heuristics based on larger samples
- schema diffing between environments
- optional HTTP transport for remote MCP deployment
- RBAC-aware collection allowlists per client