From f1f10551cef88ed1466d26703424a245ae772c5d Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 11:28:17 +0200 Subject: [PATCH 1/9] chore: add hints --- package-lock.json | 8 ++++---- package.json | 2 +- src/common/atlas/apiClient.ts | 4 +++- src/tools/tool.ts | 23 ++++++++++++++++++++--- 4 files changed, 28 insertions(+), 9 deletions(-) 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 e4b40acc..35463463 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/tool.ts b/src/tools/tool.ts index 5e4fc1a3..06831ec2 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 { ToolAnnotationsSchema, type CallToolResult, type ToolAnnotations } from "@modelcontextprotocol/sdk/types.js"; import { Session } from "../session.js"; import logger, { LogId } from "../logger.js"; import { Telemetry } from "../telemetry/telemetry.js"; @@ -27,13 +27,17 @@ export abstract class ToolBase { protected abstract argsShape: ZodRawShape; + protected abstract annotations: ToolAnnotations; + protected abstract execute(...args: Parameters>): Promise; constructor( protected readonly session: Session, protected readonly config: UserConfig, protected readonly telemetry: Telemetry - ) {} + ) { + this.updateAnnotations(); + } public register(server: McpServer): void { if (!this.verifyAllowed()) { @@ -56,7 +60,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 @@ -132,6 +136,19 @@ export abstract class ToolBase { }; } + protected updateAnnotations() { + this.annotations: ToolAnnotationsSchema = { + description: this.description, + }; + if (this.operationType === "read") { + this.annotations.readOnlyHint = true; + } + + if (this.operationType == "delete") { + this.annotations.destructiveHint = true; + } + } + protected abstract resolveTelemetryMetadata( ...args: Parameters> ): TelemetryToolMetadata; From 4477c4db0d683faa1905a03f9143f3314a23dc94 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 11:51:18 +0200 Subject: [PATCH 2/9] update --- src/tools/mongodb/metadata/listDatabases.ts | 1 - src/tools/tool.ts | 34 ++++++++++----------- 2 files changed, 17 insertions(+), 18 deletions(-) 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 06831ec2..099b6d0d 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -27,7 +27,22 @@ export abstract class ToolBase { protected abstract argsShape: ZodRawShape; - protected abstract annotations: ToolAnnotations; + protected get annotations(): ToolAnnotations { + const annotations: ToolAnnotations = { + title: this.name, + description: this.description, + }; + + if (this.operationType === "read" || this.operationType === "metadata") { + annotations.readOnlyHint = true; + } + + if (this.operationType === "delete") { + annotations.destructiveHint = true; + } + + return annotations; + } protected abstract execute(...args: Parameters>): Promise; @@ -35,9 +50,7 @@ export abstract class ToolBase { protected readonly session: Session, protected readonly config: UserConfig, protected readonly telemetry: Telemetry - ) { - this.updateAnnotations(); - } + ) {} public register(server: McpServer): void { if (!this.verifyAllowed()) { @@ -136,19 +149,6 @@ export abstract class ToolBase { }; } - protected updateAnnotations() { - this.annotations: ToolAnnotationsSchema = { - description: this.description, - }; - if (this.operationType === "read") { - this.annotations.readOnlyHint = true; - } - - if (this.operationType == "delete") { - this.annotations.destructiveHint = true; - } - } - protected abstract resolveTelemetryMetadata( ...args: Parameters> ): TelemetryToolMetadata; From ea9a65e585f2d025f1a84b806667541d5827a82d Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 11:55:54 +0200 Subject: [PATCH 3/9] update --- src/tools/tool.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 099b6d0d..8cadb610 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -35,10 +35,9 @@ export abstract class ToolBase { if (this.operationType === "read" || this.operationType === "metadata") { annotations.readOnlyHint = true; - } - - if (this.operationType === "delete") { - annotations.destructiveHint = true; + annotations.destructiveHint = false; + } else { + annotations.destructiveHint = this.operationType === "delete"; } return annotations; From bafd491e134076d0ae38d7f7631a68dd02dd9407 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 12:01:23 +0200 Subject: [PATCH 4/9] check --- src/tools/tool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 8cadb610..0e90c398 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 { ToolAnnotationsSchema, type CallToolResult, type ToolAnnotations } 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"; From 4bb9ddc7866e538191fd15c44273d0b01ae24af9 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 12:17:14 +0200 Subject: [PATCH 5/9] add tests --- src/tools/tool.ts | 3 +++ tests/integration/helpers.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/tools/tool.ts b/src/tools/tool.ts index 0e90c398..c4d9086c 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -81,14 +81,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..61bd3d60 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -206,6 +206,9 @@ export function validateToolMetadata( expectDefined(tool); expect(tool.description).toBe(description); + expectDefined(tool.annotations); + // expect(tool.annotations.title).toBe(name); + expect(tool.annotations.description).toBe(description); const toolParameters = getParameters(tool); expect(toolParameters).toHaveLength(parameters.length); expect(toolParameters).toIncludeAllMembers(parameters); From b8833e0530000f3384a5c825a2d3ec34a2246d13 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 12:18:14 +0200 Subject: [PATCH 6/9] update --- tests/integration/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index 61bd3d60..10b19030 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -207,7 +207,7 @@ export function validateToolMetadata( expect(tool.description).toBe(description); expectDefined(tool.annotations); - // expect(tool.annotations.title).toBe(name); + expect(tool.annotations.title).toBe(name); expect(tool.annotations.description).toBe(description); const toolParameters = getParameters(tool); expect(toolParameters).toHaveLength(parameters.length); From 21c0fa30e720ef681219026876c2d7d7aa5e9fbd Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 12:39:57 +0200 Subject: [PATCH 7/9] address comment: use swithc --- src/tools/tool.ts | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tools/tool.ts b/src/tools/tool.ts index c4d9086c..d04fbda8 100644 --- a/src/tools/tool.ts +++ b/src/tools/tool.ts @@ -33,11 +33,23 @@ export abstract class ToolBase { description: this.description, }; - if (this.operationType === "read" || this.operationType === "metadata") { - annotations.readOnlyHint = true; - annotations.destructiveHint = false; - } else { - annotations.destructiveHint = this.operationType === "delete"; + 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; From f759cd304d3dede4d226bc4168b96bc56c530999 Mon Sep 17 00:00:00 2001 From: Bianca Lisle Date: Wed, 14 May 2025 12:47:35 +0200 Subject: [PATCH 8/9] validate annotations --- tests/integration/helpers.ts | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index 10b19030..8c35a93f 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -206,9 +206,7 @@ export function validateToolMetadata( expectDefined(tool); expect(tool.description).toBe(description); - expectDefined(tool.annotations); - expect(tool.annotations.title).toBe(name); - expect(tool.annotations.description).toBe(description); + validateToolAnnotations(tool, name, description); const toolParameters = getParameters(tool); expect(toolParameters).toHaveLength(parameters.length); expect(toolParameters).toIncludeAllMembers(parameters); @@ -243,3 +241,25 @@ export function expectDefined(arg: T): asserts arg is Exclude Date: Wed, 14 May 2025 12:48:13 +0200 Subject: [PATCH 9/9] update --- tests/integration/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/helpers.ts b/tests/integration/helpers.ts index 8c35a93f..e407e250 100644 --- a/tests/integration/helpers.ts +++ b/tests/integration/helpers.ts @@ -260,6 +260,6 @@ function validateToolAnnotations(tool: ToolInfo, name: string, description: stri case "create": case "update": expect(tool.annotations.readOnlyHint).toBe(false); - expect(tool.annotations.destructiveHint).toBe(false); + expect(tool.annotations.destructiveHint).toBe(false); } }