feat: support Standard Schema for tool/prompt schemas (#1689)
Co-authored-by: Matt Carey <mcarey@cloudflare.com>
This commit is contained in:
parent
ccb78f2043
commit
0784be1a67
5 changed files with 1327 additions and 212 deletions
|
|
@ -4,12 +4,12 @@
|
|||
*/
|
||||
|
||||
import type {
|
||||
AnySchema,
|
||||
CallToolResult,
|
||||
CreateTaskResult,
|
||||
CreateTaskServerContext,
|
||||
GetTaskResult,
|
||||
Result,
|
||||
StandardSchemaWithJSON,
|
||||
TaskServerContext
|
||||
} from '@modelcontextprotocol/core';
|
||||
|
||||
|
|
@ -23,18 +23,17 @@ import type { BaseToolCallback } from '../../server/mcp.js';
|
|||
* Handler for creating a task.
|
||||
* @experimental
|
||||
*/
|
||||
export type CreateTaskRequestHandler<ResultT extends Result, Args extends AnySchema | undefined = undefined> = BaseToolCallback<
|
||||
ResultT,
|
||||
CreateTaskServerContext,
|
||||
Args
|
||||
>;
|
||||
export type CreateTaskRequestHandler<
|
||||
SendResultT extends Result,
|
||||
Args extends StandardSchemaWithJSON | undefined = undefined
|
||||
> = BaseToolCallback<SendResultT, CreateTaskServerContext, Args>;
|
||||
|
||||
/**
|
||||
* Handler for task operations (`get`, `getResult`).
|
||||
* @experimental
|
||||
*/
|
||||
export type TaskRequestHandler<ResultT extends Result, Args extends AnySchema | undefined = undefined> = BaseToolCallback<
|
||||
ResultT,
|
||||
export type TaskRequestHandler<SendResultT extends Result, Args extends StandardSchemaWithJSON | undefined = undefined> = BaseToolCallback<
|
||||
SendResultT,
|
||||
TaskServerContext,
|
||||
Args
|
||||
>;
|
||||
|
|
@ -48,7 +47,7 @@ export type TaskRequestHandler<ResultT extends Result, Args extends AnySchema |
|
|||
* @see {@linkcode @modelcontextprotocol/server!experimental/tasks/mcpServer.ExperimentalMcpServerTasks#registerToolTask | registerToolTask} for registration.
|
||||
* @experimental
|
||||
*/
|
||||
export interface ToolTaskHandler<Args extends AnySchema | undefined = undefined> {
|
||||
export interface ToolTaskHandler<Args extends StandardSchemaWithJSON | undefined = undefined> {
|
||||
/**
|
||||
* Called on the initial `tools/call` request.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
* @experimental
|
||||
*/
|
||||
|
||||
import type { AnySchema, TaskToolExecution, ToolAnnotations, ToolExecution } from '@modelcontextprotocol/core';
|
||||
import type { StandardSchemaWithJSON, TaskToolExecution, ToolAnnotations, ToolExecution } from '@modelcontextprotocol/core';
|
||||
|
||||
import type { AnyToolHandler, McpServer, RegisteredTool } from '../../server/mcp.js';
|
||||
import type { ToolTaskHandler } from './interfaces.js';
|
||||
|
|
@ -19,12 +19,12 @@ interface McpServerInternal {
|
|||
name: string,
|
||||
title: string | undefined,
|
||||
description: string | undefined,
|
||||
inputSchema: AnySchema | undefined,
|
||||
outputSchema: AnySchema | undefined,
|
||||
inputSchema: StandardSchemaWithJSON | undefined,
|
||||
outputSchema: StandardSchemaWithJSON | undefined,
|
||||
annotations: ToolAnnotations | undefined,
|
||||
execution: ToolExecution | undefined,
|
||||
_meta: Record<string, unknown> | undefined,
|
||||
handler: AnyToolHandler<AnySchema | undefined>
|
||||
handler: AnyToolHandler<StandardSchemaWithJSON | undefined>
|
||||
): RegisteredTool;
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ export class ExperimentalMcpServerTasks {
|
|||
*
|
||||
* @experimental
|
||||
*/
|
||||
registerToolTask<OutputArgs extends AnySchema | undefined>(
|
||||
registerToolTask<OutputArgs extends StandardSchemaWithJSON | undefined>(
|
||||
name: string,
|
||||
config: {
|
||||
title?: string;
|
||||
|
|
@ -89,7 +89,7 @@ export class ExperimentalMcpServerTasks {
|
|||
handler: ToolTaskHandler<undefined>
|
||||
): RegisteredTool;
|
||||
|
||||
registerToolTask<InputArgs extends AnySchema, OutputArgs extends AnySchema | undefined>(
|
||||
registerToolTask<InputArgs extends StandardSchemaWithJSON, OutputArgs extends StandardSchemaWithJSON | undefined>(
|
||||
name: string,
|
||||
config: {
|
||||
title?: string;
|
||||
|
|
@ -103,7 +103,7 @@ export class ExperimentalMcpServerTasks {
|
|||
handler: ToolTaskHandler<InputArgs>
|
||||
): RegisteredTool;
|
||||
|
||||
registerToolTask<InputArgs extends AnySchema | undefined, OutputArgs extends AnySchema | undefined>(
|
||||
registerToolTask<InputArgs extends StandardSchemaWithJSON | undefined, OutputArgs extends StandardSchemaWithJSON | undefined>(
|
||||
name: string,
|
||||
config: {
|
||||
title?: string;
|
||||
|
|
@ -133,7 +133,7 @@ export class ExperimentalMcpServerTasks {
|
|||
config.annotations,
|
||||
execution,
|
||||
config._meta,
|
||||
handler as AnyToolHandler<AnySchema | undefined>
|
||||
handler as AnyToolHandler<StandardSchemaWithJSON | undefined>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,21 +6,21 @@
|
|||
*/
|
||||
|
||||
import type {
|
||||
AnySchema,
|
||||
CancelTaskResult,
|
||||
CreateMessageRequestParams,
|
||||
CreateMessageResult,
|
||||
ElicitRequestFormParams,
|
||||
ElicitRequestURLParams,
|
||||
ElicitResult,
|
||||
GetTaskPayloadResult,
|
||||
GetTaskResult,
|
||||
ListTasksResult,
|
||||
RequestMethod,
|
||||
RequestOptions,
|
||||
ResponseMessage,
|
||||
ResultTypeMap,
|
||||
SchemaOutput
|
||||
ResultTypeMap
|
||||
} from '@modelcontextprotocol/core';
|
||||
import { GetTaskPayloadResultSchema } from '@modelcontextprotocol/core';
|
||||
|
||||
import type { Server } from '../../server/server.js';
|
||||
|
||||
|
|
@ -258,22 +258,22 @@ export class ExperimentalServerTasks {
|
|||
* Retrieves the result of a completed task.
|
||||
*
|
||||
* @param taskId - The task identifier
|
||||
* @param resultSchema - Zod schema for validating the result
|
||||
* @param options - Optional request options
|
||||
* @returns The task result
|
||||
* @returns The task result. The payload structure matches the result type of the
|
||||
* original request (e.g., a `tools/call` task returns a `CallToolResult`).
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
async getTaskResult<T extends AnySchema>(taskId: string, resultSchema?: T, options?: RequestOptions): Promise<SchemaOutput<T>> {
|
||||
async getTaskResult(taskId: string, options?: RequestOptions): Promise<GetTaskPayloadResult> {
|
||||
return (
|
||||
this._server as unknown as {
|
||||
getTaskResult: <U extends AnySchema>(
|
||||
getTaskResult: (
|
||||
params: { taskId: string },
|
||||
resultSchema?: U,
|
||||
resultSchema: typeof GetTaskPayloadResultSchema,
|
||||
options?: RequestOptions
|
||||
) => Promise<SchemaOutput<U>>;
|
||||
) => Promise<GetTaskPayloadResult>;
|
||||
}
|
||||
).getTaskResult({ taskId }, resultSchema, options);
|
||||
).getTaskResult({ taskId }, GetTaskPayloadResultSchema, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import type { AnySchema } from '@modelcontextprotocol/core';
|
||||
import type * as z from 'zod/v4';
|
||||
import type { StandardSchemaWithJSON } from '@modelcontextprotocol/core';
|
||||
|
||||
export const COMPLETABLE_SYMBOL: unique symbol = Symbol.for('mcp.completable');
|
||||
|
||||
export type CompleteCallback<T extends AnySchema = AnySchema> = (
|
||||
value: z.input<T>,
|
||||
export type CompleteCallback<T extends StandardSchemaWithJSON = StandardSchemaWithJSON> = (
|
||||
value: StandardSchemaWithJSON.InferInput<T>,
|
||||
context?: {
|
||||
arguments?: Record<string, string>;
|
||||
}
|
||||
) => z.input<T>[] | Promise<z.input<T>[]>;
|
||||
) => StandardSchemaWithJSON.InferInput<T>[] | Promise<StandardSchemaWithJSON.InferInput<T>[]>;
|
||||
|
||||
export type CompletableMeta<T extends AnySchema = AnySchema> = {
|
||||
export type CompletableMeta<T extends StandardSchemaWithJSON = StandardSchemaWithJSON> = {
|
||||
complete: CompleteCallback<T>;
|
||||
};
|
||||
|
||||
export type CompletableSchema<T extends AnySchema> = T & {
|
||||
export type CompletableSchema<T extends StandardSchemaWithJSON> = T & {
|
||||
[COMPLETABLE_SYMBOL]: CompletableMeta<T>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps a Zod type to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP.
|
||||
* Wraps a schema to provide autocompletion capabilities. Useful for, e.g., prompt arguments in MCP.
|
||||
*
|
||||
* @example
|
||||
* ```ts source="./completable.examples.ts#completable_basicUsage"
|
||||
|
|
@ -49,7 +48,7 @@ export type CompletableSchema<T extends AnySchema> = T & {
|
|||
*
|
||||
* @see {@linkcode server/mcp.McpServer.registerPrompt | McpServer.registerPrompt} for using completable schemas in prompt argument definitions
|
||||
*/
|
||||
export function completable<T extends AnySchema>(schema: T, complete: CompleteCallback<T>): CompletableSchema<T> {
|
||||
export function completable<T extends StandardSchemaWithJSON>(schema: T, complete: CompleteCallback<T>): CompletableSchema<T> {
|
||||
Object.defineProperty(schema as object, COMPLETABLE_SYMBOL, {
|
||||
value: { complete } as CompletableMeta<T>,
|
||||
enumerable: false,
|
||||
|
|
@ -62,14 +61,14 @@ export function completable<T extends AnySchema>(schema: T, complete: CompleteCa
|
|||
/**
|
||||
* Checks if a schema is completable (has completion metadata).
|
||||
*/
|
||||
export function isCompletable(schema: unknown): schema is CompletableSchema<AnySchema> {
|
||||
export function isCompletable(schema: unknown): schema is CompletableSchema<StandardSchemaWithJSON> {
|
||||
return !!schema && typeof schema === 'object' && COMPLETABLE_SYMBOL in (schema as object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the completer callback from a completable schema, if it exists.
|
||||
*/
|
||||
export function getCompleter<T extends AnySchema>(schema: T): CompleteCallback<T> | undefined {
|
||||
export function getCompleter<T extends StandardSchemaWithJSON>(schema: T): CompleteCallback<T> | undefined {
|
||||
const meta = (schema as unknown as { [COMPLETABLE_SYMBOL]?: CompletableMeta<T> })[COMPLETABLE_SYMBOL];
|
||||
return meta?.complete as CompleteCallback<T> | undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import type {
|
||||
AnySchema,
|
||||
BaseMetadata,
|
||||
CallToolRequest,
|
||||
CallToolResult,
|
||||
|
|
@ -15,14 +14,13 @@ import type {
|
|||
ListToolsResult,
|
||||
LoggingMessageNotification,
|
||||
Prompt,
|
||||
PromptArgument,
|
||||
PromptReference,
|
||||
ReadResourceResult,
|
||||
Resource,
|
||||
ResourceTemplateReference,
|
||||
Result,
|
||||
SchemaOutput,
|
||||
ServerContext,
|
||||
StandardSchemaWithJSON,
|
||||
Tool,
|
||||
ToolAnnotations,
|
||||
ToolExecution,
|
||||
|
|
@ -32,16 +30,13 @@ import type {
|
|||
import {
|
||||
assertCompleteRequestPrompt,
|
||||
assertCompleteRequestResourceTemplate,
|
||||
getSchemaDescription,
|
||||
getSchemaShape,
|
||||
isOptionalSchema,
|
||||
parseSchemaAsync,
|
||||
promptArgumentsFromStandardSchema,
|
||||
ProtocolError,
|
||||
ProtocolErrorCode,
|
||||
schemaToJson,
|
||||
unwrapOptionalSchema,
|
||||
standardSchemaToJsonSchema,
|
||||
UriTemplate,
|
||||
validateAndWarnToolName
|
||||
validateAndWarnToolName,
|
||||
validateStandardSchema
|
||||
} from '@modelcontextprotocol/core';
|
||||
|
||||
import type { ToolTaskHandler } from '../experimental/tasks/interfaces.js';
|
||||
|
|
@ -147,7 +142,7 @@ export class McpServer {
|
|||
title: tool.title,
|
||||
description: tool.description,
|
||||
inputSchema: tool.inputSchema
|
||||
? (schemaToJson(tool.inputSchema, { io: 'input' }) as Tool['inputSchema'])
|
||||
? (standardSchemaToJsonSchema(tool.inputSchema, 'input') as Tool['inputSchema'])
|
||||
: EMPTY_OBJECT_JSON_SCHEMA,
|
||||
annotations: tool.annotations,
|
||||
execution: tool.execution,
|
||||
|
|
@ -155,9 +150,7 @@ export class McpServer {
|
|||
};
|
||||
|
||||
if (tool.outputSchema) {
|
||||
toolDefinition.outputSchema = schemaToJson(tool.outputSchema, {
|
||||
io: 'output'
|
||||
}) as Tool['outputSchema'];
|
||||
toolDefinition.outputSchema = standardSchemaToJsonSchema(tool.outputSchema, 'output') as Tool['outputSchema'];
|
||||
}
|
||||
|
||||
return toolDefinition;
|
||||
|
|
@ -177,7 +170,7 @@ export class McpServer {
|
|||
try {
|
||||
const isTaskRequest = !!request.params.task;
|
||||
const taskSupport = tool.execution?.taskSupport;
|
||||
const isTaskHandler = 'createTask' in (tool.handler as AnyToolHandler<AnySchema>);
|
||||
const isTaskHandler = 'createTask' in (tool.handler as AnyToolHandler<StandardSchemaWithJSON>);
|
||||
|
||||
// Validate task hint configuration
|
||||
if ((taskSupport === 'required' || taskSupport === 'optional') && !isTaskHandler) {
|
||||
|
|
@ -245,23 +238,22 @@ export class McpServer {
|
|||
* Validates tool input arguments against the tool's input schema.
|
||||
*/
|
||||
private async validateToolInput<
|
||||
Tool extends RegisteredTool,
|
||||
Args extends Tool['inputSchema'] extends infer InputSchema
|
||||
? InputSchema extends AnySchema
|
||||
? SchemaOutput<InputSchema>
|
||||
ToolType extends RegisteredTool,
|
||||
Args extends ToolType['inputSchema'] extends infer InputSchema
|
||||
? InputSchema extends StandardSchemaWithJSON
|
||||
? StandardSchemaWithJSON.InferOutput<InputSchema>
|
||||
: undefined
|
||||
: undefined
|
||||
>(tool: Tool, args: Args, toolName: string): Promise<Args> {
|
||||
>(tool: ToolType, args: Args, toolName: string): Promise<Args> {
|
||||
if (!tool.inputSchema) {
|
||||
return undefined as Args;
|
||||
}
|
||||
|
||||
const parseResult = await parseSchemaAsync(tool.inputSchema, args ?? {});
|
||||
const parseResult = await validateStandardSchema(tool.inputSchema, args ?? {});
|
||||
if (!parseResult.success) {
|
||||
const errorMessage = parseResult.error.issues.map((i: { message: string }) => i.message).join(', ');
|
||||
throw new ProtocolError(
|
||||
ProtocolErrorCode.InvalidParams,
|
||||
`Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`
|
||||
`Input validation error: Invalid arguments for tool ${toolName}: ${parseResult.error}`
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -293,12 +285,11 @@ export class McpServer {
|
|||
}
|
||||
|
||||
// if the tool has an output schema, validate structured content
|
||||
const parseResult = await parseSchemaAsync(tool.outputSchema, result.structuredContent);
|
||||
const parseResult = await validateStandardSchema(tool.outputSchema, result.structuredContent);
|
||||
if (!parseResult.success) {
|
||||
const errorMessage = parseResult.error.issues.map((i: { message: string }) => i.message).join(', ');
|
||||
throw new ProtocolError(
|
||||
ProtocolErrorCode.InvalidParams,
|
||||
`Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`
|
||||
`Output validation error: Invalid structured content for tool ${toolName}: ${parseResult.error}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -394,7 +385,7 @@ export class McpServer {
|
|||
}
|
||||
|
||||
const promptShape = getSchemaShape(prompt.argsSchema);
|
||||
const field = promptShape?.[request.params.argument.name];
|
||||
const field = unwrapOptionalSchema(promptShape?.[request.params.argument.name]);
|
||||
if (!isCompletable(field)) {
|
||||
return EMPTY_COMPLETION_RESULT;
|
||||
}
|
||||
|
|
@ -403,6 +394,7 @@ export class McpServer {
|
|||
if (!completer) {
|
||||
return EMPTY_COMPLETION_RESULT;
|
||||
}
|
||||
|
||||
const suggestions = await completer(request.params.argument.value, request.params.context);
|
||||
return createCompletionResult(suggestions);
|
||||
}
|
||||
|
|
@ -538,7 +530,7 @@ export class McpServer {
|
|||
name,
|
||||
title: prompt.title,
|
||||
description: prompt.description,
|
||||
arguments: prompt.argsSchema ? promptArgumentsFromSchema(prompt.argsSchema) : undefined
|
||||
arguments: prompt.argsSchema ? promptArgumentsFromStandardSchema(prompt.argsSchema) : undefined
|
||||
};
|
||||
})
|
||||
})
|
||||
|
|
@ -706,8 +698,8 @@ export class McpServer {
|
|||
name: string,
|
||||
title: string | undefined,
|
||||
description: string | undefined,
|
||||
argsSchema: AnySchema | undefined,
|
||||
callback: PromptCallback<AnySchema | undefined>
|
||||
argsSchema: StandardSchemaWithJSON | undefined,
|
||||
callback: PromptCallback<StandardSchemaWithJSON | undefined>
|
||||
): RegisteredPrompt {
|
||||
// Track current schema and callback for handler regeneration
|
||||
let currentArgsSchema = argsSchema;
|
||||
|
|
@ -738,7 +730,7 @@ export class McpServer {
|
|||
needsHandlerRegen = true;
|
||||
}
|
||||
if (updates.callback !== undefined) {
|
||||
currentCallback = updates.callback as PromptCallback<AnySchema | undefined>;
|
||||
currentCallback = updates.callback as PromptCallback<StandardSchemaWithJSON | undefined>;
|
||||
needsHandlerRegen = true;
|
||||
}
|
||||
if (needsHandlerRegen) {
|
||||
|
|
@ -772,12 +764,12 @@ export class McpServer {
|
|||
name: string,
|
||||
title: string | undefined,
|
||||
description: string | undefined,
|
||||
inputSchema: AnySchema | undefined,
|
||||
outputSchema: AnySchema | undefined,
|
||||
inputSchema: StandardSchemaWithJSON | undefined,
|
||||
outputSchema: StandardSchemaWithJSON | undefined,
|
||||
annotations: ToolAnnotations | undefined,
|
||||
execution: ToolExecution | undefined,
|
||||
_meta: Record<string, unknown> | undefined,
|
||||
handler: AnyToolHandler<AnySchema | undefined>
|
||||
handler: AnyToolHandler<StandardSchemaWithJSON | undefined>
|
||||
): RegisteredTool {
|
||||
// Validate tool name according to SEP specification
|
||||
validateAndWarnToolName(name);
|
||||
|
|
@ -818,7 +810,7 @@ export class McpServer {
|
|||
}
|
||||
if (updates.callback !== undefined) {
|
||||
registeredTool.handler = updates.callback;
|
||||
currentHandler = updates.callback as AnyToolHandler<AnySchema | undefined>;
|
||||
currentHandler = updates.callback as AnyToolHandler<StandardSchemaWithJSON | undefined>;
|
||||
needsExecutorRegen = true;
|
||||
}
|
||||
if (needsExecutorRegen) {
|
||||
|
|
@ -866,7 +858,7 @@ export class McpServer {
|
|||
* );
|
||||
* ```
|
||||
*/
|
||||
registerTool<OutputArgs extends AnySchema, InputArgs extends AnySchema | undefined = undefined>(
|
||||
registerTool<OutputArgs extends StandardSchemaWithJSON, InputArgs extends StandardSchemaWithJSON | undefined = undefined>(
|
||||
name: string,
|
||||
config: {
|
||||
title?: string;
|
||||
|
|
@ -893,7 +885,7 @@ export class McpServer {
|
|||
annotations,
|
||||
{ taskSupport: 'forbidden' },
|
||||
_meta,
|
||||
cb as ToolCallback<AnySchema | undefined>
|
||||
cb as ToolCallback<StandardSchemaWithJSON | undefined>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -923,7 +915,7 @@ export class McpServer {
|
|||
* );
|
||||
* ```
|
||||
*/
|
||||
registerPrompt<Args extends AnySchema>(
|
||||
registerPrompt<Args extends StandardSchemaWithJSON>(
|
||||
name: string,
|
||||
config: {
|
||||
title?: string;
|
||||
|
|
@ -943,7 +935,7 @@ export class McpServer {
|
|||
title,
|
||||
description,
|
||||
argsSchema,
|
||||
cb as PromptCallback<AnySchema | undefined>
|
||||
cb as PromptCallback<StandardSchemaWithJSON | undefined>
|
||||
);
|
||||
|
||||
this.setPromptRequestHandlers();
|
||||
|
|
@ -1064,19 +1056,27 @@ export class ResourceTemplate {
|
|||
}
|
||||
}
|
||||
|
||||
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>;
|
||||
export type BaseToolCallback<
|
||||
SendResultT extends Result,
|
||||
Ctx extends ServerContext,
|
||||
Args extends StandardSchemaWithJSON | undefined
|
||||
> = Args extends StandardSchemaWithJSON
|
||||
? (args: StandardSchemaWithJSON.InferOutput<Args>, ctx: Ctx) => SendResultT | Promise<SendResultT>
|
||||
: (ctx: Ctx) => SendResultT | Promise<SendResultT>;
|
||||
|
||||
/**
|
||||
* Callback for a tool handler registered with {@linkcode McpServer.registerTool}.
|
||||
*/
|
||||
export type ToolCallback<Args extends AnySchema | undefined = undefined> = BaseToolCallback<CallToolResult, ServerContext, Args>;
|
||||
export type ToolCallback<Args extends StandardSchemaWithJSON | undefined = undefined> = BaseToolCallback<
|
||||
CallToolResult,
|
||||
ServerContext,
|
||||
Args
|
||||
>;
|
||||
|
||||
/**
|
||||
* Supertype that can handle both regular tools (simple callback) and task-based tools (task handler object).
|
||||
*/
|
||||
export type AnyToolHandler<Args extends AnySchema | undefined = undefined> = ToolCallback<Args> | ToolTaskHandler<Args>;
|
||||
export type AnyToolHandler<Args extends StandardSchemaWithJSON | undefined = undefined> = ToolCallback<Args> | ToolTaskHandler<Args>;
|
||||
|
||||
/**
|
||||
* Internal executor type that encapsulates handler invocation with proper types.
|
||||
|
|
@ -1086,12 +1086,12 @@ type ToolExecutor = (args: unknown, ctx: ServerContext) => Promise<CallToolResul
|
|||
export type RegisteredTool = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
inputSchema?: AnySchema;
|
||||
outputSchema?: AnySchema;
|
||||
inputSchema?: StandardSchemaWithJSON;
|
||||
outputSchema?: StandardSchemaWithJSON;
|
||||
annotations?: ToolAnnotations;
|
||||
execution?: ToolExecution;
|
||||
_meta?: Record<string, unknown>;
|
||||
handler: AnyToolHandler<AnySchema | undefined>;
|
||||
handler: AnyToolHandler<StandardSchemaWithJSON | undefined>;
|
||||
/** @hidden */
|
||||
executor: ToolExecutor;
|
||||
enabled: boolean;
|
||||
|
|
@ -1101,11 +1101,11 @@ export type RegisteredTool = {
|
|||
name?: string | null;
|
||||
title?: string;
|
||||
description?: string;
|
||||
paramsSchema?: AnySchema;
|
||||
outputSchema?: AnySchema;
|
||||
paramsSchema?: StandardSchemaWithJSON;
|
||||
outputSchema?: StandardSchemaWithJSON;
|
||||
annotations?: ToolAnnotations;
|
||||
_meta?: Record<string, unknown>;
|
||||
callback?: ToolCallback<AnySchema>;
|
||||
callback?: ToolCallback<StandardSchemaWithJSON>;
|
||||
enabled?: boolean;
|
||||
}): void;
|
||||
remove(): void;
|
||||
|
|
@ -1116,7 +1116,10 @@ export type RegisteredTool = {
|
|||
* When `inputSchema` is defined, the handler is called with `(args, ctx)`.
|
||||
* When `inputSchema` is undefined, the handler is called with just `(ctx)`.
|
||||
*/
|
||||
function createToolExecutor(inputSchema: AnySchema | undefined, handler: AnyToolHandler<AnySchema | undefined>): ToolExecutor {
|
||||
function createToolExecutor(
|
||||
inputSchema: StandardSchemaWithJSON | undefined,
|
||||
handler: AnyToolHandler<StandardSchemaWithJSON | undefined>
|
||||
): ToolExecutor {
|
||||
const isTaskHandler = 'createTask' in handler;
|
||||
|
||||
if (isTaskHandler) {
|
||||
|
|
@ -1211,8 +1214,8 @@ export type RegisteredResourceTemplate = {
|
|||
remove(): void;
|
||||
};
|
||||
|
||||
export type PromptCallback<Args extends AnySchema | undefined = undefined> = Args extends AnySchema
|
||||
? (args: SchemaOutput<Args>, ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>
|
||||
export type PromptCallback<Args extends StandardSchemaWithJSON | undefined = undefined> = Args extends StandardSchemaWithJSON
|
||||
? (args: StandardSchemaWithJSON.InferOutput<Args>, ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>
|
||||
: (ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>;
|
||||
|
||||
/**
|
||||
|
|
@ -1230,13 +1233,13 @@ type TaskHandlerInternal = {
|
|||
export type RegisteredPrompt = {
|
||||
title?: string;
|
||||
description?: string;
|
||||
argsSchema?: AnySchema;
|
||||
argsSchema?: StandardSchemaWithJSON;
|
||||
/** @hidden */
|
||||
handler: PromptHandler;
|
||||
enabled: boolean;
|
||||
enable(): void;
|
||||
disable(): void;
|
||||
update<Args extends AnySchema>(updates: {
|
||||
update<Args extends StandardSchemaWithJSON>(updates: {
|
||||
name?: string | null;
|
||||
title?: string;
|
||||
description?: string;
|
||||
|
|
@ -1253,19 +1256,18 @@ export type RegisteredPrompt = {
|
|||
*/
|
||||
function createPromptHandler(
|
||||
name: string,
|
||||
argsSchema: AnySchema | undefined,
|
||||
callback: PromptCallback<AnySchema | undefined>
|
||||
argsSchema: StandardSchemaWithJSON | undefined,
|
||||
callback: PromptCallback<StandardSchemaWithJSON | undefined>
|
||||
): PromptHandler {
|
||||
if (argsSchema) {
|
||||
const typedCallback = callback as (args: SchemaOutput<AnySchema>, ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>;
|
||||
const typedCallback = callback as (args: unknown, ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>;
|
||||
|
||||
return async (args, ctx) => {
|
||||
const parseResult = await parseSchemaAsync(argsSchema, args);
|
||||
const parseResult = await validateStandardSchema(argsSchema, args);
|
||||
if (!parseResult.success) {
|
||||
const errorMessage = parseResult.error.issues.map((i: { message: string }) => i.message).join(', ');
|
||||
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for prompt ${name}: ${errorMessage}`);
|
||||
throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for prompt ${name}: ${parseResult.error}`);
|
||||
}
|
||||
return typedCallback(parseResult.data as SchemaOutput<AnySchema>, ctx);
|
||||
return typedCallback(parseResult.data, ctx);
|
||||
};
|
||||
} else {
|
||||
const typedCallback = callback as (ctx: ServerContext) => GetPromptResult | Promise<GetPromptResult>;
|
||||
|
|
@ -1276,18 +1278,6 @@ function createPromptHandler(
|
|||
}
|
||||
}
|
||||
|
||||
function promptArgumentsFromSchema(schema: AnySchema): PromptArgument[] {
|
||||
const shape = getSchemaShape(schema);
|
||||
if (!shape) return [];
|
||||
return Object.entries(shape).map(([name, field]): PromptArgument => {
|
||||
return {
|
||||
name,
|
||||
description: getSchemaDescription(field),
|
||||
required: !isOptionalSchema(field)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function createCompletionResult(suggestions: readonly unknown[]): CompleteResult {
|
||||
const values = suggestions.map(String).slice(0, 100);
|
||||
return {
|
||||
|
|
@ -1305,3 +1295,27 @@ const EMPTY_COMPLETION_RESULT: CompleteResult = {
|
|||
hasMore: false
|
||||
}
|
||||
};
|
||||
|
||||
/** @internal Gets the shape of a Zod object schema */
|
||||
function getSchemaShape(schema: unknown): Record<string, unknown> | undefined {
|
||||
const candidate = schema as { shape?: unknown };
|
||||
if (candidate.shape && typeof candidate.shape === 'object') {
|
||||
return candidate.shape as Record<string, unknown>;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @internal Checks if a Zod schema is optional */
|
||||
function isOptionalSchema(schema: unknown): boolean {
|
||||
const candidate = schema as { type?: string } | null | undefined;
|
||||
return candidate?.type === 'optional';
|
||||
}
|
||||
|
||||
/** @internal Unwraps an optional Zod schema */
|
||||
function unwrapOptionalSchema(schema: unknown): unknown {
|
||||
if (!isOptionalSchema(schema)) {
|
||||
return schema;
|
||||
}
|
||||
const candidate = schema as { def?: { innerType?: unknown } };
|
||||
return candidate.def?.innerType ?? schema;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue