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
21 changed files with 1327 additions and 212 deletions
34
.changeset/support-standard-json-schema.md
Normal file
34
.changeset/support-standard-json-schema.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
'@modelcontextprotocol/core': minor
|
||||
'@modelcontextprotocol/server': minor
|
||||
'@modelcontextprotocol/client': minor
|
||||
---
|
||||
|
||||
Support Standard Schema for tool and prompt schemas
|
||||
|
||||
Tool and prompt registration now accepts any schema library that implements the [Standard Schema spec](https://standardschema.dev/): Zod v4, Valibot, ArkType, and others. `RegisteredTool.inputSchema`, `RegisteredTool.outputSchema`, and `RegisteredPrompt.argsSchema` now use `StandardSchemaWithJSON` (requires both `~standard.validate` and `~standard.jsonSchema`) instead of the Zod-specific `AnySchema` type.
|
||||
|
||||
**Zod v4 schemas continue to work unchanged** — Zod v4 implements the required interfaces natively.
|
||||
|
||||
```typescript
|
||||
import { type } from 'arktype';
|
||||
|
||||
server.registerTool('greet', {
|
||||
inputSchema: type({ name: 'string' })
|
||||
}, async ({ name }) => ({ content: [{ type: 'text', text: `Hello, ${name}!` }] }));
|
||||
```
|
||||
|
||||
For raw JSON Schema (e.g. TypeBox output), use the new `fromJsonSchema` adapter:
|
||||
|
||||
```typescript
|
||||
import { fromJsonSchema, AjvJsonSchemaValidator } from '@modelcontextprotocol/core';
|
||||
|
||||
server.registerTool('greet', {
|
||||
inputSchema: fromJsonSchema({ type: 'object', properties: { name: { type: 'string' } } }, new AjvJsonSchemaValidator())
|
||||
}, handler);
|
||||
```
|
||||
|
||||
**Breaking changes:**
|
||||
- `experimental.tasks.getTaskResult()` no longer accepts a `resultSchema` parameter. Returns `GetTaskPayloadResult` (a loose `Result`); cast to the expected type at the call site.
|
||||
- Removed unused exports from `@modelcontextprotocol/core`: `SchemaInput`, `schemaToJson`, `parseSchemaAsync`, `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema`. Use the new `standardSchemaToJsonSchema` and `validateStandardSchema` instead.
|
||||
- `completable()` remains Zod-specific (it relies on Zod's `.shape` introspection).
|
||||
|
|
@ -209,7 +209,7 @@ if (error instanceof OAuthError && error.code === OAuthErrorCode.InvalidClient)
|
|||
|
||||
The variadic `.tool()`, `.prompt()`, `.resource()` methods are removed. Use the `register*` methods with a config object.
|
||||
|
||||
**IMPORTANT**: v2 requires full Zod schemas — raw shapes like `{ name: z.string() }` are no longer supported. You must wrap with `z.object()`. This applies to `inputSchema`, `outputSchema`, and `argsSchema`.
|
||||
**IMPORTANT**: v2 requires schema objects implementing [Standard Schema](https://standardschema.dev/) — raw shapes like `{ name: z.string() }` are no longer supported. Wrap with `z.object()` (Zod v4), or use ArkType's `type({...})`, or Valibot. For raw JSON Schema, wrap with `fromJsonSchema(schema, validator)` from `@modelcontextprotocol/core`. Applies to `inputSchema`, `outputSchema`, and `argsSchema`.
|
||||
|
||||
### Tools
|
||||
|
||||
|
|
@ -279,13 +279,22 @@ Note: the third argument (`metadata`) is required — pass `{}` if no metadata.
|
|||
|
||||
### Schema Migration Quick Reference
|
||||
|
||||
| v1 (raw shape) | v2 (Zod schema) |
|
||||
| v1 (raw shape) | v2 (Standard Schema object) |
|
||||
|----------------|-----------------|
|
||||
| `{ name: z.string() }` | `z.object({ name: z.string() })` |
|
||||
| `{ count: z.number().optional() }` | `z.object({ count: z.number().optional() })` |
|
||||
| `{}` (empty) | `z.object({})` |
|
||||
| `undefined` (no schema) | `undefined` or omit the field |
|
||||
|
||||
### Removed core exports
|
||||
|
||||
| Removed from `@modelcontextprotocol/core` | Replacement |
|
||||
|---|---|
|
||||
| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` |
|
||||
| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` |
|
||||
| `SchemaInput<T>` | `StandardSchemaWithJSON.InferInput<T>` |
|
||||
| `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema` | none (internal Zod introspection helpers) |
|
||||
|
||||
## 7. Headers API
|
||||
|
||||
Transport constructors and `RequestInfo.headers` now use the Web Standard `Headers` object instead of plain objects.
|
||||
|
|
|
|||
|
|
@ -200,17 +200,17 @@ import * as z from 'zod/v4';
|
|||
const server = new McpServer({ name: 'demo', version: '1.0.0' });
|
||||
|
||||
// Tool with schema
|
||||
server.registerTool('greet', { inputSchema: { name: z.string() } }, async ({ name }) => {
|
||||
server.registerTool('greet', { inputSchema: z.object({ name: z.string() }) }, async ({ name }) => {
|
||||
return { content: [{ type: 'text', text: `Hello, ${name}!` }] };
|
||||
});
|
||||
|
||||
// Tool with description
|
||||
server.registerTool('greet', { description: 'Greet a user', inputSchema: { name: z.string() } }, async ({ name }) => {
|
||||
server.registerTool('greet', { description: 'Greet a user', inputSchema: z.object({ name: z.string() }) }, async ({ name }) => {
|
||||
return { content: [{ type: 'text', text: `Hello, ${name}!` }] };
|
||||
});
|
||||
|
||||
// Prompt
|
||||
server.registerPrompt('summarize', { argsSchema: { text: z.string() } }, async ({ text }) => {
|
||||
server.registerPrompt('summarize', { argsSchema: z.object({ text: z.string() }) }, async ({ text }) => {
|
||||
return { messages: [{ role: 'user', content: { type: 'text', text: `Summarize: ${text}` } }] };
|
||||
});
|
||||
|
||||
|
|
@ -220,9 +220,9 @@ server.registerResource('config', 'config://app', {}, async uri => {
|
|||
});
|
||||
```
|
||||
|
||||
### Zod schemas required (raw shapes no longer supported)
|
||||
### Standard Schema objects required (raw shapes no longer supported)
|
||||
|
||||
v2 requires full Zod schemas for `inputSchema` and `argsSchema`. Raw object shapes are no longer accepted.
|
||||
v2 requires schema objects implementing the [Standard Schema spec](https://standardschema.dev/) for `inputSchema`, `outputSchema`, and `argsSchema`. Raw object shapes are no longer accepted. Zod v4, ArkType, and Valibot all implement the spec.
|
||||
|
||||
**Before (v1):**
|
||||
|
||||
|
|
@ -240,11 +240,23 @@ server.registerTool('greet', {
|
|||
```typescript
|
||||
import * as z from 'zod/v4';
|
||||
|
||||
// Must wrap with z.object()
|
||||
// Wrap with z.object() (or use any Standard Schema library)
|
||||
server.registerTool('greet', {
|
||||
inputSchema: z.object({ name: z.string() }) // full Zod schema
|
||||
inputSchema: z.object({ name: z.string() })
|
||||
}, async ({ name }) => { ... });
|
||||
|
||||
// ArkType works too
|
||||
import { type } from 'arktype';
|
||||
server.registerTool('greet', {
|
||||
inputSchema: type({ name: 'string' })
|
||||
}, async ({ name }) => { ... });
|
||||
|
||||
// Raw JSON Schema via fromJsonSchema
|
||||
import { fromJsonSchema, AjvJsonSchemaValidator } from '@modelcontextprotocol/core';
|
||||
server.registerTool('greet', {
|
||||
inputSchema: fromJsonSchema({ type: 'object', properties: { name: { type: 'string' } } }, new AjvJsonSchemaValidator())
|
||||
}, handler);
|
||||
|
||||
// For tools with no parameters, use z.object({})
|
||||
server.registerTool('ping', {
|
||||
inputSchema: z.object({})
|
||||
|
|
@ -256,6 +268,15 @@ This applies to:
|
|||
- `outputSchema` in `registerTool()`
|
||||
- `argsSchema` in `registerPrompt()`
|
||||
|
||||
**Removed Zod-specific helpers** from `@modelcontextprotocol/core` (use Standard Schema equivalents):
|
||||
|
||||
| Removed | Replacement |
|
||||
|---|---|
|
||||
| `schemaToJson(schema)` | `standardSchemaToJsonSchema(schema)` |
|
||||
| `parseSchemaAsync(schema, data)` | `validateStandardSchema(schema, data)` |
|
||||
| `SchemaInput<T>` | `StandardSchemaWithJSON.InferInput<T>` |
|
||||
| `getSchemaShape`, `getSchemaDescription`, `isOptionalSchema`, `unwrapOptionalSchema` | No replacement — these are now internal Zod introspection helpers |
|
||||
|
||||
### Host header validation moved
|
||||
|
||||
Express-specific middleware (`hostHeaderValidation()`, `localhostHostValidation()`) moved from the server package to `@modelcontextprotocol/express`. The server package now exports framework-agnostic functions instead: `validateHostHeader()`, `localhostAllowedHostnames()`,
|
||||
|
|
|
|||
|
|
@ -34,14 +34,17 @@
|
|||
"dependencies": {
|
||||
"@hono/node-server": "catalog:runtimeServerOnly",
|
||||
"@modelcontextprotocol/examples-shared": "workspace:^",
|
||||
"@modelcontextprotocol/node": "workspace:^",
|
||||
"@modelcontextprotocol/server": "workspace:^",
|
||||
"@modelcontextprotocol/express": "workspace:^",
|
||||
"@modelcontextprotocol/hono": "workspace:^",
|
||||
"@modelcontextprotocol/node": "workspace:^",
|
||||
"@modelcontextprotocol/server": "workspace:^",
|
||||
"@valibot/to-json-schema": "catalog:devTools",
|
||||
"arktype": "catalog:devTools",
|
||||
"better-auth": "^1.4.17",
|
||||
"cors": "catalog:runtimeServerOnly",
|
||||
"express": "catalog:runtimeServerOnly",
|
||||
"hono": "catalog:runtimeServerOnly",
|
||||
"valibot": "catalog:devTools",
|
||||
"zod": "catalog:runtimeShared"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
|||
28
examples/server/src/arktypeExample.ts
Normal file
28
examples/server/src/arktypeExample.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Minimal MCP server using ArkType for schema validation.
|
||||
* ArkType implements the Standard Schema spec with built-in JSON Schema conversion.
|
||||
*/
|
||||
|
||||
import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
|
||||
import { type } from 'arktype';
|
||||
|
||||
const server = new McpServer({
|
||||
name: 'arktype-example',
|
||||
version: '1.0.0'
|
||||
});
|
||||
|
||||
// Register a tool with ArkType schema
|
||||
server.registerTool(
|
||||
'greet',
|
||||
{
|
||||
description: 'Generate a greeting',
|
||||
inputSchema: type({ name: 'string' })
|
||||
},
|
||||
async ({ name }) => ({
|
||||
content: [{ type: 'text', text: `Hello, ${name}!` }]
|
||||
})
|
||||
);
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
30
examples/server/src/valibotExample.ts
Normal file
30
examples/server/src/valibotExample.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env node
|
||||
/**
|
||||
* Minimal MCP server using Valibot for schema validation.
|
||||
* Use toStandardJsonSchema() from @valibot/to-json-schema to create
|
||||
* StandardJSONSchemaV1-compliant schemas.
|
||||
*/
|
||||
|
||||
import { McpServer, StdioServerTransport } from '@modelcontextprotocol/server';
|
||||
import { toStandardJsonSchema } from '@valibot/to-json-schema';
|
||||
import * as v from 'valibot';
|
||||
|
||||
const server = new McpServer({
|
||||
name: 'valibot-example',
|
||||
version: '1.0.0'
|
||||
});
|
||||
|
||||
// Register a tool with Valibot schema
|
||||
server.registerTool(
|
||||
'greet',
|
||||
{
|
||||
description: 'Generate a greeting',
|
||||
inputSchema: toStandardJsonSchema(v.object({ name: v.string() }))
|
||||
},
|
||||
async ({ name }) => ({
|
||||
content: [{ type: 'text', text: `Hello, ${name}!` }]
|
||||
})
|
||||
);
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
|
|
@ -6,20 +6,19 @@
|
|||
*/
|
||||
|
||||
import type {
|
||||
AnyObjectSchema,
|
||||
CallToolRequest,
|
||||
CallToolResult,
|
||||
CancelTaskResult,
|
||||
CreateTaskResult,
|
||||
GetTaskPayloadResult,
|
||||
GetTaskResult,
|
||||
ListTasksResult,
|
||||
RequestMethod,
|
||||
RequestOptions,
|
||||
ResponseMessage,
|
||||
ResultTypeMap,
|
||||
SchemaOutput
|
||||
ResultTypeMap
|
||||
} from '@modelcontextprotocol/core';
|
||||
import { ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/core';
|
||||
import { GetTaskPayloadResultSchema, ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/core';
|
||||
|
||||
import type { Client } from '../../client/client.js';
|
||||
|
||||
|
|
@ -185,23 +184,22 @@ export class ExperimentalClientTasks {
|
|||
* 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 AnyObjectSchema>(taskId: string, resultSchema?: T, options?: RequestOptions): Promise<SchemaOutput<T>> {
|
||||
// Delegate to the client's underlying Protocol method
|
||||
async getTaskResult(taskId: string, options?: RequestOptions): Promise<GetTaskPayloadResult> {
|
||||
return (
|
||||
this._client as unknown as {
|
||||
getTaskResult: <U extends AnyObjectSchema>(
|
||||
getTaskResult: (
|
||||
params: { taskId: string },
|
||||
resultSchema?: U,
|
||||
resultSchema: typeof GetTaskPayloadResultSchema,
|
||||
options?: RequestOptions
|
||||
) => Promise<SchemaOutput<U>>;
|
||||
) => Promise<GetTaskPayloadResult>;
|
||||
}
|
||||
).getTaskResult({ taskId }, resultSchema, options);
|
||||
).getTaskResult({ taskId }, GetTaskPayloadResultSchema, options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -12,11 +12,13 @@ export * from './shared/uriTemplate.js';
|
|||
export * from './types/types.js';
|
||||
export * from './util/inMemory.js';
|
||||
export * from './util/schema.js';
|
||||
export * from './util/standardSchema.js';
|
||||
|
||||
// experimental exports
|
||||
export * from './experimental/index.js';
|
||||
export * from './validators/ajvProvider.js';
|
||||
export * from './validators/cfWorkerProvider.js';
|
||||
export * from './validators/fromJsonSchema.js';
|
||||
/**
|
||||
* JSON Schema validation
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,34 +1,25 @@
|
|||
/**
|
||||
* Internal Zod schema utilities for protocol handling.
|
||||
* These are used internally by the SDK for protocol message validation.
|
||||
*/
|
||||
|
||||
import * as z from 'zod/v4';
|
||||
|
||||
/**
|
||||
* Base type for any Zod schema.
|
||||
* This is the canonical type to use when accepting user-provided schemas.
|
||||
*/
|
||||
export type AnySchema = z.core.$ZodType;
|
||||
|
||||
/**
|
||||
* A Zod schema for objects specifically (not unions).
|
||||
* Use this when you need to constrain to ZodObject schemas.
|
||||
* A Zod schema for objects specifically.
|
||||
*/
|
||||
export type AnyObjectSchema = z.core.$ZodObject;
|
||||
|
||||
/**
|
||||
* Extracts the input type from a Zod schema.
|
||||
*/
|
||||
export type SchemaInput<T extends AnySchema> = z.input<T>;
|
||||
|
||||
/**
|
||||
* Extracts the output type from a Zod schema.
|
||||
*/
|
||||
export type SchemaOutput<T extends AnySchema> = z.output<T>;
|
||||
|
||||
/**
|
||||
* Converts a Zod schema to JSON Schema.
|
||||
*/
|
||||
export function schemaToJson(schema: AnySchema, options?: { io?: 'input' | 'output' }): Record<string, unknown> {
|
||||
return z.toJSONSchema(schema, options) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses data against a Zod schema (synchronous).
|
||||
* Returns a discriminated union with success/error.
|
||||
|
|
@ -39,56 +30,3 @@ export function parseSchema<T extends AnySchema>(
|
|||
): { success: true; data: z.output<T> } | { success: false; error: z.core.$ZodError } {
|
||||
return z.safeParse(schema, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses data against a Zod schema (asynchronous).
|
||||
* Returns a discriminated union with success/error.
|
||||
*/
|
||||
export function parseSchemaAsync<T extends AnySchema>(
|
||||
schema: T,
|
||||
data: unknown
|
||||
): Promise<{ success: true; data: z.output<T> } | { success: false; error: z.core.$ZodError }> {
|
||||
return z.safeParseAsync(schema, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shape of an object schema.
|
||||
* Returns undefined if the schema is not an object schema.
|
||||
*/
|
||||
export function getSchemaShape(schema: AnySchema): Record<string, AnySchema> | undefined {
|
||||
const candidate = schema as { shape?: unknown };
|
||||
if (candidate.shape && typeof candidate.shape === 'object') {
|
||||
return candidate.shape as Record<string, AnySchema>;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the description from a schema if it has one.
|
||||
*/
|
||||
export function getSchemaDescription(schema: AnySchema): string | undefined {
|
||||
const candidate = schema as { description?: string };
|
||||
return candidate.description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a schema is optional (accepts undefined).
|
||||
* Uses the public .type property which works in both zod/v4 and zod/v4/mini.
|
||||
*/
|
||||
export function isOptionalSchema(schema: AnySchema): boolean {
|
||||
const candidate = schema as { type?: string };
|
||||
return candidate.type === 'optional';
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps an optional schema to get the inner schema.
|
||||
* If the schema is not optional, returns it unchanged.
|
||||
* Uses the public .def.innerType property which works in both zod/v4 and zod/v4/mini.
|
||||
*/
|
||||
export function unwrapOptionalSchema(schema: AnySchema): AnySchema {
|
||||
if (!isOptionalSchema(schema)) {
|
||||
return schema;
|
||||
}
|
||||
const candidate = schema as { def?: { innerType?: AnySchema } };
|
||||
return candidate.def?.innerType ?? schema;
|
||||
}
|
||||
|
|
|
|||
180
packages/core/src/util/standardSchema.ts
Normal file
180
packages/core/src/util/standardSchema.ts
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/**
|
||||
* Standard Schema utilities for user-provided schemas.
|
||||
* Supports Zod v4, Valibot, ArkType, and other Standard Schema implementations.
|
||||
* @see https://standardschema.dev
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
|
||||
// Standard Schema interfaces — vendored from https://standardschema.dev (spec v1, Jan 2025)
|
||||
|
||||
export interface StandardTypedV1<Input = unknown, Output = Input> {
|
||||
readonly '~standard': StandardTypedV1.Props<Input, Output>;
|
||||
}
|
||||
|
||||
export namespace StandardTypedV1 {
|
||||
export interface Props<Input = unknown, Output = Input> {
|
||||
readonly version: 1;
|
||||
readonly vendor: string;
|
||||
readonly types?: Types<Input, Output> | undefined;
|
||||
}
|
||||
|
||||
export interface Types<Input = unknown, Output = Input> {
|
||||
readonly input: Input;
|
||||
readonly output: Output;
|
||||
}
|
||||
|
||||
export type InferInput<Schema extends StandardTypedV1> = NonNullable<Schema['~standard']['types']>['input'];
|
||||
export type InferOutput<Schema extends StandardTypedV1> = NonNullable<Schema['~standard']['types']>['output'];
|
||||
}
|
||||
|
||||
export interface StandardSchemaV1<Input = unknown, Output = Input> {
|
||||
readonly '~standard': StandardSchemaV1.Props<Input, Output>;
|
||||
}
|
||||
|
||||
export namespace StandardSchemaV1 {
|
||||
export interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
|
||||
readonly validate: (value: unknown, options?: Options | undefined) => Result<Output> | Promise<Result<Output>>;
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
readonly libraryOptions?: Record<string, unknown> | undefined;
|
||||
}
|
||||
|
||||
export type Result<Output> = SuccessResult<Output> | FailureResult;
|
||||
|
||||
export interface SuccessResult<Output> {
|
||||
readonly value: Output;
|
||||
readonly issues?: undefined;
|
||||
}
|
||||
|
||||
export interface FailureResult {
|
||||
readonly issues: ReadonlyArray<Issue>;
|
||||
}
|
||||
|
||||
export interface Issue {
|
||||
readonly message: string;
|
||||
readonly path?: ReadonlyArray<PropertyKey | PathSegment> | undefined;
|
||||
}
|
||||
|
||||
export interface PathSegment {
|
||||
readonly key: PropertyKey;
|
||||
}
|
||||
|
||||
export type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
||||
export type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
||||
}
|
||||
|
||||
export interface StandardJSONSchemaV1<Input = unknown, Output = Input> {
|
||||
readonly '~standard': StandardJSONSchemaV1.Props<Input, Output>;
|
||||
}
|
||||
|
||||
export namespace StandardJSONSchemaV1 {
|
||||
export interface Props<Input = unknown, Output = Input> extends StandardTypedV1.Props<Input, Output> {
|
||||
readonly jsonSchema: Converter;
|
||||
}
|
||||
|
||||
export interface Converter {
|
||||
readonly input: (options: Options) => Record<string, unknown>;
|
||||
readonly output: (options: Options) => Record<string, unknown>;
|
||||
}
|
||||
|
||||
export type Target = 'draft-2020-12' | 'draft-07' | 'openapi-3.0' | (object & string);
|
||||
|
||||
export interface Options {
|
||||
readonly target: Target;
|
||||
readonly libraryOptions?: Record<string, unknown> | undefined;
|
||||
}
|
||||
|
||||
export type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
||||
export type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combined interface for schemas with both validation and JSON Schema conversion —
|
||||
* the intersection of {@linkcode StandardSchemaV1} and {@linkcode StandardJSONSchemaV1}.
|
||||
*
|
||||
* This is the type accepted by `registerTool` / `registerPrompt`. The SDK needs
|
||||
* `~standard.jsonSchema` to advertise the tool's argument shape in `tools/list`, and
|
||||
* `~standard.validate` to check incoming arguments when a `tools/call` arrives.
|
||||
*
|
||||
* Zod v4, ArkType, and Valibot (via `@valibot/to-json-schema`'s `toStandardJsonSchema`)
|
||||
* all implement both interfaces.
|
||||
*
|
||||
* @see https://standardschema.dev/ for the Standard Schema specification
|
||||
*/
|
||||
export interface StandardSchemaWithJSON<Input = unknown, Output = Input> {
|
||||
readonly '~standard': StandardSchemaV1.Props<Input, Output> & StandardJSONSchemaV1.Props<Input, Output>;
|
||||
}
|
||||
|
||||
export namespace StandardSchemaWithJSON {
|
||||
export type InferInput<Schema extends StandardTypedV1> = StandardTypedV1.InferInput<Schema>;
|
||||
export type InferOutput<Schema extends StandardTypedV1> = StandardTypedV1.InferOutput<Schema>;
|
||||
}
|
||||
|
||||
// Type guards
|
||||
|
||||
export function isStandardJSONSchema(schema: unknown): schema is StandardJSONSchemaV1 {
|
||||
if (schema == null) return false;
|
||||
const schemaType = typeof schema;
|
||||
if (schemaType !== 'object' && schemaType !== 'function') return false;
|
||||
if (!('~standard' in (schema as object))) return false;
|
||||
const std = (schema as StandardJSONSchemaV1)['~standard'];
|
||||
return typeof std?.jsonSchema?.input === 'function' && typeof std?.jsonSchema?.output === 'function';
|
||||
}
|
||||
|
||||
export function isStandardSchema(schema: unknown): schema is StandardSchemaV1 {
|
||||
if (schema == null) return false;
|
||||
const schemaType = typeof schema;
|
||||
if (schemaType !== 'object' && schemaType !== 'function') return false;
|
||||
if (!('~standard' in (schema as object))) return false;
|
||||
const std = (schema as StandardSchemaV1)['~standard'];
|
||||
return typeof std?.validate === 'function';
|
||||
}
|
||||
|
||||
export function isStandardSchemaWithJSON(schema: unknown): schema is StandardSchemaWithJSON {
|
||||
return isStandardJSONSchema(schema) && isStandardSchema(schema);
|
||||
}
|
||||
|
||||
// JSON Schema conversion
|
||||
|
||||
export function standardSchemaToJsonSchema(schema: StandardJSONSchemaV1, io: 'input' | 'output' = 'input'): Record<string, unknown> {
|
||||
return schema['~standard'].jsonSchema[io]({ target: 'draft-2020-12' });
|
||||
}
|
||||
|
||||
// Validation
|
||||
|
||||
export type StandardSchemaValidationResult<T> = { success: true; data: T } | { success: false; error: string };
|
||||
|
||||
function formatIssue(issue: StandardSchemaV1.Issue): string {
|
||||
if (!issue.path?.length) return issue.message;
|
||||
const path = issue.path.map(p => String(typeof p === 'object' ? p.key : p)).join('.');
|
||||
return `${path}: ${issue.message}`;
|
||||
}
|
||||
|
||||
export async function validateStandardSchema<T extends StandardSchemaWithJSON>(
|
||||
schema: T,
|
||||
data: unknown
|
||||
): Promise<StandardSchemaValidationResult<StandardSchemaWithJSON.InferOutput<T>>> {
|
||||
const result = await schema['~standard'].validate(data);
|
||||
if (result.issues && result.issues.length > 0) {
|
||||
return { success: false, error: result.issues.map(i => formatIssue(i)).join(', ') };
|
||||
}
|
||||
return { success: true, data: (result as StandardSchemaV1.SuccessResult<unknown>).value as StandardSchemaWithJSON.InferOutput<T> };
|
||||
}
|
||||
|
||||
// Prompt argument extraction
|
||||
|
||||
export function promptArgumentsFromStandardSchema(
|
||||
schema: StandardJSONSchemaV1
|
||||
): Array<{ name: string; description?: string; required: boolean }> {
|
||||
const jsonSchema = standardSchemaToJsonSchema(schema, 'input');
|
||||
const properties = (jsonSchema.properties as Record<string, { description?: string }>) || {};
|
||||
const required = (jsonSchema.required as string[]) || [];
|
||||
|
||||
return Object.entries(properties).map(([name, prop]) => ({
|
||||
name,
|
||||
description: prop?.description,
|
||||
required: required.includes(name)
|
||||
}));
|
||||
}
|
||||
24
packages/core/src/validators/fromJsonSchema.examples.ts
Normal file
24
packages/core/src/validators/fromJsonSchema.examples.ts
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Type-checked examples for `fromJsonSchema.ts`.
|
||||
*
|
||||
* These examples are synced into JSDoc comments via the sync-snippets script.
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
import { AjvJsonSchemaValidator } from './ajvProvider.js';
|
||||
import { fromJsonSchema } from './fromJsonSchema.js';
|
||||
|
||||
/**
|
||||
* Example: wrap a raw JSON Schema object for use with registerTool.
|
||||
*/
|
||||
function fromJsonSchema_basicUsage() {
|
||||
//#region fromJsonSchema_basicUsage
|
||||
const inputSchema = fromJsonSchema<{ name: string }>(
|
||||
{ type: 'object', properties: { name: { type: 'string' } }, required: ['name'] },
|
||||
new AjvJsonSchemaValidator()
|
||||
);
|
||||
// Use with server.registerTool('greet', { inputSchema }, handler)
|
||||
//#endregion fromJsonSchema_basicUsage
|
||||
return inputSchema;
|
||||
}
|
||||
38
packages/core/src/validators/fromJsonSchema.ts
Normal file
38
packages/core/src/validators/fromJsonSchema.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import type { StandardSchemaV1, StandardSchemaWithJSON } from '../util/standardSchema.js';
|
||||
import type { JsonSchemaType, jsonSchemaValidator } from './types.js';
|
||||
|
||||
/**
|
||||
* Wrap a raw JSON Schema object as a {@linkcode StandardSchemaWithJSON} so it can be
|
||||
* passed to `registerTool` / `registerPrompt`. Use this when you already have JSON
|
||||
* Schema (e.g. from TypeBox, or hand-written) and want to register it without going
|
||||
* through a Standard Schema library.
|
||||
*
|
||||
* The callback arguments will be typed `unknown` (raw JSON Schema has no TypeScript
|
||||
* types attached). Cast at the call site, or use the generic `fromJsonSchema<MyType>(...)`.
|
||||
*
|
||||
* @example
|
||||
* ```ts source="./fromJsonSchema.examples.ts#fromJsonSchema_basicUsage"
|
||||
* const inputSchema = fromJsonSchema<{ name: string }>(
|
||||
* { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] },
|
||||
* new AjvJsonSchemaValidator()
|
||||
* );
|
||||
* // Use with server.registerTool('greet', { inputSchema }, handler)
|
||||
* ```
|
||||
*/
|
||||
export function fromJsonSchema<T = unknown>(schema: JsonSchemaType, validator: jsonSchemaValidator): StandardSchemaWithJSON<T, T> {
|
||||
const check = validator.getValidator<T>(schema);
|
||||
return {
|
||||
'~standard': {
|
||||
version: 1,
|
||||
vendor: 'mcp',
|
||||
jsonSchema: {
|
||||
input: () => schema as Record<string, unknown>,
|
||||
output: () => schema as Record<string, unknown>
|
||||
},
|
||||
validate: (data: unknown): StandardSchemaV1.Result<T> => {
|
||||
const result = check(data);
|
||||
return result.valid ? { value: result.data } : { issues: [{ message: result.errorMessage }] };
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
76
pnpm-lock.yaml
generated
76
pnpm-lock.yaml
generated
|
|
@ -36,6 +36,12 @@ catalogs:
|
|||
'@typescript/native-preview':
|
||||
specifier: ^7.0.0-dev.20251217.1
|
||||
version: 7.0.0-dev.20260105.1
|
||||
'@valibot/to-json-schema':
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0
|
||||
arktype:
|
||||
specifier: ^2.1.29
|
||||
version: 2.2.0
|
||||
eslint:
|
||||
specifier: ^9.39.2
|
||||
version: 9.39.2
|
||||
|
|
@ -66,6 +72,9 @@ catalogs:
|
|||
typescript-eslint:
|
||||
specifier: ^8.48.1
|
||||
version: 8.51.0
|
||||
valibot:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
vite-tsconfig-paths:
|
||||
specifier: ^5.1.4
|
||||
version: 5.1.4
|
||||
|
|
@ -351,6 +360,12 @@ importers:
|
|||
'@modelcontextprotocol/server':
|
||||
specifier: workspace:^
|
||||
version: link:../../packages/server
|
||||
'@valibot/to-json-schema':
|
||||
specifier: catalog:devTools
|
||||
version: 1.5.0(valibot@1.2.0(typescript@5.9.3))
|
||||
arktype:
|
||||
specifier: catalog:devTools
|
||||
version: 2.2.0
|
||||
better-auth:
|
||||
specifier: ^1.4.17
|
||||
version: 1.4.17(better-sqlite3@12.6.2)(vitest@4.0.16(@types/node@24.10.4)(tsx@4.21.0)(yaml@2.8.2))
|
||||
|
|
@ -363,6 +378,9 @@ importers:
|
|||
hono:
|
||||
specifier: catalog:runtimeServerOnly
|
||||
version: 4.11.4
|
||||
valibot:
|
||||
specifier: catalog:devTools
|
||||
version: 1.2.0(typescript@5.9.3)
|
||||
zod:
|
||||
specifier: catalog:runtimeShared
|
||||
version: 4.3.5
|
||||
|
|
@ -963,9 +981,18 @@ importers:
|
|||
'@modelcontextprotocol/vitest-config':
|
||||
specifier: workspace:^
|
||||
version: link:../../common/vitest-config
|
||||
'@valibot/to-json-schema':
|
||||
specifier: catalog:devTools
|
||||
version: 1.5.0(valibot@1.2.0(typescript@5.9.3))
|
||||
arktype:
|
||||
specifier: catalog:devTools
|
||||
version: 2.2.0
|
||||
supertest:
|
||||
specifier: catalog:devTools
|
||||
version: 7.1.4
|
||||
valibot:
|
||||
specifier: catalog:devTools
|
||||
version: 1.2.0(typescript@5.9.3)
|
||||
vitest:
|
||||
specifier: catalog:devTools
|
||||
version: 4.0.16(@types/node@24.10.4)(tsx@4.21.0)(yaml@2.8.2)
|
||||
|
|
@ -987,6 +1014,12 @@ packages:
|
|||
zod:
|
||||
optional: true
|
||||
|
||||
'@ark/schema@0.56.0':
|
||||
resolution: {integrity: sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==}
|
||||
|
||||
'@ark/util@0.56.0':
|
||||
resolution: {integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==}
|
||||
|
||||
'@babel/generator@7.28.5':
|
||||
resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
|
@ -2451,6 +2484,11 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@valibot/to-json-schema@1.5.0':
|
||||
resolution: {integrity: sha512-GE7DmSr1C2UCWPiV0upRH6mv0cCPsqYGs819fb6srCS1tWhyXrkGGe+zxUiwzn/L1BOfADH4sNjY/YHCuP8phQ==}
|
||||
peerDependencies:
|
||||
valibot: ^1.2.0
|
||||
|
||||
'@vitest/expect@4.0.16':
|
||||
resolution: {integrity: sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==}
|
||||
|
||||
|
|
@ -2530,6 +2568,12 @@ packages:
|
|||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
arkregex@0.0.5:
|
||||
resolution: {integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==}
|
||||
|
||||
arktype@2.2.0:
|
||||
resolution: {integrity: sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==}
|
||||
|
||||
array-buffer-byte-length@1.0.2:
|
||||
resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
|
@ -4512,6 +4556,14 @@ packages:
|
|||
util-deprecate@1.0.2:
|
||||
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
|
||||
|
||||
valibot@1.2.0:
|
||||
resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==}
|
||||
peerDependencies:
|
||||
typescript: '>=5'
|
||||
peerDependenciesMeta:
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
vary@1.1.2:
|
||||
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
|
@ -4714,6 +4766,12 @@ snapshots:
|
|||
optionalDependencies:
|
||||
zod: 4.3.5
|
||||
|
||||
'@ark/schema@0.56.0':
|
||||
dependencies:
|
||||
'@ark/util': 0.56.0
|
||||
|
||||
'@ark/util@0.56.0': {}
|
||||
|
||||
'@babel/generator@7.28.5':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
|
|
@ -5956,6 +6014,10 @@ snapshots:
|
|||
'@unrs/resolver-binding-win32-x64-msvc@1.11.1':
|
||||
optional: true
|
||||
|
||||
'@valibot/to-json-schema@1.5.0(valibot@1.2.0(typescript@5.9.3))':
|
||||
dependencies:
|
||||
valibot: 1.2.0(typescript@5.9.3)
|
||||
|
||||
'@vitest/expect@4.0.16':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.1.0
|
||||
|
|
@ -6040,6 +6102,16 @@ snapshots:
|
|||
|
||||
argparse@2.0.1: {}
|
||||
|
||||
arkregex@0.0.5:
|
||||
dependencies:
|
||||
'@ark/util': 0.56.0
|
||||
|
||||
arktype@2.2.0:
|
||||
dependencies:
|
||||
'@ark/schema': 0.56.0
|
||||
'@ark/util': 0.56.0
|
||||
arkregex: 0.0.5
|
||||
|
||||
array-buffer-byte-length@1.0.2:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
|
|
@ -8239,6 +8311,10 @@ snapshots:
|
|||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
valibot@1.2.0(typescript@5.9.3):
|
||||
optionalDependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
vary@1.1.2: {}
|
||||
|
||||
vite-tsconfig-paths@5.1.4(typescript@5.9.3)(vite@7.3.0(@types/node@24.10.4)(tsx@4.21.0)(yaml@2.8.2)):
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ packages:
|
|||
catalogs:
|
||||
devTools:
|
||||
'@eslint/js': ^9.39.2
|
||||
'@valibot/to-json-schema': ^1.5.0
|
||||
arktype: ^2.1.29
|
||||
valibot: ^1.2.0
|
||||
wrangler: ^4.14.4
|
||||
'@types/content-type': ^1.1.8
|
||||
'@types/cors': ^2.8.17
|
||||
|
|
|
|||
|
|
@ -34,19 +34,22 @@
|
|||
"test:integration:deno": "deno test --no-check --allow-net --allow-read --allow-env test/server/deno.test.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@modelcontextprotocol/core": "workspace:^",
|
||||
"@cfworker/json-schema": "catalog:runtimeShared",
|
||||
"@modelcontextprotocol/client": "workspace:^",
|
||||
"@modelcontextprotocol/server": "workspace:^",
|
||||
"@modelcontextprotocol/core": "workspace:^",
|
||||
"@modelcontextprotocol/eslint-config": "workspace:^",
|
||||
"@modelcontextprotocol/express": "workspace:^",
|
||||
"@modelcontextprotocol/node": "workspace:^",
|
||||
"@cfworker/json-schema": "catalog:runtimeShared",
|
||||
"zod": "catalog:runtimeShared",
|
||||
"vitest": "catalog:devTools",
|
||||
"supertest": "catalog:devTools",
|
||||
"wrangler": "catalog:devTools",
|
||||
"@modelcontextprotocol/server": "workspace:^",
|
||||
"@modelcontextprotocol/test-helpers": "workspace:^",
|
||||
"@modelcontextprotocol/tsconfig": "workspace:^",
|
||||
"@modelcontextprotocol/vitest-config": "workspace:^",
|
||||
"@modelcontextprotocol/eslint-config": "workspace:^",
|
||||
"@modelcontextprotocol/test-helpers": "workspace:^"
|
||||
"@valibot/to-json-schema": "catalog:devTools",
|
||||
"arktype": "catalog:devTools",
|
||||
"supertest": "catalog:devTools",
|
||||
"valibot": "catalog:devTools",
|
||||
"vitest": "catalog:devTools",
|
||||
"wrangler": "catalog:devTools",
|
||||
"zod": "catalog:runtimeShared"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue