v2 refactor!: remove Zod schema parameter from public-facing APIs (#1606)
This commit is contained in:
parent
1a78b0138f
commit
f88c376c52
3 changed files with 1483 additions and 1708 deletions
|
|
@ -7,7 +7,6 @@ import type {
|
|||
ClientNotification,
|
||||
ClientRequest,
|
||||
ClientResult,
|
||||
CompatibilityCallToolResultSchema,
|
||||
CompleteRequest,
|
||||
GetPromptRequest,
|
||||
Implementation,
|
||||
|
|
@ -476,7 +475,7 @@ export class Client extends Protocol<ClientContext> {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
const result = await this.request(
|
||||
const result = await this._requestWithSchema(
|
||||
{
|
||||
method: 'initialize',
|
||||
params: {
|
||||
|
|
@ -709,22 +708,22 @@ export class Client extends Protocol<ClientContext> {
|
|||
|
||||
/** Sends a ping to the server to check connectivity. */
|
||||
async ping(options?: RequestOptions) {
|
||||
return this.request({ method: 'ping' }, EmptyResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'ping' }, EmptyResultSchema, options);
|
||||
}
|
||||
|
||||
/** Requests argument autocompletion suggestions from the server for a prompt or resource. */
|
||||
async complete(params: CompleteRequest['params'], options?: RequestOptions) {
|
||||
return this.request({ method: 'completion/complete', params }, CompleteResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'completion/complete', params }, CompleteResultSchema, options);
|
||||
}
|
||||
|
||||
/** Sets the minimum severity level for log messages sent by the server. */
|
||||
async setLoggingLevel(level: LoggingLevel, options?: RequestOptions) {
|
||||
return this.request({ method: 'logging/setLevel', params: { level } }, EmptyResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'logging/setLevel', params: { level } }, EmptyResultSchema, options);
|
||||
}
|
||||
|
||||
/** Retrieves a prompt by name from the server, passing the given arguments for template substitution. */
|
||||
async getPrompt(params: GetPromptRequest['params'], options?: RequestOptions) {
|
||||
return this.request({ method: 'prompts/get', params }, GetPromptResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'prompts/get', params }, GetPromptResultSchema, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -754,7 +753,7 @@ export class Client extends Protocol<ClientContext> {
|
|||
console.debug('Client.listPrompts() called but server does not advertise prompts capability - returning empty list');
|
||||
return { prompts: [] };
|
||||
}
|
||||
return this.request({ method: 'prompts/list', params }, ListPromptsResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'prompts/list', params }, ListPromptsResultSchema, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -784,7 +783,7 @@ export class Client extends Protocol<ClientContext> {
|
|||
console.debug('Client.listResources() called but server does not advertise resources capability - returning empty list');
|
||||
return { resources: [] };
|
||||
}
|
||||
return this.request({ method: 'resources/list', params }, ListResourcesResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'resources/list', params }, ListResourcesResultSchema, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -801,22 +800,22 @@ export class Client extends Protocol<ClientContext> {
|
|||
);
|
||||
return { resourceTemplates: [] };
|
||||
}
|
||||
return this.request({ method: 'resources/templates/list', params }, ListResourceTemplatesResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'resources/templates/list', params }, ListResourceTemplatesResultSchema, options);
|
||||
}
|
||||
|
||||
/** Reads the contents of a resource by URI. */
|
||||
async readResource(params: ReadResourceRequest['params'], options?: RequestOptions) {
|
||||
return this.request({ method: 'resources/read', params }, ReadResourceResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'resources/read', params }, ReadResourceResultSchema, options);
|
||||
}
|
||||
|
||||
/** Subscribes to change notifications for a resource. The server must support resource subscriptions. */
|
||||
async subscribeResource(params: SubscribeRequest['params'], options?: RequestOptions) {
|
||||
return this.request({ method: 'resources/subscribe', params }, EmptyResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'resources/subscribe', params }, EmptyResultSchema, options);
|
||||
}
|
||||
|
||||
/** Unsubscribes from change notifications for a resource. */
|
||||
async unsubscribeResource(params: UnsubscribeRequest['params'], options?: RequestOptions) {
|
||||
return this.request({ method: 'resources/unsubscribe', params }, EmptyResultSchema, options);
|
||||
return this._requestWithSchema({ method: 'resources/unsubscribe', params }, EmptyResultSchema, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -858,11 +857,7 @@ export class Client extends Protocol<ClientContext> {
|
|||
* }
|
||||
* ```
|
||||
*/
|
||||
async callTool(
|
||||
params: CallToolRequest['params'],
|
||||
resultSchema: typeof CallToolResultSchema | typeof CompatibilityCallToolResultSchema = CallToolResultSchema,
|
||||
options?: RequestOptions
|
||||
) {
|
||||
async callTool(params: CallToolRequest['params'], options?: RequestOptions) {
|
||||
// Guard: required-task tools need experimental API
|
||||
if (this.isToolTaskRequired(params.name)) {
|
||||
throw new ProtocolError(
|
||||
|
|
@ -871,7 +866,7 @@ export class Client extends Protocol<ClientContext> {
|
|||
);
|
||||
}
|
||||
|
||||
const result = await this.request({ method: 'tools/call', params }, resultSchema, options);
|
||||
const result = await this._requestWithSchema({ method: 'tools/call', params }, CallToolResultSchema, options);
|
||||
|
||||
// Check if the tool has an outputSchema
|
||||
const validator = this.getToolOutputValidator(params.name);
|
||||
|
|
@ -988,7 +983,7 @@ export class Client extends Protocol<ClientContext> {
|
|||
console.debug('Client.listTools() called but server does not advertise tools capability - returning empty list');
|
||||
return { tools: [] };
|
||||
}
|
||||
const result = await this.request({ method: 'tools/list', params }, ListToolsResultSchema, options);
|
||||
const result = await this._requestWithSchema({ method: 'tools/list', params }, ListToolsResultSchema, options);
|
||||
|
||||
// Cache the tools and their output schemas for future validation
|
||||
this.cacheToolMetadata(result.tools);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
* @module
|
||||
*/
|
||||
|
||||
import type { Request, RequestOptions } from '@modelcontextprotocol/core';
|
||||
import { CallToolResultSchema } from '@modelcontextprotocol/core';
|
||||
import type { RequestOptions } from '@modelcontextprotocol/core';
|
||||
|
||||
import type { Client } from '../../client/client.js';
|
||||
|
||||
|
|
@ -44,9 +43,9 @@ async function ExperimentalClientTasks_callToolStream(client: Client) {
|
|||
/**
|
||||
* Example: Using requestStream to consume task lifecycle events for any request type.
|
||||
*/
|
||||
async function ExperimentalClientTasks_requestStream(client: Client, request: Request, options: RequestOptions) {
|
||||
async function ExperimentalClientTasks_requestStream(client: Client, options: RequestOptions) {
|
||||
//#region ExperimentalClientTasks_requestStream
|
||||
const stream = client.experimental.tasks.requestStream(request, CallToolResultSchema, options);
|
||||
const stream = client.experimental.tasks.requestStream({ method: 'tools/call', params: { name: 'my-tool', arguments: {} } }, options);
|
||||
for await (const message of stream) {
|
||||
switch (message.type) {
|
||||
case 'taskCreated': {
|
||||
|
|
|
|||
|
|
@ -8,15 +8,18 @@
|
|||
import type {
|
||||
AnyObjectSchema,
|
||||
CallToolRequest,
|
||||
CallToolResult,
|
||||
CancelTaskResult,
|
||||
CreateTaskResult,
|
||||
GetTaskResult,
|
||||
ListTasksResult,
|
||||
Request,
|
||||
RequestMethod,
|
||||
RequestOptions,
|
||||
ResponseMessage,
|
||||
ResultTypeMap,
|
||||
SchemaOutput
|
||||
} from '@modelcontextprotocol/core';
|
||||
import { CallToolResultSchema, ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/core';
|
||||
import { ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/core';
|
||||
|
||||
import type { Client } from '../../client/client.js';
|
||||
|
||||
|
|
@ -25,11 +28,10 @@ import type { Client } from '../../client/client.js';
|
|||
* @internal
|
||||
*/
|
||||
interface ClientInternal {
|
||||
requestStream<T extends AnyObjectSchema>(
|
||||
request: Request,
|
||||
resultSchema: T,
|
||||
requestStream<M extends RequestMethod>(
|
||||
request: { method: M; params?: Record<string, unknown> },
|
||||
options?: RequestOptions
|
||||
): AsyncGenerator<ResponseMessage<SchemaOutput<T>>, void, void>;
|
||||
): AsyncGenerator<ResponseMessage<ResultTypeMap[M]>, void, void>;
|
||||
isToolTask(toolName: string): boolean;
|
||||
getToolOutputValidator(toolName: string): ((data: unknown) => { valid: boolean; errorMessage?: string }) | undefined;
|
||||
}
|
||||
|
|
@ -90,7 +92,7 @@ export class ExperimentalClientTasks {
|
|||
async *callToolStream(
|
||||
params: CallToolRequest['params'],
|
||||
options?: RequestOptions
|
||||
): AsyncGenerator<ResponseMessage<SchemaOutput<typeof CallToolResultSchema>>, void, void> {
|
||||
): AsyncGenerator<ResponseMessage<CallToolResult | CreateTaskResult>, void, void> {
|
||||
// Access Client's internal methods
|
||||
const clientInternal = this._client as unknown as ClientInternal;
|
||||
|
||||
|
|
@ -102,7 +104,7 @@ export class ExperimentalClientTasks {
|
|||
task: options?.task ?? (clientInternal.isToolTask(params.name) ? {} : undefined)
|
||||
};
|
||||
|
||||
const stream = clientInternal.requestStream({ method: 'tools/call', params }, CallToolResultSchema, optionsWithTask);
|
||||
const stream = clientInternal.requestStream({ method: 'tools/call', params }, optionsWithTask);
|
||||
|
||||
// Get the validator for this tool (if it has an output schema)
|
||||
const validator = clientInternal.getToolOutputValidator(params.name);
|
||||
|
|
@ -110,8 +112,9 @@ export class ExperimentalClientTasks {
|
|||
// Iterate through the stream and validate the final result if needed
|
||||
for await (const message of stream) {
|
||||
// If this is a result message and the tool has an output schema, validate it
|
||||
if (message.type === 'result' && validator) {
|
||||
const result = message.result;
|
||||
// Only validate CallToolResult (has 'content'), not CreateTaskResult (has 'task')
|
||||
if (message.type === 'result' && validator && 'content' in message.result) {
|
||||
const result = message.result as CallToolResult;
|
||||
|
||||
// If tool has outputSchema, it MUST return structuredContent (unless it's an error)
|
||||
if (!result.structuredContent && !result.isError) {
|
||||
|
|
@ -245,7 +248,7 @@ export class ExperimentalClientTasks {
|
|||
*
|
||||
* @example
|
||||
* ```ts source="./client.examples.ts#ExperimentalClientTasks_requestStream"
|
||||
* const stream = client.experimental.tasks.requestStream(request, CallToolResultSchema, options);
|
||||
* const stream = client.experimental.tasks.requestStream({ method: 'tools/call', params: { name: 'my-tool', arguments: {} } }, options);
|
||||
* for await (const message of stream) {
|
||||
* switch (message.type) {
|
||||
* case 'taskCreated': {
|
||||
|
|
@ -269,25 +272,16 @@ export class ExperimentalClientTasks {
|
|||
* ```
|
||||
*
|
||||
* @param request - The request to send
|
||||
* @param resultSchema - Zod schema for validating the result
|
||||
* @param options - Optional request options (timeout, signal, task creation params, etc.)
|
||||
* @returns AsyncGenerator that yields {@linkcode ResponseMessage} objects
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
requestStream<T extends AnyObjectSchema>(
|
||||
request: Request,
|
||||
resultSchema: T,
|
||||
requestStream<M extends RequestMethod>(
|
||||
request: { method: M; params?: Record<string, unknown> },
|
||||
options?: RequestOptions
|
||||
): AsyncGenerator<ResponseMessage<SchemaOutput<T>>, void, void> {
|
||||
): AsyncGenerator<ResponseMessage<ResultTypeMap[M]>, void, void> {
|
||||
// Delegate to the client's underlying Protocol method
|
||||
type ClientWithRequestStream = {
|
||||
requestStream<U extends AnyObjectSchema>(
|
||||
request: Request,
|
||||
resultSchema: U,
|
||||
options?: RequestOptions
|
||||
): AsyncGenerator<ResponseMessage<SchemaOutput<U>>, void, void>;
|
||||
};
|
||||
return (this._client as unknown as ClientWithRequestStream).requestStream(request, resultSchema, options);
|
||||
return (this._client as unknown as ClientInternal).requestStream(request, options);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue