Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions src/server/mcp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1633,6 +1633,99 @@ describe("tool()", () => {
),
).rejects.toThrow(/Tool nonexistent-tool not found/);
});

/***
* Test: Tool Registration with _meta field
*/
test("should register tool with _meta field and include it in list response", async () => {
const mcpServer = new McpServer({
name: "test server",
version: "1.0",
});
const client = new Client({
name: "test client",
version: "1.0",
});

const metaData = {
author: "test-author",
version: "1.2.3",
category: "utility",
tags: ["test", "example"]
};

mcpServer.registerTool(
"test-with-meta",
{
description: "A tool with _meta field",
inputSchema: { name: z.string() },
_meta: metaData,
},
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}!` }]
})
);

const [clientTransport, serverTransport] =
InMemoryTransport.createLinkedPair();

await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);

const result = await client.request(
{ method: "tools/list" },
ListToolsResultSchema,
);

expect(result.tools).toHaveLength(1);
expect(result.tools[0].name).toBe("test-with-meta");
expect(result.tools[0].description).toBe("A tool with _meta field");
expect(result.tools[0]._meta).toEqual(metaData);
});

/***
* Test: Tool Registration without _meta field should have undefined _meta
*/
test("should register tool without _meta field and have undefined _meta in response", async () => {
const mcpServer = new McpServer({
name: "test server",
version: "1.0",
});
const client = new Client({
name: "test client",
version: "1.0",
});

mcpServer.registerTool(
"test-without-meta",
{
description: "A tool without _meta field",
inputSchema: { name: z.string() },
},
async ({ name }) => ({
content: [{ type: "text", text: `Hello, ${name}!` }]
})
);

const [clientTransport, serverTransport] =
InMemoryTransport.createLinkedPair();

await Promise.all([
client.connect(clientTransport),
mcpServer.server.connect(serverTransport),
]);

const result = await client.request(
{ method: "tools/list" },
ListToolsResultSchema,
);

expect(result.tools).toHaveLength(1);
expect(result.tools[0].name).toBe("test-without-meta");
expect(result.tools[0]._meta).toBeUndefined();
});
});

describe("resource()", () => {
Expand Down
12 changes: 10 additions & 2 deletions src/server/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ export class McpServer {
}) as Tool["inputSchema"])
: EMPTY_OBJECT_JSON_SCHEMA,
annotations: tool.annotations,
_meta: tool._meta,
};

if (tool.outputSchema) {
Expand Down Expand Up @@ -773,6 +774,7 @@ export class McpServer {
inputSchema: ZodRawShape | undefined,
outputSchema: ZodRawShape | undefined,
annotations: ToolAnnotations | undefined,
_meta: Record<string, unknown> | undefined,
callback: ToolCallback<ZodRawShape | undefined>
): RegisteredTool {
const registeredTool: RegisteredTool = {
Expand All @@ -783,6 +785,7 @@ export class McpServer {
outputSchema:
outputSchema === undefined ? undefined : z.object(outputSchema),
annotations,
_meta,
callback,
enabled: true,
disable: () => registeredTool.update({ enabled: false }),
Expand All @@ -798,6 +801,7 @@ export class McpServer {
if (typeof updates.paramsSchema !== "undefined") registeredTool.inputSchema = z.object(updates.paramsSchema)
if (typeof updates.callback !== "undefined") registeredTool.callback = updates.callback
if (typeof updates.annotations !== "undefined") registeredTool.annotations = updates.annotations
if (typeof updates._meta !== "undefined") registeredTool._meta = updates._meta
if (typeof updates.enabled !== "undefined") registeredTool.enabled = updates.enabled
this.sendToolListChanged()
},
Expand Down Expand Up @@ -915,7 +919,7 @@ export class McpServer {
}
const callback = rest[0] as ToolCallback<ZodRawShape | undefined>;

return this._createRegisteredTool(name, undefined, description, inputSchema, outputSchema, annotations, callback)
return this._createRegisteredTool(name, undefined, description, inputSchema, outputSchema, annotations, undefined, callback)
}

/**
Expand All @@ -929,14 +933,15 @@ export class McpServer {
inputSchema?: InputArgs;
outputSchema?: OutputArgs;
annotations?: ToolAnnotations;
_meta?: Record<string, unknown>;
},
cb: ToolCallback<InputArgs>
): RegisteredTool {
if (this._registeredTools[name]) {
throw new Error(`Tool ${name} is already registered`);
}

const { title, description, inputSchema, outputSchema, annotations } = config;
const { title, description, inputSchema, outputSchema, annotations, _meta } = config;

return this._createRegisteredTool(
name,
Expand All @@ -945,6 +950,7 @@ export class McpServer {
inputSchema,
outputSchema,
annotations,
_meta,
cb as ToolCallback<ZodRawShape | undefined>
);
}
Expand Down Expand Up @@ -1173,6 +1179,7 @@ export type RegisteredTool = {
inputSchema?: AnyZodObject;
outputSchema?: AnyZodObject;
annotations?: ToolAnnotations;
_meta?: Record<string, unknown>;
callback: ToolCallback<undefined | ZodRawShape>;
enabled: boolean;
enable(): void;
Expand All @@ -1185,6 +1192,7 @@ export type RegisteredTool = {
paramsSchema?: InputArgs,
outputSchema?: OutputArgs,
annotations?: ToolAnnotations,
_meta?: Record<string, unknown>,
callback?: ToolCallback<InputArgs>,
enabled?: boolean
}): void
Expand Down