Skip to content

Commit 4a6b4de

Browse files
feat(compat): restore McpServer.tool/.prompt/.resource variadic overloads as deprecated shims
1 parent a33c305 commit 4a6b4de

File tree

5 files changed

+253
-2
lines changed

5 files changed

+253
-2
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@modelcontextprotocol/server': patch
3+
---
4+
5+
Restore `McpServer.tool()`, `.prompt()`, `.resource()` variadic overloads as `@deprecated` v1-compat shims forwarding to `registerTool`/`registerPrompt`/`registerResource`. Emits a one-time deprecation warning; removed in v3.

packages/core/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ export * from './validators/fromJsonSchema.js';
4848
*/
4949

5050
// Core types only - implementations are exported via separate entry points
51+
export { _resetDeprecationWarnings, deprecate } from './util/deprecate.js';
5152
export type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator, JsonSchemaValidatorResult } from './validators/types.js';
52-
export { deprecate } from './util/deprecate.js';

packages/server/src/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ export type {
1212
AnyToolHandler,
1313
BaseToolCallback,
1414
CompleteResourceTemplateCallback,
15+
// eslint-disable-next-line @typescript-eslint/no-deprecated
16+
LegacyPromptCallback,
17+
// eslint-disable-next-line @typescript-eslint/no-deprecated
18+
LegacyToolCallback,
1519
ListResourcesCallback,
1620
PromptCallback,
1721
ReadResourceCallback,
@@ -21,7 +25,9 @@ export type {
2125
RegisteredResourceTemplate,
2226
RegisteredTool,
2327
ResourceMetadata,
24-
ToolCallback
28+
ToolCallback,
29+
// eslint-disable-next-line @typescript-eslint/no-deprecated
30+
ZodRawShape
2531
} from './server/mcp.js';
2632
export { McpServer, ResourceTemplate } from './server/mcp.js';
2733
export type { HostHeaderValidationResult } from './server/middleware/hostHeaderValidation.js';

packages/server/src/server/mcp.ts

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import type {
3030
import {
3131
assertCompleteRequestPrompt,
3232
assertCompleteRequestResourceTemplate,
33+
isStandardSchema,
3334
promptArgumentsFromStandardSchema,
3435
ProtocolError,
3536
ProtocolErrorCode,
@@ -38,6 +39,7 @@ import {
3839
validateAndWarnToolName,
3940
validateStandardSchema
4041
} from '@modelcontextprotocol/core';
42+
import { z } from 'zod';
4143

4244
import type { ToolTaskHandler } from '../experimental/tasks/interfaces.js';
4345
import { ExperimentalMcpServerTasks } from '../experimental/tasks/mcpServer.js';
@@ -950,6 +952,132 @@ export class McpServer {
950952
return registeredPrompt;
951953
}
952954

955+
// ---------------------------------------------------------------------
956+
// v1-compat variadic registration methods. Frozen at 2025-03-26 surface.
957+
// ---------------------------------------------------------------------
958+
959+
/** @deprecated Use {@linkcode registerTool}. */
960+
tool(name: string, cb: LegacyToolCallback<undefined>): RegisteredTool;
961+
/** @deprecated Use {@linkcode registerTool}. */
962+
tool(name: string, description: string, cb: LegacyToolCallback<undefined>): RegisteredTool;
963+
/** @deprecated Use {@linkcode registerTool}. */
964+
tool<Args extends ZodRawShape>(
965+
name: string,
966+
paramsSchemaOrAnnotations: Args | ToolAnnotations,
967+
cb: LegacyToolCallback<Args>
968+
): RegisteredTool;
969+
/** @deprecated Use {@linkcode registerTool}. */
970+
tool<Args extends ZodRawShape>(
971+
name: string,
972+
description: string,
973+
paramsSchemaOrAnnotations: Args | ToolAnnotations,
974+
cb: LegacyToolCallback<Args>
975+
): RegisteredTool;
976+
/** @deprecated Use {@linkcode registerTool}. */
977+
tool<Args extends ZodRawShape>(
978+
name: string,
979+
paramsSchema: Args,
980+
annotations: ToolAnnotations,
981+
cb: LegacyToolCallback<Args>
982+
): RegisteredTool;
983+
/** @deprecated Use {@linkcode registerTool}. */
984+
tool<Args extends ZodRawShape>(
985+
name: string,
986+
description: string,
987+
paramsSchema: Args,
988+
annotations: ToolAnnotations,
989+
cb: LegacyToolCallback<Args>
990+
): RegisteredTool;
991+
tool(name: string, ...rest: unknown[]): RegisteredTool {
992+
let description: string | undefined;
993+
let inputSchema: StandardSchemaWithJSON | undefined;
994+
let annotations: ToolAnnotations | undefined;
995+
996+
if (typeof rest[0] === 'string') description = rest.shift() as string;
997+
998+
if (rest.length > 1) {
999+
const first = rest[0];
1000+
if (isZodRawShape(first) || isStandardSchema(first)) {
1001+
inputSchema = wrapRawShape(rest.shift());
1002+
if (
1003+
rest.length > 1 &&
1004+
typeof rest[0] === 'object' &&
1005+
rest[0] !== null &&
1006+
!isZodRawShape(rest[0]) &&
1007+
!isStandardSchema(rest[0])
1008+
) {
1009+
annotations = rest.shift() as ToolAnnotations;
1010+
}
1011+
} else if (typeof first === 'object' && first !== null) {
1012+
annotations = rest.shift() as ToolAnnotations;
1013+
}
1014+
}
1015+
1016+
if (this._registeredTools[name]) {
1017+
throw new Error(`Tool ${name} is already registered`);
1018+
}
1019+
const cb = rest[0] as ToolCallback<StandardSchemaWithJSON | undefined>;
1020+
return this._createRegisteredTool(
1021+
name,
1022+
undefined,
1023+
description,
1024+
inputSchema,
1025+
undefined,
1026+
annotations,
1027+
{ taskSupport: 'forbidden' },
1028+
undefined,
1029+
cb
1030+
);
1031+
}
1032+
1033+
/** @deprecated Use {@linkcode registerPrompt}. */
1034+
prompt(name: string, cb: PromptCallback): RegisteredPrompt;
1035+
/** @deprecated Use {@linkcode registerPrompt}. */
1036+
prompt(name: string, description: string, cb: PromptCallback): RegisteredPrompt;
1037+
/** @deprecated Use {@linkcode registerPrompt}. */
1038+
prompt<Args extends ZodRawShape>(name: string, argsSchema: Args, cb: LegacyPromptCallback<Args>): RegisteredPrompt;
1039+
/** @deprecated Use {@linkcode registerPrompt}. */
1040+
prompt<Args extends ZodRawShape>(name: string, description: string, argsSchema: Args, cb: LegacyPromptCallback<Args>): RegisteredPrompt;
1041+
prompt(name: string, ...rest: unknown[]): RegisteredPrompt {
1042+
let description: string | undefined;
1043+
if (typeof rest[0] === 'string') description = rest.shift() as string;
1044+
1045+
let argsSchema: StandardSchemaWithJSON | undefined;
1046+
if (rest.length > 1) argsSchema = wrapRawShape(rest.shift());
1047+
1048+
if (this._registeredPrompts[name]) {
1049+
throw new Error(`Prompt ${name} is already registered`);
1050+
}
1051+
const cb = rest[0] as PromptCallback<StandardSchemaWithJSON | undefined>;
1052+
const registered = this._createRegisteredPrompt(name, undefined, description, argsSchema, cb, undefined);
1053+
this.setPromptRequestHandlers();
1054+
this.sendPromptListChanged();
1055+
return registered;
1056+
}
1057+
1058+
/** @deprecated Use {@linkcode registerResource}. */
1059+
resource(name: string, uri: string, readCallback: ReadResourceCallback): RegisteredResource;
1060+
/** @deprecated Use {@linkcode registerResource}. */
1061+
resource(name: string, uri: string, metadata: ResourceMetadata, readCallback: ReadResourceCallback): RegisteredResource;
1062+
/** @deprecated Use {@linkcode registerResource}. */
1063+
resource(name: string, template: ResourceTemplate, readCallback: ReadResourceTemplateCallback): RegisteredResourceTemplate;
1064+
/** @deprecated Use {@linkcode registerResource}. */
1065+
resource(
1066+
name: string,
1067+
template: ResourceTemplate,
1068+
metadata: ResourceMetadata,
1069+
readCallback: ReadResourceTemplateCallback
1070+
): RegisteredResourceTemplate;
1071+
resource(name: string, uriOrTemplate: string | ResourceTemplate, ...rest: unknown[]): RegisteredResource | RegisteredResourceTemplate {
1072+
let metadata: ResourceMetadata = {};
1073+
if (typeof rest[0] === 'object') metadata = rest.shift() as ResourceMetadata;
1074+
const readCallback = rest[0] as ReadResourceCallback & ReadResourceTemplateCallback;
1075+
if (typeof uriOrTemplate === 'string') {
1076+
return this.registerResource(name, uriOrTemplate, metadata, readCallback);
1077+
}
1078+
return this.registerResource(name, uriOrTemplate, metadata, readCallback);
1079+
}
1080+
9531081
/**
9541082
* Checks if the server is connected to a transport.
9551083
* @returns `true` if the server is connected
@@ -1062,6 +1190,51 @@ export class ResourceTemplate {
10621190
}
10631191
}
10641192

1193+
/**
1194+
* v1 raw-shape: a plain record of Zod field schemas, e.g. `{ name: z.string() }`.
1195+
* @deprecated Wrap in `z.object({...})` and pass to `registerTool`.
1196+
*/
1197+
export type ZodRawShape = z.ZodRawShape;
1198+
1199+
/** @deprecated */
1200+
export type InferRawShape<S extends ZodRawShape> = { [K in keyof S]: z.infer<S[K]> };
1201+
1202+
/** @deprecated Use {@linkcode ToolCallback} with `registerTool`. */
1203+
export type LegacyToolCallback<Args extends ZodRawShape | undefined> = Args extends ZodRawShape
1204+
? (args: InferRawShape<Args>, ctx: ServerContext) => CallToolResult | Promise<CallToolResult>
1205+
: (ctx: ServerContext) => CallToolResult | Promise<CallToolResult>;
1206+
1207+
/** @deprecated Use {@linkcode PromptCallback} with `registerPrompt`. */
1208+
export type LegacyPromptCallback<Args extends ZodRawShape | undefined> = Args extends ZodRawShape
1209+
? (args: InferRawShape<Args>, ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>
1210+
: (ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>;
1211+
1212+
/**
1213+
* Detects a v1 "raw shape" — a plain object whose values are Standard Schema
1214+
* field schemas, e.g. `{ name: z.string() }`. Used by the deprecated variadic
1215+
* `.tool()`/`.prompt()` shims to disambiguate the schema arg from annotations.
1216+
*
1217+
* @internal
1218+
*/
1219+
function isZodRawShape(obj: unknown): obj is ZodRawShape {
1220+
if (typeof obj !== 'object' || obj === null) return false;
1221+
if (isStandardSchema(obj)) return false;
1222+
const values = Object.values(obj);
1223+
return values.length > 0 && values.every(v => isStandardSchema(v));
1224+
}
1225+
1226+
/**
1227+
* Wraps a v1 raw shape in `z.object()` for the variadic shims; passes Standard
1228+
* Schemas through unchanged.
1229+
*
1230+
* @internal
1231+
*/
1232+
function wrapRawShape(schema: unknown): StandardSchemaWithJSON | undefined {
1233+
if (schema === undefined) return undefined;
1234+
if (isZodRawShape(schema)) return z.object(schema);
1235+
return schema as StandardSchemaWithJSON;
1236+
}
1237+
10651238
export type BaseToolCallback<
10661239
SendResultT extends Result,
10671240
Ctx extends ServerContext,
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/* eslint-disable @typescript-eslint/no-deprecated */
2+
import { z } from 'zod';
3+
import { McpServer, ResourceTemplate } from '../../src/server/mcp.js';
4+
5+
describe('McpServer v1-compat variadic shims', () => {
6+
describe('.tool()', () => {
7+
it('registers with raw-shape schema', () => {
8+
const server = new McpServer({ name: 't', version: '1' });
9+
10+
server.tool('x', { a: z.string() }, ({ a }) => ({ content: [{ type: 'text', text: a }] }));
11+
server.tool('y', { b: z.number() }, ({ b }) => ({ content: [{ type: 'text', text: String(b) }] }));
12+
13+
// @ts-expect-error private access for test
14+
expect(server._registeredTools['x']).toBeDefined();
15+
// @ts-expect-error private access for test
16+
expect(server._registeredTools['y']).toBeDefined();
17+
});
18+
19+
it('supports (name, description, paramsSchema, annotations, cb) overload', () => {
20+
const server = new McpServer({ name: 't', version: '1' });
21+
22+
const reg = server.tool('x', 'desc', { a: z.string() }, { readOnlyHint: true }, ({ a }) => ({
23+
content: [{ type: 'text', text: a }]
24+
}));
25+
26+
expect(reg.description).toBe('desc');
27+
expect(reg.annotations).toEqual({ readOnlyHint: true });
28+
expect(reg.inputSchema).toBeDefined();
29+
});
30+
31+
it('supports (name, cb) zero-arg overload', () => {
32+
const server = new McpServer({ name: 't', version: '1' });
33+
const reg = server.tool('x', () => ({ content: [{ type: 'text', text: 'ok' }] }));
34+
expect(reg.inputSchema).toBeUndefined();
35+
});
36+
});
37+
38+
describe('.prompt()', () => {
39+
it('registers with raw-shape argsSchema', () => {
40+
const server = new McpServer({ name: 't', version: '1' });
41+
42+
server.prompt('p1', { topic: z.string() }, ({ topic }) => ({
43+
messages: [{ role: 'user', content: { type: 'text', text: topic } }]
44+
}));
45+
server.prompt('p2', () => ({ messages: [] }));
46+
47+
// @ts-expect-error private access for test
48+
expect(server._registeredPrompts['p1']).toBeDefined();
49+
// @ts-expect-error private access for test
50+
expect(server._registeredPrompts['p2']).toBeDefined();
51+
});
52+
});
53+
54+
describe('.resource()', () => {
55+
it('forwards to registerResource for both string URIs and ResourceTemplates', () => {
56+
const server = new McpServer({ name: 't', version: '1' });
57+
58+
server.resource('r1', 'file:///a', () => ({ contents: [] }));
59+
server.resource('r2', new ResourceTemplate('file:///{id}', { list: undefined }), () => ({ contents: [] }));
60+
61+
// @ts-expect-error private access for test
62+
expect(server._registeredResources['file:///a']).toBeDefined();
63+
// @ts-expect-error private access for test
64+
expect(server._registeredResourceTemplates['r2']).toBeDefined();
65+
});
66+
});
67+
});

0 commit comments

Comments
 (0)