-
Notifications
You must be signed in to change notification settings - Fork 111
feat(atlas-local): Added Atlas Local List Deployments tool #520
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 all commits
4900b02
5c660da
9e4e3c5
3743557
f3e1064
e6b0d15
06da4c5
50637ea
da63baf
1fd7ae3
db2f049
c436204
558411c
c79cb48
e4fb282
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,7 +19,7 @@ import { | |
UnsubscribeRequestSchema, | ||
} from "@modelcontextprotocol/sdk/types.js"; | ||
import assert from "assert"; | ||
import type { ToolBase } from "./tools/tool.js"; | ||
import type { ToolBase, ToolConstructor } from "./tools/tool.js"; | ||
import { validateConnectionString } from "./helpers/connectionOptions.js"; | ||
import { packageInfo } from "./common/packageInfo.js"; | ||
import { type ConnectionErrorHandler } from "./common/connectionErrorHandler.js"; | ||
|
@@ -69,6 +69,9 @@ export class Server { | |
// TODO: Eventually we might want to make tools reactive too instead of relying on custom logic. | ||
this.registerTools(); | ||
|
||
// Atlas Local tools are optional and require async initialization | ||
void this.registerAtlasLocalTools(); | ||
|
||
// This is a workaround for an issue we've seen with some models, where they'll see that everything in the `arguments` | ||
// object is optional, and then not pass it at all. However, the MCP server expects the `arguments` object to be if | ||
// the tool accepts any arguments, even if they're all optional. | ||
|
@@ -197,8 +200,41 @@ export class Server { | |
this.telemetry.emitEvents([event]).catch(() => {}); | ||
} | ||
|
||
private async registerAtlasLocalTools(): Promise<void> { | ||
// If Atlas Local tools are disabled, don't attempt to connect to the client | ||
if (this.userConfig.disabledTools.includes("atlas-local")) { | ||
return; | ||
} | ||
|
||
try { | ||
// Import Atlas Local client asyncronously | ||
// This will fail on unsupported platforms | ||
const { Client: AtlasLocalClient } = await import("@mongodb-js-preview/atlas-local"); | ||
|
||
// Connect to Atlas Local client | ||
// This will fail if docker is not running | ||
const client = AtlasLocalClient.connect(); | ||
|
||
// Set Atlas Local client | ||
this.session.setAtlasLocalClient(client); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for streamable http, we'll have multiple sessions that will connect to the server and manage the local atlas env, do we think this is the best place to add set client or should we set it somewhere else? if you think we need a single client, you could check the transport file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is ok to have multiple instances of the client, it should not cause any issues |
||
|
||
// Register Atlas Local tools | ||
this.registerToolInstances(AtlasLocalTools); | ||
} catch (error) { | ||
console.warn( | ||
"Failed to initialize Atlas Local client, atlas-local tools will be disabled (error: ", | ||
error, | ||
")" | ||
); | ||
} | ||
} | ||
|
||
private registerTools(): void { | ||
for (const toolConstructor of [...AtlasTools, ...AtlasLocalTools, ...MongoDbTools]) { | ||
this.registerToolInstances([...AtlasTools, ...MongoDbTools]); | ||
} | ||
|
||
private registerToolInstances(tools: Array<ToolConstructor>): void { | ||
for (const toolConstructor of tools) { | ||
const tool = new toolConstructor(this.session, this.userConfig, this.telemetry); | ||
if (tool.register(this)) { | ||
this.tools.push(tool); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; | ||
import { AtlasLocalToolBase } from "../atlasLocalTool.js"; | ||
import type { OperationType } from "../../tool.js"; | ||
import { formatUntrustedData } from "../../tool.js"; | ||
import type { Deployment } from "@mongodb-js-preview/atlas-local"; | ||
import type { Client } from "@mongodb-js-preview/atlas-local"; | ||
|
||
export class ListDeploymentsTool extends AtlasLocalToolBase { | ||
public name = "atlas-local-list-deployments"; | ||
protected description = "List MongoDB Atlas local deployments"; | ||
public operationType: OperationType = "read"; | ||
protected argsShape = {}; | ||
|
||
protected async executeWithAtlasLocalClient(client: Client): Promise<CallToolResult> { | ||
// List the deployments | ||
const deployments = await client.listDeployments(); | ||
|
||
// Format the deployments | ||
return this.formatDeploymentsTable(deployments); | ||
} | ||
|
||
private formatDeploymentsTable(deployments: Deployment[]): CallToolResult { | ||
// Check if deployments are absent | ||
if (!deployments?.length) { | ||
return { | ||
content: [{ type: "text", text: "No deployments found." }], | ||
}; | ||
} | ||
|
||
// Turn the deployments into a markdown table | ||
const rows = deployments | ||
.map((deployment) => { | ||
return `${deployment.name || "Unknown"} | ${deployment.state} | ${deployment.mongodbVersion}`; | ||
}) | ||
.join("\n"); | ||
|
||
return { | ||
content: formatUntrustedData( | ||
`Found ${deployments.length} deployments:`, | ||
`Deployment Name | State | MongoDB Version | ||
----------------|----------------|---------------- | ||
${rows}` | ||
), | ||
}; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
export const AtlasLocalTools = []; | ||
import { ListDeploymentsTool } from "./read/listDeployments.js"; | ||
|
||
export const AtlasLocalTools = [ListDeploymentsTool]; |