diff --git a/package-lock.json b/package-lock.json index 102519b0..e08faa28 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.1", "license": "Apache-2.0", "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", + "@modelcontextprotocol/sdk": "^1.11.2", "@mongodb-js/device-id": "^0.2.1", "@mongodb-js/devtools-connect": "^3.7.2", "@mongosh/service-provider-node-driver": "^3.6.0", @@ -2736,9 +2736,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.10.2.tgz", - "integrity": "sha512-rb6AMp2DR4SN+kc6L1ta2NCpApyA9WYNx3CrTSZvGxq9wH71bRur+zRqPfg0vQ9mjywR7qZdX2RGHOPq3ss+tA==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.2.tgz", + "integrity": "sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==", "license": "MIT", "dependencies": { "content-type": "^1.0.5", diff --git a/package.json b/package.json index 57b1fb3c..9ca3489e 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "yaml": "^2.7.1" }, "dependencies": { - "@modelcontextprotocol/sdk": "^1.8.0", + "@modelcontextprotocol/sdk": "^1.11.2", "@mongodb-js/device-id": "^0.2.1", "@mongodb-js/devtools-connect": "^3.7.2", "@mongosh/service-provider-node-driver": "^3.6.0", diff --git a/src/common/atlas/apiClient.ts b/src/common/atlas/apiClient.ts index 1a80be6a..1773aba5 100644 --- a/src/common/atlas/apiClient.ts +++ b/src/common/atlas/apiClient.ts @@ -34,7 +34,9 @@ export class ApiClient { private getAccessToken = async () => { if (this.oauth2Client && (!this.accessToken || this.accessToken.expired())) { - this.accessToken = await this.oauth2Client.getToken({}); + this.accessToken = await this.oauth2Client.getToken({ + agent: this.options.userAgent, + }); } return this.accessToken?.token.access_token as string | undefined; }; diff --git a/src/tools/mongodb/metadata/listDatabases.ts b/src/tools/mongodb/metadata/listDatabases.ts index ce943a69..fe324f07 100644 --- a/src/tools/mongodb/metadata/listDatabases.ts +++ b/src/tools/mongodb/metadata/listDatabases.ts @@ -7,7 +7,6 @@ export class ListDatabasesTool extends MongoDBToolBase { protected name = "list-databases"; protected description = "List all databases for a MongoDB connection"; protected argsShape = {}; - protected operationType: OperationType = "metadata"; protected async execute(): Promise { diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 5e4fc1a3..d04fbda8 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -1,6 +1,6 @@ import { z, type ZodRawShape, type ZodNever, AnyZodObject } from "zod"; import type { McpServer, RegisteredTool, ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js"; -import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; +import type { CallToolResult, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js"; import { Session } from "../session.js"; import logger, { LogId } from "../logger.js"; import { Telemetry } from "../telemetry/telemetry.js"; @@ -27,6 +27,34 @@ export abstract class ToolBase { protected abstract argsShape: ZodRawShape; + protected get annotations(): ToolAnnotations { + const annotations: ToolAnnotations = { + title: this.name, + description: this.description, + }; + + switch (this.operationType) { + case "read": + case "metadata": + annotations.readOnlyHint = true; + annotations.destructiveHint = false; + break; + case "delete": + annotations.readOnlyHint = false; + annotations.destructiveHint = true; + break; + case "create": + case "update": + annotations.destructiveHint = false; + annotations.readOnlyHint = false; + break; + default: + break; + } + + return annotations; + } + protected abstract execute(...args: Parameters>): Promise; constructor( @@ -56,7 +84,7 @@ export abstract class ToolBase { } }; - server.tool(this.name, this.description, this.argsShape, callback); + server.tool(this.name, this.description, this.argsShape, this.annotations, callback); // This is very similar to RegisteredTool.update, but without the bugs around the name. // In the upstream update method, the name is captured in the closure and not updated when @@ -65,14 +93,17 @@ export abstract class ToolBase { this.update = (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => { const tools = server["_registeredTools"] as { [toolName: string]: RegisteredTool }; const existingTool = tools[this.name]; + existingTool.annotations = this.annotations; if (updates.name && updates.name !== this.name) { + existingTool.annotations.title = updates.name; delete tools[this.name]; this.name = updates.name; tools[this.name] = existingTool; } if (updates.description) { + existingTool.annotations.description = updates.description; existingTool.description = updates.description; this.description = updates.description; } diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index 9d529376..e407e250 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -206,6 +206,7 @@ export function validateToolMetadata( expectDefined(tool); expect(tool.description).toBe(description); + validateToolAnnotations(tool, name, description); const toolParameters = getParameters(tool); expect(toolParameters).toHaveLength(parameters.length); expect(toolParameters).toIncludeAllMembers(parameters); @@ -240,3 +241,25 @@ export function expectDefined(arg: T): asserts arg is Exclude