#
tokens: 5059/50000 6/6 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│   ├── index.ts
│   └── mongodb
│       └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | npm-debug.log*
 4 | yarn-debug.log*
 5 | yarn-error.log*
 6 | 
 7 | # TypeScript
 8 | dist/
 9 | build/
10 | 
11 | # Environment variables
12 | .env
13 | .env.local
14 | .env.*.local
15 | 
16 | # IDE
17 | .idea/
18 | .vscode/
19 | *.swp
20 | *.swo
21 | 
22 | # OS
23 | .DS_Store
24 | Thumbs.db
25 | 
26 | # Logs
27 | logs/
28 | *.log
29 | 
30 | # Coverage
31 | coverage/
32 | 
33 | # Temporary files
34 | tmp/
35 | temp/ 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
  1 | # MCP Database Server
  2 | 
  3 | 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:
  4 | 
  5 | - PostgreSQL
  6 | - CockroachDB
  7 | - Redis
  8 | - And more...
  9 | 
 10 | ## Features
 11 | 
 12 | - Database operations through natural language
 13 | - Currently supports MongoDB with features:
 14 |   - List all collections
 15 |   - Query documents with filtering and projection
 16 |   - Insert documents
 17 |   - Delete documents
 18 |   - Aggregate pipeline operations
 19 | - Future support for other databases:
 20 |   - PostgreSQL: SQL queries, table operations
 21 |   - CockroachDB: Distributed SQL operations
 22 |   - Redis: Key-value operations, caching
 23 | 
 24 | ## Prerequisites
 25 | 
 26 | - Node.js v20.12.2 or higher
 27 | - Database (currently MongoDB, other databases coming soon)
 28 | - Claude Desktop Application
 29 | 
 30 | ## Installation
 31 | 
 32 | 1. Clone the repository:
 33 | 
 34 | ```bash
 35 | git clone https://github.com/manpreet2000/mcp-database-server.git
 36 | cd mcp-database-server
 37 | ```
 38 | 
 39 | 2. Install dependencies:
 40 | 
 41 | ```bash
 42 | npm install
 43 | ```
 44 | 
 45 | 3. Build the TypeScript code:
 46 | 
 47 | ```bash
 48 | npm run build
 49 | ```
 50 | 
 51 | ## Configuration
 52 | 
 53 | To get started, you need to configure your database connection in your Claude Desktop configuration file:
 54 | 
 55 | ### MacOS
 56 | 
 57 | ```bash
 58 | ~/Library/Application\ Support/Claude/claude_desktop_config.json
 59 | ```
 60 | 
 61 | ### Windows
 62 | 
 63 | ```bash
 64 | %APPDATA%/Claude/claude_desktop_config.json
 65 | ```
 66 | 
 67 | Add the following configuration to your `claude_desktop_config.json`:
 68 | 
 69 | ```json
 70 | {
 71 |   "mcpServers": {
 72 |     "database": {
 73 |       "command": "/path/to/node",
 74 |       "args": ["/path/to/mcp-database/dist/index.js"],
 75 |       "env": {
 76 |         "MONGODB_URI": "your-mongodb-connection-string"
 77 |       }
 78 |     }
 79 |   }
 80 | }
 81 | ```
 82 | 
 83 | Replace:
 84 | 
 85 | - `/path/to/node` with your Node.js executable path or just use `node`
 86 | - `/path/to/mcp-database` with the absolute path to this repository
 87 | - `your-mongodb-connection-string` with your MongoDB connection URL
 88 | 
 89 | ## Usage Examples
 90 | 
 91 | ### MongoDB Examples
 92 | 
 93 | 1. List all collections in your database:
 94 | 
 95 | ```
 96 | Can you show me all the collections in my database?
 97 | ```
 98 | 
 99 | 2. Get specific records from a collection:
100 | 
101 | ```
102 | Give me 2 records from the chargers collection
103 | ```
104 | 
105 | 3. Query with filters:
106 | 
107 | ```
108 | Show me all documents in the users collection where status is active
109 | ```
110 | 
111 | 4. Insert a document:
112 | 
113 | ```
114 | Add a new user to the users collection with name John and email [email protected]
115 | ```
116 | 
117 | 5. Delete a document:
118 | 
119 | ```
120 | Remove the user with email [email protected] from the users collection
121 | ```
122 | 
123 | 6. Aggregate data:
124 | 
125 | ```
126 | Show me the total count of users by status in the users collection
127 | ```
128 | 
129 | ## Available Tools
130 | 
131 | ### 1. getCollections
132 | 
133 | Lists all collections in the connected database.
134 | 
135 | ### 2. getCollection
136 | 
137 | Retrieves documents from a collection with optional query parameters:
138 | 
139 | - `collectionName`: Name of the collection
140 | - `limit`: Maximum number of documents to return (default: 10, max: 1000)
141 | - `query`: MongoDB query object
142 | - `projection`: Fields to include/exclude
143 | 
144 | ### 3. insertOne
145 | 
146 | Inserts a single document into a collection:
147 | 
148 | - `collectionName`: Name of the collection
149 | - `document`: Document object to insert
150 | 
151 | ### 4. deleteOne
152 | 
153 | Deletes a single document from a collection:
154 | 
155 | - `collectionName`: Name of the collection
156 | - `query`: Query to match the document to delete
157 | 
158 | ### 5. aggregate
159 | 
160 | Executes an aggregation pipeline:
161 | 
162 | - `collectionName`: Name of the collection
163 | - `pipeline`: Array of aggregation stages
164 | - `options`: Optional aggregation options
165 | 
166 | ## Future Database Support
167 | 
168 | ### PostgreSQL
169 | 
170 | - SQL query execution
171 | - Table operations
172 | - Schema management
173 | - Transaction support
174 | 
175 | ### CockroachDB
176 | 
177 | - Distributed SQL operations
178 | - Multi-region support
179 | - Transaction management
180 | - Schema operations
181 | 
182 | ### Redis
183 | 
184 | - Key-value operations
185 | - Caching mechanisms
186 | - Pub/sub operations
187 | - Data structure operations
188 | 
189 | ## Security
190 | 
191 | - Never commit your database connection strings to version control
192 | - Use environment variables for sensitive information
193 | - Follow database-specific security best practices
194 | 
195 | ## Contributing
196 | 
197 | 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.
198 | 
199 | ## License
200 | 
201 | MIT License - See [LICENSE](LICENSE) for details
202 | 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "compilerOptions": {
 3 |     "target": "ES2022",
 4 |     "module": "Node16",
 5 |     "moduleResolution": "Node16",
 6 |     "outDir": "./dist",
 7 |     "rootDir": "./src",
 8 |     "strict": true,
 9 |     "esModuleInterop": true,
10 |     "skipLibCheck": true,
11 |     "forceConsistentCasingInFileNames": true
12 |   },
13 |   "include": ["src/**/*"],
14 |   "exclude": ["node_modules"]
15 | }
```

--------------------------------------------------------------------------------
/src/mongodb/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { MongoClient, Db } from "mongodb";
 2 | 
 3 | export class MongoDBConnection {
 4 |   private client: MongoClient | null = null;
 5 |   private db: Db | null = null;
 6 | 
 7 |   async connect(databaseUrl: string) {
 8 |     try {
 9 |       this.client = new MongoClient(databaseUrl);
10 |       await this.client.connect();
11 |       this.db = this.client.db();
12 |       return this.db;
13 |     } catch (error) {
14 |       console.error("MongoDB connection error:", error);
15 |       throw error;
16 |     }
17 |   }
18 | 
19 |   async close() {
20 |     await this.client?.close();
21 |   }
22 | 
23 |   getClient(): MongoClient | null {
24 |     return this.client;
25 |   }
26 | 
27 |   getDb(): Db | null {
28 |     return this.db;
29 |   }
30 | }
31 | 
32 | export const mongodbConnection = new MongoDBConnection();
33 | 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "mcp-database",
 3 |   "version": "1.0.0",
 4 |   "description": "MCP Server for database operations",
 5 |   "main": "index.js",
 6 |   "bin": {
 7 |     "database": "./dist/index.js"
 8 |   },
 9 |   "files": [
10 |     "dist"
11 |   ],
12 |   "scripts": {
13 |     "build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
14 |     "prepare": "npm run build",
15 |     "start": "node dist/index.js",
16 |     "dev": "ts-node src/index.ts",
17 |     "watch": "tsc --watch",
18 |     "test": "echo \"Error: no test specified\" && exit 1"
19 |   },
20 |   "keywords": [
21 |     "mcp",
22 |     "mongodb",
23 |     "database"
24 |   ],
25 |   "author": "",
26 |   "license": "ISC",
27 |   "dependencies": {
28 |     "@modelcontextprotocol/sdk": "^1.4.0",
29 |     "dotenv": "^16.4.5",
30 |     "mongodb": "^6.15.0",
31 |     "zod": "^3.24.2"
32 |   },
33 |   "devDependencies": {
34 |     "@types/node": "^20.11.24",
35 |     "ts-node": "^10.9.2",
36 |     "typescript": "^5.3.3"
37 |   }
38 | }
39 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
  3 | import * as dotenv from "dotenv";
  4 | import { z } from "zod";
  5 | import { mongodbConnection } from "./mongodb/index.js";
  6 | dotenv.config();
  7 | 
  8 | class DatabaseMCPServer {
  9 |   private mcpServer: McpServer;
 10 |   private readonly MONGODB_URI: string;
 11 | 
 12 |   constructor() {
 13 |     const uri = process.env.MONGODB_URI;
 14 |     if (!uri) {
 15 |       throw new Error("MONGODB_URI environment variable is required");
 16 |     }
 17 |     this.MONGODB_URI = uri;
 18 |     this.mcpServer = new McpServer({
 19 |       name: "database-mcp",
 20 |       version: "1.0.0",
 21 |     });
 22 |     this.initializeTools();
 23 |   }
 24 | 
 25 |   private initializeTools() {
 26 |     this.mcpServer.tool("getCollections", async () => {
 27 |       try {
 28 |         let db = mongodbConnection.getDb();
 29 |         if (!db) {
 30 |           await mongodbConnection.connect(this.MONGODB_URI);
 31 |           db = mongodbConnection.getDb();
 32 |           if (!db) throw new Error("Failed to connect to database");
 33 |         }
 34 |         const collections = await db.listCollections().toArray();
 35 |         return {
 36 |           content: [
 37 |             { type: "text", text: collections.map((c) => c.name).join(", ") },
 38 |           ],
 39 |         };
 40 |       } catch (error) {
 41 |         console.error(error);
 42 |         return {
 43 |           content: [{ type: "text", text: "Error: " + error }],
 44 |         };
 45 |       }
 46 |     });
 47 | 
 48 |     this.mcpServer.tool(
 49 |       "getCollection",
 50 |       {
 51 |         collectionName: z.string(),
 52 |         limit: z.number().min(1).max(1000).optional().default(10),
 53 |         query: z.object({}).optional(),
 54 |         projection: z.object({}).optional(),
 55 |       },
 56 |       async ({
 57 |         collectionName,
 58 |         limit,
 59 |         query,
 60 |         projection,
 61 |       }: {
 62 |         collectionName: string;
 63 |         limit?: number;
 64 |         query?: any;
 65 |         projection?: any;
 66 |       }) => {
 67 |         try {
 68 |           let db = mongodbConnection.getDb();
 69 |           if (!db) {
 70 |             await mongodbConnection.connect(this.MONGODB_URI);
 71 |             db = mongodbConnection.getDb();
 72 |             if (!db) throw new Error("Failed to connect to database");
 73 |           }
 74 |           const collection = db.collection(collectionName);
 75 |           const documents = await collection
 76 |             .find(query ?? {})
 77 |             .limit(limit ?? 100)
 78 |             .project(projection ?? {})
 79 |             .toArray();
 80 |           return {
 81 |             content: [
 82 |               {
 83 |                 type: "text",
 84 |                 text: documents.map((d) => JSON.stringify(d)).join("\n"),
 85 |               },
 86 |             ],
 87 |           };
 88 |         } catch (error) {
 89 |           console.error(error);
 90 |           return {
 91 |             content: [{ type: "text", text: "Error: " + error }],
 92 |           };
 93 |         }
 94 |       }
 95 |     );
 96 | 
 97 |     this.mcpServer.tool(
 98 |       "insertOne",
 99 |       {
100 |         collectionName: z.string(),
101 |         document: z.object({}),
102 |       },
103 |       async ({ collectionName, document }) => {
104 |         try {
105 |           let db = mongodbConnection.getDb();
106 |           if (!db) {
107 |             await mongodbConnection.connect(this.MONGODB_URI);
108 |             db = mongodbConnection.getDb();
109 |             if (!db) throw new Error("Failed to connect to database");
110 |           }
111 |           const collection = db.collection(collectionName);
112 |           await collection.insertOne(document);
113 |           return {
114 |             content: [{ type: "text", text: "Document inserted successfully" }],
115 |           };
116 |         } catch (error) {
117 |           console.error(error);
118 |           return {
119 |             content: [{ type: "text", text: "Error: " + error }],
120 |           };
121 |         }
122 |       }
123 |     );
124 |     this.mcpServer.tool(
125 |       "deleteOne",
126 |       {
127 |         collectionName: z.string(),
128 |         query: z.object({}),
129 |       },
130 |       async ({ collectionName, query }) => {
131 |         try {
132 |           let db = mongodbConnection.getDb();
133 |           if (!db) {
134 |             await mongodbConnection.connect(this.MONGODB_URI);
135 |             db = mongodbConnection.getDb();
136 |             if (!db) throw new Error("Failed to connect to database");
137 |           }
138 |           const collection = db.collection(collectionName);
139 |           await collection.deleteOne(query);
140 |           return {
141 |             content: [{ type: "text", text: "Document deleted successfully" }],
142 |           };
143 |         } catch (error) {
144 |           console.error(error);
145 |           return {
146 |             content: [{ type: "text", text: "Error: " + error }],
147 |           };
148 |         }
149 |       }
150 |     );
151 |     this.mcpServer.tool(
152 |       "aggregate",
153 |       {
154 |         collectionName: z.string(),
155 |         pipeline: z.array(
156 |           z
157 |             .object({
158 |               $match: z.record(z.any()).optional(),
159 |               $group: z.record(z.any()).optional(),
160 |               $project: z.record(z.any()).optional(),
161 |               $sort: z.record(z.any()).optional(),
162 |               $limit: z.number().optional(),
163 |               $skip: z.number().optional(),
164 |               $unwind: z.string().optional(),
165 |               $lookup: z
166 |                 .object({
167 |                   from: z.string(),
168 |                   localField: z.string(),
169 |                   foreignField: z.string(),
170 |                   as: z.string(),
171 |                 })
172 |                 .optional(),
173 |               $count: z.string().optional(),
174 |               $addFields: z.record(z.any()).optional(),
175 |               $replaceRoot: z.record(z.any()).optional(),
176 |               $facet: z.record(z.any()).optional(),
177 |               $bucket: z.record(z.any()).optional(),
178 |               $geoNear: z.record(z.any()).optional(),
179 |               $indexStats: z.record(z.any()).optional(),
180 |               $listLocalSessions: z.record(z.any()).optional(),
181 |               $listSessions: z.record(z.any()).optional(),
182 |               $merge: z.record(z.any()).optional(),
183 |               $out: z.string().optional(),
184 |               $planCacheStats: z.record(z.any()).optional(),
185 |               $redact: z.record(z.any()).optional(),
186 |               $replaceWith: z.record(z.any()).optional(),
187 |               $sample: z.object({ size: z.number() }).optional(),
188 |               $search: z.record(z.any()).optional(),
189 |               $searchMeta: z.record(z.any()).optional(),
190 |               $set: z.record(z.any()).optional(),
191 |               $setWindowFields: z.record(z.any()).optional(),
192 |               $unionWith: z.record(z.any()).optional(),
193 |               $unset: z.string().optional(),
194 |             })
195 |             .refine(
196 |               (obj) => {
197 |                 // Count the number of defined fields
198 |                 const definedFields = Object.keys(obj).filter(
199 |                   (key) => obj[key as keyof typeof obj] !== undefined
200 |                 );
201 |                 return definedFields.length === 1;
202 |               },
203 |               {
204 |                 message: "Each pipeline stage must contain exactly one field",
205 |               }
206 |             )
207 |         ),
208 |         options: z.object({}).optional(),
209 |       },
210 |       async ({ collectionName, pipeline, options }) => {
211 |         try {
212 |           let db = mongodbConnection.getDb();
213 |           if (!db) {
214 |             await mongodbConnection.connect(this.MONGODB_URI);
215 |             db = mongodbConnection.getDb();
216 |             if (!db) throw new Error("Failed to connect to database");
217 |           }
218 |           const collection = db.collection(collectionName);
219 |           const result = await collection
220 |             .aggregate(pipeline, { maxTimeMS: 30000, ...options })
221 |             .toArray();
222 |           return {
223 |             content: [
224 |               {
225 |                 type: "text",
226 |                 text: JSON.stringify(result),
227 |               },
228 |             ],
229 |           };
230 |         } catch (error) {
231 |           console.error(error);
232 |           return {
233 |             content: [{ type: "text", text: "Error: " + error }],
234 |           };
235 |         }
236 |       }
237 |     );
238 |   }
239 | 
240 |   async connect(transport: StdioServerTransport) {
241 |     await this.mcpServer.connect(transport);
242 |   }
243 | 
244 |   async close() {
245 |     await mongodbConnection.close();
246 |   }
247 | }
248 | 
249 | async function main() {
250 |   const server = new DatabaseMCPServer();
251 |   const transport = new StdioServerTransport();
252 |   await server.connect(transport);
253 |   console.log("Database MCP Server running on stdio");
254 | }
255 | 
256 | process.on("SIGINT", async () => {
257 |   try {
258 |     console.error("SIGINT received");
259 |     await mongodbConnection.close();
260 |   } finally {
261 |     process.exit(0);
262 |   }
263 | });
264 | 
265 | process.on("unhandledRejection", (error) => {
266 |   console.error("Unhandled promise rejection:", error);
267 |   process.exit(1);
268 | });
269 | 
270 | main().catch((error) => {
271 |   console.error("Fatal error in main():", error);
272 |   process.exit(1);
273 | });
274 | 
```