Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/common/atlas/accessListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ export async function makeCurrentIpAccessListEntry(
* If the IP is already present, this is a no-op.
* @param apiClient The Atlas API client instance
* @param projectId The Atlas project ID
* @returns Promise<boolean> - true if a new IP access list entry was created, false if it already existed
*/
export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectId: string): Promise<void> {
export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectId: string): Promise<boolean> {
const entry = await makeCurrentIpAccessListEntry(apiClient, projectId, DEFAULT_ACCESS_LIST_COMMENT);
try {
await apiClient.createProjectIpAccessList({
Expand All @@ -35,6 +36,7 @@ export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectI
context: "accessListUtils",
message: `IP access list created: ${JSON.stringify(entry)}`,
});
return true;
} catch (err) {
if (err instanceof ApiClientError && err.response?.status === 409) {
// 409 Conflict: entry already exists, log info
Expand All @@ -43,12 +45,13 @@ export async function ensureCurrentIpInAccessList(apiClient: ApiClient, projectI
context: "accessListUtils",
message: `IP address ${entry.ipAddress} is already present in the access list for project ${projectId}.`,
});
return;
return false;
}
apiClient.logger.warning({
id: LogId.atlasIpAccessListAddFailure,
context: "accessListUtils",
message: `Error adding IP access list: ${err instanceof Error ? err.message : String(err)}`,
});
}
return false;
}
80 changes: 52 additions & 28 deletions src/tools/atlas/connect/connectCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export class ConnectClusterTool extends AtlasToolBase {
private async prepareClusterConnection(
projectId: string,
clusterName: string
): Promise<{ connectionString: string; atlas: AtlasClusterConnectionInfo }> {
): Promise<{ connectionString: string; atlas: AtlasClusterConnectionInfo; userCreated: boolean }> {
const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName);

if (!cluster.connectionString) {
Expand Down Expand Up @@ -110,7 +110,7 @@ export class ConnectClusterTool extends AtlasToolBase {
cn.password = password;
cn.searchParams.set("authSource", "admin");

return { connectionString: cn.toString(), atlas: connectedAtlasCluster };
return { connectionString: cn.toString(), atlas: connectedAtlasCluster, userCreated: true };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we're always returning true for userCreated - am I missing something?

Copy link
Collaborator Author

@blva blva Sep 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, if the connection state doesn't require preparation (e.g. is already connected*) then we'd not execute this

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right - my point is that prepareClusterConnection always returns userCreated: true (or at least it's not obvious in which case it will return false for userCreated. In the already connected state, we wouldn't invoke this method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, got it! yes, good point since we error if something goes wrong. I can remove that from now, I think we'll need to do more work in the connectCluster anyways but this is out of scope here, so let me simplify

}

private async connectToCluster(connectionString: string, atlas: AtlasClusterConnectionInfo): Promise<void> {
Expand Down Expand Up @@ -190,19 +190,29 @@ export class ConnectClusterTool extends AtlasToolBase {
}

protected async execute({ projectId, clusterName }: ToolArgs<typeof this.argsShape>): Promise<CallToolResult> {
await ensureCurrentIpInAccessList(this.session.apiClient, projectId);
const ipAccessListUpdated = await ensureCurrentIpInAccessList(this.session.apiClient, projectId);
let createdUser = false;

for (let i = 0; i < 60; i++) {
const state = this.queryConnection(projectId, clusterName);
switch (state) {
case "connected": {
return {
content: [
{
type: "text",
text: `Connected to cluster "${clusterName}".`,
},
],
};
const content: CallToolResult["content"] = [
{
type: "text",
text: `Connected to cluster "${clusterName}".`,
},
];

// Add feedback about IP access list if it was updated
if (ipAccessListUpdated) {
content.push({
type: "text",
text: `Note: Your current IP address has been added to the Atlas project's IP access list to enable secure connection.`,
});
}

return { content };
}
case "connecting":
case "unknown": {
Expand All @@ -212,8 +222,12 @@ export class ConnectClusterTool extends AtlasToolBase {
case "disconnected":
default: {
await this.session.disconnect();
const { connectionString, atlas } = await this.prepareClusterConnection(projectId, clusterName);
const { connectionString, atlas, userCreated } = await this.prepareClusterConnection(
projectId,
clusterName
);

createdUser = userCreated;
// try to connect for about 5 minutes asynchronously
void this.connectToCluster(connectionString, atlas).catch((err: unknown) => {
const error = err instanceof Error ? err : new Error(String(err));
Expand All @@ -230,21 +244,31 @@ export class ConnectClusterTool extends AtlasToolBase {
await sleep(500);
}

return {
content: [
{
type: "text" as const,
text: `Attempting to connect to cluster "${clusterName}"...`,
},
{
type: "text" as const,
text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`,
},
{
type: "text" as const,
text: `Warning: Make sure your IP address was enabled in the allow list setting of the Atlas cluster.`,
},
],
};
const content: CallToolResult["content"] = [
{
type: "text" as const,
text: `Attempting to connect to cluster "${clusterName}"...`,
},
{
type: "text" as const,
text: `Warning: Provisioning a user and connecting to the cluster may take more time, please check again in a few seconds.`,
},
];

if (ipAccessListUpdated) {
content.push({
type: "text" as const,
text: `Note: Your current IP address has been added to the Atlas project's IP access list to enable secure connection.`,
});
}

if (createdUser) {
content.push({
type: "text" as const,
text: `Note: A temporary user has been created to enable secure connection to the cluster. For more information, see https://dochub.mongodb.org/core/mongodb-mcp-server-tools-considerations`,
});
}

return { content };
}
}
Loading