feat: auto-select JSON Schema validator based on runtime (#1456)
This commit is contained in:
parent
1fb7b25697
commit
bc883afe3c
22 changed files with 1176 additions and 107 deletions
|
|
@ -25,6 +25,24 @@
|
|||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"import": "./dist/index.mjs"
|
||||
},
|
||||
"./_shims": {
|
||||
"workerd": {
|
||||
"types": "./dist/shimsWorkerd.d.mts",
|
||||
"import": "./dist/shimsWorkerd.mjs"
|
||||
},
|
||||
"browser": {
|
||||
"types": "./dist/shimsWorkerd.d.mts",
|
||||
"import": "./dist/shimsWorkerd.mjs"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/shimsNode.d.mts",
|
||||
"import": "./dist/shimsNode.mjs"
|
||||
},
|
||||
"default": {
|
||||
"types": "./dist/shimsNode.d.mts",
|
||||
"import": "./dist/shimsNode.mjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/client/_shims';
|
||||
import type {
|
||||
CallToolRequest,
|
||||
ClientCapabilities,
|
||||
|
|
@ -35,7 +36,6 @@ import type {
|
|||
UnsubscribeRequest
|
||||
} from '@modelcontextprotocol/core';
|
||||
import {
|
||||
AjvJsonSchemaValidator,
|
||||
assertClientRequestTaskCapability,
|
||||
assertToolsCallTaskCapability,
|
||||
CallToolResultSchema,
|
||||
|
|
@ -153,28 +153,7 @@ export type ClientOptions = ProtocolOptions & {
|
|||
* The validator is used to validate structured content returned by tools
|
||||
* against their declared output schemas.
|
||||
*
|
||||
* @default AjvJsonSchemaValidator
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // ajv
|
||||
* const client = new Client(
|
||||
* { name: 'my-client', version: '1.0.0' },
|
||||
* {
|
||||
* capabilities: {},
|
||||
* jsonSchemaValidator: new AjvJsonSchemaValidator()
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* // @cfworker/json-schema
|
||||
* const client = new Client(
|
||||
* { name: 'my-client', version: '1.0.0' },
|
||||
* {
|
||||
* capabilities: {},
|
||||
* jsonSchemaValidator: new CfWorkerJsonSchemaValidator()
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
* @default DefaultJsonSchemaValidator (AjvJsonSchemaValidator on Node.js, CfWorkerJsonSchemaValidator on Cloudflare Workers)
|
||||
*/
|
||||
jsonSchemaValidator?: jsonSchemaValidator;
|
||||
|
||||
|
|
@ -259,7 +238,7 @@ export class Client<
|
|||
) {
|
||||
super(options);
|
||||
this._capabilities = options?.capabilities ?? {};
|
||||
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator();
|
||||
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new DefaultJsonSchemaValidator();
|
||||
this._enforceStrictCapabilities = options?.enforceStrictCapabilities ?? false;
|
||||
|
||||
// Store list changed config for setup after connection (when we know server capabilities)
|
||||
|
|
|
|||
6
packages/client/src/shimsNode.ts
Normal file
6
packages/client/src/shimsNode.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Node.js runtime shims for client package
|
||||
*
|
||||
* This file is selected via package.json export conditions when running in Node.js.
|
||||
*/
|
||||
export { AjvJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core';
|
||||
6
packages/client/src/shimsWorkerd.ts
Normal file
6
packages/client/src/shimsWorkerd.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Cloudflare Workers runtime shims for client package
|
||||
*
|
||||
* This file is selected via package.json export conditions when running in workerd.
|
||||
*/
|
||||
export { CfWorkerJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core';
|
||||
|
|
@ -6,7 +6,8 @@
|
|||
"paths": {
|
||||
"*": ["./*"],
|
||||
"@modelcontextprotocol/core": ["./node_modules/@modelcontextprotocol/core/src/index.ts"],
|
||||
"@modelcontextprotocol/test-helpers": ["./node_modules/@modelcontextprotocol/test-helpers/src/index.ts"]
|
||||
"@modelcontextprotocol/test-helpers": ["./node_modules/@modelcontextprotocol/test-helpers/src/index.ts"],
|
||||
"@modelcontextprotocol/client/_shims": ["./src/shimsNode.ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { defineConfig } from 'tsdown';
|
|||
export default defineConfig({
|
||||
// 1. Entry Points
|
||||
// Directly matches package.json include/exclude globs
|
||||
entry: ['src/index.ts'],
|
||||
entry: ['src/index.ts', 'src/shimsNode.ts', 'src/shimsWorkerd.ts'],
|
||||
|
||||
// 2. Output Configuration
|
||||
format: ['esm'],
|
||||
|
|
@ -30,5 +30,8 @@ export default defineConfig({
|
|||
},
|
||||
// 5. Vendoring Strategy - Bundle the code for this specific package into the output,
|
||||
// but treat all other dependencies as external (require/import).
|
||||
noExternal: ['@modelcontextprotocol/core']
|
||||
noExternal: ['@modelcontextprotocol/core'],
|
||||
|
||||
// 6. External packages - keep self-reference imports external for runtime resolution
|
||||
external: ['@modelcontextprotocol/client/_shims']
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
/**
|
||||
* In-memory implementations of TaskStore and TaskMessageQueue.
|
||||
* WARNING: These APIs are experimental and may change without notice.
|
||||
*
|
||||
* @experimental
|
||||
*/
|
||||
|
||||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
import type { Request, RequestId, Result, Task } from '../../../types/types.js';
|
||||
import type { CreateTaskOptions, QueuedMessage, TaskMessageQueue, TaskStore } from '../interfaces.js';
|
||||
import { isTerminal } from '../interfaces.js';
|
||||
|
|
@ -19,14 +15,8 @@ interface StoredTask {
|
|||
}
|
||||
|
||||
/**
|
||||
* A simple in-memory implementation of TaskStore for demonstration purposes.
|
||||
*
|
||||
* This implementation stores all tasks in memory and provides automatic cleanup
|
||||
* based on the ttl duration specified in the task creation parameters.
|
||||
*
|
||||
* Note: This is not suitable for production use as all data is lost on restart.
|
||||
* For production, consider implementing TaskStore with a database or distributed cache.
|
||||
*
|
||||
* In-memory TaskStore implementation for development and testing.
|
||||
* For production, use a database or distributed cache.
|
||||
* @experimental
|
||||
*/
|
||||
export class InMemoryTaskStore implements TaskStore {
|
||||
|
|
@ -34,11 +24,10 @@ export class InMemoryTaskStore implements TaskStore {
|
|||
private cleanupTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
/**
|
||||
* Generates a unique task ID.
|
||||
* Uses 16 bytes of random data encoded as hex (32 characters).
|
||||
* Generates a unique task ID using Web Crypto API.
|
||||
*/
|
||||
private generateTaskId(): string {
|
||||
return randomBytes(16).toString('hex');
|
||||
return crypto.randomUUID().replaceAll('-', '');
|
||||
}
|
||||
|
||||
async createTask(taskParams: CreateTaskOptions, requestId: RequestId, request: Request, _sessionId?: string): Promise<Task> {
|
||||
|
|
@ -216,14 +205,8 @@ export class InMemoryTaskStore implements TaskStore {
|
|||
}
|
||||
|
||||
/**
|
||||
* A simple in-memory implementation of TaskMessageQueue for demonstration purposes.
|
||||
*
|
||||
* This implementation stores messages in memory, organized by task ID and optional session ID.
|
||||
* Messages are stored in FIFO queues per task.
|
||||
*
|
||||
* Note: This is not suitable for production use in distributed systems.
|
||||
* For production, consider implementing TaskMessageQueue with Redis or other distributed queues.
|
||||
*
|
||||
* In-memory TaskMessageQueue implementation for development and testing.
|
||||
* For production, use Redis or another distributed queue.
|
||||
* @experimental
|
||||
*/
|
||||
export class InMemoryTaskMessageQueue implements TaskMessageQueue {
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@
|
|||
"build": "tsdown",
|
||||
"build:watch": "tsdown --watch",
|
||||
"prepack": "npm run build",
|
||||
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
|
||||
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
|
||||
"lint": "eslint src/ && prettier --ignore-path ../../../.prettierignore --check .",
|
||||
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../../.prettierignore --write .",
|
||||
"check": "npm run typecheck && npm run lint",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"paths": {
|
||||
"*": ["./*"],
|
||||
"@modelcontextprotocol/server": ["./node_modules/@modelcontextprotocol/server/src/index.ts"],
|
||||
"@modelcontextprotocol/server/_shims": ["./node_modules/@modelcontextprotocol/server/src/shimsNode.ts"],
|
||||
"@modelcontextprotocol/core": [
|
||||
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/src/index.ts"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@
|
|||
"build": "tsdown",
|
||||
"build:watch": "tsdown --watch",
|
||||
"prepack": "npm run build",
|
||||
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
|
||||
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
|
||||
"lint": "eslint src/ && prettier --ignore-path ../../../.prettierignore --check .",
|
||||
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../../.prettierignore --write .",
|
||||
"check": "npm run typecheck && npm run lint",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"paths": {
|
||||
"*": ["./*"],
|
||||
"@modelcontextprotocol/server": ["./node_modules/@modelcontextprotocol/server/src/index.ts"],
|
||||
"@modelcontextprotocol/server/_shims": ["./node_modules/@modelcontextprotocol/server/src/shimsNode.ts"],
|
||||
"@modelcontextprotocol/core": [
|
||||
"./node_modules/@modelcontextprotocol/server/node_modules/@modelcontextprotocol/core/src/index.ts"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@
|
|||
"build": "tsdown",
|
||||
"build:watch": "tsdown --watch",
|
||||
"prepack": "npm run build",
|
||||
"lint": "eslint src/ && prettier --ignore-path ../../.prettierignore --check .",
|
||||
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../.prettierignore --write .",
|
||||
"lint": "eslint src/ && prettier --ignore-path ../../../.prettierignore --check .",
|
||||
"lint:fix": "eslint src/ --fix && prettier --ignore-path ../../../.prettierignore --write .",
|
||||
"check": "npm run typecheck && npm run lint",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"paths": {
|
||||
"*": ["./*"],
|
||||
"@modelcontextprotocol/server": ["./node_modules/@modelcontextprotocol/server/src/index.ts"],
|
||||
"@modelcontextprotocol/server/_shims": ["./node_modules/@modelcontextprotocol/server/src/shimsNode.ts"],
|
||||
"@modelcontextprotocol/core": ["./node_modules/@modelcontextprotocol/core/src/index.ts"],
|
||||
"@modelcontextprotocol/test-helpers": ["./node_modules/@modelcontextprotocol/test-helpers/src/index.ts"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,24 @@
|
|||
".": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"import": "./dist/index.mjs"
|
||||
},
|
||||
"./_shims": {
|
||||
"workerd": {
|
||||
"types": "./dist/shimsWorkerd.d.mts",
|
||||
"import": "./dist/shimsWorkerd.mjs"
|
||||
},
|
||||
"browser": {
|
||||
"types": "./dist/shimsWorkerd.d.mts",
|
||||
"import": "./dist/shimsWorkerd.mjs"
|
||||
},
|
||||
"node": {
|
||||
"types": "./dist/shimsNode.d.mts",
|
||||
"import": "./dist/shimsNode.mjs"
|
||||
},
|
||||
"default": {
|
||||
"types": "./dist/shimsNode.d.mts",
|
||||
"import": "./dist/shimsNode.mjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
|
|
|
|||
|
|
@ -5,9 +5,6 @@
|
|||
* @experimental
|
||||
*/
|
||||
|
||||
// SDK implementation interfaces
|
||||
export * from './interfaces.js';
|
||||
|
||||
// Wrapper classes
|
||||
export * from './mcpServer.js';
|
||||
export * from './server.js';
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import type {
|
|||
ToolUseContent
|
||||
} from '@modelcontextprotocol/core';
|
||||
import {
|
||||
AjvJsonSchemaValidator,
|
||||
assertClientRequestTaskCapability,
|
||||
assertToolsCallTaskCapability,
|
||||
CallToolRequestSchema,
|
||||
|
|
@ -55,6 +54,7 @@ import {
|
|||
SdkError,
|
||||
SdkErrorCode
|
||||
} from '@modelcontextprotocol/core';
|
||||
import { DefaultJsonSchemaValidator } from '@modelcontextprotocol/server/_shims';
|
||||
|
||||
import { ExperimentalServerTasks } from '../experimental/tasks/server.js';
|
||||
|
||||
|
|
@ -75,28 +75,7 @@ export type ServerOptions = ProtocolOptions & {
|
|||
* The validator is used to validate user input returned from elicitation
|
||||
* requests against the requested schema.
|
||||
*
|
||||
* @default AjvJsonSchemaValidator
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // ajv (default)
|
||||
* const server = new Server(
|
||||
* { name: 'my-server', version: '1.0.0' },
|
||||
* {
|
||||
* capabilities: {}
|
||||
* jsonSchemaValidator: new AjvJsonSchemaValidator()
|
||||
* }
|
||||
* );
|
||||
*
|
||||
* // @cfworker/json-schema
|
||||
* const server = new Server(
|
||||
* { name: 'my-server', version: '1.0.0' },
|
||||
* {
|
||||
* capabilities: {},
|
||||
* jsonSchemaValidator: new CfWorkerJsonSchemaValidator()
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
* @default DefaultJsonSchemaValidator (AjvJsonSchemaValidator on Node.js, CfWorkerJsonSchemaValidator on Cloudflare Workers)
|
||||
*/
|
||||
jsonSchemaValidator?: jsonSchemaValidator;
|
||||
};
|
||||
|
|
@ -154,7 +133,7 @@ export class Server<
|
|||
super(options);
|
||||
this._capabilities = options?.capabilities ?? {};
|
||||
this._instructions = options?.instructions;
|
||||
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator();
|
||||
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new DefaultJsonSchemaValidator();
|
||||
|
||||
this.setRequestHandler('initialize', request => this._oninitialize(request));
|
||||
this.setNotificationHandler('notifications/initialized', () => this.oninitialized?.());
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import process from 'node:process';
|
||||
import type { Readable, Writable } from 'node:stream';
|
||||
|
||||
import type { JSONRPCMessage, Transport } from '@modelcontextprotocol/core';
|
||||
import { ReadBuffer, serializeMessage } from '@modelcontextprotocol/core';
|
||||
import { process } from '@modelcontextprotocol/server/_shims';
|
||||
|
||||
/**
|
||||
* Server transport for stdio: this communicates with an MCP client by reading from the current process' stdin and writing to stdout.
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@
|
|||
* For Node.js Express/HTTP compatibility, use `NodeStreamableHTTPServerTransport` which wraps this transport.
|
||||
*/
|
||||
|
||||
import { TextEncoder } from 'node:util';
|
||||
|
||||
import type { AuthInfo, JSONRPCMessage, MessageExtraInfo, RequestId, RequestInfo, Transport } from '@modelcontextprotocol/core';
|
||||
import {
|
||||
DEFAULT_NEGOTIATED_PROTOCOL_VERSION,
|
||||
|
|
@ -62,7 +60,7 @@ interface StreamMapping {
|
|||
/** Stream controller for pushing SSE data - only used with ReadableStream approach */
|
||||
controller?: ReadableStreamDefaultController<Uint8Array>;
|
||||
/** Text encoder for SSE formatting */
|
||||
encoder?: TextEncoder;
|
||||
encoder?: InstanceType<typeof TextEncoder>;
|
||||
/** Promise resolver for JSON response mode */
|
||||
resolveJson?: (response: Response) => void;
|
||||
/** Cleanup function to close stream and remove mapping */
|
||||
|
|
@ -367,7 +365,7 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
|
|||
*/
|
||||
private async writePrimingEvent(
|
||||
controller: ReadableStreamDefaultController<Uint8Array>,
|
||||
encoder: TextEncoder,
|
||||
encoder: InstanceType<typeof TextEncoder>,
|
||||
streamId: string,
|
||||
protocolVersion: string
|
||||
): Promise<void> {
|
||||
|
|
@ -558,7 +556,7 @@ export class WebStandardStreamableHTTPServerTransport implements Transport {
|
|||
*/
|
||||
private writeSSEEvent(
|
||||
controller: ReadableStreamDefaultController<Uint8Array>,
|
||||
encoder: TextEncoder,
|
||||
encoder: InstanceType<typeof TextEncoder>,
|
||||
message: JSONRPCMessage,
|
||||
eventId?: string
|
||||
): boolean {
|
||||
|
|
|
|||
7
packages/server/src/shimsNode.ts
Normal file
7
packages/server/src/shimsNode.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* Node.js runtime shims for server package
|
||||
*
|
||||
* This file is selected via package.json export conditions when running in Node.js.
|
||||
*/
|
||||
export { AjvJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core';
|
||||
export { default as process } from 'node:process';
|
||||
23
packages/server/src/shimsWorkerd.ts
Normal file
23
packages/server/src/shimsWorkerd.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Cloudflare Workers runtime shims for server package
|
||||
*
|
||||
* This file is selected via package.json export conditions when running in workerd.
|
||||
*/
|
||||
export { CfWorkerJsonSchemaValidator as DefaultJsonSchemaValidator } from '@modelcontextprotocol/core';
|
||||
|
||||
/**
|
||||
* Stub process object for non-Node.js environments.
|
||||
* StdioServerTransport is not supported in Cloudflare Workers/browser environments.
|
||||
*/
|
||||
function notSupported(): never {
|
||||
throw new Error('StdioServerTransport is not supported in this environment. Use StreamableHTTPServerTransport instead.');
|
||||
}
|
||||
|
||||
export const process = {
|
||||
get stdin(): never {
|
||||
return notSupported();
|
||||
},
|
||||
get stdout(): never {
|
||||
return notSupported();
|
||||
}
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue