-
Notifications
You must be signed in to change notification settings - Fork 129
feat(atlas-local): Adds Atlas Local Connect Deployment tool #612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
c7cf37b
0d0581d
0c53f48
ba884f8
975909e
9ad0257
fa6d616
a169d63
6bf04ce
74fe9fe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
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"; | ||
import { z } from "zod"; | ||
|
||
export class ConnectDeploymentTool extends AtlasLocalToolBase { | ||
public name = "atlas-local-connect-deployment"; | ||
protected description = "Connect to a MongoDB Atlas Local deployment"; | ||
public operationType: OperationType = "connect"; | ||
protected argsShape = { | ||
deploymentIdOrName: z.string().describe("Name or ID of the deployment to connect to"), | ||
himanshusinghs marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
}; | ||
|
||
protected async executeWithAtlasLocalClient( | ||
client: Client, | ||
{ deploymentIdOrName }: ToolArgs<typeof this.argsShape> | ||
kmruiz marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
): Promise<CallToolResult> { | ||
// Get the connection string for the deployment | ||
const connectionString = await client.getConnectionString(deploymentIdOrName); | ||
|
||
// Connect to the deployment | ||
await this.session.connectToMongoDB({ connectionString }); | ||
|
||
return { | ||
content: [ | ||
{ | ||
type: "text", | ||
text: `Successfully connected to Atlas Local deployment "${deploymentIdOrName}".`, | ||
}, | ||
], | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
import { DeleteDeploymentTool } from "./delete/deleteDeployment.js"; | ||
import { ListDeploymentsTool } from "./read/listDeployments.js"; | ||
import { CreateDeploymentTool } from "./create/createDeployment.js"; | ||
import { ConnectDeploymentTool } from "./connect/connectDeployment.js"; | ||
|
||
export const AtlasLocalTools = [ListDeploymentsTool, DeleteDeploymentTool, CreateDeploymentTool]; | ||
export const AtlasLocalTools = [ListDeploymentsTool, DeleteDeploymentTool, CreateDeploymentTool, ConnectDeploymentTool]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import { describeAccuracyTests } from "./sdk/describeAccuracyTests.js"; | ||
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; | ||
|
||
describeAccuracyTests([ | ||
{ | ||
prompt: "Connect to the local MongoDB cluster called 'my-database'", | ||
expectedToolCalls: [ | ||
{ | ||
toolName: "atlas-local-connect-deployment", | ||
parameters: { | ||
deploymentIdOrName: "my-database", | ||
}, | ||
}, | ||
], | ||
}, | ||
{ | ||
prompt: "Connect to the local MongoDB atlas database called 'my-instance'", | ||
expectedToolCalls: [ | ||
{ | ||
toolName: "atlas-local-connect-deployment", | ||
parameters: { | ||
deploymentIdOrName: "my-instance", | ||
}, | ||
}, | ||
], | ||
}, | ||
{ | ||
prompt: "If and only if, the local MongoDB deployment 'local-mflix' exists, then connect to it", | ||
kmruiz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
mockedTools: { | ||
"atlas-local-list-deployments": (): CallToolResult => ({ | ||
content: [ | ||
{ type: "text", text: "Found 1 deployment:" }, | ||
{ | ||
type: "text", | ||
text: "Deployment Name | State | MongoDB Version\n----------------|----------------|----------------\nlocal-mflix | Running | 6.0", | ||
}, | ||
], | ||
}), | ||
}, | ||
expectedToolCalls: [ | ||
{ | ||
toolName: "atlas-local-list-deployments", | ||
parameters: {}, | ||
}, | ||
{ | ||
toolName: "atlas-local-connect-deployment", | ||
parameters: { | ||
deploymentIdOrName: "local-mflix", | ||
}, | ||
}, | ||
], | ||
}, | ||
{ | ||
prompt: "Connect to a new local MongoDB cluster named 'local-mflix'", | ||
expectedToolCalls: [ | ||
{ | ||
toolName: "atlas-local-create-deployment", | ||
parameters: { | ||
deploymentName: "local-mflix", | ||
}, | ||
}, | ||
{ | ||
toolName: "atlas-local-connect-deployment", | ||
parameters: { | ||
deploymentIdOrName: "local-mflix", | ||
}, | ||
}, | ||
], | ||
}, | ||
]); |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,105 @@ | ||||||
import { | ||||||
defaultDriverOptions, | ||||||
defaultTestConfig, | ||||||
expectDefined, | ||||||
getResponseElements, | ||||||
setupIntegrationTest, | ||||||
waitUntilMcpClientIsSet, | ||||||
} from "../../helpers.js"; | ||||||
import { afterEach, 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-connect-deployment", () => { | ||||||
|
||||||
let deploymentNamesToCleanup: string[] = []; | ||||||
|
||||||
afterEach(async () => { | ||||||
// Clean up any deployments created during the test | ||||||
for (const deploymentName of deploymentNamesToCleanup) { | ||||||
try { | ||||||
await integration.mcpClient().callTool({ | ||||||
name: "atlas-local-delete-deployment", | ||||||
arguments: { deploymentName }, | ||||||
}); | ||||||
} catch (error) { | ||||||
console.warn(`Failed to delete deployment ${deploymentName}:`, error); | ||||||
} | ||||||
} | ||||||
deploymentNamesToCleanup = []; | ||||||
}); | ||||||
const integration = setupIntegrationTest( | ||||||
() => defaultTestConfig, | ||||||
() => defaultDriverOptions | ||||||
); | ||||||
|
||||||
it.skipIf(isMacOSInGitHubActions)("should have the atlas-local-connect-deployment tool", async ({ signal }) => { | ||||||
himanshusinghs marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||
await waitUntilMcpClientIsSet(integration.mcpServer(), signal); | ||||||
|
||||||
const { tools } = await integration.mcpClient().listTools(); | ||||||
const connectDeployment = tools.find((tool) => tool.name === "atlas-local-connect-deployment"); | ||||||
expectDefined(connectDeployment); | ||||||
}); | ||||||
|
||||||
it.skipIf(!isMacOSInGitHubActions)( | ||||||
"[MacOS in GitHub Actions] should not have the atlas-local-connect-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 connectDeployment = tools.find((tool) => tool.name === "atlas-local-connect-deployment"); | ||||||
expect(connectDeployment).toBeUndefined(); | ||||||
} | ||||||
); | ||||||
|
||||||
it.skipIf(isMacOSInGitHubActions)("should have correct metadata", async ({ signal }) => { | ||||||
await waitUntilMcpClientIsSet(integration.mcpServer(), signal); | ||||||
const { tools } = await integration.mcpClient().listTools(); | ||||||
const connectDeployment = tools.find((tool) => tool.name === "atlas-local-connect-deployment"); | ||||||
expectDefined(connectDeployment); | ||||||
expect(connectDeployment.inputSchema.type).toBe("object"); | ||||||
expectDefined(connectDeployment.inputSchema.properties); | ||||||
expect(connectDeployment.inputSchema.properties).toHaveProperty("deploymentIdOrName"); | ||||||
}); | ||||||
|
||||||
|
||||||
it.skipIf(isMacOSInGitHubActions)( | ||||||
"should return 'no such container' error when connecting to non-existent deployment", | ||||||
async ({ signal }) => { | ||||||
await waitUntilMcpClientIsSet(integration.mcpServer(), signal); | ||||||
|
||||||
const response = await integration.mcpClient().callTool({ | ||||||
name: "atlas-local-connect-deployment", | ||||||
arguments: { deploymentIdOrName: "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" | ||||||
|
||||||
); | ||||||
} | ||||||
); | ||||||
|
||||||
it.skipIf(isMacOSInGitHubActions)("should connect to a deployment when calling the tool", async ({ signal }) => { | ||||||
|
||||||
await waitUntilMcpClientIsSet(integration.mcpServer(), signal); | ||||||
// Create a deployment | ||||||
const deploymentName = `test-deployment-${Date.now()}`; | ||||||
deploymentNamesToCleanup.push(deploymentName); | ||||||
await integration.mcpClient().callTool({ | ||||||
name: "atlas-local-create-deployment", | ||||||
arguments: { deploymentName }, | ||||||
}); | ||||||
|
||||||
// Connect to the deployment | ||||||
const response = await integration.mcpClient().callTool({ | ||||||
name: "atlas-local-connect-deployment", | ||||||
arguments: { deploymentIdOrName: deploymentName }, | ||||||
}); | ||||||
const elements = getResponseElements(response.content); | ||||||
expect(elements.length).toBeGreaterThanOrEqual(1); | ||||||
expect(elements[0]?.text).toContain( | ||||||
'Successfully connected to Atlas Local deployment "' + deploymentName + '".' | ||||||
|
'Successfully connected to Atlas Local deployment "' + deploymentName + '".' | |
`Successfully connected to Atlas Local deployment "${deploymentName}".` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -239,7 +239,7 @@ describeWithAtlas("clusters", (integration) => { | |
"You need to connect to a MongoDB instance before you can access its data." | ||
); | ||
expect(elements[1]?.text).toContain( | ||
'Please use one of the following tools: "atlas-connect-cluster", "connect" to connect to a MongoDB instance' | ||
'Please use one of the following tools: "atlas-connect-cluster", "atlas-local-connect-deployment", "connect" to connect to a MongoDB instance' | ||
|
||
); | ||
}); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... I wonder if we want to be that prescriptive - my understanding is that this tool would be available as long as the user has docker installed, regardless of whether they're connecting to a local deployment or not. I guess we'd need to vibe check this in the real world.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nirinchev Thoughts on these actions?:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable to me!