Skip to content

Commit 9ef7f4f

Browse files
Add icons field support to registerTool() and registerToolTask()
The MCP spec's ToolSchema includes icons via IconsSchema, but the high-level registerTool() and registerToolTask() APIs did not accept or pass through the icons field. This meant tools/list responses never included icons even when the spec supports them. Changes: - Add icons to RegisteredTool type and update() method - Add icons parameter to _createRegisteredTool() - Add icons to registerTool() and registerToolTask() config types - Include icons in toolDefinition for tools/list responses - Add test for tool registration with icons Fixes #1864
1 parent 7ba58da commit 9ef7f4f

3 files changed

Lines changed: 68 additions & 2 deletions

File tree

packages/server/src/experimental/tasks/mcpServer.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @experimental
66
*/
77

8-
import type { StandardSchemaWithJSON, TaskToolExecution, ToolAnnotations, ToolExecution } from '@modelcontextprotocol/core';
8+
import type { Icon, StandardSchemaWithJSON, TaskToolExecution, ToolAnnotations, ToolExecution } from '@modelcontextprotocol/core';
99

1010
import type { AnyToolHandler, McpServer, RegisteredTool } from '../../server/mcp.js';
1111
import type { ToolTaskHandler } from './interfaces.js';
@@ -23,6 +23,7 @@ interface McpServerInternal {
2323
outputSchema: StandardSchemaWithJSON | undefined,
2424
annotations: ToolAnnotations | undefined,
2525
execution: ToolExecution | undefined,
26+
icons: Icon[] | undefined,
2627
_meta: Record<string, unknown> | undefined,
2728
handler: AnyToolHandler<StandardSchemaWithJSON | undefined>
2829
): RegisteredTool;
@@ -83,6 +84,7 @@ export class ExperimentalMcpServerTasks {
8384
description?: string;
8485
outputSchema?: OutputArgs;
8586
annotations?: ToolAnnotations;
87+
icons?: Icon[];
8688
execution?: TaskToolExecution;
8789
_meta?: Record<string, unknown>;
8890
},
@@ -97,6 +99,7 @@ export class ExperimentalMcpServerTasks {
9799
inputSchema: InputArgs;
98100
outputSchema?: OutputArgs;
99101
annotations?: ToolAnnotations;
102+
icons?: Icon[];
100103
execution?: TaskToolExecution;
101104
_meta?: Record<string, unknown>;
102105
},
@@ -111,6 +114,7 @@ export class ExperimentalMcpServerTasks {
111114
inputSchema?: InputArgs;
112115
outputSchema?: OutputArgs;
113116
annotations?: ToolAnnotations;
117+
icons?: Icon[];
114118
execution?: TaskToolExecution;
115119
_meta?: Record<string, unknown>;
116120
},
@@ -132,6 +136,7 @@ export class ExperimentalMcpServerTasks {
132136
config.outputSchema,
133137
config.annotations,
134138
execution,
139+
config.icons,
135140
config._meta,
136141
handler as AnyToolHandler<StandardSchemaWithJSON | undefined>
137142
);

packages/server/src/server/mcp.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
CreateTaskResult,
99
CreateTaskServerContext,
1010
GetPromptResult,
11+
Icon,
1112
Implementation,
1213
ListPromptsResult,
1314
ListResourcesResult,
@@ -145,6 +146,7 @@ export class McpServer {
145146
? (standardSchemaToJsonSchema(tool.inputSchema, 'input') as Tool['inputSchema'])
146147
: EMPTY_OBJECT_JSON_SCHEMA,
147148
annotations: tool.annotations,
149+
icons: tool.icons,
148150
execution: tool.execution,
149151
_meta: tool._meta
150152
};
@@ -772,6 +774,7 @@ export class McpServer {
772774
outputSchema: StandardSchemaWithJSON | undefined,
773775
annotations: ToolAnnotations | undefined,
774776
execution: ToolExecution | undefined,
777+
icons: Icon[] | undefined,
775778
_meta: Record<string, unknown> | undefined,
776779
handler: AnyToolHandler<StandardSchemaWithJSON | undefined>
777780
): RegisteredTool {
@@ -788,6 +791,7 @@ export class McpServer {
788791
outputSchema,
789792
annotations,
790793
execution,
794+
icons,
791795
_meta,
792796
handler: handler,
793797
executor: createToolExecutor(inputSchema, handler),
@@ -822,6 +826,7 @@ export class McpServer {
822826
}
823827

824828
if (updates.outputSchema !== undefined) registeredTool.outputSchema = updates.outputSchema;
829+
if (updates.icons !== undefined) registeredTool.icons = updates.icons;
825830
if (updates.annotations !== undefined) registeredTool.annotations = updates.annotations;
826831
if (updates._meta !== undefined) registeredTool._meta = updates._meta;
827832
if (updates.enabled !== undefined) registeredTool.enabled = updates.enabled;
@@ -870,6 +875,7 @@ export class McpServer {
870875
inputSchema?: InputArgs;
871876
outputSchema?: OutputArgs;
872877
annotations?: ToolAnnotations;
878+
icons?: Icon[];
873879
_meta?: Record<string, unknown>;
874880
},
875881
cb: ToolCallback<InputArgs>
@@ -878,7 +884,7 @@ export class McpServer {
878884
throw new Error(`Tool ${name} is already registered`);
879885
}
880886

881-
const { title, description, inputSchema, outputSchema, annotations, _meta } = config;
887+
const { title, description, inputSchema, outputSchema, annotations, icons, _meta } = config;
882888

883889
return this._createRegisteredTool(
884890
name,
@@ -888,6 +894,7 @@ export class McpServer {
888894
outputSchema,
889895
annotations,
890896
{ taskSupport: 'forbidden' },
897+
icons,
891898
_meta,
892899
cb as ToolCallback<StandardSchemaWithJSON | undefined>
893900
);
@@ -1095,6 +1102,7 @@ export type RegisteredTool = {
10951102
inputSchema?: StandardSchemaWithJSON;
10961103
outputSchema?: StandardSchemaWithJSON;
10971104
annotations?: ToolAnnotations;
1105+
icons?: Icon[];
10981106
execution?: ToolExecution;
10991107
_meta?: Record<string, unknown>;
11001108
handler: AnyToolHandler<StandardSchemaWithJSON | undefined>;
@@ -1110,6 +1118,7 @@ export type RegisteredTool = {
11101118
paramsSchema?: StandardSchemaWithJSON;
11111119
outputSchema?: StandardSchemaWithJSON;
11121120
annotations?: ToolAnnotations;
1121+
icons?: Icon[];
11131122
_meta?: Record<string, unknown>;
11141123
callback?: ToolCallback<StandardSchemaWithJSON>;
11151124
enabled?: boolean;

test/integration/test/server/mcp.test.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,58 @@ describe('Zod v4', () => {
10381038
});
10391039
});
10401040

1041+
/***
1042+
* Test: Tool Registration with Icons
1043+
*/
1044+
test('should register tool with icons', async () => {
1045+
const mcpServer = new McpServer({
1046+
name: 'test server',
1047+
version: '1.0'
1048+
});
1049+
const client = new Client({
1050+
name: 'test client',
1051+
version: '1.0'
1052+
});
1053+
1054+
mcpServer.registerTool(
1055+
'test',
1056+
{
1057+
description: 'A tool with icons',
1058+
icons: [
1059+
{
1060+
src: 'https://example.com/icon.png',
1061+
mimeType: 'image/png'
1062+
}
1063+
]
1064+
},
1065+
async () => ({
1066+
content: [
1067+
{
1068+
type: 'text',
1069+
text: 'Test response'
1070+
}
1071+
]
1072+
})
1073+
);
1074+
1075+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
1076+
1077+
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
1078+
1079+
const result = await client.request({
1080+
method: 'tools/list'
1081+
});
1082+
1083+
expect(result.tools).toHaveLength(1);
1084+
expect(result.tools[0]!.name).toBe('test');
1085+
expect(result.tools[0]!.icons).toEqual([
1086+
{
1087+
src: 'https://example.com/icon.png',
1088+
mimeType: 'image/png'
1089+
}
1090+
]);
1091+
});
1092+
10411093
/***
10421094
* Test: Tool Registration with Parameters and Annotations
10431095
*/

0 commit comments

Comments
 (0)