UNPKG

@modelcontextprotocol/sdk

Version:

Model Context Protocol implementation for TypeScript

443 lines 18.8 kB
import { AnySchema, AnyObjectSchema, SchemaOutput } from '../server/zod-compat.js'; import { ClientCapabilities, GetTaskRequest, GetTaskPayloadRequest, ListTasksResultSchema, CancelTaskResultSchema, JSONRPCRequest, Notification, Progress, Request, RequestId, Result, ServerCapabilities, RequestMeta, RequestInfo, GetTaskResult, TaskCreationParams, RelatedTaskMetadata, Task } from '../types.js'; import { Transport, TransportSendOptions } from './transport.js'; import { AuthInfo } from '../server/auth/types.js'; import { TaskStore, TaskMessageQueue, CreateTaskOptions } from '../experimental/tasks/interfaces.js'; import { ResponseMessage } from './responseMessage.js'; /** * Callback for progress notifications. */ export type ProgressCallback = (progress: Progress) => void; /** * Additional initialization options. */ export type ProtocolOptions = { /** * Whether to restrict emitted requests to only those that the remote side has indicated that they can handle, through their advertised capabilities. * * Note that this DOES NOT affect checking of _local_ side capabilities, as it is considered a logic error to mis-specify those. * * Currently this defaults to false, for backwards compatibility with SDK versions that did not advertise capabilities correctly. In future, this will default to true. */ enforceStrictCapabilities?: boolean; /** * An array of notification method names that should be automatically debounced. * Any notifications with a method in this list will be coalesced if they * occur in the same tick of the event loop. * e.g., ['notifications/tools/list_changed'] */ debouncedNotificationMethods?: string[]; /** * Optional task storage implementation. If provided, enables task-related request handlers * and provides task storage capabilities to request handlers. */ taskStore?: TaskStore; /** * Optional task message queue implementation for managing server-initiated messages * that will be delivered through the tasks/result response stream. */ taskMessageQueue?: TaskMessageQueue; /** * Default polling interval (in milliseconds) for task status checks when no pollInterval * is provided by the server. Defaults to 5000ms if not specified. */ defaultTaskPollInterval?: number; /** * Maximum number of messages that can be queued per task for side-channel delivery. * If undefined, the queue size is unbounded. * When the limit is exceeded, the TaskMessageQueue implementation's enqueue() method * will throw an error. It's the implementation's responsibility to handle overflow * appropriately (e.g., by failing the task, dropping messages, etc.). */ maxTaskQueueSize?: number; }; /** * The default request timeout, in miliseconds. */ export declare const DEFAULT_REQUEST_TIMEOUT_MSEC = 60000; /** * Options that can be given per request. */ export type RequestOptions = { /** * If set, requests progress notifications from the remote end (if supported). When progress notifications are received, this callback will be invoked. * * For task-augmented requests: progress notifications continue after CreateTaskResult is returned and stop automatically when the task reaches a terminal status. */ onprogress?: ProgressCallback; /** * Can be used to cancel an in-flight request. This will cause an AbortError to be raised from request(). */ signal?: AbortSignal; /** * A timeout (in milliseconds) for this request. If exceeded, an McpError with code `RequestTimeout` will be raised from request(). * * If not specified, `DEFAULT_REQUEST_TIMEOUT_MSEC` will be used as the timeout. */ timeout?: number; /** * If true, receiving a progress notification will reset the request timeout. * This is useful for long-running operations that send periodic progress updates. * Default: false */ resetTimeoutOnProgress?: boolean; /** * Maximum total time (in milliseconds) to wait for a response. * If exceeded, an McpError with code `RequestTimeout` will be raised, regardless of progress notifications. * If not specified, there is no maximum total timeout. */ maxTotalTimeout?: number; /** * If provided, augments the request with task creation parameters to enable call-now, fetch-later execution patterns. */ task?: TaskCreationParams; /** * If provided, associates this request with a related task. */ relatedTask?: RelatedTaskMetadata; } & TransportSendOptions; /** * Options that can be given per notification. */ export type NotificationOptions = { /** * May be used to indicate to the transport which incoming request to associate this outgoing notification with. */ relatedRequestId?: RequestId; /** * If provided, associates this notification with a related task. */ relatedTask?: RelatedTaskMetadata; }; /** * Options that can be given per request. */ export type TaskRequestOptions = Omit<RequestOptions, 'relatedTask'>; /** * Request-scoped TaskStore interface. */ export interface RequestTaskStore { /** * Creates a new task with the given creation parameters. * The implementation generates a unique taskId and createdAt timestamp. * * @param taskParams - The task creation parameters from the request * @returns The created task object */ createTask(taskParams: CreateTaskOptions): Promise<Task>; /** * Gets the current status of a task. * * @param taskId - The task identifier * @returns The task object * @throws If the task does not exist */ getTask(taskId: string): Promise<Task>; /** * Stores the result of a task and sets its final status. * * @param taskId - The task identifier * @param status - The final status: 'completed' for success, 'failed' for errors * @param result - The result to store */ storeTaskResult(taskId: string, status: 'completed' | 'failed', result: Result): Promise<void>; /** * Retrieves the stored result of a task. * * @param taskId - The task identifier * @returns The stored result */ getTaskResult(taskId: string): Promise<Result>; /** * Updates a task's status (e.g., to 'cancelled', 'failed', 'completed'). * * @param taskId - The task identifier * @param status - The new status * @param statusMessage - Optional diagnostic message for failed tasks or other status information */ updateTaskStatus(taskId: string, status: Task['status'], statusMessage?: string): Promise<void>; /** * Lists tasks, optionally starting from a pagination cursor. * * @param cursor - Optional cursor for pagination * @returns An object containing the tasks array and an optional nextCursor */ listTasks(cursor?: string): Promise<{ tasks: Task[]; nextCursor?: string; }>; } /** * Extra data given to request handlers. */ export type RequestHandlerExtra<SendRequestT extends Request, SendNotificationT extends Notification> = { /** * An abort signal used to communicate if the request was cancelled from the sender's side. */ signal: AbortSignal; /** * Information about a validated access token, provided to request handlers. */ authInfo?: AuthInfo; /** * The session ID from the transport, if available. */ sessionId?: string; /** * Metadata from the original request. */ _meta?: RequestMeta; /** * The JSON-RPC ID of the request being handled. * This can be useful for tracking or logging purposes. */ requestId: RequestId; taskId?: string; taskStore?: RequestTaskStore; taskRequestedTtl?: number | null; /** * The original HTTP request. */ requestInfo?: RequestInfo; /** * Sends a notification that relates to the current request being handled. * * This is used by certain transports to correctly associate related messages. */ sendNotification: (notification: SendNotificationT) => Promise<void>; /** * Sends a request that relates to the current request being handled. * * This is used by certain transports to correctly associate related messages. */ sendRequest: <U extends AnySchema>(request: SendRequestT, resultSchema: U, options?: TaskRequestOptions) => Promise<SchemaOutput<U>>; /** * Closes the SSE stream for this request, triggering client reconnection. * Only available when using StreamableHTTPServerTransport with eventStore configured. * Use this to implement polling behavior during long-running operations. */ closeSSEStream?: () => void; /** * Closes the standalone GET SSE stream, triggering client reconnection. * Only available when using StreamableHTTPServerTransport with eventStore configured. * Use this to implement polling behavior for server-initiated notifications. */ closeStandaloneSSEStream?: () => void; }; /** * Implements MCP protocol framing on top of a pluggable transport, including * features like request/response linking, notifications, and progress. */ export declare abstract class Protocol<SendRequestT extends Request, SendNotificationT extends Notification, SendResultT extends Result> { private _options?; private _transport?; private _requestMessageId; private _requestHandlers; private _requestHandlerAbortControllers; private _notificationHandlers; private _responseHandlers; private _progressHandlers; private _timeoutInfo; private _pendingDebouncedNotifications; private _taskProgressTokens; private _taskStore?; private _taskMessageQueue?; private _requestResolvers; /** * Callback for when the connection is closed for any reason. * * This is invoked when close() is called as well. */ onclose?: () => void; /** * Callback for when an error occurs. * * Note that errors are not necessarily fatal; they are used for reporting any kind of exceptional condition out of band. */ onerror?: (error: Error) => void; /** * A handler to invoke for any request types that do not have their own handler installed. */ fallbackRequestHandler?: (request: JSONRPCRequest, extra: RequestHandlerExtra<SendRequestT, SendNotificationT>) => Promise<SendResultT>; /** * A handler to invoke for any notification types that do not have their own handler installed. */ fallbackNotificationHandler?: (notification: Notification) => Promise<void>; constructor(_options?: ProtocolOptions | undefined); private _oncancel; private _setupTimeout; private _resetTimeout; private _cleanupTimeout; /** * Attaches to the given transport, starts it, and starts listening for messages. * * The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward. */ connect(transport: Transport): Promise<void>; private _onclose; private _onerror; private _onnotification; private _onrequest; private _onprogress; private _onresponse; get transport(): Transport | undefined; /** * Closes the connection. */ close(): Promise<void>; /** * A method to check if a capability is supported by the remote side, for the given method to be called. * * This should be implemented by subclasses. */ protected abstract assertCapabilityForMethod(method: SendRequestT['method']): 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; /** * A method to check if a request handler is supported by the local side, for the given method to be handled. * * This should be implemented by subclasses. */ protected abstract assertRequestHandlerCapability(method: string): void; /** * A method to check if task creation is supported for the given request method. * * This should be implemented by subclasses. */ protected abstract assertTaskCapability(method: string): void; /** * A method to check if task handler is supported by the local side, for the given method to be handled. * * This should be implemented by subclasses. */ protected abstract assertTaskHandlerCapability(method: string): void; /** * Sends a request and returns an AsyncGenerator that yields response messages. * The generator is guaranteed to end with either a 'result' or 'error' message. * * @example * ```typescript * const stream = protocol.requestStream(request, resultSchema, options); * for await (const message of stream) { * switch (message.type) { * case 'taskCreated': * console.log('Task created:', message.task.taskId); * break; * case 'taskStatus': * console.log('Task status:', message.task.status); * break; * case 'result': * console.log('Final result:', message.result); * break; * case 'error': * console.error('Error:', message.error); * break; * } * } * ``` * * @experimental Use `client.experimental.tasks.requestStream()` to access this method. */ protected requestStream<T extends AnySchema>(request: SendRequestT, resultSchema: T, options?: RequestOptions): AsyncGenerator<ResponseMessage<SchemaOutput<T>>, void, void>; /** * Sends a request and waits for a response. * * Do not use this method to emit notifications! Use notification() instead. */ request<T extends AnySchema>(request: SendRequestT, resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>>; /** * Gets the current status of a task. * * @experimental Use `client.experimental.tasks.getTask()` to access this method. */ protected getTask(params: GetTaskRequest['params'], options?: RequestOptions): Promise<GetTaskResult>; /** * Retrieves the result of a completed task. * * @experimental Use `client.experimental.tasks.getTaskResult()` to access this method. */ protected getTaskResult<T extends AnySchema>(params: GetTaskPayloadRequest['params'], resultSchema: T, options?: RequestOptions): Promise<SchemaOutput<T>>; /** * Lists tasks, optionally starting from a pagination cursor. * * @experimental Use `client.experimental.tasks.listTasks()` to access this method. */ protected listTasks(params?: { cursor?: string; }, options?: RequestOptions): Promise<SchemaOutput<typeof ListTasksResultSchema>>; /** * Cancels a specific task. * * @experimental Use `client.experimental.tasks.cancelTask()` to access this method. */ protected cancelTask(params: { taskId: string; }, options?: RequestOptions): Promise<SchemaOutput<typeof CancelTaskResultSchema>>; /** * Emits a notification, which is a one-way message that does not expect a response. */ notification(notification: SendNotificationT, options?: NotificationOptions): Promise<void>; /** * Registers a handler to invoke when this protocol object receives a request with the given method. * * Note that this will replace any previous request handler for the same method. */ setRequestHandler<T extends AnyObjectSchema>(requestSchema: T, handler: (request: SchemaOutput<T>, extra: RequestHandlerExtra<SendRequestT, SendNotificationT>) => SendResultT | Promise<SendResultT>): void; /** * Removes the request handler for the given method. */ removeRequestHandler(method: string): void; /** * Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed. */ assertCanSetRequestHandler(method: string): void; /** * Registers a handler to invoke when this protocol object receives a notification with the given method. * * Note that this will replace any previous notification handler for the same method. */ setNotificationHandler<T extends AnyObjectSchema>(notificationSchema: T, handler: (notification: SchemaOutput<T>) => void | Promise<void>): void; /** * Removes the notification handler for the given method. */ removeNotificationHandler(method: string): void; /** * Cleans up the progress handler associated with a task. * This should be called when a task reaches a terminal status. */ private _cleanupTaskProgressHandler; /** * Enqueues a task-related message for side-channel delivery via tasks/result. * @param taskId The task ID to associate the message with * @param message The message to enqueue * @param sessionId Optional session ID for binding the operation to a specific session * @throws Error if taskStore is not configured or if enqueue fails (e.g., queue overflow) * * Note: If enqueue fails, it's the TaskMessageQueue implementation's responsibility to handle * the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer * simply propagates the error. */ private _enqueueTaskMessage; /** * Clears the message queue for a task and rejects any pending request resolvers. * @param taskId The task ID whose queue should be cleared * @param sessionId Optional session ID for binding the operation to a specific session */ private _clearTaskQueue; /** * Waits for a task update (new messages or status change) with abort signal support. * Uses polling to check for updates at the task's configured poll interval. * @param taskId The task ID to wait for * @param signal Abort signal to cancel the wait * @returns Promise that resolves when an update occurs or rejects if aborted */ private _waitForTaskUpdate; private requestTaskStore; } export declare function mergeCapabilities(base: ServerCapabilities, additional: Partial<ServerCapabilities>): ServerCapabilities; export declare function mergeCapabilities(base: ClientCapabilities, additional: Partial<ClientCapabilities>): ClientCapabilities; //# sourceMappingURL=protocol.d.ts.map