Keep the 1.x handler registration forms as deprecated overloads
Consumers on 1.x wrote setRequestHandler(Schema, handler) and
setNotificationHandler(Schema, handler) with a whole-message Zod schema.
SDK 2.0 keys handlers by method name, so App and AppBridge now accept
that form as a third, deprecated overload: a small helper detects the
schema, extracts the method literal, forwards to the 2.x
(method, { params }, handler) form, and hands the legacy handler the
reassembled { method, params } message plus a 1.x-shaped extra
(signal, requestId, sessionId, _meta). A one-time console warning
points at the new form. The double-registration guard applies to both
forms.
The AppRequest, AppNotification and AppResult unions that 1.x fed to
the SDK's Protocol generics come back as deprecated type aliases, since
they leaked through the root entry and cost nothing to keep.
Both go away in 3.0.
This commit is contained in:
parent
4cd4273947
commit
728b0bf38e
1 changed files with 420 additions and 31 deletions
94
src/legacy-handlers.ts
Normal file
94
src/legacy-handlers.ts
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
import type { BaseContext, RequestId } from "@modelcontextprotocol/client";
|
||||
import type { ZodLiteral, ZodObject, ZodType } from "zod/v4";
|
||||
|
||||
/**
|
||||
* A whole-message Zod schema (`method` literal plus `params`), as accepted by
|
||||
* the 1.x `setRequestHandler(Schema, handler)` forms.
|
||||
*/
|
||||
export type LegacyMethodSchema = ZodObject<{
|
||||
method: ZodLiteral<string>;
|
||||
params: ZodType;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* The `extra` argument 1.x request handlers received.
|
||||
*
|
||||
* @deprecated Use the 2.x `BaseContext` (`extra.mcpReq.signal`, `extra.mcpReq.id`).
|
||||
*/
|
||||
export type LegacyRequestHandlerExtra = {
|
||||
signal: AbortSignal;
|
||||
requestId: RequestId;
|
||||
sessionId?: string;
|
||||
_meta?: BaseContext["mcpReq"]["_meta"];
|
||||
sendRequest: BaseContext["mcpReq"]["send"];
|
||||
sendNotification: BaseContext["mcpReq"]["notify"];
|
||||
authInfo?: NonNullable<BaseContext["http"]>["authInfo"];
|
||||
};
|
||||
|
||||
/** @deprecated Use `setRequestHandler("method", { params, result }, (params, ctx) => …)`. */
|
||||
export type LegacyRequestHandler<S extends LegacyMethodSchema> = (
|
||||
request: S["_output"],
|
||||
extra: LegacyRequestHandlerExtra,
|
||||
) => unknown;
|
||||
|
||||
/** @deprecated Use `setNotificationHandler("method", { params }, (params) => …)`. */
|
||||
export type LegacyNotificationHandler<S extends LegacyMethodSchema> = (
|
||||
notification: S["_output"],
|
||||
) => void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Arguments for the 2.x three-argument `setRequestHandler` /
|
||||
* `setNotificationHandler` form.
|
||||
*/
|
||||
type ModernArgs = [
|
||||
method: string,
|
||||
schemas: { params: ZodType },
|
||||
handler: (params: unknown, ctx: BaseContext) => unknown,
|
||||
];
|
||||
|
||||
const warned = new Set<string>();
|
||||
|
||||
/**
|
||||
* Translate a 1.x `(Schema, handler)` registration into the 2.x
|
||||
* `(method, { params }, handler)` form, or return `undefined` when the call
|
||||
* is already in the 2.x form. The 1.x handler receives the reassembled
|
||||
* `{ method, params }` message and, for requests, a 1.x-shaped `extra`.
|
||||
*/
|
||||
export function toModernArgs(
|
||||
kind: "request" | "notification",
|
||||
args: unknown[],
|
||||
): ModernArgs | undefined {
|
||||
const [first, handler] = args;
|
||||
const shape = (first as Partial<LegacyMethodSchema> | undefined)?.shape;
|
||||
if (typeof shape?.method?.value !== "string") return undefined;
|
||||
const method = shape.method.value;
|
||||
const setter =
|
||||
kind === "request" ? "setRequestHandler" : "setNotificationHandler";
|
||||
if (!warned.has(setter)) {
|
||||
warned.add(setter);
|
||||
console.warn(
|
||||
`[ext-apps] ${setter}(Schema, handler) is deprecated; use ` +
|
||||
`${setter}("${method}", { params: Schema.shape.params }, handler).`,
|
||||
);
|
||||
}
|
||||
const call = handler as (...a: unknown[]) => unknown;
|
||||
return [
|
||||
method,
|
||||
{ params: shape.params },
|
||||
kind === "request"
|
||||
? (params, ctx) => call({ method, params }, legacyExtra(ctx))
|
||||
: (params) => call({ method, params }),
|
||||
];
|
||||
}
|
||||
|
||||
function legacyExtra(ctx: BaseContext): LegacyRequestHandlerExtra {
|
||||
return {
|
||||
signal: ctx.mcpReq.signal,
|
||||
requestId: ctx.mcpReq.id,
|
||||
sessionId: ctx.sessionId,
|
||||
_meta: ctx.mcpReq._meta,
|
||||
sendRequest: ctx.mcpReq.send,
|
||||
sendNotification: ctx.mcpReq.notify,
|
||||
authInfo: ctx.http?.authInfo,
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue