Skip to content

Commit 6938a8f

Browse files
feat(atlas-local): Add Atlas Local List Deployments tool (#538)
1 parent 8a2db27 commit 6938a8f

File tree

6 files changed

+126
-29
lines changed

6 files changed

+126
-29
lines changed

package-lock.json

Lines changed: 24 additions & 24 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@
121121
"node": "^20.19.0 || ^22.12.0 || >= 23.0.0"
122122
},
123123
"optionalDependencies": {
124-
"@mongodb-js-preview/atlas-local": "^0.0.0-preview.1",
124+
"@mongodb-js-preview/atlas-local": "^0.0.0-preview.2",
125125
"kerberos": "^2.2.2"
126126
}
127127
}

src/tools/atlasLocal/atlasLocalTool.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export abstract class AtlasLocalToolBase extends ToolBase {
1111
return this.session.atlasLocalClient !== undefined && super.verifyAllowed();
1212
}
1313

14-
protected async execute(): Promise<CallToolResult> {
14+
protected async execute(...args: Parameters<ToolCallback<typeof this.argsShape>>): Promise<CallToolResult> {
1515
// Get the client
1616
const client = this.session.atlasLocalClient;
1717

@@ -35,10 +35,13 @@ please log a ticket here: https://github.com/mongodb-js/mongodb-mcp-server/issue
3535
};
3636
}
3737

38-
return this.executeWithAtlasLocalClient(client);
38+
return this.executeWithAtlasLocalClient(client, ...args);
3939
}
4040

41-
protected abstract executeWithAtlasLocalClient(client: Client): Promise<CallToolResult>;
41+
protected abstract executeWithAtlasLocalClient(
42+
client: Client,
43+
...args: Parameters<ToolCallback<typeof this.argsShape>>
44+
): Promise<CallToolResult>;
4245

4346
protected handleError(
4447
error: unknown,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { z } from "zod";
2+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3+
import { AtlasLocalToolBase } from "../atlasLocalTool.js";
4+
import type { OperationType, ToolArgs } from "../../tool.js";
5+
import type { Client } from "@mongodb-js-preview/atlas-local";
6+
7+
export class DeleteDeploymentTool extends AtlasLocalToolBase {
8+
public name = "atlas-local-delete-deployment";
9+
protected description = "Delete a MongoDB Atlas local deployment";
10+
public operationType: OperationType = "delete";
11+
protected argsShape = {
12+
deploymentName: z.string().describe("Name of the deployment to delete"),
13+
};
14+
15+
protected async executeWithAtlasLocalClient(
16+
client: Client,
17+
{ deploymentName }: ToolArgs<typeof this.argsShape>
18+
): Promise<CallToolResult> {
19+
// Delete the deployment
20+
await client.deleteDeployment(deploymentName);
21+
22+
return {
23+
content: [{ type: "text", text: `Deployment "${deploymentName}" deleted successfully.` }],
24+
};
25+
}
26+
}

src/tools/atlasLocal/tools.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { DeleteDeploymentTool } from "./delete/deleteDeployment.js";
12
import { ListDeploymentsTool } from "./read/listDeployments.js";
23

3-
export const AtlasLocalTools = [ListDeploymentsTool];
4+
export const AtlasLocalTools = [ListDeploymentsTool, DeleteDeploymentTool];
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {
2+
defaultDriverOptions,
3+
defaultTestConfig,
4+
expectDefined,
5+
getResponseElements,
6+
setupIntegrationTest,
7+
waitUntilMcpClientIsSet,
8+
} from "../../helpers.js";
9+
import { describe, expect, it } from "vitest";
10+
11+
const isMacOSInGitHubActions = process.platform === "darwin" && process.env.GITHUB_ACTIONS === "true";
12+
13+
// Docker is not available on macOS in GitHub Actions
14+
// That's why we skip the tests on macOS in GitHub Actions
15+
describe("atlas-local-delete-deployment", () => {
16+
const integration = setupIntegrationTest(
17+
() => defaultTestConfig,
18+
() => defaultDriverOptions
19+
);
20+
21+
it.skipIf(isMacOSInGitHubActions)("should have the atlas-local-delete-deployment tool", async ({ signal }) => {
22+
await waitUntilMcpClientIsSet(integration.mcpServer(), signal);
23+
24+
const { tools } = await integration.mcpClient().listTools();
25+
const deleteDeployment = tools.find((tool) => tool.name === "atlas-local-delete-deployment");
26+
expectDefined(deleteDeployment);
27+
});
28+
29+
it.skipIf(!isMacOSInGitHubActions)(
30+
"[MacOS in GitHub Actions] should not have the atlas-local-delete-deployment tool",
31+
async ({ signal }) => {
32+
// This should throw an error because the client is not set within the timeout of 5 seconds (default)
33+
await expect(waitUntilMcpClientIsSet(integration.mcpServer(), signal)).rejects.toThrow();
34+
35+
const { tools } = await integration.mcpClient().listTools();
36+
const deleteDeployment = tools.find((tool) => tool.name === "atlas-local-delete-deployment");
37+
expect(deleteDeployment).toBeUndefined();
38+
}
39+
);
40+
41+
it.skipIf(isMacOSInGitHubActions)("should have correct metadata", async ({ signal }) => {
42+
await waitUntilMcpClientIsSet(integration.mcpServer(), signal);
43+
const { tools } = await integration.mcpClient().listTools();
44+
const deleteDeployment = tools.find((tool) => tool.name === "atlas-local-delete-deployment");
45+
expectDefined(deleteDeployment);
46+
expect(deleteDeployment.inputSchema.type).toBe("object");
47+
expectDefined(deleteDeployment.inputSchema.properties);
48+
expect(deleteDeployment.inputSchema.properties).toHaveProperty("deploymentName");
49+
});
50+
51+
it.skipIf(isMacOSInGitHubActions)(
52+
"should return 'no such container' error when deployment to delete does not exist",
53+
async ({ signal }) => {
54+
await waitUntilMcpClientIsSet(integration.mcpServer(), signal);
55+
56+
const response = await integration.mcpClient().callTool({
57+
name: "atlas-local-delete-deployment",
58+
arguments: { deploymentName: "non-existent" },
59+
});
60+
const elements = getResponseElements(response.content);
61+
expect(elements.length).toBeGreaterThanOrEqual(1);
62+
expect(elements[0]?.text).toContain(
63+
"Docker responded with status code 404: No such container: non-existent"
64+
);
65+
}
66+
);
67+
});

0 commit comments

Comments
 (0)