Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
31 changes: 25 additions & 6 deletions src/common/atlas/cluster.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { ClusterDescription20240805, FlexClusterDescription20241113 } from "./openapi.js";
import type {
ClusterConnectionStrings,
ClusterDescription20240805,
FlexClusterDescription20241113,
} from "./openapi.js";
import type { ApiClient } from "./apiClient.js";
import { LogId } from "../logger.js";
import { ConnectionString } from "mongodb-connection-string-url";
Expand All @@ -18,7 +22,8 @@ export interface Cluster {
instanceSize?: string;
state?: "IDLE" | "CREATING" | "UPDATING" | "DELETING" | "REPAIRING";
mongoDBVersion?: string;
connectionString?: string;
standardConnectionString?: string;
connectionStrings?: ClusterConnectionStrings;
processIds?: Array<string>;
}

Expand All @@ -30,7 +35,8 @@ export function formatFlexCluster(cluster: FlexClusterDescription20241113): Clus
instanceSize: undefined,
state: cluster.stateName,
mongoDBVersion: cluster.mongoDBVersion,
connectionString,
standardConnectionString: connectionString,
connectionStrings: cluster.connectionStrings,
processIds: extractProcessIds(cluster.connectionStrings?.standard ?? ""),
};
}
Expand Down Expand Up @@ -65,19 +71,32 @@ export function formatCluster(cluster: ClusterDescription20240805): Cluster {

const instanceSize = regionConfigs[0]?.instanceSize ?? "UNKNOWN";
const clusterInstanceType = instanceSize === "M0" ? "FREE" : "DEDICATED";
const connectionString = cluster.connectionStrings?.standardSrv || cluster.connectionStrings?.standard;

const standardConnectionString = cluster.connectionStrings?.standardSrv || cluster.connectionStrings?.standard;
return {
name: cluster.name,
instanceType: clusterInstanceType,
instanceSize: clusterInstanceType === "DEDICATED" ? instanceSize : undefined,
state: cluster.stateName,
mongoDBVersion: cluster.mongoDBVersion,
connectionString,
standardConnectionString: standardConnectionString,
connectionStrings: cluster.connectionStrings,
processIds: extractProcessIds(cluster.connectionStrings?.standard ?? ""),
};
}

export function getConnectionString(
connectionStrings: ClusterConnectionStrings,
connectionType: "standard" | "private"
): string | undefined {
if (connectionStrings === undefined) {
return undefined;
}
if (connectionType === "standard") {
return connectionStrings.standardSrv || connectionStrings.standard;
}
return connectionStrings.privateSrv || connectionStrings.private;
}

export async function inspectCluster(apiClient: ApiClient, projectId: string, clusterName: string): Promise<Cluster> {
try {
const cluster = await apiClient.getCluster({
Expand Down
6 changes: 6 additions & 0 deletions src/tools/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ export const AtlasArgs = {

password: (): z.ZodString =>
z.string().min(1, "Password is required").max(100, "Password must be 100 characters or less"),

connectionType: (): z.ZodDefault<z.ZodEnum<["standard", "private"]>> =>
z
.enum(["standard", "private"])
.default("standard")
.describe("Desired connection type (standard or private) to an Atlas cluster"),
};

function toEJSON<T extends object | undefined>(value: T): T {
Expand Down
28 changes: 21 additions & 7 deletions src/tools/atlas/connect/connectCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import type { AtlasClusterConnectionInfo } from "../../../common/connectionManager.js";
import { getDefaultRoleFromConfig } from "../../../common/atlas/roles.js";
import { AtlasArgs } from "../../args.js";
import { getConnectionString } from "../../../common/atlas/cluster.js";

const addedIpAccessListMessage =
"Note: Your current IP address has been added to the Atlas project's IP access list to enable secure connection.";
Expand All @@ -22,6 +23,7 @@
export const ConnectClusterArgs = {
projectId: AtlasArgs.projectId().describe("Atlas project ID"),
clusterName: AtlasArgs.clusterName().describe("Atlas cluster name"),
connectionType: AtlasArgs.connectionType().optional(),
};

export class ConnectClusterTool extends AtlasToolBase {
Expand Down Expand Up @@ -69,12 +71,16 @@

private async prepareClusterConnection(
projectId: string,
clusterName: string
clusterName: string,
connectionType: "standard" | "private"
): Promise<{ connectionString: string; atlas: AtlasClusterConnectionInfo }> {
const cluster = await inspectCluster(this.session.apiClient, projectId, clusterName);

if (!cluster.connectionString) {
throw new Error("Connection string not available");
if (cluster.connectionStrings === undefined) {
throw new Error("Connection strings not available");
}
const connectionString = getConnectionString(cluster.connectionStrings, connectionType);
if (connectionString === undefined) {
throw new Error("Connection string not available for connection type: " + connectionType);
}

const username = `mcpUser${Math.floor(Math.random() * 100000)}`;
Expand Down Expand Up @@ -113,7 +119,7 @@
expiryDate,
};

const cn = new URL(cluster.connectionString);
const cn = new URL(connectionString);
cn.username = username;
cn.password = password;
cn.searchParams.set("authSource", "admin");
Expand Down Expand Up @@ -200,7 +206,11 @@
});
}

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

Expand Down Expand Up @@ -239,7 +249,11 @@
case "disconnected":
default: {
await this.session.disconnect();
const { connectionString, atlas } = await this.prepareClusterConnection(projectId, clusterName);
const { connectionString, atlas } = await this.prepareClusterConnection(
projectId,
clusterName,
connectionType

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / check-style

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / check-generate

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / Check dependencies

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / Run Atlas tests

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / Run MongoDB tests (ubuntu-latest)

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / Run MongoDB tests (windows-latest)

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / Run MongoDB tests (macos-latest)

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.

Check failure on line 255 in src/tools/atlas/connect/connectCluster.ts

View workflow job for this annotation

GitHub Actions / Report Coverage

Argument of type '"standard" | "private" | undefined' is not assignable to parameter of type '"standard" | "private"'.
);

createdUser = true;
// try to connect for about 5 minutes asynchronously
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/read/inspectCluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class InspectClusterTool extends AtlasToolBase {
"Cluster details:",
`Cluster Name | Cluster Type | Tier | State | MongoDB Version | Connection String
----------------|----------------|----------------|----------------|----------------|----------------
${formattedCluster.name || "Unknown"} | ${formattedCluster.instanceType} | ${formattedCluster.instanceSize || "N/A"} | ${formattedCluster.state || "UNKNOWN"} | ${formattedCluster.mongoDBVersion || "N/A"} | ${formattedCluster.connectionString || "N/A"}`
${formattedCluster.name || "Unknown"} | ${formattedCluster.instanceType} | ${formattedCluster.instanceSize || "N/A"} | ${formattedCluster.state || "UNKNOWN"} | ${formattedCluster.mongoDBVersion || "N/A"} | ${formattedCluster.standardConnectionString || "N/A"}`
),
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/atlas/read/listClusters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ ${rows}`,
----------------|----------------|----------------|----------------|----------------|----------------
${allClusters
.map((formattedCluster) => {
return `${formattedCluster.name || "Unknown"} | ${formattedCluster.instanceType} | ${formattedCluster.instanceSize || "N/A"} | ${formattedCluster.state || "UNKNOWN"} | ${formattedCluster.mongoDBVersion || "N/A"} | ${formattedCluster.connectionString || "N/A"}`;
return `${formattedCluster.name || "Unknown"} | ${formattedCluster.instanceType} | ${formattedCluster.instanceSize || "N/A"} | ${formattedCluster.state || "UNKNOWN"} | ${formattedCluster.mongoDBVersion || "N/A"} | ${formattedCluster.standardConnectionString || "N/A"}`;
})
.join("\n")}`
),
Expand Down
Loading