generate schema with refs
This commit is contained in:
parent
b5f037fea3
commit
b8acfa0b6b
8 changed files with 1497 additions and 380 deletions
19
.github/workflows/ci.yml
vendored
Normal file
19
.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-schema:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: "24"
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run check:schema:ts
|
||||||
|
- run: npm run check:schema:json
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -7,3 +7,4 @@ package-lock.json
|
||||||
.jj/
|
.jj/
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
dist/
|
dist/
|
||||||
|
schema/**/generated/
|
||||||
|
|
|
||||||
11
package.json
11
package.json
|
|
@ -2,13 +2,20 @@
|
||||||
"name": "@modelcontextprotocol/experimental-ext-tasks",
|
"name": "@modelcontextprotocol/experimental-ext-tasks",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
"description": "MCP Tasks Extension - Experimental extension for task management in the Model Context Protocol",
|
"description": "MCP Tasks Extension - Experimental extension for task management in the Model Context Protocol",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"check:schema": "tsc --noEmit"
|
"generate:schemas": "tsx scripts/generate-schemas.ts",
|
||||||
|
"check:schema": "npm run check:schema:ts && npm run check:schema:json",
|
||||||
|
"check:schema:ts": "tsc --noEmit",
|
||||||
|
"check:schema:json": "tsx scripts/generate-schemas.ts --check"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.29.0",
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
||||||
"typescript": "^5.0.0"
|
"ts-to-zod": "^5.1.0",
|
||||||
|
"tsx": "^4.21.0",
|
||||||
|
"typescript": "^5.0.0",
|
||||||
|
"zod": "^4.4.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1134
schema/2025-11-25/schema.json
Normal file
1134
schema/2025-11-25/schema.json
Normal file
File diff suppressed because it is too large
Load diff
342
schema/2025-11-25/schema.ts
Normal file
342
schema/2025-11-25/schema.ts
Normal file
|
|
@ -0,0 +1,342 @@
|
||||||
|
/**
|
||||||
|
* MCP Tasks Extension Schema (spec.types.ts)
|
||||||
|
* Extension Identifier: io.modelcontextprotocol/tasks
|
||||||
|
*
|
||||||
|
* This file contains pure TypeScript interface definitions for the MCP Tasks extension.
|
||||||
|
* These types are the source of truth and are used to generate Zod schemas via `ts-to-zod`.
|
||||||
|
*
|
||||||
|
* - Use `@description` JSDoc tags to generate `.describe()` calls on schemas
|
||||||
|
* - Run `npm run generate:schemas` to regenerate schemas from these types
|
||||||
|
*
|
||||||
|
* @see https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type {
|
||||||
|
Cursor,
|
||||||
|
JSONRPCNotification,
|
||||||
|
JSONRPCRequest,
|
||||||
|
NotificationParams,
|
||||||
|
PaginatedRequest,
|
||||||
|
PaginatedResult,
|
||||||
|
ProgressToken,
|
||||||
|
RequestId,
|
||||||
|
RequestParams,
|
||||||
|
Result,
|
||||||
|
} from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
|
/* Tasks */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The status of a task.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export type TaskStatus =
|
||||||
|
| "working" // The request is currently being processed
|
||||||
|
| "input_required" // The task is waiting for input (e.g., elicitation or sampling)
|
||||||
|
| "completed" // The request completed successfully and results are available
|
||||||
|
| "failed" // The associated request did not complete successfully. For tool calls specifically, this includes cases where the tool call result has `isError` set to true.
|
||||||
|
| "cancelled"; // The request was cancelled before completion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for augmenting a request with task execution.
|
||||||
|
* Include this in the `task` field of the request parameters.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export interface TaskMetadata {
|
||||||
|
/**
|
||||||
|
* Requested duration in milliseconds to retain task from creation.
|
||||||
|
*/
|
||||||
|
ttl?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata for associating messages with a task.
|
||||||
|
* Include this in the `_meta` field under the key `io.modelcontextprotocol/related-task`.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export interface RelatedTaskMetadata {
|
||||||
|
/**
|
||||||
|
* The task identifier this message is associated with.
|
||||||
|
*/
|
||||||
|
taskId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data associated with a task.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export interface Task {
|
||||||
|
/**
|
||||||
|
* The task identifier.
|
||||||
|
*/
|
||||||
|
taskId: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current task state.
|
||||||
|
*/
|
||||||
|
status: TaskStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional human-readable message describing the current task state.
|
||||||
|
* This can provide context for any status, including:
|
||||||
|
* - Reasons for "cancelled" status
|
||||||
|
* - Summaries for "completed" status
|
||||||
|
* - Diagnostic information for "failed" status (e.g., error details, what went wrong)
|
||||||
|
*/
|
||||||
|
statusMessage?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ISO 8601 timestamp when the task was created.
|
||||||
|
*/
|
||||||
|
createdAt: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ISO 8601 timestamp when the task was last updated.
|
||||||
|
*/
|
||||||
|
lastUpdatedAt: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actual retention duration from creation in milliseconds, null for unlimited.
|
||||||
|
* @nullable
|
||||||
|
*/
|
||||||
|
ttl: number | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggested polling interval in milliseconds.
|
||||||
|
*/
|
||||||
|
pollInterval?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A response to a task-augmented request.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export interface CreateTaskResult extends Result {
|
||||||
|
task: Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Task Operations */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to retrieve the state of a task.
|
||||||
|
*
|
||||||
|
* @category `tasks/get`
|
||||||
|
*/
|
||||||
|
export interface GetTaskRequest extends JSONRPCRequest {
|
||||||
|
method: "tasks/get";
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* The task identifier to query.
|
||||||
|
*/
|
||||||
|
taskId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response to a tasks/get request.
|
||||||
|
*
|
||||||
|
* @category `tasks/get`
|
||||||
|
*/
|
||||||
|
export type GetTaskResult = Result & Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to retrieve the result of a completed task.
|
||||||
|
*
|
||||||
|
* @category `tasks/result`
|
||||||
|
*/
|
||||||
|
export interface GetTaskPayloadRequest extends JSONRPCRequest {
|
||||||
|
method: "tasks/result";
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* The task identifier to retrieve results for.
|
||||||
|
*/
|
||||||
|
taskId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response to a tasks/result request.
|
||||||
|
* The structure matches the result type of the original request.
|
||||||
|
* For example, a tools/call task would return the CallToolResult structure.
|
||||||
|
*
|
||||||
|
* @category `tasks/result`
|
||||||
|
*/
|
||||||
|
export interface GetTaskPayloadResult extends Result {
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to cancel a task.
|
||||||
|
*
|
||||||
|
* @category `tasks/cancel`
|
||||||
|
*/
|
||||||
|
export interface CancelTaskRequest extends JSONRPCRequest {
|
||||||
|
method: "tasks/cancel";
|
||||||
|
params: {
|
||||||
|
/**
|
||||||
|
* The task identifier to cancel.
|
||||||
|
*/
|
||||||
|
taskId: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response to a tasks/cancel request.
|
||||||
|
*
|
||||||
|
* @category `tasks/cancel`
|
||||||
|
*/
|
||||||
|
export type CancelTaskResult = Result & Task;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request to retrieve a list of tasks.
|
||||||
|
*
|
||||||
|
* @category `tasks/list`
|
||||||
|
*/
|
||||||
|
export interface ListTasksRequest extends PaginatedRequest {
|
||||||
|
method: "tasks/list";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The response to a tasks/list request.
|
||||||
|
*
|
||||||
|
* @category `tasks/list`
|
||||||
|
*/
|
||||||
|
export interface ListTasksResult extends PaginatedResult {
|
||||||
|
tasks: Task[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Task Notifications */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters for a `notifications/tasks/status` notification.
|
||||||
|
*
|
||||||
|
* @category `notifications/tasks/status`
|
||||||
|
*/
|
||||||
|
export type TaskStatusNotificationParams = NotificationParams &
|
||||||
|
Task & { [key: string]: unknown };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An optional notification from the receiver to the requestor, informing them that a task's status has changed. Receivers are not required to send these notifications.
|
||||||
|
*
|
||||||
|
* @category `notifications/tasks/status`
|
||||||
|
*/
|
||||||
|
export interface TaskStatusNotification extends JSONRPCNotification {
|
||||||
|
method: "notifications/tasks/status";
|
||||||
|
params: TaskStatusNotificationParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Task-Augmented Request Params */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common params for any task-augmented request.
|
||||||
|
* Extends the base RequestParams to include an optional `task` field.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export interface TaskAugmentedRequestParams extends RequestParams {
|
||||||
|
/**
|
||||||
|
* If specified, the caller is requesting task-augmented execution for this request.
|
||||||
|
* The request will return a CreateTaskResult immediately, and the actual result can be
|
||||||
|
* retrieved later via tasks/result.
|
||||||
|
*
|
||||||
|
* Task augmentation is subject to capability negotiation - receivers MUST declare support
|
||||||
|
* for task augmentation of specific request types in their capabilities.
|
||||||
|
*/
|
||||||
|
task?: TaskMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Capability Additions */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task-related server capabilities.
|
||||||
|
* Include this in the server's `capabilities` object during initialization.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export interface TaskServerCapabilities {
|
||||||
|
tasks?: {
|
||||||
|
/**
|
||||||
|
* Whether this server supports tasks/list.
|
||||||
|
*/
|
||||||
|
list?: Record<string, never>;
|
||||||
|
/**
|
||||||
|
* Whether this server supports tasks/cancel.
|
||||||
|
*/
|
||||||
|
cancel?: Record<string, never>;
|
||||||
|
/**
|
||||||
|
* Specifies which request types can be augmented with tasks.
|
||||||
|
*/
|
||||||
|
requests?: {
|
||||||
|
/**
|
||||||
|
* Task support for tool-related requests.
|
||||||
|
*/
|
||||||
|
tools?: {
|
||||||
|
/**
|
||||||
|
* Whether the server supports task-augmented tools/call requests.
|
||||||
|
*/
|
||||||
|
call?: Record<string, never>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task-related client capabilities.
|
||||||
|
* Include this in the client's `capabilities` object during initialization.
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export interface TaskClientCapabilities {
|
||||||
|
tasks?: {
|
||||||
|
/**
|
||||||
|
* Whether this client supports tasks/list.
|
||||||
|
*/
|
||||||
|
list?: Record<string, never>;
|
||||||
|
/**
|
||||||
|
* Whether this client supports tasks/cancel.
|
||||||
|
*/
|
||||||
|
cancel?: Record<string, never>;
|
||||||
|
/**
|
||||||
|
* Specifies which request types can be augmented with tasks.
|
||||||
|
*/
|
||||||
|
requests?: {
|
||||||
|
/**
|
||||||
|
* Task support for sampling-related requests.
|
||||||
|
*/
|
||||||
|
sampling?: {
|
||||||
|
/**
|
||||||
|
* Whether the client supports task-augmented sampling/createMessage requests.
|
||||||
|
*/
|
||||||
|
createMessage?: Record<string, never>;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Task support for elicitation-related requests.
|
||||||
|
*/
|
||||||
|
elicitation?: {
|
||||||
|
/**
|
||||||
|
* Whether the client supports task-augmented elicitation/create requests.
|
||||||
|
*/
|
||||||
|
create?: Record<string, never>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tool-Level Task Support */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task support declaration for individual tools.
|
||||||
|
* Include this in the tool's definition returned by tools/list.
|
||||||
|
*
|
||||||
|
* - "forbidden": Tool does not support task-augmented execution (default when absent)
|
||||||
|
* - "optional": Tool may support task-augmented execution
|
||||||
|
* - "required": Tool requires task-augmented execution
|
||||||
|
*
|
||||||
|
* @category `tasks`
|
||||||
|
*/
|
||||||
|
export type ToolTaskSupport = "forbidden" | "optional" | "required";
|
||||||
|
|
@ -1,374 +0,0 @@
|
||||||
{
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"description": "MCP Tasks Extension Schema. Contains only task-related definitions from the MCP 2025-11-25 specification. Core MCP types are referenced via remote $ref pointing to the 2025-11-25 core schema.",
|
|
||||||
"$comment": "Extension Identifier: io.modelcontextprotocol/tasks. Core schema: https://github.com/modelcontextprotocol/modelcontextprotocol/tree/2025-11-25/schema/2025-11-25",
|
|
||||||
"$defs": {
|
|
||||||
"TaskStatus": {
|
|
||||||
"description": "The status of a task.",
|
|
||||||
"enum": ["cancelled", "completed", "failed", "input_required", "working"],
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"TaskMetadata": {
|
|
||||||
"description": "Metadata for augmenting a request with task execution.\nInclude this in the `task` field of the request parameters.",
|
|
||||||
"properties": {
|
|
||||||
"ttl": {
|
|
||||||
"description": "Requested duration in milliseconds to retain task from creation.",
|
|
||||||
"type": "integer"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"RelatedTaskMetadata": {
|
|
||||||
"description": "Metadata for associating messages with a task.\nInclude this in the `_meta` field under the key `io.modelcontextprotocol/related-task`.",
|
|
||||||
"properties": {
|
|
||||||
"taskId": {
|
|
||||||
"description": "The task identifier this message is associated with.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["taskId"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"Task": {
|
|
||||||
"description": "Data associated with a task.",
|
|
||||||
"properties": {
|
|
||||||
"createdAt": {
|
|
||||||
"description": "ISO 8601 timestamp when the task was created.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"lastUpdatedAt": {
|
|
||||||
"description": "ISO 8601 timestamp when the task was last updated.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"pollInterval": {
|
|
||||||
"description": "Suggested polling interval in milliseconds.",
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"status": {
|
|
||||||
"$ref": "#/$defs/TaskStatus",
|
|
||||||
"description": "Current task state."
|
|
||||||
},
|
|
||||||
"statusMessage": {
|
|
||||||
"description": "Optional human-readable message describing the current task state.\nThis can provide context for any status, including:\n- Reasons for \"cancelled\" status\n- Summaries for \"completed\" status\n- Diagnostic information for \"failed\" status (e.g., error details, what went wrong)",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"taskId": {
|
|
||||||
"description": "The task identifier.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"ttl": {
|
|
||||||
"description": "Actual retention duration from creation in milliseconds, null for unlimited.",
|
|
||||||
"type": ["integer", "null"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["createdAt", "lastUpdatedAt", "status", "taskId", "ttl"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"CreateTaskResult": {
|
|
||||||
"description": "A response to a task-augmented request.",
|
|
||||||
"properties": {
|
|
||||||
"_meta": {
|
|
||||||
"additionalProperties": {},
|
|
||||||
"description": "See [General fields: `_meta`](/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage.",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"task": {
|
|
||||||
"$ref": "#/$defs/Task"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["task"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"TaskAugmentedRequestParams": {
|
|
||||||
"description": "Common params for any task-augmented request.",
|
|
||||||
"properties": {
|
|
||||||
"_meta": {
|
|
||||||
"additionalProperties": {},
|
|
||||||
"description": "See [General fields: `_meta`](/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage.",
|
|
||||||
"properties": {
|
|
||||||
"progressToken": {
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/ProgressToken",
|
|
||||||
"description": "If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"task": {
|
|
||||||
"$ref": "#/$defs/TaskMetadata",
|
|
||||||
"description": "If specified, the caller is requesting task-augmented execution for this request.\nThe request will return a CreateTaskResult immediately, and the actual result can be\nretrieved later via tasks/result.\n\nTask augmentation is subject to capability negotiation - receivers MUST declare support\nfor task augmentation of specific request types in their capabilities."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"GetTaskRequest": {
|
|
||||||
"description": "A request to retrieve the state of a task.",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/RequestId"
|
|
||||||
},
|
|
||||||
"jsonrpc": {
|
|
||||||
"const": "2.0",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"method": {
|
|
||||||
"const": "tasks/get",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"params": {
|
|
||||||
"properties": {
|
|
||||||
"taskId": {
|
|
||||||
"description": "The task identifier to query.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["taskId"],
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "jsonrpc", "method", "params"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"GetTaskResult": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/Result"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/$defs/Task"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "The response to a tasks/get request."
|
|
||||||
},
|
|
||||||
"GetTaskPayloadRequest": {
|
|
||||||
"description": "A request to retrieve the result of a completed task.",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/RequestId"
|
|
||||||
},
|
|
||||||
"jsonrpc": {
|
|
||||||
"const": "2.0",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"method": {
|
|
||||||
"const": "tasks/result",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"params": {
|
|
||||||
"properties": {
|
|
||||||
"taskId": {
|
|
||||||
"description": "The task identifier to retrieve results for.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["taskId"],
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "jsonrpc", "method", "params"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"GetTaskPayloadResult": {
|
|
||||||
"additionalProperties": {},
|
|
||||||
"description": "The response to a tasks/result request.\nThe structure matches the result type of the original request.\nFor example, a tools/call task would return the CallToolResult structure.",
|
|
||||||
"properties": {
|
|
||||||
"_meta": {
|
|
||||||
"additionalProperties": {},
|
|
||||||
"description": "See [General fields: `_meta`](/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage.",
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"CancelTaskRequest": {
|
|
||||||
"description": "A request to cancel a task.",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/RequestId"
|
|
||||||
},
|
|
||||||
"jsonrpc": {
|
|
||||||
"const": "2.0",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"method": {
|
|
||||||
"const": "tasks/cancel",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"params": {
|
|
||||||
"properties": {
|
|
||||||
"taskId": {
|
|
||||||
"description": "The task identifier to cancel.",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["taskId"],
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "jsonrpc", "method", "params"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"CancelTaskResult": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/Result"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/$defs/Task"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "The response to a tasks/cancel request."
|
|
||||||
},
|
|
||||||
"ListTasksRequest": {
|
|
||||||
"description": "A request to retrieve a list of tasks.",
|
|
||||||
"properties": {
|
|
||||||
"id": {
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/RequestId"
|
|
||||||
},
|
|
||||||
"jsonrpc": {
|
|
||||||
"const": "2.0",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"method": {
|
|
||||||
"const": "tasks/list",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"params": {
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/PaginatedRequestParams"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["id", "jsonrpc", "method"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"ListTasksResult": {
|
|
||||||
"description": "The response to a tasks/list request.",
|
|
||||||
"properties": {
|
|
||||||
"_meta": {
|
|
||||||
"additionalProperties": {},
|
|
||||||
"description": "See [General fields: `_meta`](/specification/2025-11-25/basic/index#meta) for notes on `_meta` usage.",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"nextCursor": {
|
|
||||||
"description": "An opaque token representing the pagination position after the last returned result.\nIf present, there may be more results available.",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tasks": {
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/$defs/Task"
|
|
||||||
},
|
|
||||||
"type": "array"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["tasks"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"TaskStatusNotificationParams": {
|
|
||||||
"allOf": [
|
|
||||||
{
|
|
||||||
"$ref": "https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/2025-11-25/schema.json#/$defs/NotificationParams"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"$ref": "#/$defs/Task"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Parameters for a `notifications/tasks/status` notification."
|
|
||||||
},
|
|
||||||
"TaskStatusNotification": {
|
|
||||||
"description": "An optional notification from the receiver to the requestor, informing them that a task's status has changed. Receivers are not required to send these notifications.",
|
|
||||||
"properties": {
|
|
||||||
"jsonrpc": {
|
|
||||||
"const": "2.0",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"method": {
|
|
||||||
"const": "notifications/tasks/status",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"params": {
|
|
||||||
"$ref": "#/$defs/TaskStatusNotificationParams"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": ["jsonrpc", "method", "params"],
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"TaskServerCapabilities": {
|
|
||||||
"description": "Task-related server capabilities. Include this in the server's capabilities object during initialization.",
|
|
||||||
"properties": {
|
|
||||||
"tasks": {
|
|
||||||
"properties": {
|
|
||||||
"list": {
|
|
||||||
"description": "Whether this server supports tasks/list.",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"cancel": {
|
|
||||||
"description": "Whether this server supports tasks/cancel.",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"requests": {
|
|
||||||
"properties": {
|
|
||||||
"tools": {
|
|
||||||
"properties": {
|
|
||||||
"call": {
|
|
||||||
"description": "Whether the server supports task-augmented tools/call requests.",
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"TaskClientCapabilities": {
|
|
||||||
"description": "Task-related client capabilities. Include this in the client's capabilities object during initialization.",
|
|
||||||
"properties": {
|
|
||||||
"tasks": {
|
|
||||||
"properties": {
|
|
||||||
"list": {
|
|
||||||
"description": "Whether this client supports tasks/list.",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"cancel": {
|
|
||||||
"description": "Whether this client supports tasks/cancel.",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"requests": {
|
|
||||||
"properties": {
|
|
||||||
"sampling": {
|
|
||||||
"properties": {
|
|
||||||
"createMessage": {
|
|
||||||
"description": "Whether the client supports task-augmented sampling/createMessage requests.",
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"elicitation": {
|
|
||||||
"properties": {
|
|
||||||
"create": {
|
|
||||||
"description": "Whether the client supports task-augmented elicitation/create requests.",
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"ToolTaskSupport": {
|
|
||||||
"description": "Task support declaration for individual tools.\n- \"forbidden\": Tool does not support task-augmented execution (default when absent)\n- \"optional\": Tool may support task-augmented execution\n- \"required\": Tool requires task-augmented execution",
|
|
||||||
"enum": ["forbidden", "optional", "required"],
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
326
scripts/generate-schemas.ts
Normal file
326
scripts/generate-schemas.ts
Normal file
|
|
@ -0,0 +1,326 @@
|
||||||
|
/**
|
||||||
|
* Schema Generation Script using ts-to-zod as a library
|
||||||
|
*
|
||||||
|
* This script generates Zod schemas from schema.ts and performs necessary
|
||||||
|
* post-processing for compatibility with this project.
|
||||||
|
*
|
||||||
|
* Follows the same pattern as ext-apps/scripts/generate-schemas.ts.
|
||||||
|
*
|
||||||
|
* ## Post-Processing Steps
|
||||||
|
*
|
||||||
|
* ### 1. Zod Import Path (`"zod"` → `"zod/v4"`)
|
||||||
|
* ts-to-zod generates `import { z } from "zod"`. We rewrite to `import { z } from "zod/v4"`
|
||||||
|
* to match MCP SDK's zod/v4 schemas.
|
||||||
|
*
|
||||||
|
* ### 2. External Type References (`z.any()` → SDK schemas)
|
||||||
|
* ts-to-zod cannot resolve types imported from @modelcontextprotocol/sdk.
|
||||||
|
* We replace z.any() placeholders with actual SDK schema imports.
|
||||||
|
*
|
||||||
|
* ### 3. Index Signatures (`z.record().and()` → `.passthrough()`)
|
||||||
|
* TypeScript index signatures are translated to z.record().and(z.object({...})).
|
||||||
|
* We replace with z.object({...}).passthrough() for compatibility.
|
||||||
|
*
|
||||||
|
* @see https://github.com/fabien0102/ts-to-zod
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||||
|
import { dirname, join, relative } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { generate } from "ts-to-zod";
|
||||||
|
import { toJSONSchema, type $ZodType } from "zod/v4/core";
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = dirname(__filename);
|
||||||
|
const PROJECT_ROOT = join(__dirname, "..");
|
||||||
|
|
||||||
|
const SCHEMA_DIR = join(PROJECT_ROOT, "schema", "draft", "2025-11-25");
|
||||||
|
const SPEC_TYPES_FILE = join(SCHEMA_DIR, "schema.ts");
|
||||||
|
const GENERATED_DIR = join(SCHEMA_DIR, "generated");
|
||||||
|
const SCHEMA_OUTPUT_FILE = join(GENERATED_DIR, "schema.ts");
|
||||||
|
const JSON_SCHEMA_OUTPUT_FILE = join(SCHEMA_DIR, "schema.json");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mapping from ts-to-zod generated placeholder names to actual SDK schema exports.
|
||||||
|
* Only includes types that have matching SDK exports.
|
||||||
|
*/
|
||||||
|
const EXTERNAL_TYPE_SCHEMAS: string[] = [
|
||||||
|
"JSONRPCRequestSchema",
|
||||||
|
"JSONRPCNotificationSchema",
|
||||||
|
"PaginatedRequestSchema",
|
||||||
|
"PaginatedResultSchema",
|
||||||
|
"ResultSchema",
|
||||||
|
"RequestIdSchema",
|
||||||
|
"ProgressTokenSchema",
|
||||||
|
"CursorSchema",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External types that don't have direct SDK schema exports.
|
||||||
|
* We replace their z.any() placeholders with local Zod definitions.
|
||||||
|
*/
|
||||||
|
const LOCAL_SCHEMA_REPLACEMENTS: Record<string, string> = {
|
||||||
|
RequestParamsSchema: `z.object({
|
||||||
|
_meta: z.object({ progressToken: ProgressTokenSchema.optional() }).passthrough().optional(),
|
||||||
|
}).passthrough()`,
|
||||||
|
NotificationParamsSchema: `z.object({
|
||||||
|
_meta: z.record(z.string(), z.unknown()).optional(),
|
||||||
|
}).passthrough()`,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const isCheck = process.argv.includes("--check");
|
||||||
|
|
||||||
|
if (isCheck) {
|
||||||
|
console.log("Checking schemas...");
|
||||||
|
} else {
|
||||||
|
console.log("🔧 Generating Zod schemas from schema.ts...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceText = readFileSync(SPEC_TYPES_FILE, "utf-8");
|
||||||
|
|
||||||
|
const result = generate({
|
||||||
|
sourceText,
|
||||||
|
keepComments: true,
|
||||||
|
skipParseJSDoc: false,
|
||||||
|
getSchemaName: (typeName: string) => `${typeName}Schema`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.errors.length > 0) {
|
||||||
|
console.error("❌ Generation errors:");
|
||||||
|
for (const error of result.errors) {
|
||||||
|
console.error(` - ${error}`);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.hasCircularDependencies) {
|
||||||
|
console.warn("⚠️ Warning: Circular dependencies detected in types");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate Zod schemas file
|
||||||
|
const relativeImportPath = relative(GENERATED_DIR, SCHEMA_DIR).replace(
|
||||||
|
/\\/g,
|
||||||
|
"/"
|
||||||
|
);
|
||||||
|
let schemasContent = result.getZodSchemasFile(
|
||||||
|
`${relativeImportPath}/schema.js`
|
||||||
|
);
|
||||||
|
schemasContent = postProcess(schemasContent);
|
||||||
|
|
||||||
|
if (isCheck) {
|
||||||
|
// Check mode: compare generated output against existing files
|
||||||
|
let hasChanges = false;
|
||||||
|
|
||||||
|
if (existsSync(SCHEMA_OUTPUT_FILE)) {
|
||||||
|
const existing = readFileSync(SCHEMA_OUTPUT_FILE, "utf-8");
|
||||||
|
if (existing !== schemasContent) {
|
||||||
|
console.error(
|
||||||
|
"❌ Generated Zod schemas are out of date. Run: npm run generate:schemas"
|
||||||
|
);
|
||||||
|
hasChanges = true;
|
||||||
|
} else {
|
||||||
|
console.log(" ✓ Zod schemas are up to date");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"❌ Generated Zod schemas file does not exist. Run: npm run generate:schemas"
|
||||||
|
);
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write temp file to generate JSON Schema for comparison
|
||||||
|
const tempFile = SCHEMA_OUTPUT_FILE + ".check.ts";
|
||||||
|
writeFileSync(tempFile, schemasContent, "utf-8");
|
||||||
|
try {
|
||||||
|
const jsonSchemaContent = await generateJsonSchemaContent(tempFile);
|
||||||
|
if (existsSync(JSON_SCHEMA_OUTPUT_FILE)) {
|
||||||
|
const existing = readFileSync(JSON_SCHEMA_OUTPUT_FILE, "utf-8");
|
||||||
|
if (existing !== jsonSchemaContent) {
|
||||||
|
console.error(
|
||||||
|
"❌ JSON Schema is out of date. Run: npm run generate:schemas"
|
||||||
|
);
|
||||||
|
hasChanges = true;
|
||||||
|
} else {
|
||||||
|
console.log(" ✓ JSON Schema is up to date");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
"❌ JSON Schema file does not exist. Run: npm run generate:schemas"
|
||||||
|
);
|
||||||
|
hasChanges = true;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
// Clean up temp file
|
||||||
|
const { unlinkSync } = await import("node:fs");
|
||||||
|
try {
|
||||||
|
unlinkSync(tempFile);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasChanges) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
console.log("\nAll schemas are up to date!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write Zod schemas
|
||||||
|
writeFileSync(SCHEMA_OUTPUT_FILE, schemasContent, "utf-8");
|
||||||
|
console.log(`✅ Written: ${SCHEMA_OUTPUT_FILE}`);
|
||||||
|
|
||||||
|
// Generate and write JSON Schema
|
||||||
|
const jsonSchemaContent = await generateJsonSchemaContent(SCHEMA_OUTPUT_FILE);
|
||||||
|
writeFileSync(JSON_SCHEMA_OUTPUT_FILE, jsonSchemaContent, "utf-8");
|
||||||
|
console.log(`✅ Written: ${JSON_SCHEMA_OUTPUT_FILE}`);
|
||||||
|
|
||||||
|
console.log("\n🎉 Schema generation complete!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate JSON Schema content from the Zod schemas file.
|
||||||
|
*/
|
||||||
|
async function generateJsonSchemaContent(
|
||||||
|
zodSchemasFile: string
|
||||||
|
): Promise<string> {
|
||||||
|
// Convert to file:// URL for dynamic import (required on Windows)
|
||||||
|
const fileUrl = new URL(`file:///${zodSchemasFile.replace(/\\/g, "/")}`);
|
||||||
|
const schemas = await import(fileUrl.href);
|
||||||
|
|
||||||
|
const jsonSchema: {
|
||||||
|
$schema: string;
|
||||||
|
$id: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
$defs: Record<string, unknown>;
|
||||||
|
} = {
|
||||||
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
$id: "https://modelcontextprotocol.io/ext-tasks/schema.json",
|
||||||
|
title: "MCP Tasks Extension",
|
||||||
|
description:
|
||||||
|
"JSON Schema for MCP Tasks extension protocol messages. Extension Identifier: io.modelcontextprotocol/tasks",
|
||||||
|
$defs: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [name, schema] of Object.entries(schemas)) {
|
||||||
|
if (
|
||||||
|
name.endsWith("Schema") &&
|
||||||
|
typeof schema === "object" &&
|
||||||
|
schema !== null
|
||||||
|
) {
|
||||||
|
const typeName = name.replace(/Schema$/, "");
|
||||||
|
|
||||||
|
// Skip SDK external types and local replacement types — only include task-specific types
|
||||||
|
if (
|
||||||
|
EXTERNAL_TYPE_SCHEMAS.includes(name) ||
|
||||||
|
name in LOCAL_SCHEMA_REPLACEMENTS
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
jsonSchema.$defs[typeName] = toJSONSchema(schema as $ZodType, {
|
||||||
|
unrepresentable: "any",
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`⚠️ Could not convert ${name} to JSON Schema: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.stringify(jsonSchema, null, 2) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Post-process generated Zod schemas for project compatibility.
|
||||||
|
*/
|
||||||
|
function postProcess(content: string): string {
|
||||||
|
// 1. Rewrite to zod/v4 and add MCP SDK schema imports
|
||||||
|
const sdkImports = EXTERNAL_TYPE_SCHEMAS.join(",\n ");
|
||||||
|
|
||||||
|
content = content.replace(
|
||||||
|
'import { z } from "zod";',
|
||||||
|
`import { z } from "zod/v4";
|
||||||
|
import {
|
||||||
|
${sdkImports},
|
||||||
|
} from "@modelcontextprotocol/sdk/types.js";`
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Remove z.any() placeholders for SDK-imported external types
|
||||||
|
for (const schemaName of EXTERNAL_TYPE_SCHEMAS) {
|
||||||
|
content = content.replace(
|
||||||
|
new RegExp(
|
||||||
|
`(?:export )?const ${schemaName} = z\\.any\\(\\);\\n?`,
|
||||||
|
"g"
|
||||||
|
),
|
||||||
|
""
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Replace z.any() placeholders for locally-defined types
|
||||||
|
for (const [schemaName, replacement] of Object.entries(
|
||||||
|
LOCAL_SCHEMA_REPLACEMENTS
|
||||||
|
)) {
|
||||||
|
content = content.replace(
|
||||||
|
new RegExp(
|
||||||
|
`(?:export )?const ${schemaName} = z\\.any\\(\\);`,
|
||||||
|
"g"
|
||||||
|
),
|
||||||
|
`const ${schemaName} = ${replacement};`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Replace z.record().and(z.object({...})) with z.object({...}).passthrough()
|
||||||
|
content = replaceRecordAndWithPassthrough(content);
|
||||||
|
|
||||||
|
// 5. Add header comment
|
||||||
|
content = content.replace(
|
||||||
|
"// Generated by ts-to-zod",
|
||||||
|
`// Generated by ts-to-zod
|
||||||
|
// Post-processed for Zod v4 compatibility and MCP SDK integration
|
||||||
|
// Run: npm run generate:schemas`
|
||||||
|
);
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace z.record(z.string(), z.unknown()).and(z.object({...})) with z.object({...}).passthrough()
|
||||||
|
* Uses brace-counting to handle nested objects correctly.
|
||||||
|
* passthrough() works in both Zod v3 and v4, allowing extra properties.
|
||||||
|
*/
|
||||||
|
function replaceRecordAndWithPassthrough(content: string): string {
|
||||||
|
const pattern = "z.record(z.string(), z.unknown()).and(z.object({";
|
||||||
|
let result = content;
|
||||||
|
let startIndex = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const matchStart = result.indexOf(pattern, startIndex);
|
||||||
|
if (matchStart === -1) break;
|
||||||
|
|
||||||
|
const objectStart = matchStart + pattern.length;
|
||||||
|
let braceCount = 1;
|
||||||
|
let i = objectStart;
|
||||||
|
|
||||||
|
while (i < result.length && braceCount > 0) {
|
||||||
|
if (result[i] === "{") braceCount++;
|
||||||
|
else if (result[i] === "}") braceCount--;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.slice(i, i + 2) === "))") {
|
||||||
|
const objectContent = result.slice(objectStart, i - 1);
|
||||||
|
const replacement = `z.object({${objectContent}}).passthrough()`;
|
||||||
|
result = result.slice(0, matchStart) + replacement + result.slice(i + 2);
|
||||||
|
startIndex = matchStart + replacement.length;
|
||||||
|
} else {
|
||||||
|
startIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error("❌ Schema generation failed:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|
@ -7,5 +7,6 @@
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
"include": ["schema/**/*.ts"]
|
"include": ["schema/**/*.ts"],
|
||||||
|
"exclude": ["schema/**/generated/**"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue