diff --git a/package-lock.json b/package-lock.json index 229e3081..8ae6845a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -75,7 +75,7 @@ "node": "^20.19.0 || ^22.12.0 || >= 23.0.0" }, "optionalDependencies": { - "@mongodb-js-preview/atlas-local": "^0.0.0-preview.1", + "@mongodb-js-preview/atlas-local": "^0.0.0-preview.2", "kerberos": "^2.2.2" } }, @@ -2050,26 +2050,26 @@ } }, "node_modules/@mongodb-js-preview/atlas-local": { - "version": "0.0.0-preview.1", - "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local/-/atlas-local-0.0.0-preview.1.tgz", - "integrity": "sha512-py3roloK+dyq9bCU139f3JdFykige1kWwUli9qWE4daODFdJ0mvQPN1EChw3lzI4rv53cX1CvApbh20liOukoQ==", + "version": "0.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local/-/atlas-local-0.0.0-preview.2.tgz", + "integrity": "sha512-gDU+xL3p//aSfGqjr3Zth8rlfjXXiu8D9+K7Q8s4Z83+0V0TciT7x4hVQYYtVdsny4MxHokTr/6PnG83z0yHIw==", "license": "Apache-2.0", "optional": true, "engines": { "node": ">= 12.22.0 < 13 || >= 14.17.0 < 15 || >= 15.12.0 < 16 || >= 16.0.0" }, "optionalDependencies": { - "@mongodb-js-preview/atlas-local-darwin-arm64": "0.0.0-preview.1", - "@mongodb-js-preview/atlas-local-darwin-x64": "0.0.0-preview.1", - "@mongodb-js-preview/atlas-local-linux-arm64-gnu": "0.0.0-preview.1", - "@mongodb-js-preview/atlas-local-linux-x64-gnu": "0.0.0-preview.1", - "@mongodb-js-preview/atlas-local-win32-x64-msvc": "0.0.0-preview.1" + "@mongodb-js-preview/atlas-local-darwin-arm64": "0.0.0-preview.2", + "@mongodb-js-preview/atlas-local-darwin-x64": "0.0.0-preview.2", + "@mongodb-js-preview/atlas-local-linux-arm64-gnu": "0.0.0-preview.2", + "@mongodb-js-preview/atlas-local-linux-x64-gnu": "0.0.0-preview.2", + "@mongodb-js-preview/atlas-local-win32-x64-msvc": "0.0.0-preview.2" } }, "node_modules/@mongodb-js-preview/atlas-local-darwin-arm64": { - "version": "0.0.0-preview.1", - "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-darwin-arm64/-/atlas-local-darwin-arm64-0.0.0-preview.1.tgz", - "integrity": "sha512-TcH7CFCg6pAx0KPhTUOyaZRwXOOTb5WCo9on12GqEk/oM+vERwfK5ztGSZns45IvxgN6zUsERoV+O6SEoK1gsA==", + "version": "0.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-darwin-arm64/-/atlas-local-darwin-arm64-0.0.0-preview.2.tgz", + "integrity": "sha512-aqiMeXCjawUYIP63y4buP+oRgg5jJ4g9HTOU7nMVZPO4aidLAMbIMgZVtolwplqofRlzxB9V6g4TiXS+Ksr8LA==", "cpu": [ "arm64" ], @@ -2083,9 +2083,9 @@ } }, "node_modules/@mongodb-js-preview/atlas-local-darwin-x64": { - "version": "0.0.0-preview.1", - "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-darwin-x64/-/atlas-local-darwin-x64-0.0.0-preview.1.tgz", - "integrity": "sha512-KmG+xKCS5f3adhznYH569mq0PHrFoGuqsGN5XEtVtUEYgv/gQicgJ0voWMrwTHu3jIFFQeGEvMgKsciyXAlVaQ==", + "version": "0.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-darwin-x64/-/atlas-local-darwin-x64-0.0.0-preview.2.tgz", + "integrity": "sha512-0wKAf+XddBHYqDJ9ofnXUKZhOKv2ruqv1Ev1M+mksIiX+b321yz3K2HCDRjYMaLva75QYxbBy2csBoxSUBwbmA==", "cpu": [ "x64" ], @@ -2099,9 +2099,9 @@ } }, "node_modules/@mongodb-js-preview/atlas-local-linux-arm64-gnu": { - "version": "0.0.0-preview.1", - "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-linux-arm64-gnu/-/atlas-local-linux-arm64-gnu-0.0.0-preview.1.tgz", - "integrity": "sha512-0ImE3RUdWiO38JWXiG6xAZzpz3CA2MHfpQdcwIomF0ldw/14ofRYkH31KX0M444j2rS2/AHBa+zdswYqFZCQbg==", + "version": "0.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-linux-arm64-gnu/-/atlas-local-linux-arm64-gnu-0.0.0-preview.2.tgz", + "integrity": "sha512-6qaA64ffmKbnjfDFo9s+jESSWOJO2v85HYlEdZlCj//7gWKFIN+5sDqCYHmSVlLUFGJAWSsQcJVhai2ojhQyjQ==", "cpu": [ "arm64" ], @@ -2115,9 +2115,9 @@ } }, "node_modules/@mongodb-js-preview/atlas-local-linux-x64-gnu": { - "version": "0.0.0-preview.1", - "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-linux-x64-gnu/-/atlas-local-linux-x64-gnu-0.0.0-preview.1.tgz", - "integrity": "sha512-AM+s8ygWU5gkNm6rDkLnWueOIon9T3kaeSleo4qgeAt4rgA7C9f2XUkQmFsv8b1E9g6CbNYrrbAAcnc0xVMtLQ==", + "version": "0.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-linux-x64-gnu/-/atlas-local-linux-x64-gnu-0.0.0-preview.2.tgz", + "integrity": "sha512-Y/AjlvP6rJqxByygycS0jtmNphsEjNcVFI2+uEFlY/QqU8I74RIYkFArJSuNJjv5vBh8/i+bw10gDwYlWVEuYA==", "cpu": [ "x64" ], @@ -2131,9 +2131,9 @@ } }, "node_modules/@mongodb-js-preview/atlas-local-win32-x64-msvc": { - "version": "0.0.0-preview.1", - "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-win32-x64-msvc/-/atlas-local-win32-x64-msvc-0.0.0-preview.1.tgz", - "integrity": "sha512-1oEsFgKz4Hatp+lD4pIB5EcrCqSwAx3p4FEJqwCKHbYGIsSVuvjhvN28hZHPHqkQhM9l8ZWFa8Z87/LR/Hy8FQ==", + "version": "0.0.0-preview.2", + "resolved": "https://registry.npmjs.org/@mongodb-js-preview/atlas-local-win32-x64-msvc/-/atlas-local-win32-x64-msvc-0.0.0-preview.2.tgz", + "integrity": "sha512-5xo3k+o/4m6P3CkMI8IPebVxdxVVSWwc+amMISuLq7DPFKmhU5m9gDiXfeg/tS67W1+HIHlUFeuCbcOpnIqRZQ==", "cpu": [ "x64" ], diff --git a/package.json b/package.json index 7246ec1d..352fb3dc 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,7 @@ "node": "^20.19.0 || ^22.12.0 || >= 23.0.0" }, "optionalDependencies": { - "@mongodb-js-preview/atlas-local": "^0.0.0-preview.1", + "@mongodb-js-preview/atlas-local": "^0.0.0-preview.2", "kerberos": "^2.2.2" } } diff --git a/src/tools/atlasLocal/atlasLocalTool.ts b/src/tools/atlasLocal/atlasLocalTool.ts index 8aca9d55..b67e83bd 100644 --- a/src/tools/atlasLocal/atlasLocalTool.ts +++ b/src/tools/atlasLocal/atlasLocalTool.ts @@ -11,7 +11,7 @@ export abstract class AtlasLocalToolBase extends ToolBase { return this.session.atlasLocalClient !== undefined && super.verifyAllowed(); } - protected async execute(): Promise { + protected async execute(...args: Parameters>): Promise { // Get the client const client = this.session.atlasLocalClient; @@ -35,10 +35,13 @@ please log a ticket here: https://github.com/mongodb-js/mongodb-mcp-server/issue }; } - return this.executeWithAtlasLocalClient(client); + return this.executeWithAtlasLocalClient(client, ...args); } - protected abstract executeWithAtlasLocalClient(client: Client): Promise; + protected abstract executeWithAtlasLocalClient( + client: Client, + ...args: Parameters> + ): Promise; protected handleError( error: unknown, diff --git a/src/tools/atlasLocal/delete/deleteDeployment.ts b/src/tools/atlasLocal/delete/deleteDeployment.ts new file mode 100644 index 00000000..6d0d6616 --- /dev/null +++ b/src/tools/atlasLocal/delete/deleteDeployment.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; +import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import { AtlasLocalToolBase } from "../atlasLocalTool.js"; +import type { OperationType, ToolArgs } from "../../tool.js"; +import type { Client } from "@mongodb-js-preview/atlas-local"; + +export class DeleteDeploymentTool extends AtlasLocalToolBase { + public name = "atlas-local-delete-deployment"; + protected description = "Delete a MongoDB Atlas local deployment"; + public operationType: OperationType = "delete"; + protected argsShape = { + deploymentName: z.string().describe("Name of the deployment to delete"), + }; + + protected async executeWithAtlasLocalClient( + client: Client, + { deploymentName }: ToolArgs + ): Promise { + // Delete the deployment + await client.deleteDeployment(deploymentName); + + return { + content: [{ type: "text", text: `Deployment "${deploymentName}" deleted successfully.` }], + }; + } +} diff --git a/src/tools/atlasLocal/tools.ts b/src/tools/atlasLocal/tools.ts index 6d8cf7a5..5284be1d 100644 --- a/src/tools/atlasLocal/tools.ts +++ b/src/tools/atlasLocal/tools.ts @@ -1,3 +1,4 @@ +import { DeleteDeploymentTool } from "./delete/deleteDeployment.js"; import { ListDeploymentsTool } from "./read/listDeployments.js"; -export const AtlasLocalTools = [ListDeploymentsTool]; +export const AtlasLocalTools = [ListDeploymentsTool, DeleteDeploymentTool]; diff --git a/tests/integration/tools/atlas-local/deleteDeployment.test.ts b/tests/integration/tools/atlas-local/deleteDeployment.test.ts new file mode 100644 index 00000000..87a30918 --- /dev/null +++ b/tests/integration/tools/atlas-local/deleteDeployment.test.ts @@ -0,0 +1,67 @@ +import { + defaultDriverOptions, + defaultTestConfig, + expectDefined, + getResponseElements, + setupIntegrationTest, + waitUntilMcpClientIsSet, +} from "../../helpers.js"; +import { describe, expect, it } from "vitest"; + +const isMacOSInGitHubActions = process.platform === "darwin" && process.env.GITHUB_ACTIONS === "true"; + +// Docker is not available on macOS in GitHub Actions +// That's why we skip the tests on macOS in GitHub Actions +describe("atlas-local-delete-deployment", () => { + const integration = setupIntegrationTest( + () => defaultTestConfig, + () => defaultDriverOptions + ); + + it.skipIf(isMacOSInGitHubActions)("should have the atlas-local-delete-deployment tool", async ({ signal }) => { + await waitUntilMcpClientIsSet(integration.mcpServer(), signal); + + const { tools } = await integration.mcpClient().listTools(); + const deleteDeployment = tools.find((tool) => tool.name === "atlas-local-delete-deployment"); + expectDefined(deleteDeployment); + }); + + it.skipIf(!isMacOSInGitHubActions)( + "[MacOS in GitHub Actions] should not have the atlas-local-delete-deployment tool", + async ({ signal }) => { + // This should throw an error because the client is not set within the timeout of 5 seconds (default) + await expect(waitUntilMcpClientIsSet(integration.mcpServer(), signal)).rejects.toThrow(); + + const { tools } = await integration.mcpClient().listTools(); + const deleteDeployment = tools.find((tool) => tool.name === "atlas-local-delete-deployment"); + expect(deleteDeployment).toBeUndefined(); + } + ); + + it.skipIf(isMacOSInGitHubActions)("should have correct metadata", async ({ signal }) => { + await waitUntilMcpClientIsSet(integration.mcpServer(), signal); + const { tools } = await integration.mcpClient().listTools(); + const deleteDeployment = tools.find((tool) => tool.name === "atlas-local-delete-deployment"); + expectDefined(deleteDeployment); + expect(deleteDeployment.inputSchema.type).toBe("object"); + expectDefined(deleteDeployment.inputSchema.properties); + expect(deleteDeployment.inputSchema.properties).toHaveProperty("deploymentName"); + }); + + it.skipIf(isMacOSInGitHubActions)( + "should return 'no such container' error when deployment to delete does not exist", + async ({ signal }) => { + await waitUntilMcpClientIsSet(integration.mcpServer(), signal); + + const response = await integration.mcpClient().callTool({ + name: "atlas-local-delete-deployment", + arguments: { deploymentName: "non-existent" }, + }); + const elements = getResponseElements(response.content); + expect(elements.length).toBeGreaterThanOrEqual(1); + expect(elements[0]?.text).toContain( + "Docker responded with status code 404: No such container: non-existent" + ); + } + ); +});