# Directory Structure
```
├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│ ├── index.ts
│ └── mongodb
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Dependencies
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# TypeScript
dist/
build/
# Environment variables
.env
.env.local
.env.*.local
# IDE
.idea/
.vscode/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Logs
logs/
*.log
# Coverage
coverage/
# Temporary files
tmp/
temp/
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# MCP Database Server
A Model Context Protocol (MCP) server implementation that allows Large Language Models (LLMs) to interact with various databases through natural language. Currently supports MongoDB, with plans to support:
- PostgreSQL
- CockroachDB
- Redis
- And more...
## Features
- Database operations through natural language
- Currently supports MongoDB with features:
- List all collections
- Query documents with filtering and projection
- Insert documents
- Delete documents
- Aggregate pipeline operations
- Future support for other databases:
- PostgreSQL: SQL queries, table operations
- CockroachDB: Distributed SQL operations
- Redis: Key-value operations, caching
## Prerequisites
- Node.js v20.12.2 or higher
- Database (currently MongoDB, other databases coming soon)
- Claude Desktop Application
## Installation
1. Clone the repository:
```bash
git clone https://github.com/manpreet2000/mcp-database-server.git
cd mcp-database-server
```
2. Install dependencies:
```bash
npm install
```
3. Build the TypeScript code:
```bash
npm run build
```
## Configuration
To get started, you need to configure your database connection in your Claude Desktop configuration file:
### MacOS
```bash
~/Library/Application\ Support/Claude/claude_desktop_config.json
```
### Windows
```bash
%APPDATA%/Claude/claude_desktop_config.json
```
Add the following configuration to your `claude_desktop_config.json`:
```json
{
"mcpServers": {
"database": {
"command": "/path/to/node",
"args": ["/path/to/mcp-database/dist/index.js"],
"env": {
"MONGODB_URI": "your-mongodb-connection-string"
}
}
}
}
```
Replace:
- `/path/to/node` with your Node.js executable path or just use `node`
- `/path/to/mcp-database` with the absolute path to this repository
- `your-mongodb-connection-string` with your MongoDB connection URL
## Usage Examples
### MongoDB Examples
1. List all collections in your database:
```
Can you show me all the collections in my database?
```
2. Get specific records from a collection:
```
Give me 2 records from the chargers collection
```
3. Query with filters:
```
Show me all documents in the users collection where status is active
```
4. Insert a document:
```
Add a new user to the users collection with name John and email [email protected]
```
5. Delete a document:
```
Remove the user with email [email protected] from the users collection
```
6. Aggregate data:
```
Show me the total count of users by status in the users collection
```
## Available Tools
### 1. getCollections
Lists all collections in the connected database.
### 2. getCollection
Retrieves documents from a collection with optional query parameters:
- `collectionName`: Name of the collection
- `limit`: Maximum number of documents to return (default: 10, max: 1000)
- `query`: MongoDB query object
- `projection`: Fields to include/exclude
### 3. insertOne
Inserts a single document into a collection:
- `collectionName`: Name of the collection
- `document`: Document object to insert
### 4. deleteOne
Deletes a single document from a collection:
- `collectionName`: Name of the collection
- `query`: Query to match the document to delete
### 5. aggregate
Executes an aggregation pipeline:
- `collectionName`: Name of the collection
- `pipeline`: Array of aggregation stages
- `options`: Optional aggregation options
## Future Database Support
### PostgreSQL
- SQL query execution
- Table operations
- Schema management
- Transaction support
### CockroachDB
- Distributed SQL operations
- Multi-region support
- Transaction management
- Schema operations
### Redis
- Key-value operations
- Caching mechanisms
- Pub/sub operations
- Data structure operations
## Security
- Never commit your database connection strings to version control
- Use environment variables for sensitive information
- Follow database-specific security best practices
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
## License
MIT License - See [LICENSE](LICENSE) for details
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/src/mongodb/index.ts:
--------------------------------------------------------------------------------
```typescript
import { MongoClient, Db } from "mongodb";
export class MongoDBConnection {
private client: MongoClient | null = null;
private db: Db | null = null;
async connect(databaseUrl: string) {
try {
this.client = new MongoClient(databaseUrl);
await this.client.connect();
this.db = this.client.db();
return this.db;
} catch (error) {
console.error("MongoDB connection error:", error);
throw error;
}
}
async close() {
await this.client?.close();
}
getClient(): MongoClient | null {
return this.client;
}
getDb(): Db | null {
return this.db;
}
}
export const mongodbConnection = new MongoDBConnection();
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "mcp-database",
"version": "1.0.0",
"description": "MCP Server for database operations",
"main": "index.js",
"bin": {
"database": "./dist/index.js"
},
"files": [
"dist"
],
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
"prepare": "npm run build",
"start": "node dist/index.js",
"dev": "ts-node src/index.ts",
"watch": "tsc --watch",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"mcp",
"mongodb",
"database"
],
"author": "",
"license": "ISC",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.4.0",
"dotenv": "^16.4.5",
"mongodb": "^6.15.0",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/node": "^20.11.24",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
}
}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import * as dotenv from "dotenv";
import { z } from "zod";
import { mongodbConnection } from "./mongodb/index.js";
dotenv.config();
class DatabaseMCPServer {
private mcpServer: McpServer;
private readonly MONGODB_URI: string;
constructor() {
const uri = process.env.MONGODB_URI;
if (!uri) {
throw new Error("MONGODB_URI environment variable is required");
}
this.MONGODB_URI = uri;
this.mcpServer = new McpServer({
name: "database-mcp",
version: "1.0.0",
});
this.initializeTools();
}
private initializeTools() {
this.mcpServer.tool("getCollections", async () => {
try {
let db = mongodbConnection.getDb();
if (!db) {
await mongodbConnection.connect(this.MONGODB_URI);
db = mongodbConnection.getDb();
if (!db) throw new Error("Failed to connect to database");
}
const collections = await db.listCollections().toArray();
return {
content: [
{ type: "text", text: collections.map((c) => c.name).join(", ") },
],
};
} catch (error) {
console.error(error);
return {
content: [{ type: "text", text: "Error: " + error }],
};
}
});
this.mcpServer.tool(
"getCollection",
{
collectionName: z.string(),
limit: z.number().min(1).max(1000).optional().default(10),
query: z.object({}).optional(),
projection: z.object({}).optional(),
},
async ({
collectionName,
limit,
query,
projection,
}: {
collectionName: string;
limit?: number;
query?: any;
projection?: any;
}) => {
try {
let db = mongodbConnection.getDb();
if (!db) {
await mongodbConnection.connect(this.MONGODB_URI);
db = mongodbConnection.getDb();
if (!db) throw new Error("Failed to connect to database");
}
const collection = db.collection(collectionName);
const documents = await collection
.find(query ?? {})
.limit(limit ?? 100)
.project(projection ?? {})
.toArray();
return {
content: [
{
type: "text",
text: documents.map((d) => JSON.stringify(d)).join("\n"),
},
],
};
} catch (error) {
console.error(error);
return {
content: [{ type: "text", text: "Error: " + error }],
};
}
}
);
this.mcpServer.tool(
"insertOne",
{
collectionName: z.string(),
document: z.object({}),
},
async ({ collectionName, document }) => {
try {
let db = mongodbConnection.getDb();
if (!db) {
await mongodbConnection.connect(this.MONGODB_URI);
db = mongodbConnection.getDb();
if (!db) throw new Error("Failed to connect to database");
}
const collection = db.collection(collectionName);
await collection.insertOne(document);
return {
content: [{ type: "text", text: "Document inserted successfully" }],
};
} catch (error) {
console.error(error);
return {
content: [{ type: "text", text: "Error: " + error }],
};
}
}
);
this.mcpServer.tool(
"deleteOne",
{
collectionName: z.string(),
query: z.object({}),
},
async ({ collectionName, query }) => {
try {
let db = mongodbConnection.getDb();
if (!db) {
await mongodbConnection.connect(this.MONGODB_URI);
db = mongodbConnection.getDb();
if (!db) throw new Error("Failed to connect to database");
}
const collection = db.collection(collectionName);
await collection.deleteOne(query);
return {
content: [{ type: "text", text: "Document deleted successfully" }],
};
} catch (error) {
console.error(error);
return {
content: [{ type: "text", text: "Error: " + error }],
};
}
}
);
this.mcpServer.tool(
"aggregate",
{
collectionName: z.string(),
pipeline: z.array(
z
.object({
$match: z.record(z.any()).optional(),
$group: z.record(z.any()).optional(),
$project: z.record(z.any()).optional(),
$sort: z.record(z.any()).optional(),
$limit: z.number().optional(),
$skip: z.number().optional(),
$unwind: z.string().optional(),
$lookup: z
.object({
from: z.string(),
localField: z.string(),
foreignField: z.string(),
as: z.string(),
})
.optional(),
$count: z.string().optional(),
$addFields: z.record(z.any()).optional(),
$replaceRoot: z.record(z.any()).optional(),
$facet: z.record(z.any()).optional(),
$bucket: z.record(z.any()).optional(),
$geoNear: z.record(z.any()).optional(),
$indexStats: z.record(z.any()).optional(),
$listLocalSessions: z.record(z.any()).optional(),
$listSessions: z.record(z.any()).optional(),
$merge: z.record(z.any()).optional(),
$out: z.string().optional(),
$planCacheStats: z.record(z.any()).optional(),
$redact: z.record(z.any()).optional(),
$replaceWith: z.record(z.any()).optional(),
$sample: z.object({ size: z.number() }).optional(),
$search: z.record(z.any()).optional(),
$searchMeta: z.record(z.any()).optional(),
$set: z.record(z.any()).optional(),
$setWindowFields: z.record(z.any()).optional(),
$unionWith: z.record(z.any()).optional(),
$unset: z.string().optional(),
})
.refine(
(obj) => {
// Count the number of defined fields
const definedFields = Object.keys(obj).filter(
(key) => obj[key as keyof typeof obj] !== undefined
);
return definedFields.length === 1;
},
{
message: "Each pipeline stage must contain exactly one field",
}
)
),
options: z.object({}).optional(),
},
async ({ collectionName, pipeline, options }) => {
try {
let db = mongodbConnection.getDb();
if (!db) {
await mongodbConnection.connect(this.MONGODB_URI);
db = mongodbConnection.getDb();
if (!db) throw new Error("Failed to connect to database");
}
const collection = db.collection(collectionName);
const result = await collection
.aggregate(pipeline, { maxTimeMS: 30000, ...options })
.toArray();
return {
content: [
{
type: "text",
text: JSON.stringify(result),
},
],
};
} catch (error) {
console.error(error);
return {
content: [{ type: "text", text: "Error: " + error }],
};
}
}
);
}
async connect(transport: StdioServerTransport) {
await this.mcpServer.connect(transport);
}
async close() {
await mongodbConnection.close();
}
}
async function main() {
const server = new DatabaseMCPServer();
const transport = new StdioServerTransport();
await server.connect(transport);
console.log("Database MCP Server running on stdio");
}
process.on("SIGINT", async () => {
try {
console.error("SIGINT received");
await mongodbConnection.close();
} finally {
process.exit(0);
}
});
process.on("unhandledRejection", (error) => {
console.error("Unhandled promise rejection:", error);
process.exit(1);
});
main().catch((error) => {
console.error("Fatal error in main():", error);
process.exit(1);
});
```