[RFC] refactor: remove vestigial generic type parameters from Protocol, Server, and Client (#1451)

This commit is contained in:
Felix Weinberger 2026-02-03 20:55:32 +00:00 • committed by GitHub
commit faeb03ec17
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 210 additions and 306 deletions

View file

@ -293,7 +293,7 @@ async function connect(url?: string): Promise<void> {
attempts++;
console.log(`\nPlease provide the following information (attempt ${attempts}/${maxAttempts}):`);
const content: Record<string, unknown> = {};
const content: Record<string, string | number | boolean | string[]> = {};
let inputCancelled = false;
// Collect input for each field
@ -357,7 +357,7 @@ async function connect(url?: string): Promise<void> {
// Parse and validate the input
try {
if (answer === '' && field.default !== undefined) {
content[fieldName] = field.default;
content[fieldName] = field.default as string | number | boolean | string[];
} else if (answer === '' && !isRequired) {
// Skip optional empty fields
continue;
@ -401,7 +401,7 @@ async function connect(url?: string): Promise<void> {
}
}
content[fieldName] = parsedValue;
content[fieldName] = parsedValue as string | number | boolean | string[];
}
} catch (error) {
console.log(`❌ Error: ${error}`);

View file

@ -22,15 +22,13 @@ import type {
ListToolsRequest,
LoggingLevel,
MessageExtraInfo,
Notification,
NotificationMethod,
ProtocolOptions,
ReadResourceRequest,
Request,
RequestMethod,
RequestOptions,
RequestTypeMap,
Result,
ResultTypeMap,
ServerCapabilities,
SubscribeRequest,
Tool,
@ -193,36 +191,8 @@ export type ClientOptions = ProtocolOptions & {
*
* The client will automatically begin the initialization flow with the server when connect() is called.
*
* To use with custom types, extend the base Request/Notification/Result types and pass them as type parameters:
*
* ```typescript
* // Custom schemas
* const CustomRequestSchema = RequestSchema.extend({...})
* const CustomNotificationSchema = NotificationSchema.extend({...})
* const CustomResultSchema = ResultSchema.extend({...})
*
* // Type aliases
* type CustomRequest = z.infer<typeof CustomRequestSchema>
* type CustomNotification = z.infer<typeof CustomNotificationSchema>
* type CustomResult = z.infer<typeof CustomResultSchema>
*
* // Create typed client
* const client = new Client<CustomRequest, CustomNotification, CustomResult>({
* name: "CustomClient",
* version: "1.0.0"
* })
* ```
*/
export class Client<
RequestT extends Request = Request,
NotificationT extends Notification = Notification,
ResultT extends Result = Result
> extends Protocol<
ClientRequest | RequestT,
ClientNotification | NotificationT,
ClientResult | ResultT,
ClientContext<ClientRequest | RequestT, ClientNotification | NotificationT>
> {
export class Client extends Protocol<ClientContext> {
private _serverCapabilities?: ServerCapabilities;
private _serverVersion?: Implementation;
private _capabilities: ClientCapabilities;
@ -231,7 +201,7 @@ export class Client<
private _cachedToolOutputValidators: Map<string, JsonSchemaValidator<unknown>> = new Map();
private _cachedKnownTaskTools: Set<string> = new Set();
private _cachedRequiredTaskTools: Set<string> = new Set();
private _experimental?: { tasks: ExperimentalClientTasks<RequestT, NotificationT, ResultT> };
private _experimental?: { tasks: ExperimentalClientTasks };
private _listChangedDebounceTimers: Map<string, ReturnType<typeof setTimeout>> = new Map();
private _pendingListChangedConfig?: ListChangedHandlers;
private _enforceStrictCapabilities: boolean;
@ -254,10 +224,7 @@ export class Client<
}
}
protected override buildContext(
ctx: BaseContext<ClientRequest | RequestT, ClientNotification | NotificationT>,
_transportInfo?: MessageExtraInfo
): ClientContext<ClientRequest | RequestT, ClientNotification | NotificationT> {
protected override buildContext(ctx: BaseContext, _transportInfo?: MessageExtraInfo): ClientContext {
return ctx;
}
@ -297,7 +264,7 @@ export class Client<
*
* @experimental
*/
get experimental(): { tasks: ExperimentalClientTasks<RequestT, NotificationT, ResultT> } {
get experimental(): { tasks: ExperimentalClientTasks } {
if (!this._experimental) {
this._experimental = {
tasks: new ExperimentalClientTasks(this)
@ -324,16 +291,10 @@ export class Client<
*/
public override setRequestHandler<M extends RequestMethod>(
method: M,
handler: (
request: RequestTypeMap[M],
ctx: ClientContext<ClientRequest | RequestT, ClientNotification | NotificationT>
) => ClientResult | ResultT | Promise<ClientResult | ResultT>
handler: (request: RequestTypeMap[M], ctx: ClientContext) => ResultTypeMap[M] | Promise<ResultTypeMap[M]>
): void {
if (method === 'elicitation/create') {
const wrappedHandler = async (
request: RequestTypeMap[M],
ctx: ClientContext<ClientRequest | RequestT, ClientNotification | NotificationT>
): Promise<ClientResult | ResultT> => {
const wrappedHandler = async (request: RequestTypeMap[M], ctx: ClientContext): Promise<ClientResult> => {
const validatedRequest = parseSchema(ElicitRequestSchema, request);
if (!validatedRequest.success) {
// Type guard: if success is false, error is guaranteed to exist
@ -403,10 +364,7 @@ export class Client<
}
if (method === 'sampling/createMessage') {
const wrappedHandler = async (
request: RequestTypeMap[M],
ctx: ClientContext<ClientRequest | RequestT, ClientNotification | NotificationT>
): Promise<ClientResult | ResultT> => {
const wrappedHandler = async (request: RequestTypeMap[M], ctx: ClientContext): Promise<ClientResult> => {
const validatedRequest = parseSchema(CreateMessageRequestSchema, request);
if (!validatedRequest.success) {
const errorMessage =
@ -533,7 +491,7 @@ export class Client<
return this._instructions;
}
protected assertCapabilityForMethod(method: RequestT['method']): void {
protected assertCapabilityForMethod(method: RequestMethod): void {
switch (method as ClientRequest['method']) {
case 'logging/setLevel': {
if (!this._serverCapabilities?.logging) {
@ -596,7 +554,7 @@ export class Client<
}
}
protected assertNotificationCapability(method: NotificationT['method']): void {
protected assertNotificationCapability(method: NotificationMethod): void {
switch (method as ClientNotification['method']) {
case 'notifications/roots/list_changed': {
if (!this._capabilities.roots?.listChanged) {

View file

@ -9,14 +9,11 @@ import type {
AnyObjectSchema,
CallToolRequest,
CancelTaskResult,
ClientRequest,
GetTaskResult,
ListTasksResult,
Notification,
Request,
RequestOptions,
ResponseMessage,
Result,
SchemaOutput
} from '@modelcontextprotocol/core';
import { CallToolResultSchema, ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/core';
@ -27,9 +24,9 @@ import type { Client } from '../../client/client.js';
* Internal interface for accessing Client's private methods.
* @internal
*/
interface ClientInternal<RequestT extends Request> {
interface ClientInternal {
requestStream<T extends AnyObjectSchema>(
request: ClientRequest | RequestT,
request: Request,
resultSchema: T,
options?: RequestOptions
): AsyncGenerator<ResponseMessage<SchemaOutput<T>>, void, void>;
@ -48,12 +45,8 @@ interface ClientInternal<RequestT extends Request> {
*
* @experimental
*/
export class ExperimentalClientTasks<
RequestT extends Request = Request,
NotificationT extends Notification = Notification,
ResultT extends Result = Result
> {
constructor(private readonly _client: Client<RequestT, NotificationT, ResultT>) {}
export class ExperimentalClientTasks {
constructor(private readonly _client: Client) {}
/**
* Calls a tool and returns an AsyncGenerator that yields response messages.
@ -96,7 +89,7 @@ export class ExperimentalClientTasks<
options?: RequestOptions
): AsyncGenerator<ResponseMessage<SchemaOutput<typeof CallToolResultSchema>>, void, void> {
// Access Client's internal methods
const clientInternal = this._client as unknown as ClientInternal<RequestT>;
const clientInternal = this._client as unknown as ClientInternal;
// Add task creation parameters if server supports it and not explicitly provided
const optionsWithTask = {
@ -255,14 +248,14 @@ export class ExperimentalClientTasks<
* @experimental
*/
requestStream<T extends AnyObjectSchema>(
request: ClientRequest | RequestT,
request: Request,
resultSchema: T,
options?: RequestOptions
): AsyncGenerator<ResponseMessage<SchemaOutput<T>>, void, void> {
// Delegate to the client's underlying Protocol method
type ClientWithRequestStream = {
requestStream<U extends AnyObjectSchema>(
request: ClientRequest | RequestT,
request: Request,
resultSchema: U,
options?: RequestOptions
): AsyncGenerator<ResponseMessage<SchemaOutput<U>>, void, void>;

View file

@ -5,8 +5,6 @@ import type {
AuthInfo,
CancelledNotification,
ClientCapabilities,
ClientNotification,
ClientRequest,
CreateMessageRequest,
CreateMessageResult,
CreateMessageResultWithTools,
@ -37,8 +35,6 @@ import type {
RequestTypeMap,
Result,
ServerCapabilities,
ServerNotification,
ServerRequest,
Task,
TaskCreationParams,
TaskStatusNotification
@ -266,7 +262,7 @@ export type TaskContext = {
/**
* Base context provided to all request handlers.
*/
export type BaseContext<SendRequestT extends Request, SendNotificationT extends Notification> = {
export type BaseContext = {
/**
* The session ID from the transport, if available.
*/
@ -301,14 +297,14 @@ export type BaseContext<SendRequestT extends Request, SendNotificationT extends
*
* This is used by certain transports to correctly associate related messages.
*/
send: <U extends AnySchema>(request: SendRequestT, resultSchema: U, options?: TaskRequestOptions) => Promise<SchemaOutput<U>>;
send: <U extends AnySchema>(request: Request, resultSchema: U, options?: TaskRequestOptions) => Promise<SchemaOutput<U>>;
/**
* Sends a notification that relates to the current request being handled.
*
* This is used by certain transports to correctly associate related messages.
*/
notify: (notification: SendNotificationT) => Promise<void>;
notify: (notification: Notification) => Promise<void>;
};
/**
@ -330,10 +326,7 @@ export type BaseContext<SendRequestT extends Request, SendNotificationT extends
/**
* Context provided to server-side request handlers, extending BaseContext with server-specific fields.
*/
export type ServerContext<
SendRequestT extends Request = ServerRequest,
SendNotificationT extends Notification = ServerNotification
> = BaseContext<SendRequestT, SendNotificationT> & {
export type ServerContext = BaseContext & {
mcpReq: {
/**
* Send a log message notification to the client.
@ -378,10 +371,7 @@ export type ServerContext<
/**
* Context provided to client-side request handlers.
*/
export type ClientContext<
SendRequestT extends Request = ClientRequest,
SendNotificationT extends Notification = ClientNotification
> = BaseContext<SendRequestT, SendNotificationT>;
export type ClientContext = BaseContext;
/**
* Information about a request's timeout state
@ -399,15 +389,10 @@ type TimeoutInfo = {
* Implements MCP protocol framing on top of a pluggable transport, including
* features like request/response linking, notifications, and progress.
*/
export abstract class Protocol<
SendRequestT extends Request,
SendNotificationT extends Notification,
SendResultT extends Result,
ContextT extends BaseContext<SendRequestT, SendNotificationT>
> {
export abstract class Protocol<ContextT extends BaseContext> {
private _transport?: Transport;
private _requestMessageId = 0;
private _requestHandlers: Map<string, (request: JSONRPCRequest, ctx: ContextT) => Promise<SendResultT>> = new Map();
private _requestHandlers: Map<string, (request: JSONRPCRequest, ctx: ContextT) => Promise<Result>> = new Map();
private _requestHandlerAbortControllers: Map<RequestId, AbortController> = new Map();
private _notificationHandlers: Map<string, (notification: JSONRPCNotification) => Promise<void>> = new Map();
private _responseHandlers: Map<number, (response: JSONRPCResultResponse | Error) => void> = new Map();
@ -442,7 +427,7 @@ export abstract class Protocol<
/**
* A handler to invoke for any request types that do not have their own handler installed.
*/
fallbackRequestHandler?: (request: JSONRPCRequest, ctx: ContextT) => Promise<SendResultT>;
fallbackRequestHandler?: (request: JSONRPCRequest, ctx: ContextT) => Promise<Result>;
/**
* A handler to invoke for any notification types that do not have their own handler installed.
@ -463,7 +448,7 @@ export abstract class Protocol<
this.setRequestHandler(
'ping',
// Automatic pong by default.
_request => ({}) as SendResultT
_request => ({}) as Result
);
// Install task handlers if TaskStore is provided
@ -480,11 +465,11 @@ export abstract class Protocol<
// as the taskId parameter is the source of truth
return {
...task
} as unknown as SendResultT;
} as Result;
});
this.setRequestHandler('tasks/result', async (request, ctx) => {
const handleTaskResult = async (): Promise<SendResultT> => {
const handleTaskResult = async (): Promise<Result> => {
const taskId = request.params.taskId;
// Deliver queued messages
@ -561,7 +546,7 @@ export abstract class Protocol<
taskId: taskId
}
}
} as unknown as SendResultT;
} as Result;
}
return await handleTaskResult();
@ -577,7 +562,7 @@ export abstract class Protocol<
tasks,
nextCursor,
_meta: {}
} as unknown as SendResultT;
} as Result;
} catch (error) {
throw new ProtocolError(
ProtocolErrorCode.InvalidParams,
@ -621,7 +606,7 @@ export abstract class Protocol<
return {
_meta: {},
...cancelledTask
} as unknown as SendResultT;
} as Result;
} catch (error) {
// Re-throw ProtocolError as-is
if (error instanceof ProtocolError) {
@ -640,7 +625,7 @@ export abstract class Protocol<
* Builds the context object for request handlers. Subclasses must override
* to return the appropriate context type (e.g., ServerContext adds requestInfo).
*/
protected abstract buildContext(ctx: BaseContext<SendRequestT, SendNotificationT>, transportInfo?: MessageExtraInfo): ContextT;
protected abstract buildContext(ctx: BaseContext, transportInfo?: MessageExtraInfo): ContextT;
private async _oncancel(notification: CancelledNotification): Promise<void> {
if (!notification.params.requestId) {
@ -816,7 +801,7 @@ export abstract class Protocol<
? { id: relatedTaskId, store: taskStore, requestedTtl: taskCreationParams?.ttl }
: undefined;
const baseCtx: BaseContext<SendRequestT, SendNotificationT> = {
const baseCtx: BaseContext = {
sessionId: capturedTransport?.sessionId,
mcpReq: {
id: request.id,
@ -1014,14 +999,14 @@ export abstract class Protocol<
*
* This should be implemented by subclasses.
*/
protected abstract assertCapabilityForMethod(method: SendRequestT['method']): void;
protected abstract assertCapabilityForMethod(method: RequestMethod): void;
/**
* A method to check if a notification is supported by the local side, for the given method to be sent.
*
* This should be implemented by subclasses.
*/
protected abstract assertNotificationCapability(method: SendNotificationT['method']): void;
protected abstract assertNotificationCapability(method: NotificationMethod): void;
/**
* A method to check if a request handler is supported by the local side, for the given method to be handled.
@ -1072,7 +1057,7 @@ export abstract class Protocol<
* @experimental Use `client.experimental.tasks.requestStream()` to access this method.
*/
protected async *requestStream<T extends AnyObjectSchema>(
request: SendRequestT,
request: Request,
resultSchema: T,
options?: RequestOptions
): AsyncGenerator<ResponseMessage<SchemaOutput<T>>, void, void> {
@ -1172,7 +1157,7 @@ export abstract class Protocol<
*
* Do not use this method to emit notifications! Use notification() instead.
*/
request<T extends AnySchema>(request: SendRequestT, resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>> {
request<T extends AnySchema>(request: Request, resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>> {
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
// Send the request
@ -1188,7 +1173,7 @@ export abstract class Protocol<
if (this._options?.enforceStrictCapabilities === true) {
try {
this.assertCapabilityForMethod(request.method);
this.assertCapabilityForMethod(request.method as RequestMethod);
// If task creation is requested, also check task capabilities
if (task) {
@ -1335,7 +1320,6 @@ export abstract class Protocol<
* @experimental Use `client.experimental.tasks.getTask()` to access this method.
*/
protected async getTask(params: GetTaskRequest['params'], options?: RequestOptions): Promise<GetTaskResult> {
// @ts-expect-error SendRequestT cannot directly contain GetTaskRequest, but we ensure all type instantiations contain it anyways
return this.request({ method: 'tasks/get', params }, GetTaskResultSchema, options);
}
@ -1349,7 +1333,6 @@ export abstract class Protocol<
resultSchema: T,
options?: RequestOptions
): Promise<SchemaOutput<T>> {
// @ts-expect-error SendRequestT cannot directly contain GetTaskPayloadRequest, but we ensure all type instantiations contain it anyways
return this.request({ method: 'tasks/result', params }, resultSchema, options);
}
@ -1359,7 +1342,6 @@ export abstract class Protocol<
* @experimental Use `client.experimental.tasks.listTasks()` to access this method.
*/
protected async listTasks(params?: { cursor?: string }, options?: RequestOptions): Promise<SchemaOutput<typeof ListTasksResultSchema>> {
// @ts-expect-error SendRequestT cannot directly contain ListTasksRequest, but we ensure all type instantiations contain it anyways
return this.request({ method: 'tasks/list', params }, ListTasksResultSchema, options);
}
@ -1369,19 +1351,18 @@ export abstract class Protocol<
* @experimental Use `client.experimental.tasks.cancelTask()` to access this method.
*/
protected async cancelTask(params: { taskId: string }, options?: RequestOptions): Promise<SchemaOutput<typeof CancelTaskResultSchema>> {
// @ts-expect-error SendRequestT cannot directly contain CancelTaskRequest, but we ensure all type instantiations contain it anyways
return this.request({ method: 'tasks/cancel', params }, CancelTaskResultSchema, options);
}
/**
* Emits a notification, which is a one-way message that does not expect a response.
*/
async notification(notification: SendNotificationT, options?: NotificationOptions): Promise<void> {
async notification(notification: Notification, options?: NotificationOptions): Promise<void> {
if (!this._transport) {
throw new SdkError(SdkErrorCode.NotConnected, 'Not connected');
}
this.assertNotificationCapability(notification.method);
this.assertNotificationCapability(notification.method as NotificationMethod);
// Queue notification if related to a task
const relatedTaskId = options?.relatedTask?.taskId;
@ -1493,7 +1474,7 @@ export abstract class Protocol<
*/
setRequestHandler<M extends RequestMethod>(
method: M,
handler: (request: RequestTypeMap[M], ctx: ContextT) => SendResultT | Promise<SendResultT>
handler: (request: RequestTypeMap[M], ctx: ContextT) => Result | Promise<Result>
): void {
this.assertRequestHandlerCapability(method);
const schema = getRequestSchema(method);
@ -1683,7 +1664,7 @@ export abstract class Protocol<
method: 'notifications/tasks/status',
params: task
});
await this.notification(notification as SendNotificationT);
await this.notification(notification as Notification);
if (isTerminal(task.status)) {
this._cleanupTaskProgressHandler(taskId);
@ -1718,7 +1699,7 @@ export abstract class Protocol<
method: 'notifications/tasks/status',
params: updatedTask
});
await this.notification(notification as SendNotificationT);
await this.notification(notification as Notification);
if (isTerminal(updatedTask.status)) {
this._cleanupTaskProgressHandler(taskId);

View file

@ -2615,6 +2615,28 @@ export type RequestMethod = ClientRequest['method'] | ServerRequest['method'];
export type NotificationMethod = ClientNotification['method'] | ServerNotification['method'];
export type RequestTypeMap = MethodToTypeMap<ClientRequest | ServerRequest>;
export type NotificationTypeMap = MethodToTypeMap<ClientNotification | ServerNotification>;
export type ResultTypeMap = {
ping: EmptyResult;
initialize: InitializeResult;
'completion/complete': CompleteResult;
'logging/setLevel': EmptyResult;
'prompts/get': GetPromptResult;
'prompts/list': ListPromptsResult;
'resources/list': ListResourcesResult;
'resources/templates/list': ListResourceTemplatesResult;
'resources/read': ReadResourceResult;
'resources/subscribe': EmptyResult;
'resources/unsubscribe': EmptyResult;
'tools/call': CallToolResult | CreateTaskResult;
'tools/list': ListToolsResult;
'sampling/createMessage': CreateMessageResult | CreateMessageResultWithTools;
'elicitation/create': ElicitResult;
'roots/list': ListRootsResult;
'tasks/get': GetTaskResult;
'tasks/result': Result;
'tasks/list': ListTasksResult;
'tasks/cancel': CancelTaskResult;
};
/* Runtime schema lookup */
type RequestSchemaType = (typeof ClientRequestSchema.options)[number] | (typeof ServerRequestSchema.options)[number];

View file

@ -150,19 +150,19 @@ function assertQueuedRequest(o?: QueuedMessage): asserts o is QueuedRequest {
}
describe('protocol tests', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transport: MockTransport;
let sendSpy: MockInstance;
beforeEach(() => {
transport = new MockTransport();
sendSpy = vi.spyOn(transport, 'send');
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -634,12 +634,12 @@ describe('protocol tests', () => {
it('should NOT debounce a notification that has parameters', async () => {
// ARRANGE
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -660,12 +660,12 @@ describe('protocol tests', () => {
it('should NOT debounce a notification that has a relatedRequestId', async () => {
// ARRANGE
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -684,12 +684,12 @@ describe('protocol tests', () => {
it('should clear pending debounced notifications on connection close', async () => {
// ARRANGE
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -713,12 +713,12 @@ describe('protocol tests', () => {
it('should debounce multiple synchronous calls when params property is omitted', async () => {
// ARRANGE
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -745,12 +745,12 @@ describe('protocol tests', () => {
it('should debounce calls when params is explicitly undefined', async () => {
// ARRANGE
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -775,12 +775,12 @@ describe('protocol tests', () => {
it('should send non-debounced notifications immediately and multiple times', async () => {
// ARRANGE
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -813,12 +813,12 @@ describe('protocol tests', () => {
it('should handle sequential batches of debounced notifications correctly', async () => {
// ARRANGE
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1028,19 +1028,19 @@ describe('mergeCapabilities', () => {
});
describe('Task-based execution', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transport: MockTransport;
let sendSpy: MockInstance;
beforeEach(() => {
transport = new MockTransport();
sendSpy = vi.spyOn(transport, 'send');
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1285,12 +1285,12 @@ describe('Task-based execution', () => {
// rather than in _meta, and that task management is handled by tool implementors
const mockTaskStore = createMockTaskStore();
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1357,12 +1357,12 @@ describe('Task-based execution', () => {
}
);
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1421,12 +1421,12 @@ describe('Task-based execution', () => {
}
);
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1470,12 +1470,12 @@ describe('Task-based execution', () => {
onList: () => listedTasks.releaseLatch()
});
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1506,12 +1506,12 @@ describe('Task-based execution', () => {
const mockTaskStore = createMockTaskStore();
mockTaskStore.listTasks.mockRejectedValue(new Error('Invalid cursor: bad-cursor'));
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1643,12 +1643,12 @@ describe('Task-based execution', () => {
throw new Error('Task not found');
});
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1688,12 +1688,12 @@ describe('Task-based execution', () => {
mockTaskStore.getTask.mockResolvedValue(null);
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1739,12 +1739,12 @@ describe('Task-based execution', () => {
mockTaskStore.updateTaskStatus.mockClear();
mockTaskStore.getTask.mockResolvedValue(completedTask);
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1824,12 +1824,12 @@ describe('Task-based execution', () => {
params: {}
});
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1877,12 +1877,12 @@ describe('Task-based execution', () => {
params: {}
});
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1929,12 +1929,12 @@ describe('Task-based execution', () => {
params: {}
});
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -1969,12 +1969,12 @@ describe('Task-based execution', () => {
params: {}
});
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2017,12 +2017,12 @@ describe('Task-based execution', () => {
await mockTaskStore.storeTaskResult(task.taskId, 'completed', testResult);
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2063,12 +2063,12 @@ describe('Task-based execution', () => {
it('should propagate related-task metadata to handler sendRequest and sendNotification', async () => {
const mockTaskStore = createMockTaskStore();
const serverProtocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const serverProtocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2142,19 +2142,19 @@ describe('Task-based execution', () => {
});
describe('Request Cancellation vs Task Cancellation', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transport: MockTransport;
let taskStore: TaskStore;
beforeEach(() => {
transport = new MockTransport();
taskStore = createMockTaskStore();
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2430,19 +2430,19 @@ describe('Request Cancellation vs Task Cancellation', () => {
});
describe('Progress notification support for tasks', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transport: MockTransport;
let sendSpy: MockInstance;
beforeEach(() => {
transport = new MockTransport();
sendSpy = vi.spyOn(transport, 'send');
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2451,12 +2451,12 @@ describe('Progress notification support for tasks', () => {
it('should maintain progress token association after CreateTaskResult is returned', async () => {
const taskStore = createMockTaskStore();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2547,12 +2547,12 @@ describe('Progress notification support for tasks', () => {
it('should stop progress notifications when task reaches terminal status (completed)', async () => {
const taskStore = createMockTaskStore();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2686,12 +2686,12 @@ describe('Progress notification support for tasks', () => {
it('should stop progress notifications when task reaches terminal status (failed)', async () => {
const taskStore = createMockTaskStore();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2787,12 +2787,12 @@ describe('Progress notification support for tasks', () => {
it('should stop progress notifications when task is cancelled', async () => {
const taskStore = createMockTaskStore();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -2885,12 +2885,12 @@ describe('Progress notification support for tasks', () => {
it('should use the same progressToken throughout task lifetime', async () => {
const taskStore = createMockTaskStore();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -3157,12 +3157,12 @@ describe('Message interception for task-related notifications', () => {
it('should queue notifications with io.modelcontextprotocol/related-task metadata', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3197,12 +3197,12 @@ describe('Message interception for task-related notifications', () => {
it('should not queue notifications without related-task metadata', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3227,12 +3227,12 @@ describe('Message interception for task-related notifications', () => {
it('should propagate queue overflow errors without failing the task', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3277,12 +3277,12 @@ describe('Message interception for task-related notifications', () => {
it('should extract task ID correctly from metadata', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3313,12 +3313,12 @@ describe('Message interception for task-related notifications', () => {
it('should preserve message order when queuing multiple notifications', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3358,12 +3358,12 @@ describe('Message interception for task-related requests', () => {
it('should queue requests with io.modelcontextprotocol/related-task metadata', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3413,12 +3413,12 @@ describe('Message interception for task-related requests', () => {
it('should not queue requests without related-task metadata', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3455,12 +3455,12 @@ describe('Message interception for task-related requests', () => {
it('should store request resolver for response routing', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3511,12 +3511,12 @@ describe('Message interception for task-related requests', () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const queue = new InMemoryTaskMessageQueue();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3579,12 +3579,12 @@ describe('Message interception for task-related requests', () => {
it('should log error when resolver is missing for side-channeled request', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3658,12 +3658,12 @@ describe('Message interception for task-related requests', () => {
it('should propagate queue overflow errors for requests without failing the task', async () => {
const taskStore = createMockTaskStore();
const transport = new MockTransport();
const server = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const server = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}
@ -3715,19 +3715,19 @@ describe('Message interception for task-related requests', () => {
});
describe('Message Interception', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transport: MockTransport;
let mockTaskStore: TaskStore & { [K in keyof TaskStore]: MockInstance };
beforeEach(() => {
transport = new MockTransport();
mockTaskStore = createMockTaskStore();
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4236,19 +4236,19 @@ describe('Message Interception', () => {
});
describe('Queue lifecycle management', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transport: MockTransport;
let mockTaskStore: TaskStore & { [K in keyof TaskStore]: MockInstance };
beforeEach(() => {
transport = new MockTransport();
mockTaskStore = createMockTaskStore();
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4560,12 +4560,12 @@ describe('requestStream() method', () => {
test('should yield result immediately for non-task requests', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4606,12 +4606,12 @@ describe('requestStream() method', () => {
test('should yield error message on request failure', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4655,12 +4655,12 @@ describe('requestStream() method', () => {
test('should handle cancellation via AbortSignal', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4696,12 +4696,12 @@ describe('requestStream() method', () => {
describe('Error responses', () => {
test('should yield error as terminal message for server error response', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4741,12 +4741,12 @@ describe('requestStream() method', () => {
vi.useFakeTimers();
try {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4783,12 +4783,12 @@ describe('requestStream() method', () => {
test('should yield error as terminal message for cancellation', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4819,12 +4819,12 @@ describe('requestStream() method', () => {
test('should not yield any messages after error message', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4874,12 +4874,12 @@ describe('requestStream() method', () => {
test('should yield error as terminal message for task failure', async () => {
const transport = new MockTransport();
const mockTaskStore = createMockTaskStore();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4937,12 +4937,12 @@ describe('requestStream() method', () => {
test('should yield error as terminal message for network error', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -4968,12 +4968,12 @@ describe('requestStream() method', () => {
test('should ensure error is always the final message', async () => {
const transport = new MockTransport();
const protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
const protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}
@ -5016,7 +5016,7 @@ describe('requestStream() method', () => {
});
describe('Error handling for missing resolvers', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transport: MockTransport;
let taskStore: TaskStore & { [K in keyof TaskStore]: MockInstance };
let taskMessageQueue: TaskMessageQueue;
@ -5027,12 +5027,12 @@ describe('Error handling for missing resolvers', () => {
taskMessageQueue = new InMemoryTaskMessageQueue();
errorHandler = vi.fn();
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(_method: string): void {}
protected assertNotificationCapability(_method: string): void {}
protected assertRequestHandlerCapability(_method: string): void {}
protected assertTaskCapability(_method: string): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(_method: string): void {}

View file

@ -29,17 +29,17 @@ class MockTransport implements Transport {
}
describe('Protocol transport handling bug', () => {
let protocol: Protocol<Request, Notification, Result, BaseContext<Request, Notification>>;
let protocol: Protocol<BaseContext>;
let transportA: MockTransport;
let transportB: MockTransport;
beforeEach(() => {
protocol = new (class extends Protocol<Request, Notification, Result, BaseContext<Request, Notification>> {
protocol = new (class extends Protocol<BaseContext> {
protected assertCapabilityForMethod(): void {}
protected assertNotificationCapability(): void {}
protected assertRequestHandlerCapability(): void {}
protected assertTaskCapability(): void {}
protected buildContext(ctx: any) {
protected buildContext(ctx: BaseContext): BaseContext {
return ctx;
}
protected assertTaskHandlerCapability(): void {}

View file

@ -23,8 +23,8 @@ import type { BaseToolCallback } from '../../server/mcp.js';
* Handler for creating a task.
* @experimental
*/
export type CreateTaskRequestHandler<SendResultT extends Result, Args extends AnySchema | undefined = undefined> = BaseToolCallback<
SendResultT,
export type CreateTaskRequestHandler<ResultT extends Result, Args extends AnySchema | undefined = undefined> = BaseToolCallback<
ResultT,
CreateTaskServerContext,
Args
>;
@ -33,8 +33,8 @@ export type CreateTaskRequestHandler<SendResultT extends Result, Args extends An
* Handler for task operations (get, getResult).
* @experimental
*/
export type TaskRequestHandler<SendResultT extends Result, Args extends AnySchema | undefined = undefined> = BaseToolCallback<
SendResultT,
export type TaskRequestHandler<ResultT extends Result, Args extends AnySchema | undefined = undefined> = BaseToolCallback<
ResultT,
TaskServerContext,
Args
>;

View file

@ -10,13 +10,11 @@ import type {
CancelTaskResult,
GetTaskResult,
ListTasksResult,
Notification,
Request,
RequestOptions,
ResponseMessage,
Result,
SchemaOutput,
ServerRequest
SchemaOutput
} from '@modelcontextprotocol/core';
import type { Server } from '../../server/server.js';
@ -33,12 +31,8 @@ import type { Server } from '../../server/server.js';
*
* @experimental
*/
export class ExperimentalServerTasks<
RequestT extends Request = Request,
NotificationT extends Notification = Notification,
ResultT extends Result = Result
> {
constructor(private readonly _server: Server<RequestT, NotificationT, ResultT>) {}
export class ExperimentalServerTasks {
constructor(private readonly _server: Server) {}
/**
* Sends a request and returns an AsyncGenerator that yields response messages.
@ -55,14 +49,14 @@ export class ExperimentalServerTasks<
* @experimental
*/
requestStream<T extends AnySchema>(
request: ServerRequest | RequestT,
request: Request,
resultSchema: T,
options?: RequestOptions
): AsyncGenerator<ResponseMessage<SchemaOutput<T> & Result>, void, void> {
// Delegate to the server's underlying Protocol method
type ServerWithRequestStream = {
requestStream<U extends AnySchema>(
request: ServerRequest | RequestT,
request: Request,
resultSchema: U,
options?: RequestOptions
): AsyncGenerator<ResponseMessage<SchemaOutput<U> & Result>, void, void>;

View file

@ -980,13 +980,9 @@ export class ResourceTemplate {
}
}
export type BaseToolCallback<
SendResultT extends Result,
Ctx extends ServerContext,
Args extends AnySchema | undefined
> = Args extends AnySchema
? (args: SchemaOutput<Args>, ctx: Ctx) => SendResultT | Promise<SendResultT>
: (ctx: Ctx) => SendResultT | Promise<SendResultT>;
export type BaseToolCallback<ResultT extends Result, Ctx extends ServerContext, Args extends AnySchema | undefined> = Args extends AnySchema
? (args: SchemaOutput<Args>, ctx: Ctx) => ResultT | Promise<ResultT>
: (ctx: Ctx) => ResultT | Promise<ResultT>;
/**
* Callback for a tool handler registered with Server.tool().

View file

@ -18,19 +18,16 @@ import type {
LoggingLevel,
LoggingMessageNotification,
MessageExtraInfo,
Notification,
NotificationMethod,
NotificationOptions,
ProtocolOptions,
Request,
RequestMethod,
RequestOptions,
RequestTypeMap,
ResourceUpdatedNotification,
Result,
ResultTypeMap,
ServerCapabilities,
ServerContext,
ServerNotification,
ServerRequest,
ServerResult,
ToolResultContent,
ToolUseContent
@ -87,43 +84,15 @@ export type ServerOptions = ProtocolOptions & {
*
* This server will automatically respond to the initialization flow as initiated from the client.
*
* To use with custom types, extend the base Request/Notification/Result types and pass them as type parameters:
*
* ```typescript
* // Custom schemas
* const CustomRequestSchema = RequestSchema.extend({...})
* const CustomNotificationSchema = NotificationSchema.extend({...})
* const CustomResultSchema = ResultSchema.extend({...})
*
* // Type aliases
* type CustomRequest = z.infer<typeof CustomRequestSchema>
* type CustomNotification = z.infer<typeof CustomNotificationSchema>
* type CustomResult = z.infer<typeof CustomResultSchema>
*
* // Create typed server
* const server = new Server<CustomRequest, CustomNotification, CustomResult>({
* name: "CustomServer",
* version: "1.0.0"
* })
* ```
* @deprecated Use `McpServer` instead for the high-level API. Only use `Server` for advanced use cases.
*/
export class Server<
RequestT extends Request = Request,
NotificationT extends Notification = Notification,
ResultT extends Result = Result
> extends Protocol<
ServerRequest | RequestT,
ServerNotification | NotificationT,
ServerResult | ResultT,
ServerContext<ServerRequest | RequestT, ServerNotification | NotificationT>
> {
export class Server extends Protocol<ServerContext> {
private _clientCapabilities?: ClientCapabilities;
private _clientVersion?: Implementation;
private _capabilities: ServerCapabilities;
private _instructions?: string;
private _jsonSchemaValidator: jsonSchemaValidator;
private _experimental?: { tasks: ExperimentalServerTasks<RequestT, NotificationT, ResultT> };
private _experimental?: { tasks: ExperimentalServerTasks };
/**
* Callback for when initialization has fully completed (i.e., the client has sent an `initialized` notification).
@ -159,10 +128,7 @@ export class Server<
}
}
protected override buildContext(
ctx: BaseContext<ServerRequest | RequestT, ServerNotification | NotificationT>,
transportInfo?: MessageExtraInfo
): ServerContext<ServerRequest | RequestT, ServerNotification | NotificationT> {
protected override buildContext(ctx: BaseContext, transportInfo?: MessageExtraInfo): ServerContext {
// Only create http when there's actual HTTP transport info or auth info
const hasHttpInfo =
ctx.http || transportInfo?.requestInfo || transportInfo?.closeSSEStream || transportInfo?.closeStandaloneSSEStream;
@ -192,7 +158,7 @@ export class Server<
*
* @experimental
*/
get experimental(): { tasks: ExperimentalServerTasks<RequestT, NotificationT, ResultT> } {
get experimental(): { tasks: ExperimentalServerTasks } {
if (!this._experimental) {
this._experimental = {
tasks: new ExperimentalServerTasks(this)
@ -230,16 +196,10 @@ export class Server<
*/
public override setRequestHandler<M extends RequestMethod>(
method: M,
handler: (
request: RequestTypeMap[M],
ctx: ServerContext<ServerRequest | RequestT, ServerNotification | NotificationT>
) => ServerResult | ResultT | Promise<ServerResult | ResultT>
handler: (request: RequestTypeMap[M], ctx: ServerContext) => ResultTypeMap[M] | Promise<ResultTypeMap[M]>
): void {
if (method === 'tools/call') {
const wrappedHandler = async (
request: RequestTypeMap[M],
ctx: ServerContext<ServerRequest | RequestT, ServerNotification | NotificationT>
): Promise<ServerResult | ResultT> => {
const wrappedHandler = async (request: RequestTypeMap[M], ctx: ServerContext): Promise<ServerResult> => {
const validatedRequest = parseSchema(CallToolRequestSchema, request);
if (!validatedRequest.success) {
const errorMessage =
@ -283,8 +243,8 @@ export class Server<
return super.setRequestHandler(method, handler);
}
protected assertCapabilityForMethod(method: RequestT['method']): void {
switch (method as ServerRequest['method']) {
protected assertCapabilityForMethod(method: RequestMethod): void {
switch (method) {
case 'sampling/createMessage': {
if (!this._clientCapabilities?.sampling) {
throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Client does not support sampling (required for ${method})`);
@ -316,8 +276,8 @@ export class Server<
}
}
protected assertNotificationCapability(method: (ServerNotification | NotificationT)['method']): void {
switch (method as ServerNotification['method']) {
protected assertNotificationCapability(method: NotificationMethod): void {
switch (method) {
case 'notifications/message': {
if (!this._capabilities.logging) {
throw new SdkError(SdkErrorCode.CapabilityNotSupported, `Server does not support logging (required for ${method})`);