feat: pluggable JSON schema validator providers (#1012)
This commit is contained in:
parent
7098bff073
commit
5a8fb39430
1 changed files with 2373 additions and 134 deletions
|
|
@ -1,37 +1,38 @@
|
|||
import { mergeCapabilities, Protocol, ProtocolOptions, RequestOptions } from '../shared/protocol.js';
|
||||
import { mergeCapabilities, Protocol, type ProtocolOptions, type RequestOptions } from '../shared/protocol.js';
|
||||
import {
|
||||
ClientCapabilities,
|
||||
CreateMessageRequest,
|
||||
type ClientCapabilities,
|
||||
type CreateMessageRequest,
|
||||
CreateMessageResultSchema,
|
||||
ElicitRequest,
|
||||
ElicitResult,
|
||||
type ElicitRequest,
|
||||
type ElicitResult,
|
||||
ElicitResultSchema,
|
||||
EmptyResultSchema,
|
||||
Implementation,
|
||||
InitializedNotificationSchema,
|
||||
InitializeRequest,
|
||||
InitializeRequestSchema,
|
||||
InitializeResult,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
ListRootsRequest,
|
||||
ListRootsResultSchema,
|
||||
LoggingMessageNotification,
|
||||
McpError,
|
||||
ErrorCode,
|
||||
Notification,
|
||||
Request,
|
||||
ResourceUpdatedNotification,
|
||||
Result,
|
||||
ServerCapabilities,
|
||||
ServerNotification,
|
||||
ServerRequest,
|
||||
ServerResult,
|
||||
SUPPORTED_PROTOCOL_VERSIONS,
|
||||
LoggingLevel,
|
||||
type Implementation,
|
||||
InitializedNotificationSchema,
|
||||
type InitializeRequest,
|
||||
InitializeRequestSchema,
|
||||
type InitializeResult,
|
||||
LATEST_PROTOCOL_VERSION,
|
||||
type ListRootsRequest,
|
||||
ListRootsResultSchema,
|
||||
type LoggingLevel,
|
||||
LoggingLevelSchema,
|
||||
type LoggingMessageNotification,
|
||||
McpError,
|
||||
type Notification,
|
||||
type Request,
|
||||
type ResourceUpdatedNotification,
|
||||
type Result,
|
||||
type ServerCapabilities,
|
||||
type ServerNotification,
|
||||
type ServerRequest,
|
||||
type ServerResult,
|
||||
SetLevelRequestSchema,
|
||||
LoggingLevelSchema
|
||||
SUPPORTED_PROTOCOL_VERSIONS
|
||||
} from '../types.js';
|
||||
import Ajv from 'ajv';
|
||||
import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js';
|
||||
import type { JsonSchemaType, jsonSchemaValidator } from '../validation/types.js';
|
||||
|
||||
export type ServerOptions = ProtocolOptions & {
|
||||
/**
|
||||
|
|
@ -43,6 +44,37 @@ export type ServerOptions = ProtocolOptions & {
|
|||
* Optional instructions describing how to use the server and its features.
|
||||
*/
|
||||
instructions?: string;
|
||||
|
||||
/**
|
||||
* JSON Schema validator for elicitation response validation.
|
||||
*
|
||||
* 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()
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
jsonSchemaValidator?: jsonSchemaValidator;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -79,6 +111,7 @@ export class Server<
|
|||
private _clientVersion?: Implementation;
|
||||
private _capabilities: ServerCapabilities;
|
||||
private _instructions?: string;
|
||||
private _jsonSchemaValidator: jsonSchemaValidator;
|
||||
|
||||
/**
|
||||
* Callback for when initialization has fully completed (i.e., the client has sent an `initialized` notification).
|
||||
|
|
@ -95,6 +128,7 @@ export class Server<
|
|||
super(options);
|
||||
this._capabilities = options?.capabilities ?? {};
|
||||
this._instructions = options?.instructions;
|
||||
this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator();
|
||||
|
||||
this.setRequestHandler(InitializeRequestSchema, request => this._oninitialize(request));
|
||||
this.setNotificationHandler(InitializedNotificationSchema, () => this.oninitialized?.());
|
||||
|
|
@ -289,24 +323,25 @@ export class Server<
|
|||
const result = await this.request({ method: 'elicitation/create', params }, ElicitResultSchema, options);
|
||||
|
||||
// Validate the response content against the requested schema if action is "accept"
|
||||
if (result.action === 'accept' && result.content) {
|
||||
if (result.action === 'accept' && result.content && params.requestedSchema) {
|
||||
try {
|
||||
const ajv = new Ajv();
|
||||
const validator = this._jsonSchemaValidator.getValidator(params.requestedSchema as JsonSchemaType);
|
||||
const validationResult = validator(result.content);
|
||||
|
||||
const validate = ajv.compile(params.requestedSchema);
|
||||
const isValid = validate(result.content);
|
||||
|
||||
if (!isValid) {
|
||||
if (!validationResult.valid) {
|
||||
throw new McpError(
|
||||
ErrorCode.InvalidParams,
|
||||
`Elicitation response content does not match requested schema: ${ajv.errorsText(validate.errors)}`
|
||||
`Elicitation response content does not match requested schema: ${validationResult.errorMessage}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof McpError) {
|
||||
throw error;
|
||||
}
|
||||
throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error}`);
|
||||
throw new McpError(
|
||||
ErrorCode.InternalError,
|
||||
`Error validating elicitation response: ${error instanceof Error ? error.message : String(error)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue