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

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

# Files

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

```
 1 | # Dependency directories
 2 | node_modules/
 3 | npm-debug.log
 4 | yarn-debug.log
 5 | yarn-error.log
 6 | 
 7 | # Build output
 8 | build/
 9 | dist/
10 | *.tsbuildinfo
11 | 
12 | # Environment variables
13 | .env
14 | .env.local
15 | .env.development.local
16 | .env.test.local
17 | .env.production.local
18 | 
19 | # Editor directories and files
20 | .idea/
21 | .vscode/
22 | *.swp
23 | *.swo
24 | *~
25 | mcp-mongodb-atlas-*
26 | 
27 | # OS specific files
28 | .DS_Store
29 | Thumbs.db
30 | 
```

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

```markdown
  1 | <h2 align="center">
  2 |   📢 <strong>COMMUNITY SERVER NOTICE</strong><br/>
  3 |   This is a community-maintained MCP Server.<br/>
  4 |   👉 For the <strong>official</strong> MongoDB MCP Server, visit  
  5 |   <a href="https://github.com/mongodb-js/mongodb-mcp-server">mongodb-js/mongodb-mcp-server</a>
  6 | </h2>
  7 | 
  8 | # MongoDB Atlas MCP Server
  9 | 
 10 | An MCP (Model Context Protocol) server for managing MongoDB Atlas projects. This package provides tools for creating and managing MongoDB Atlas clusters, users, and network access through the MCP interface.
 11 | 
 12 | 
 13 | ## Demo Video
 14 | 
 15 | [![MongoDB Atlas MCP Server Demo](https://img.youtube.com/vi/h8nmRsOGUew/0.jpg)](https://www.youtube.com/watch?v=h8nmRsOGUew)
 16 | 
 17 | Watch the demonstration video to see MongoDB Atlas MCP Server in action.
 18 | 
 19 | ## Features
 20 | 
 21 | ### MCP Tools
 22 | 
 23 | - `create_atlas_cluster` - Create a new MongoDB Atlas cluster in an existing project
 24 | - `setup_atlas_network_access` - Configure network access for an Atlas project
 25 | - `create_atlas_user` - Create a new database user with atlasAdmin role
 26 | - `get_atlas_connection_strings` - Retrieve connection strings for a cluster
 27 | - `list_atlas_projects` - List all Atlas projects accessible with the provided API key
 28 | - `list_atlas_clusters` - List all clusters in a specific Atlas project
 29 | 
 30 | ## Installation
 31 | 
 32 | ```bash
 33 | npm install mcp-mongodb-atlas
 34 | ```
 35 | 
 36 | ## Usage
 37 | 
 38 | ### As a Command Line Tool
 39 | 
 40 | You can run the Atlas Project Manager directly from the command line:
 41 | 
 42 | ```bash
 43 | # Using environment variables
 44 | export ATLAS_PUBLIC_KEY="your-public-key"
 45 | export ATLAS_PRIVATE_KEY="your-private-key"
 46 | npx mcp-mongodb-atlas
 47 | 
 48 | # Or passing keys as arguments
 49 | npx mcp-mongodb-atlas "your-public-key" "your-private-key"
 50 | ```
 51 | 
 52 | 
 53 | ### With Cline (VSCode Extension)
 54 | 
 55 | To use with Cline in VSCode, add the server config to your MCP settings file:
 56 | 
 57 | ```json
 58 | {
 59 |   "mcpServers": {
 60 |     "atlas": {
 61 |       "command": "npx",
 62 |       "args": ["mcp-mongodb-atlas"],
 63 |       "env": {
 64 |         "ATLAS_PUBLIC_KEY": "your-public-key",
 65 |         "ATLAS_PRIVATE_KEY": "your-private-key"
 66 |       },
 67 |       "disabled": false,
 68 |       "autoApprove": []
 69 |     }
 70 |   }
 71 | }
 72 | ```
 73 | 
 74 | The MCP settings file is located at:
 75 | - macOS: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
 76 | - Windows: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
 77 | - Linux: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
 78 | 
 79 | ### With Cursor
 80 | 
 81 | To use with Cursor, go to "Cursor settings" > "MCP" in the settings and add a new server with the following configuration:
 82 | 
 83 | 1. **Name**: `atlas` (or any name you prefer)
 84 | 2. **Command**: `npx mcp-mongodb-atlas`
 85 | 3. **Arguments**: provide your API keys as arguments
 86 | ```bash
 87 | ## Suggested Command
 88 | npx mcp-mongodb-atlas <public_key> <private_key>
 89 | ```
 90 | 
 91 | Newer versions can set the `~/.cursor/mcp.json` file with:
 92 | ```
 93 | {
 94 |   "mcpServers": {
 95 |     "atlas": {
 96 |       "command": "npx",
 97 |       "args": ["mcp-mongodb-atlas"],
 98 |       "env": {
 99 |         "ATLAS_PUBLIC_KEY": "your-public-key",
100 |         "ATLAS_PRIVATE_KEY": "your-private-key"
101 |       },
102 |       "disabled": false,
103 |       "autoApprove": []
104 |     }
105 |   }
106 | }
107 | ```
108 | 
109 | 4. **Environment Variables** (Optional):
110 |    - `ATLAS_PUBLIC_KEY`: Your MongoDB Atlas public key
111 |    - `ATLAS_PRIVATE_KEY`: Your MongoDB Atlas private key
112 | 
113 | ### With Claude Desktop
114 | 
115 | To use with Claude Desktop, add the server config:
116 | 
117 | On macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
118 | On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
119 | 
120 | ```json
121 | {
122 |   "mcpServers": {
123 |     "atlas": {
124 |       "command": "npx",
125 |       "args": ["mcp-mongodb-atlas"],
126 |       "env": {
127 |         "ATLAS_PUBLIC_KEY": "your-public-key",
128 |         "ATLAS_PRIVATE_KEY": "your-private-key"
129 |       }
130 |     }
131 |   }
132 | }
133 | ```
134 | 
135 | ## API Keys
136 | 
137 | You need MongoDB Atlas API keys to use this tool. To create API keys:
138 | 
139 | 1. Log in to your MongoDB Atlas account
140 | 2. Go to Access Manager > API Keys
141 | 3. Create a new API key with the appropriate permissions
142 | 4. Save the public and private keys
143 | 
144 | ## Development
145 | 
146 | Clone the repository and install dependencies:
147 | 
148 | ```bash
149 | git clone https://github.com/mongodb-developer/mcp-mongodb-atlas.git
150 | cd mcp-mongodb-atlas
151 | npm install
152 | ```
153 | 
154 | Build the project:
155 | 
156 | ```bash
157 | npm run build
158 | ```
159 | 
160 | For development with auto-rebuild:
161 | 
162 | ```bash
163 | npm run watch
164 | ```
165 | 
166 | ### Debugging
167 | 
168 | Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the MCP Inspector:
169 | 
170 | ```bash
171 | npm run inspector
172 | ```
173 | 
174 | The Inspector will provide a URL to access debugging tools in your browser.
175 | 
176 | ## License
177 | 
178 | MIT
179 | 
```

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

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

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

```json
 1 | {
 2 |   "name": "mcp-mongodb-atlas",
 3 |   "version": "0.1.10",
 4 |   "description": "MCP Tool to operate and integrate MongoDB Atlas projects into an AI developed project",
 5 |   "type": "module",
 6 |   "bin": {
 7 |     "mcp-mongodb-atlas": "./build/index.js"
 8 |   },
 9 |   "main": "./build/index.js",
10 |   "types": "./build/index.d.ts",
11 |   "files": [
12 |     "build"
13 |   ],
14 |   "scripts": {
15 |     "build": "tsc && chmod +x build/index.js",
16 |     "prepare": "npm run build",
17 |     "watch": "tsc --watch",
18 |     "inspector": "npx @modelcontextprotocol/inspector build/index.js"
19 |   },
20 |   "dependencies": {
21 |     "@modelcontextprotocol/sdk": "0.6.0",
22 |     "axios": "^1.6.0"
23 |   },
24 |   "devDependencies": {
25 |     "@types/node": "^20.11.24",
26 |     "typescript": "^5.3.3"
27 |   },
28 |   "keywords": [
29 |     "mongodb",
30 |     "atlas",
31 |     "mcp",
32 |     "model-context-protocol",
33 |     "ai",
34 |     "llm"
35 |   ],
36 |   "author": "",
37 |   "license": "MIT",
38 |   "engines": {
39 |     "node": ">=16.0.0"
40 |   }
41 | }
42 | 
```

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

```typescript
  1 | #!/usr/bin/env node
  2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js';
  3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
  4 | import crypto from 'crypto';
  5 | import {
  6 |   CallToolRequestSchema,
  7 |   ErrorCode,
  8 |   ListToolsRequestSchema,
  9 |   McpError,
 10 | } from '@modelcontextprotocol/sdk/types.js';
 11 | 
 12 | interface CreateClusterInput {
 13 |   projectId: string;
 14 |   clusterName: string;
 15 |   region: string;
 16 |   cloudProvider: string;
 17 |   tier: string;
 18 | }
 19 | 
 20 | interface NetworkAccessInput {
 21 |   projectId: string;
 22 |   ipAddresses: string[];
 23 | }
 24 | 
 25 | interface CreateUserInput {
 26 |   projectId: string;
 27 |   username: string;
 28 |   password: string;
 29 |   roles: string[];
 30 | }
 31 | 
 32 | interface ConnectionStringsInput {
 33 |   projectId: string;
 34 |   clusterName: string;
 35 | }
 36 | 
 37 | interface ListClustersInput {
 38 |   projectId: string;
 39 | }
 40 | 
 41 | const isValidCreateClusterInput = (args: any): args is CreateClusterInput =>
 42 |   typeof args === 'object' &&
 43 |   args !== null &&
 44 |   typeof args.projectId === 'string' &&
 45 |   typeof args.clusterName === 'string' &&
 46 |   typeof args.region === 'string' &&
 47 |   typeof args.cloudProvider === 'string' &&
 48 |   typeof args.tier === 'string';
 49 | 
 50 | class AtlasProjectManager {
 51 |   private server: Server;
 52 |   private apiKey: string;
 53 |   private privateKey: string;
 54 | 
 55 |   private async makeAtlasRequest(url: string, method: string, body?: any) {
 56 |     // Step 1: Make initial request to get digest challenge
 57 |     const initialResponse = await fetch(url, {
 58 |       method,
 59 |       headers: {
 60 |         'Content-Type': 'application/json'
 61 |       },
 62 |       body: body ? JSON.stringify(body) : undefined
 63 |     });
 64 | 
 65 |     // Check if we got a 401 with WWW-Authenticate header (digest challenge)
 66 |     if (initialResponse.status === 401) {
 67 |       const wwwAuthHeader = initialResponse.headers.get('WWW-Authenticate');
 68 |       if (!wwwAuthHeader || !wwwAuthHeader.startsWith('Digest ')) {
 69 |         throw new Error('Expected Digest authentication challenge not received');
 70 |       }
 71 | 
 72 |       // Parse the digest challenge
 73 |       const authDetails: Record<string, string> = {};
 74 |       wwwAuthHeader.substring(7).split(',').forEach(part => {
 75 |         const [key, value] = part.trim().split('=');
 76 |         // Remove quotes if present
 77 |         authDetails[key] = value.startsWith('"') ? value.slice(1, -1) : value;
 78 |       });
 79 | 
 80 |       // Generate a random client nonce (cnonce)
 81 |       const cnonce = Math.random().toString(36).substring(2, 15);
 82 |       const nc = '00000001'; // nonce count, incremented for each request with the same nonce
 83 | 
 84 |       // Calculate the response hash
 85 |       const ha1 = this.md5(`${this.apiKey}:${authDetails.realm}:${this.privateKey}`);
 86 |       const ha2 = this.md5(`${method}:${new URL(url).pathname}`);
 87 |       const response = this.md5(`${ha1}:${authDetails.nonce}:${nc}:${cnonce}:${authDetails.qop}:${ha2}`);
 88 | 
 89 |       // Build the Authorization header
 90 |       const authHeader = `Digest username="${this.apiKey}", realm="${authDetails.realm}", nonce="${authDetails.nonce}", uri="${new URL(url).pathname}", qop=${authDetails.qop}, nc=${nc}, cnonce="${cnonce}", response="${response}", algorithm=${authDetails.algorithm || 'MD5'}`;
 91 | 
 92 |       // Make the actual request with the digest authentication
 93 |       const digestResponse = await fetch(url, {
 94 |         method,
 95 |         headers: {
 96 |           'Content-Type': 'application/json',
 97 |           'Authorization': authHeader
 98 |         },
 99 |         body: body ? JSON.stringify(body) : undefined
100 |       });
101 | 
102 |       if (!digestResponse.ok) {
103 |         throw new Error(`Atlas API error: ${digestResponse.statusText}`);
104 |       }
105 | 
106 |       return digestResponse.json();
107 |     } else if (initialResponse.ok) {
108 |       // If the initial request succeeded without authentication (unlikely)
109 |       return initialResponse.json();
110 |     } else {
111 |       throw new Error(`Atlas API error: ${initialResponse.statusText}`);
112 |     }
113 |   }
114 | 
115 |   // Helper method to calculate MD5 hash
116 |   private md5(data: string): string {
117 |     // Use createRequire to enable require in ES modules
118 |     return crypto.createHash('md5').update(data).digest('hex');
119 |   }
120 | 
121 |   private async createAtlasCluster(input: CreateClusterInput) {
122 |     if (input.tier === 'M0') {
123 |       return {
124 |         content: [{
125 |           type: 'text',
126 |           text: 'M0 (Free Tier) clusters cannot be created via the API. Please use the MongoDB Atlas UI to create an M0 cluster.'
127 |         }],
128 |         isError: true
129 |       };
130 |     }
131 | 
132 |     try {
133 |       const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/clusters?pretty=true`;
134 |       const body = {
135 |         name: input.clusterName,
136 |         providerSettings: {
137 |           providerName: input.cloudProvider,
138 |           instanceSizeName: input.tier,
139 |           regionName: input.region
140 |         }
141 |       };
142 | 
143 |       const result = await this.makeAtlasRequest(url, 'POST', body);
144 |       return {
145 |         content: [{
146 |           type: 'text',
147 |           text: JSON.stringify(result, null, 2)
148 |         }]
149 |       };
150 |     } catch (error: any) {
151 |       return {
152 |         content: [{
153 |           type: 'text',
154 |           text: error.message
155 |         }],
156 |         isError: true
157 |       };
158 |     }
159 |   }
160 | 
161 |   private async setupAtlasNetworkAccess(input: NetworkAccessInput) {
162 |     try {
163 |       const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/accessList`;
164 |       const body = input.ipAddresses.map(ip => ({
165 |         ipAddress: ip,
166 |         comment: "Added via Atlas Project Manager MCP"
167 |       }));
168 | 
169 |       const result = await this.makeAtlasRequest(url, 'POST', body);
170 |       return {
171 |         content: [{
172 |           type: 'text',
173 |           text: JSON.stringify(result, null, 2)
174 |         }]
175 |       };
176 |     } catch (error: any) {
177 |       return {
178 |         content: [{
179 |           type: 'text',
180 |           text: error.message
181 |         }],
182 |         isError: true
183 |       };
184 |     }
185 |   }
186 | 
187 |   private async createAtlasUser(input: CreateUserInput) {
188 |     try {
189 |       const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/databaseUsers`;
190 |       const body = {
191 |         databaseName: "admin",
192 |         username: input.username,
193 |         password: input.password,
194 |         roles: input.roles.map(role => ({ databaseName: 'admin', roleName: role }))
195 |       };
196 | 
197 |       const result = await this.makeAtlasRequest(url, 'POST', body);
198 |       return {
199 |         content: [{
200 |           type: 'text',
201 |           text: JSON.stringify(result, null, 2)
202 |         }]
203 |       };
204 |     } catch (error: any) {
205 |       return {
206 |         content: [{
207 |           type: 'text',
208 |           text: error.message
209 |         }],
210 |         isError: true
211 |       };
212 |     }
213 |   }
214 | 
215 |   private async getAtlasConnectionStrings(input: ConnectionStringsInput) {
216 |     try {
217 |       const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/clusters/${input.clusterName}`;
218 |       const result = await this.makeAtlasRequest(url, 'GET');
219 |       
220 |       // Add appName to connection strings if they exist
221 |       if (result.connectionStrings) {
222 |         this.addAppNameToConnectionStrings(result);
223 |       }
224 |       
225 |       return {
226 |         content: [{
227 |           type: 'text',
228 |           text: JSON.stringify(result, null, 2)
229 |         }]
230 |       };
231 |     } catch (error: any) {
232 |       return {
233 |         content: [{
234 |           type: 'text',
235 |           text: error.message
236 |         }],
237 |         isError: true
238 |       };
239 |     }
240 |   }
241 | 
242 |   private async listAtlasProjects() {
243 |     try {
244 |       const url = 'https://cloud.mongodb.com/api/atlas/v1.0/groups';
245 |       const result = await this.makeAtlasRequest(url, 'GET');
246 |       return {
247 |         content: [{
248 |           type: 'text',
249 |           text: JSON.stringify(result, null, 2)
250 |         }]
251 |       };
252 |     } catch (error: any) {
253 |       return {
254 |         content: [{
255 |           type: 'text',
256 |           text: error.message
257 |         }],
258 |         isError: true
259 |       };
260 |     }
261 |   }
262 | 
263 |   // Helper method to add appName to connection strings
264 |   private addAppNameToConnectionStrings(result: any) {
265 |     const appName = "devrel.integration.mcp-atlas";
266 |     
267 |     // Helper function to safely add appName parameter to a connection string
268 |     const addAppNameParam = (connectionString: string): string => {
269 |       if (!connectionString) return connectionString;
270 |       
271 |       // Add appName parameter
272 |       return connectionString + (connectionString.includes('?') ? '&' : '?') + `appName=${appName}`;
273 |     };
274 |     
275 |     // Handle single cluster object
276 |     if (result.connectionStrings) {
277 |       // Add appName to standard connection string
278 |       if (result.connectionStrings.standard) {
279 |         result.connectionStrings.standard = addAppNameParam(result.connectionStrings.standard);
280 |       }
281 |       
282 |       // Add appName to standardSrv connection string
283 |       if (result.connectionStrings.standardSrv) {
284 |         result.connectionStrings.standardSrv = addAppNameParam(result.connectionStrings.standardSrv);
285 |       }
286 |       
287 |       // Add appName to other connection string formats
288 |       if (result.mongoURI) {
289 |         result.mongoURI = addAppNameParam(result.mongoURI);
290 |       }
291 |       
292 |       if (result.mongoURIWithOptions) {
293 |         result.mongoURIWithOptions = addAppNameParam(result.mongoURIWithOptions);
294 |       }
295 |       
296 |       if (result.srvAddress) {
297 |         result.srvAddress = addAppNameParam(result.srvAddress);
298 |       }
299 |     }
300 |     
301 |     // Handle array of clusters (for listAtlasClusters)
302 |     if (result.results && Array.isArray(result.results)) {
303 |       result.results.forEach((cluster: any) => {
304 |         if (cluster.connectionStrings) {
305 |           this.addAppNameToConnectionStrings(cluster);
306 |         }
307 |       });
308 |     }
309 |     
310 |     return result;
311 |   }
312 | 
313 |   private async listAtlasClusters(input: ListClustersInput) {
314 |     try {
315 |       const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/clusters`;
316 |       const result = await this.makeAtlasRequest(url, 'GET');
317 |       
318 |       // Add appName to connection strings in all clusters
319 |       this.addAppNameToConnectionStrings(result);
320 |       
321 |       return {
322 |         content: [{
323 |           type: 'text',
324 |           text: JSON.stringify(result, null, 2)
325 |         }]
326 |       };
327 |     } catch (error: any) {
328 |       return {
329 |         content: [{
330 |           type: 'text',
331 |           text: error.message
332 |         }],
333 |         isError: true
334 |       };
335 |     }
336 |   }
337 | 
338 |   constructor() {
339 |     this.server = new Server(
340 |       {
341 |         name: 'atlas-project-manager',
342 |         version: '0.1.0',
343 |       },
344 |       {
345 |         capabilities: {
346 |           tools: {},
347 |         },
348 |       }
349 |     );
350 | 
351 |     let apiKey = process.env.ATLAS_PUBLIC_KEY;
352 |     let privateKey = process.env.ATLAS_PRIVATE_KEY;
353 | 
354 |     if (!apiKey || !privateKey) {
355 |       const args = process.argv.slice(2);
356 |       if (args.length >= 2) {
357 |         apiKey = args[0];
358 |         privateKey = args[1];
359 |         console.error('Using API keys from command line arguments');
360 |       } else {
361 |         throw new Error('ATLAS_PUBLIC_KEY and ATLAS_PRIVATE_KEY must be provided either as environment variables or as command line arguments');
362 |       }
363 |     }
364 | 
365 |     this.apiKey = apiKey;
366 |     this.privateKey = privateKey;
367 | 
368 |     this.setupToolHandlers();
369 | 
370 |     this.server.onerror = (error) => console.error('[MCP Error]', error);
371 |     process.on('SIGINT', async () => {
372 |       await this.server.close();
373 |       process.exit(0);
374 |     });
375 |   }
376 | 
377 |   private setupToolHandlers() {
378 |     this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
379 |       tools: [
380 |         {
381 |           name: 'create_atlas_cluster',
382 |           description: 'Creates a new Atlas cluster in an existing Atlas project.',
383 |           inputSchema: {
384 |             type: 'object',
385 |             properties: {
386 |               projectId: {
387 |                 type: 'string',
388 |                 description: 'The ID of the Atlas project.',
389 |               },
390 |               clusterName: {
391 |                 type: 'string',
392 |                 description: 'The name of the cluster to create.',
393 |               },
394 |               region: {
395 |                 type: 'string',
396 |                 description: 'The cloud provider region to deploy the cluster in. eg. US_EAST_1',
397 |               },
398 |               cloudProvider: {
399 |                 type: 'string',
400 |                 description: 'The cloud provider (e.g., AWS, GCP, AZURE).',
401 |               },
402 |               tier: {
403 |                 type: 'string',
404 |                 description: 'The instance size (e.g., M0, M2, M5).',
405 |               }
406 |             },
407 |             required: ['projectId', 'clusterName', 'region', 'cloudProvider', 'tier'],
408 |           },
409 |         },
410 |         {
411 |           name: 'setup_atlas_network_access',
412 |           description: 'Sets up network access for an existing Atlas project. Accepts list of IP addresses or CIDR blocks.',
413 |           inputSchema: {
414 |             type: 'object',
415 |             properties: {
416 |               projectId: {
417 |                 type: 'string',
418 |                 description: 'The ID of the Atlas project.',
419 |               },
420 |               ipAddresses: {
421 |                 type: 'array',
422 |                 items: {
423 |                   type: 'string',
424 |                 },
425 |                 description: 'An array of IP addresses or CIDR blocks for network access.',
426 |               },
427 |             },
428 |             required: ['projectId', 'ipAddresses'],
429 |           },
430 |         },
431 |         {
432 |           name: 'create_atlas_user',
433 |           description: 'Creates a new database user for an existing Atlas project. User will have atlasAdmin role.',
434 |           inputSchema: {
435 |             type: 'object',
436 |             properties: {
437 |               projectId: {
438 |                 type: 'string',
439 |                 description: 'The ID of the Atlas project.',
440 |               },
441 |               username: {
442 |                 type: 'string',
443 |                 description: 'The username for the database user.',
444 |               },
445 |               password: {
446 |                 type: 'string',
447 |                 description: 'The password for the database user.',
448 |               },
449 |               roles: {
450 |                 type: 'array',
451 |                 items: {
452 |                   type: 'string',
453 |                 },
454 |                 description: 'An array of roles for the user. Default is [atlasAdmin].',
455 |               }
456 |             },
457 |             required: ['projectId', 'username', 'password'],
458 |           },
459 |         },
460 |         {
461 |           name: 'get_atlas_connection_strings',
462 |           description: 'Retrieves connection strings for a cluster in an existing Atlas project.',
463 |           inputSchema: {
464 |             type: 'object',
465 |             properties: {
466 |               projectId: {
467 |                 type: 'string',
468 |                 description: 'The ID of the Atlas project.',
469 |               },
470 |               clusterName: {
471 |                 type: 'string',
472 |                 description: 'The name of the cluster.',
473 |               },
474 |             },
475 |             required: ['projectId', 'clusterName'],
476 |           },
477 |         },
478 |         {
479 |           name: 'list_atlas_projects',
480 |           description: 'Lists all Atlas projects that the API key has access to.',
481 |           inputSchema: {
482 |             type: 'object',
483 |             properties: {},
484 |           },
485 |         },
486 |         {
487 |           name: 'list_atlas_clusters',
488 |           description: 'Lists all clusters in an Atlas project.',
489 |           inputSchema: {
490 |             type: 'object',
491 |             properties: {
492 |               projectId: {
493 |                 type: 'string',
494 |                 description: 'The ID of the Atlas project.',
495 |               },
496 |             },
497 |             required: ['projectId'],
498 |           },
499 |         }
500 |       ],
501 |     }));
502 | 
503 |     this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
504 |       if (!['create_atlas_cluster', 'setup_atlas_network_access', 'create_atlas_user', 'get_atlas_connection_strings', 'list_atlas_projects', 'list_atlas_clusters'].includes(request.params.name)) {
505 |         throw new McpError(
506 |           ErrorCode.MethodNotFound,
507 |           `Unknown tool: ${request.params.name}`
508 |         );
509 |       }
510 | 
511 |       if (!request.params.arguments) {
512 |         throw new McpError(ErrorCode.InvalidParams, 'Missing arguments');
513 |       }
514 | 
515 |       const input = request.params.arguments as Record<string, unknown>;
516 | 
517 |       switch (request.params.name) {
518 |         case 'create_atlas_cluster':
519 |           if (!isValidCreateClusterInput(input)) {
520 |             throw new McpError(ErrorCode.InvalidParams, 'Invalid cluster creation arguments');
521 |           }
522 |           break;
523 |         case 'setup_atlas_network_access':
524 |           if (!input.projectId || !input.ipAddresses || !Array.isArray(input.ipAddresses)) {
525 |             throw new McpError(ErrorCode.InvalidParams, 'Invalid network access arguments');
526 |           }
527 |           break;
528 |         case 'create_atlas_user':
529 |           if (!input.projectId || !input.username || !input.password) {
530 |             throw new McpError(ErrorCode.InvalidParams, 'Invalid user creation arguments');
531 |           }
532 |           break;
533 |         case 'get_atlas_connection_strings':
534 |           if (!input.projectId || !input.clusterName) {
535 |             throw new McpError(ErrorCode.InvalidParams, 'Invalid connection string arguments');
536 |           }
537 |           break;
538 |         case 'list_atlas_clusters':
539 |           if (!input.projectId) {
540 |             throw new McpError(ErrorCode.InvalidParams, 'Invalid list clusters arguments');
541 |           }
542 |           break;
543 |       }
544 | 
545 |       let result;
546 | 
547 |       try {
548 |         switch (request.params.name) {
549 |           case 'create_atlas_cluster':
550 |             result = await this.createAtlasCluster(input as unknown as CreateClusterInput);
551 |             break;
552 |           case 'setup_atlas_network_access':
553 |             result = await this.setupAtlasNetworkAccess(input as unknown as NetworkAccessInput);
554 |             break;
555 |           case 'create_atlas_user':
556 |             result = await this.createAtlasUser(input as unknown as CreateUserInput);
557 |             break;
558 |           case 'get_atlas_connection_strings':
559 |             result = await this.getAtlasConnectionStrings(input as unknown as ConnectionStringsInput);
560 |             break;
561 |           case 'list_atlas_projects':
562 |             result = await this.listAtlasProjects();
563 |             break;
564 |           case 'list_atlas_clusters':
565 |             result = await this.listAtlasClusters(input as unknown as ListClustersInput);
566 |             break;
567 |           default:
568 |             throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
569 |         }
570 | 
571 |         // Ensure we return the expected format
572 |         return {
573 |           content: result.content,
574 |           _meta: request.params._meta
575 |         };
576 |       } catch (error: any) {
577 |         // Handle any errors that might occur
578 |         return {
579 |           content: [{
580 |             type: 'text',
581 |             text: `Error: ${error.message}`
582 |           }],
583 |           isError: true,
584 |           _meta: request.params._meta
585 |         };
586 |       }
587 |     });
588 |   }
589 | 
590 |   async run() {
591 |     const transport = new StdioServerTransport();
592 |     await this.server.connect(transport);
593 |     console.error('Atlas Project Manager MCP server running on stdio');
594 |   }
595 | }
596 | 
597 | const server = new AtlasProjectManager();
598 | server.run().catch(console.error);
599 | 
```