# 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 | [](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 |
```