[RFC] refactor: remove vestigial generic type parameters from Protocol, Server, and Client (#1451)
This commit is contained in:
parent
7bb79ebbbb
commit
faeb03ec17
11 changed files with 210 additions and 306 deletions
|
|
@ -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}`);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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 {}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
>;
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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().
|
||||
|
|
|
|||
|
|
@ -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})`);
|
||||
|
|
|
|||
Loading…
Reference in a new issue