Skip to content

Commit e07612f

Browse files
authored
Migrate tool registration and add annotation hints (#34)
* Migrate mcp tool registration to registerTool after the mcp sdk version update * Add hints for tool annotations: destructiveHint and idempotentHint * Bump mcp typescript sdk version to 0.1.4
1 parent 3b13144 commit e07612f

16 files changed

Lines changed: 2855 additions & 2921 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.DS_Store
22
.vscode
33
.idea
4-
.eslintcache
4+
.eslintcache
5+
.npmrc

modelcontextprotocol/package-lock.json

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

modelcontextprotocol/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
},
6161
"dependencies": {
6262
"@mastercard/developers-agent-toolkit": "0.1.3",
63-
"@modelcontextprotocol/sdk": "^1.17.0"
63+
"@modelcontextprotocol/sdk": "^1.27.1"
6464
},
6565
"lint-staged": {
6666
"*.{mjs,js,jsx,ts,tsx}": [

typescript/package-lock.json

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

typescript/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "0.1.3",
2+
"version": "0.1.4",
33
"name": "@mastercard/developers-agent-toolkit",
44
"homepage": "https://github.com/mastercard/developers-agent-toolkit",
55
"description": "Agent Toolkit for Mastercard Developers Platform",
@@ -57,9 +57,9 @@
5757
"typescript": "^5.8.3"
5858
},
5959
"dependencies": {
60-
"@modelcontextprotocol/sdk": "^1.17.0",
60+
"@modelcontextprotocol/sdk": "^1.27.1",
6161
"node-fetch": "^2.7.0",
62-
"zod": "^3.24.4"
62+
"zod": "^3.25.67"
6363
},
6464
"lint-staged": {
6565
"*.{mjs,js,jsx,ts,tsx}": [

typescript/src/modelcontextprotocol/__tests__/index.test.ts

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,107 @@
11
import { MastercardDevelopersAgentToolkit, buildContext } from '../';
2-
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
3+
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
34
import { tools } from '@/shared/tools';
45

5-
const mockTool = jest.spyOn(McpServer.prototype, 'tool');
6-
76
describe('MastercardDevelopersAgentToolkit', () => {
8-
afterEach(() => {
9-
jest.clearAllMocks();
10-
});
11-
12-
it('should register all tools with correct method/description when no config', () => {
13-
new MastercardDevelopersAgentToolkit({});
14-
7+
function expectDefined<T>(value: T | undefined, message: string): T {
8+
if (value === undefined) {
9+
throw new Error(message);
10+
}
11+
12+
return value;
13+
}
14+
15+
async function listRegisteredTools(
16+
config: ConstructorParameters<typeof MastercardDevelopersAgentToolkit>[0]
17+
) {
18+
const server = new MastercardDevelopersAgentToolkit(config);
19+
const client = new Client(
20+
{ name: 'test-client', version: '1.0.0' },
21+
{ capabilities: {} }
22+
);
23+
const [clientTransport, serverTransport] =
24+
InMemoryTransport.createLinkedPair();
25+
26+
await Promise.all([
27+
server.connect(serverTransport),
28+
client.connect(clientTransport),
29+
]);
30+
31+
try {
32+
const result = await client.listTools();
33+
return result.tools;
34+
} finally {
35+
await Promise.all([client.close(), server.close()]);
36+
}
37+
}
38+
39+
it('should list all tools with correct name/description when no config', async () => {
40+
const registeredTools = await listRegisteredTools({});
1541
const expectedTools = tools({});
16-
expect(mockTool).toHaveBeenCalledTimes(expectedTools.length);
42+
expect(registeredTools).toHaveLength(expectedTools.length);
1743

1844
expectedTools.forEach((expectedTool, index) => {
19-
const [method, description] = mockTool.mock.calls[index];
20-
expect(method).toBe(expectedTool.method);
21-
expect(description).toBe(expectedTool.description);
45+
const registeredTool = expectDefined(
46+
registeredTools[index],
47+
`Missing registered tool at index ${index}`
48+
);
49+
expect(registeredTool.name).toBe(expectedTool.name);
50+
expect(registeredTool.title).toBe(expectedTool.title);
51+
expect(registeredTool.description).toBe(expectedTool.description);
52+
expect(registeredTool.annotations).toEqual(expectedTool.annotations);
53+
expect(registeredTool.inputSchema).toBeDefined();
2254
});
2355
});
2456

25-
it('should register context-aware tools when apiSpecificationPath configured', () => {
26-
new MastercardDevelopersAgentToolkit({
57+
it('should list context-aware tools when apiSpecificationPath configured', async () => {
58+
const registeredTools = await listRegisteredTools({
2759
apiSpecification:
2860
'https://static.developer.mastercard.com/content/service/swagger/path.yaml',
2961
});
3062

3163
const expectedTools = tools({
3264
serviceId: 'service',
3365
apiSpecificationPath: '/service/swagger/path.yaml',
34-
}).filter((tool) => tool.method !== 'get-services-list');
35-
expect(mockTool).toHaveBeenCalledTimes(expectedTools.length);
66+
}).filter((tool) => tool.name !== 'get-services-list');
67+
expect(registeredTools).toHaveLength(expectedTools.length);
3668

3769
expectedTools.forEach((expectedTool, index) => {
38-
const [method, description] = mockTool.mock.calls[index];
39-
expect(method).toBe(expectedTool.method);
40-
expect(description).toBe(expectedTool.description);
70+
const registeredTool = expectDefined(
71+
registeredTools[index],
72+
`Missing registered tool at index ${index}`
73+
);
74+
expect(registeredTool.name).toBe(expectedTool.name);
75+
expect(registeredTool.title).toBe(expectedTool.title);
76+
expect(registeredTool.description).toBe(expectedTool.description);
77+
expect(registeredTool.annotations).toEqual(expectedTool.annotations);
78+
expect(registeredTool.inputSchema).toBeDefined();
4179
});
4280
});
4381

44-
it('should exclude get-services-list when serviceId configured', () => {
45-
new MastercardDevelopersAgentToolkit({
82+
it('should exclude get-services-list when serviceId configured', async () => {
83+
const registeredTools = await listRegisteredTools({
4684
service: 'https://developer.mastercard.com/test-service/documentation/',
4785
});
4886

4987
const expectedTools = tools({ serviceId: 'test-service' }).filter(
50-
(tool) => tool.method !== 'get-services-list'
88+
(tool) => tool.name !== 'get-services-list'
5189
);
52-
const registeredMethods = mockTool.mock.calls.map((call) => call[0]);
90+
const registeredNames = registeredTools.map((tool) => tool.name);
5391

54-
expect(registeredMethods).not.toContain('get-services-list');
55-
expect(mockTool).toHaveBeenCalledTimes(expectedTools.length);
92+
expect(registeredNames).not.toContain('get-services-list');
93+
expect(registeredTools).toHaveLength(expectedTools.length);
5694

5795
expectedTools.forEach((expectedTool, index) => {
58-
const [method, description] = mockTool.mock.calls[index];
59-
expect(method).toBe(expectedTool.method);
60-
expect(description).toBe(expectedTool.description);
96+
const registeredTool = expectDefined(
97+
registeredTools[index],
98+
`Missing registered tool at index ${index}`
99+
);
100+
expect(registeredTool.name).toBe(expectedTool.name);
101+
expect(registeredTool.title).toBe(expectedTool.title);
102+
expect(registeredTool.description).toBe(expectedTool.description);
103+
expect(registeredTool.annotations).toEqual(expectedTool.annotations);
104+
expect(registeredTool.inputSchema).toBeDefined();
61105
});
62106
});
63107
});

typescript/src/modelcontextprotocol/index.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ export class MastercardDevelopersAgentToolkit extends McpServer {
1313
super({
1414
name: 'mastercard-developers-mcp',
1515
version: version,
16-
capabilities: {
17-
tools: {},
18-
},
1916
});
2017

2118
this.registerAllTools(config);
@@ -27,18 +24,22 @@ export class MastercardDevelopersAgentToolkit extends McpServer {
2724
const availableTools = tools(context);
2825
const enabledTools = availableTools.filter((tool) => {
2926
// If serviceId is provided, disable the services list tool
30-
if (context.serviceId && tool.method === 'get-services-list') {
27+
if (context.serviceId && tool.name === 'get-services-list') {
3128
return false;
3229
}
3330

3431
return true;
3532
});
3633

3734
enabledTools.forEach((tool) => {
38-
this.tool(
39-
tool.method,
40-
tool.description,
41-
tool.parameters.shape,
35+
this.registerTool(
36+
tool.name,
37+
{
38+
title: tool.title,
39+
description: tool.description,
40+
inputSchema: tool.parameters,
41+
annotations: tool.annotations,
42+
},
4243
async (params: any) => {
4344
try {
4445
const result = await tool.execute(params);
@@ -47,9 +48,8 @@ export class MastercardDevelopersAgentToolkit extends McpServer {
4748
const message =
4849
error instanceof Error ? error.message : String(error);
4950
return {
50-
content: [
51-
{ type: 'text' as const, text: message, isError: true },
52-
],
51+
content: [{ type: 'text' as const, text: message }],
52+
isError: true,
5353
};
5454
}
5555
}

typescript/src/shared/tools/documentation/getDocumentation.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,15 @@ export const execute = async (
4242
};
4343

4444
export const getDocumentation = (context: ToolContext): Tool => ({
45-
method: 'get-documentation',
46-
name: 'Get Documentation',
45+
name: 'get-documentation',
46+
title: 'Get Documentation',
4747
description: getDescription(context),
4848
parameters: getParameters(context),
49+
annotations: {
50+
readOnlyHint: true,
51+
destructiveHint: false,
52+
idempotentHint: true,
53+
openWorldHint: true,
54+
},
4955
execute: (params) => execute(context, params),
5056
});

typescript/src/shared/tools/documentation/getDocumentationPage.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,15 @@ export const execute = async (
2828
};
2929

3030
export const getDocumentationPage = (context: ToolContext): Tool => ({
31-
method: 'get-documentation-page',
32-
name: 'Get Documentation Page',
31+
name: 'get-documentation-page',
32+
title: 'Get Documentation Page',
3333
description: getDescription(context),
3434
parameters: getParameters(context),
35+
annotations: {
36+
readOnlyHint: true,
37+
destructiveHint: false,
38+
idempotentHint: true,
39+
openWorldHint: true,
40+
},
3541
execute: (params) => execute(context, params),
3642
});

typescript/src/shared/tools/documentation/getDocumentationSection.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,15 @@ export const execute = async (
5959
};
6060

6161
export const getDocumentationSection = (context: ToolContext): Tool => ({
62-
method: 'get-documentation-section-content',
63-
name: 'Get Documentation Section Content',
62+
name: 'get-documentation-section-content',
63+
title: 'Get Documentation Section Content',
6464
description: getDescription(context),
6565
parameters: getParameters(context),
66+
annotations: {
67+
readOnlyHint: true,
68+
destructiveHint: false,
69+
idempotentHint: true,
70+
openWorldHint: true,
71+
},
6672
execute: (params) => execute(context, params),
6773
});

0 commit comments

Comments
 (0)