# Directory Structure
```
├── .gitignore
├── LICENSE
├── package-lock.json
├── package.json
├── README.md
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
# Dependency directories
node_modules/
npm-debug.log
yarn-debug.log
yarn-error.log
# Build output
build/
dist/
*.tsbuildinfo
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
# Editor directories and files
.idea/
.vscode/
*.swp
*.swo
*~
mcp-mongodb-atlas-*
# OS specific files
.DS_Store
Thumbs.db
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
<h2 align="center">
📢 <strong>COMMUNITY SERVER NOTICE</strong><br/>
This is a community-maintained MCP Server.<br/>
👉 For the <strong>official</strong> MongoDB MCP Server, visit
<a href="https://github.com/mongodb-js/mongodb-mcp-server">mongodb-js/mongodb-mcp-server</a>
</h2>
# MongoDB Atlas MCP Server
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.
## Demo Video
[](https://www.youtube.com/watch?v=h8nmRsOGUew)
Watch the demonstration video to see MongoDB Atlas MCP Server in action.
## Features
### MCP Tools
- `create_atlas_cluster` - Create a new MongoDB Atlas cluster in an existing project
- `setup_atlas_network_access` - Configure network access for an Atlas project
- `create_atlas_user` - Create a new database user with atlasAdmin role
- `get_atlas_connection_strings` - Retrieve connection strings for a cluster
- `list_atlas_projects` - List all Atlas projects accessible with the provided API key
- `list_atlas_clusters` - List all clusters in a specific Atlas project
## Installation
```bash
npm install mcp-mongodb-atlas
```
## Usage
### As a Command Line Tool
You can run the Atlas Project Manager directly from the command line:
```bash
# Using environment variables
export ATLAS_PUBLIC_KEY="your-public-key"
export ATLAS_PRIVATE_KEY="your-private-key"
npx mcp-mongodb-atlas
# Or passing keys as arguments
npx mcp-mongodb-atlas "your-public-key" "your-private-key"
```
### With Cline (VSCode Extension)
To use with Cline in VSCode, add the server config to your MCP settings file:
```json
{
"mcpServers": {
"atlas": {
"command": "npx",
"args": ["mcp-mongodb-atlas"],
"env": {
"ATLAS_PUBLIC_KEY": "your-public-key",
"ATLAS_PRIVATE_KEY": "your-private-key"
},
"disabled": false,
"autoApprove": []
}
}
}
```
The MCP settings file is located at:
- macOS: `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
- Windows: `%APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json`
- Linux: `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
### With Cursor
To use with Cursor, go to "Cursor settings" > "MCP" in the settings and add a new server with the following configuration:
1. **Name**: `atlas` (or any name you prefer)
2. **Command**: `npx mcp-mongodb-atlas`
3. **Arguments**: provide your API keys as arguments
```bash
## Suggested Command
npx mcp-mongodb-atlas <public_key> <private_key>
```
Newer versions can set the `~/.cursor/mcp.json` file with:
```
{
"mcpServers": {
"atlas": {
"command": "npx",
"args": ["mcp-mongodb-atlas"],
"env": {
"ATLAS_PUBLIC_KEY": "your-public-key",
"ATLAS_PRIVATE_KEY": "your-private-key"
},
"disabled": false,
"autoApprove": []
}
}
}
```
4. **Environment Variables** (Optional):
- `ATLAS_PUBLIC_KEY`: Your MongoDB Atlas public key
- `ATLAS_PRIVATE_KEY`: Your MongoDB Atlas private key
### With Claude Desktop
To use with Claude Desktop, add the server config:
On macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
```json
{
"mcpServers": {
"atlas": {
"command": "npx",
"args": ["mcp-mongodb-atlas"],
"env": {
"ATLAS_PUBLIC_KEY": "your-public-key",
"ATLAS_PRIVATE_KEY": "your-private-key"
}
}
}
}
```
## API Keys
You need MongoDB Atlas API keys to use this tool. To create API keys:
1. Log in to your MongoDB Atlas account
2. Go to Access Manager > API Keys
3. Create a new API key with the appropriate permissions
4. Save the public and private keys
## Development
Clone the repository and install dependencies:
```bash
git clone https://github.com/mongodb-developer/mcp-mongodb-atlas.git
cd mcp-mongodb-atlas
npm install
```
Build the project:
```bash
npm run build
```
For development with auto-rebuild:
```bash
npm run watch
```
### Debugging
Since MCP servers communicate over stdio, debugging can be challenging. We recommend using the MCP Inspector:
```bash
npm run inspector
```
The Inspector will provide a URL to access debugging tools in your browser.
## License
MIT
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "mcp-mongodb-atlas",
"version": "0.1.10",
"description": "MCP Tool to operate and integrate MongoDB Atlas projects into an AI developed project",
"type": "module",
"bin": {
"mcp-mongodb-atlas": "./build/index.js"
},
"main": "./build/index.js",
"types": "./build/index.d.ts",
"files": [
"build"
],
"scripts": {
"build": "tsc && chmod +x build/index.js",
"prepare": "npm run build",
"watch": "tsc --watch",
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0",
"axios": "^1.6.0"
},
"devDependencies": {
"@types/node": "^20.11.24",
"typescript": "^5.3.3"
},
"keywords": [
"mongodb",
"atlas",
"mcp",
"model-context-protocol",
"ai",
"llm"
],
"author": "",
"license": "MIT",
"engines": {
"node": ">=16.0.0"
}
}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import crypto from 'crypto';
import {
CallToolRequestSchema,
ErrorCode,
ListToolsRequestSchema,
McpError,
} from '@modelcontextprotocol/sdk/types.js';
interface CreateClusterInput {
projectId: string;
clusterName: string;
region: string;
cloudProvider: string;
tier: string;
}
interface NetworkAccessInput {
projectId: string;
ipAddresses: string[];
}
interface CreateUserInput {
projectId: string;
username: string;
password: string;
roles: string[];
}
interface ConnectionStringsInput {
projectId: string;
clusterName: string;
}
interface ListClustersInput {
projectId: string;
}
const isValidCreateClusterInput = (args: any): args is CreateClusterInput =>
typeof args === 'object' &&
args !== null &&
typeof args.projectId === 'string' &&
typeof args.clusterName === 'string' &&
typeof args.region === 'string' &&
typeof args.cloudProvider === 'string' &&
typeof args.tier === 'string';
class AtlasProjectManager {
private server: Server;
private apiKey: string;
private privateKey: string;
private async makeAtlasRequest(url: string, method: string, body?: any) {
// Step 1: Make initial request to get digest challenge
const initialResponse = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json'
},
body: body ? JSON.stringify(body) : undefined
});
// Check if we got a 401 with WWW-Authenticate header (digest challenge)
if (initialResponse.status === 401) {
const wwwAuthHeader = initialResponse.headers.get('WWW-Authenticate');
if (!wwwAuthHeader || !wwwAuthHeader.startsWith('Digest ')) {
throw new Error('Expected Digest authentication challenge not received');
}
// Parse the digest challenge
const authDetails: Record<string, string> = {};
wwwAuthHeader.substring(7).split(',').forEach(part => {
const [key, value] = part.trim().split('=');
// Remove quotes if present
authDetails[key] = value.startsWith('"') ? value.slice(1, -1) : value;
});
// Generate a random client nonce (cnonce)
const cnonce = Math.random().toString(36).substring(2, 15);
const nc = '00000001'; // nonce count, incremented for each request with the same nonce
// Calculate the response hash
const ha1 = this.md5(`${this.apiKey}:${authDetails.realm}:${this.privateKey}`);
const ha2 = this.md5(`${method}:${new URL(url).pathname}`);
const response = this.md5(`${ha1}:${authDetails.nonce}:${nc}:${cnonce}:${authDetails.qop}:${ha2}`);
// Build the Authorization header
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'}`;
// Make the actual request with the digest authentication
const digestResponse = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
'Authorization': authHeader
},
body: body ? JSON.stringify(body) : undefined
});
if (!digestResponse.ok) {
throw new Error(`Atlas API error: ${digestResponse.statusText}`);
}
return digestResponse.json();
} else if (initialResponse.ok) {
// If the initial request succeeded without authentication (unlikely)
return initialResponse.json();
} else {
throw new Error(`Atlas API error: ${initialResponse.statusText}`);
}
}
// Helper method to calculate MD5 hash
private md5(data: string): string {
// Use createRequire to enable require in ES modules
return crypto.createHash('md5').update(data).digest('hex');
}
private async createAtlasCluster(input: CreateClusterInput) {
if (input.tier === 'M0') {
return {
content: [{
type: 'text',
text: 'M0 (Free Tier) clusters cannot be created via the API. Please use the MongoDB Atlas UI to create an M0 cluster.'
}],
isError: true
};
}
try {
const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/clusters?pretty=true`;
const body = {
name: input.clusterName,
providerSettings: {
providerName: input.cloudProvider,
instanceSizeName: input.tier,
regionName: input.region
}
};
const result = await this.makeAtlasRequest(url, 'POST', body);
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error: any) {
return {
content: [{
type: 'text',
text: error.message
}],
isError: true
};
}
}
private async setupAtlasNetworkAccess(input: NetworkAccessInput) {
try {
const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/accessList`;
const body = input.ipAddresses.map(ip => ({
ipAddress: ip,
comment: "Added via Atlas Project Manager MCP"
}));
const result = await this.makeAtlasRequest(url, 'POST', body);
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error: any) {
return {
content: [{
type: 'text',
text: error.message
}],
isError: true
};
}
}
private async createAtlasUser(input: CreateUserInput) {
try {
const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/databaseUsers`;
const body = {
databaseName: "admin",
username: input.username,
password: input.password,
roles: input.roles.map(role => ({ databaseName: 'admin', roleName: role }))
};
const result = await this.makeAtlasRequest(url, 'POST', body);
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error: any) {
return {
content: [{
type: 'text',
text: error.message
}],
isError: true
};
}
}
private async getAtlasConnectionStrings(input: ConnectionStringsInput) {
try {
const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/clusters/${input.clusterName}`;
const result = await this.makeAtlasRequest(url, 'GET');
// Add appName to connection strings if they exist
if (result.connectionStrings) {
this.addAppNameToConnectionStrings(result);
}
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error: any) {
return {
content: [{
type: 'text',
text: error.message
}],
isError: true
};
}
}
private async listAtlasProjects() {
try {
const url = 'https://cloud.mongodb.com/api/atlas/v1.0/groups';
const result = await this.makeAtlasRequest(url, 'GET');
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error: any) {
return {
content: [{
type: 'text',
text: error.message
}],
isError: true
};
}
}
// Helper method to add appName to connection strings
private addAppNameToConnectionStrings(result: any) {
const appName = "devrel.integration.mcp-atlas";
// Helper function to safely add appName parameter to a connection string
const addAppNameParam = (connectionString: string): string => {
if (!connectionString) return connectionString;
// Add appName parameter
return connectionString + (connectionString.includes('?') ? '&' : '?') + `appName=${appName}`;
};
// Handle single cluster object
if (result.connectionStrings) {
// Add appName to standard connection string
if (result.connectionStrings.standard) {
result.connectionStrings.standard = addAppNameParam(result.connectionStrings.standard);
}
// Add appName to standardSrv connection string
if (result.connectionStrings.standardSrv) {
result.connectionStrings.standardSrv = addAppNameParam(result.connectionStrings.standardSrv);
}
// Add appName to other connection string formats
if (result.mongoURI) {
result.mongoURI = addAppNameParam(result.mongoURI);
}
if (result.mongoURIWithOptions) {
result.mongoURIWithOptions = addAppNameParam(result.mongoURIWithOptions);
}
if (result.srvAddress) {
result.srvAddress = addAppNameParam(result.srvAddress);
}
}
// Handle array of clusters (for listAtlasClusters)
if (result.results && Array.isArray(result.results)) {
result.results.forEach((cluster: any) => {
if (cluster.connectionStrings) {
this.addAppNameToConnectionStrings(cluster);
}
});
}
return result;
}
private async listAtlasClusters(input: ListClustersInput) {
try {
const url = `https://cloud.mongodb.com/api/atlas/v1.0/groups/${input.projectId}/clusters`;
const result = await this.makeAtlasRequest(url, 'GET');
// Add appName to connection strings in all clusters
this.addAppNameToConnectionStrings(result);
return {
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
};
} catch (error: any) {
return {
content: [{
type: 'text',
text: error.message
}],
isError: true
};
}
}
constructor() {
this.server = new Server(
{
name: 'atlas-project-manager',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
let apiKey = process.env.ATLAS_PUBLIC_KEY;
let privateKey = process.env.ATLAS_PRIVATE_KEY;
if (!apiKey || !privateKey) {
const args = process.argv.slice(2);
if (args.length >= 2) {
apiKey = args[0];
privateKey = args[1];
console.error('Using API keys from command line arguments');
} else {
throw new Error('ATLAS_PUBLIC_KEY and ATLAS_PRIVATE_KEY must be provided either as environment variables or as command line arguments');
}
}
this.apiKey = apiKey;
this.privateKey = privateKey;
this.setupToolHandlers();
this.server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await this.server.close();
process.exit(0);
});
}
private setupToolHandlers() {
this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'create_atlas_cluster',
description: 'Creates a new Atlas cluster in an existing Atlas project.',
inputSchema: {
type: 'object',
properties: {
projectId: {
type: 'string',
description: 'The ID of the Atlas project.',
},
clusterName: {
type: 'string',
description: 'The name of the cluster to create.',
},
region: {
type: 'string',
description: 'The cloud provider region to deploy the cluster in. eg. US_EAST_1',
},
cloudProvider: {
type: 'string',
description: 'The cloud provider (e.g., AWS, GCP, AZURE).',
},
tier: {
type: 'string',
description: 'The instance size (e.g., M0, M2, M5).',
}
},
required: ['projectId', 'clusterName', 'region', 'cloudProvider', 'tier'],
},
},
{
name: 'setup_atlas_network_access',
description: 'Sets up network access for an existing Atlas project. Accepts list of IP addresses or CIDR blocks.',
inputSchema: {
type: 'object',
properties: {
projectId: {
type: 'string',
description: 'The ID of the Atlas project.',
},
ipAddresses: {
type: 'array',
items: {
type: 'string',
},
description: 'An array of IP addresses or CIDR blocks for network access.',
},
},
required: ['projectId', 'ipAddresses'],
},
},
{
name: 'create_atlas_user',
description: 'Creates a new database user for an existing Atlas project. User will have atlasAdmin role.',
inputSchema: {
type: 'object',
properties: {
projectId: {
type: 'string',
description: 'The ID of the Atlas project.',
},
username: {
type: 'string',
description: 'The username for the database user.',
},
password: {
type: 'string',
description: 'The password for the database user.',
},
roles: {
type: 'array',
items: {
type: 'string',
},
description: 'An array of roles for the user. Default is [atlasAdmin].',
}
},
required: ['projectId', 'username', 'password'],
},
},
{
name: 'get_atlas_connection_strings',
description: 'Retrieves connection strings for a cluster in an existing Atlas project.',
inputSchema: {
type: 'object',
properties: {
projectId: {
type: 'string',
description: 'The ID of the Atlas project.',
},
clusterName: {
type: 'string',
description: 'The name of the cluster.',
},
},
required: ['projectId', 'clusterName'],
},
},
{
name: 'list_atlas_projects',
description: 'Lists all Atlas projects that the API key has access to.',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'list_atlas_clusters',
description: 'Lists all clusters in an Atlas project.',
inputSchema: {
type: 'object',
properties: {
projectId: {
type: 'string',
description: 'The ID of the Atlas project.',
},
},
required: ['projectId'],
},
}
],
}));
this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
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)) {
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${request.params.name}`
);
}
if (!request.params.arguments) {
throw new McpError(ErrorCode.InvalidParams, 'Missing arguments');
}
const input = request.params.arguments as Record<string, unknown>;
switch (request.params.name) {
case 'create_atlas_cluster':
if (!isValidCreateClusterInput(input)) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid cluster creation arguments');
}
break;
case 'setup_atlas_network_access':
if (!input.projectId || !input.ipAddresses || !Array.isArray(input.ipAddresses)) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid network access arguments');
}
break;
case 'create_atlas_user':
if (!input.projectId || !input.username || !input.password) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid user creation arguments');
}
break;
case 'get_atlas_connection_strings':
if (!input.projectId || !input.clusterName) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid connection string arguments');
}
break;
case 'list_atlas_clusters':
if (!input.projectId) {
throw new McpError(ErrorCode.InvalidParams, 'Invalid list clusters arguments');
}
break;
}
let result;
try {
switch (request.params.name) {
case 'create_atlas_cluster':
result = await this.createAtlasCluster(input as unknown as CreateClusterInput);
break;
case 'setup_atlas_network_access':
result = await this.setupAtlasNetworkAccess(input as unknown as NetworkAccessInput);
break;
case 'create_atlas_user':
result = await this.createAtlasUser(input as unknown as CreateUserInput);
break;
case 'get_atlas_connection_strings':
result = await this.getAtlasConnectionStrings(input as unknown as ConnectionStringsInput);
break;
case 'list_atlas_projects':
result = await this.listAtlasProjects();
break;
case 'list_atlas_clusters':
result = await this.listAtlasClusters(input as unknown as ListClustersInput);
break;
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
}
// Ensure we return the expected format
return {
content: result.content,
_meta: request.params._meta
};
} catch (error: any) {
// Handle any errors that might occur
return {
content: [{
type: 'text',
text: `Error: ${error.message}`
}],
isError: true,
_meta: request.params._meta
};
}
});
}
async run() {
const transport = new StdioServerTransport();
await this.server.connect(transport);
console.error('Atlas Project Manager MCP server running on stdio');
}
}
const server = new AtlasProjectManager();
server.run().catch(console.error);
```