# 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 |
```