Skip to content

Commit d13f7c6

Browse files
authored
chore: add hints and update mcp (#249)
1 parent ab21838 commit d13f7c6

File tree

6 files changed

+64
-9
lines changed

6 files changed

+64
-9
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"yaml": "^2.7.1"
6161
},
6262
"dependencies": {
63-
"@modelcontextprotocol/sdk": "^1.8.0",
63+
"@modelcontextprotocol/sdk": "^1.11.2",
6464
"@mongodb-js/device-id": "^0.2.1",
6565
"@mongodb-js/devtools-connect": "^3.7.2",
6666
"@mongosh/service-provider-node-driver": "^3.6.0",

src/common/atlas/apiClient.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ export class ApiClient {
3434

3535
private getAccessToken = async () => {
3636
if (this.oauth2Client && (!this.accessToken || this.accessToken.expired())) {
37-
this.accessToken = await this.oauth2Client.getToken({});
37+
this.accessToken = await this.oauth2Client.getToken({
38+
agent: this.options.userAgent,
39+
});
3840
}
3941
return this.accessToken?.token.access_token as string | undefined;
4042
};

src/tools/mongodb/metadata/listDatabases.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export class ListDatabasesTool extends MongoDBToolBase {
77
protected name = "list-databases";
88
protected description = "List all databases for a MongoDB connection";
99
protected argsShape = {};
10-
1110
protected operationType: OperationType = "metadata";
1211

1312
protected async execute(): Promise<CallToolResult> {

src/tools/tool.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { z, type ZodRawShape, type ZodNever, AnyZodObject } from "zod";
22
import type { McpServer, RegisteredTool, ToolCallback } from "@modelcontextprotocol/sdk/server/mcp.js";
3-
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3+
import type { CallToolResult, ToolAnnotations } from "@modelcontextprotocol/sdk/types.js";
44
import { Session } from "../session.js";
55
import logger, { LogId } from "../logger.js";
66
import { Telemetry } from "../telemetry/telemetry.js";
@@ -27,6 +27,34 @@ export abstract class ToolBase {
2727

2828
protected abstract argsShape: ZodRawShape;
2929

30+
protected get annotations(): ToolAnnotations {
31+
const annotations: ToolAnnotations = {
32+
title: this.name,
33+
description: this.description,
34+
};
35+
36+
switch (this.operationType) {
37+
case "read":
38+
case "metadata":
39+
annotations.readOnlyHint = true;
40+
annotations.destructiveHint = false;
41+
break;
42+
case "delete":
43+
annotations.readOnlyHint = false;
44+
annotations.destructiveHint = true;
45+
break;
46+
case "create":
47+
case "update":
48+
annotations.destructiveHint = false;
49+
annotations.readOnlyHint = false;
50+
break;
51+
default:
52+
break;
53+
}
54+
55+
return annotations;
56+
}
57+
3058
protected abstract execute(...args: Parameters<ToolCallback<typeof this.argsShape>>): Promise<CallToolResult>;
3159

3260
constructor(
@@ -56,7 +84,7 @@ export abstract class ToolBase {
5684
}
5785
};
5886

59-
server.tool(this.name, this.description, this.argsShape, callback);
87+
server.tool(this.name, this.description, this.argsShape, this.annotations, callback);
6088

6189
// This is very similar to RegisteredTool.update, but without the bugs around the name.
6290
// In the upstream update method, the name is captured in the closure and not updated when
@@ -65,14 +93,17 @@ export abstract class ToolBase {
6593
this.update = (updates: { name?: string; description?: string; inputSchema?: AnyZodObject }) => {
6694
const tools = server["_registeredTools"] as { [toolName: string]: RegisteredTool };
6795
const existingTool = tools[this.name];
96+
existingTool.annotations = this.annotations;
6897

6998
if (updates.name && updates.name !== this.name) {
99+
existingTool.annotations.title = updates.name;
70100
delete tools[this.name];
71101
this.name = updates.name;
72102
tools[this.name] = existingTool;
73103
}
74104

75105
if (updates.description) {
106+
existingTool.annotations.description = updates.description;
76107
existingTool.description = updates.description;
77108
this.description = updates.description;
78109
}

tests/integration/helpers.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ export function validateToolMetadata(
206206
expectDefined(tool);
207207
expect(tool.description).toBe(description);
208208

209+
validateToolAnnotations(tool, name, description);
209210
const toolParameters = getParameters(tool);
210211
expect(toolParameters).toHaveLength(parameters.length);
211212
expect(toolParameters).toIncludeAllMembers(parameters);
@@ -240,3 +241,25 @@ export function expectDefined<T>(arg: T): asserts arg is Exclude<T, undefined |
240241
expect(arg).toBeDefined();
241242
expect(arg).not.toBeNull();
242243
}
244+
245+
function validateToolAnnotations(tool: ToolInfo, name: string, description: string): void {
246+
expectDefined(tool.annotations);
247+
expect(tool.annotations.title).toBe(name);
248+
expect(tool.annotations.description).toBe(description);
249+
250+
switch (tool.operationType) {
251+
case "read":
252+
case "metadata":
253+
expect(tool.annotations.readOnlyHint).toBe(true);
254+
expect(tool.annotations.destructiveHint).toBe(false);
255+
break;
256+
case "delete":
257+
expect(tool.annotations.readOnlyHint).toBe(false);
258+
expect(tool.annotations.destructiveHint).toBe(true);
259+
break;
260+
case "create":
261+
case "update":
262+
expect(tool.annotations.readOnlyHint).toBe(false);
263+
expect(tool.annotations.destructiveHint).toBe(false);
264+
}
265+
}

0 commit comments

Comments
 (0)