diff --git a/.windsurfrules b/.windsurfrules index 7a1c6170..1f786e91 100644 --- a/.windsurfrules +++ b/.windsurfrules @@ -1,142 +1,3 @@ -## 1. Project Structure +# MCP Unity 2.0 -- The project consists of two main parts: - - **`Editor/` (Unity C#/Editor Package)**: A C# Unity Editor package that exposes Unity Editor functionality via a WebSocket bridge. - - **`Server/` (Node.js/TypeScript)**: A Node.js server implementing the Model Context Protocol (MCP), using the official TypeScript SDK. - -## 2. Communication & Protocol - -* **Primary Protocol**: Model Context Protocol (MCP). - * The Unity side uses the `websocket-sharp` library: https://github.com/sta/websocket-sharp/tree/master/websocket-sharp/Server - * The Node.js server uses the `@modelcontextprotocol/sdk` for protocol implementation and exposes tools/resources/prompts to LLMs and AI clients. -* **Transport Layer**: WebSockets. - * Node.js server (`Server/`) acts as a WebSocket *client* to the WebSocket *server* running within the Unity Editor (`Editor/`). -* **Message Format**: JSON. MCP defines the structure for tool/resource calls. - * Node.js tools typically send a request like: `{ method: "csharp_tool_name", params: { ... } }` to Unity via `mcpUnity.sendRequest()`. - * Unity C# tools receive this, execute, and return a `JObject` response. -* **Response Format**: JSON. MCP defines the structure for tool/resource responses. - * Unity C# tools return a `JObject` response. - * Node.js tools receive this and return a `JObject` response. -* **Error Handling**: MCP defines a standard error format. - * Unity C# tools return an error object if an exception occurs. - * Node.js tools receive this and return an error object. - -**Communication Flow**: -- Request: AI LLM Assistant -> Node.js MCP Server -> WebSocket Bridge -> Unity Editor Package -> Unity Editor API -- Response: Unity Editor Package -> WebSocket Bridge -> Node.js MCP Server -> AI LLM Assistant - -## 3. Minimum Supported Versions - -- Unity: 2022.3 or newer -- Node.js: 18.0.0 or newer - -## 4. Key Components - -### Unity Editor Package (Editor/) - -* **`UnityBridge/`**: - * `McpUnityServer.cs`: **Singleton**. The central nervous system on the Unity side. Initializes WebSocket server, registers tools/resources, and dispatches incoming requests to appropriate handlers. - * *LLM Assistant Note*: When adding new tools/resources, they must be registered here in `RegisterTools()` or `RegisterResources()`. Pay attention to how existing tools are instantiated (currently direct `new Tool()`). - * `McpUnitySocketHandler.cs`: Handles individual WebSocket client connections, message parsing, and routing requests to `McpUnityServer` for tool/resource execution. - * `McpUnityEditorWindow.cs`: // TODO: Complete -* **`Tools/`**: Contains C# classes inheriting from `McpToolBase`. Each class implements a specific action that can be triggered in Unity. - * *LLM Assistant Note*: New tools should follow the `McpToolBase` pattern: define `Name` (matching Node.js tool name), `Description`, `IsAsync`, and override `Execute()` or `ExecuteAsync()`. Use `Undo.RecordObject` for actions that modify the scene or assets. -* **`Resources/`**: Contains C# classes inheriting from `McpResourceBase` (assumption, or similar base). These provide read-only access to Unity Editor state. - * *LLM Assistant Note*: New resources should follow the `McpResourceBase` pattern: define `Name` (matching Node.js resource name), `Description`, and override `Fetch()` or `FetchAsync()` -* **`Services/`**: Contains classes providing specific functionalities, often designed for dependency injection. - * Example: `TestRunnerService.cs`, `ConsoleLogsService.cs`. - * *LLM Assistant Note*: Prefer dependency injection for new services. If a tool needs a complex piece of logic, consider abstracting it into a service. -* **`Utils/`**: Utility classes for common tasks like logging (`McpLogger.cs`), settings management (`McpUnitySettings.cs`), GameObject creation (`GameObjectHierarchyCreator.cs`) - -### Node.js Server (Server/) - -* **`src/`**: - * `index.ts`: **Entry point**. Initializes the MCP server, registers all MCP tools and resources, and starts the `McpUnity` WebSocket bridge. - * *LLM Assistant Note*: New tools/resources implemented in TypeScript must be registered here using `server.tool(...)` or `server.resource(...)`. - * `unity/mcpUnity.ts` (or [.js](cci:7://file:///c:/Users/migas/Desktop/mcp-unity/Server~/build/prompts/gameobjectHandlingPrompt.js:0:0-0:0) if compiled): Manages the WebSocket client connection *to* the Unity Editor. Handles sending requests and receiving responses. - * **`tools/`**: TypeScript modules defining MCP tools. Each tool module typically: - * Defines input/output schemas using `zod`. - * Provides a registration function (e.g., `registerMyTool(server, mcpUnity, logger)`). - * Implements a `toolHandler` function that interacts with `mcpUnity.sendRequest()` to call the corresponding C# tool in Unity. - * *LLM Assistant Note*: Follow the existing pattern for new tools: define clear Zod schemas, structure the request for `mcpUnity.sendRequest` correctly (matching the C# tool's `Name` and expected parameters). - * **`resources/`**: TypeScript modules defining MCP resources. Similar structure to tools but for read-only data. - * **`prompts/`**: Contains predefined MCP prompts that guide LLMs in using the available tools and resources for specific workflows. - * *LLM Assistant Note*: If a new complex workflow emerges, consider adding a prompt here. Prompts should clearly list relevant tools/resources and outline step-by-step procedures. - * **`utils/`**: Helper utilities, e.g., `logger.ts`, `errors.ts`. -* **`package.json`**: Manages Node.js dependencies, scripts for building (`tsc`), running, and debugging, including `@modelcontextprotocol/sdk`, `ws`, `express`, etc. -* **`tsconfig.json`**: TypeScript compiler configuration. -* **`build/`**: Output directory for compiled JavaScript files from `src/`. - -## 5. Integration & Usage - -- The Unity Editor package is designed to be used as a package (via UPM or direct import). -- The Node.js server can be started independently and connects to Unity via WebSocket (port configurable, default 8090). -- The system is designed for use with LLM-based IDEs (e.g., Windsurf, Cursor, Claude Desktop) to enable AI-powered Unity Editor automation and queries. - -## 6. Configuration - -- Configuration utilities are provided for generating and injecting MCP config into various IDEs (Cursor, Claude Desktop, Windsurf). -- Unity-side settings are persisted in `ProjectSettings/McpUnitySettings.json`. - -## 7. Design Patterns & Best Practices - -### 7.1. Node.js (Server/ - TypeScript) - -* **Modularity**: Keep tools, resources, and utilities in separate files/modules. -* **Schema Validation**: Use `zod` extensively for defining and validating the `inputSchema` for all tools and resources. This catches errors early. -* **Async/Await**: Use `async/await` for all I/O operations, especially calls to Unity. -* **Error Handling**: Implement robust error handling in tool handlers. Use the `McpUnityError` class for custom errors. Return meaningful error messages to the MCP client. -* **Logging**: Use the provided `Logger` for comprehensive logging. Log entry/exit points of handlers, parameters, and significant events. -* **Configuration**: Externalize configuration (e.g., WebSocket ports, though Unity side is primary for port). -* **MCP SDK Adherence**: Follow best practices for the `@modelcontextprotocol/sdk` when registering tools and resources. - -### 7.2. Unity C# (Editor/) - -* **`McpToolBase` / `McpResourceBase`**: Adhere to the established base class patterns for tools and resources. - * Ensure `Name` property in C# tools matches the `method` string sent from Node.js. -* **Single Responsibility Principle (SRP)**: Tools should be focused on a single task. Complex logic can be delegated to services. -* **Unity API Usage**: - * Use `EditorUtility` for tasks like marking objects dirty (`EditorUtility.SetDirty()`), displaying progress bars, etc. - * Use `Undo.RecordObject()` before modifying any `UnityEngine.Object` to support undo functionality in the editor. Use `Undo.RegisterCreatedObjectUndo()` for newly created objects. - * Be mindful of operations that must run on Unity's main thread. If a tool is `IsAsync = true`, its `ExecuteAsync` method will be marshaled to the main thread. -* **Immutability**: Prefer immutable data structures where possible, though Unity's API often requires direct object manipulation. -* **Error Handling**: Return `JObject` responses indicating success or failure, with clear messages. Use `McpUnitySocketHandler.CreateErrorResponse()` or similar utility if available. -* **Logging**: Use `McpLogger` for consistent logging within Unity. -* **No Blocking Operations in WebSocket Handlers**: For long-running tasks, tools should be marked `IsAsync = true` and use `ExecuteAsync` to avoid blocking the WebSocket communication thread. -* **Dependency Injection**: The project shows a trend towards DI (e.g., `TestRunnerService`). Prefer injecting dependencies into services and tools where practical, rather than relying on singletons or static access, to improve testability and maintainability. - * If `McpUnityServer` needs to provide these, they should be initialized in its constructor or an `InitializeServices` method and passed down. - -## 8. References - -- MCP Protocol: https://modelcontextprotocol.io -- TypeScript SDK: https://github.com/modelcontextprotocol/typescript-sdk -- Inspector: https://github.com/modelcontextprotocol/inspector - -## 9. Guidelines for LLM assistant Contributions and Conventions - -* **Understand the Flow**: Before adding/modifying, trace the call flow from the Node.js tool/resource definition to the corresponding C# handler. -* **Consistency**: - * **Naming**: Follow existing naming conventions for tools, methods, and parameters (e.g., `camelCase` for JSON/JS/TS, `PascalCase` for C#). Tool names (string identifiers) should be consistent across Node.js and C#. - * **Parameter Passing**: If a Node.js tool sends `params.someData`, the C# tool should expect `parameters["someData"]`. For complex objects (like `gameObjectData` or `componentData`), keep them as nested JSON objects. - * **Response Structure**: C# tools should return `JObject`s that the Node.js tool handler can easily process and convert into an MCP `CallToolResult`. -* **Schema First (Node.js)**: When creating a new tool/resource on the Node.js side, define its `zod` schema for parameters first. -* **C# Implementation Second**: Implement the corresponding C# `McpToolBase` (or resource equivalent). Ensure its `Name` matches what the Node.js tool will send. -* **Registration**: - * Register the Node.js tool/resource/prompt in `Server/src/index.ts`. - * Register the C# tool/resource in `Editor/UnityBridge/McpUnityServer.cs`. -* **Prompts**: If adding a significant new capability or workflow, update or add an MCP prompt in `Server/src/prompts/` to guide users/LLMs. -* **Error Handling is Key**: Ensure errors are caught and propagated correctly with informative messages at both Node.js and C# levels. -* **Idempotency**: Where possible, design tools to be idempotent (applying them multiple times with the same input yields the same result). This is not always feasible but is a good goal. -* **Security/Safety**: Be cautious with tools that modify files or execute arbitrary code. Currently, the scope is within Unity, but general caution is advised. -* **Testability**: Write code that is testable. DI helps significantly on the C# side. -* **Conventional Commits**: Follow Conventional Commits for all commit messages. Example: `feat(unity): add new_tool_name for X functionality`. - -## 10. Debugging with MCP Inspector - -To debug the MCP Node.js server using the Model Context Protocol Inspector, run the following command from the project root: - -```shell -npx @modelcontextprotocol/inspector node Server/build/index.js -``` - -This will launch the MCP Inspector, allowing you to inspect and debug live MCP traffic between the Node.js server and connected clients (such as Unity or LLM AI Assistant IDEs). \ No newline at end of file +Read and follow `AGENTS.md` before changing this repository. It is the authoritative guide for the Unity CLI/Pipeline architecture, public catalogs, invariants, and verification matrix. diff --git a/AGENTS.md b/AGENTS.md index b73c5776..60d2c12a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,172 +1,157 @@ -## MCP Unity — AI Agent Guide (MCP Package) +# MCP Unity 2.0 maintainer guide -### Purpose (what this repo is) -**MCP Unity** exposes Unity Editor capabilities to MCP-enabled clients by running: -- **Unity-side “client” (C# Editor scripts)**: a WebSocket server inside the Unity Editor that executes tools/resources. -- **Node-side “server” (TypeScript)**: an MCP stdio server that registers MCP tools/resources and forwards requests to Unity over WebSocket. +## Scope -### How it works (high-level data flow) -- **MCP client** ⇄ (stdio / MCP SDK) ⇄ **Node server** (`Server~/src/index.ts`) -- **Node server** ⇄ (WebSocket JSON-RPC-ish) ⇄ **Unity Editor** (`Editor/UnityBridge/McpUnityServer.cs` + `McpUnitySocketHandler.cs`) -- **Tool/Resource names must match exactly** across Node and Unity (typically `lower_snake_case`). +MCP Unity 2.0.0 extends Unity CLI and Pipeline. It is not a transport bridge. -### Key defaults & invariants -- **Unity WebSocket endpoint**: `ws://localhost:8090/McpUnity` by default. -- **Config file**: `ProjectSettings/McpUnitySettings.json` (written/read by Unity; read opportunistically by Node). -- **Execution thread**: Tool/resource execution is dispatched via `EditorCoroutineUtility` and runs on the **Unity main thread**. Keep synchronous work short; use async patterns for long work. +Supported Editors are Unity 6000.0, Unity 6000.3, and Unity 6000.5. The root package must keep these exact release pins: -### Repo layout (where to change what) +- `com.unity.pipeline@0.3.1-exp.1` +- `com.unity.test-framework@1.3.3` +- minimum Unity `6000.0` +- minimum Unity CLI 1.0.0-beta.2 + +Unity CLI remains an explicit developer/CI installation. `Window > MCP Unity > Setup` may detect it with only `unity --version` and copy instructions/configuration after a user action. It must never install, upgrade, elevate, edit PATH/shell files, write client configuration, or persist a machine path in the project. + +## Architecture and data flow + +Primary flow: + +```text +MCP host -> `unity mcp --project-path ` -> Pipeline + -> Pipeline built-ins + MCP Unity `[CliCommand]` extensions -> Unity Editor +``` + +Optional read-oriented flow: + +```text +MCP host -> private `Server~/build/index.js` + -> lazily owned Unity CLI stdio session -> Pipeline -> Unity Editor +``` + +UPM resolves Pipeline from the root `package.json`. Do not invoke `unity pipeline install`, use `Client.Add` during initialization, or vendor Pipeline. + +The package is local-only. Remote users run Unity CLI on the Unity host and reach it through SSH or external agent infrastructure. + +## Layout + +```text +Editor/ + Commands/ Five public Pipeline extension commands and DTOs + Setup/ User-initiated CLI/Pipeline checks and copy helpers + Tests/ EditMode contracts, safety, Undo, bounds, setup tests +Server~/ + src/cli/ Args, CLI lookup, strict version check + src/unity/ Owned official Unity MCP child session + src/resources/ Read-only resource projection and dashboard + src/prompts/ Companion prompts + src/ui/ Dashboard source + src/__tests__/ Companion and release-contract tests + build/ Bundled private companion output +package.json UPM identity and exact Pipeline dependency +README.md User setup and exhaustive 1.4 migration table +CHANGELOG.md Release notes +``` + +`Server~` is private and bundled as one self-contained ESM entrypoint. Keep Node 20+, `@modelcontextprotocol/sdk@1.26.0`, `@modelcontextprotocol/ext-apps@1.0.1`, and `zod@3.25.76` exact until a coordinated upgrade is tested. It has no npm `bin`, publish configuration, registry manifest, Docker image, or Smithery surface. `npm run build` must regenerate `build/index.js`, copy the dashboard, and update `THIRD_PARTY_NOTICES.md`; `npm run build:check` must then prove parity. The Node 20 clean archive MCP smoke must initialize the shipped bundle, launch its fake `mcp` child through `process.execPath`, and read the copied dashboard with no reachable `node_modules`, shell, chmod, or platform-specific command wrapper. Never require package users to install npm dependencies. + +## Public catalogs + +The only MCP Unity commands are: + +- `inspect_gameobject` +- `duplicate_gameobject` +- `unload_scene` +- `editor_step` +- `assign_material` + +The optional companion exposes only: + +- tool `show_unity_dashboard` +- resources: + - `unity://logs{?severity,limit}` + - `unity://scenes-hierarchy{?path,max_nodes}` + - `unity://gameobject/{target}` + - `unity://packages{?include_indirect}` + - `unity://tests/{mode}` + - `ui://unity-dashboard` +- prompts: + - `gameobject_handling_strategy` + - `unity_dashboard` + +Everything else maps to the official Pipeline 0.3.1-exp.1 catalog. Legacy aliases are intentionally absent. + +## Adding or changing an extension command + +1. Start with a failing EditMode test under `Editor/Tests`. +2. Implement the command under `Editor/Commands` with Pipeline `[CliCommand]`, `[CliArg]`, `ObjectRef`, and `AuthoringResult` contracts. +3. Keep inputs explicit and bounded. Inspection-like outputs need depth, count, collection, and string limits. `inspect_gameobject` must use one shared aggregate conversion-work budget across all component/property scans and lazy conversions, reserve before every serialized reader/iterator/wrapper allocation, treat exact exhaustion as unavailable to future work, avoid rewalking materialized values for accounting, retain stable camelCase counters and honest limit markers, and serialize to at most 512 KiB. +4. Record Undo for scene/object mutation and record prefab instance modifications. +5. Return stable DTOs with explicit camelCase serialization names. +6. Update `CommandDiscoveryTests` so a collision with the pinned Pipeline catalog fails. +7. Update README/AGENTS catalogs and migration guidance when public behavior changes. +8. Run the complete Unity matrix. + +Do not add Node proxies for mutation commands. + +## Adding or changing a companion resource + +1. Start with a failing Jest test in `Server~/src/__tests__`. +2. Map the URI to an official read-only Pipeline command or one of the five extensions in `Server~/src/resources/companionResources.ts`. +3. Validate and bound all URI inputs. Every Unity-backed resource must remain at or below 512 KiB and carry honest top-level projection/truncation metadata; bound strings, arrays, objects, keys, depth, values, and traversal work. +4. Decode structured content and JSON text defensively. Route transport, command, URI, CLI, disconnect, malformed-payload, dashboard-read, and outer MCP resource errors through the centralized 4 KiB UTF-8-safe error-detail bound with an explicit `[truncated]` marker. +5. Permit at most one reconnect/retry for a transport-interrupted read. +6. Never retry, mirror, or expose a mutation tool. +7. Register only the approved URI in `Server~/src/companionServer.ts` and update catalog contract tests. + +The companion CLI lookup order is `--unity-cli-path`, `UNITY_CLI_PATH`, then `unity` from `PATH`. It may execute `--version` for validation and lazily launch `unity mcp --project-path ` for its MCP session. It must not install Unity CLI. + +## Test and build commands + +Companion: + +```bash +cd Server~ +npm ci +npm test -- --runInBand --detectOpenHandles +npm run build +npm run build:check +npm audit --omit=dev ``` -/ -├── Editor/ # Unity Editor package code (C#) -│ ├── Tools/ # Tools (inherit McpToolBase) -│ ├── Resources/ # Resources (inherit McpResourceBase) -│ ├── UnityBridge/ # WebSocket server + message routing -│ ├── Services/ # Test/log services used by tools/resources -│ └── Utils/ # Shared helpers (config, logging, workspace integration) -├── Server~/ # Node MCP server (TypeScript, ESM) -│ ├── src/index.ts # Registers tools/resources/prompts with MCP SDK -│ ├── src/tools/ # MCP tool definitions (zod schema + handler) -│ ├── src/resources/ # MCP resource definitions -│ └── src/unity/mcpUnity.ts # WebSocket client that talks to Unity + +The pinned MCP SDK currently reports two moderate advisories through an unused Hono HTTP adapter. The companion is stdio-only. Record the audit result; do not change the mandated SDK pin without a compatibility upgrade task. + +Unity EditMode batch pattern: + +```bash +"/Contents/MacOS/Unity" \ + -batchmode -nographics -projectPath "" \ + -runTests -testPlatform EditMode -testResults "" ``` -### Quickstart (local dev) -- **Unity side** - - Open the Unity project that has this package installed. - - Ensure the server is running (auto-start is controlled by `McpUnitySettings.AutoStartServer`). - - Settings persist in `ProjectSettings/McpUnitySettings.json`. - -- **Node side (build)** - - `cd Server~ && npm run build` - - The MCP entrypoint is `Server~/build/index.js` (published as an MCP stdio server). - -- **Node side (debug/inspect)** - - `cd Server~ && npm run inspector` to use the MCP Inspector. - -### Configuration (Unity ↔ Node bridge) -The Unity settings file is the shared contract: -- **Path**: `ProjectSettings/McpUnitySettings.json` -- **Fields** - - **Port** (default **8090**): Unity WebSocket server port. - - **RequestTimeoutSeconds** (default **10**): Node request timeout (Node reads this if the settings file is discoverable). - - **AllowRemoteConnections** (default **false**): Unity binds to `0.0.0.0` when enabled; otherwise `localhost`. - - **EnableInfoLogs**: Unity console logging verbosity. - - **NpmExecutablePath**: optional npm path for Unity-driven install/build. - -Node reads config from `../ProjectSettings/McpUnitySettings.json` relative to **its current working directory**. If not found, Node falls back to: -- **host**: `localhost` -- **port**: `8090` -- **timeout**: `10s` - -**Remote connection note**: -- If Unity is on another machine, set `AllowRemoteConnections=true` in Unity and set `UNITY_HOST=` for the Node process. - -### Adding a new capability - -### Add a tool -1. **Unity (C#)** - - Add `Editor/Tools/Tool.cs` inheriting `McpToolBase`. - - Set `Name` to the MCP tool name (recommended: `lower_snake_case`). - - Implement: - - `Execute(JObject parameters)` for synchronous work, or - - set `IsAsync = true` and implement `ExecuteAsync(JObject parameters, TaskCompletionSource tcs)` for long-running operations. - - Register it in `Editor/UnityBridge/McpUnityServer.cs` (`RegisterTools()`). - -2. **Node (TypeScript)** - - Add `Server~/src/tools/Tool.ts`. - - Register the tool in `Server~/src/index.ts`. - - Use a zod schema for params; forward to Unity using the same `method` string: - - `mcpUnity.sendRequest({ method: toolName, params: {...} })` - -3. **Build** - - `cd Server~ && npm run build` - -### Add a resource -1. **Unity (C#)** - - Add `Editor/Resources/Resource.cs` inheriting `McpResourceBase`. - - Set `Name` (method string) and `Uri` (e.g. `unity://...`). - - Implement `Fetch(...)` or `FetchAsync(...)`. - - Register in `Editor/UnityBridge/McpUnityServer.cs` (`RegisterResources()`). - -2. **Node (TypeScript)** - - Add `Server~/src/resources/.ts`, register in `Server~/src/index.ts`. - - Forward to Unity via `mcpUnity.sendRequest({ method: resourceName, params: {} })`. - -### Logging & debugging -- **Unity** - - Uses `McpUnity.Utils.McpLogger` (info logs gated by `EnableInfoLogs`). - - Connection lifecycle is managed in `Editor/UnityBridge/McpUnityServer.cs` (domain reload & playmode transitions stop/restart the server). - -- **Node** - - Logging is controlled by env vars: - - `LOGGING=true` enables console logging. - - `LOGGING_FILE=true` writes `log.txt` in the Node process working directory. - -### Common pitfalls -- **Port mismatch**: Unity default is **8090**; update docs/config if you change it. -- **Name mismatch**: Node `toolName`/`resourceName` must equal Unity `Name` exactly, or Unity responds `unknown_method`. -- **Long main-thread work**: synchronous `Execute()` blocks the Unity editor; use async patterns for heavy operations. -- **Remote connections**: Unity must bind `0.0.0.0` (`AllowRemoteConnections=true`) and Node must target the correct host (`UNITY_HOST`). -- **Unity domain reload**: the server stops during script reloads and may restart; avoid relying on persistent in-memory state across reloads. -- **Multiplayer Play Mode**: Clone instances automatically skip server startup; only the main editor hosts the MCP server. -- **Schema compatibility across clients**: avoid reusing the same nested Zod object instance for multiple sibling fields (for example `position`, `rotation`, `scale`). Some MCP clients fail on local refs like `#/properties/position`; prefer creating a fresh nested schema per field. - -### Release/version bump checklist -- Update versions consistently: - - Unity package `package.json` (`version`) - - Node server `Server~/package.json` (`version`) -- Rebuild Node output: `cd Server~ && npm run build` - -### Available tools (current) -- `execute_menu_item` — Execute Unity menu items -- `select_gameobject` — Select GameObjects in hierarchy -- `update_gameobject` — Update or create GameObject properties -- `update_component` — Update or add components on GameObjects -- `add_package` — Install packages via Package Manager -- `run_tests` — Run Unity Test Runner tests -- `send_console_log` — Send logs to Unity console -- `add_asset_to_scene` — Add assets to scene -- `create_prefab` — Create prefabs with optional scripts -- `create_scene` — Create and save new scenes -- `load_scene` — Load scenes (single or additive) -- `delete_scene` — Delete scenes and remove from Build Settings -- `save_scene` — Save current scene (with optional Save As) -- `get_scene_info` — Get active scene info and loaded scenes list -- `get_play_mode_status` — Get Unity play mode status (isPlaying, isPaused) -- `set_play_mode_status` — Control Unity play mode (play, pause, stop, step) -- `unload_scene` — Unload scene from hierarchy -- `get_gameobject` — Get detailed GameObject info -- `get_console_logs` — Retrieve Unity console logs -- `recompile_scripts` — Recompile all project scripts -- `duplicate_gameobject` — Duplicate GameObjects with optional rename/reparent -- `delete_gameobject` — Delete GameObjects from scene -- `reparent_gameobject` — Change GameObject parent in hierarchy -- `create_material` — Create materials with specified shader -- `assign_material` — Assign materials to Renderer components -- `modify_material` — Modify material properties (colors, floats, textures) -- `get_material_info` — Get material details including all properties - -### Available apps (current) -- `show_unity_dashboard` — Open the Unity dashboard MCP App in VS Code - -### Available resources (current) -- `unity://menu-items` — List of available menu items -- `unity://scenes-hierarchy` — Current scene hierarchy -- `unity://gameobject/{id}` — GameObject details by ID or path -- `unity://logs` — Unity console logs -- `unity://packages` — Installed and available packages -- `unity://assets` — Asset database information -- `unity://tests/{testMode}` — Test Runner test information -- `ui://unity-dashboard` — Unity dashboard MCP App UI - -### Available prompts (current) -- `unity_dashboard` — Opens Unity dashboard MCP app with guided information about features -- `gameobject_handling_strategy` — Provides structured workflow for GameObject operations - -### Update policy (for agents) -- Update this file when: - - tools/resources/prompts are added/removed/renamed, - - config shape or default ports/paths change, - - the bridge protocol changes (request/response contract). -- Keep it **high-signal**: where to edit code, how to run/build/debug, and the invariants that prevent subtle breakage. +Run on: + +- `/Applications/Unity/Hub/Editor/6000.0.80f1/Unity.app` +- `/Applications/Unity/Hub/Editor/6000.3.18f1/Unity.app` +- `/Applications/Unity/Hub/Editor/6000.5.5f1/Unity.app` + +Also run `git diff --check`, verify a clean UPM install/removal, confirm no old bridge listener exists, and confirm no legacy settings file is created. + +## Release and architecture invariants + +- Root and companion versions stay synchronized at `2.0.0` for this release. +- README and this guide state Pipeline `0.3.1-exp.1`, Unity CLI 1.0.0-beta.2, and all three supported Unity lines. +- UPM declares Pipeline exactly once as a transitive dependency of consuming projects. +- Removing MCP Unity from a consumer manifest must not leave a direct Pipeline entry. +- `Window > MCP Unity > Setup` never opens automatically. +- No custom WebSocket server/client, socket handler, command queue, reconnect generation, port 8090 management, or `ProjectSettings/McpUnitySettings.json` may return. +- No direct Editor Coroutines or Newtonsoft package dependency may return. +- No registry server manifest, npm publication surface, automatic client configuration, Unity-driven npm build, or PackedCache workspace mutation may return. +- The package remains useful without the companion. +- The exhaustive migration inventory in README is checked against the actual `1.4.0` tag. + +Run `unity mcp --project-path ` in a disposable project and verify official plus custom commands are discoverable before a release. + +## Update policy + +Update README, this file, release contract tests, and CHANGELOG whenever a version pin, supported Unity line, command/resource/prompt name, setup behavior, or companion contract changes. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..fdf2fb87 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,74 @@ +# Changelog + +All notable changes to MCP Unity are documented here. + +## [2.0.0] - Unreleased + +### Breaking architecture + +- Replaced the custom Unity WebSocket/Node bridge with Unity CLI and `com.unity.pipeline@0.3.1-exp.1`. +- Raised the minimum Editor version to Unity 6000.0 and defined support for Unity 6000.0, 6000.3, and 6000.5. +- Removed the former bridge listener, settings file, remote socket mode, tool aliases, command queues, retry generations, port/time-out controls, automatic MCP client configuration, Unity-driven npm workflow, and PackedCache workspace mutation. +- Removed the registry/published-server surface. `Server~` is now a private optional companion. + +### Added + +- Five Pipeline extensions: `inspect_gameobject`, `duplicate_gameobject`, `unload_scene`, `editor_step`, and `assign_material`. +- User-initiated `Window > MCP Unity > Setup` flow for Pipeline status, Unity CLI detection, official installation instructions, and configuration copying. +- Optional read-oriented companion resources, prompts, and dashboard. +- Exhaustive 1.4.0 migration table and release-contract tests. +- A self-contained Node 20 companion bundle with generated third-party notices plus portable shell-free clean-archive startup, fake child MCP, and stdio dashboard verification. +- Aggregate 512 KiB output ceilings for `inspect_gameobject` and every companion resource, including exact-boundary pre-allocation work accounting without a second value traversal, explicit component/property/projection truncation metadata, stack-safe adversarial payload handling, and 4 KiB bounded companion errors including dashboard failures. + +### Experimental dependencies + +- Unity CLI 1.0.0-beta.2 or newer is installed separately by each user or CI environment. +- Pipeline is pinned to experimental version `0.3.1-exp.1` and is resolved automatically by UPM. +- The exact MCP SDK pin has two inherited moderate npm advisories in an unused Hono HTTP adapter; the companion uses stdio only. + +## [1.4.0] - 2026-07-24 + +Final release of the legacy custom bridge. + +### 🆕 What is New + +- Added the Unity Dashboard MCP App with Play Mode controls, scene hierarchy browsing, console and package views, GameObject selection, focus filtering, an inspector panel, and bidirectional agent context. The release includes the `show_unity_dashboard` tool, `ui://unity-dashboard` resource, and `unity_dashboard` prompt ([#109](https://github.com/CoderGamester/mcp-unity/pull/109)). +- Added `get_play_mode_status` and `set_play_mode_status` for starting, pausing, stepping, and stopping Play Mode, plus tool access to scene hierarchy and console data used by the dashboard ([#109](https://github.com/CoderGamester/mcp-unity/pull/109)). +- Added project-local auto-configuration for Cursor, Claude Code, and Codex CLI, with portable project-relative paths suitable for Git-shared MCP configuration ([`a32e47d`](https://github.com/CoderGamester/mcp-unity/commit/a32e47d4ec8731685394dd562aa6f4f119f2bf79)). +- Added OpenCode auto-configuration and relative-path support for GitHub Copilot, OpenCode, and manually copied workspace configurations ([`ec0e1e8`](https://github.com/CoderGamester/mcp-unity/commit/ec0e1e859e9f26c2e0e65577bee609ce318715e6), [`320e443`](https://github.com/CoderGamester/mcp-unity/commit/320e443e93ebba4fbf2e3630424b3b939448d55f)). +- Added Unity bridge request diagnostics and expanded lifecycle, restart, tool, dashboard, resource, and release-metadata test coverage. + +### 🛠️ What Was Fixed + +- Fixed `update_component` support for private serialized fields, inherited fields, nested property paths, and asset references resolved by path or GUID ([#106](https://github.com/CoderGamester/mcp-unity/pull/106)). +- Bounded deep and oversized `get_gameobject` responses with configurable depth/component scopes, a 5 MB safety ceiling, and explicit truncation markers instead of dropping the MCP connection ([`dfc623a`](https://github.com/CoderGamester/mcp-unity/commit/dfc623acbfb08438dcddd4e9b25de61de0cb1ebf)). +- Moved WebSocket request execution onto Unity's main thread to prevent Editor API access from corrupting the Inspector, then replaced cross-thread `delayCall` mutation with an update-drained concurrent queue so requests continue while the Editor is unfocused ([#139](https://github.com/CoderGamester/mcp-unity/pull/139), [#151](https://github.com/CoderGamester/mcp-unity/pull/151)). +- Kept the Windows Editor loop ticking while minimized or unfocused. The native timer now starts only after the WebSocket listener succeeds and stops on failed startup, shutdown, disposal, assembly reload, Editor quit, and Play Mode transitions; its callback is rooted, exception-guarded, and reports Win32 timer errors ([#150](https://github.com/CoderGamester/mcp-unity/pull/150)). +- Restarted the WebSocket server after entering Play Mode when domain reload is disabled, while preserving the existing reload-enabled lifecycle ([#152](https://github.com/CoderGamester/mcp-unity/pull/152)). +- Removed invalid WebSocket origin headers that prevented some Codex and MCP clients from connecting ([#149](https://github.com/CoderGamester/mcp-unity/pull/149)). +- Hardened WebSocket shutdown, restart cleanup, retry cancellation, failed-start handling, and request diagnostics to avoid stale listeners and duplicate restart attempts. +- Fixed a Unity `.meta` GUID collision with Meta XR SDK and Unity AI Assistant packages ([#133](https://github.com/CoderGamester/mcp-unity/pull/133)). +- Updated object lookup and response IDs for Unity's newer `EntityId` API while retaining `InstanceID` behavior on older supported Editors, and migrated material inspection to Unity's public shader-property APIs to remove Unity 6 compatibility warnings. +- Reduced noisy bridge logging, disabled info logs by default, and made batch-tool lookup independently testable. + +### 🔄 What Changed + +- Clarified global versus project-local MCP configuration and documented portable, team-shared configuration variants ([#140](https://github.com/CoderGamester/mcp-unity/pull/140)). +- Synchronized the Unity package, Node server, and MCP runtime versions at `1.4.0`. +- Removed the invalid npm publication declaration from the legacy registry manifest. +- Preserved Unity 2022.3 compatibility for this final custom WebSocket bridge release. + +### New Contributors + +* @Hinneman made their first contribution in https://github.com/CoderGamester/mcp-unity/pull/109 +* @cfirz made their first contribution in https://github.com/CoderGamester/mcp-unity/pull/139 +* @mashai made their first contribution in https://github.com/CoderGamester/mcp-unity/pull/140 +* @Fen747 made their first contribution in https://github.com/CoderGamester/mcp-unity/pull/149 +* @stefangrosu44-stack made their first contribution in https://github.com/CoderGamester/mcp-unity/pull/150 +* @Feetschaa made their first contribution in https://github.com/CoderGamester/mcp-unity/pull/151 +* @Numbcris made their first contribution in https://github.com/CoderGamester/mcp-unity/pull/152 + +**Full Changelog**: https://github.com/CoderGamester/mcp-unity/compare/1.3.0...1.4.0 + +[2.0.0]: https://github.com/CoderGamester/mcp-unity/compare/1.4.0...HEAD +[1.4.0]: https://github.com/CoderGamester/mcp-unity/releases/tag/1.4.0 diff --git a/CHANGELOG.md.meta b/CHANGELOG.md.meta new file mode 100644 index 00000000..9a2a1d79 --- /dev/null +++ b/CHANGELOG.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 7eb2ad57f3904c398a5d2f5b00a9fb8d +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/CLAUDE.md b/CLAUDE.md index 1033cf9d..8b88a6a9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,132 +1,5 @@ -# CLAUDE.md +# Claude Code guidance -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +Read [AGENTS.md](AGENTS.md) completely before changing this repository. It is the authoritative MCP Unity 2.0 maintainer guide. -## Project Overview - -MCP Unity exposes Unity Editor capabilities to MCP-enabled clients (Cursor, Windsurf, Claude Code, Codex CLI, GitHub Copilot) through a two-tier architecture: - -- **Unity Editor (C#)**: WebSocket server inside Unity that executes tools/resources -- **Node.js Server (TypeScript)**: MCP stdio server that bridges AI clients to Unity via WebSocket - -**Data flow**: MCP Client ⇄ (stdio) ⇄ Node Server (`Server~/src/index.ts`) ⇄ (WebSocket) ⇄ Unity Editor (`Editor/UnityBridge/McpUnityServer.cs`) - -## Build & Development Commands - -### Node.js Server (`Server~/`) -```bash -npm install # Install dependencies -npm run build # Compile TypeScript to build/ -npm run watch # Watch mode compilation -npm start # Run server (node build/index.js) -npm test # Run Jest tests (uses --experimental-vm-modules) -npm run test:watch # Watch mode testing -npm run inspector # Launch MCP Inspector for debugging -``` - -### Unity Side -- Build/Test via Unity Editor -- **Tools > MCP Unity > Server Window** for configuration -- **Window > General > Test Runner** for EditMode tests - -## Key Directories - -``` -Editor/ # Unity Editor package (C#) -├── Tools/ # MCP tools (inherit McpToolBase) -├── Resources/ # MCP resources (inherit McpResourceBase) -├── Services/ # TestRunnerService, ConsoleLogsService -├── UnityBridge/ # WebSocket server + message routing -│ ├── McpUnityServer.cs # Singleton managing server lifecycle -│ └── McpUnitySocketHandler.cs # WebSocket handler -└── Utils/ # Logging, config, workspace helpers - -Server~/ # Node.js MCP server (TypeScript/ESM) -├── src/index.ts # Entry point - registers tools/resources -├── src/tools/ # MCP tool definitions (zod + handler) -├── src/resources/ # MCP resource definitions -└── src/unity/mcpUnity.ts # WebSocket client connecting to Unity -``` - -## Key Invariants - -- **WebSocket endpoint**: `ws://localhost:8090/McpUnity` (configurable) -- **Config file**: `ProjectSettings/McpUnitySettings.json` -- **Tool/resource names must match exactly** between Node and Unity (use `lower_snake_case`) -- **Execution thread**: All tool execution runs on Unity main thread via EditorCoroutineUtility - -## Adding a New Tool - -### 1. Unity Side (C#) -Create `Editor/Tools/YourTool.cs`: -```csharp -public class YourTool : McpToolBase { - public override string Name => "your_tool"; // Must match Node side - public override JObject Execute(JObject parameters) { - // Implementation - } -} -``` -Register in `McpUnityServer.cs` → `RegisterTools()`. - -### 2. Node Side (TypeScript) -Create `Server~/src/tools/yourTool.ts`: -```typescript -export function registerYourTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - server.tool("your_tool", "Description", paramsSchema.shape, async (params) => { - return await mcpUnity.sendRequest({ method: "your_tool", params }); - }); -} -``` -Register in `Server~/src/index.ts`. - -### 3. Build -```bash -cd Server~ && npm run build -``` - -## Adding a New Resource - -Same pattern as tools: -- Unity: inherit `McpResourceBase`, implement `Fetch()`, register in `RegisterResources()` -- Node: register with `server.resource()`, forward via `mcpUnity.sendRequest()` - -## Configuration - -**McpUnitySettings.json** fields: -- `Port` (default 8090): Unity WebSocket server port -- `RequestTimeoutSeconds` (default 10): Node request timeout -- `AllowRemoteConnections` (default false): Bind to 0.0.0.0 when true - -**Environment variables** (Node side): -- `UNITY_HOST`: Override Unity host (for remote connections) -- `LOGGING=true`: Enable console logging -- `LOGGING_FILE=true`: Write logs to log.txt - -## Debugging - -- **MCP Inspector**: `cd Server~ && npm run inspector` -- **Unity logs**: Controlled by `EnableInfoLogs` in settings -- **Node logs**: Set `LOGGING=true` environment variable - -## Common Pitfalls - -- **Name mismatch**: Node tool/resource name must equal Unity `Name` exactly -- **Long main-thread work**: Synchronous `Execute()` blocks Unity; use `IsAsync = true` with `ExecuteAsync()` for long operations -- **Unity domain reload**: Server stops during script reloads; avoid persistent in-memory state -- **Port conflicts**: Default is 8090; check if another process is using it -- **Multiplayer Play Mode**: Clone instances auto-skip server startup; only main editor hosts MCP - -## Code Conventions - -- **C# classes**: PascalCase (e.g., `CreateSceneTool`) -- **TypeScript functions**: camelCase (e.g., `registerCreateSceneTool`) -- **Tool/resource names**: lower_snake_case (e.g., `create_scene`) -- **Commits**: Conventional format - `feat(scope):`, `fix(scope):`, `chore:` -- **Undo support**: Use `Undo.RecordObject()` for scene modifications - -## Requirements - -- Unity 2022.3+ (Unity 6 recommended) -- Node.js 18+ -- npm 9+ +Use [README.md](README.md) for user-facing Unity CLI/Pipeline setup and the exhaustive 1.4.0 migration table. Use [CHANGELOG.md](CHANGELOG.md) for release history. diff --git a/Editor/Commands.meta b/Editor/Commands.meta new file mode 100644 index 00000000..94467340 --- /dev/null +++ b/Editor/Commands.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0a731b41c2154a8296cfd67165517aac +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/AssignMaterialCommand.cs b/Editor/Commands/AssignMaterialCommand.cs new file mode 100644 index 00000000..9c7be8d3 --- /dev/null +++ b/Editor/Commands/AssignMaterialCommand.cs @@ -0,0 +1,50 @@ +using System; +using Unity.Pipeline.Commands; +using Unity.Pipeline.Editor.Authoring; +using Unity.Pipeline.Models; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; + +namespace McpUnity.Extensions.Commands +{ + public static class AssignMaterialCommand + { + [CliCommand("assign_material", "Assign a Material to a Renderer shared-material slot.")] + public static AssignMaterialResult Assign( + [CliArg("game_object", "GameObject reference whose Renderer will be edited.", Required = true)] ObjectRef gameObject, + [CliArg("material", "Material reference to assign.", Required = true)] ObjectRef material, + [CliArg("slot", "Renderer shared-material slot index.")] int slot = 0) + { + var target = CommandObjectResolver.Resolve(gameObject, "game_object"); + var assignedMaterial = CommandObjectResolver.Resolve(material, "material"); + var renderer = target.GetComponent(); + if (renderer == null) + throw new InvalidOperationException($"GameObject '{target.name}' does not have a Renderer."); + + var sharedMaterials = renderer.sharedMaterials; + if (slot < 0 || slot >= sharedMaterials.Length) + { + throw new ArgumentOutOfRangeException( + nameof(slot), + slot, + $"Slot must be between 0 and {sharedMaterials.Length - 1} for Renderer '{renderer.name}'."); + } + + Undo.RecordObject(renderer, "Assign Material"); + sharedMaterials[slot] = assignedMaterial; + renderer.sharedMaterials = sharedMaterials; + EditorUtility.SetDirty(renderer); + if (target.scene.IsValid()) + EditorSceneManager.MarkSceneDirty(target.scene); + PrefabUtility.RecordPrefabInstancePropertyModifications(renderer); + + return new AssignMaterialResult + { + GameObject = ObjectResolver.Describe(target), + Material = ObjectResolver.Describe(assignedMaterial), + Slot = slot + }; + } + } +} diff --git a/Editor/Tests/MaterialToolsTests.cs.meta b/Editor/Commands/AssignMaterialCommand.cs.meta similarity index 83% rename from Editor/Tests/MaterialToolsTests.cs.meta rename to Editor/Commands/AssignMaterialCommand.cs.meta index 068fdb3e..a540357e 100644 --- a/Editor/Tests/MaterialToolsTests.cs.meta +++ b/Editor/Commands/AssignMaterialCommand.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 805928c97ab04ebeb48861191f722287 +guid: 1e03020ef7f6484b8e70a4f5f0908cdb MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Editor/Commands/CommandObjectResolver.cs b/Editor/Commands/CommandObjectResolver.cs new file mode 100644 index 00000000..fde3e149 --- /dev/null +++ b/Editor/Commands/CommandObjectResolver.cs @@ -0,0 +1,28 @@ +using System; +using Unity.Pipeline.Editor.Authoring; +using Unity.Pipeline.Models; +using UnityEngine; + +namespace McpUnity.Extensions.Commands +{ + internal static class CommandObjectResolver + { + public static T Resolve(ObjectRef reference, string argumentName) + where T : UnityEngine.Object + { + if (reference == null || reference.IsEmpty) + throw new ArgumentException($"'{argumentName}' is required."); + + if (!ObjectResolver.TryResolve(reference, out var resolved, out var error)) + throw new ArgumentException($"Could not resolve '{argumentName}': {error}"); + + if (!(resolved is T typed)) + { + throw new ArgumentException( + $"'{argumentName}' resolved to {resolved.GetType().Name}, not {typeof(T).Name}."); + } + + return typed; + } + } +} diff --git a/Editor/Tools/MaterialTools.cs.meta b/Editor/Commands/CommandObjectResolver.cs.meta similarity index 83% rename from Editor/Tools/MaterialTools.cs.meta rename to Editor/Commands/CommandObjectResolver.cs.meta index 74c237a8..08d965ae 100644 --- a/Editor/Tools/MaterialTools.cs.meta +++ b/Editor/Commands/CommandObjectResolver.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c66563b66ef943c78ce4eaabe4ddf0fa +guid: 0c28ab6ef485433cab1afba28d4b4658 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Editor/Commands/CommandResults.cs b/Editor/Commands/CommandResults.cs new file mode 100644 index 00000000..8e281b15 --- /dev/null +++ b/Editor/Commands/CommandResults.cs @@ -0,0 +1,288 @@ +using System.Collections.Generic; +using System.Runtime.Serialization; +using Unity.Pipeline.Models; + +namespace McpUnity.Extensions.Commands +{ + [DataContract] + public sealed class InspectGameObjectResult + { + [DataMember(Name = "root")] + public GameObjectInspection Root { get; set; } + + [DataMember(Name = "maxDepth")] + public int MaxDepth { get; set; } + + [DataMember(Name = "maxNodes")] + public int MaxNodes { get; set; } + + [DataMember(Name = "maxPropertiesPerComponent")] + public int MaxPropertiesPerComponent { get; set; } + + [DataMember(Name = "nodesReturned")] + public int NodesReturned { get; set; } + + [DataMember(Name = "nodeLimitReached")] + public bool NodeLimitReached { get; set; } + + [DataMember(Name = "maxComponentsPerGameObject")] + public int MaxComponentsPerGameObject { get; set; } + + [DataMember(Name = "maxTotalComponents")] + public int MaxTotalComponents { get; set; } + + [DataMember(Name = "componentsReturned")] + public int ComponentsReturned { get; set; } + + [DataMember(Name = "componentLimitReached")] + public bool ComponentLimitReached { get; set; } + + [DataMember(Name = "aggregateWorkBudget")] + public int AggregateWorkBudget { get; set; } + + [DataMember(Name = "aggregateWorkUsed")] + public int AggregateWorkUsed { get; set; } + + [DataMember(Name = "aggregateWorkLimitReached")] + public bool AggregateWorkLimitReached { get; set; } + + [DataMember(Name = "aggregateConversionCount")] + public int AggregateConversionCount { get; set; } + + [DataMember(Name = "aggregatePropertiesScanned")] + public int AggregatePropertiesScanned { get; set; } + + [DataMember(Name = "aggregateContentBudgetBytes")] + public int AggregateContentBudgetBytes { get; set; } + + [DataMember(Name = "aggregateEstimatedContentBytes")] + public int AggregateEstimatedContentBytes { get; set; } + + [DataMember(Name = "aggregateContentLimitReached")] + public bool AggregateContentLimitReached { get; set; } + + [DataMember(Name = "conversionTruncated")] + public bool ConversionTruncated { get; set; } + + [DataMember(Name = "payloadBudgetBytes")] + public int PayloadBudgetBytes { get; set; } + + [DataMember(Name = "payloadBytes")] + public int PayloadBytes { get; set; } + + [DataMember(Name = "payloadTruncated")] + public bool PayloadTruncated { get; set; } + + [DataMember(Name = "payloadTruncationReason")] + public string PayloadTruncationReason { get; set; } + } + + [DataContract] + public sealed class GameObjectInspection + { + [DataMember(Name = "identity")] + public AuthoringResult Identity { get; set; } + + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "path")] + public string Path { get; set; } + + [DataMember(Name = "scenePath")] + public string ScenePath { get; set; } + + [DataMember(Name = "activeSelf")] + public bool ActiveSelf { get; set; } + + [DataMember(Name = "activeInHierarchy")] + public bool ActiveInHierarchy { get; set; } + + [DataMember(Name = "layer")] + public int Layer { get; set; } + + [DataMember(Name = "layerName")] + public string LayerName { get; set; } + + [DataMember(Name = "tag")] + public string Tag { get; set; } + + [DataMember(Name = "isStatic")] + public bool IsStatic { get; set; } + + [DataMember(Name = "transform")] + public TransformInspection Transform { get; set; } + + [DataMember(Name = "childCount")] + public int ChildCount { get; set; } + + [DataMember(Name = "children")] + public List Children { get; set; } = new List(); + + [DataMember(Name = "childrenTruncated")] + public bool ChildrenTruncated { get; set; } + + [DataMember(Name = "componentsIncluded")] + public bool ComponentsIncluded { get; set; } + + [DataMember(Name = "componentCount")] + public int ComponentCount { get; set; } + + [DataMember(Name = "components")] + public List Components { get; set; } = new List(); + + [DataMember(Name = "componentsTruncated")] + public bool ComponentsTruncated { get; set; } + + [DataMember(Name = "componentsOmitted")] + public int ComponentsOmitted { get; set; } + } + + [DataContract] + public sealed class TransformInspection + { + [DataMember(Name = "localPosition")] + public Vector3Inspection LocalPosition { get; set; } + + [DataMember(Name = "localEulerAngles")] + public Vector3Inspection LocalEulerAngles { get; set; } + + [DataMember(Name = "localScale")] + public Vector3Inspection LocalScale { get; set; } + + [DataMember(Name = "worldPosition")] + public Vector3Inspection WorldPosition { get; set; } + + [DataMember(Name = "worldEulerAngles")] + public Vector3Inspection WorldEulerAngles { get; set; } + } + + [DataContract] + public sealed class Vector3Inspection + { + [DataMember(Name = "x")] + public float X { get; set; } + + [DataMember(Name = "y")] + public float Y { get; set; } + + [DataMember(Name = "z")] + public float Z { get; set; } + } + + [DataContract] + public sealed class ComponentInspection + { + [DataMember(Name = "identity")] + public AuthoringResult Identity { get; set; } + + [DataMember(Name = "type")] + public string Type { get; set; } + + [DataMember(Name = "missing")] + public bool Missing { get; set; } + + [DataMember(Name = "enabled")] + public bool? Enabled { get; set; } + + [DataMember(Name = "propertiesIncluded")] + public bool PropertiesIncluded { get; set; } + + [DataMember(Name = "serializedPropertyCount")] + public int SerializedPropertyCount { get; set; } + + [DataMember(Name = "properties")] + public List Properties { get; set; } = + new List(); + + [DataMember(Name = "propertiesTruncated")] + public bool PropertiesTruncated { get; set; } + + [DataMember(Name = "propertiesReturned")] + public int PropertiesReturned { get; set; } + + [DataMember(Name = "propertiesOmittedAtLeast")] + public int PropertiesOmittedAtLeast { get; set; } + + [DataMember(Name = "propertiesTruncationReason")] + public string PropertiesTruncationReason { get; set; } + + [DataMember(Name = "propertiesError")] + public string PropertiesError { get; set; } + } + + [DataContract] + public sealed class SerializedPropertyInspection + { + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "path")] + public string Path { get; set; } + + [DataMember(Name = "type")] + public string Type { get; set; } + + [DataMember(Name = "value")] + public object Value { get; set; } + + [DataMember(Name = "valueTruncated")] + public bool ValueTruncated { get; set; } + + [DataMember(Name = "valueTruncations")] + public List ValueTruncations { get; set; } = + new List(); + } + + [DataContract] + public sealed class SerializationTruncationInspection + { + [DataMember(Name = "path")] + public string Path { get; set; } + + [DataMember(Name = "reason")] + public string Reason { get; set; } + + [DataMember(Name = "limit")] + public int Limit { get; set; } + + [DataMember(Name = "originalCount")] + public int? OriginalCount { get; set; } + } + + [DataContract] + public sealed class UnloadSceneResult + { + [DataMember(Name = "unloadedPath")] + public string UnloadedPath { get; set; } + + [DataMember(Name = "activeSceneName")] + public string ActiveSceneName { get; set; } + + [DataMember(Name = "activeScenePath")] + public string ActiveScenePath { get; set; } + } + + [DataContract] + public sealed class EditorStepResult + { + [DataMember(Name = "isPlaying")] + public bool IsPlaying { get; set; } + + [DataMember(Name = "isPaused")] + public bool IsPaused { get; set; } + } + + [DataContract] + public sealed class AssignMaterialResult + { + [DataMember(Name = "gameObject")] + public AuthoringResult GameObject { get; set; } + + [DataMember(Name = "material")] + public AuthoringResult Material { get; set; } + + [DataMember(Name = "slot")] + public int Slot { get; set; } + } +} diff --git a/Editor/Tools/GetGameObjectTool.cs.meta b/Editor/Commands/CommandResults.cs.meta similarity index 83% rename from Editor/Tools/GetGameObjectTool.cs.meta rename to Editor/Commands/CommandResults.cs.meta index 3597710d..89e93b07 100644 --- a/Editor/Tools/GetGameObjectTool.cs.meta +++ b/Editor/Commands/CommandResults.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 405fbb4324c64fb0942faed640b82da2 +guid: 30dca5dbf19e4132a171136acdfd1d02 MonoImporter: externalObjects: {} serializedVersion: 2 @@ -9,4 +9,3 @@ MonoImporter: userData: assetBundleName: assetBundleVariant: - diff --git a/Editor/Commands/DuplicateGameObjectCommand.cs b/Editor/Commands/DuplicateGameObjectCommand.cs new file mode 100644 index 00000000..ae35ed31 --- /dev/null +++ b/Editor/Commands/DuplicateGameObjectCommand.cs @@ -0,0 +1,55 @@ +using System; +using Unity.Pipeline.Commands; +using Unity.Pipeline.Editor.Authoring; +using Unity.Pipeline.Models; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; +using Object = UnityEngine.Object; + +namespace McpUnity.Extensions.Commands +{ + public static class DuplicateGameObjectCommand + { + [CliCommand("duplicate_gameobject", "Duplicate a GameObject in its loaded scene with optional parenting and renaming.")] + public static AuthoringResult Duplicate( + [CliArg("source", "GameObject reference to duplicate.", Required = true)] ObjectRef source, + [CliArg("parent", "Optional GameObject parent for the duplicate.")] ObjectRef parent = null, + [CliArg("name", "Optional name for the duplicate.")] string name = null, + [CliArg("world_position_stays", "Preserve world transform when applying the optional parent.")] bool worldPositionStays = false) + { + var sourceObject = CommandObjectResolver.Resolve(source, "source"); + if (!sourceObject.scene.IsValid() || !sourceObject.scene.isLoaded) + throw new ArgumentException("'source' must be a GameObject in a loaded scene."); + + GameObject parentObject = null; + if (parent != null && !parent.IsEmpty) + { + parentObject = CommandObjectResolver.Resolve(parent, "parent"); + if (!parentObject.scene.IsValid() || !parentObject.scene.isLoaded) + throw new ArgumentException("'parent' must be a GameObject in a loaded scene."); + } + + var duplicate = Object.Instantiate(sourceObject); + duplicate.name = string.IsNullOrEmpty(name) ? sourceObject.name : name; + Undo.RegisterCreatedObjectUndo(duplicate, "Duplicate GameObject"); + + if (duplicate.scene != sourceObject.scene) + SceneManager.MoveGameObjectToScene(duplicate, sourceObject.scene); + + if (parentObject != null) + { + duplicate.transform.SetParent(parentObject.transform, worldPositionStays); + } + else if (sourceObject.transform.parent != null) + { + duplicate.transform.SetParent(sourceObject.transform.parent, true); + } + + EditorUtility.SetDirty(duplicate); + EditorSceneManager.MarkSceneDirty(duplicate.scene); + return ObjectResolver.Describe(duplicate); + } + } +} diff --git a/Editor/Resources/GetGameObjectResource.cs.meta b/Editor/Commands/DuplicateGameObjectCommand.cs.meta similarity index 60% rename from Editor/Resources/GetGameObjectResource.cs.meta rename to Editor/Commands/DuplicateGameObjectCommand.cs.meta index 03befb96..0e930fa9 100644 --- a/Editor/Resources/GetGameObjectResource.cs.meta +++ b/Editor/Commands/DuplicateGameObjectCommand.cs.meta @@ -1,11 +1,11 @@ fileFormatVersion: 2 -guid: 120d85b6d23e56448b1fa3b9afedd7ee +guid: b1ef3ef19dcc4cc581a47c43dbcac787 MonoImporter: externalObjects: {} serializedVersion: 2 defaultReferences: [] executionOrder: 0 icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/EditorStepCommand.cs b/Editor/Commands/EditorStepCommand.cs new file mode 100644 index 00000000..d796963b --- /dev/null +++ b/Editor/Commands/EditorStepCommand.cs @@ -0,0 +1,22 @@ +using System; +using Unity.Pipeline.Commands; + +namespace McpUnity.Extensions.Commands +{ + public static class EditorStepCommand + { + [CliCommand("editor_step", "Advance play mode by one frame.")] + public static EditorStepResult Step() + { + if (!UnityEditor.EditorApplication.isPlaying) + throw new InvalidOperationException("'editor_step' requires the editor to be in play mode."); + + UnityEditor.EditorApplication.Step(); + return new EditorStepResult + { + IsPlaying = UnityEditor.EditorApplication.isPlaying, + IsPaused = UnityEditor.EditorApplication.isPaused + }; + } + } +} diff --git a/Editor/Commands/EditorStepCommand.cs.meta b/Editor/Commands/EditorStepCommand.cs.meta new file mode 100644 index 00000000..da7efe26 --- /dev/null +++ b/Editor/Commands/EditorStepCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b71a60b9e0414cdba358997461747e57 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/InspectGameObjectCommand.cs b/Editor/Commands/InspectGameObjectCommand.cs new file mode 100644 index 00000000..c2aaa478 --- /dev/null +++ b/Editor/Commands/InspectGameObjectCommand.cs @@ -0,0 +1,573 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Text; +using Unity.Pipeline.Commands; +using Unity.Pipeline.Editor.Authoring; +using Unity.Pipeline.Models; +using UnityEditor; +using UnityEngine; + +namespace McpUnity.Extensions.Commands +{ + public static class InspectGameObjectCommand + { + internal const int MaxComponentsPerGameObject = 32; + internal const int MaxTotalComponents = 128; + internal const int PayloadBudgetBytes = 512 * 1024; + internal const int AggregateWorkBudget = 4096; + internal const int AggregateContentBudgetBytes = 384 * 1024; + private const int SerializedObjectReservationBytes = 256; + private const int SerializedIteratorReservationBytes = 128; + private const int SerializedPropertyEnvelopeBytes = + 1024 + + 2 + 256 * 6 + + 2 + 1024 * 6 + + 2 + 128 * 6; + + internal static Action PropertyReaderAllocationObserver { get; set; } + internal static Func SerializationOverride { get; set; } + + [CliCommand("inspect_gameobject", "Inspect a bounded GameObject hierarchy with optional component and serialized-property details.")] + public static InspectGameObjectResult Inspect( + [CliArg("target", "GameObject reference to inspect.", Required = true)] ObjectRef target, + [CliArg("max_depth", "Maximum child depth to include.")] int maxDepth = 2, + [CliArg("max_nodes", "Maximum number of GameObjects to include.")] int maxNodes = 200, + [CliArg("include_components", "Include component summaries.")] bool includeComponents = true, + [CliArg("include_properties", "Include serialized component properties.")] bool includeProperties = false, + [CliArg("max_properties_per_component", "Maximum serialized properties per component.")] int maxPropertiesPerComponent = 100) + { + var gameObject = CommandObjectResolver.Resolve(target, "target"); + var context = new InspectionContext( + Mathf.Clamp(maxDepth, 0, 8), + Mathf.Clamp(maxNodes, 1, 1000), + includeComponents, + includeProperties, + Mathf.Clamp(maxPropertiesPerComponent, 1, 200)); + + var root = BuildNode(gameObject, 0, context); + var result = new InspectGameObjectResult + { + Root = root, + MaxDepth = context.MaxDepth, + MaxNodes = context.MaxNodes, + MaxPropertiesPerComponent = context.MaxPropertiesPerComponent, + NodesReturned = context.NodesReturned, + NodeLimitReached = context.NodeLimitReached, + MaxComponentsPerGameObject = MaxComponentsPerGameObject, + MaxTotalComponents = MaxTotalComponents, + ComponentsReturned = context.ComponentsReturned, + ComponentLimitReached = context.ComponentLimitReached, + AggregateWorkBudget = context.Budget.WorkBudget, + AggregateWorkUsed = context.Budget.WorkUsed, + AggregateWorkLimitReached = context.Budget.WorkLimitReached, + AggregateConversionCount = context.Budget.ConversionCount, + AggregatePropertiesScanned = context.Budget.PropertiesScanned, + AggregateContentBudgetBytes = context.Budget.ContentBudgetBytes, + AggregateEstimatedContentBytes = context.Budget.EstimatedContentBytes, + AggregateContentLimitReached = context.Budget.ContentLimitReached, + ConversionTruncated = context.Budget.ConversionTruncated, + PayloadBudgetBytes = PayloadBudgetBytes, + PayloadTruncated = context.PayloadTruncated, + PayloadTruncationReason = context.PayloadTruncationReason + }; + StabilizePayloadBytes(result); + return result; + } + + private static GameObjectInspection BuildNode( + GameObject gameObject, + int depth, + InspectionContext context) + { + if (context.NodesReturned >= context.MaxNodes) + { + context.NodeLimitReached = true; + context.MarkPayloadTruncated("nodeLimit"); + return null; + } + + var identity = BoundedIdentity(ObjectResolver.Describe(gameObject), context); + var boundedName = BoundedString(gameObject.name, 256, context); + var boundedPath = BoundedString(identity?.HierarchyPath, 1024, context); + var boundedScenePath = BoundedString( + gameObject.scene.IsValid() ? gameObject.scene.path : null, + 1024, + context); + var boundedLayerName = BoundedString( + LayerMask.LayerToName(gameObject.layer), + 128, + context); + var boundedTag = BoundedString(gameObject.tag, 128, context); + if (!context.TryConsume( + 2048 + + IdentityBudget(identity) + + WorstCaseJsonStringBytes(boundedName) + + WorstCaseJsonStringBytes(boundedPath) + + WorstCaseJsonStringBytes(boundedScenePath) + + WorstCaseJsonStringBytes(boundedLayerName) + + WorstCaseJsonStringBytes(boundedTag))) + { + return null; + } + + context.NodesReturned++; + var transform = gameObject.transform; + var node = new GameObjectInspection + { + Identity = identity, + Name = boundedName, + Path = boundedPath, + ScenePath = boundedScenePath, + ActiveSelf = gameObject.activeSelf, + ActiveInHierarchy = gameObject.activeInHierarchy, + Layer = gameObject.layer, + LayerName = boundedLayerName, + Tag = boundedTag, + IsStatic = gameObject.isStatic, + Transform = new TransformInspection + { + LocalPosition = Vector(transform.localPosition), + LocalEulerAngles = Vector(transform.localEulerAngles), + LocalScale = Vector(transform.localScale), + WorldPosition = Vector(transform.position), + WorldEulerAngles = Vector(transform.eulerAngles) + }, + ChildCount = transform.childCount, + ComponentsIncluded = context.IncludeComponents + }; + + AddComponents(gameObject, node, context); + + if (depth >= context.MaxDepth) + { + node.ChildrenTruncated = transform.childCount > 0; + if (node.ChildrenTruncated) + context.MarkPayloadTruncated("depthLimit"); + return node; + } + + for (var index = 0; index < transform.childCount; index++) + { + if (context.NodesReturned >= context.MaxNodes) + { + context.NodeLimitReached = true; + context.MarkPayloadTruncated("nodeLimit"); + node.ChildrenTruncated = true; + break; + } + + var child = BuildNode(transform.GetChild(index).gameObject, depth + 1, context); + if (child == null) + { + node.ChildrenTruncated = true; + break; + } + + node.Children.Add(child); + } + + return node; + } + + private static void AddComponents( + GameObject gameObject, + GameObjectInspection node, + InspectionContext context) + { + var components = gameObject.GetComponents(); + node.ComponentCount = components.Length; + if (!context.IncludeComponents) + return; + + var perObjectLimit = Mathf.Min(components.Length, MaxComponentsPerGameObject); + for (var index = 0; index < perObjectLimit; index++) + { + if (context.ComponentsReturned >= MaxTotalComponents) + { + context.ComponentLimitReached = true; + node.ComponentsTruncated = true; + break; + } + + var component = components[index]; + if (component == null) + { + if (!context.TryConsume(256)) + { + node.ComponentsTruncated = true; + break; + } + node.Components.Add(new ComponentInspection + { + Type = "", + Missing = true + }); + context.ComponentsReturned++; + continue; + } + + var typeName = BoundedString(component.GetType().Name, 256, context); + var identity = BoundedIdentity(ObjectResolver.Describe(component), context); + if (!context.TryConsume( + 1024 + + WorstCaseJsonStringBytes(typeName) + + IdentityBudget(identity))) + { + node.ComponentsTruncated = true; + break; + } + var summary = new ComponentInspection + { + Identity = identity, + Type = typeName, + Enabled = GetEnabled(component), + PropertiesIncluded = context.IncludeProperties + }; + context.ComponentsReturned++; + + if (context.IncludeProperties && context.Budget.LimitReached) + MarkAggregatePropertyTruncation(summary, context); + else if (context.IncludeProperties) + ReadProperties(component, summary, context); + + node.Components.Add(summary); + } + + if (node.Components.Count < components.Length) + { + node.ComponentsTruncated = true; + node.ComponentsOmitted = components.Length - node.Components.Count; + context.MarkPayloadTruncated( + context.ComponentLimitReached + ? "totalComponentLimit" + : components.Length > MaxComponentsPerGameObject + ? "perGameObjectComponentLimit" + : "payloadBudget"); + } + } + + private static void ReadProperties( + Component component, + ComponentInspection summary, + InspectionContext context) + { + try + { + if (!TryReservePropertyReader( + "serializedObject", + SerializedObjectReservationBytes, + summary, + context)) + { + return; + } + var serializedObject = new SerializedObject(component); + if (!TryReservePropertyReader( + "iterator", + SerializedIteratorReservationBytes, + summary, + context)) + { + return; + } + var iterator = serializedObject.GetIterator(); + var enterChildren = true; + while (true) + { + if (!context.Budget.TryScanProperty()) + { + MarkAggregatePropertyTruncation(summary, context); + break; + } + if (!iterator.NextVisible(enterChildren)) + break; + + enterChildren = false; + if (iterator.propertyPath == "m_Script") + continue; + + if (!SerializedPropertyValueReader.CanRead(iterator)) + continue; + + summary.SerializedPropertyCount++; + if (summary.Properties.Count >= context.MaxPropertiesPerComponent) + { + summary.PropertiesTruncated = true; + summary.PropertiesOmittedAtLeast++; + summary.PropertiesTruncationReason = "perComponentPropertyLimit"; + context.MarkPayloadTruncated("perComponentPropertyLimit"); + break; + } + + if (!SerializedPropertyValueReader.TryRead( + iterator, + context.Budget, + out var readResult)) + { + if (context.Budget.LimitReached) + { + MarkAggregatePropertyTruncation(summary, context); + break; + } + continue; + } + + if (!TryReservePropertyOutput(readResult, context)) + { + MarkAggregatePropertyTruncation(summary, context); + break; + } + var property = new SerializedPropertyInspection + { + Name = BoundedString(iterator.displayName, 256, context), + Path = BoundedString(iterator.propertyPath, 1024, context), + Type = BoundedString(iterator.propertyType.ToString(), 128, context), + Value = readResult.Value, + ValueTruncated = readResult.Truncations.Count > 0, + ValueTruncations = readResult.Truncations + }; + if (property.ValueTruncated) + context.MarkPayloadTruncated("serializedValueBounds"); + + summary.Properties.Add(property); + summary.PropertiesReturned = summary.Properties.Count; + } + } + catch (Exception exception) + { + if (!context.Budget.TryReserve( + 1, + 256 + 2 + 1024 * 6)) + { + MarkAggregatePropertyTruncation(summary, context); + return; + } + var boundedError = BoundedString(exception.Message, 1024, context); + summary.PropertiesError = boundedError; + } + } + + private static bool TryReservePropertyReader( + string stage, + int estimatedContentBytes, + ComponentInspection summary, + InspectionContext context) + { + if (!context.Budget.TryReserve(1, estimatedContentBytes)) + { + MarkAggregatePropertyTruncation(summary, context); + return false; + } + PropertyReaderAllocationObserver?.Invoke(stage); + return true; + } + + private static bool TryReservePropertyOutput( + SerializedPropertyReadResult readResult, + InspectionContext context) + { + var totalEstimatedBytes = + SerializedPropertyEnvelopeBytes + + readResult.ReservedContentBytes + + readResult.Truncations.Count * 512; + var additionalBytes = + totalEstimatedBytes - readResult.ReservedContentBytes; + return context.Budget.TryReserve(1, additionalBytes); + } + + private static void MarkAggregatePropertyTruncation( + ComponentInspection summary, + InspectionContext context) + { + summary.PropertiesTruncated = true; + summary.PropertiesOmittedAtLeast++; + summary.PropertiesTruncationReason = context.Budget.LimitReason; + context.Budget.MarkConversionTruncated(); + context.MarkPayloadTruncated(context.Budget.LimitReason); + } + + internal static AuthoringResult BoundedIdentity( + AuthoringResult source, + InspectionContext context = null) + { + if (source == null) + return null; + return new AuthoringResult + { + GlobalId = BoundedString(source.GlobalId, 1024, context), + AssetPath = BoundedString(source.AssetPath, 1024, context), + Guid = BoundedString(source.Guid, 128, context), + FileId = source.FileId, + InstanceId = source.InstanceId, + HierarchyPath = BoundedString(source.HierarchyPath, 1024, context), + Type = BoundedString(source.Type, 128, context) + }; + } + + private static string BoundedString( + string value, + int maxLength, + InspectionContext context) + { + if (value == null || value.Length <= maxLength) + return value; + context?.MarkPayloadTruncated("stringLength"); + return value.Substring(0, maxLength); + } + + private static int IdentityBudget(AuthoringResult identity) + { + if (identity == null) + return 16; + return 256 + + WorstCaseJsonStringBytes(identity.GlobalId) + + WorstCaseJsonStringBytes(identity.AssetPath) + + WorstCaseJsonStringBytes(identity.Guid) + + WorstCaseJsonStringBytes(identity.HierarchyPath) + + WorstCaseJsonStringBytes(identity.Type); + } + + private static int WorstCaseJsonStringBytes(string value) => + value == null ? 4 : 2 + value.Length * 6; + + private static void StabilizePayloadBytes(InspectGameObjectResult result) + { + MeasureStablePayloadBytes(result); + if (result.PayloadBytes <= PayloadBudgetBytes) + return; + + // This guard is deliberately independent of the reservation estimator. + // Normal bounded inspections never reach it; if an estimator misses a + // value shape, return an honest minimal result instead of an oversized one. + result.Root = null; + result.NodesReturned = 0; + result.ComponentsReturned = 0; + result.PayloadTruncated = true; + result.PayloadTruncationReason = "serializedPayloadBudget"; + MeasureStablePayloadBytes(result); + if (result.PayloadBytes > PayloadBudgetBytes) + throw new InvalidOperationException( + "The bounded inspection result exceeded its serialized payload budget."); + } + + private static void MeasureStablePayloadBytes(InspectGameObjectResult result) + { + for (var attempt = 0; attempt < 6; attempt++) + { + var serialized = SerializeWithPipelineJson(result); + var bytes = Encoding.UTF8.GetByteCount(serialized); + if (result.PayloadBytes == bytes) + return; + result.PayloadBytes = bytes; + } + + throw new InvalidOperationException( + "The inspection payload size did not stabilize."); + } + + private static string SerializeWithPipelineJson(object value) + { + if (SerializationOverride != null) + { + return SerializationOverride(value) ?? + throw new InvalidOperationException( + "The inspection payload serializer returned no result."); + } + + var jsonType = AppDomain.CurrentDomain.GetAssemblies() + .Select(assembly => assembly.GetType("Newtonsoft.Json.JsonConvert", false)) + .FirstOrDefault(type => type != null); + if (jsonType == null) + { + throw new InvalidOperationException( + "The Pipeline JSON serializer is unavailable."); + } + var method = jsonType?.GetMethod( + "SerializeObject", + BindingFlags.Public | BindingFlags.Static, + null, + new[] { typeof(object) }, + null); + if (method == null) + { + throw new InvalidOperationException( + "The Pipeline JSON serializer entry point is unavailable."); + } + + try + { + return method.Invoke(null, new[] { value }) as string ?? + throw new InvalidOperationException( + "The Pipeline JSON serializer returned no result."); + } + catch (TargetInvocationException exception) + { + throw new InvalidOperationException( + "The Pipeline JSON serializer failed.", + exception.InnerException ?? exception); + } + } + + private static bool? GetEnabled(Component component) + { + if (component is Behaviour behaviour) + return behaviour.enabled; + if (component is Renderer renderer) + return renderer.enabled; + if (component is Collider collider) + return collider.enabled; + return null; + } + + private static Vector3Inspection Vector(Vector3 value) => + new Vector3Inspection { X = value.x, Y = value.y, Z = value.z }; + + internal sealed class InspectionContext + { + public InspectionContext( + int maxDepth, + int maxNodes, + bool includeComponents, + bool includeProperties, + int maxPropertiesPerComponent) + { + MaxDepth = maxDepth; + MaxNodes = maxNodes; + IncludeComponents = includeComponents; + IncludeProperties = includeProperties; + MaxPropertiesPerComponent = maxPropertiesPerComponent; + Budget = new InspectionBudget( + AggregateWorkBudget, + AggregateContentBudgetBytes); + } + + public int MaxDepth { get; } + public int MaxNodes { get; } + public bool IncludeComponents { get; } + public bool IncludeProperties { get; } + public int MaxPropertiesPerComponent { get; } + public InspectionBudget Budget { get; } + public int NodesReturned { get; set; } + public bool NodeLimitReached { get; set; } + public int ComponentsReturned { get; set; } + public bool ComponentLimitReached { get; set; } + public bool PayloadTruncated { get; private set; } + public string PayloadTruncationReason { get; private set; } + public bool TryConsume(int bytes) + { + if (Budget.TryReserve(0, bytes)) + return true; + MarkPayloadTruncated("payloadBudget"); + return false; + } + + public void MarkPayloadTruncated(string reason) + { + PayloadTruncated = true; + if (string.IsNullOrEmpty(PayloadTruncationReason)) + PayloadTruncationReason = reason; + } + } + } +} diff --git a/Editor/Commands/InspectGameObjectCommand.cs.meta b/Editor/Commands/InspectGameObjectCommand.cs.meta new file mode 100644 index 00000000..88970771 --- /dev/null +++ b/Editor/Commands/InspectGameObjectCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1fc54e949b7f4e5795930e35b9c749ac +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/SerializedPropertyValueReader.cs b/Editor/Commands/SerializedPropertyValueReader.cs new file mode 100644 index 00000000..eb59b667 --- /dev/null +++ b/Editor/Commands/SerializedPropertyValueReader.cs @@ -0,0 +1,591 @@ +using System; +using System.Collections.Generic; +using Unity.Pipeline.Editor.Authoring; +using UnityEditor; +using UnityEngine; + +namespace McpUnity.Extensions.Commands +{ + internal sealed class InspectionBudget + { + public InspectionBudget(int workBudget, int contentBudgetBytes) + { + WorkBudget = workBudget; + ContentBudgetBytes = contentBudgetBytes; + } + + public int WorkBudget { get; } + public int ContentBudgetBytes { get; } + public int WorkUsed { get; private set; } + public int EstimatedContentBytes { get; private set; } + public int ConversionCount { get; private set; } + public int PropertiesScanned { get; private set; } + private bool WorkReservationRejected { get; set; } + private bool ContentReservationRejected { get; set; } + public bool WorkLimitReached => + WorkUsed >= WorkBudget || WorkReservationRejected; + public bool ContentLimitReached => + EstimatedContentBytes >= ContentBudgetBytes || + ContentReservationRejected; + public bool ConversionTruncated { get; private set; } + public bool LimitReached => WorkLimitReached || ContentLimitReached; + public string LimitReason => + WorkLimitReached ? "aggregateWorkBudget" : + ContentLimitReached ? "aggregateContentBudget" : + "aggregateBudget"; + + public bool TryReserve( + int workUnits, + int estimatedContentBytes, + bool conversion = false, + bool propertyScan = false) + { + workUnits = Math.Max(0, workUnits); + estimatedContentBytes = Math.Max(0, estimatedContentBytes); + if ((workUnits > 0 || estimatedContentBytes > 0) && LimitReached) + { + if (conversion || propertyScan) + ConversionTruncated = true; + return false; + } + if (WorkUsed > WorkBudget - workUnits) + { + WorkReservationRejected = true; + if (conversion || propertyScan) + ConversionTruncated = true; + return false; + } + if (EstimatedContentBytes > ContentBudgetBytes - estimatedContentBytes) + { + ContentReservationRejected = true; + if (conversion || propertyScan) + ConversionTruncated = true; + return false; + } + + WorkUsed += workUnits; + EstimatedContentBytes += estimatedContentBytes; + if (conversion) + ConversionCount++; + if (propertyScan) + PropertiesScanned++; + return true; + } + + public bool TryScanProperty() => + TryReserve(1, 0, propertyScan: true); + + public void MarkConversionTruncated() + { + ConversionTruncated = true; + } + } + + internal static class SerializedPropertyValueReader + { + internal const int MaxStringLength = 4096; + internal const int MaxCollectionLength = 100; + internal const int MaxSerializationDepth = 4; + private const int MaxPropertyNameLength = 256; + + internal static Action ConversionObserver { get; set; } + + public static bool CanRead(SerializedProperty property) + { + if (property.isArray && property.propertyType != SerializedPropertyType.String) + return true; + + switch (property.propertyType) + { + case SerializedPropertyType.Boolean: + case SerializedPropertyType.Integer: + case SerializedPropertyType.Float: + case SerializedPropertyType.String: + case SerializedPropertyType.Enum: + case SerializedPropertyType.Vector2: + case SerializedPropertyType.Vector3: + case SerializedPropertyType.Vector4: + case SerializedPropertyType.Vector2Int: + case SerializedPropertyType.Vector3Int: + case SerializedPropertyType.Color: + case SerializedPropertyType.Rect: + case SerializedPropertyType.RectInt: + case SerializedPropertyType.Bounds: + case SerializedPropertyType.BoundsInt: + case SerializedPropertyType.Quaternion: + case SerializedPropertyType.Hash128: + case SerializedPropertyType.Generic: + case SerializedPropertyType.ObjectReference: + return true; + default: + return false; + } + } + + public static bool TryRead( + SerializedProperty property, + out SerializedPropertyReadResult result) => + TryRead( + property, + new InspectionBudget( + InspectGameObjectCommand.AggregateWorkBudget, + InspectGameObjectCommand.AggregateContentBudgetBytes), + out result); + + public static bool TryRead( + SerializedProperty property, + InspectionBudget budget, + out SerializedPropertyReadResult result) + { + result = null; + if (!CanRead(property)) + return false; + var initialWork = budget.WorkUsed; + var initialContent = budget.EstimatedContentBytes; + if (!budget.TryReserve(1, 128)) + { + budget.MarkConversionTruncated(); + return false; + } + + var truncations = new List(); + if (!TryReadValue(property, 0, budget, truncations, out var value)) + return false; + + result = new SerializedPropertyReadResult( + value, + truncations, + budget.WorkUsed - initialWork, + budget.EstimatedContentBytes - initialContent); + return true; + } + + private static bool TryReadValue( + SerializedProperty property, + int depth, + InspectionBudget budget, + List truncations, + out object value) + { + value = null; + if (!budget.TryReserve( + 1, + ConservativeValueReservationBytes(property), + conversion: true)) + { + return false; + } + + ConversionObserver?.Invoke(property.propertyPath); + if (property.isArray && property.propertyType != SerializedPropertyType.String) + return TryReadArray(property, depth, budget, truncations, out value); + + switch (property.propertyType) + { + case SerializedPropertyType.Boolean: + value = property.boolValue; + return true; + case SerializedPropertyType.Integer: + value = property.longValue; + return true; + case SerializedPropertyType.Float: + value = property.doubleValue; + return true; + case SerializedPropertyType.String: + var stringValue = property.stringValue ?? string.Empty; + if (stringValue.Length > MaxStringLength) + { + value = stringValue.Substring(0, MaxStringLength); + truncations.Add(Truncation( + property, + "stringLength", + MaxStringLength, + stringValue.Length)); + } + else + { + value = stringValue; + } + return true; + case SerializedPropertyType.Enum: + value = property.intValue; + return true; + case SerializedPropertyType.Vector2: + value = Values(property.vector2Value.x, property.vector2Value.y); + return true; + case SerializedPropertyType.Vector3: + value = Values( + property.vector3Value.x, + property.vector3Value.y, + property.vector3Value.z); + return true; + case SerializedPropertyType.Vector4: + value = Values( + property.vector4Value.x, + property.vector4Value.y, + property.vector4Value.z, + property.vector4Value.w); + return true; + case SerializedPropertyType.Vector2Int: + value = Values(property.vector2IntValue.x, property.vector2IntValue.y); + return true; + case SerializedPropertyType.Vector3Int: + value = Values( + property.vector3IntValue.x, + property.vector3IntValue.y, + property.vector3IntValue.z); + return true; + case SerializedPropertyType.Color: + var color = property.colorValue; + value = Values(color.r, color.g, color.b, color.a); + return true; + case SerializedPropertyType.Rect: + var rect = property.rectValue; + value = NamedValues( + ("x", rect.x), + ("y", rect.y), + ("width", rect.width), + ("height", rect.height)); + return true; + case SerializedPropertyType.RectInt: + var rectInt = property.rectIntValue; + value = NamedValues( + ("x", rectInt.x), + ("y", rectInt.y), + ("width", rectInt.width), + ("height", rectInt.height)); + return true; + case SerializedPropertyType.Bounds: + var bounds = property.boundsValue; + value = new Dictionary + { + ["center"] = Values(bounds.center.x, bounds.center.y, bounds.center.z), + ["size"] = Values(bounds.size.x, bounds.size.y, bounds.size.z) + }; + return true; + case SerializedPropertyType.BoundsInt: + var boundsInt = property.boundsIntValue; + value = new Dictionary + { + ["position"] = Values( + boundsInt.position.x, + boundsInt.position.y, + boundsInt.position.z), + ["size"] = Values(boundsInt.size.x, boundsInt.size.y, boundsInt.size.z) + }; + return true; + case SerializedPropertyType.Quaternion: + var quaternion = property.quaternionValue; + value = Values(quaternion.x, quaternion.y, quaternion.z, quaternion.w); + return true; + case SerializedPropertyType.Hash128: + value = property.hash128Value.ToString(); + return true; + case SerializedPropertyType.ObjectReference: + var referenced = property.objectReferenceValue; + if (referenced is MonoScript) + return false; + value = referenced == null + ? null + : InspectGameObjectCommand.BoundedIdentity( + ObjectResolver.Describe(referenced)); + return true; + case SerializedPropertyType.Generic: + return TryReadObject( + property, + depth, + budget, + truncations, + out value); + default: + return false; + } + } + + private static bool TryReadArray( + SerializedProperty property, + int depth, + InspectionBudget budget, + List truncations, + out object value) + { + if (depth >= MaxSerializationDepth) + { + value = null; + truncations.Add(Truncation( + property, + "serializationDepth", + MaxSerializationDepth, + null)); + return true; + } + + if (!budget.TryReserve(1, 64)) + { + budget.MarkConversionTruncated(); + value = null; + AddAggregateTruncation(property, budget, truncations); + return true; + } + var originalCount = property.arraySize; + var count = Mathf.Min(originalCount, MaxCollectionLength); + if (!budget.TryReserve(1, count * 8)) + { + budget.MarkConversionTruncated(); + value = null; + AddAggregateTruncation(property, budget, truncations); + return true; + } + + var values = new List(count); + for (var index = 0; index < count; index++) + { + if (!budget.TryReserve(1, 16)) + { + budget.MarkConversionTruncated(); + AddAggregateTruncation(property, budget, truncations); + break; + } + var element = property.GetArrayElementAtIndex(index); + if (!TryReadValue( + element, + depth + 1, + budget, + truncations, + out var elementValue)) + { + if (budget.LimitReached) + { + budget.MarkConversionTruncated(); + AddAggregateTruncation(property, budget, truncations); + break; + } + values.Add(null); + continue; + } + values.Add(elementValue); + } + + if (originalCount > MaxCollectionLength) + { + truncations.Add(Truncation( + property, + "collectionLength", + MaxCollectionLength, + originalCount)); + } + + value = values; + return true; + } + + private static bool TryReadObject( + SerializedProperty property, + int depth, + InspectionBudget budget, + List truncations, + out object value) + { + if (depth >= MaxSerializationDepth) + { + value = null; + truncations.Add(Truncation( + property, + "serializationDepth", + MaxSerializationDepth, + null)); + return true; + } + + if (!budget.TryReserve(1, 128)) + { + budget.MarkConversionTruncated(); + value = null; + AddAggregateTruncation(property, budget, truncations); + return true; + } + var values = new Dictionary(); + var iterator = property.Copy(); + var end = iterator.GetEndProperty(); + var enterChildren = true; + var supportedCount = 0; + while (true) + { + if (!budget.TryScanProperty()) + { + budget.MarkConversionTruncated(); + AddAggregateTruncation(property, budget, truncations); + break; + } + if (!iterator.NextVisible(enterChildren) || + SerializedProperty.EqualContents(iterator, end)) + { + break; + } + + enterChildren = false; + if (!CanRead(iterator)) + continue; + + if (supportedCount >= MaxCollectionLength) + { + truncations.Add(Truncation( + property, + "collectionLength", + MaxCollectionLength, + null)); + break; + } + + if (!budget.TryReserve(1, 64)) + { + budget.MarkConversionTruncated(); + AddAggregateTruncation(property, budget, truncations); + break; + } + var rawKey = iterator.name ?? string.Empty; + var key = rawKey.Length > MaxPropertyNameLength + ? rawKey.Substring(0, MaxPropertyNameLength) + : rawKey; + if (!budget.TryReserve(0, WorstCaseJsonStringBytes(key))) + { + budget.MarkConversionTruncated(); + AddAggregateTruncation(property, budget, truncations); + break; + } + if (rawKey.Length > MaxPropertyNameLength) + { + truncations.Add(Truncation( + iterator, + "propertyNameLength", + MaxPropertyNameLength, + rawKey.Length)); + } + + supportedCount++; + if (!TryReadValue( + iterator, + depth + 1, + budget, + truncations, + out var childValue)) + { + if (budget.LimitReached) + { + budget.MarkConversionTruncated(); + AddAggregateTruncation(property, budget, truncations); + break; + } + continue; + } + values[key] = childValue; + } + + value = values; + return true; + } + + private static int ConservativeValueReservationBytes( + SerializedProperty property) + { + if (property.isArray && property.propertyType != SerializedPropertyType.String) + return 32; + switch (property.propertyType) + { + case SerializedPropertyType.String: + return WorstCaseJsonStringBytes(MaxStringLength); + case SerializedPropertyType.Rect: + case SerializedPropertyType.RectInt: + case SerializedPropertyType.Bounds: + case SerializedPropertyType.BoundsInt: + return 256; + case SerializedPropertyType.Vector2: + case SerializedPropertyType.Vector3: + case SerializedPropertyType.Vector4: + case SerializedPropertyType.Vector2Int: + case SerializedPropertyType.Vector3Int: + case SerializedPropertyType.Color: + case SerializedPropertyType.Quaternion: + return 128; + case SerializedPropertyType.ObjectReference: + return 8 * 1024; + case SerializedPropertyType.Enum: + return 64; + case SerializedPropertyType.Hash128: + return 512; + case SerializedPropertyType.Generic: + return 32; + default: + return 64; + } + } + + private static int WorstCaseJsonStringBytes(string value) => + value == null ? 4 : 2 + value.Length * 6; + + private static int WorstCaseJsonStringBytes(int characterCount) => + 2 + characterCount * 6; + + private static void AddAggregateTruncation( + SerializedProperty property, + InspectionBudget budget, + List truncations) + { + if (truncations.Exists(marker => marker.Reason == budget.LimitReason)) + return; + truncations.Add(Truncation( + property, + budget.LimitReason, + budget.WorkLimitReached + ? budget.WorkBudget + : budget.ContentBudgetBytes, + null)); + } + + private static SerializationTruncationInspection Truncation( + SerializedProperty property, + string reason, + int limit, + int? originalCount) => + new SerializationTruncationInspection + { + Path = property.propertyPath?.Length > 1024 + ? property.propertyPath.Substring(0, 1024) + : property.propertyPath, + Reason = reason, + Limit = limit, + OriginalCount = originalCount + }; + + private static object[] Values(params object[] values) => values; + + private static Dictionary NamedValues( + params (string Name, object Value)[] values) + { + var result = new Dictionary(); + foreach (var value in values) + result[value.Name] = value.Value; + return result; + } + } + + internal sealed class SerializedPropertyReadResult + { + public SerializedPropertyReadResult( + object value, + List truncations, + int reservedWorkUnits, + int reservedContentBytes) + { + Value = value; + Truncations = truncations; + ReservedWorkUnits = reservedWorkUnits; + ReservedContentBytes = reservedContentBytes; + } + + public object Value { get; } + public List Truncations { get; } + public int ReservedWorkUnits { get; } + public int ReservedContentBytes { get; } + } +} diff --git a/Editor/Commands/SerializedPropertyValueReader.cs.meta b/Editor/Commands/SerializedPropertyValueReader.cs.meta new file mode 100644 index 00000000..79f90b45 --- /dev/null +++ b/Editor/Commands/SerializedPropertyValueReader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 301181933041459aa6170004a140b7d3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Commands/UnloadSceneCommand.cs b/Editor/Commands/UnloadSceneCommand.cs new file mode 100644 index 00000000..f5d184b7 --- /dev/null +++ b/Editor/Commands/UnloadSceneCommand.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Unity.Pipeline.Commands; +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; + +namespace McpUnity.Extensions.Commands +{ + public static class UnloadSceneCommand + { + [CliCommand("unload_scene", "Unload an already-loaded scene while protecting dirty and active scene state.")] + public static UnloadSceneResult Unload( + [CliArg("path", "Path of the already-loaded scene to unload.", Required = true)] string path, + [CliArg("force", "Discard unsaved scene changes when true.")] bool force = false) + { + if (string.IsNullOrWhiteSpace(path)) + throw new ArgumentException("'path' is required."); + + var normalizedPath = path.Trim().Replace('\\', '/'); + var loadedScenes = GetLoadedScenes(); + var target = loadedScenes.FirstOrDefault(scene => + string.Equals(scene.path, normalizedPath, StringComparison.OrdinalIgnoreCase)); + + if (!target.IsValid() || !target.isLoaded) + throw new InvalidOperationException($"Scene '{normalizedPath}' is not loaded."); + + if (target.isDirty && !force) + { + throw new InvalidOperationException( + $"Scene '{target.path}' has unsaved changes. Pass force=true to discard them."); + } + + var active = SceneManager.GetActiveScene(); + if (target.handle == active.handle && loadedScenes.Count == 1) + { + throw new InvalidOperationException( + $"Cannot unload '{target.path}' because it is the sole loaded active scene."); + } + + if (target.handle == active.handle) + { + var alternative = loadedScenes + .Where(scene => scene.handle != target.handle) + .OrderBy(scene => scene.path, StringComparer.Ordinal) + .ThenBy(scene => scene.name, StringComparer.Ordinal) + .ThenBy(scene => scene.handle) + .First(); + + if (!SceneManager.SetActiveScene(alternative)) + throw new InvalidOperationException( + $"Could not make '{alternative.path}' active before unloading '{target.path}'."); + } + + if (!EditorSceneManager.CloseScene(target, true)) + { + if (target.IsValid() && target.isLoaded) + SceneManager.SetActiveScene(target); + throw new InvalidOperationException($"Failed to unload scene '{normalizedPath}'."); + } + + var remainingActive = SceneManager.GetActiveScene(); + return new UnloadSceneResult + { + UnloadedPath = normalizedPath, + ActiveSceneName = remainingActive.name, + ActiveScenePath = remainingActive.path + }; + } + + private static List GetLoadedScenes() + { + var scenes = new List(); + for (var index = 0; index < SceneManager.sceneCount; index++) + { + var scene = SceneManager.GetSceneAt(index); + if (scene.isLoaded) + scenes.Add(scene); + } + + return scenes; + } + } +} diff --git a/Editor/Commands/UnloadSceneCommand.cs.meta b/Editor/Commands/UnloadSceneCommand.cs.meta new file mode 100644 index 00000000..22b8e925 --- /dev/null +++ b/Editor/Commands/UnloadSceneCommand.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fcbd971b228d4107bdaf3a1becacc64f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Lib.meta b/Editor/Lib.meta deleted file mode 100644 index 94188fb4..00000000 --- a/Editor/Lib.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: 05cb8bd640a00d942af19e6ce940a2d7 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Lib/websocket-sharp.dll b/Editor/Lib/websocket-sharp.dll deleted file mode 100644 index 6ec719f7..00000000 Binary files a/Editor/Lib/websocket-sharp.dll and /dev/null differ diff --git a/Editor/Lib/websocket-sharp.dll.meta b/Editor/Lib/websocket-sharp.dll.meta deleted file mode 100644 index 8a56b91a..00000000 --- a/Editor/Lib/websocket-sharp.dll.meta +++ /dev/null @@ -1,33 +0,0 @@ -fileFormatVersion: 2 -guid: c8939e1023ebcf048bb5f16cdbb51310 -PluginImporter: - externalObjects: {} - serializedVersion: 2 - iconMap: {} - executionOrder: {} - defineConstraints: [] - isPreloaded: 0 - isOverridable: 1 - isExplicitlyReferenced: 0 - validateReferences: 1 - platformData: - - first: - Any: - second: - enabled: 0 - settings: {} - - first: - Editor: Editor - second: - enabled: 1 - settings: - DefaultValueInitialized: true - - first: - Windows Store Apps: WindowsStoreApps - second: - enabled: 0 - settings: - CPU: AnyCPU - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/McpUnity.Editor.asmdef b/Editor/McpUnity.Editor.asmdef index 2f4156cb..c0521524 100644 --- a/Editor/McpUnity.Editor.asmdef +++ b/Editor/McpUnity.Editor.asmdef @@ -1,8 +1,9 @@ { - "name": "McpUnity.Editor", - "rootNamespace": "", + "name": "McpUnity.Extensions", + "rootNamespace": "McpUnity.Extensions", "references": [ - "GUID:478a2357cc57436488a56e564b08d223" + "Unity.Pipeline", + "Unity.Pipeline.Editor" ], "includePlatforms": [ "Editor" @@ -13,12 +14,6 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [ - { - "name": "Unity", - "expression": "6000.3", - "define": "MCP_UNITY_ENTITY_ID_API" - } - ], + "versionDefines": [], "noEngineReferences": false } diff --git a/Editor/Models.meta b/Editor/Models.meta deleted file mode 100644 index 14b7d099..00000000 --- a/Editor/Models.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: e5573ac08a5d13d45a7c8b26d6052563 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Models/UpdateGameObjectRequest.cs b/Editor/Models/UpdateGameObjectRequest.cs deleted file mode 100644 index ebc71d2d..00000000 --- a/Editor/Models/UpdateGameObjectRequest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using Newtonsoft.Json; - -namespace McpUnity.Models -{ - [Serializable] - public class UpdateGameObjectRequest - { - [JsonProperty("instanceId")] - public int? InstanceId { get; set; } - - [JsonProperty("objectPath")] - public string ObjectPath { get; set; } - - [JsonProperty("name")] - public string Name { get; set; } - - [JsonProperty("tag")] - public string Tag { get; set; } - - [JsonProperty("layer")] - public int? Layer { get; set; } - - [JsonProperty("isActiveSelf")] - public bool? IsActiveSelf { get; set; } - - [JsonProperty("isStatic")] - public bool? IsStatic { get; set; } - } -} diff --git a/Editor/Models/UpdateGameObjectRequest.cs.meta b/Editor/Models/UpdateGameObjectRequest.cs.meta deleted file mode 100644 index b9949bbd..00000000 --- a/Editor/Models/UpdateGameObjectRequest.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2eb0c35b41a79274faeabeac50452871 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources.meta b/Editor/Resources.meta deleted file mode 100644 index 55f77af4..00000000 --- a/Editor/Resources.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: fbd154b4035adf24eaaf8011cfae5db8 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources/GetAssetsResource.cs b/Editor/Resources/GetAssetsResource.cs deleted file mode 100644 index 4aa9254c..00000000 --- a/Editor/Resources/GetAssetsResource.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; -using UnityEngine; -using UnityEditor; -using McpUnity.Unity; -using Newtonsoft.Json.Linq; - -namespace McpUnity.Resources -{ - /// - /// Resource for getting asset information from the Unity Asset Database - /// - public class GetAssetsResource : McpResourceBase - { - public GetAssetsResource() - { - Name = "get_assets"; - Description = "Retrieves assets from the Unity Asset Database"; - Uri = "unity://assets"; - } - - /// - /// Execute the resource to get asset information - /// - /// Optional parameters for filtering - /// JObject containing asset information - public override JObject Fetch(JObject parameters) - { - // Extract optional filter parameters - string assetType = parameters?["assetType"]?.ToObject(); - string searchPattern = parameters?["searchPattern"]?.ToObject(); - - // Get all assets from the project - JArray assets = GetAllAssets(assetType, searchPattern); - - // Return result - return new JObject - { - ["success"] = true, - ["message"] = $"Retrieved {assets.Count} assets", - ["assets"] = assets - }; - } - - /// - /// Get all assets from the project, optionally filtered by type and search pattern - /// - /// Optional filter by asset type - /// Optional search pattern for asset names - /// JArray containing asset information - private JArray GetAllAssets(string assetType, string searchPattern) - { - JArray result = new JArray(); - - // Find all assets - string[] assetGuids = AssetDatabase.FindAssets(string.IsNullOrEmpty(searchPattern) ? "" : searchPattern); - - foreach (string guid in assetGuids) - { - string assetPath = AssetDatabase.GUIDToAssetPath(guid); - - // Skip folders - if (AssetDatabase.IsValidFolder(assetPath)) - { - continue; - } - - // Get asset type - var asset = AssetDatabase.LoadAssetAtPath(assetPath); - if (asset == null) - { - continue; - } - - string fileType = asset.GetType().Name; - - // Filter by asset type if specified - if (!string.IsNullOrEmpty(assetType) && !fileType.Equals(assetType, StringComparison.OrdinalIgnoreCase)) - { - continue; - } - - // Create asset information - JObject assetInfo = new JObject - { - ["name"] = Path.GetFileNameWithoutExtension(assetPath), - ["filename"] = Path.GetFileName(assetPath), - ["path"] = assetPath, - ["type"] = fileType, - ["extension"] = Path.GetExtension(assetPath).TrimStart('.'), - ["guid"] = guid, - ["size"] = GetAssetSize(assetPath) - }; - - result.Add(assetInfo); - } - - return result; - } - - /// - /// Get the size of an asset file - /// - /// Path to the asset - /// Size in bytes, or -1 if the file cannot be found - private long GetAssetSize(string assetPath) - { - string fullPath = Path.Combine(Application.dataPath, "..", assetPath); - FileInfo fileInfo = new FileInfo(fullPath); - return fileInfo.Exists ? fileInfo.Length : -1; - } - } -} diff --git a/Editor/Resources/GetAssetsResource.cs.meta b/Editor/Resources/GetAssetsResource.cs.meta deleted file mode 100644 index f1ba1f64..00000000 --- a/Editor/Resources/GetAssetsResource.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: a4e564d8f8bab33449d5207e1df951f2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources/GetConsoleLogsResource.cs b/Editor/Resources/GetConsoleLogsResource.cs deleted file mode 100644 index 0c7c71fd..00000000 --- a/Editor/Resources/GetConsoleLogsResource.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using Newtonsoft.Json.Linq; -using McpUnity.Services; - -namespace McpUnity.Resources -{ - /// - /// Resource for retrieving all logs from the Unity console - /// - public class GetConsoleLogsResource : McpResourceBase - { - private readonly IConsoleLogsService _consoleLogsService; - - public GetConsoleLogsResource(IConsoleLogsService consoleLogsService) - { - Name = "get_console_logs"; - Description = "Retrieves logs from the Unity console (newest first), optionally filtered by type (error, warning, info). Use pagination parameters (offset, limit) to avoid LLM token limits. Set includeStackTrace=false to exclude stack traces and reduce token usage. Recommended: limit=20-50 for optimal performance."; - Uri = "unity://logs/{logType}"; - - _consoleLogsService = consoleLogsService; - } - - /// - /// Fetch logs from the Unity console, optionally filtered by type with pagination support - /// - /// Resource parameters as a JObject (may include 'logType', 'offset', 'limit') - /// A JObject containing the list of logs with pagination info - public override JObject Fetch(JObject parameters) - { - string logType = parameters?["logType"]?.ToString(); - if (string.IsNullOrWhiteSpace(logType)) logType = null; - - int offset = Math.Max(0, GetIntParameter(parameters, "offset", 0)); - int limit = Math.Max(1, Math.Min(1000, GetIntParameter(parameters, "limit", 100))); - bool includeStackTrace = GetBoolParameter(parameters, "includeStackTrace", true); - - // Debug logging - temporarily remove to avoid console clutter - - // Use the new paginated method with stack trace option - JObject result = _consoleLogsService.GetLogsAsJson(logType, offset, limit, includeStackTrace); - - // Add formatted message with pagination info - string typeFilter = logType != null ? $" of type '{logType}'" : ""; - int returnedCount = result["_returnedCount"]?.Value() ?? 0; - int filteredCount = result["_filteredCount"]?.Value() ?? 0; - int totalCount = result["_totalCount"]?.Value() ?? 0; - - result["message"] = $"Retrieved {returnedCount} of {filteredCount} log entries{typeFilter} (offset: {offset}, limit: {limit}, includeStackTrace: {includeStackTrace}, total: {totalCount})"; - result["success"] = true; - - // Remove internal count fields (they're now in the message) - result.Remove("_totalCount"); - result.Remove("_filteredCount"); - result.Remove("_returnedCount"); - - return result; - } - - /// - /// Helper method to safely extract integer parameters with default values - /// - /// JObject containing parameters - /// Parameter key to extract - /// Default value if parameter is missing or invalid - /// Extracted integer value or default - private static int GetIntParameter(JObject parameters, string key, int defaultValue) - { - if (parameters?[key] != null && int.TryParse(parameters[key].ToString(), out int value)) - return value; - return defaultValue; - } - - /// - /// Helper method to safely extract boolean parameters with default values - /// - /// JObject containing parameters - /// Parameter key to extract - /// Default value if parameter is missing or invalid - /// Extracted boolean value or default - private static bool GetBoolParameter(JObject parameters, string key, bool defaultValue) - { - if (parameters?[key] != null && bool.TryParse(parameters[key].ToString(), out bool value)) - return value; - return defaultValue; - } - - - } -} diff --git a/Editor/Resources/GetConsoleLogsResource.cs.meta b/Editor/Resources/GetConsoleLogsResource.cs.meta deleted file mode 100644 index 4baeb7ff..00000000 --- a/Editor/Resources/GetConsoleLogsResource.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 5527fc71c2a481f428816932a15a9a2a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources/GetGameObjectResource.cs b/Editor/Resources/GetGameObjectResource.cs deleted file mode 100644 index f5278b5f..00000000 --- a/Editor/Resources/GetGameObjectResource.cs +++ /dev/null @@ -1,605 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using UnityEngine; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using UnityEditor; -using McpUnity.Utils; - -namespace McpUnity.Resources -{ - /// - /// Resource for retrieving detailed information about a specific GameObject - /// - public class GetGameObjectResource : McpResourceBase - { - public GetGameObjectResource() - { - Name = "get_gameobject"; - Description = "Retrieves detailed information about a specific GameObject by instance ID or object name or path"; - Uri = "unity://gameobject/{idOrName}"; - } - - /// - /// Default maximum hierarchy depth when serializing children. Prevents oversized responses that - /// would otherwise exceed the MCP framework's response size limit and drop the WebSocket - /// connection. See: https://github.com/CoderGamester/mcp-unity/issues/134 - /// - public const int DefaultMaxChildDepth = 2; - - /// - /// Hard cap on serialized response size, in bytes. Once recursion accumulates this much JSON - /// the remaining children are replaced with a truncation marker so the caller can drill in - /// instead of losing the connection. - /// - public const int MaxResponseBytes = 5 * 1024 * 1024; - - /// - /// Fetch information about a specific GameObject - /// - /// Resource parameters as a JObject. Should include 'objectPathId' which can be either an instance ID or a path - /// A JObject containing the GameObject data - public override JObject Fetch(JObject parameters) - { - // Validate parameters - if (parameters == null || !parameters.ContainsKey("idOrName")) - { - return new JObject - { - ["success"] = false, - ["message"] = "Missing required parameter: idOrName" - }; - } - - string idOrName = parameters["idOrName"]?.ToObject(); - - if (string.IsNullOrEmpty(idOrName)) - { - return new JObject - { - ["success"] = false, - ["message"] = "Parameter 'objectPathId' cannot be null or empty" - }; - } - - GameObject gameObject = null; - - // Try to parse as an instance ID first - if (int.TryParse(idOrName, out int instanceId)) - { - // Unity Instance IDs are typically negative, but we'll accept any integer - UnityEngine.Object unityObject = UnityObjectId.ObjectFromId(instanceId); - gameObject = unityObject as GameObject; - } - else - { - // Otherwise, treat it as a name or hierarchical path - gameObject = GameObject.Find(idOrName); - } - - // Check if the GameObject was found - if (gameObject == null) - { - return new JObject - { - ["success"] = false, - ["message"] = $"GameObject with '{idOrName}' reference not found. Make sure the GameObject exists and is loaded in the current scene(s)." - }; - } - - int maxDepth = parameters["maxDepth"]?.ToObject() ?? DefaultMaxChildDepth; - bool includeComponents = parameters["includeComponents"]?.ToObject() ?? true; - bool includeComponentProperties = parameters["includeComponentProperties"]?.ToObject() ?? true; - - // Convert the GameObject to a JObject - JObject gameObjectData = GameObjectToJObject( - gameObject, true, maxDepth, includeComponents, includeComponentProperties); - - // Create the response - return new JObject - { - ["success"] = true, - ["message"] = $"Retrieved GameObject data for '{gameObject.name}'", - ["gameObject"] = gameObjectData, - ["instanceId"] = UnityObjectId.GetObjectId(gameObject) - }; - } - - /// - /// Convert a GameObject to a JObject with its hierarchy, scoped by depth, component toggles, - /// and a hard byte cap. The byte cap guards against responses exceeding the MCP framework's - /// 15 MB ceiling, which would otherwise drop the WebSocket connection (issue #134). - /// - /// The GameObject to convert - /// Whether to include detailed component information - /// Maximum child levels to recurse. 0 = no children. Default: - /// When false, the components array is omitted entirely - /// When false, components are listed without their reflected properties - /// A JObject representing the GameObject - public static JObject GameObjectToJObject( - GameObject gameObject, - bool includeDetailedComponents, - int maxDepth = DefaultMaxChildDepth, - bool includeComponents = true, - bool includeComponentProperties = true) - { - int[] bytesUsed = { 0 }; - return GameObjectToJObjectInternal( - gameObject, - includeDetailedComponents, - maxDepth, - includeComponents, - includeComponentProperties, - currentDepth: 0, - bytesUsed); - } - - private static JObject GameObjectToJObjectInternal( - GameObject gameObject, - bool includeDetailedComponents, - int maxDepth, - bool includeComponents, - bool includeComponentProperties, - int currentDepth, - int[] bytesUsed) - { - if (gameObject == null) return null; - - JObject gameObjectJson = new JObject - { - ["name"] = gameObject.name, - ["activeSelf"] = gameObject.activeSelf, - ["activeInHierarchy"] = gameObject.activeInHierarchy, - ["tag"] = gameObject.tag, - ["layer"] = gameObject.layer, - ["layerName"] = LayerMask.LayerToName(gameObject.layer), - ["instanceId"] = UnityObjectId.GetObjectId(gameObject) - }; - - if (includeComponents) - { - gameObjectJson["components"] = GetComponentsInfo( - gameObject, includeDetailedComponents, includeComponentProperties); - } - - int childCount = gameObject.transform.childCount; - JArray childrenArray = new JArray(); - gameObjectJson["children"] = childrenArray; - - // Account for this node's own JSON before deciding whether to recurse further. - bytesUsed[0] += gameObjectJson.ToString(Formatting.None).Length; - - if (childCount > 0 && currentDepth >= maxDepth) - { - gameObjectJson["_truncated"] = true; - gameObjectJson["_truncatedReason"] = "depth_limit"; - gameObjectJson["_childCount"] = childCount; - } - else if (childCount > 0 && bytesUsed[0] > MaxResponseBytes) - { - gameObjectJson["_truncated"] = true; - gameObjectJson["_truncatedReason"] = "size_limit_exceeded"; - gameObjectJson["_childCount"] = childCount; - gameObjectJson["_hint"] = "Response exceeded 5MB cap. Re-query specific children with maxDepth=0 or includeComponentProperties=false."; - } - else - { - foreach (Transform child in gameObject.transform) - { - if (bytesUsed[0] > MaxResponseBytes) - { - gameObjectJson["_truncated"] = true; - gameObjectJson["_truncatedReason"] = "size_limit_exceeded"; - gameObjectJson["_childCount"] = childCount; - gameObjectJson["_serializedChildCount"] = childrenArray.Count; - gameObjectJson["_hint"] = "Response exceeded 5MB cap. Re-query specific children with maxDepth=0 or includeComponentProperties=false."; - break; - } - - childrenArray.Add(GameObjectToJObjectInternal( - child.gameObject, - includeDetailedComponents, - maxDepth, - includeComponents, - includeComponentProperties, - currentDepth + 1, - bytesUsed)); - } - } - - return gameObjectJson; - } - - /// - /// Namespace prefixes for components with native/C++ code that crash when accessed via reflection. - /// These components will only have basic info (type, enabled) serialized, not detailed properties. - /// - private static readonly string[] UnsafeNamespacePrefixes = new string[] - { - "Pathfinding", // A* Pathfinding Project - "FMOD", // FMOD audio - "FMODUnity", // FMOD Unity integration - }; - - /// - /// Component base types that should never have public properties reflected because some native-backed getters - /// can crash the Unity editor before a managed exception is thrown. - /// - private static readonly Type[] UnsafeDetailedInspectionBaseTypes = new Type[] - { - typeof(Collider) - }; - - /// - /// Common expensive or unsafe properties that should be skipped for all component types. - /// - private static readonly HashSet GloballySkippedPropertyNames = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "mesh", - "sharedMesh", - "material", - "materials", - "sharedMaterial", - "sharedMaterials", - "sprite", - "mainTexture", - "mainTextureOffset", - "mainTextureScale" - }; - - /// - /// Per-component property denylist for getters known to be unsafe via reflection. - /// Keys are matched against the declaring component type and its base types. - /// - private static readonly Dictionary> SkippedPropertiesByComponentType = new Dictionary> - { - [typeof(Collider)] = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "GeometryHolder" - } - }; - - /// - /// Check if a component type is from a native plugin that may crash when accessed via reflection - /// - private static bool IsUnsafeNativeComponent(Type componentType) - { - if (componentType == null) return true; - - string fullName = componentType.FullName ?? ""; - string namespaceName = componentType.Namespace ?? ""; - - foreach (string prefix in UnsafeNamespacePrefixes) - { - if (namespaceName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase) || - fullName.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - } - - return false; - } - - /// - /// Check if a component type is unsafe to inspect in detail. - /// - private static bool ShouldSkipDetailedInspection(Type componentType) - { - if (IsUnsafeNativeComponent(componentType)) - { - return true; - } - - foreach (Type unsafeBaseType in UnsafeDetailedInspectionBaseTypes) - { - if (unsafeBaseType.IsAssignableFrom(componentType)) - { - return true; - } - } - - return false; - } - - /// - /// Get information about the components attached to a GameObject - /// - /// The GameObject to get components from - /// Whether to include detailed component information - /// When false, the per-component 'properties' object is omitted entirely. - /// Useful for callers that only need component type names, saving substantial response size. - /// A JArray containing component information - private static JArray GetComponentsInfo( - GameObject gameObject, - bool includeDetailedInfo = false, - bool includeProperties = true) - { - Component[] components = gameObject.GetComponents(); - JArray componentsArray = new JArray(); - - foreach (Component component in components) - { - if (component == null) continue; - - Type componentType = component.GetType(); - bool skipDetailedInspection = ShouldSkipDetailedInspection(componentType); - - JObject componentJson = new JObject - { - ["type"] = componentType.Name, - ["enabled"] = IsComponentEnabled(component) - }; - - // Add detailed information if requested and component is safe to inspect - if (includeDetailedInfo && includeProperties) - { - if (skipDetailedInspection) - { - componentJson["properties"] = new JObject - { - ["_skipped"] = "Detailed property serialization skipped for safety" - }; - } - else - { - componentJson["properties"] = GetComponentProperties(component); - } - } - - componentsArray.Add(componentJson); - } - - return componentsArray; - } - - /// - /// Check if a component is enabled (if it has an enabled property) - /// - /// The component to check - /// True if the component is enabled, false otherwise - private static bool IsComponentEnabled(Component component) - { - // Check if the component is a Behaviour (has enabled property) - if (component is Behaviour behaviour) - { - return behaviour.enabled; - } - - // Check if the component is a Renderer - if (component is Renderer renderer) - { - return renderer.enabled; - } - - // Check if the component is a Collider - if (component is Collider collider) - { - return collider.enabled; - } - - // Default to true for components without an enabled property - return true; - } - - /// - /// Maximum depth for serializing nested objects to prevent stack overflow from circular references - /// - private const int MaxSerializationDepth = 5; - - /// - /// Maximum items to serialize in a collection to prevent excessive output - /// - private const int MaxCollectionItems = 50; - - /// - /// Get all serialized fields, public fields and public properties of a component - /// - /// The component to get properties from - /// A JObject containing all the component properties - private static JObject GetComponentProperties(Component component) - { - if (component == null) return null; - - JObject propertiesJson = new JObject(); - Type componentType = component.GetType(); - - // Track visited objects to prevent circular reference loops - HashSet visited = new HashSet(new ReferenceEqualityComparer()); - - // Get serialized fields (both public and private with SerializeField attribute) - FieldInfo[] fields = componentType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); - foreach (FieldInfo field in fields) - { - // Include public fields and serialized private fields - bool isSerializedField = field.IsPublic || field.GetCustomAttributes(typeof(SerializeField), true).Length > 0; - - if (!isSerializedField) continue; - try - { - object value = field.GetValue(component); - propertiesJson[field.Name] = SerializeValue(value, 0, visited); - } - catch (Exception) - { - // Skip fields that cannot be serialized - propertiesJson[field.Name] = "Unable to serialize"; - } - } - - // Get public properties - PropertyInfo[] properties = componentType.GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (PropertyInfo property in properties) - { - // Only include properties with a getter and skip properties that might cause issues or are not useful - if (!property.CanRead || ShouldSkipProperty(componentType, property)) continue; - - try - { - object value = property.GetValue(component); - propertiesJson[property.Name] = SerializeValue(value, 0, visited); - } - catch (Exception) - { - // Skip properties that cannot be serialized - propertiesJson[property.Name] = "Unable to serialize"; - } - } - - return propertiesJson; - } - - /// - /// Reference equality comparer for tracking visited objects (prevents circular reference infinite loops) - /// - private class ReferenceEqualityComparer : IEqualityComparer - { - public new bool Equals(object x, object y) => ReferenceEquals(x, y); - public int GetHashCode(object obj) => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(obj); - } - - /// - /// Determine if a property should be skipped during serialization - /// - /// The property to check - /// True if the property should be skipped, false otherwise - private static bool ShouldSkipProperty(Type componentType, PropertyInfo property) - { - if (property == null) - { - return true; - } - - // Skip non-public getters and indexers exposed as properties. - if (property.GetMethod == null || !property.GetMethod.IsPublic || property.GetIndexParameters().Length > 0) - { - return true; - } - - if (GloballySkippedPropertyNames.Contains(property.Name)) - { - return true; - } - - foreach (KeyValuePair> skippedEntry in SkippedPropertiesByComponentType) - { - if (skippedEntry.Key.IsAssignableFrom(componentType) && - skippedEntry.Value.Contains(property.Name)) - { - return true; - } - } - - return false; - } - - /// - /// Serialize a value to a JToken with depth limiting and circular reference protection - /// - /// The value to serialize - /// Current recursion depth - /// Set of already visited reference objects to detect circular references - /// A JToken representing the value - private static JToken SerializeValue(object value, int depth = 0, HashSet visited = null) - { - if (value == null) - return JValue.CreateNull(); - - // Depth limit check to prevent stack overflow - if (depth > MaxSerializationDepth) - return "[max depth exceeded]"; - - Type valueType = value.GetType(); - - // For reference types (excluding strings), check for circular references - if (!valueType.IsValueType && !(value is string)) - { - if (visited == null) - visited = new HashSet(new ReferenceEqualityComparer()); - - if (visited.Contains(value)) - return "[circular reference]"; - - visited.Add(value); - } - - // Handle common Unity types - if (value is Vector2 vector2) - return new JObject { ["x"] = vector2.x, ["y"] = vector2.y }; - - if (value is Vector3 vector3) - return new JObject { ["x"] = vector3.x, ["y"] = vector3.y, ["z"] = vector3.z }; - - if (value is Vector4 vector4) - return new JObject { ["x"] = vector4.x, ["y"] = vector4.y, ["z"] = vector4.z, ["w"] = vector4.w }; - - if (value is Quaternion quaternion) - return new JObject { ["x"] = quaternion.x, ["y"] = quaternion.y, ["z"] = quaternion.z, ["w"] = quaternion.w }; - - if (value is Color color) - return new JObject { ["r"] = color.r, ["g"] = color.g, ["b"] = color.b, ["a"] = color.a }; - - if (value is Bounds bounds) - return new JObject { - ["center"] = SerializeValue(bounds.center, depth + 1, visited), - ["size"] = SerializeValue(bounds.size, depth + 1, visited) - }; - - if (value is Rect rect) - return new JObject { ["x"] = rect.x, ["y"] = rect.y, ["width"] = rect.width, ["height"] = rect.height }; - - if (value is UnityEngine.Object unityObject) - return unityObject != null ? unityObject.name : null; - - // Handle arrays and lists with item limit - if (value is System.Collections.IList list) - { - JArray array = new JArray(); - int count = 0; - foreach (var item in list) - { - if (count >= MaxCollectionItems) - { - array.Add($"[... and {list.Count - count} more items]"); - break; - } - array.Add(SerializeValue(item, depth + 1, visited)); - count++; - } - return array; - } - - // Handle dictionaries with item limit - if (value is System.Collections.IDictionary dict) - { - JObject obj = new JObject(); - int count = 0; - foreach (System.Collections.DictionaryEntry entry in dict) - { - if (count >= MaxCollectionItems) - { - obj["_truncated"] = $"{dict.Count - count} more entries"; - break; - } - obj[entry.Key.ToString()] = SerializeValue(entry.Value, depth + 1, visited); - count++; - } - return obj; - } - - // Handle enum by using the name - if (value is Enum enumValue) - return enumValue.ToString(); - - // Handle primitive types directly - if (valueType.IsPrimitive || value is string || value is decimal) - { - return JToken.FromObject(value); - } - - // For complex types we don't recognize, return type name to avoid unsafe deep serialization - return $"[{valueType.Name}]"; - } - } -} diff --git a/Editor/Resources/GetMenuItemsResource.cs b/Editor/Resources/GetMenuItemsResource.cs deleted file mode 100644 index 84ce488f..00000000 --- a/Editor/Resources/GetMenuItemsResource.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using UnityEngine; -using UnityEditor; -using Newtonsoft.Json.Linq; - -namespace McpUnity.Resources -{ - /// - /// Resource for retrieving all available Unity menu items - /// - public class GetMenuItemsResource : McpResourceBase - { - private string _description; - - public GetMenuItemsResource() - { - Name = "get_menu_items"; - Description = "List of available menu items in Unity to execute"; - Uri = "unity://menu-items"; - } - - /// - /// Fetch all available menu items in the Unity Editor - /// - /// Resource parameters as a JObject (not used) - /// A JObject containing the list of menu items - public override JObject Fetch(JObject parameters) - { - // Get all menu items - JArray menuItems = GetAllMenuItems(); - - // Create the response - return new JObject - { - ["success"] = true, - ["message"] = $"Retrieved {menuItems.Count} menu items", - ["menuItems"] = menuItems - }; - } - - /// - /// Get all available menu items in the Unity Editor - /// - /// A list of menu item paths - private JArray GetAllMenuItems() - { - var menuItemsArray = new JArray(); - - // Find all methods with MenuItem attribute in loaded assemblies - var assemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (var assembly in assemblies) - { - // Skip system assemblies to improve performance - if (assembly.FullName.StartsWith("System.") || - assembly.FullName.StartsWith("Microsoft.") || - assembly.FullName.StartsWith("mscorlib")) - { - continue; - } - - foreach (var type in assembly.GetTypes()) - { - foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)) - { - var menuItemAttributes = method.GetCustomAttributes(typeof(MenuItem), false); - foreach (MenuItem menuItemAttribute in menuItemAttributes) - { - // Ignore object type context menu items - if(menuItemAttribute.menuItem.StartsWith("CONTEXT")) continue; - - menuItemsArray.Add(menuItemAttribute.menuItem); - } - } - } - } - - return menuItemsArray; - } - } -} diff --git a/Editor/Resources/GetMenuItemsResource.cs.meta b/Editor/Resources/GetMenuItemsResource.cs.meta deleted file mode 100644 index d4e88d42..00000000 --- a/Editor/Resources/GetMenuItemsResource.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 22bd2edf991e27943ab8aeb2827dfcf6 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources/GetPackagesResource.cs b/Editor/Resources/GetPackagesResource.cs deleted file mode 100644 index 4bcfc9c6..00000000 --- a/Editor/Resources/GetPackagesResource.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using UnityEditor; -using UnityEditor.PackageManager; -using UnityEditor.PackageManager.Requests; -using McpUnity.Unity; -using Newtonsoft.Json.Linq; - -namespace McpUnity.Resources -{ - /// - /// Resource for getting package information from the Unity Package Manager - /// - public class GetPackagesResource : McpResourceBase - { - private ListRequest _listRequest; - - public GetPackagesResource() - { - Name = "get_packages"; - Description = "Retrieve all packages from the Unity Package Manager"; - Uri = "unity://packages"; - } - - /// - /// Execute the resource to get packages information - /// - /// Optional parameters for filtering - /// JObject containing packages information - public override JObject Fetch(JObject parameters) - { - // Get project packages (installed) - var projectPackages = GetProjectPackages(); - - // Get registry packages - var registryPackages = GetRegistryPackages(); - - // Return combined result - return new JObject - { - ["success"] = true, - ["message"] = $"Retrieved {projectPackages.Count} project packages and {registryPackages.Count} registry packages", - ["projectPackages"] = projectPackages, - ["registryPackages"] = registryPackages - }; - } - - /// - /// Get packages installed in the current project - /// - private JArray GetProjectPackages() - { - JArray result = new JArray(); - - // List installed packages - _listRequest = Client.List(true); - - // Wait for the request to complete - while (!_listRequest.IsCompleted) - { - System.Threading.Thread.Sleep(100); - } - - if (_listRequest.Status == StatusCode.Success) - { - foreach (var package in _listRequest.Result) - { - result.Add(PackageToJObject(package, "installed")); - } - } - else if (_listRequest.Status == StatusCode.Failure) - { - Debug.LogError($"[MCP Unity] Failed to list project packages: {_listRequest.Error.message}"); - } - - return result; - } - - /// - /// Get packages available from the Unity Registry - /// - private JArray GetRegistryPackages() - { - JArray result = new JArray(); - - // Search Unity registry packages - SearchRequest searchRequest = Client.SearchAll(); - - // Wait for the request to complete - while (!searchRequest.IsCompleted) - { - System.Threading.Thread.Sleep(100); - } - - if (searchRequest.Status == StatusCode.Success) - { - foreach (var package in searchRequest.Result) - { - // Check if package is already installed - string state = "not_installed"; - if (_listRequest.Status == StatusCode.Success) - { - foreach (var installedPackage in _listRequest.Result) - { - if (installedPackage.name == package.name) - { - state = "installed"; - break; - } - } - } - - result.Add(PackageToJObject(package, state)); - } - } - else if (searchRequest.Status == StatusCode.Failure) - { - Debug.LogError($"[MCP Unity] Failed to search registry packages: {searchRequest.Error.message}"); - } - - return result; - } - - /// - /// Convert a package info object to JObject - /// - /// Package info - /// Installation state - /// JObject with package info - private JObject PackageToJObject(UnityEditor.PackageManager.PackageInfo package, string state) - { - return new JObject - { - ["name"] = package.name, - ["displayName"] = package.displayName, - ["version"] = package.version, - ["description"] = package.description, - ["category"] = package.category, - ["source"] = package.source.ToString(), - ["state"] = state, - ["author"] = new JObject - { - ["name"] = package.author?.name, - ["email"] = package.author?.email, - ["url"] = package.author?.url - } - }; - } - } -} diff --git a/Editor/Resources/GetPackagesResource.cs.meta b/Editor/Resources/GetPackagesResource.cs.meta deleted file mode 100644 index 6afccef1..00000000 --- a/Editor/Resources/GetPackagesResource.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 86b7a5e264997a9429dc226752dc16de -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources/GetScenesHierarchyResource.cs b/Editor/Resources/GetScenesHierarchyResource.cs deleted file mode 100644 index 95a377f9..00000000 --- a/Editor/Resources/GetScenesHierarchyResource.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine; -using UnityEngine.SceneManagement; -using Newtonsoft.Json.Linq; - -namespace McpUnity.Resources -{ - /// - /// Resource for retrieving all game objects in the Unity scenes hierarchy - /// - /// -/// Resource for retrieving all game objects in the Unity scenes hierarchy -/// -public class GetScenesHierarchyResource : McpResourceBase - { - public GetScenesHierarchyResource() - { - Name = "get_scenes_hierarchy"; - Description = "Retrieves all game objects in the Unity loaded scenes with their active state"; - Uri = "unity://scenes_hierarchy"; - } - - /// - /// Fetch all game objects in the Unity loaded scenes - /// - /// Resource parameters as a JObject (not used) - /// A JObject containing the hierarchy of game objects - public override JObject Fetch(JObject parameters) - { - // Get all game objects in the hierarchy - JArray hierarchyArray = GetSceneHierarchy(); - - // Create the response - return new JObject - { - ["success"] = true, - ["message"] = $"Retrieved hierarchy with {hierarchyArray.Count} root objects", - ["hierarchy"] = hierarchyArray - }; - } - - /// - /// Get all game objects in the Unity loaded scenes - /// - /// A JArray containing the hierarchy of game objects - private JArray GetSceneHierarchy() - { - JArray rootObjectsArray = new JArray(); - - // Get all loaded scenes - int sceneCount = SceneManager.sceneCount; - for (int i = 0; i < sceneCount; i++) - { - Scene scene = SceneManager.GetSceneAt(i); - - if (scene.isLoaded == false) - { - continue; - } - - // Create a scene object - JObject sceneObject = new JObject - { - ["name"] = scene.name, - ["path"] = scene.path, - ["buildIndex"] = scene.buildIndex, - ["isDirty"] = scene.isDirty, - ["rootObjects"] = new JArray() - }; - - // Get root game objects in the scene - GameObject[] rootObjects = scene.GetRootGameObjects(); - JArray rootObjectsInScene = (JArray)sceneObject["rootObjects"]; - - foreach (GameObject rootObject in rootObjects) - { - // Add the root object and its children to the array - rootObjectsInScene.Add(GetGameObjectResource.GameObjectToJObject(rootObject, false)); - } - - // Add the scene to the root objects array - rootObjectsArray.Add(sceneObject); - } - - return rootObjectsArray; - } - } -} diff --git a/Editor/Resources/GetScenesHierarchyResource.cs.meta b/Editor/Resources/GetScenesHierarchyResource.cs.meta deleted file mode 100644 index 452d65e6..00000000 --- a/Editor/Resources/GetScenesHierarchyResource.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2f976786d0da9fc4b8791c32d26fee43 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources/GetTestsResource.cs b/Editor/Resources/GetTestsResource.cs deleted file mode 100644 index ac099d11..00000000 --- a/Editor/Resources/GetTestsResource.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using McpUnity.Services; -using Newtonsoft.Json.Linq; -using UnityEditor.TestTools.TestRunner.Api; - -namespace McpUnity.Resources -{ - /// - /// Resource for getting available tests from Unity Test Runner - /// - public class GetTestsResource : McpResourceBase - { - private readonly ITestRunnerService _testRunnerService; - - /// - /// Constructor - /// - public GetTestsResource(ITestRunnerService testRunnerService) - { - Name = "get_tests"; - Description = "Gets available tests from Unity Test Runner"; - Uri = "unity://tests/{testMode}"; - IsAsync = true; - _testRunnerService = testRunnerService; - } - - /// - /// Asynchronously fetch tests based on provided parameters - /// - /// Resource parameters as a JObject - /// TaskCompletionSource to set the result or exception - public override async void FetchAsync(JObject parameters, TaskCompletionSource tcs) - { - // Get filter parameters - string testModeFilter = parameters["testMode"]?.ToObject(); - List allTests = await _testRunnerService.GetAllTestsAsync(testModeFilter); - var results = new JArray(); - - foreach (ITestAdaptor test in allTests) - { - results.Add(new JObject - { - ["name"] = test.Name, - ["fullName"] = test.FullName, - ["testMode"] = test.TestMode.ToString(), - ["runState"] = test.RunState.ToString() - }); - } - - tcs.SetResult(new JObject - { - ["success"] = true, - ["message"] = $"Retrieved {allTests.Count} tests", - ["tests"] = results - }); - } - } -} diff --git a/Editor/Resources/GetTestsResource.cs.meta b/Editor/Resources/GetTestsResource.cs.meta deleted file mode 100644 index 32d4a814..00000000 --- a/Editor/Resources/GetTestsResource.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 81803790e9b5f0a48a1f41138abd3fb5 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Resources/McpResourceBase.cs b/Editor/Resources/McpResourceBase.cs deleted file mode 100644 index 08843e36..00000000 --- a/Editor/Resources/McpResourceBase.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using UnityEngine; -using Newtonsoft.Json.Linq; - -namespace McpUnity.Resources -{ - /// - /// Base class for MCP Unity resources that provide data from the Unity Editor - /// - public abstract class McpResourceBase - { - /// - /// The name of the resource as used in API calls - /// - public string Name { get; protected set; } - - /// - /// Description of the resource's functionality - /// - public string Description { get; protected set; } - - /// - /// The URI pattern of the resource - /// - public string Uri { get; protected set; } - - /// - /// Whether this resource is enabled and available for use - /// - public bool IsEnabled { get; protected set; } = true; - - /// - /// Indicates if the fetch operation is asynchronous. - /// - public bool IsAsync { get; protected set; } = false; - - /// - /// Synchronously fetch the resource data. - /// Implement this for synchronous resources (IsAsync = false). - /// - /// Parameters extracted from the URI or query. - /// Result as JObject. - public virtual JObject Fetch(JObject parameters) - { - // Default implementation throws, forcing sync resources to override. - throw new NotImplementedException($"Synchronous Fetch not implemented for resource '{Name}'. Mark IsAsync=true and implement FetchAsync, or override Fetch."); - } - - /// - /// Asynchronously fetch the resource data. - /// Implement this for asynchronous resources (IsAsync = true). - /// The implementation MUST eventually call tcs.SetResult() or tcs.SetException(). - /// - /// Parameters extracted from the URI or query. - /// TaskCompletionSource to set the result on. - public virtual void FetchAsync(JObject parameters, TaskCompletionSource tcs) - { - // Default implementation throws, forcing async resources to override. - tcs.SetException(new NotImplementedException($"Asynchronous FetchAsync not implemented for resource '{Name}'. Mark IsAsync=false and implement Fetch, or override FetchAsync.")); - } - } -} diff --git a/Editor/Resources/McpResourceBase.cs.meta b/Editor/Resources/McpResourceBase.cs.meta deleted file mode 100644 index 52ee8662..00000000 --- a/Editor/Resources/McpResourceBase.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 693aa406b841aec4eb4e6507a6e4ce8e -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Services.meta b/Editor/Services.meta deleted file mode 100644 index 763a1e48..00000000 --- a/Editor/Services.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: b8235205ed1cea24e8a3b09364705a64 -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Services/ConsoleLogsService.cs b/Editor/Services/ConsoleLogsService.cs deleted file mode 100644 index 1dbbf01d..00000000 --- a/Editor/Services/ConsoleLogsService.cs +++ /dev/null @@ -1,273 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using Newtonsoft.Json.Linq; -using UnityEditor; -using UnityEngine; - -namespace McpUnity.Services -{ - /// - /// Service for managing Unity console logs - /// - public class ConsoleLogsService : IConsoleLogsService - { - // Static mapping for MCP log types to Unity log types - // Some MCP types map to multiple Unity types (e.g., "error" includes Error, Exception and Assert) - private static readonly Dictionary> LogTypeMapping = new Dictionary>(StringComparer.OrdinalIgnoreCase) - { - { "info", new HashSet(StringComparer.OrdinalIgnoreCase) { "Log" } }, - { "error", new HashSet(StringComparer.OrdinalIgnoreCase) { "Error", "Exception", "Assert" } } - }; - - // Structure to store log information - private class LogEntry - { - public string Message { get; set; } - public string StackTrace { get; set; } - public LogType Type { get; set; } - public DateTime Timestamp { get; set; } - } - - // Constants for log management - private const int MaxLogEntries = 1000; - private const int CleanupThreshold = 200; // Remove oldest entries when exceeding max - - // Collection to store all log messages - private readonly List _logEntries = new List(); - - /// - /// Constructor - /// - public ConsoleLogsService() - { - StartListening(); - } - - /// - /// Start listening for logs - /// - public void StartListening() - { - // Register for log messages - Application.logMessageReceivedThreaded += OnLogMessageReceived; - -#if UNITY_6000_0_OR_NEWER - // Unity 6 specific implementation - ConsoleWindowUtility.consoleLogsChanged += OnConsoleCountChanged; -#else - // Unity 2022.3 implementation using reflection - EditorApplication.update += CheckConsoleClearViaReflection; -#endif - } - - /// - /// Stop listening for logs - /// - public void StopListening() - { - // Unregister from log messages - Application.logMessageReceivedThreaded -= OnLogMessageReceived; - -#if UNITY_6000_0_OR_NEWER - // Unity 6 specific implementation - ConsoleWindowUtility.consoleLogsChanged -= OnConsoleCountChanged; -#else - // Unity 2022.3 implementation using reflection - EditorApplication.update -= CheckConsoleClearViaReflection; -#endif - } - /// - /// Get logs as a JSON array with pagination support - /// - /// Filter by log type (empty for all) - /// Starting index (0-based) - /// Maximum number of logs to return (default: 100) - /// Whether to include stack trace in logs (default: true) - /// JObject containing logs array and pagination info - public JObject GetLogsAsJson(string logType = "", int offset = 0, int limit = 100, bool includeStackTrace = true) - { - // Convert log entries to a JSON array, filtering by logType if provided - JArray logsArray = new JArray(); - bool filter = !string.IsNullOrEmpty(logType); - int totalCount = 0; - int filteredCount = 0; - int currentIndex = 0; - - // Map MCP log types to Unity log types outside the loop for better performance - HashSet unityLogTypes = null; - if (filter) - { - if (LogTypeMapping.TryGetValue(logType, out var mapped)) - { - unityLogTypes = mapped; - } - else - { - // If no mapping exists, create a set with the original type for case-insensitive comparison - unityLogTypes = new HashSet(StringComparer.OrdinalIgnoreCase) { logType }; - } - } - - lock (_logEntries) - { - totalCount = _logEntries.Count; - - // Single pass: count filtered entries and collect the requested page (newest first) - for (int i = _logEntries.Count - 1; i >= 0; i--) - { - var entry = _logEntries[i]; - - // Skip if filtering and entry doesn't match the filter - if (filter && !unityLogTypes.Contains(entry.Type.ToString())) - continue; - - // Count filtered entries - filteredCount++; - - // Check if we're in the offset range and haven't reached the limit yet - if (currentIndex >= offset && logsArray.Count < limit) - { - var logObject = new JObject - { - ["message"] = entry.Message, - ["type"] = entry.Type.ToString(), - ["timestamp"] = entry.Timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff") - }; - - // Only include stack trace if requested - if (includeStackTrace) - { - logObject["stackTrace"] = entry.StackTrace; - } - - logsArray.Add(logObject); - } - - currentIndex++; - - // Early exit if we've collected enough logs - if (currentIndex >= offset + limit) break; - } - } - - return new JObject - { - ["logs"] = logsArray, - ["_totalCount"] = totalCount, - ["_filteredCount"] = filteredCount, - ["_returnedCount"] = logsArray.Count - }; - } - - /// - /// Clear all stored logs - /// - private void ClearLogs() - { - lock (_logEntries) - { - _logEntries.Clear(); - } - } - - /// - /// Manually clean up old log entries, keeping only the most recent ones - /// - /// Number of recent entries to keep (default: 500) - public void CleanupOldLogs(int keepCount = 500) - { - lock (_logEntries) - { - if (_logEntries.Count > keepCount) - { - int removeCount = _logEntries.Count - keepCount; - _logEntries.RemoveRange(0, removeCount); - } - } - } - - /// - /// Get current log count - /// - /// Number of stored log entries - public int GetLogCount() - { - lock (_logEntries) - { - return _logEntries.Count; - } - } - - /// - /// Check if console was cleared using reflection (for Unity 2022.3) - /// - private void CheckConsoleClearViaReflection() - { - try - { - // Get current log counts using LogEntries (internal Unity API) - var logEntriesType = Type.GetType("UnityEditor.LogEntries,UnityEditor"); - if (logEntriesType == null) return; - - var getCountMethod = logEntriesType.GetMethod("GetCount", - BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic); - if (getCountMethod == null) return; - - int currentTotalCount = (int)getCountMethod.Invoke(null, null); - - // If we had logs before, but now we don't, console was likely cleared - if (currentTotalCount == 0 && _logEntries.Count > 0) - { - ClearLogs(); - } - } - catch (Exception ex) - { - // Just log the error but don't break functionality - Debug.LogError($"[MCP Unity] Error checking console clear: {ex.Message}"); - } - } - - /// - /// Callback for when a log message is received - /// - /// The log message - /// The stack trace - /// The log type - private void OnLogMessageReceived(string logString, string stackTrace, LogType type) - { - // Add the log entry to our collection - lock (_logEntries) - { - _logEntries.Add(new LogEntry - { - Message = logString, - StackTrace = stackTrace, - Type = type, - Timestamp = DateTime.Now - }); - - // Clean up old entries if we exceed the maximum - if (_logEntries.Count > MaxLogEntries) - { - _logEntries.RemoveRange(0, CleanupThreshold); - } - } - } - -#if UNITY_6000_0_OR_NEWER - /// - /// Called when the console logs count changes - /// - private void OnConsoleCountChanged() - { - ConsoleWindowUtility.GetConsoleLogCounts(out int error, out int warning, out int log); - if (error == 0 && warning == 0 && log == 0 && _logEntries.Count > 0) - { - ClearLogs(); - } - } -#endif - } -} diff --git a/Editor/Services/ConsoleLogsService.cs.meta b/Editor/Services/ConsoleLogsService.cs.meta deleted file mode 100644 index 244b6696..00000000 --- a/Editor/Services/ConsoleLogsService.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2ffc26907cd8fcf4f830d24697fbf712 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Services/IConsoleLogsService.cs b/Editor/Services/IConsoleLogsService.cs deleted file mode 100644 index 23ce313b..00000000 --- a/Editor/Services/IConsoleLogsService.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Collections.Generic; -using Newtonsoft.Json.Linq; -using UnityEngine; - -namespace McpUnity.Services -{ - /// - /// Interface for the console logs service - /// - public interface IConsoleLogsService - { - /// - /// Get logs as a JSON object with pagination support - /// - /// Filter by log type (empty for all) - /// Starting index (0-based) - /// Maximum number of logs to return (default: 100) - /// Whether to include stack trace in logs (default: true) - /// JObject containing logs array and pagination info - JObject GetLogsAsJson(string logType = "", int offset = 0, int limit = 100, bool includeStackTrace = true); - - /// - /// Start listening for logs - /// - void StartListening(); - - /// - /// Stop listening for logs - /// - void StopListening(); - - /// - /// Manually clean up old log entries, keeping only the most recent ones - /// - /// Number of recent entries to keep (default: 500) - void CleanupOldLogs(int keepCount = 500); - - /// - /// Get current log count - /// - /// Number of stored log entries - int GetLogCount(); - } -} diff --git a/Editor/Services/IConsoleLogsService.cs.meta b/Editor/Services/IConsoleLogsService.cs.meta deleted file mode 100644 index f962d63f..00000000 --- a/Editor/Services/IConsoleLogsService.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 817c810fa155fd349bd98b255d892759 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Services/ITestRunnerService.cs b/Editor/Services/ITestRunnerService.cs deleted file mode 100644 index bc26da68..00000000 --- a/Editor/Services/ITestRunnerService.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using Newtonsoft.Json.Linq; -using UnityEditor.TestTools.TestRunner.Api; - -namespace McpUnity.Services -{ - /// - /// Interface for the test runner service - /// - public interface ITestRunnerService - { - /// - /// Asynchronously retrieves all available tests using the TestRunnerApi. - /// - /// Optional test mode filter (EditMode, PlayMode, or empty for all) - /// List of test items matching the specified test mode, or all tests if no mode specified - Task> GetAllTestsAsync(string testModeFilter = ""); - - /// - /// Executes tests using the TestRunnerApi and returns the results as a JSON object. - /// - /// The test mode to run (EditMode or PlayMode). - /// If true, only failed test results are included in the output. - /// If true, all logs are included in the output. - /// A filter string to select specific tests to run. - /// Task that resolves with test results when tests are complete - Task ExecuteTestsAsync(TestMode testMode, bool returnOnlyFailures, bool returnWithLogs, string testFilter); - } -} \ No newline at end of file diff --git a/Editor/Services/ITestRunnerService.cs.meta b/Editor/Services/ITestRunnerService.cs.meta deleted file mode 100644 index fc131414..00000000 --- a/Editor/Services/ITestRunnerService.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 6588e2534852ce4469c45cfc3da5ab73 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Services/TestRunnerService.cs b/Editor/Services/TestRunnerService.cs deleted file mode 100644 index 9af4557a..00000000 --- a/Editor/Services/TestRunnerService.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using McpUnity.Unity; -using McpUnity.Utils; -using UnityEngine; -using UnityEditor; -using UnityEditor.TestTools.TestRunner.Api; -using Newtonsoft.Json.Linq; - -namespace McpUnity.Services -{ - /// - /// Service for accessing Unity Test Runner functionality - /// Implements ICallbacks for TestRunnerApi. - /// - public class TestRunnerService : ITestRunnerService, ICallbacks - { - private readonly TestRunnerApi _testRunnerApi; - private TaskCompletionSource _tcs; - private bool _returnOnlyFailures; - private bool _returnWithLogs; - private List _results; - - /// - /// Constructor - /// - public TestRunnerService() - { - _testRunnerApi = ScriptableObject.CreateInstance(); - _results = new List(); - _testRunnerApi.RegisterCallbacks(this); - } - - /// - /// Async retrieval of all tests using TestRunnerApi callbacks - /// - /// Optional test mode filter (EditMode, PlayMode, or empty for all) - /// List of test items matching the specified test mode, or all tests if no mode specified - public async Task> GetAllTestsAsync(string testModeFilter = "") - { - var tests = new List(); - var tasks = new List>>(); - - if (string.IsNullOrEmpty(testModeFilter) || testModeFilter.Equals("EditMode", StringComparison.OrdinalIgnoreCase)) - { - tasks.Add(RetrieveTestsAsync(TestMode.EditMode)); - } - if (string.IsNullOrEmpty(testModeFilter) || testModeFilter.Equals("PlayMode", StringComparison.OrdinalIgnoreCase)) - { - tasks.Add(RetrieveTestsAsync(TestMode.PlayMode)); - } - - var results = await Task.WhenAll(tasks); - - foreach (var result in results) - { - tests.AddRange(result); - } - - return tests; - } - - /// - /// Executes tests and returns a JSON summary. - /// - /// The test mode to run (EditMode or PlayMode). - /// If true, only failed test results are included in the output. - /// If true, all logs are included in the output. - /// A filter string to select specific tests to run. - /// Task that resolves with test results when tests are complete - public async Task ExecuteTestsAsync(TestMode testMode, bool returnOnlyFailures, bool returnWithLogs, string testFilter = "") - { - var filter = new Filter { testMode = testMode }; - - _tcs = new TaskCompletionSource(); - _returnOnlyFailures = returnOnlyFailures; - _returnWithLogs = returnWithLogs; - - if (!string.IsNullOrEmpty(testFilter)) - { - filter.testNames = new[] { testFilter }; - } - - _testRunnerApi.Execute(new ExecutionSettings(filter)); - - return await WaitForCompletionAsync( - McpUnitySettings.Instance.RequestTimeoutSeconds); - } - - /// - /// Asynchronously retrieves all test adaptors for the specified test mode. - /// - /// The test mode to retrieve tests for (EditMode or PlayMode). - /// A task that resolves to a list of ITestAdaptor representing all tests in the given mode. - private Task> RetrieveTestsAsync(TestMode mode) - { - var tcs = new TaskCompletionSource>(); - var tests = new List(); - - _testRunnerApi.RetrieveTestList(mode, adaptor => - { - CollectTestItems(adaptor, tests); - tcs.SetResult(tests); - }); - - return tcs.Task; - } - - /// - /// Recursively collect test items from test adaptors - /// - private void CollectTestItems(ITestAdaptor testAdaptor, List tests) - { - if (testAdaptor.IsSuite) - { - // For suites (namespaces, classes), collect all children - foreach (var child in testAdaptor.Children) - { - CollectTestItems(child, tests); - } - } - else - { - tests.Add(testAdaptor); - } - } - - #region ICallbacks Implementation - - /// - /// Called when the test run starts. - /// - public void RunStarted(ITestAdaptor testsToRun) - { - if (_tcs == null) - return; - - _results.Clear(); - McpLogger.LogInfo($"Test run started: {testsToRun?.Name}"); - } - - /// - /// Called when an individual test starts. - /// - public void TestStarted(ITestAdaptor test) - { - // Optionally implement per-test start logic or logging. - } - - /// - /// Called when an individual test finishes. - /// - public void TestFinished(ITestResultAdaptor result) - { - if (_tcs == null) - return; - - _results.Add(result); - } - - /// - /// Called when the test run finishes. - /// - public void RunFinished(ITestResultAdaptor result) - { - if (_tcs == null) - return; - - var summary = BuildResultJson(_results, result); - _tcs.TrySetResult(summary); - _tcs = null; - } - - #endregion - - #region Helpers - - private async Task WaitForCompletionAsync(int timeoutSeconds) - { - var delayTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)); - var winner = await Task.WhenAny(_tcs.Task, delayTask); - - if (winner != _tcs.Task) - { - _tcs.TrySetResult( - McpUnitySocketHandler.CreateErrorResponse( - $"Test run timed out after {timeoutSeconds} seconds", - "test_runner_timeout")); - } - return await _tcs.Task; - } - - private JObject BuildResultJson(List results, ITestResultAdaptor result) - { - var arr = new JArray(results - .Where(r => !r.HasChildren) - .Where(r => !_returnOnlyFailures || r.ResultState.StartsWith("Failed")) - .Select(r => new JObject { - ["name"] = r.Name, - ["fullName"] = r.FullName, - ["state"] = r.ResultState, - ["message"] = r.Message, - ["duration"] = r.Duration, - ["logs"] = _returnWithLogs ? r.Output : null, - ["stackTrace"] = r.StackTrace - })); - - int testCount = result.PassCount + result.SkipCount + result.FailCount; - return new JObject { - ["success"] = true, - ["type"] = "text", - ["message"] = $"{result.Test.Name} test run completed: {result.PassCount}/{testCount} passed - {result.FailCount}/{testCount} failed - {result.SkipCount}/{testCount} skipped", - ["resultState"] = result.ResultState, - ["durationSeconds"] = result.Duration, - ["testCount"] = results.Count, - ["passCount"] = result.PassCount, - ["failCount"] = result.FailCount, - ["skipCount"] = result.SkipCount, - ["results"] = arr - }; - } - - #endregion - } -} diff --git a/Editor/Services/TestRunnerService.cs.meta b/Editor/Services/TestRunnerService.cs.meta deleted file mode 100644 index 5ddc7e83..00000000 --- a/Editor/Services/TestRunnerService.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b904b066c31e2bc498baf64673388731 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Editor/Setup.meta b/Editor/Setup.meta new file mode 100644 index 00000000..1cd730b9 --- /dev/null +++ b/Editor/Setup.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 58bb5e08f9fb44eeda3e654454b6ffba +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Editor/Setup/SystemUnityCliProcessRunner.cs b/Editor/Setup/SystemUnityCliProcessRunner.cs new file mode 100644 index 00000000..f9e3c9a4 --- /dev/null +++ b/Editor/Setup/SystemUnityCliProcessRunner.cs @@ -0,0 +1,139 @@ +using System; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; + +namespace McpUnity.Extensions.Setup +{ + public sealed class SystemUnityCliProcessRunner : IUnityCliProcessRunner + { + private static readonly TimeSpan CleanupGrace = TimeSpan.FromMilliseconds(250); + + public async Task RunAsync( + string executablePath, + string arguments, + TimeSpan timeout, + CancellationToken cancellationToken) + { + using (var process = new Process()) + using (var deadlineSource = new CancellationTokenSource(timeout)) + using (var interruptionSource = CancellationTokenSource.CreateLinkedTokenSource( + cancellationToken, + deadlineSource.Token)) + { + try + { + process.StartInfo = new ProcessStartInfo + { + FileName = executablePath, + Arguments = arguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true + }; + process.EnableRaisingEvents = true; + var exited = new TaskCompletionSource(); + process.Exited += (sender, eventArgs) => exited.TrySetResult(true); + + try + { + process.Start(); + } + catch (Exception exception) + { + return new UnityCliProcessResult(string.Empty, exception.Message, -1, false); + } + + var standardOutput = process.StandardOutput.ReadToEndAsync(); + var standardError = process.StandardError.ReadToEndAsync(); + if (process.HasExited) + { + exited.TrySetResult(true); + } + + var interruptionTask = Task.Delay(System.Threading.Timeout.Infinite, interruptionSource.Token); + var completed = await Task.WhenAny(exited.Task, interruptionTask); + if (completed == exited.Task) + { + var streams = Task.WhenAll(standardOutput, standardError); + if (await Task.WhenAny(streams, interruptionTask) == streams) + { + return new UnityCliProcessResult( + await standardOutput, + await standardError, + process.ExitCode, + false); + } + } + + var timedOut = deadlineSource.IsCancellationRequested && !cancellationToken.IsCancellationRequested; + TryKillOwnedProcess(process); + return await CompleteInterruptedProcessAsync( + process, + exited.Task, + standardOutput, + standardError, + timedOut); + } + finally + { + interruptionSource.Cancel(); + deadlineSource.Cancel(); + } + } + } + + private static async Task CompleteInterruptedProcessAsync( + Process process, + Task exited, + Task standardOutput, + Task standardError, + bool timedOut) + { + using (var cleanupSource = new CancellationTokenSource(CleanupGrace)) + { + var cleanupTask = Task.Delay(System.Threading.Timeout.Infinite, cleanupSource.Token); + await Task.WhenAny(exited, cleanupTask); + await Task.WhenAny(Task.WhenAll(standardOutput, standardError), cleanupTask); + return new UnityCliProcessResult( + GetCompletedResult(standardOutput), + GetCompletedResult(standardError), + TryGetExitCode(process), + timedOut, + !timedOut); + } + } + + private static void TryKillOwnedProcess(Process process) + { + try + { + if (!process.HasExited) + { + process.Kill(); + } + } + catch (InvalidOperationException) + { + } + } + + private static int TryGetExitCode(Process process) + { + try + { + return process.HasExited ? process.ExitCode : -1; + } + catch (InvalidOperationException) + { + return -1; + } + } + + private static string GetCompletedResult(Task task) + { + return task.Status == TaskStatus.RanToCompletion ? task.Result : string.Empty; + } + } +} diff --git a/Editor/Setup/SystemUnityCliProcessRunner.cs.meta b/Editor/Setup/SystemUnityCliProcessRunner.cs.meta new file mode 100644 index 00000000..3e53ae74 --- /dev/null +++ b/Editor/Setup/SystemUnityCliProcessRunner.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 0e5d63a2a325c40f0949a71835205006 \ No newline at end of file diff --git a/Editor/Setup/UnityCliCheckService.cs b/Editor/Setup/UnityCliCheckService.cs new file mode 100644 index 00000000..441c84fa --- /dev/null +++ b/Editor/Setup/UnityCliCheckService.cs @@ -0,0 +1,95 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace McpUnity.Extensions.Setup +{ + public sealed class UnityCliProcessResult + { + public UnityCliProcessResult( + string standardOutput, + string standardError, + int exitCode, + bool timedOut, + bool cancelled = false) + { + StandardOutput = standardOutput ?? string.Empty; + StandardError = standardError ?? string.Empty; + ExitCode = exitCode; + TimedOut = timedOut; + Cancelled = cancelled; + } + + public string StandardOutput { get; } + + public string StandardError { get; } + + public int ExitCode { get; } + + public bool TimedOut { get; } + + public bool Cancelled { get; } + } + + public interface IUnityCliProcessRunner + { + Task RunAsync( + string executablePath, + string arguments, + TimeSpan timeout, + CancellationToken cancellationToken); + } + + public sealed class UnityCliCheckResult + { + public UnityCliCheckResult( + UnityCliPathResolution candidate, + UnityCliProcessResult process, + UnityCliCompatibilityResult compatibility) + { + Candidate = candidate; + Process = process; + Compatibility = compatibility; + } + + public UnityCliPathResolution Candidate { get; } + + public UnityCliProcessResult Process { get; } + + public UnityCliCompatibilityResult Compatibility { get; } + } + + public sealed class UnityCliCheckService + { + private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(5); + private readonly IUnityCliProcessRunner processRunner; + private readonly Func environmentPath; + + public UnityCliCheckService(IUnityCliProcessRunner processRunner, Func environmentPath) + { + this.processRunner = processRunner; + this.environmentPath = environmentPath; + } + + public async Task CheckAsync(string liveWindowPath, CancellationToken cancellationToken) + { + var candidate = UnityCliPathResolver.Resolve(liveWindowPath, environmentPath()); + UnityCliProcessResult process; + try + { + process = await processRunner.RunAsync(candidate.ExecutablePath, "--version", Timeout, cancellationToken); + } + catch (Exception exception) + { + process = new UnityCliProcessResult(string.Empty, exception.Message, -1, false); + } + + var output = process.StandardOutput + Environment.NewLine + process.StandardError; + var compatibility = UnityCliVersionClassifier.Classify( + output, + process.ExitCode == 0 && !process.Cancelled, + process.TimedOut); + return new UnityCliCheckResult(candidate, process, compatibility); + } + } +} diff --git a/Editor/Setup/UnityCliCheckService.cs.meta b/Editor/Setup/UnityCliCheckService.cs.meta new file mode 100644 index 00000000..21c4ae11 --- /dev/null +++ b/Editor/Setup/UnityCliCheckService.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1397a7a0d3d4b4a7fa67592b66478508 \ No newline at end of file diff --git a/Editor/Setup/UnityCliConfiguration.cs b/Editor/Setup/UnityCliConfiguration.cs new file mode 100644 index 00000000..33fba4bc --- /dev/null +++ b/Editor/Setup/UnityCliConfiguration.cs @@ -0,0 +1,67 @@ +using System.Text; + +namespace McpUnity.Extensions.Setup +{ + public static class UnityCliConfiguration + { + public static string CreateOfficial(string executablePath, string projectPath) + { + return "{\n \"mcpServers\": {\n \"unity\": {\n" + + " \"command\": \"" + Escape(executablePath) + "\",\n" + + " \"args\": [\"mcp\", \"--project-path\", \"" + Escape(projectPath) + "\"]\n" + + " }\n }\n}"; + } + + public static string CreateCompanion( + string packagePath, + string projectPath, + string executablePath, + bool includeExplicitCliPath) + { + var serverPath = packagePath.TrimEnd('/', '\\') + "/Server~/build/index.js"; + var builder = new StringBuilder(); + builder.Append("{\n \"mcpServers\": {\n \"mcp-unity-companion\": {\n"); + builder.Append(" \"command\": \"node\",\n"); + builder.Append(" \"args\": [\"").Append(Escape(serverPath)).Append("\", \"--project-path\", \"") + .Append(Escape(projectPath)).Append("\"]"); + if (includeExplicitCliPath) + { + builder.Append(",\n \"env\": { \"UNITY_CLI_PATH\": \"") + .Append(Escape(executablePath)).Append("\" }"); + } + + builder.Append("\n }\n }\n}"); + return builder.ToString(); + } + + private static string Escape(string value) + { + var builder = new StringBuilder(); + foreach (var character in value ?? string.Empty) + { + switch (character) + { + case '\\': builder.Append("\\\\"); break; + case '\"': builder.Append("\\\""); break; + case '\b': builder.Append("\\b"); break; + case '\f': builder.Append("\\f"); break; + case '\n': builder.Append("\\n"); break; + case '\r': builder.Append("\\r"); break; + case '\t': builder.Append("\\t"); break; + default: + if (character < 32) + { + builder.Append("\\u").Append(((int)character).ToString("x4")); + } + else + { + builder.Append(character); + } + break; + } + } + + return builder.ToString(); + } + } +} diff --git a/Editor/Setup/UnityCliConfiguration.cs.meta b/Editor/Setup/UnityCliConfiguration.cs.meta new file mode 100644 index 00000000..5f295659 --- /dev/null +++ b/Editor/Setup/UnityCliConfiguration.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 600ee04563e3c478ca78fab9f9cf096d \ No newline at end of file diff --git a/Editor/Setup/UnityCliPathResolver.cs b/Editor/Setup/UnityCliPathResolver.cs new file mode 100644 index 00000000..a8b53bfa --- /dev/null +++ b/Editor/Setup/UnityCliPathResolver.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; + +namespace McpUnity.Extensions.Setup +{ + public enum UnityCliPathSource + { + Window, + Environment, + Path + } + + public sealed class UnityCliPathResolution + { + public UnityCliPathResolution(string executablePath, UnityCliPathSource source) + { + ExecutablePath = executablePath; + Source = source; + } + + public string ExecutablePath { get; } + + public UnityCliPathSource Source { get; } + + public bool IsExplicitAbsolutePath => + Source == UnityCliPathSource.Window && IsAbsolutePath(ExecutablePath); + + private static bool IsAbsolutePath(string path) + { + return Path.IsPathRooted(path) || + (path.Length >= 3 && char.IsLetter(path[0]) && path[1] == ':' && + (path[2] == '\\' || path[2] == '/')) || + path.StartsWith("\\\\", StringComparison.Ordinal); + } + } + + public static class UnityCliPathResolver + { + public static UnityCliPathResolution Resolve(string liveWindowPath, string environmentPath) + { + if (!string.IsNullOrWhiteSpace(liveWindowPath)) + { + return new UnityCliPathResolution(liveWindowPath.Trim(), UnityCliPathSource.Window); + } + + if (!string.IsNullOrWhiteSpace(environmentPath)) + { + return new UnityCliPathResolution(environmentPath.Trim(), UnityCliPathSource.Environment); + } + + return new UnityCliPathResolution("unity", UnityCliPathSource.Path); + } + } +} diff --git a/Editor/Setup/UnityCliPathResolver.cs.meta b/Editor/Setup/UnityCliPathResolver.cs.meta new file mode 100644 index 00000000..ef4faabd --- /dev/null +++ b/Editor/Setup/UnityCliPathResolver.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 20f66b2f36c374427834a5fe84b78e02 \ No newline at end of file diff --git a/Editor/Setup/UnityCliPipelineStatus.cs b/Editor/Setup/UnityCliPipelineStatus.cs new file mode 100644 index 00000000..31975cc2 --- /dev/null +++ b/Editor/Setup/UnityCliPipelineStatus.cs @@ -0,0 +1,37 @@ +namespace McpUnity.Extensions.Setup +{ + public enum UnityCliPipelineState + { + ExactSupported, + Missing, + DifferentUntested + } + + public static class UnityCliPipelineStatus + { + public static UnityCliPipelineState Classify(string version) + { + if (string.IsNullOrEmpty(version)) + { + return UnityCliPipelineState.Missing; + } + + return version == "0.3.1-exp.1" + ? UnityCliPipelineState.ExactSupported + : UnityCliPipelineState.DifferentUntested; + } + + public static string GetDisplayName(UnityCliPipelineState state) + { + switch (state) + { + case UnityCliPipelineState.ExactSupported: + return "exact supported"; + case UnityCliPipelineState.Missing: + return "missing"; + default: + return "different/untested"; + } + } + } +} diff --git a/Editor/Setup/UnityCliPipelineStatus.cs.meta b/Editor/Setup/UnityCliPipelineStatus.cs.meta new file mode 100644 index 00000000..6b774b6d --- /dev/null +++ b/Editor/Setup/UnityCliPipelineStatus.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4abeb53d04f4a401f8d82f5f2d829d83 \ No newline at end of file diff --git a/Editor/Setup/UnityCliSetupContent.cs b/Editor/Setup/UnityCliSetupContent.cs new file mode 100644 index 00000000..2e60736d --- /dev/null +++ b/Editor/Setup/UnityCliSetupContent.cs @@ -0,0 +1,14 @@ +namespace McpUnity.Extensions.Setup +{ + public static class UnityCliSetupContent + { + public const string DocumentationUrl = "https://docs.unity.com/en-us/unity-cli/use-unity-cli"; + + public static string GetInstallCommand(bool isWindows) + { + return isWindows + ? "$env:UNITY_CLI_CHANNEL='beta'; irm https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.ps1 | iex" + : "curl -fsSL https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.sh | UNITY_CLI_CHANNEL=beta bash"; + } + } +} diff --git a/Editor/Setup/UnityCliSetupContent.cs.meta b/Editor/Setup/UnityCliSetupContent.cs.meta new file mode 100644 index 00000000..5ddc149e --- /dev/null +++ b/Editor/Setup/UnityCliSetupContent.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f6f3adfe06e8d44b0821ba9862281627 \ No newline at end of file diff --git a/Editor/Setup/UnityCliSetupWindow.cs b/Editor/Setup/UnityCliSetupWindow.cs new file mode 100644 index 00000000..c8ea0db6 --- /dev/null +++ b/Editor/Setup/UnityCliSetupWindow.cs @@ -0,0 +1,121 @@ +using System; +using System.IO; +using System.Threading; +using McpUnity.Extensions.Commands; +using UnityEditor; +using UnityEngine; + +namespace McpUnity.Extensions.Setup +{ + public sealed class UnityCliSetupWindow : EditorWindow + { + private string cliPath = string.Empty; + private UnityCliCheckResult checkResult; + private bool isChecking; + + [MenuItem("Window/MCP Unity/Setup")] + private static void Open() + { + GetWindow("MCP Unity Setup"); + } + + private void OnGUI() + { + var projectPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..")); + EditorGUILayout.LabelField("Unity project", projectPath); + DrawPipelineStatus(); + EditorGUILayout.Space(); + + cliPath = EditorGUILayout.TextField("Unity CLI executable (optional)", cliPath); + using (new EditorGUI.DisabledScope(isChecking)) + { + if (GUILayout.Button(isChecking ? "Checking Unity CLI..." : "Check Unity CLI")) + { + CheckUnityCli(); + } + } + + if (checkResult != null) + { + DrawCheckResult(projectPath); + } + } + + private void DrawPipelineStatus() + { + var package = UnityEditor.PackageManager.PackageInfo.FindForPackageName("com.unity.pipeline"); + var version = package == null ? null : package.version; + var state = UnityCliPipelineStatus.GetDisplayName(UnityCliPipelineStatus.Classify(version)); + EditorGUILayout.LabelField("com.unity.pipeline", version ?? "missing"); + EditorGUILayout.LabelField("Pipeline state", state); + } + + private void DrawCheckResult(string projectPath) + { + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Unity CLI", checkResult.Candidate.ExecutablePath); + EditorGUILayout.LabelField("Status", checkResult.Compatibility.Status.ToString()); + EditorGUILayout.LabelField("Version", checkResult.Compatibility.Version ?? "not detected"); + if (!string.IsNullOrEmpty(checkResult.Process.StandardError)) + { + EditorGUILayout.HelpBox(checkResult.Process.StandardError, MessageType.Warning); + } + + if (checkResult.Compatibility.Status == UnityCliCompatibility.MissingOrFailed || + checkResult.Compatibility.Status == UnityCliCompatibility.Incompatible) + { + if (GUILayout.Button("Copy official install command")) + { + EditorGUIUtility.systemCopyBuffer = UnityCliSetupContent.GetInstallCommand(Application.platform == RuntimePlatform.WindowsEditor); + } + + if (GUILayout.Button("Open Unity CLI documentation")) + { + Application.OpenURL(UnityCliSetupContent.DocumentationUrl); + } + + return; + } + + if (GUILayout.Button("Copy official MCP configuration")) + { + EditorGUIUtility.systemCopyBuffer = UnityCliConfiguration.CreateOfficial( + checkResult.Candidate.ExecutablePath, + projectPath); + } + + if (GUILayout.Button("Copy companion configuration")) + { + var package = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(AssignMaterialCommand).Assembly); + EditorGUIUtility.systemCopyBuffer = UnityCliConfiguration.CreateCompanion( + package.resolvedPath, + projectPath, + checkResult.Candidate.ExecutablePath, + checkResult.Candidate.IsExplicitAbsolutePath); + } + } + + private async void CheckUnityCli() + { + isChecking = true; + Repaint(); + var service = new UnityCliCheckService( + new SystemUnityCliProcessRunner(), + () => Environment.GetEnvironmentVariable("UNITY_CLI_PATH")); + var result = await service.CheckAsync(cliPath, CancellationToken.None); + EditorApplication.delayCall += () => ApplyCheckResult(result); + } + + private void ApplyCheckResult(UnityCliCheckResult result) + { + if (this == null) + { + return; + } + + checkResult = result; + isChecking = false; + Repaint(); + } + } +} diff --git a/Editor/Setup/UnityCliSetupWindow.cs.meta b/Editor/Setup/UnityCliSetupWindow.cs.meta new file mode 100644 index 00000000..8abb9d3f --- /dev/null +++ b/Editor/Setup/UnityCliSetupWindow.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c61f1f1a7c81841bd9d6b180a3c05d35 \ No newline at end of file diff --git a/Editor/Setup/UnityCliVersionClassifier.cs b/Editor/Setup/UnityCliVersionClassifier.cs new file mode 100644 index 00000000..c64271ce --- /dev/null +++ b/Editor/Setup/UnityCliVersionClassifier.cs @@ -0,0 +1,183 @@ +using System; +using System.Text.RegularExpressions; + +namespace McpUnity.Extensions.Setup +{ + public enum UnityCliCompatibility + { + MissingOrFailed, + Incompatible, + Compatible, + UntestedNewer + } + + public sealed class UnityCliCompatibilityResult + { + public UnityCliCompatibilityResult(UnityCliCompatibility status, string version) + { + Status = status; + Version = version; + } + + public UnityCliCompatibility Status { get; } + + public string Version { get; } + } + + public static class UnityCliVersionClassifier + { + private static readonly Regex VersionPattern = new Regex( + @"(?0|[1-9][0-9]*)\.(?0|[1-9][0-9]*)\.(?0|[1-9][0-9]*)(?:-(?
[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+(?[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?=$|[\s,;:()\[\]{}])",
+            RegexOptions.Compiled);
+
+        public static UnityCliCompatibilityResult Classify(string output, bool processSucceeded, bool timedOut)
+        {
+            if (!processSucceeded || timedOut || string.IsNullOrEmpty(output))
+            {
+                return new UnityCliCompatibilityResult(UnityCliCompatibility.MissingOrFailed, null);
+            }
+
+            try
+            {
+                var match = VersionPattern.Match(output);
+                if (!match.Success || !HasValidPrerelease(match))
+                {
+                    return new UnityCliCompatibilityResult(UnityCliCompatibility.MissingOrFailed, null);
+                }
+
+                var version = match.Value;
+                var majorComparison = CompareNumericIdentifier(match.Groups["major"].Value, "1");
+                if (majorComparison > 0)
+                {
+                    return new UnityCliCompatibilityResult(UnityCliCompatibility.UntestedNewer, version);
+                }
+
+                if (majorComparison < 0 || CompareToMinimum(match) < 0)
+                {
+                    return new UnityCliCompatibilityResult(UnityCliCompatibility.Incompatible, version);
+                }
+
+                return new UnityCliCompatibilityResult(UnityCliCompatibility.Compatible, version);
+            }
+            catch
+            {
+                return new UnityCliCompatibilityResult(UnityCliCompatibility.MissingOrFailed, null);
+            }
+        }
+
+        private static int CompareToMinimum(Match match)
+        {
+            var minorComparison = CompareNumericIdentifier(match.Groups["minor"].Value, "0");
+            if (minorComparison != 0)
+            {
+                return minorComparison;
+            }
+
+            var patchComparison = CompareNumericIdentifier(match.Groups["patch"].Value, "0");
+            if (patchComparison != 0)
+            {
+                return patchComparison;
+            }
+
+            var prerelease = match.Groups["pre"].Success ? match.Groups["pre"].Value : null;
+            if (prerelease == null)
+            {
+                return 1;
+            }
+
+            return ComparePrerelease(prerelease, "beta.2");
+        }
+
+        private static int ComparePrerelease(string left, string right)
+        {
+            var leftIdentifiers = left.Split('.');
+            var rightIdentifiers = right.Split('.');
+            var length = Math.Max(leftIdentifiers.Length, rightIdentifiers.Length);
+            for (var index = 0; index < length; index++)
+            {
+                if (index == leftIdentifiers.Length)
+                {
+                    return -1;
+                }
+
+                if (index == rightIdentifiers.Length)
+                {
+                    return 1;
+                }
+
+                var leftIsNumber = IsNumericIdentifier(leftIdentifiers[index]);
+                var rightIsNumber = IsNumericIdentifier(rightIdentifiers[index]);
+                if (leftIsNumber && rightIsNumber)
+                {
+                    var numericComparison = CompareNumericIdentifier(leftIdentifiers[index], rightIdentifiers[index]);
+                    if (numericComparison != 0)
+                    {
+                        return numericComparison;
+                    }
+
+                    continue;
+                }
+
+                if (leftIsNumber != rightIsNumber)
+                {
+                    return leftIsNumber ? -1 : 1;
+                }
+
+                var comparison = string.CompareOrdinal(leftIdentifiers[index], rightIdentifiers[index]);
+                if (comparison != 0)
+                {
+                    return comparison;
+                }
+            }
+
+            return 0;
+        }
+
+        private static bool HasValidPrerelease(Match match)
+        {
+            if (!match.Groups["pre"].Success)
+            {
+                return true;
+            }
+
+            foreach (var identifier in match.Groups["pre"].Value.Split('.'))
+            {
+                if (string.IsNullOrEmpty(identifier) ||
+                    (IsNumericIdentifier(identifier) && identifier.Length > 1 && identifier[0] == '0'))
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private static bool IsNumericIdentifier(string value)
+        {
+            if (string.IsNullOrEmpty(value))
+            {
+                return false;
+            }
+
+            for (var index = 0; index < value.Length; index++)
+            {
+                if (value[index] < '0' || value[index] > '9')
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private static int CompareNumericIdentifier(string left, string right)
+        {
+            if (left.Length != right.Length)
+            {
+                return left.Length.CompareTo(right.Length);
+            }
+
+            return string.CompareOrdinal(left, right);
+        }
+    }
+}
diff --git a/Editor/Setup/UnityCliVersionClassifier.cs.meta b/Editor/Setup/UnityCliVersionClassifier.cs.meta
new file mode 100644
index 00000000..fe321b67
--- /dev/null
+++ b/Editor/Setup/UnityCliVersionClassifier.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 39ac19caed2384cea9518b94d480df48
\ No newline at end of file
diff --git a/Editor/Tests/AssignMaterialCommandTests.cs b/Editor/Tests/AssignMaterialCommandTests.cs
new file mode 100644
index 00000000..8655f0a8
--- /dev/null
+++ b/Editor/Tests/AssignMaterialCommandTests.cs
@@ -0,0 +1,157 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using McpUnity.Extensions.Commands;
+using NUnit.Framework;
+using Unity.Pipeline;
+using Unity.Pipeline.Models;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class AssignMaterialCommandTests
+    {
+        private const string Root = "Assets/__McpUnityAssignMaterialTests";
+
+        [SetUp]
+        public void SetUp()
+        {
+            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+            EnsureFolder(Root);
+        }
+
+        [TearDown]
+        public void TearDown()
+        {
+            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+            AssetDatabase.DeleteAsset(Root);
+            AssetDatabase.Refresh();
+        }
+
+        [Test]
+        public void Assign_RejectsNonGameObjectInput()
+        {
+            var material = CreateMaterialAsset("Input");
+
+            Assert.Throws(() =>
+                AssignMaterialCommand.Assign(Ref(material), Ref(material)));
+        }
+
+        [Test]
+        public void Assign_RejectsNonMaterialInput()
+        {
+            var gameObject = CreateRendererObject("Renderer");
+
+            Assert.Throws(() =>
+                AssignMaterialCommand.Assign(Ref(gameObject), Ref(gameObject)));
+        }
+
+        [Test]
+        public void Assign_RequiresRenderer()
+        {
+            var gameObject = new GameObject("NoRenderer");
+            var material = CreateMaterialAsset("Material");
+
+            Assert.Throws(() =>
+                AssignMaterialCommand.Assign(Ref(gameObject), Ref(material)));
+        }
+
+        [TestCase(-1)]
+        [TestCase(1)]
+        public void Assign_ValidatesSlot(int slot)
+        {
+            var gameObject = CreateRendererObject("Renderer");
+            var renderer = gameObject.GetComponent();
+            renderer.sharedMaterials = new[] { CreateMaterialAsset("Original") };
+            var replacement = CreateMaterialAsset("Replacement");
+
+            Assert.Throws(() =>
+                AssignMaterialCommand.Assign(Ref(gameObject), Ref(replacement), slot));
+        }
+
+        [Test]
+        public void Assign_UpdatesSharedMaterialMarksDirtyAndReturnsIdentities()
+        {
+            var gameObject = CreateRendererObject("Renderer");
+            var renderer = gameObject.GetComponent();
+            renderer.sharedMaterials = new[] { CreateMaterialAsset("Original") };
+            var replacement = CreateMaterialAsset("Replacement");
+
+            object result = null;
+            Assert.DoesNotThrow(() =>
+                result = AssignMaterialCommand.Assign(Ref(gameObject), Ref(replacement)));
+
+            Assert.That(renderer.sharedMaterials[0], Is.SameAs(replacement));
+            Assert.That(EditorUtility.IsDirty(renderer), Is.True);
+            Assert.That(Property(result, "Slot"), Is.EqualTo(0));
+            Assert.That(Property(result, "GameObject").InstanceId,
+                Is.EqualTo(PipelineUtils.GetObjectId(gameObject)));
+            Assert.That(Property(result, "Material").Guid, Is.Not.Empty);
+        }
+
+        [Test]
+        public void Assign_RecordsPrefabInstancePropertyModification()
+        {
+            var source = CreateRendererObject("PrefabSource");
+            source.GetComponent().sharedMaterials = new[] { CreateMaterialAsset("Original") };
+            var prefabPath = Root + "/Renderer.prefab";
+            var prefab = PrefabUtility.SaveAsPrefabAsset(source, prefabPath);
+            UnityEngine.Object.DestroyImmediate(source);
+            var instance = (GameObject)PrefabUtility.InstantiatePrefab(prefab);
+            var replacement = CreateMaterialAsset("Replacement");
+
+            Assert.DoesNotThrow(() =>
+                AssignMaterialCommand.Assign(Ref(instance), Ref(replacement)));
+
+            var modifications = PrefabUtility.GetPropertyModifications(instance.GetComponent());
+            Assert.That(modifications, Is.Not.Null);
+            Assert.That(
+                modifications.Any(modification =>
+                    modification.propertyPath == "m_Materials.Array.data[0]"),
+                Is.True);
+        }
+
+        private static GameObject CreateRendererObject(string name)
+        {
+            var gameObject = new GameObject(name);
+            gameObject.AddComponent();
+            return gameObject;
+        }
+
+        private static Material CreateMaterialAsset(string name)
+        {
+            var shader = Shader.Find("Standard") ??
+                         Shader.Find("Sprites/Default") ??
+                         Shader.Find("Hidden/InternalErrorShader");
+            Assert.That(shader, Is.Not.Null);
+            var material = new Material(shader);
+            AssetDatabase.CreateAsset(material, Root + "/" + name + ".mat");
+            return material;
+        }
+
+        private static ObjectRef Ref(UnityEngine.Object obj) =>
+            new ObjectRef { InstanceId = PipelineUtils.GetObjectId(obj) };
+
+        private static T Property(object instance, string name)
+        {
+            Assert.That(instance, Is.Not.Null);
+            var property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
+            Assert.That(property, Is.Not.Null, $"Expected public property '{name}'.");
+            return (T)property.GetValue(instance);
+        }
+
+        private static void EnsureFolder(string path)
+        {
+            if (AssetDatabase.IsValidFolder(path))
+                return;
+
+            var parent = Path.GetDirectoryName(path)?.Replace('\\', '/');
+            if (!string.IsNullOrEmpty(parent) && !AssetDatabase.IsValidFolder(parent))
+                EnsureFolder(parent);
+            AssetDatabase.CreateFolder(parent, Path.GetFileName(path));
+        }
+    }
+}
diff --git a/Editor/Tests/AssignMaterialCommandTests.cs.meta b/Editor/Tests/AssignMaterialCommandTests.cs.meta
new file mode 100644
index 00000000..c194864e
--- /dev/null
+++ b/Editor/Tests/AssignMaterialCommandTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1a7a1c2c47f64ead9f44d1898230fba7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Editor/Tests/BatchExecuteToolTests.cs b/Editor/Tests/BatchExecuteToolTests.cs
deleted file mode 100644
index 585e444f..00000000
--- a/Editor/Tests/BatchExecuteToolTests.cs
+++ /dev/null
@@ -1,501 +0,0 @@
-using System.Collections;
-using System.Threading.Tasks;
-using NUnit.Framework;
-using McpUnity.Tools;
-using McpUnity.Unity;
-using UnityEngine;
-using UnityEngine.TestTools;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tests
-{
-    /// 
-    /// Tests for BatchExecuteTool functionality
-    /// 
-    public class BatchExecuteToolTests
-    {
-        private BatchExecuteTool _batchTool;
-        private GameObject _testObject;
-
-        [SetUp]
-        public void SetUp()
-        {
-            _batchTool = new BatchExecuteTool(name =>
-                name == "get_scene_info" ? new GetSceneInfoTool() : null);
-        }
-
-        [TearDown]
-        public void TearDown()
-        {
-            // Clean up any test objects
-            if (_testObject != null)
-            {
-                Object.DestroyImmediate(_testObject);
-                _testObject = null;
-            }
-        }
-
-        #region Basic Properties Tests
-
-        [Test]
-        public void BatchExecuteTool_HasCorrectName()
-        {
-            Assert.AreEqual("batch_execute", _batchTool.Name);
-        }
-
-        [Test]
-        public void BatchExecuteTool_IsAsync()
-        {
-            Assert.IsTrue(_batchTool.IsAsync, "BatchExecuteTool should be async");
-        }
-
-        [Test]
-        public void BatchExecuteTool_HasDescription()
-        {
-            Assert.IsNotNull(_batchTool.Description);
-            Assert.IsTrue(_batchTool.Description.Contains("batch"), "Description should mention batch");
-        }
-
-        #endregion
-
-        #region Validation Tests
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_WithEmptyOperations_ReturnsError()
-        {
-            // Arrange
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray()
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error for empty operations");
-            Assert.IsTrue(result["error"]["message"].ToString().Contains("operations"),
-                "Error should mention operations");
-        }
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_WithNullOperations_ReturnsError()
-        {
-            // Arrange
-            JObject parameters = new JObject();
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error for null operations");
-        }
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_WithTooManyOperations_ReturnsError()
-        {
-            // Arrange
-            JArray operations = new JArray();
-            for (int i = 0; i < 101; i++)
-            {
-                operations.Add(new JObject
-                {
-                    ["tool"] = "get_scene_info",
-                    ["params"] = new JObject()
-                });
-            }
-
-            JObject parameters = new JObject
-            {
-                ["operations"] = operations
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error for too many operations");
-            Assert.IsTrue(result["error"]["message"].ToString().Contains("100"),
-                "Error should mention limit");
-        }
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_WithNestedBatchExecute_ReturnsError()
-        {
-            // Arrange
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "batch_execute",
-                        ["params"] = new JObject
-                        {
-                            ["operations"] = new JArray()
-                        }
-                    }
-                }
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert - Should fail because of nested batch_execute
-            Assert.IsFalse(result["success"]?.ToObject() ?? true, "Should fail for nested batch");
-        }
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_WithUnknownTool_ReturnsError()
-        {
-            // Arrange
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "nonexistent_tool_12345",
-                        ["params"] = new JObject()
-                    }
-                },
-                ["stopOnError"] = true
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            Assert.IsFalse(result["success"]?.ToObject() ?? true, "Should fail for unknown tool");
-            Assert.IsNotNull(result["results"], "Should have results array");
-            JArray results = result["results"] as JArray;
-            Assert.AreEqual(1, results.Count);
-            Assert.IsFalse(results[0]["success"]?.ToObject() ?? true);
-            Assert.IsTrue(results[0]["error"]?.ToString().Contains("Unknown tool"));
-        }
-
-        #endregion
-
-        #region Successful Execution Tests
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_WithSingleOperation_Succeeds()
-        {
-            // Arrange - get_scene_info requires no parameters and always succeeds
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "get_scene_info",
-                        ["params"] = new JObject(),
-                        ["id"] = "op1"
-                    }
-                }
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            Assert.IsTrue(result["success"]?.ToObject() ?? false, "Should succeed");
-            Assert.IsNotNull(result["results"], "Should have results array");
-            Assert.IsNotNull(result["summary"], "Should have summary");
-
-            JObject summary = result["summary"] as JObject;
-            Assert.AreEqual(1, summary["total"]?.ToObject());
-            Assert.AreEqual(1, summary["succeeded"]?.ToObject());
-            Assert.AreEqual(0, summary["failed"]?.ToObject());
-        }
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_WithMultipleOperations_Succeeds()
-        {
-            // Arrange - Use operations that don't require external dependencies
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "get_scene_info",
-                        ["params"] = new JObject(),
-                        ["id"] = "op1"
-                    },
-                    new JObject
-                    {
-                        ["tool"] = "get_scene_info",
-                        ["params"] = new JObject(),
-                        ["id"] = "op2"
-                    }
-                }
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            Assert.IsTrue(result["success"]?.ToObject() ?? false, "Should succeed");
-
-            JObject summary = result["summary"] as JObject;
-            Assert.AreEqual(2, summary["total"]?.ToObject());
-            Assert.AreEqual(2, summary["succeeded"]?.ToObject());
-            Assert.AreEqual(0, summary["failed"]?.ToObject());
-
-            JArray results = result["results"] as JArray;
-            Assert.AreEqual(2, results.Count);
-            Assert.AreEqual("op1", results[0]["id"]?.ToString());
-            Assert.AreEqual("op2", results[1]["id"]?.ToString());
-        }
-
-        #endregion
-
-        #region StopOnError Tests
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_StopOnErrorTrue_StopsAtFirstError()
-        {
-            // Arrange - First operation fails, second should not execute
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "nonexistent_tool",
-                        ["params"] = new JObject(),
-                        ["id"] = "fail1"
-                    },
-                    new JObject
-                    {
-                        ["tool"] = "get_scene_info",
-                        ["params"] = new JObject(),
-                        ["id"] = "should_not_run"
-                    }
-                },
-                ["stopOnError"] = true
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            Assert.IsFalse(result["success"]?.ToObject() ?? true, "Should fail");
-
-            JObject summary = result["summary"] as JObject;
-            Assert.AreEqual(2, summary["total"]?.ToObject());
-            Assert.AreEqual(0, summary["succeeded"]?.ToObject());
-            Assert.AreEqual(1, summary["failed"]?.ToObject());
-            Assert.AreEqual(1, summary["executed"]?.ToObject(), "Should only execute 1 operation");
-        }
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_StopOnErrorFalse_ContinuesAfterError()
-        {
-            // Arrange - First operation fails, but should continue
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "nonexistent_tool",
-                        ["params"] = new JObject(),
-                        ["id"] = "fail1"
-                    },
-                    new JObject
-                    {
-                        ["tool"] = "get_scene_info",
-                        ["params"] = new JObject(),
-                        ["id"] = "should_run"
-                    }
-                },
-                ["stopOnError"] = false
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert - Should have partial success
-            Assert.IsFalse(result["success"]?.ToObject() ?? true, "Overall should fail");
-
-            JObject summary = result["summary"] as JObject;
-            Assert.AreEqual(2, summary["total"]?.ToObject());
-            Assert.AreEqual(1, summary["succeeded"]?.ToObject(), "Second operation should succeed");
-            Assert.AreEqual(1, summary["failed"]?.ToObject());
-            Assert.AreEqual(2, summary["executed"]?.ToObject(), "Should execute both operations");
-        }
-
-        #endregion
-
-        #region Response Format Tests
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_ResponseContainsRequiredFields()
-        {
-            // Arrange
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "get_scene_info",
-                        ["params"] = new JObject()
-                    }
-                }
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert - Check all required fields are present
-            Assert.IsNotNull(result["success"], "Response should have 'success' field");
-            Assert.IsNotNull(result["type"], "Response should have 'type' field");
-            Assert.IsNotNull(result["message"], "Response should have 'message' field");
-            Assert.IsNotNull(result["results"], "Response should have 'results' field");
-            Assert.IsNotNull(result["summary"], "Response should have 'summary' field");
-
-            JObject summary = result["summary"] as JObject;
-            Assert.IsNotNull(summary["total"], "Summary should have 'total' field");
-            Assert.IsNotNull(summary["succeeded"], "Summary should have 'succeeded' field");
-            Assert.IsNotNull(summary["failed"], "Summary should have 'failed' field");
-            Assert.IsNotNull(summary["executed"], "Summary should have 'executed' field");
-        }
-
-        [UnityTest]
-        public IEnumerator BatchExecuteTool_OperationResultsHaveCorrectFormat()
-        {
-            // Arrange
-            JObject parameters = new JObject
-            {
-                ["operations"] = new JArray
-                {
-                    new JObject
-                    {
-                        ["tool"] = "get_scene_info",
-                        ["params"] = new JObject(),
-                        ["id"] = "custom_id"
-                    }
-                }
-            };
-
-            var tcs = new TaskCompletionSource();
-
-            // Act
-            _batchTool.ExecuteAsync(parameters, tcs);
-
-            while (!tcs.Task.IsCompleted)
-            {
-                yield return null;
-            }
-
-            JObject result = tcs.Task.Result;
-
-            // Assert
-            JArray results = result["results"] as JArray;
-            Assert.AreEqual(1, results.Count);
-
-            JObject opResult = results[0] as JObject;
-            Assert.IsNotNull(opResult["index"], "Operation result should have 'index' field");
-            Assert.IsNotNull(opResult["id"], "Operation result should have 'id' field");
-            Assert.IsNotNull(opResult["success"], "Operation result should have 'success' field");
-            Assert.AreEqual("custom_id", opResult["id"]?.ToString());
-            Assert.AreEqual(0, opResult["index"]?.ToObject());
-        }
-
-        #endregion
-    }
-}
diff --git a/Editor/Tests/BatchExecuteToolTests.cs.meta b/Editor/Tests/BatchExecuteToolTests.cs.meta
deleted file mode 100644
index ad371822..00000000
--- a/Editor/Tests/BatchExecuteToolTests.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: d9fbcf3701ae045a38915e6104c5b58b
\ No newline at end of file
diff --git a/Editor/Tests/CommandDiscoveryTests.cs b/Editor/Tests/CommandDiscoveryTests.cs
new file mode 100644
index 00000000..b259b4aa
--- /dev/null
+++ b/Editor/Tests/CommandDiscoveryTests.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using NUnit.Framework;
+using Unity.Pipeline.Commands;
+using Unity.Pipeline.Editor;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class CommandDiscoveryTests
+    {
+        private static readonly string[] ExpectedNames =
+        {
+            "assign_material",
+            "duplicate_gameobject",
+            "editor_step",
+            "inspect_gameobject",
+            "unload_scene"
+        };
+
+        [SetUp]
+        public void SetUp()
+        {
+            CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
+        }
+
+        [Test]
+        public void ExtensionAssembly_DeclaresExactlyFivePublicStaticCommandsWithExactNames()
+        {
+            var methods = FindExtensionCommandMethods();
+
+            Assert.That(methods, Has.Count.EqualTo(ExpectedNames.Length));
+            Assert.That(
+                methods.Select(method => method.GetCustomAttribute().Name),
+                Is.EquivalentTo(ExpectedNames));
+            Assert.That(methods, Has.All.Matches(method => method.IsPublic && method.IsStatic));
+        }
+
+        [Test]
+        public void PipelineDiscovery_ContainsEachExtensionCommandExactlyOnce()
+        {
+            var discoveredNames = CommandRegistry.DiscoverCommands()
+                .Select(command => command.Name)
+                .ToList();
+
+            foreach (var expectedName in ExpectedNames)
+            {
+                Assert.That(
+                    discoveredNames.Count(name => string.Equals(name, expectedName, StringComparison.Ordinal)),
+                    Is.EqualTo(1),
+                    $"Pipeline discovery should contain exactly one '{expectedName}' command.");
+            }
+        }
+
+        private static List FindExtensionCommandMethods()
+        {
+            var methods = new List();
+            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+            {
+                foreach (var type in GetLoadableTypes(assembly))
+                {
+                    if (type.Namespace == null ||
+                        !type.Namespace.StartsWith("McpUnity.Extensions", StringComparison.Ordinal))
+                    {
+                        continue;
+                    }
+
+                    methods.AddRange(type
+                        .GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
+                        .Where(method => method.GetCustomAttribute() != null));
+                }
+            }
+
+            return methods;
+        }
+
+        private static IEnumerable GetLoadableTypes(Assembly assembly)
+        {
+            try
+            {
+                return assembly.GetTypes();
+            }
+            catch (ReflectionTypeLoadException exception)
+            {
+                return exception.Types.Where(type => type != null);
+            }
+        }
+    }
+}
diff --git a/Editor/Tests/CommandDiscoveryTests.cs.meta b/Editor/Tests/CommandDiscoveryTests.cs.meta
new file mode 100644
index 00000000..2c809b4b
--- /dev/null
+++ b/Editor/Tests/CommandDiscoveryTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 29f9b791e34b4d19ab7081587377a5ef
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Editor/Tests/DuplicateGameObjectCommandTests.cs b/Editor/Tests/DuplicateGameObjectCommandTests.cs
new file mode 100644
index 00000000..ef638cae
--- /dev/null
+++ b/Editor/Tests/DuplicateGameObjectCommandTests.cs
@@ -0,0 +1,183 @@
+using System;
+using System.Collections;
+using McpUnity.Extensions.Commands;
+using NUnit.Framework;
+using Unity.Pipeline;
+using Unity.Pipeline.Editor.Authoring;
+using Unity.Pipeline.Models;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using UnityEngine.TestTools;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class DuplicateGameObjectCommandTests
+    {
+        private const string ActiveScenePath = "Assets/__McpUnityDuplicateActiveScene.unity";
+
+        [SetUp]
+        public void SetUp()
+        {
+            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+            Undo.ClearAll();
+        }
+
+        [TearDown]
+        public void TearDown()
+        {
+            Undo.ClearAll();
+            AssetDatabase.DeleteAsset(ActiveScenePath);
+        }
+
+        [Test]
+        public void Duplicate_RejectsNonGameObjectSource()
+        {
+            var material = new Material(FindShader());
+            try
+            {
+                Assert.Throws(() =>
+                    DuplicateGameObjectCommand.Duplicate(Ref(material)));
+            }
+            finally
+            {
+                UnityEngine.Object.DestroyImmediate(material);
+            }
+        }
+
+        [Test]
+        public void Duplicate_PreservesSourceSceneWhenParentIsOmitted()
+        {
+            var source = new GameObject("Source");
+            source.transform.position = new Vector3(3f, 4f, 5f);
+
+            AuthoringResult result = null;
+            Assert.DoesNotThrow(() =>
+                result = DuplicateGameObjectCommand.Duplicate(Ref(source), name: "Copy"));
+
+            Assert.That(ObjectResolver.TryResolve(ToRef(result), out var resolved, out var error), Is.True, error);
+            var duplicate = (GameObject)resolved;
+            Assert.That(duplicate, Is.Not.SameAs(source));
+            Assert.That(duplicate.name, Is.EqualTo("Copy"));
+            Assert.That(duplicate.scene, Is.EqualTo(source.scene));
+            Assert.That(duplicate.transform.position, Is.EqualTo(source.transform.position));
+        }
+
+        [Test]
+        public void Duplicate_LeavesNameUnchangedWhenRenameIsOmitted()
+        {
+            var source = new GameObject("Source");
+
+            var result = DuplicateGameObjectCommand.Duplicate(Ref(source));
+
+            Assert.That(ObjectResolver.TryResolve(ToRef(result), out var resolved, out var error), Is.True, error);
+            Assert.That(resolved.name, Is.EqualTo(source.name));
+        }
+
+        [Test]
+        public void Duplicate_AppliesOptionalParentWithWorldPositionStaysFalseByDefault()
+        {
+            var source = new GameObject("Source");
+            source.transform.localPosition = new Vector3(2f, 0f, 0f);
+            var parent = new GameObject("Parent");
+            parent.transform.position = new Vector3(10f, 0f, 0f);
+
+            AuthoringResult result = null;
+            Assert.DoesNotThrow(() =>
+                result = DuplicateGameObjectCommand.Duplicate(
+                    Ref(source),
+                    parent: Ref(parent),
+                    name: "ChildCopy"));
+
+            Assert.That(ObjectResolver.TryResolve(ToRef(result), out var resolved, out var error), Is.True, error);
+            var duplicate = (GameObject)resolved;
+            Assert.That(duplicate.transform.parent, Is.SameAs(parent.transform));
+            Assert.That(duplicate.transform.localPosition, Is.EqualTo(source.transform.localPosition));
+            Assert.That(duplicate.name, Is.EqualTo("ChildCopy"));
+        }
+
+        [Test]
+        public void Duplicate_RegistersCreatedObjectWithUndo()
+        {
+            var source = new GameObject("Source");
+
+            AuthoringResult result = null;
+            Assert.DoesNotThrow(() => result = DuplicateGameObjectCommand.Duplicate(Ref(source)));
+            Assert.That(result.InstanceId, Is.Not.Null);
+            Assert.That(PipelineUtils.IdToObject(result.InstanceId.Value), Is.Not.Null);
+
+            Undo.PerformUndo();
+
+            Assert.That(PipelineUtils.IdToObject(result.InstanceId.Value), Is.Null);
+            Assert.That(source, Is.Not.Null, "Undo should remove only the duplicate.");
+        }
+
+        [UnityTest]
+        public IEnumerator Duplicate_UndoRedoRestoresConfiguredStateFromNonActiveSourceScene()
+        {
+            var activeScene = SceneManager.GetActiveScene();
+            Assert.That(EditorSceneManager.SaveScene(activeScene, ActiveScenePath), Is.True);
+            var sourceScene = EditorSceneManager.NewScene(
+                NewSceneSetup.EmptyScene,
+                NewSceneMode.Additive);
+            Assert.That(SceneManager.SetActiveScene(activeScene), Is.True);
+
+            var parent = new GameObject("DestinationParent");
+            parent.transform.position = new Vector3(10f, 20f, 30f);
+
+            var source = new GameObject("Source");
+            source.transform.localPosition = new Vector3(1f, 2f, 3f);
+            source.transform.localRotation = Quaternion.Euler(15f, 25f, 35f);
+            source.transform.localScale = new Vector3(2f, 3f, 4f);
+            SceneManager.MoveGameObjectToScene(source, sourceScene);
+            Assert.That(SceneManager.GetActiveScene(), Is.Not.EqualTo(sourceScene));
+
+            DuplicateGameObjectCommand.Duplicate(
+                Ref(source),
+                parent: Ref(parent),
+                name: "RestoredCopy");
+            yield return null;
+
+            var duplicate = parent.transform.Find("RestoredCopy");
+            Assert.That(duplicate, Is.Not.Null);
+            AssertConfiguredState(duplicate, activeScene, parent.transform, source.transform);
+
+            Undo.PerformUndo();
+            yield return null;
+            Assert.That(parent.transform.Find("RestoredCopy"), Is.Null);
+
+            Undo.PerformRedo();
+            yield return null;
+            var restored = parent.transform.Find("RestoredCopy");
+            Assert.That(restored, Is.Not.Null);
+            AssertConfiguredState(restored, activeScene, parent.transform, source.transform);
+        }
+
+        private static void AssertConfiguredState(
+            Transform actual,
+            Scene expectedScene,
+            Transform expectedParent,
+            Transform expectedTransform)
+        {
+            Assert.That(actual.gameObject.scene, Is.EqualTo(expectedScene));
+            Assert.That(actual.parent, Is.SameAs(expectedParent));
+            Assert.That(actual.name, Is.EqualTo("RestoredCopy"));
+            Assert.That(actual.localPosition, Is.EqualTo(expectedTransform.localPosition));
+            Assert.That(Quaternion.Angle(actual.localRotation, expectedTransform.localRotation),
+                Is.LessThan(0.001f));
+            Assert.That(actual.localScale, Is.EqualTo(expectedTransform.localScale));
+        }
+
+        private static ObjectRef Ref(UnityEngine.Object obj) =>
+            new ObjectRef { InstanceId = PipelineUtils.GetObjectId(obj) };
+
+        private static ObjectRef ToRef(AuthoringResult result) =>
+            new ObjectRef { InstanceId = result.InstanceId };
+
+        private static Shader FindShader() =>
+            Shader.Find("Standard") ??
+            Shader.Find("Sprites/Default") ??
+            Shader.Find("Hidden/InternalErrorShader");
+    }
+}
diff --git a/Editor/Tests/DuplicateGameObjectCommandTests.cs.meta b/Editor/Tests/DuplicateGameObjectCommandTests.cs.meta
new file mode 100644
index 00000000..e464d0bf
--- /dev/null
+++ b/Editor/Tests/DuplicateGameObjectCommandTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d2b6ee93ac4042b4891bb222765ca406
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Editor/Tests/EditorStepCommandTests.cs b/Editor/Tests/EditorStepCommandTests.cs
new file mode 100644
index 00000000..b725677e
--- /dev/null
+++ b/Editor/Tests/EditorStepCommandTests.cs
@@ -0,0 +1,17 @@
+using System;
+using McpUnity.Extensions.Commands;
+using NUnit.Framework;
+using UnityEditor;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class EditorStepCommandTests
+    {
+        [Test]
+        public void Step_RejectsOutsidePlayMode()
+        {
+            Assert.That(EditorApplication.isPlaying, Is.False);
+            Assert.Throws(() => EditorStepCommand.Step());
+        }
+    }
+}
diff --git a/Editor/Tests/EditorStepCommandTests.cs.meta b/Editor/Tests/EditorStepCommandTests.cs.meta
new file mode 100644
index 00000000..fc3ef546
--- /dev/null
+++ b/Editor/Tests/EditorStepCommandTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4293364d13db4bd18ed39c29ed865d4b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Editor/Tests/GetGameObjectResourceTests.cs b/Editor/Tests/GetGameObjectResourceTests.cs
deleted file mode 100644
index 99378710..00000000
--- a/Editor/Tests/GetGameObjectResourceTests.cs
+++ /dev/null
@@ -1,176 +0,0 @@
-using NUnit.Framework;
-using McpUnity.Resources;
-using Newtonsoft.Json.Linq;
-using UnityEngine;
-
-namespace McpUnity.Tests
-{
-    /// 
-    /// Regression tests for GameObject serialization safety.
-    /// 
-    public class GetGameObjectResourceTests
-    {
-        private GameObject _testObject;
-
-        [SetUp]
-        public void SetUp()
-        {
-            _testObject = new GameObject("GetGameObjectResourceTests_Object");
-        }
-
-        [TearDown]
-        public void TearDown()
-        {
-            if (_testObject != null)
-            {
-                Object.DestroyImmediate(_testObject);
-                _testObject = null;
-            }
-        }
-
-        [Test]
-        public void GameObjectToJObject_WithCollider_SkipsDetailedPropertiesForSafety()
-        {
-            // Arrange
-            _testObject.AddComponent();
-
-            // Act
-            JObject result = GetGameObjectResource.GameObjectToJObject(_testObject, true);
-
-            // Assert
-            Assert.IsNotNull(result);
-
-            JArray components = (JArray)result["components"];
-            Assert.IsNotNull(components);
-
-            JObject colliderJson = null;
-            foreach (JToken component in components)
-            {
-                if (component?["type"]?.ToString() == nameof(BoxCollider))
-                {
-                    colliderJson = (JObject)component;
-                    break;
-                }
-            }
-
-            Assert.IsNotNull(colliderJson, "Expected serialized component list to include BoxCollider.");
-            Assert.AreEqual(true, colliderJson["enabled"]?.ToObject());
-            Assert.AreEqual(
-                "Detailed property serialization skipped for safety",
-                colliderJson["properties"]?["_skipped"]?.ToString());
-        }
-
-        [Test]
-        public void GameObjectToJObject_WithMaxDepthZero_HasNoChildrenAndIsTruncated()
-        {
-            // Arrange: parent with one child.
-            var child = new GameObject("Child");
-            child.transform.SetParent(_testObject.transform);
-
-            try
-            {
-                // Act
-                JObject result = GetGameObjectResource.GameObjectToJObject(
-                    _testObject, includeDetailedComponents: true, maxDepth: 0);
-
-                // Assert
-                Assert.IsNotNull(result);
-                JArray children = (JArray)result["children"];
-                Assert.IsNotNull(children);
-                Assert.AreEqual(0, children.Count, "maxDepth=0 should not serialize any children.");
-                Assert.AreEqual(true, result["_truncated"]?.ToObject());
-                Assert.AreEqual("depth_limit", result["_truncatedReason"]?.ToString());
-                Assert.AreEqual(1, result["_childCount"]?.ToObject());
-            }
-            finally
-            {
-                Object.DestroyImmediate(child);
-            }
-        }
-
-        [Test]
-        public void GameObjectToJObject_WithMaxDepthOne_StopsAtGrandchildren()
-        {
-            // Arrange: parent → child → grandchild.
-            var child = new GameObject("Child");
-            child.transform.SetParent(_testObject.transform);
-            var grandchild = new GameObject("Grandchild");
-            grandchild.transform.SetParent(child.transform);
-
-            try
-            {
-                // Act
-                JObject result = GetGameObjectResource.GameObjectToJObject(
-                    _testObject, includeDetailedComponents: true, maxDepth: 1);
-
-                // Assert: parent has child, but child must be flagged truncated and have no grandchildren.
-                Assert.IsNotNull(result);
-                Assert.IsNull(result["_truncated"], "Root should not be truncated when its own children fit at maxDepth=1.");
-
-                JArray children = (JArray)result["children"];
-                Assert.AreEqual(1, children.Count);
-
-                JObject childJson = (JObject)children[0];
-                Assert.AreEqual("Child", childJson["name"]?.ToString());
-
-                JArray grandchildren = (JArray)childJson["children"];
-                Assert.AreEqual(0, grandchildren.Count, "Grandchildren should be omitted at depth=1.");
-                Assert.AreEqual(true, childJson["_truncated"]?.ToObject());
-                Assert.AreEqual("depth_limit", childJson["_truncatedReason"]?.ToString());
-                Assert.AreEqual(1, childJson["_childCount"]?.ToObject());
-            }
-            finally
-            {
-                Object.DestroyImmediate(grandchild);
-                Object.DestroyImmediate(child);
-            }
-        }
-
-        [Test]
-        public void GameObjectToJObject_WithIncludeComponentsFalse_OmitsComponents()
-        {
-            // Arrange
-            _testObject.AddComponent();
-
-            // Act
-            JObject result = GetGameObjectResource.GameObjectToJObject(
-                _testObject, includeDetailedComponents: true, includeComponents: false);
-
-            // Assert
-            Assert.IsNotNull(result);
-            Assert.IsFalse(result.ContainsKey("components"),
-                "Expected 'components' key to be omitted when includeComponents=false.");
-        }
-
-        [Test]
-        public void GameObjectToJObject_WithIncludeComponentPropertiesFalse_OmitsProperties()
-        {
-            // Arrange
-            _testObject.AddComponent();
-
-            // Act
-            JObject result = GetGameObjectResource.GameObjectToJObject(
-                _testObject, includeDetailedComponents: true, includeComponentProperties: false);
-
-            // Assert
-            JArray components = (JArray)result["components"];
-            Assert.IsNotNull(components);
-
-            JObject rigidbodyJson = null;
-            foreach (JToken component in components)
-            {
-                if (component?["type"]?.ToString() == nameof(Rigidbody))
-                {
-                    rigidbodyJson = (JObject)component;
-                    break;
-                }
-            }
-
-            Assert.IsNotNull(rigidbodyJson, "Expected Rigidbody to be listed.");
-            Assert.IsNotNull(rigidbodyJson["type"]);
-            Assert.IsNotNull(rigidbodyJson["enabled"]);
-            Assert.IsFalse(rigidbodyJson.ContainsKey("properties"),
-                "Expected 'properties' to be omitted on each component when includeComponentProperties=false.");
-        }
-    }
-}
diff --git a/Editor/Tests/GetGameObjectResourceTests.cs.meta b/Editor/Tests/GetGameObjectResourceTests.cs.meta
deleted file mode 100644
index 059123b8..00000000
--- a/Editor/Tests/GetGameObjectResourceTests.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 97b7ccdbc03964341937c2f49e6a2186
diff --git a/Editor/Tests/InspectionCommandTests.cs b/Editor/Tests/InspectionCommandTests.cs
new file mode 100644
index 00000000..6831a926
--- /dev/null
+++ b/Editor/Tests/InspectionCommandTests.cs
@@ -0,0 +1,683 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using McpUnity.Extensions.Commands;
+using Newtonsoft.Json;
+using NUnit.Framework;
+using Unity.Pipeline;
+using Unity.Pipeline.Models;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class InspectionCommandTests
+    {
+        [SetUp]
+        public void SetUp()
+        {
+            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+        }
+
+        [Test]
+        public void Inspect_ReturnsIdentityStateTransformAndComponentSummaries()
+        {
+            var target = new GameObject("Inspectable")
+            {
+                layer = 5,
+                tag = "Untagged",
+                isStatic = true
+            };
+            target.transform.localPosition = new Vector3(1f, 2f, 3f);
+            target.AddComponent();
+            target.SetActive(false);
+
+            object result = null;
+            Assert.DoesNotThrow(() => result = InspectGameObjectCommand.Inspect(Ref(target)));
+
+            var root = Property(result, "Root");
+            Assert.That(Property(root, "Name"), Is.EqualTo("Inspectable"));
+            Assert.That(Property(root, "Path"), Is.EqualTo("/Inspectable"));
+            Assert.That(Property(root, "ActiveSelf"), Is.False);
+            Assert.That(Property(root, "ActiveInHierarchy"), Is.False);
+            Assert.That(Property(root, "Layer"), Is.EqualTo(5));
+            Assert.That(Property(root, "Tag"), Is.EqualTo("Untagged"));
+            Assert.That(Property(root, "IsStatic"), Is.True);
+
+            var identity = Property(root, "Identity");
+            Assert.That(identity.InstanceId, Is.EqualTo(PipelineUtils.GetObjectId(target)));
+
+            var transform = Property(root, "Transform");
+            var localPosition = Property(transform, "LocalPosition");
+            Assert.That(Property(localPosition, "X"), Is.EqualTo(1f));
+            Assert.That(Property(localPosition, "Y"), Is.EqualTo(2f));
+            Assert.That(Property(localPosition, "Z"), Is.EqualTo(3f));
+
+            var components = Property(root, "Components");
+            Assert.That(
+                components.Cast().Select(component => Property(component, "Type")),
+                Does.Contain(nameof(BoxCollider)));
+        }
+
+        [Test]
+        public void Inspect_ClampsDepthToZeroAndMarksOmittedChildren()
+        {
+            var root = new GameObject("Root");
+            var child = new GameObject("Child");
+            child.transform.SetParent(root.transform);
+
+            object result = null;
+            Assert.DoesNotThrow(() =>
+                result = InspectGameObjectCommand.Inspect(Ref(root), maxDepth: -20));
+
+            Assert.That(Property(result, "MaxDepth"), Is.EqualTo(0));
+            var rootNode = Property(result, "Root");
+            Assert.That(Property(rootNode, "Children"), Is.Empty);
+            Assert.That(Property(rootNode, "ChildrenTruncated"), Is.True);
+            Assert.That(Property(result, "PayloadTruncated"), Is.True);
+        }
+
+        [Test]
+        public void Inspect_ClampsNodeLimitAndMarksNodeTruncation()
+        {
+            var root = new GameObject("Root");
+            new GameObject("First").transform.SetParent(root.transform);
+            new GameObject("Second").transform.SetParent(root.transform);
+
+            object result = null;
+            Assert.DoesNotThrow(() =>
+                result = InspectGameObjectCommand.Inspect(Ref(root), maxNodes: 0));
+
+            Assert.That(Property(result, "MaxNodes"), Is.EqualTo(1));
+            Assert.That(Property(result, "NodesReturned"), Is.EqualTo(1));
+            Assert.That(Property(result, "NodeLimitReached"), Is.True);
+            Assert.That(Property(result, "PayloadTruncated"), Is.True);
+            Assert.That(Property(Property(result, "Root"), "ChildrenTruncated"), Is.True);
+        }
+
+        [Test]
+        public void Inspect_BoundsDeepAndWideHierarchiesWithHonestMarkers()
+        {
+            var root = new GameObject("Root");
+            var parent = root.transform;
+            for (var depth = 0; depth < 100; depth++)
+            {
+                var child = new GameObject($"Deep-{depth}");
+                child.transform.SetParent(parent);
+                parent = child.transform;
+            }
+            for (var index = 0; index < 2000; index++)
+                new GameObject($"Wide-{index}").transform.SetParent(root.transform);
+
+            var result = InspectGameObjectCommand.Inspect(
+                Ref(root),
+                maxDepth: 999,
+                maxNodes: 9999,
+                includeComponents: false);
+            var serializedBytes = System.Text.Encoding.UTF8.GetByteCount(
+                JsonConvert.SerializeObject(result));
+
+            Assert.That(result.MaxDepth, Is.EqualTo(8));
+            Assert.That(result.MaxNodes, Is.EqualTo(1000));
+            Assert.That(result.NodesReturned, Is.LessThanOrEqualTo(1000));
+            Assert.That(result.PayloadTruncated, Is.True);
+            Assert.That(serializedBytes, Is.LessThanOrEqualTo(512 * 1024));
+            Assert.That(
+                Flatten(result.Root).Any(node => node.ChildrenTruncated),
+                Is.True);
+        }
+
+        [Test]
+        public void Inspect_ClampsUpperBoundsAndUsesDocumentedDefaults()
+        {
+            var root = new GameObject("Root");
+
+            object defaults = null;
+            Assert.DoesNotThrow(() => defaults = InspectGameObjectCommand.Inspect(Ref(root)));
+            Assert.That(Property(defaults, "MaxDepth"), Is.EqualTo(2));
+            Assert.That(Property(defaults, "MaxNodes"), Is.EqualTo(200));
+            Assert.That(Property(defaults, "MaxPropertiesPerComponent"), Is.EqualTo(100));
+
+            object clamped = null;
+            Assert.DoesNotThrow(() =>
+                clamped = InspectGameObjectCommand.Inspect(
+                    Ref(root),
+                    maxDepth: 99,
+                    maxNodes: 5000,
+                    maxPropertiesPerComponent: 999));
+            Assert.That(Property(clamped, "MaxDepth"), Is.EqualTo(8));
+            Assert.That(Property(clamped, "MaxNodes"), Is.EqualTo(1000));
+            Assert.That(Property(clamped, "MaxPropertiesPerComponent"), Is.EqualTo(200));
+        }
+
+        [Test]
+        public void Inspect_BoundsSerializedPropertiesAndSkipsScriptReference()
+        {
+            var root = new GameObject("Root");
+            root.AddComponent();
+
+            object result = null;
+            Assert.DoesNotThrow(() =>
+                result = InspectGameObjectCommand.Inspect(
+                    Ref(root),
+                    includeProperties: true,
+                    maxPropertiesPerComponent: 1));
+
+            var components = Property(Property(result, "Root"), "Components");
+            var fixture = components.Cast()
+                .Single(component => Property(component, "Type") == nameof(InspectionFixtureComponent));
+            var properties = Property(fixture, "Properties");
+
+            Assert.That(properties, Has.Count.EqualTo(1));
+            Assert.That(Property(fixture, "PropertiesTruncated"), Is.True);
+            Assert.That(
+                properties.Cast().Select(property => Property(property, "Path")),
+                Does.Not.Contain("m_Script"));
+        }
+
+        [Test]
+        public void Inspect_BoundsLargeStringsCollectionsAndNestedValuesWithExplicitMarkers()
+        {
+            var root = new GameObject("Root");
+            var fixture = root.AddComponent();
+            fixture.LargeString = new string('x', 5000);
+            fixture.LargeArray = Enumerable.Range(0, 150).ToArray();
+
+            var result = InspectGameObjectCommand.Inspect(
+                Ref(root),
+                includeProperties: true,
+                maxPropertiesPerComponent: 10);
+            Assert.That(result.PayloadTruncated, Is.True);
+
+            var components = Property(result.Root, "Components");
+            var component = components.Cast()
+                .Single(item => Property(item, "Type") == nameof(LargeInspectionFixtureComponent));
+            var properties = Property(component, "Properties").Cast().ToList();
+
+            var largeString = properties.Single(item => Property(item, "Path") == "LargeString");
+            Assert.That(Property(largeString, "Value"), Has.Length.EqualTo(4096));
+            AssertValueTruncation(largeString, "stringLength", 4096, 5000);
+
+            var largeArray = properties.Single(item => Property(item, "Path") == "LargeArray");
+            Assert.That(Property(largeArray, "Value"), Has.Count.EqualTo(100));
+            AssertValueTruncation(largeArray, "collectionLength", 100, 150);
+
+            var nested = properties.Single(item => Property(item, "Path") == "Nested");
+            AssertValueTruncation(nested, "serializationDepth", 4, null);
+        }
+
+        [Test]
+        public void Inspect_SerializesEnumsWithoutMaterializingNameCollections()
+        {
+            var root = new GameObject("Root");
+            root.AddComponent();
+
+            var result = InspectGameObjectCommand.Inspect(
+                Ref(root),
+                includeProperties: true,
+                maxPropertiesPerComponent: 10);
+            var component = result.Root.Components.Single(
+                item => item.Type == nameof(OversizedEnumInspectionFixtureComponent));
+            var property = component.Properties.Single(
+                item => item.Path == nameof(OversizedEnumInspectionFixtureComponent.Value));
+
+            Assert.That(property.Value, Is.EqualTo(0));
+            Assert.That(property.ValueTruncated, Is.False);
+            Assert.That(
+                JsonConvert.SerializeObject(result),
+                Does.Not.Contain(OversizedEnumInspectionFixtureComponent.SelectedName));
+        }
+
+        [Test]
+        public void Inspect_StopsConvertingValuesAfterFirstSupportedPropertyBeyondCap()
+        {
+            var root = new GameObject("Root");
+            root.AddComponent();
+            var readerType = typeof(InspectGameObjectCommand).Assembly.GetType(
+                "McpUnity.Extensions.Commands.SerializedPropertyValueReader",
+                throwOnError: true);
+            var observer = readerType.GetProperty(
+                "ConversionObserver",
+                BindingFlags.Static | BindingFlags.NonPublic);
+            Assert.That(observer, Is.Not.Null, "Expected an internal value-conversion test seam.");
+
+            var convertedPaths = new List();
+            observer.SetValue(null, (Action)(path => convertedPaths.Add(path)));
+            try
+            {
+                var result = InspectGameObjectCommand.Inspect(
+                    Ref(root),
+                    includeProperties: true,
+                    maxPropertiesPerComponent: 1);
+
+                var component = result.Root.Components
+                    .Single(item => item.Type == nameof(InspectionFixtureComponent));
+                Assert.That(component.PropertiesTruncated, Is.True);
+            }
+            finally
+            {
+                observer.SetValue(null, null);
+            }
+
+            Assert.That(
+                convertedPaths.Where(path =>
+                    path == "First" ||
+                    path == "Second" ||
+                    path == "Third"),
+                Is.EqualTo(new[] { "First" }),
+                "The omitted property and all later values must not be materialized.");
+        }
+
+        [Test]
+        public void Inspect_CapsComponentsPerNodeAndAcrossTheWholeInspection()
+        {
+            var root = new GameObject("Root");
+            for (var index = 0; index < 80; index++)
+                root.AddComponent();
+            for (var childIndex = 0; childIndex < 10; childIndex++)
+            {
+                var child = new GameObject($"Child-{childIndex}");
+                child.transform.SetParent(root.transform);
+                for (var componentIndex = 0; componentIndex < 40; componentIndex++)
+                    child.AddComponent();
+            }
+
+            var result = InspectGameObjectCommand.Inspect(
+                Ref(root),
+                maxDepth: 2,
+                maxNodes: 100,
+                includeProperties: false,
+                maxPropertiesPerComponent: 100);
+
+            Assert.That(result.ComponentsReturned, Is.LessThanOrEqualTo(result.MaxTotalComponents));
+            Assert.That(result.ComponentLimitReached, Is.True);
+            Assert.That(result.Root.Components, Has.Count.LessThanOrEqualTo(result.MaxComponentsPerGameObject));
+            Assert.That(result.Root.ComponentsTruncated, Is.True);
+            Assert.That(
+                result.Root.Children.SelectMany(child => child.Components).Count() +
+                result.Root.Components.Count,
+                Is.EqualTo(result.ComponentsReturned));
+        }
+
+        [Test]
+        public void Inspect_StopsAtAggregatePayloadBudgetWithHonestMarkers()
+        {
+            var root = new GameObject(new string('R', 5000));
+            for (var childIndex = 0; childIndex < 200; childIndex++)
+            {
+                var child = new GameObject($"Child-{childIndex}-{new string('N', 5000)}");
+                child.transform.SetParent(root.transform);
+                for (var componentIndex = 0; componentIndex < 8; componentIndex++)
+                {
+                    var fixture = child.AddComponent();
+                    fixture.LargeString = new string('"', 200_000);
+                    fixture.LargeArray = Enumerable.Range(0, 1000).ToArray();
+                }
+            }
+
+            var result = InspectGameObjectCommand.Inspect(
+                Ref(root),
+                maxDepth: 8,
+                maxNodes: 1000,
+                includeProperties: true,
+                maxPropertiesPerComponent: 200);
+            var json = JsonConvert.SerializeObject(result);
+            var serializedBytes = System.Text.Encoding.UTF8.GetByteCount(json);
+
+            Assert.That(serializedBytes, Is.LessThanOrEqualTo(512 * 1024));
+            Assert.That(result.PayloadBudgetBytes, Is.EqualTo(512 * 1024));
+            Assert.That(result.PayloadBytes, Is.EqualTo(serializedBytes));
+            Assert.That(result.PayloadTruncated, Is.True);
+            Assert.That(result.ComponentsReturned, Is.LessThanOrEqualTo(result.MaxTotalComponents));
+            Assert.That(result.Root.Name.Length, Is.LessThanOrEqualTo(256));
+            Assert.That(
+                Flatten(result.Root).Any(node =>
+                    node.ComponentsTruncated ||
+                    node.Components.Any(component => component.PropertiesTruncated)),
+                Is.True);
+        }
+
+        [Test]
+        public void Inspect_FinalSerializedSizeGuardCannotReportAnOversizedPayload()
+        {
+            var result = new InspectGameObjectResult
+            {
+                Root = new GameObjectInspection
+                {
+                    Name = new string('x', 600_000)
+                },
+                NodesReturned = 1,
+                PayloadBudgetBytes = 512 * 1024
+            };
+            var stabilize = typeof(InspectGameObjectCommand).GetMethod(
+                "StabilizePayloadBytes",
+                BindingFlags.Static | BindingFlags.NonPublic);
+
+            Assert.That(stabilize, Is.Not.Null);
+            stabilize.Invoke(null, new object[] { result });
+
+            var serializedBytes = System.Text.Encoding.UTF8.GetByteCount(
+                JsonConvert.SerializeObject(result));
+            Assert.That(serializedBytes, Is.LessThanOrEqualTo(512 * 1024));
+            Assert.That(result.PayloadBytes, Is.EqualTo(serializedBytes));
+            Assert.That(result.PayloadTruncated, Is.True);
+            Assert.That(result.PayloadTruncationReason, Is.EqualTo("serializedPayloadBudget"));
+            Assert.That(result.Root, Is.Null);
+            Assert.That(result.NodesReturned, Is.Zero);
+            Assert.That(result.ComponentsReturned, Is.Zero);
+        }
+
+        [Test]
+        public void Inspect_FailsClosedWhenPayloadSerializerIsUnavailable()
+        {
+            var serializerOverride = typeof(InspectGameObjectCommand).GetProperty(
+                "SerializationOverride",
+                BindingFlags.Static | BindingFlags.NonPublic);
+            Assert.That(
+                serializerOverride,
+                Is.Not.Null,
+                "Expected a serializer-failure test seam.");
+            if (serializerOverride == null)
+                return;
+
+            serializerOverride.SetValue(null, (Func)(_ => null));
+            try
+            {
+                var root = new GameObject("Root");
+                Assert.Throws(() =>
+                    InspectGameObjectCommand.Inspect(Ref(root)));
+            }
+            finally
+            {
+                serializerOverride.SetValue(null, null);
+            }
+        }
+
+        [Test]
+        public void Inspect_SharesAggregateConversionBudgetAcrossBroadNestedComponents()
+        {
+            var root = new GameObject("Root");
+            for (var componentIndex = 0; componentIndex < 12; componentIndex++)
+            {
+                var fixture = root.AddComponent();
+                fixture.Groups = Enumerable.Range(0, 8)
+                    .Select(groupIndex => new BroadInspectionGroup
+                    {
+                        Values = Enumerable.Range(0, 10)
+                            .Select(valueIndex => new BroadInspectionValue
+                            {
+                                First = valueIndex,
+                                Second = componentIndex,
+                                Third = groupIndex
+                            })
+                            .ToArray()
+                    })
+                    .ToArray();
+            }
+
+            var readerType = typeof(InspectGameObjectCommand).Assembly.GetType(
+                "McpUnity.Extensions.Commands.SerializedPropertyValueReader",
+                throwOnError: true);
+            var observer = readerType.GetProperty(
+                "ConversionObserver",
+                BindingFlags.Static | BindingFlags.NonPublic);
+            var convertedPaths = new List();
+            observer.SetValue(null, (Action)(path => convertedPaths.Add(path)));
+            InspectGameObjectResult result;
+            try
+            {
+                result = InspectGameObjectCommand.Inspect(
+                    Ref(root),
+                    includeProperties: true,
+                    maxPropertiesPerComponent: 200);
+            }
+            finally
+            {
+                observer.SetValue(null, null);
+            }
+
+            var serializedBytes = System.Text.Encoding.UTF8.GetByteCount(
+                JsonConvert.SerializeObject(result));
+            var fixtures = result.Root.Components
+                .Where(component => component.Type == nameof(BroadInspectionFixtureComponent))
+                .ToList();
+
+            Assert.That(fixtures, Has.Count.GreaterThan(1));
+            Assert.That(result.AggregateWorkBudget, Is.GreaterThan(0));
+            Assert.That(result.AggregateWorkUsed, Is.LessThanOrEqualTo(result.AggregateWorkBudget));
+            Assert.That(result.AggregateWorkLimitReached, Is.True);
+            Assert.That(result.AggregateConversionCount, Is.EqualTo(convertedPaths.Count));
+            Assert.That(convertedPaths.Count, Is.LessThanOrEqualTo(result.AggregateWorkBudget));
+            Assert.That(result.ConversionTruncated, Is.True);
+            Assert.That(result.AggregatePropertiesScanned, Is.LessThanOrEqualTo(result.AggregateWorkBudget));
+            Assert.That(
+                fixtures.Count(component =>
+                    component.PropertiesTruncationReason == "aggregateWorkBudget"),
+                Is.GreaterThanOrEqualTo(1),
+                "The one inspect-call budget must remain exhausted for a later component.");
+            Assert.That(
+                fixtures[0].PropertiesTruncationReason,
+                Is.Not.EqualTo("aggregateWorkBudget"),
+                "The work limit must be consumed by earlier conversion work before it affects a later component.");
+            Assert.That(serializedBytes, Is.LessThanOrEqualTo(512 * 1024));
+        }
+
+        [Test]
+        public void InspectionBudget_ExactBoundaryIsExhaustedWithoutClaimingTruncation()
+        {
+            var budgetType = typeof(InspectGameObjectCommand).Assembly.GetType(
+                "McpUnity.Extensions.Commands.InspectionBudget",
+                throwOnError: true);
+            var budget = Activator.CreateInstance(budgetType, 3, 128);
+            var reserve = budgetType.GetMethod("TryReserve");
+
+            Assert.That(
+                reserve.Invoke(budget, new object[] { 3, 128, false, false }),
+                Is.True);
+            Assert.That(Property(budget, "WorkUsed"), Is.EqualTo(3));
+            Assert.That(Property(budget, "EstimatedContentBytes"), Is.EqualTo(128));
+            Assert.That(Property(budget, "WorkLimitReached"), Is.True);
+            Assert.That(Property(budget, "ContentLimitReached"), Is.True);
+            Assert.That(Property(budget, "LimitReached"), Is.True);
+            Assert.That(Property(budget, "ConversionTruncated"), Is.False);
+            Assert.That(
+                reserve.Invoke(budget, new object[] { 1, 0, false, false }),
+                Is.False);
+            Assert.That(Property(budget, "ConversionTruncated"), Is.False);
+        }
+
+        [Test]
+        public void Inspect_DoesNotAllocatePropertyReadersAfterExactWorkExhaustion()
+        {
+            var root = new GameObject("Root");
+            var fixture = root.AddComponent();
+            var commandType = typeof(InspectGameObjectCommand);
+            var contextType = commandType.GetNestedType(
+                "InspectionContext",
+                BindingFlags.NonPublic);
+            var context = Activator.CreateInstance(
+                contextType,
+                0,
+                1,
+                true,
+                true,
+                10);
+            var budget = Property(context, "Budget");
+            var reserve = budget.GetType().GetMethod("TryReserve");
+            var workBudget = Property(budget, "WorkBudget");
+            Assert.That(
+                reserve.Invoke(budget, new object[] { workBudget, 0, false, false }),
+                Is.True);
+
+            var allocationObserver = commandType.GetProperty(
+                "PropertyReaderAllocationObserver",
+                BindingFlags.Static | BindingFlags.NonPublic);
+            Assert.That(
+                allocationObserver,
+                Is.Not.Null,
+                "Expected a reader-allocation test seam after successful reservations.");
+            var allocations = new List();
+            allocationObserver.SetValue(
+                null,
+                (Action)(stage => allocations.Add(stage)));
+            var summary = new ComponentInspection
+            {
+                Type = nameof(InspectionFixtureComponent),
+                PropertiesIncluded = true
+            };
+            try
+            {
+                var readProperties = commandType.GetMethod(
+                    "ReadProperties",
+                    BindingFlags.Static | BindingFlags.NonPublic);
+                readProperties.Invoke(null, new[] { fixture, summary, context });
+            }
+            finally
+            {
+                allocationObserver.SetValue(null, null);
+            }
+
+            Assert.That(allocations, Is.Empty);
+            Assert.That(summary.Properties, Is.Empty);
+            Assert.That(summary.PropertiesTruncated, Is.True);
+            Assert.That(
+                summary.PropertiesTruncationReason,
+                Is.EqualTo("aggregateWorkBudget"));
+            Assert.That(Property(budget, "ConversionTruncated"), Is.True);
+        }
+
+        [Test]
+        public void SerializedPropertyReader_ReportsItsPreMaterializationReservationDeltas()
+        {
+            var resultType = typeof(InspectGameObjectCommand).Assembly.GetType(
+                "McpUnity.Extensions.Commands.SerializedPropertyReadResult",
+                throwOnError: true);
+
+            Assert.That(
+                resultType.GetProperty("ReservedWorkUnits"),
+                Is.Not.Null);
+            Assert.That(
+                resultType.GetProperty("ReservedContentBytes"),
+                Is.Not.Null);
+        }
+
+        private static void AssertValueTruncation(
+            object property,
+            string reason,
+            int limit,
+            int? originalCount)
+        {
+            Assert.That(Property(property, "ValueTruncated"), Is.True);
+            var markers = Property(property, "ValueTruncations").Cast().ToList();
+            var marker = markers.Single(item => Property(item, "Reason") == reason);
+            Assert.That(Property(marker, "Limit"), Is.EqualTo(limit));
+            Assert.That(Property(marker, "OriginalCount"), Is.EqualTo(originalCount));
+        }
+
+        private static ObjectRef Ref(UnityEngine.Object obj) =>
+            new ObjectRef { InstanceId = PipelineUtils.GetObjectId(obj) };
+
+        private static IEnumerable Flatten(GameObjectInspection root)
+        {
+            var pending = new Stack();
+            pending.Push(root);
+            while (pending.Count > 0)
+            {
+                var current = pending.Pop();
+                yield return current;
+                for (var index = current.Children.Count - 1; index >= 0; index--)
+                    pending.Push(current.Children[index]);
+            }
+        }
+
+        private static object Property(object instance, string name)
+        {
+            Assert.That(instance, Is.Not.Null, $"Cannot read '{name}' from a null result.");
+            var property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
+            Assert.That(property, Is.Not.Null, $"Expected public property '{name}' on {instance.GetType().Name}.");
+            return property.GetValue(instance);
+        }
+
+        private static T Property(object instance, string name) => (T)Property(instance, name);
+    }
+
+    public sealed class InspectionFixtureComponent : MonoBehaviour
+    {
+        public int First = 1;
+        public string Second = "two";
+        public Vector3 Third = Vector3.one;
+        public AnimationCurve Unsupported = AnimationCurve.Linear(0f, 0f, 1f, 1f);
+    }
+
+    public sealed class LargeInspectionFixtureComponent : MonoBehaviour
+    {
+        public string LargeString;
+        public int[] LargeArray;
+        public InspectionNestedLevel1 Nested = new InspectionNestedLevel1();
+    }
+
+    public sealed class BroadInspectionFixtureComponent : MonoBehaviour
+    {
+        public BroadInspectionGroup[] Groups;
+    }
+
+    public sealed class OversizedEnumInspectionFixtureComponent : MonoBehaviour
+    {
+        public const string SelectedName =
+            nameof(OversizedEnum.ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput_ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput_ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput);
+
+        public OversizedEnum Value =
+            OversizedEnum.ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput_ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput_ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput;
+    }
+
+    public enum OversizedEnum
+    {
+        ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput_ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput_ThisEnumMemberNameIsIntentionallyLongToExerciseBoundedSerializedEnumOutput
+    }
+
+    [Serializable]
+    public sealed class BroadInspectionGroup
+    {
+        public BroadInspectionValue[] Values;
+    }
+
+    [Serializable]
+    public sealed class BroadInspectionValue
+    {
+        public int First;
+        public int Second;
+        public int Third;
+    }
+
+    [Serializable]
+    public sealed class InspectionNestedLevel1
+    {
+        public InspectionNestedLevel2 Child = new InspectionNestedLevel2();
+    }
+
+    [Serializable]
+    public sealed class InspectionNestedLevel2
+    {
+        public InspectionNestedLevel3 Child = new InspectionNestedLevel3();
+    }
+
+    [Serializable]
+    public sealed class InspectionNestedLevel3
+    {
+        public InspectionNestedLevel4 Child = new InspectionNestedLevel4();
+    }
+
+    [Serializable]
+    public sealed class InspectionNestedLevel4
+    {
+        public InspectionNestedLevel5 Child = new InspectionNestedLevel5();
+    }
+
+    [Serializable]
+    public sealed class InspectionNestedLevel5
+    {
+        public int Value = 42;
+    }
+}
diff --git a/Editor/Tests/InspectionCommandTests.cs.meta b/Editor/Tests/InspectionCommandTests.cs.meta
new file mode 100644
index 00000000..f5adec0f
--- /dev/null
+++ b/Editor/Tests/InspectionCommandTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d1e1731d24f54c68af99f6570fe329d7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Editor/Tests/MaterialToolsTests.cs b/Editor/Tests/MaterialToolsTests.cs
deleted file mode 100644
index 71eef06a..00000000
--- a/Editor/Tests/MaterialToolsTests.cs
+++ /dev/null
@@ -1,670 +0,0 @@
-using System.IO;
-using System.Text.RegularExpressions;
-using NUnit.Framework;
-using McpUnity.Tools;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEngine.TestTools;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tests
-{
-    /// 
-    /// Tests for Material Tools functionality
-    /// 
-    public class MaterialToolsTests
-    {
-        private string _testMaterialPath;
-        private string _testMaterialDir;
-
-        [SetUp]
-        public void SetUp()
-        {
-            // Create test directory
-            _testMaterialDir = "Assets/TestMaterials";
-            _testMaterialPath = Path.Combine(_testMaterialDir, "TestMaterial.mat");
-
-            // Ensure test directory exists
-            if (!AssetDatabase.IsValidFolder(_testMaterialDir))
-            {
-                AssetDatabase.CreateFolder("Assets", "TestMaterials");
-            }
-        }
-
-        [TearDown]
-        public void TearDown()
-        {
-            // Clean up test materials
-            if (File.Exists(_testMaterialPath))
-            {
-                AssetDatabase.DeleteAsset(_testMaterialPath);
-            }
-
-            // Clean up test directory
-            if (AssetDatabase.IsValidFolder(_testMaterialDir))
-            {
-                AssetDatabase.DeleteAsset(_testMaterialDir);
-            }
-
-            AssetDatabase.Refresh();
-        }
-
-        #region MaterialToolUtils Tests
-
-        [Test]
-        public void FindShader_WithStandardShader_ReturnsShader()
-        {
-            // Act
-            Shader shader = MaterialToolUtils.FindShader("Standard");
-
-            // Assert
-            Assert.IsNotNull(shader, "Standard shader should be found");
-            Assert.AreEqual("Standard", shader.name);
-        }
-
-        [Test]
-        public void FindShader_WithUnlitColor_ReturnsShader()
-        {
-            // Act
-            Shader shader = MaterialToolUtils.FindShader("Unlit/Color");
-
-            // Assert
-            Assert.IsNotNull(shader, "Unlit/Color shader should be found");
-        }
-
-        [Test]
-        public void FindShader_WithNonExistentShader_ReturnsNull()
-        {
-            // Act
-            Shader shader = MaterialToolUtils.FindShader("NonExistent/Shader/12345");
-
-            // Assert
-            Assert.IsNull(shader, "Non-existent shader should return null");
-        }
-
-        [Test]
-        public void LoadMaterial_WithNonExistentPath_ReturnsNull()
-        {
-            // Act
-            Material material = MaterialToolUtils.LoadMaterial("Assets/NonExistent/Material.mat");
-
-            // Assert
-            Assert.IsNull(material, "Non-existent material should return null");
-        }
-
-        [Test]
-        public void LoadMaterial_WithNullPath_ReturnsNull()
-        {
-            // Act
-            Material material = MaterialToolUtils.LoadMaterial(null);
-
-            // Assert
-            Assert.IsNull(material, "Null path should return null");
-        }
-
-        [Test]
-        public void LoadMaterial_WithEmptyPath_ReturnsNull()
-        {
-            // Act
-            Material material = MaterialToolUtils.LoadMaterial("");
-
-            // Assert
-            Assert.IsNull(material, "Empty path should return null");
-        }
-
-        #endregion
-
-        #region CreateMaterialTool Tests
-
-        [Test]
-        public void CreateMaterialTool_WithValidParameters_CreatesMaterial()
-        {
-            // Arrange
-            CreateMaterialTool tool = new CreateMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["name"] = "TestMaterial",
-                ["shader"] = "Standard",
-                ["savePath"] = _testMaterialPath
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsTrue(result["success"].ToObject(), "Tool should succeed");
-            Assert.IsTrue(File.Exists(_testMaterialPath), "Material file should be created");
-
-            Material material = AssetDatabase.LoadAssetAtPath(_testMaterialPath);
-            Assert.IsNotNull(material, "Material asset should be loadable");
-            Assert.AreEqual("TestMaterial", material.name);
-            Assert.AreEqual("Standard", material.shader.name);
-        }
-
-        [Test]
-        public void CreateMaterialTool_WithProperties_AppliesProperties()
-        {
-            // Arrange
-            CreateMaterialTool tool = new CreateMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["name"] = "TestMaterialWithProps",
-                ["shader"] = "Standard",
-                ["savePath"] = _testMaterialPath,
-                ["properties"] = new JObject
-                {
-                    ["_Color"] = new JObject { ["r"] = 1f, ["g"] = 0f, ["b"] = 0f, ["a"] = 1f },
-                    ["_Metallic"] = 0.5f
-                }
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsTrue(result["success"].ToObject(), "Tool should succeed");
-
-            Material material = AssetDatabase.LoadAssetAtPath(_testMaterialPath);
-            Assert.IsNotNull(material);
-
-            Color color = material.GetColor("_Color");
-            Assert.AreEqual(1f, color.r, 0.01f, "Red should be 1");
-            Assert.AreEqual(0f, color.g, 0.01f, "Green should be 0");
-
-            float metallic = material.GetFloat("_Metallic");
-            Assert.AreEqual(0.5f, metallic, 0.01f, "Metallic should be 0.5");
-        }
-
-        [Test]
-        public void CreateMaterialTool_WithMissingName_ReturnsError()
-        {
-            // Arrange
-            CreateMaterialTool tool = new CreateMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["shader"] = "Standard",
-                ["savePath"] = _testMaterialPath
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void CreateMaterialTool_WithMissingSavePath_ReturnsError()
-        {
-            // Arrange
-            CreateMaterialTool tool = new CreateMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["name"] = "TestMaterial",
-                ["shader"] = "Standard"
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void CreateMaterialTool_WithInvalidShader_ReturnsError()
-        {
-            // Arrange
-            CreateMaterialTool tool = new CreateMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["name"] = "TestMaterial",
-                ["shader"] = "NonExistent/Shader/12345",
-                ["savePath"] = _testMaterialPath
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("not_found_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void CreateMaterialTool_WithDefaultShader_UsesAutoDetectedShader()
-        {
-            // Arrange
-            CreateMaterialTool tool = new CreateMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["name"] = "TestMaterial",
-                ["savePath"] = _testMaterialPath
-            };
-            string expectedShader = MaterialToolUtils.GetDefaultShaderName();
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsTrue(result["success"].ToObject(), "Tool should succeed");
-
-            Material material = AssetDatabase.LoadAssetAtPath(_testMaterialPath);
-            Assert.IsNotNull(material);
-            Assert.AreEqual(expectedShader, material.shader.name, "Default shader should match auto-detected render pipeline shader");
-        }
-
-        #endregion
-
-        #region GetMaterialInfoTool Tests
-
-        [Test]
-        public void GetMaterialInfoTool_WithValidMaterial_ReturnsInfo()
-        {
-            // Arrange - Create a material first
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            GetMaterialInfoTool tool = new GetMaterialInfoTool();
-            JObject parameters = new JObject
-            {
-                ["materialPath"] = _testMaterialPath
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsTrue(result["success"].ToObject(), "Tool should succeed");
-            Assert.AreEqual("TestMaterial", result["materialName"].ToString());
-            Assert.AreEqual("Standard", result["shaderName"].ToString());
-            Assert.IsNotNull(result["properties"], "Should have properties array");
-            Assert.IsTrue(((JArray)result["properties"]).Count > 0, "Should have at least one property");
-        }
-
-        [Test]
-        public void GetMaterialInfoTool_WithMissingMaterialPath_ReturnsError()
-        {
-            // Arrange
-            GetMaterialInfoTool tool = new GetMaterialInfoTool();
-            JObject parameters = new JObject();
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void GetMaterialInfoTool_WithNonExistentMaterial_ReturnsError()
-        {
-            // Arrange
-            GetMaterialInfoTool tool = new GetMaterialInfoTool();
-            JObject parameters = new JObject
-            {
-                ["materialPath"] = "Assets/NonExistent/Material.mat"
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("not_found_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void GetMaterialInfoTool_ReturnsCorrectPropertyTypes()
-        {
-            // Arrange - Create a material first
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            GetMaterialInfoTool tool = new GetMaterialInfoTool();
-            JObject parameters = new JObject
-            {
-                ["materialPath"] = _testMaterialPath
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsTrue(result["success"].ToObject());
-            JArray properties = (JArray)result["properties"];
-
-            // Find _Color property
-            JObject colorProp = null;
-            foreach (JObject prop in properties)
-            {
-                if (prop["name"].ToString() == "_Color")
-                {
-                    colorProp = prop;
-                    break;
-                }
-            }
-
-            Assert.IsNotNull(colorProp, "_Color property should exist");
-            Assert.AreEqual("Color", colorProp["type"].ToString());
-            Assert.IsNotNull(colorProp["value"], "Color value should exist");
-        }
-
-        #endregion
-
-        #region ModifyMaterialTool Tests
-
-        [Test]
-        public void ModifyMaterialTool_WithValidProperties_ModifiesMaterial()
-        {
-            // Arrange - Create a material first
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            testMat.SetColor("_Color", Color.white);
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            ModifyMaterialTool tool = new ModifyMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["materialPath"] = _testMaterialPath,
-                ["properties"] = new JObject
-                {
-                    ["_Color"] = new JObject { ["r"] = 0f, ["g"] = 1f, ["b"] = 0f, ["a"] = 1f }
-                }
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsTrue(result["success"].ToObject(), "Tool should succeed");
-
-            // Reload material to verify changes were saved
-            Material modifiedMat = AssetDatabase.LoadAssetAtPath(_testMaterialPath);
-            Color color = modifiedMat.GetColor("_Color");
-            Assert.AreEqual(0f, color.r, 0.01f, "Red should be 0");
-            Assert.AreEqual(1f, color.g, 0.01f, "Green should be 1");
-        }
-
-        [Test]
-        public void ModifyMaterialTool_WithMissingMaterialPath_ReturnsError()
-        {
-            // Arrange
-            ModifyMaterialTool tool = new ModifyMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["properties"] = new JObject { ["_Color"] = new JObject { ["r"] = 1f } }
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void ModifyMaterialTool_WithEmptyProperties_ReturnsError()
-        {
-            // Arrange
-            ModifyMaterialTool tool = new ModifyMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["materialPath"] = _testMaterialPath,
-                ["properties"] = new JObject()
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void ModifyMaterialTool_WithUnknownProperty_ReportsUnknown()
-        {
-            // Arrange - Create a material first
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            ModifyMaterialTool tool = new ModifyMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["materialPath"] = _testMaterialPath,
-                ["properties"] = new JObject
-                {
-                    ["_Color"] = new JObject { ["r"] = 1f, ["g"] = 0f, ["b"] = 0f, ["a"] = 1f },
-                    ["_NonExistentProperty"] = 0.5f
-                }
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsTrue(result["success"].ToObject(), "Tool should still succeed");
-            Assert.IsNotNull(result["unknownProperties"], "Should report unknown properties");
-            JArray unknownProps = (JArray)result["unknownProperties"];
-            Assert.IsTrue(unknownProps.Count > 0, "Should have at least one unknown property");
-        }
-
-        #endregion
-
-        #region AssignMaterialTool Tests
-
-        [Test]
-        public void AssignMaterialTool_WithMissingGameObjectIdentifier_ReturnsError()
-        {
-            // Arrange
-            AssignMaterialTool tool = new AssignMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["materialPath"] = _testMaterialPath
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void AssignMaterialTool_WithMissingMaterialPath_ReturnsError()
-        {
-            // Arrange
-            AssignMaterialTool tool = new AssignMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["objectPath"] = "/TestCube"
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void AssignMaterialTool_WithNonExistentGameObject_ReturnsError()
-        {
-            // Arrange - Create a material first
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            AssignMaterialTool tool = new AssignMaterialTool();
-            JObject parameters = new JObject
-            {
-                ["objectPath"] = "/NonExistentGameObject12345",
-                ["materialPath"] = _testMaterialPath
-            };
-
-            // Act
-            JObject result = tool.Execute(parameters);
-
-            // Assert
-            Assert.IsNotNull(result["error"], "Should return error");
-            Assert.AreEqual("not_found_error", result["error"]["type"].ToString());
-        }
-
-        [Test]
-        public void AssignMaterialTool_WithNonExistentMaterial_ReturnsError()
-        {
-            // Arrange - Create a test GameObject
-            GameObject testObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
-            testObj.name = "TestCube";
-
-            try
-            {
-                AssignMaterialTool tool = new AssignMaterialTool();
-                JObject parameters = new JObject
-                {
-                    ["instanceId"] = UnityObjectId.GetObjectId(testObj),
-                    ["materialPath"] = "Assets/NonExistent/Material.mat"
-                };
-
-                // Act
-                JObject result = tool.Execute(parameters);
-
-                // Assert
-                Assert.IsNotNull(result["error"], "Should return error");
-                Assert.AreEqual("not_found_error", result["error"]["type"].ToString());
-            }
-            finally
-            {
-                // Cleanup
-                Object.DestroyImmediate(testObj);
-            }
-        }
-
-        [Test]
-        public void AssignMaterialTool_WithValidParameters_AssignsMaterial()
-        {
-            // Arrange - Create a material and a test GameObject
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            testMat.SetColor("_Color", Color.red);
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            GameObject testObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
-            testObj.name = "TestCube";
-
-            try
-            {
-                AssignMaterialTool tool = new AssignMaterialTool();
-                JObject parameters = new JObject
-                {
-                    ["instanceId"] = UnityObjectId.GetObjectId(testObj),
-                    ["materialPath"] = _testMaterialPath,
-                    ["slot"] = 0
-                };
-
-                // Act
-                JObject result = tool.Execute(parameters);
-
-                // Assert
-                Assert.IsTrue(result["success"].ToObject(), "Tool should succeed");
-
-                Renderer renderer = testObj.GetComponent();
-                Assert.AreEqual(testMat, renderer.sharedMaterial, "Material should be assigned");
-            }
-            finally
-            {
-                // Cleanup
-                Object.DestroyImmediate(testObj);
-            }
-        }
-
-        [Test]
-        public void AssignMaterialTool_WithInvalidSlot_ReturnsError()
-        {
-            // Arrange - Create a material and a test GameObject
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            GameObject testObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
-            testObj.name = "TestCube";
-
-            try
-            {
-                AssignMaterialTool tool = new AssignMaterialTool();
-                JObject parameters = new JObject
-                {
-                    ["instanceId"] = UnityObjectId.GetObjectId(testObj),
-                    ["materialPath"] = _testMaterialPath,
-                    ["slot"] = 99 // Invalid slot for a cube with 1 material
-                };
-
-                // Act
-                JObject result = tool.Execute(parameters);
-
-                // Assert
-                Assert.IsNotNull(result["error"], "Should return error");
-                Assert.AreEqual("validation_error", result["error"]["type"].ToString());
-            }
-            finally
-            {
-                // Cleanup
-                Object.DestroyImmediate(testObj);
-            }
-        }
-
-        [Test]
-        public void AssignMaterialTool_WithGameObjectWithoutRenderer_ReturnsError()
-        {
-            // Arrange - Create a material and an empty test GameObject
-            Material testMat = new Material(Shader.Find("Standard"));
-            testMat.name = "TestMaterial";
-            AssetDatabase.CreateAsset(testMat, _testMaterialPath);
-            AssetDatabase.SaveAssets();
-
-            GameObject testObj = new GameObject("TestEmpty"); // No renderer
-
-            try
-            {
-                AssignMaterialTool tool = new AssignMaterialTool();
-                JObject parameters = new JObject
-                {
-                    ["instanceId"] = UnityObjectId.GetObjectId(testObj),
-                    ["materialPath"] = _testMaterialPath
-                };
-
-                // Act
-                JObject result = tool.Execute(parameters);
-
-                // Assert
-                Assert.IsNotNull(result["error"], "Should return error");
-                Assert.AreEqual("component_error", result["error"]["type"].ToString());
-            }
-            finally
-            {
-                // Cleanup
-                Object.DestroyImmediate(testObj);
-            }
-        }
-
-        #endregion
-    }
-}
diff --git a/Editor/Tests/McpBackgroundTickLifecycleTests.cs b/Editor/Tests/McpBackgroundTickLifecycleTests.cs
deleted file mode 100644
index 7f51a32d..00000000
--- a/Editor/Tests/McpBackgroundTickLifecycleTests.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using System;
-using System.Reflection;
-using System.Runtime.InteropServices;
-using McpUnity.Utils;
-using NUnit.Framework;
-using UnityEditor;
-
-namespace McpUnity.Tests
-{
-    public class McpBackgroundTickLifecycleTests
-    {
-#if UNITY_EDITOR_WIN
-        private bool _wasTickRunning;
-
-        [SetUp]
-        public void PreserveBackgroundTickState()
-        {
-            _wasTickRunning = GetTimerId() != UIntPtr.Zero;
-        }
-
-        [TearDown]
-        public void RestoreBackgroundTickState()
-        {
-            MethodInfo lifecycleMethod = GetTickType().GetMethod(
-                _wasTickRunning ? "Start" : "Stop",
-                BindingFlags.Public | BindingFlags.Static);
-            lifecycleMethod.Invoke(null, null);
-        }
-#endif
-
-        [Test]
-        public void BackgroundTickRequiresExplicitLifecycleMethods()
-        {
-            Type tickType = typeof(McpLogger).Assembly.GetType("McpUnity.Utils.McpBackgroundTick");
-
-            Assert.NotNull(tickType);
-            Assert.IsNull(
-                tickType.GetCustomAttribute(),
-                "The background tick must not start automatically when the editor assembly loads.");
-            Assert.NotNull(tickType.GetMethod("Start", BindingFlags.Public | BindingFlags.Static));
-            Assert.NotNull(tickType.GetMethod("Stop", BindingFlags.Public | BindingFlags.Static));
-        }
-
-        [Test]
-        public void StopCanBeCalledMoreThanOnce()
-        {
-            Type tickType = typeof(McpLogger).Assembly.GetType("McpUnity.Utils.McpBackgroundTick");
-            MethodInfo stop = tickType.GetMethod("Stop", BindingFlags.Public | BindingFlags.Static);
-
-            Assert.DoesNotThrow(() => stop.Invoke(null, null));
-            Assert.DoesNotThrow(() => stop.Invoke(null, null));
-        }
-
-#if UNITY_EDITOR_WIN
-        [Test]
-        public void StartDoesNotCreateAnotherTimerWhenAlreadyStarted()
-        {
-            Type tickType = GetTickType();
-            MethodInfo start = tickType.GetMethod("Start", BindingFlags.Public | BindingFlags.Static);
-            MethodInfo stop = tickType.GetMethod("Stop", BindingFlags.Public | BindingFlags.Static);
-
-            stop.Invoke(null, null);
-            try
-            {
-                start.Invoke(null, null);
-                UIntPtr firstTimerId = GetTimerId();
-                start.Invoke(null, null);
-
-                Assert.AreNotEqual(UIntPtr.Zero, firstTimerId);
-                Assert.AreEqual(firstTimerId, GetTimerId());
-            }
-            finally
-            {
-                stop.Invoke(null, null);
-            }
-        }
-
-        [Test]
-        public void TimerInteropCapturesLastWin32Error()
-        {
-            Type tickType = GetTickType();
-            MethodInfo setTimer = tickType.GetMethod(
-                "SetTimer",
-                BindingFlags.NonPublic | BindingFlags.Static);
-
-            Assert.NotNull(setTimer);
-            DllImportAttribute import = setTimer.GetCustomAttribute();
-            Assert.NotNull(import);
-            Assert.IsTrue(import.SetLastError);
-        }
-
-        private static Type GetTickType()
-        {
-            return typeof(McpLogger).Assembly.GetType("McpUnity.Utils.McpBackgroundTick");
-        }
-
-        private static UIntPtr GetTimerId()
-        {
-            FieldInfo timerId = GetTickType().GetField("_timerId", BindingFlags.NonPublic | BindingFlags.Static);
-            return (UIntPtr)timerId.GetValue(null);
-        }
-#endif
-    }
-}
diff --git a/Editor/Tests/McpBackgroundTickLifecycleTests.cs.meta b/Editor/Tests/McpBackgroundTickLifecycleTests.cs.meta
deleted file mode 100644
index daddfb82..00000000
--- a/Editor/Tests/McpBackgroundTickLifecycleTests.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 3e0edbb3edba4147927c6d79a6a515f6
diff --git a/Editor/Tests/McpUnity.Editor.Tests.asmdef b/Editor/Tests/McpUnity.Editor.Tests.asmdef
index a4debc44..78d39558 100644
--- a/Editor/Tests/McpUnity.Editor.Tests.asmdef
+++ b/Editor/Tests/McpUnity.Editor.Tests.asmdef
@@ -1,8 +1,11 @@
 {
     "name": "McpUnity.Editor.Tests",
-    "rootNamespace": "McpUnity.Tests",
+    "rootNamespace": "McpUnity.Extensions.Tests",
     "references": [
-        "McpUnity.Editor",
+        "McpUnity.Extensions",
+        "Unity.Pipeline",
+        "Unity.Pipeline.Editor",
+        "Unity.Nuget.Newtonsoft-Json",
         "UnityEngine.TestRunner",
         "UnityEditor.TestRunner"
     ],
@@ -11,10 +14,9 @@
     ],
     "excludePlatforms": [],
     "allowUnsafeCode": false,
-    "overrideReferences": true,
+    "overrideReferences": false,
     "precompiledReferences": [
-        "nunit.framework.dll",
-        "Newtonsoft.Json.dll"
+        "nunit.framework.dll"
     ],
     "autoReferenced": false,
     "defineConstraints": [
diff --git a/Editor/Tests/McpUnityServerRetryTests.cs b/Editor/Tests/McpUnityServerRetryTests.cs
deleted file mode 100644
index 7423fb99..00000000
--- a/Editor/Tests/McpUnityServerRetryTests.cs
+++ /dev/null
@@ -1,150 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Reflection;
-using System.Reflection.Emit;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using NUnit.Framework;
-
-namespace McpUnity.Tests
-{
-    public class McpUnityServerRetryTests
-    {
-        [Test]
-        public void DelayedStartRetryDelayUsesBoundedBackoff()
-        {
-            MethodInfo method = typeof(McpUnityServer).GetMethod(
-                "GetDelayedStartDelaySeconds",
-                BindingFlags.NonPublic | BindingFlags.Static);
-
-            Assert.NotNull(method, "McpUnityServer should expose a private retry delay helper for bounded restart backoff.");
-
-            double DelayForAttempt(int attempt)
-            {
-                return (double)method.Invoke(null, new object[] { attempt });
-            }
-
-            Assert.AreEqual(0.25d, DelayForAttempt(0), 0.001d);
-            Assert.AreEqual(0.25d, DelayForAttempt(1), 0.001d);
-            Assert.AreEqual(0.5d, DelayForAttempt(2), 0.001d);
-            Assert.AreEqual(1d, DelayForAttempt(3), 0.001d);
-            Assert.AreEqual(2d, DelayForAttempt(4), 0.001d);
-            Assert.AreEqual(3d, DelayForAttempt(5), 0.001d);
-            Assert.AreEqual(5d, DelayForAttempt(6), 0.001d);
-            Assert.AreEqual(5d, DelayForAttempt(10), 0.001d);
-        }
-
-        [Test]
-        public void FailedStartCleanupStopsTheBackgroundTickBeforeTheNullServerBranch()
-        {
-            MethodInfo cleanup = typeof(McpUnityServer).GetMethod(
-                "CleanupFailedStart",
-                BindingFlags.NonPublic | BindingFlags.Instance);
-            Type tickType = typeof(McpLogger).Assembly.GetType("McpUnity.Utils.McpBackgroundTick");
-            MethodInfo stop = tickType.GetMethod("Stop", BindingFlags.Public | BindingFlags.Static);
-
-            Assert.NotNull(cleanup);
-            Assert.NotNull(stop);
-            List instructions = ReadInstructions(cleanup);
-            IlInstruction stopCall = instructions.Find(instruction =>
-                instruction.OpCode == OpCodes.Call && instruction.MetadataToken == stop.MetadataToken);
-            IlInstruction nullServerBranch = instructions.Find(instruction => instruction.OpCode.FlowControl == FlowControl.Cond_Branch);
-            IlInstruction nullServerReturn = instructions.Find(instruction =>
-                instruction.Offset > nullServerBranch.Offset && instruction.OpCode == OpCodes.Ret);
-
-            Assert.AreNotEqual(default(IlInstruction), stopCall, "Failed-start cleanup must call the background tick stop method.");
-            Assert.AreNotEqual(default(IlInstruction), nullServerBranch, "Failed-start cleanup must branch for a null WebSocket server.");
-            Assert.AreNotEqual(default(IlInstruction), nullServerReturn, "The null-server branch must return after clearing clients.");
-            Assert.Less(stopCall.Offset, nullServerBranch.Offset, "The background tick must stop before the null-server early-return branch is evaluated.");
-            Assert.Less(stopCall.Offset, nullServerReturn.Offset, "The background tick must stop before the null-server early return.");
-        }
-
-        private static List ReadInstructions(MethodInfo method)
-        {
-            byte[] bytes = method.GetMethodBody().GetILAsByteArray();
-            var instructions = new List();
-            int offset = 0;
-
-            while (offset < bytes.Length)
-            {
-                int instructionOffset = offset;
-                OpCode opCode = ReadOpCode(bytes, ref offset);
-                int metadataToken = opCode.OperandType == OperandType.InlineMethod
-                    ? BitConverter.ToInt32(bytes, offset)
-                    : 0;
-
-                instructions.Add(new IlInstruction(instructionOffset, opCode, metadataToken));
-                offset += GetOperandSize(bytes, offset, opCode.OperandType);
-            }
-
-            return instructions;
-        }
-
-        private static OpCode ReadOpCode(byte[] bytes, ref int offset)
-        {
-            short value = bytes[offset++] == 0xfe
-                ? (short)(0xfe00 | bytes[offset++])
-                : (short)bytes[offset - 1];
-
-            foreach (FieldInfo field in typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static))
-            {
-                if (field.FieldType == typeof(OpCode))
-                {
-                    OpCode opCode = (OpCode)field.GetValue(null);
-                    if (opCode.Value == value)
-                    {
-                        return opCode;
-                    }
-                }
-            }
-
-            throw new InvalidOperationException($"Unknown IL opcode: 0x{value:X4}.");
-        }
-
-        private static int GetOperandSize(byte[] bytes, int offset, OperandType operandType)
-        {
-            switch (operandType)
-            {
-                case OperandType.InlineNone:
-                    return 0;
-                case OperandType.ShortInlineBrTarget:
-                case OperandType.ShortInlineI:
-                case OperandType.ShortInlineVar:
-                    return 1;
-                case OperandType.InlineVar:
-                    return 2;
-                case OperandType.InlineBrTarget:
-                case OperandType.InlineField:
-                case OperandType.InlineI:
-                case OperandType.InlineMethod:
-                case OperandType.InlineSig:
-                case OperandType.InlineString:
-                case OperandType.InlineTok:
-                case OperandType.InlineType:
-                case OperandType.ShortInlineR:
-                    return 4;
-                case OperandType.InlineI8:
-                case OperandType.InlineR:
-                    return 8;
-                case OperandType.InlineSwitch:
-                    return sizeof(int) + BitConverter.ToInt32(bytes, offset) * sizeof(int);
-                default:
-                    throw new InvalidOperationException($"Unsupported IL operand type: {operandType}.");
-            }
-        }
-
-        private readonly struct IlInstruction
-        {
-            public IlInstruction(int offset, OpCode opCode, int metadataToken)
-            {
-                Offset = offset;
-                OpCode = opCode;
-                MetadataToken = metadataToken;
-            }
-
-            public int Offset { get; }
-            public OpCode OpCode { get; }
-            public int MetadataToken { get; }
-        }
-    }
-}
diff --git a/Editor/Tests/McpUnityServerRetryTests.cs.meta b/Editor/Tests/McpUnityServerRetryTests.cs.meta
deleted file mode 100644
index 344c0d2a..00000000
--- a/Editor/Tests/McpUnityServerRetryTests.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: df14dde02266467a91f8c70ae91e4e6c
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tests/PackageContractTests.cs b/Editor/Tests/PackageContractTests.cs
new file mode 100644
index 00000000..1f55a1fb
--- /dev/null
+++ b/Editor/Tests/PackageContractTests.cs
@@ -0,0 +1,190 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+using McpUnity.Extensions.Commands;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+using Unity.Pipeline.Models;
+using UnityEditor.PackageManager;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class PackageContractTests
+    {
+        [Test]
+        public void PackageManifest_DeclaresUnityCliExtensionIdentityAndExactDependencies()
+        {
+            var package = PackageInfo.FindForAssembly(typeof(AssignMaterialCommand).Assembly);
+            Assert.That(package, Is.Not.Null);
+            var manifest = JObject.Parse(File.ReadAllText(Path.Combine(package.assetPath, "package.json")));
+
+            Assert.That((string)manifest["version"], Is.EqualTo("2.0.0"));
+            Assert.That((string)manifest["unity"], Is.EqualTo("6000.0"));
+            Assert.That((string)manifest["displayName"],
+                Does.Contain("MCP Unity Extensions for Unity CLI"));
+            Assert.That((string)manifest["description"],
+                Does.Contain("MCP Unity Extensions for Unity CLI"));
+
+            var dependencies = (JObject)manifest["dependencies"];
+            Assert.That(dependencies.Properties().Select(property => property.Name), Is.EquivalentTo(new[]
+            {
+                "com.unity.pipeline",
+                "com.unity.test-framework"
+            }));
+            Assert.That((string)dependencies["com.unity.pipeline"], Is.EqualTo("0.3.1-exp.1"));
+            Assert.That((string)dependencies["com.unity.test-framework"], Is.EqualTo("1.3.3"));
+        }
+
+        [Test]
+        public void EditorAssembly_IsEditorOnlyAndReferencesOnlyPipelineAssemblies()
+        {
+            var package = PackageInfo.FindForAssembly(typeof(AssignMaterialCommand).Assembly);
+            var asmdef = JObject.Parse(File.ReadAllText(
+                Path.Combine(package.assetPath, "Editor/McpUnity.Editor.asmdef")));
+
+            Assert.That((string)asmdef["name"], Is.EqualTo("McpUnity.Extensions"));
+            Assert.That((string)asmdef["rootNamespace"], Is.EqualTo("McpUnity.Extensions"));
+            Assert.That(
+                asmdef["includePlatforms"].Values(),
+                Is.EquivalentTo(new[] { "Editor" }));
+            Assert.That(
+                asmdef["references"].Values(),
+                Is.EquivalentTo(new[] { "Unity.Pipeline", "Unity.Pipeline.Editor" }));
+        }
+
+        [Test]
+        public void PublicResultDtos_DeclareExplicitStableCamelCaseWireNames()
+        {
+            var dtoTypes = typeof(InspectGameObjectResult).Assembly.GetTypes()
+                .Where(type =>
+                    type.IsPublic &&
+                    type.IsClass &&
+                    type.Namespace == typeof(InspectGameObjectResult).Namespace &&
+                    type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Length > 0)
+                .ToList();
+
+            Assert.That(dtoTypes, Is.Not.Empty);
+            foreach (var type in dtoTypes)
+            {
+                Assert.That(type.GetCustomAttribute(), Is.Not.Null,
+                    $"{type.Name} must declare DataContract.");
+                foreach (var property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public))
+                {
+                    var member = property.GetCustomAttribute();
+                    Assert.That(member, Is.Not.Null,
+                        $"{type.Name}.{property.Name} must declare DataMember.");
+                    Assert.That(member.Name, Is.EqualTo(CamelCase(property.Name)),
+                        $"{type.Name}.{property.Name} must have a stable camelCase wire name.");
+                }
+            }
+        }
+
+        [Test]
+        public void PipelineJsonSerialization_UsesStableInspectUnloadAndMaterialShapes()
+        {
+            var inspect = new InspectGameObjectResult
+            {
+                Root = new GameObjectInspection
+                {
+                    Name = "Root",
+                    Transform = new TransformInspection
+                    {
+                        LocalPosition = new Vector3Inspection { X = 1f, Y = 2f, Z = 3f }
+                    },
+                    Components =
+                    {
+                        new ComponentInspection
+                        {
+                            Type = "Fixture",
+                            Properties =
+                            {
+                                new SerializedPropertyInspection
+                                {
+                                    Name = "Text",
+                                    Path = "Text",
+                                    Type = "String",
+                                    Value = "bounded"
+                                }
+                            }
+                        }
+                    }
+                }
+            };
+            var unload = new UnloadSceneResult
+            {
+                UnloadedPath = "Assets/Old.unity",
+                ActiveSceneName = "Main",
+                ActiveScenePath = "Assets/Main.unity"
+            };
+            var material = new AssignMaterialResult
+            {
+                GameObject = new AuthoringResult { HierarchyPath = "/Root" },
+                Material = new AuthoringResult { AssetPath = "Assets/Material.mat" },
+                Slot = 2
+            };
+
+            var inspectJson = JObject.Parse(JsonConvert.SerializeObject(inspect));
+            Assert.That(inspectJson.Properties().Select(property => property.Name),
+                Is.EquivalentTo(new[]
+                {
+                    "root",
+                    "maxDepth",
+                    "maxNodes",
+                    "maxPropertiesPerComponent",
+                    "nodesReturned",
+                    "nodeLimitReached",
+                    "maxComponentsPerGameObject",
+                    "maxTotalComponents",
+                    "componentsReturned",
+                    "componentLimitReached",
+                    "aggregateWorkBudget",
+                    "aggregateWorkUsed",
+                    "aggregateWorkLimitReached",
+                    "aggregateConversionCount",
+                    "aggregatePropertiesScanned",
+                    "aggregateContentBudgetBytes",
+                    "aggregateEstimatedContentBytes",
+                    "aggregateContentLimitReached",
+                    "conversionTruncated",
+                    "payloadBudgetBytes",
+                    "payloadBytes",
+                    "payloadTruncated",
+                    "payloadTruncationReason"
+                }));
+            Assert.That((string)inspectJson["root"]["name"], Is.EqualTo("Root"));
+            Assert.That((float)inspectJson["root"]["transform"]["localPosition"]["x"], Is.EqualTo(1f));
+            Assert.That((string)inspectJson["root"]["components"][0]["properties"][0]["value"],
+                Is.EqualTo("bounded"));
+
+            var unloadJson = JObject.Parse(JsonConvert.SerializeObject(unload));
+            Assert.That(unloadJson.Properties().Select(property => property.Name),
+                Is.EquivalentTo(new[] { "unloadedPath", "activeSceneName", "activeScenePath" }));
+            Assert.That((string)unloadJson["activeScenePath"], Is.EqualTo("Assets/Main.unity"));
+
+            var materialJson = JObject.Parse(JsonConvert.SerializeObject(material));
+            Assert.That(materialJson.Properties().Select(property => property.Name),
+                Is.EquivalentTo(new[] { "gameObject", "material", "slot" }));
+            Assert.That((string)materialJson["gameObject"]["hierarchyPath"], Is.EqualTo("/Root"));
+            Assert.That((string)materialJson["material"]["assetPath"],
+                Is.EqualTo("Assets/Material.mat"));
+        }
+
+        [Test]
+        public void InspectCommand_DoesNotRewalkMaterializedPropertyValuesForBudgeting()
+        {
+            var package = PackageInfo.FindForAssembly(typeof(InspectGameObjectCommand).Assembly);
+            var source = File.ReadAllText(
+                Path.Combine(package.assetPath, "Editor/Commands/InspectGameObjectCommand.cs"));
+
+            Assert.That(source, Does.Not.Contain("EstimateValueBytes("));
+            Assert.That(source, Does.Not.Contain("Stack"));
+            Assert.That(source, Does.Not.Contain("DictionaryEntry"));
+        }
+
+        private static string CamelCase(string name) =>
+            char.ToLowerInvariant(name[0]) + name.Substring(1);
+    }
+}
diff --git a/Editor/Tests/PackageContractTests.cs.meta b/Editor/Tests/PackageContractTests.cs.meta
new file mode 100644
index 00000000..7c6805ff
--- /dev/null
+++ b/Editor/Tests/PackageContractTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d44b93600de044c0a91258fbab5a7feb
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Editor/Tests/PathHandlingTests.cs b/Editor/Tests/PathHandlingTests.cs
deleted file mode 100644
index 7b00d664..00000000
--- a/Editor/Tests/PathHandlingTests.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using System.IO;
-using System.Text.RegularExpressions;
-using NUnit.Framework;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEngine.TestTools;
-
-namespace McpUnity.Tests
-{
-    /// 
-    /// Tests for path handling with spaces and special characters
-    /// 
-    public class PathHandlingTests
-    {
-        private string _tempDir;
-        private string _tempDirWithSpaces;
-
-        [SetUp]
-        public void SetUp()
-        {
-            // Create temp directories for testing
-            _tempDir = Path.Combine(Path.GetTempPath(), "McpUnityTest");
-            _tempDirWithSpaces = Path.Combine(Path.GetTempPath(), "MCP Unity Test With Spaces");
-
-            // Clean up if exists from previous test
-            if (Directory.Exists(_tempDir))
-                Directory.Delete(_tempDir, true);
-            if (Directory.Exists(_tempDirWithSpaces))
-                Directory.Delete(_tempDirWithSpaces, true);
-        }
-
-        [TearDown]
-        public void TearDown()
-        {
-            // Clean up temp directories
-            if (Directory.Exists(_tempDir))
-                Directory.Delete(_tempDir, true);
-            if (Directory.Exists(_tempDirWithSpaces))
-                Directory.Delete(_tempDirWithSpaces, true);
-        }
-
-        [Test]
-        public void ValidateServerPath_WithValidPath_ReturnsTrue()
-        {
-            // Arrange
-            Directory.CreateDirectory(_tempDir);
-            File.WriteAllText(Path.Combine(_tempDir, "package.json"), "{}");
-
-            // Act
-            bool result = McpUtils.ValidateServerPath(_tempDir);
-
-            // Assert
-            Assert.IsTrue(result, "ValidateServerPath should return true for valid path with package.json");
-        }
-
-        [Test]
-        public void ValidateServerPath_WithSpacesInPath_ReturnsTrue()
-        {
-            // Arrange
-            Directory.CreateDirectory(_tempDirWithSpaces);
-            File.WriteAllText(Path.Combine(_tempDirWithSpaces, "package.json"), "{}");
-
-            // Act
-            bool result = McpUtils.ValidateServerPath(_tempDirWithSpaces);
-
-            // Assert
-            Assert.IsTrue(result, "ValidateServerPath should return true for path with spaces");
-        }
-
-        [Test]
-        public void ValidateServerPath_WithNonExistentPath_ReturnsFalse()
-        {
-            // Arrange
-            string nonExistentPath = Path.Combine(Path.GetTempPath(), "NonExistentMcpUnityPath12345");
-            LogAssert.Expect(LogType.Error, new Regex(@"\[MCP Unity\] Server path does not exist:"));
-
-            // Act
-            bool result = McpUtils.ValidateServerPath(nonExistentPath);
-
-            // Assert
-            Assert.IsFalse(result, "ValidateServerPath should return false for non-existent path");
-        }
-
-        [Test]
-        public void ValidateServerPath_WithMissingPackageJson_ReturnsFalse()
-        {
-            // Arrange
-            Directory.CreateDirectory(_tempDir);
-            // Don't create package.json
-            LogAssert.Expect(LogType.Error, new Regex(@"\[MCP Unity\] package\.json not found in server path:"));
-
-            // Act
-            bool result = McpUtils.ValidateServerPath(_tempDir);
-
-            // Assert
-            Assert.IsFalse(result, "ValidateServerPath should return false when package.json is missing");
-        }
-
-        [Test]
-        public void ValidateServerPath_WithNullPath_ReturnsFalse()
-        {
-            // Arrange
-            LogAssert.Expect(LogType.Error, "[MCP Unity] Server path is null or empty. Cannot validate.");
-
-            // Act
-            bool result = McpUtils.ValidateServerPath(null);
-
-            // Assert
-            Assert.IsFalse(result, "ValidateServerPath should return false for null path");
-        }
-
-        [Test]
-        public void ValidateServerPath_WithEmptyPath_ReturnsFalse()
-        {
-            // Arrange
-            LogAssert.Expect(LogType.Error, "[MCP Unity] Server path is null or empty. Cannot validate.");
-
-            // Act
-            bool result = McpUtils.ValidateServerPath("");
-
-            // Assert
-            Assert.IsFalse(result, "ValidateServerPath should return false for empty path");
-        }
-
-        [Test]
-        public void EncodePathForFileUrl_WithSpaces_EncodesCorrectly()
-        {
-            // Arrange
-            string pathWithSpaces = "/Users/John Doe/My Project/package.json";
-            string expected = "/Users/John%20Doe/My%20Project/package.json";
-
-            // Act
-            string result = McpUtils.EncodePathForFileUrl(pathWithSpaces);
-
-            // Assert
-            Assert.AreEqual(expected, result, "Spaces should be encoded as %20");
-        }
-
-        [Test]
-        public void EncodePathForFileUrl_WithoutSpaces_ReturnsUnchanged()
-        {
-            // Arrange
-            string pathWithoutSpaces = "/Users/JohnDoe/MyProject/package.json";
-
-            // Act
-            string result = McpUtils.EncodePathForFileUrl(pathWithoutSpaces);
-
-            // Assert
-            Assert.AreEqual(pathWithoutSpaces, result, "Path without spaces should remain unchanged");
-        }
-
-        [Test]
-        public void EncodePathForFileUrl_WithMultipleSpaces_EncodesAll()
-        {
-            // Arrange
-            string pathWithMultipleSpaces = "C:/Users/John Doe/Game Projects/My Unity Game/Assets";
-            string expected = "C:/Users/John%20Doe/Game%20Projects/My%20Unity%20Game/Assets";
-
-            // Act
-            string result = McpUtils.EncodePathForFileUrl(pathWithMultipleSpaces);
-
-            // Assert
-            Assert.AreEqual(expected, result, "All spaces should be encoded as %20");
-        }
-
-    }
-}
diff --git a/Editor/Tests/PathHandlingTests.cs.meta b/Editor/Tests/PathHandlingTests.cs.meta
deleted file mode 100644
index 5c792bfe..00000000
--- a/Editor/Tests/PathHandlingTests.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 4a6fe7ffcdfeb410aaf62a5312c37fd2
\ No newline at end of file
diff --git a/Editor/Tests/UnityCliSetupReviewTests.cs b/Editor/Tests/UnityCliSetupReviewTests.cs
new file mode 100644
index 00000000..9dab5124
--- /dev/null
+++ b/Editor/Tests/UnityCliSetupReviewTests.cs
@@ -0,0 +1,371 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Threading;
+using System.Threading.Tasks;
+using McpUnity.Extensions.Commands;
+using McpUnity.Extensions.Setup;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+using UnityEditor.PackageManager;
+using UnityEngine;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class UnityCliSetupReviewTests
+    {
+        [Test]
+        public void CompanionConfiguration_UsesAbsoluteResolvedPackagePathThroughTheWindowBoundary()
+        {
+            var package = PackageInfo.FindForAssembly(typeof(AssignMaterialCommand).Assembly);
+            var resolvedPackagePath = Path.GetFullPath(package.resolvedPath);
+            var configuration = JObject.Parse(UnityCliConfiguration.CreateCompanion(
+                resolvedPackagePath,
+                "/absolute/project",
+                "/absolute/unity",
+                true));
+            var serverPath = (string)configuration["mcpServers"]["mcp-unity-companion"]["args"][0];
+            var windowSource = File.ReadAllText(Path.Combine(package.assetPath, "Editor/Setup/UnityCliSetupWindow.cs"));
+
+            Assert.That(Path.IsPathRooted(serverPath), Is.True);
+            Assert.That(serverPath, Is.EqualTo(
+                resolvedPackagePath.TrimEnd('/', '\\') + "/Server~/build/index.js"));
+            Assert.That(windowSource, Does.Contain("package.resolvedPath"));
+            Assert.That(windowSource, Does.Not.Contain("package.assetPath"));
+        }
+
+        [Test]
+        public void CliVersionClassifier_RejectsMalformedPrereleasesAndClassifiesHugeCoreIdentifiersWithoutThrowing()
+        {
+            var huge = new string('9', 128);
+
+            Assert.DoesNotThrow(() =>
+                UnityCliVersionClassifier.Classify("unity " + huge + ".0.0", true, false));
+            Assert.That(UnityCliVersionClassifier.Classify("unity " + huge + ".0.0", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.UntestedNewer));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-beta..2", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-beta.02", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1." + huge + ".0", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.Compatible));
+        }
+
+        [Test]
+        public void CliVersionClassifier_ValidatesBuildMetadataWithoutChangingPrecedence()
+        {
+            var huge = new string('9', 128);
+
+            var release = UnityCliVersionClassifier.Classify("unity 1.0.0+build.7", true, false);
+            var prerelease = UnityCliVersionClassifier.Classify("unity 1.0.0-beta.2+build.7", true, false);
+
+            Assert.That(release.Status, Is.EqualTo(UnityCliCompatibility.Compatible));
+            Assert.That(release.Version, Is.EqualTo("1.0.0+build.7"));
+            Assert.That(prerelease.Status, Is.EqualTo(UnityCliCompatibility.Compatible));
+            Assert.That(UnityCliVersionClassifier.Classify("unity " + huge + ".0.0+build.7", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.UntestedNewer));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0+", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0+build..7", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0+build!", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0+build_meta", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0+build@meta", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("Unity CLI version: 1.0.0+build.7 (stable)", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.Compatible));
+        }
+
+        [Test]
+        public void CliCheckService_ContainsMalformedAndLargeVersionOutput()
+        {
+            var huge = new string('9', 128);
+            var service = new UnityCliCheckService(
+                new FixedRunner(new UnityCliProcessResult("unity " + huge + ".0.0-beta.02", string.Empty, 0, false)),
+                () => null);
+
+            UnityCliCheckResult result = null;
+            Assert.DoesNotThrow(() => result = service.CheckAsync("/opt/unity", CancellationToken.None).GetAwaiter().GetResult());
+
+            Assert.That(result.Compatibility.Status, Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+        }
+
+        [Test]
+        public async Task ProcessRunner_TimeoutKillsItsOwnedProcessAndReturnsDespiteAChildHoldingPipes()
+        {
+            RequirePosixHelper();
+            var helperDirectory = CreateHelperDirectory();
+            var parentPidPath = Path.Combine(helperDirectory, "parent.pid");
+            var childPidPath = Path.Combine(helperDirectory, "child.pid");
+            var runner = new SystemUnityCliProcessRunner();
+            var command = "echo $$ > '" + parentPidPath +
+                "'; exec 3>&1 4>&2; (while :; do sleep 1; done) >&3 2>&4 & echo $! > '" + childPidPath + "'; wait";
+
+            try
+            {
+                var stopwatch = Stopwatch.StartNew();
+                var task = runner.RunAsync(
+                    "/bin/sh",
+                    "-c \"" + command + "\"",
+                    TimeSpan.FromMilliseconds(100),
+                    CancellationToken.None);
+
+                Assert.That(WaitForFile(childPidPath, TimeSpan.FromSeconds(1)), Is.True);
+                Assert.That(await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(2))), Is.EqualTo(task));
+
+                var result = await task;
+                Assert.That(stopwatch.Elapsed, Is.LessThan(TimeSpan.FromSeconds(2)));
+                Assert.That(result.TimedOut, Is.True);
+                Assert.That(result.Cancelled, Is.False);
+                Assert.That(IsProcessRunning(ReadPid(parentPidPath)), Is.False);
+            }
+            finally
+            {
+                KillIfRunning(ReadPid(childPidPath));
+                Directory.Delete(helperDirectory, true);
+            }
+        }
+
+        [Test]
+        public async Task ProcessRunner_ReportsExternalCancellationSeparatelyFromTimeout()
+        {
+            RequirePosixHelper();
+            var helperDirectory = CreateHelperDirectory();
+            var parentPidPath = Path.Combine(helperDirectory, "parent.pid");
+            var runner = new SystemUnityCliProcessRunner();
+            var command = "echo $$ > '" + parentPidPath + "'; sleep 30";
+            var cancellation = new CancellationTokenSource();
+
+            try
+            {
+                var task = runner.RunAsync(
+                    "/bin/sh",
+                    "-c \"" + command + "\"",
+                    TimeSpan.FromSeconds(5),
+                    cancellation.Token);
+                Assert.That(WaitForFile(parentPidPath, TimeSpan.FromSeconds(1)), Is.True);
+
+                cancellation.Cancel();
+                Assert.That(await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(2))), Is.EqualTo(task));
+
+                var result = await task;
+                Assert.That(result.TimedOut, Is.False);
+                Assert.That(result.Cancelled, Is.True);
+                Assert.That(IsProcessRunning(ReadPid(parentPidPath)), Is.False);
+            }
+            finally
+            {
+                cancellation.Dispose();
+                KillIfRunning(ReadPid(parentPidPath));
+                Directory.Delete(helperDirectory, true);
+            }
+        }
+
+        [Test]
+        public async Task ProcessRunner_DeadlineBoundsDrainWhenTheParentExitsButChildKeepsPipesOpen()
+        {
+            RequirePosixHelper();
+            var helperDirectory = CreateHelperDirectory();
+            var childPidPath = Path.Combine(helperDirectory, "child.pid");
+            var runner = new SystemUnityCliProcessRunner();
+            var command = "exec 3>&1 4>&2; (while :; do sleep 1; done) >&3 2>&4 & echo $! > '" +
+                childPidPath + "'; exit 0";
+
+            try
+            {
+                var stopwatch = Stopwatch.StartNew();
+                var task = runner.RunAsync(
+                    "/bin/sh",
+                    "-c \"" + command + "\"",
+                    TimeSpan.FromMilliseconds(100),
+                    CancellationToken.None);
+                Assert.That(WaitForFile(childPidPath, TimeSpan.FromSeconds(1)), Is.True);
+
+                Assert.That(await Task.WhenAny(task, Task.Delay(TimeSpan.FromSeconds(2))), Is.EqualTo(task));
+
+                var result = await task;
+                Assert.That(stopwatch.Elapsed, Is.LessThan(TimeSpan.FromSeconds(2)));
+                Assert.That(result.TimedOut, Is.True);
+                Assert.That(IsProcessRunning(ReadPid(childPidPath)), Is.True);
+            }
+            finally
+            {
+                KillIfRunning(ReadPid(childPidPath));
+                Directory.Delete(helperDirectory, true);
+            }
+        }
+
+        [Test]
+        public void ProcessRunner_UsesDisposableCancellationSourcesForDeadlineAndCleanup()
+        {
+            var package = PackageInfo.FindForAssembly(typeof(AssignMaterialCommand).Assembly);
+            var source = File.ReadAllText(Path.Combine(
+                package.assetPath,
+                "Editor/Setup/SystemUnityCliProcessRunner.cs"));
+
+            Assert.That(source, Does.Contain("new CancellationTokenSource(timeout)"));
+            Assert.That(source, Does.Contain("CancellationTokenSource.CreateLinkedTokenSource"));
+            Assert.That(source, Does.Not.Contain("Task.Delay(timeout)"));
+        }
+
+        [Test]
+        public void SetupServices_PreserveProjectSettingsContentAndEnvironment()
+        {
+            var projectSettingsDirectory = Path.Combine(Directory.GetParent(Application.dataPath).FullName, "ProjectSettings");
+            var projectSettingsBefore = SnapshotProjectSettings(projectSettingsDirectory);
+            var environmentBefore = SnapshotEnvironment();
+            var service = new UnityCliCheckService(
+                new FixedRunner(new UnityCliProcessResult("unity 1.0.0-beta.2", string.Empty, 0, false)),
+                () => null);
+
+            service.CheckAsync("/opt/unity", CancellationToken.None).GetAwaiter().GetResult();
+
+            Assert.That(SnapshotProjectSettings(projectSettingsDirectory), Is.EqualTo(projectSettingsBefore));
+            Assert.That(SnapshotEnvironment(), Is.EqualTo(environmentBefore));
+        }
+
+        [Test]
+        public void SetupServices_StaticContractHasNoPersistentWriteApis()
+        {
+            var package = PackageInfo.FindForAssembly(typeof(AssignMaterialCommand).Assembly);
+            var setupDirectory = Path.Combine(package.assetPath, "Editor/Setup");
+            var source = string.Join("\n", Directory.GetFiles(setupDirectory, "*.cs")
+                .Select(File.ReadAllText)
+                .ToArray());
+
+            Assert.That(source, Does.Not.Contain("File.Write"));
+            Assert.That(source, Does.Not.Contain("File.Append"));
+            Assert.That(source, Does.Not.Contain("WriteAll"));
+            Assert.That(source, Does.Not.Contain("EditorPrefs.Set"));
+            Assert.That(source, Does.Not.Contain("Environment.SetEnvironmentVariable"));
+            Assert.That(source, Does.Contain("process.Start()"));
+        }
+
+        private sealed class FixedRunner : IUnityCliProcessRunner
+        {
+            private readonly UnityCliProcessResult result;
+
+            public FixedRunner(UnityCliProcessResult result)
+            {
+                this.result = result;
+            }
+
+            public Task RunAsync(
+                string executablePath,
+                string arguments,
+                TimeSpan timeout,
+                CancellationToken cancellationToken)
+            {
+                return Task.FromResult(result);
+            }
+        }
+
+        private static string CreateHelperDirectory()
+        {
+            var directory = Path.Combine(Path.GetTempPath(), "mcp-unity-cli-runner-" + Guid.NewGuid().ToString("N"));
+            Directory.CreateDirectory(directory);
+            return directory;
+        }
+
+        private static void RequirePosixHelper()
+        {
+            if (Application.platform == RuntimePlatform.WindowsEditor)
+            {
+                Assert.Ignore("POSIX helper-process regression is not available on Windows.");
+            }
+        }
+
+        private static bool WaitForFile(string path, TimeSpan timeout)
+        {
+            var stopwatch = Stopwatch.StartNew();
+            while (!File.Exists(path) && stopwatch.Elapsed < timeout)
+            {
+                Thread.Sleep(10);
+            }
+
+            return File.Exists(path);
+        }
+
+        private static int ReadPid(string path)
+        {
+            int pid;
+            return File.Exists(path) && int.TryParse(File.ReadAllText(path).Trim(), out pid) ? pid : -1;
+        }
+
+        private static bool IsProcessRunning(int pid)
+        {
+            if (pid <= 0)
+            {
+                return false;
+            }
+
+            try
+            {
+                using (var process = Process.GetProcessById(pid))
+                {
+                    return !process.HasExited;
+                }
+            }
+            catch (ArgumentException)
+            {
+                return false;
+            }
+        }
+
+        private static void KillIfRunning(int pid)
+        {
+            if (!IsProcessRunning(pid))
+            {
+                return;
+            }
+
+            using (var process = Process.GetProcessById(pid))
+            {
+                process.Kill();
+            }
+        }
+
+        private static Dictionary SnapshotProjectSettings(string directory)
+        {
+            var snapshot = new Dictionary();
+            if (!Directory.Exists(directory))
+            {
+                return snapshot;
+            }
+
+            foreach (var path in Directory.GetFiles(directory, "*", SearchOption.AllDirectories))
+            {
+                snapshot[Path.GetRelativePath(directory, path)] = ComputeHash(path);
+            }
+
+            return snapshot;
+        }
+
+        private static Dictionary SnapshotEnvironment()
+        {
+            var snapshot = new Dictionary();
+            foreach (DictionaryEntry variable in Environment.GetEnvironmentVariables())
+            {
+                snapshot[(string)variable.Key] = (string)variable.Value;
+            }
+
+            return snapshot;
+        }
+
+        private static string ComputeHash(string path)
+        {
+            using (var sha256 = SHA256.Create())
+            {
+                return BitConverter.ToString(sha256.ComputeHash(File.ReadAllBytes(path)));
+            }
+        }
+    }
+}
diff --git a/Editor/Tests/UnityCliSetupReviewTests.cs.meta b/Editor/Tests/UnityCliSetupReviewTests.cs.meta
new file mode 100644
index 00000000..02218f8d
--- /dev/null
+++ b/Editor/Tests/UnityCliSetupReviewTests.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: fce13f04bd623431ab1a55384238a9e7
\ No newline at end of file
diff --git a/Editor/Tests/UnityCliSetupServiceTests.cs b/Editor/Tests/UnityCliSetupServiceTests.cs
new file mode 100644
index 00000000..539c4582
--- /dev/null
+++ b/Editor/Tests/UnityCliSetupServiceTests.cs
@@ -0,0 +1,206 @@
+using System.Reflection;
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using McpUnity.Extensions.Commands;
+using McpUnity.Extensions.Setup;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+using UnityEditor.PackageManager;
+using UnityEngine;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class UnityCliSetupServiceTests
+    {
+        [Test]
+        public void CliPathResolver_PrefersTheLiveWindowPath()
+        {
+            var assembly = typeof(AssignMaterialCommand).Assembly;
+            var resolverType = assembly.GetType("McpUnity.Extensions.Setup.UnityCliPathResolver");
+
+            Assert.That(resolverType, Is.Not.Null);
+            var resolve = resolverType.GetMethod("Resolve", BindingFlags.Public | BindingFlags.Static);
+            var result = resolve.Invoke(null, new object[] { "/window/unity", "/environment/unity" });
+            var executablePath = (string)result.GetType().GetProperty("ExecutablePath").GetValue(result);
+
+            Assert.That(executablePath, Is.EqualTo("/window/unity"));
+        }
+
+        [Test]
+        public void CliPathResolver_UsesEnvironmentThenPathWhenTheLiveWindowPathIsEmpty()
+        {
+            var environment = UnityCliPathResolver.Resolve(" ", "/environment/unity");
+            var path = UnityCliPathResolver.Resolve(string.Empty, string.Empty);
+
+            Assert.That(environment.ExecutablePath, Is.EqualTo("/environment/unity"));
+            Assert.That(environment.Source, Is.EqualTo(UnityCliPathSource.Environment));
+            Assert.That(path.ExecutablePath, Is.EqualTo("unity"));
+            Assert.That(path.Source, Is.EqualTo(UnityCliPathSource.Path));
+        }
+
+        [Test]
+        public void CliPathResolver_RecognizesExplicitWindowsAbsolutePathsOnEveryEditorPlatform()
+        {
+            var resolution = UnityCliPathResolver.Resolve(@"C:\Program Files\Unity\unity.exe", null);
+
+            Assert.That(resolution.IsExplicitAbsolutePath, Is.True);
+        }
+
+        [Test]
+        public void PipelineStatus_IdentifiesExactMissingAndUntestedVersions()
+        {
+            Assert.That(UnityCliPipelineStatus.Classify("0.3.1-exp.1"), Is.EqualTo(UnityCliPipelineState.ExactSupported));
+            Assert.That(UnityCliPipelineStatus.Classify(null), Is.EqualTo(UnityCliPipelineState.Missing));
+            Assert.That(UnityCliPipelineStatus.Classify("0.3.2-exp.1"), Is.EqualTo(UnityCliPipelineState.DifferentUntested));
+            Assert.That(UnityCliPipelineStatus.GetDisplayName(UnityCliPipelineState.ExactSupported),
+                Is.EqualTo("exact supported"));
+            Assert.That(UnityCliPipelineStatus.GetDisplayName(UnityCliPipelineState.Missing), Is.EqualTo("missing"));
+            Assert.That(UnityCliPipelineStatus.GetDisplayName(UnityCliPipelineState.DifferentUntested),
+                Is.EqualTo("different/untested"));
+        }
+
+        [Test]
+        public void CliVersionClassifier_ParsesAndClassifiesCompatibilityBoundaries()
+        {
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-beta.1", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.Incompatible));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-beta.2", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.Compatible));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.Compatible));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-rc.1", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.Compatible));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-alpha.9", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.Incompatible));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 2.0.0", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.UntestedNewer));
+            Assert.That(UnityCliVersionClassifier.Classify("unknown", true, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(UnityCliVersionClassifier.Classify("unity 1.0.0-beta.2", false, false).Status,
+                Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+        }
+
+        [Test]
+        public void CliCheck_UsesOnlyVersionCommandWithFiveSecondTimeoutAndClassifiesFailures()
+        {
+            var runner = new RecordingRunner(new UnityCliProcessResult("", "not found", 127, false));
+            var service = new UnityCliCheckService(runner, () => "/environment/unity");
+
+            var result = service.CheckAsync(string.Empty, CancellationToken.None).GetAwaiter().GetResult();
+
+            Assert.That(runner.ExecutablePath, Is.EqualTo("/environment/unity"));
+            Assert.That(runner.Arguments, Is.EqualTo("--version"));
+            Assert.That(runner.Timeout, Is.EqualTo(TimeSpan.FromSeconds(5)));
+            Assert.That(result.Compatibility.Status, Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+        }
+
+        [Test]
+        public void CliCheck_ClassifiesRunnerTimeoutAsMissingOrFailed()
+        {
+            var runner = new RecordingRunner(new UnityCliProcessResult("", "", -1, true));
+            var service = new UnityCliCheckService(runner, () => null);
+
+            var result = service.CheckAsync("/opt/unity", CancellationToken.None).GetAwaiter().GetResult();
+
+            Assert.That(result.Compatibility.Status, Is.EqualTo(UnityCliCompatibility.MissingOrFailed));
+            Assert.That(result.Process.TimedOut, Is.True);
+        }
+
+        [Test]
+        public void SetupContent_UsesOfficialPlatformInstallCommands()
+        {
+            Assert.That(UnityCliSetupContent.GetInstallCommand(false), Is.EqualTo(
+                "curl -fsSL https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.sh | UNITY_CLI_CHANNEL=beta bash"));
+            Assert.That(UnityCliSetupContent.GetInstallCommand(true), Is.EqualTo(
+                "$env:UNITY_CLI_CHANNEL='beta'; irm https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.ps1 | iex"));
+            Assert.That(UnityCliSetupContent.DocumentationUrl, Is.EqualTo(
+                "https://docs.unity.com/en-us/unity-cli/use-unity-cli"));
+        }
+
+        [Test]
+        public void CliConfiguration_GeneratesEscapedOfficialAndCompanionEntries()
+        {
+            const string projectPath = "C:\\Project Space\\\"Quoted\"";
+            const string executablePath = "C:\\Program Files\\Unity\\unity.exe";
+            const string packagePath = "C:\\Package Space\\mcp-unity";
+
+            var official = JObject.Parse(UnityCliConfiguration.CreateOfficial(executablePath, projectPath));
+            var companion = JObject.Parse(UnityCliConfiguration.CreateCompanion(
+                packagePath, projectPath, executablePath, true));
+
+            Assert.That((string)official["mcpServers"]["unity"]["command"], Is.EqualTo(executablePath));
+            Assert.That(official["mcpServers"]["unity"]["args"].Values(), Is.EqualTo(new[]
+            {
+                "mcp", "--project-path", projectPath
+            }));
+            Assert.That((string)companion["mcpServers"]["mcp-unity-companion"]["command"], Is.EqualTo("node"));
+            Assert.That((string)companion["mcpServers"]["mcp-unity-companion"]["args"][0], Is.EqualTo(
+                "C:\\Package Space\\mcp-unity/Server~/build/index.js"));
+            Assert.That((string)companion["mcpServers"]["mcp-unity-companion"]["env"]["UNITY_CLI_PATH"],
+                Is.EqualTo(executablePath));
+            Assert.That(UnityCliConfiguration.CreateCompanion(packagePath, projectPath, executablePath, false),
+                Does.Not.Contain("UNITY_CLI_PATH"));
+        }
+
+        [Test]
+        public void SetupServices_DoNotWriteProjectSettingsOrConfigurationFiles()
+        {
+            var projectSettings = Path.Combine(Directory.GetParent(Application.dataPath).FullName, "ProjectSettings");
+            var before = Directory.Exists(projectSettings)
+                ? Directory.GetFiles(projectSettings, "*", SearchOption.AllDirectories)
+                : Array.Empty();
+            var runner = new RecordingRunner(new UnityCliProcessResult("unity 1.0.0-beta.2", "", 0, false));
+            var service = new UnityCliCheckService(runner, () => null);
+
+            service.CheckAsync("/opt/unity", CancellationToken.None).GetAwaiter().GetResult();
+
+            var after = Directory.Exists(projectSettings)
+                ? Directory.GetFiles(projectSettings, "*", SearchOption.AllDirectories)
+                : Array.Empty();
+            Assert.That(after, Is.EquivalentTo(before));
+        }
+
+        [Test]
+        public void SetupWindow_IsUserInitiatedWithoutStaticInitialization()
+        {
+            var package = PackageInfo.FindForAssembly(typeof(AssignMaterialCommand).Assembly);
+            var windowPath = Path.Combine(package.assetPath, "Editor/Setup/UnityCliSetupWindow.cs");
+
+            Assert.That(File.Exists(windowPath), Is.True);
+            var source = File.ReadAllText(windowPath);
+            Assert.That(source, Does.Contain("[MenuItem(\"Window/MCP Unity/Setup\")]"));
+            Assert.That(source, Does.Not.Contain("InitializeOnLoad"));
+            Assert.That(source, Does.Not.Contain("[InitializeOnLoad"));
+        }
+
+        private sealed class RecordingRunner : IUnityCliProcessRunner
+        {
+            private readonly UnityCliProcessResult result;
+
+            public RecordingRunner(UnityCliProcessResult result)
+            {
+                this.result = result;
+            }
+
+            public string ExecutablePath { get; private set; }
+
+            public string Arguments { get; private set; }
+
+            public TimeSpan Timeout { get; private set; }
+
+            public Task RunAsync(
+                string executablePath,
+                string arguments,
+                TimeSpan timeout,
+                CancellationToken cancellationToken)
+            {
+                ExecutablePath = executablePath;
+                Arguments = arguments;
+                Timeout = timeout;
+                return Task.FromResult(result);
+            }
+        }
+    }
+}
diff --git a/Editor/Tests/UnityCliSetupServiceTests.cs.meta b/Editor/Tests/UnityCliSetupServiceTests.cs.meta
new file mode 100644
index 00000000..4320cab3
--- /dev/null
+++ b/Editor/Tests/UnityCliSetupServiceTests.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 57a475b1581b74609b7a8dbf14a334f6
\ No newline at end of file
diff --git a/Editor/Tests/UnloadSceneCommandTests.cs b/Editor/Tests/UnloadSceneCommandTests.cs
new file mode 100644
index 00000000..5fdcdf42
--- /dev/null
+++ b/Editor/Tests/UnloadSceneCommandTests.cs
@@ -0,0 +1,113 @@
+using System;
+using System.IO;
+using System.Reflection;
+using McpUnity.Extensions.Commands;
+using NUnit.Framework;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+
+namespace McpUnity.Extensions.Tests
+{
+    public class UnloadSceneCommandTests
+    {
+        private const string Root = "Assets/__McpUnityUnloadSceneTests";
+
+        [SetUp]
+        public void SetUp()
+        {
+            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+            EnsureFolder(Root);
+        }
+
+        [TearDown]
+        public void TearDown()
+        {
+            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+            AssetDatabase.DeleteAsset(Root);
+            AssetDatabase.Refresh();
+        }
+
+        [Test]
+        public void Unload_RejectsSceneThatIsNotLoaded()
+        {
+            var path = Root + "/SavedButClosed.unity";
+            CreateScene(path, NewSceneMode.Single);
+            EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+
+            Assert.Throws(() => UnloadSceneCommand.Unload(path));
+        }
+
+        [Test]
+        public void Unload_RejectsDirtySceneUnlessForced()
+        {
+            CreateScene(Root + "/Active.unity", NewSceneMode.Single);
+            var targetPath = Root + "/Dirty.unity";
+            var target = CreateScene(targetPath, NewSceneMode.Additive);
+            SceneManager.MoveGameObjectToScene(new GameObject("Unsaved"), target);
+            EditorSceneManager.MarkSceneDirty(target);
+
+            Assert.Throws(() => UnloadSceneCommand.Unload(targetPath));
+            Assert.That(SceneManager.GetSceneByPath(targetPath).isLoaded, Is.True);
+        }
+
+        [Test]
+        public void Unload_RejectsSoleLoadedActiveScene()
+        {
+            var path = Root + "/Only.unity";
+            CreateScene(path, NewSceneMode.Single);
+
+            Assert.Throws(() => UnloadSceneCommand.Unload(path, force: true));
+            Assert.That(SceneManager.GetActiveScene().path, Is.EqualTo(path));
+        }
+
+        [Test]
+        public void Unload_ForcedDirtyActiveSceneChoosesDeterministicAlternative()
+        {
+            var zPath = Root + "/Zeta.unity";
+            var aPath = Root + "/Alpha.unity";
+            var targetPath = Root + "/Target.unity";
+            CreateScene(zPath, NewSceneMode.Single);
+            CreateScene(aPath, NewSceneMode.Additive);
+            var target = CreateScene(targetPath, NewSceneMode.Additive);
+            SceneManager.SetActiveScene(target);
+            SceneManager.MoveGameObjectToScene(new GameObject("Unsaved"), target);
+            EditorSceneManager.MarkSceneDirty(target);
+
+            object result = null;
+            Assert.DoesNotThrow(() => result = UnloadSceneCommand.Unload(targetPath, force: true));
+
+            Assert.That(SceneManager.GetSceneByPath(targetPath).isLoaded, Is.False);
+            Assert.That(SceneManager.GetActiveScene().path, Is.EqualTo(aPath));
+            Assert.That(Property(result, "UnloadedPath"), Is.EqualTo(targetPath));
+            Assert.That(Property(result, "ActiveScenePath"), Is.EqualTo(aPath));
+        }
+
+        private static Scene CreateScene(string path, NewSceneMode mode)
+        {
+            var scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, mode);
+            Assert.That(EditorSceneManager.SaveScene(scene, path), Is.True);
+            return scene;
+        }
+
+        private static void EnsureFolder(string path)
+        {
+            if (AssetDatabase.IsValidFolder(path))
+                return;
+
+            var parent = Path.GetDirectoryName(path)?.Replace('\\', '/');
+            if (!string.IsNullOrEmpty(parent) && !AssetDatabase.IsValidFolder(parent))
+                EnsureFolder(parent);
+            AssetDatabase.CreateFolder(parent, Path.GetFileName(path));
+        }
+
+        private static T Property(object instance, string name)
+        {
+            Assert.That(instance, Is.Not.Null);
+            var property = instance.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public);
+            Assert.That(property, Is.Not.Null, $"Expected public property '{name}'.");
+            return (T)property.GetValue(instance);
+        }
+    }
+}
diff --git a/Editor/Tests/UnloadSceneCommandTests.cs.meta b/Editor/Tests/UnloadSceneCommandTests.cs.meta
new file mode 100644
index 00000000..923786e6
--- /dev/null
+++ b/Editor/Tests/UnloadSceneCommandTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 54480e3aad7d4814be7cf36699aa55b7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Editor/Tests/UpdateComponentToolTests.cs b/Editor/Tests/UpdateComponentToolTests.cs
deleted file mode 100644
index 88fac936..00000000
--- a/Editor/Tests/UpdateComponentToolTests.cs
+++ /dev/null
@@ -1,149 +0,0 @@
-using System;
-using McpUnity.Tools;
-using McpUnity.Utils;
-using Newtonsoft.Json.Linq;
-using NUnit.Framework;
-using UnityEditor;
-using UnityEngine;
-
-namespace McpUnity.Tests
-{
-    public class UpdateComponentToolTests
-    {
-        private const string TestAssetDir = "Assets/UpdateComponentToolTests";
-        private const string TestAssetPath = TestAssetDir + "/ReferencedAsset.asset";
-
-        private GameObject _gameObject;
-        private ScriptableObject _referencedAsset;
-
-        [SetUp]
-        public void SetUp()
-        {
-            _gameObject = new GameObject("UpdateComponentToolTestObject");
-
-            if (!AssetDatabase.IsValidFolder(TestAssetDir))
-            {
-                AssetDatabase.CreateFolder("Assets", "UpdateComponentToolTests");
-            }
-
-            _referencedAsset = ScriptableObject.CreateInstance();
-            AssetDatabase.CreateAsset(_referencedAsset, TestAssetPath);
-            AssetDatabase.SaveAssets();
-        }
-
-        [TearDown]
-        public void TearDown()
-        {
-            if (_gameObject != null)
-            {
-                UnityEngine.Object.DestroyImmediate(_gameObject);
-            }
-
-            if (AssetDatabase.IsValidFolder(TestAssetDir))
-            {
-                AssetDatabase.DeleteAsset(TestAssetDir);
-            }
-
-            AssetDatabase.Refresh();
-        }
-
-        [Test]
-        public void Execute_WithPrivateSerializedFieldInBaseClass_UpdatesValue()
-        {
-            var component = _gameObject.AddComponent();
-            var tool = new UpdateComponentTool();
-
-            JObject result = tool.Execute(new JObject
-            {
-                ["instanceId"] = UnityObjectId.GetObjectId(_gameObject),
-                ["componentName"] = nameof(DerivedUpdateComponentToolTestComponent),
-                ["componentData"] = new JObject
-                {
-                    ["_baseValue"] = 42
-                }
-            });
-
-            Assert.IsTrue(result["success"]?.Value() ?? false, result.ToString());
-            Assert.AreEqual(42, component.BaseValue);
-        }
-
-        [Test]
-        public void Execute_WithNestedObjectReferencePath_UpdatesReference()
-        {
-            var component = _gameObject.AddComponent();
-            var tool = new UpdateComponentTool();
-
-            JObject result = tool.Execute(new JObject
-            {
-                ["instanceId"] = UnityObjectId.GetObjectId(_gameObject),
-                ["componentName"] = nameof(UpdateComponentToolTestComponent),
-                ["componentData"] = new JObject
-                {
-                    ["_eventReference._event"] = TestAssetPath
-                }
-            });
-
-            Assert.IsTrue(result["success"]?.Value() ?? false, result.ToString());
-            Assert.AreSame(_referencedAsset, component.EventReference);
-        }
-
-        [Test]
-        public void Execute_WithMissingObjectReferencePath_ReturnsUpdateError()
-        {
-            var component = _gameObject.AddComponent();
-            component.SetEventReference(_referencedAsset);
-            var tool = new UpdateComponentTool();
-
-            JObject result = tool.Execute(new JObject
-            {
-                ["instanceId"] = UnityObjectId.GetObjectId(_gameObject),
-                ["componentName"] = nameof(UpdateComponentToolTestComponent),
-                ["componentData"] = new JObject
-                {
-                    ["_eventReference._event"] = "Assets/UpdateComponentToolTests/Missing.asset"
-                }
-            });
-
-            Assert.IsNotNull(result["error"], result.ToString());
-            StringAssert.Contains("Could not find asset", result["error"]["message"]?.ToString());
-            Assert.AreSame(_referencedAsset, component.EventReference);
-        }
-
-    }
-
-    [Serializable]
-    public class NestedEventReference
-    {
-        [SerializeField] private ScriptableObject _event;
-
-        public ScriptableObject Event => _event;
-
-        public void SetEvent(ScriptableObject value)
-        {
-            _event = value;
-        }
-    }
-
-    public class UpdateComponentToolTestComponent : MonoBehaviour
-    {
-        [SerializeField] private NestedEventReference _eventReference = new NestedEventReference();
-
-        public ScriptableObject EventReference => _eventReference.Event;
-
-        public void SetEventReference(ScriptableObject value)
-        {
-            _eventReference.SetEvent(value);
-        }
-    }
-
-    public class BaseUpdateComponentToolTestComponent : MonoBehaviour
-    {
-        [SerializeField] private int _baseValue;
-
-        public int BaseValue => _baseValue;
-    }
-
-    public class DerivedUpdateComponentToolTestComponent : BaseUpdateComponentToolTestComponent
-    {
-    }
-}
diff --git a/Editor/Tests/UpdateComponentToolTests.cs.meta b/Editor/Tests/UpdateComponentToolTests.cs.meta
deleted file mode 100644
index 69e0a483..00000000
--- a/Editor/Tests/UpdateComponentToolTests.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 1f7d6d963fd14294a8e91f27a14851b2
diff --git a/Editor/Tools.meta b/Editor/Tools.meta
deleted file mode 100644
index 199b3f76..00000000
--- a/Editor/Tools.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: 8e2c205bdbb4464498b907b56521fa48
-timeCreated: 1741796067
\ No newline at end of file
diff --git a/Editor/Tools/AddAssetToSceneTool.cs b/Editor/Tools/AddAssetToSceneTool.cs
deleted file mode 100644
index e0c12903..00000000
--- a/Editor/Tools/AddAssetToSceneTool.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for adding assets from the AssetDatabase to the Unity scene
-    /// 
-    public class AddAssetToSceneTool : McpToolBase
-    {
-        public AddAssetToSceneTool()
-        {
-            Name = "add_asset_to_scene";
-            Description = "Adds an asset from the AssetDatabase to the Unity scene";
-        }
-        
-        /// 
-        /// Execute the AddAssetToScene tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            string assetPath = parameters["assetPath"]?.ToObject();
-            string guid = parameters["guid"]?.ToObject();
-            Vector3 position = parameters["position"]?.ToObject() != null 
-                ? new Vector3(
-                    parameters["position"]["x"]?.ToObject() ?? 0f,
-                    parameters["position"]["y"]?.ToObject() ?? 0f,
-                    parameters["position"]["z"]?.ToObject() ?? 0f
-                ) 
-                : Vector3.zero;
-            
-            // Optional parent game object
-            string parentPath = parameters["parentPath"]?.ToObject();
-            int? parentId = parameters["parentId"]?.ToObject();
-            
-            // Validate parameters - require either assetPath or guid
-            if (string.IsNullOrEmpty(assetPath) && string.IsNullOrEmpty(guid))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'assetPath' or 'guid' not provided", 
-                    "validation_error"
-                );
-            }
-            
-            // If we have a GUID but no path, convert GUID to path
-            if (string.IsNullOrEmpty(assetPath) && !string.IsNullOrEmpty(guid))
-            {
-                assetPath = AssetDatabase.GUIDToAssetPath(guid);
-                if (string.IsNullOrEmpty(assetPath))
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Asset with GUID '{guid}' not found", 
-                        "not_found_error"
-                    );
-                }
-            }
-            
-            // Load the asset
-            UnityEngine.Object asset = AssetDatabase.LoadAssetAtPath(assetPath);
-            if (asset == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Failed to load asset at path '{assetPath}'", 
-                    "not_found_error"
-                );
-            }
-            
-            // Check if the asset is a prefab or another type that can be instantiated
-            bool isPrefab = PrefabUtility.GetPrefabAssetType(asset) != PrefabAssetType.NotAPrefab;
-            bool canInstantiate = asset is GameObject || isPrefab;
-            
-            if (!canInstantiate)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Asset of type '{asset.GetType().Name}' cannot be instantiated in the scene", 
-                    "invalid_asset_type"
-                );
-            }
-            
-            // Instantiate the asset
-            GameObject instance = null;
-            try
-            {
-                instance = (GameObject)PrefabUtility.InstantiatePrefab(asset);
-                
-                // Set position
-                instance.transform.position = position;
-                
-                // Set parent if specified
-                if (!string.IsNullOrEmpty(parentPath) || parentId.HasValue)
-                {
-                    GameObject parent = null;
-                    
-                    // Try to find parent by ID first
-                    if (parentId.HasValue)
-                    {
-                        parent = UnityObjectId.ObjectFromId(parentId.Value) as GameObject;
-                    }
-                    // Otherwise try to find by path
-                    else if (!string.IsNullOrEmpty(parentPath))
-                    {
-                        parent = GameObject.Find(parentPath);
-                    }
-                    
-                    if (parent != null)
-                    {
-                        instance.transform.SetParent(parent.transform, false);
-                    }
-                    else
-                    {
-                        McpLogger.LogWarning($"Parent object not found, asset will be created at the root of the scene");
-                    }
-                }
-                
-                // Select the newly created object
-                Selection.activeGameObject = instance;
-                EditorGUIUtility.PingObject(instance);
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error instantiating asset: {ex.Message}", 
-                    "instantiation_error"
-                );
-            }
-            
-            // Log the action
-            McpLogger.LogInfo($"Added asset '{asset.name}' to scene from path '{assetPath}'");
-            
-            // Create the response
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Successfully added asset '{asset.name}' with instance ID {UnityObjectId.GetObjectId(instance)} to the scene",
-                ["instanceId"] = UnityObjectId.GetObjectId(instance)
-            };
-        }
-    }
-}
diff --git a/Editor/Tools/AddAssetToSceneTool.cs.meta b/Editor/Tools/AddAssetToSceneTool.cs.meta
deleted file mode 100644
index 9eecf061..00000000
--- a/Editor/Tools/AddAssetToSceneTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: ef39157e609269944a62ac7391f9008e
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/AddPackageTool.cs b/Editor/Tools/AddPackageTool.cs
deleted file mode 100644
index a0f3a388..00000000
--- a/Editor/Tools/AddPackageTool.cs
+++ /dev/null
@@ -1,342 +0,0 @@
-using System;
-using System.Collections.Generic;
-using UnityEditor;
-using UnityEditor.PackageManager;
-using UnityEditor.PackageManager.Requests;
-using Newtonsoft.Json.Linq;
-using System.Threading.Tasks;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for adding new packages into the Unity Package Manager
-    /// 
-    public class AddPackageTool : McpToolBase
-    {
-        // Class to track each package operation
-        private class PackageOperation
-        {
-            public AddRequest Request { get; set; }
-            public TaskCompletionSource CompletionSource { get; set; }
-        }
-        
-        // Queue of active package operations
-        private readonly List _activeOperations = new List();
-        
-        // Flag to track if the update callback is registered
-        private bool _updateCallbackRegistered = false;
-        
-        public AddPackageTool()
-        {
-            Name = "add_package";
-            Description = "Adds a new packages into the Unity Package Manager";
-            IsAsync = true; // Package Manager operations are asynchronous
-        }
-        
-        /// 
-        /// Execute the AddPackage tool asynchronously
-        /// 
-        /// Tool parameters as a JObject
-        /// TaskCompletionSource to set the result or exception
-        public override void ExecuteAsync(JObject parameters, TaskCompletionSource tcs)
-        {
-            // Extract source parameter
-            string source = parameters["source"]?.ToObject();
-            if (string.IsNullOrEmpty(source))
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'source' not provided", 
-                    "validation_error"
-                ));
-                return;
-            }
-            
-            // Create and register the operation
-            var operation = new PackageOperation
-            {
-                CompletionSource = tcs
-            };
-            
-            switch (source.ToLowerInvariant())
-            {
-                case "registry":
-                    operation.Request = AddFromRegistry(parameters, tcs);
-                    break;
-                case "github":
-                    operation.Request = AddFromGitHub(parameters, tcs);
-                    break;
-                case "disk":
-                    operation.Request = AddFromDisk(parameters, tcs);
-                    break;
-                default:
-                    tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                        $"Unknown method '{source}'. Valid methods are: registry, github, disk",
-                        "validation_error"
-                    ));
-                    return;
-            }
-            
-            // If request creation failed, the error has already been set on the tcs
-            if (operation.Request == null)
-            {
-                return;
-            }
-            
-            lock (_activeOperations)
-            {
-                _activeOperations.Add(operation);
-                
-                // Register update callback if not already registered
-                if (!_updateCallbackRegistered)
-                {
-                    EditorApplication.update += CheckOperationsCompletion;
-                    _updateCallbackRegistered = true;
-                }
-            }
-        }
-        
-        /// 
-        /// Add a package from the Unity registry
-        /// 
-        private AddRequest AddFromRegistry(JObject parameters, TaskCompletionSource tcs)
-        {
-            // Extract parameters
-            string packageName = parameters["packageName"]?.ToObject();
-            if (string.IsNullOrEmpty(packageName))
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'packageName' not provided for registry method", 
-                    "validation_error"
-                ));
-                return null;
-            }
-            
-            string version = parameters["version"]?.ToObject();
-            string packageIdentifier = packageName;
-            
-            // Add version if specified
-            if (!string.IsNullOrEmpty(version))
-            {
-                packageIdentifier = $"{packageName}@{version}";
-            }
-            
-            McpLogger.LogInfo($"Adding package from registry: {packageIdentifier}");
-            
-            try
-            {
-                // Add the package
-                return Client.Add(packageIdentifier);
-            }
-            catch (Exception ex)
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    $"Exception adding package: {ex.Message}",
-                    "package_manager_error"
-                ));
-                return null;
-            }
-        }
-        
-        /// 
-        /// Add a package from GitHub
-        /// 
-        private AddRequest AddFromGitHub(JObject parameters, TaskCompletionSource tcs)
-        {
-            // Extract parameters
-            string packageUrl = parameters["repositoryUrl"]?.ToObject();
-            
-            if (string.IsNullOrEmpty(packageUrl))
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'repositoryUrl' not provided for github method", 
-                    "validation_error"
-                ));
-                return null;
-            }
-            
-            string branch = parameters["branch"]?.ToObject();
-            string path = parameters["path"]?.ToObject();
-            
-            // Remove any .git suffix if present
-            if (packageUrl.EndsWith(".git", StringComparison.OrdinalIgnoreCase))
-            {
-                packageUrl = packageUrl.Substring(0, packageUrl.Length - 4);
-            }
-            
-            // Add branch if specified
-            if (!string.IsNullOrEmpty(branch))
-            {
-                packageUrl += "#" + branch;
-            }
-            
-            // Add path if specified
-            if (!string.IsNullOrEmpty(path))
-            {
-                if (!string.IsNullOrEmpty(branch))
-                {
-                    // Branch is already added, append path with slash
-                    packageUrl += "/" + path;
-                }
-                else
-                {
-                    // No branch, use hash followed by path
-                    packageUrl += "#" + path;
-                }
-            }
-            
-            McpLogger.LogInfo($"Adding package from GitHub: {packageUrl}");
-            
-            try
-            {
-                // Add the package
-                return Client.Add(packageUrl);
-            }
-            catch (Exception ex)
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    $"Exception adding package: {ex.Message}",
-                    "package_manager_error"
-                ));
-                return null;
-            }
-        }
-        
-        /// 
-        /// Add a package from disk
-        /// 
-        private AddRequest AddFromDisk(JObject parameters, TaskCompletionSource tcs)
-        {
-            // Extract parameters
-            string path = parameters["path"]?.ToObject();
-            
-            if (string.IsNullOrEmpty(path))
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'path' not provided for disk method", 
-                    "validation_error"
-                ));
-                return null;
-            }
-            
-            // Format as file URL with proper encoding for paths containing spaces
-            string encodedPath = McpUtils.EncodePathForFileUrl(path);
-            string packageUrl = $"file:{encodedPath}";
-            
-            McpLogger.LogInfo($"Adding package from disk: {packageUrl}");
-            
-            try
-            {
-                // Add the package
-                return Client.Add(packageUrl);
-            }
-            catch (Exception ex)
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    $"Exception adding package: {ex.Message}",
-                    "package_manager_error"
-                ));
-                return null;
-            }
-        }
-        
-        /// 
-        /// Check all active operations for completion
-        /// 
-        private void CheckOperationsCompletion()
-        {
-            // Store initial count
-            int initialCount = _activeOperations.Count;
-            
-            lock (_activeOperations)
-            {
-                // Process operations in reverse order to safely remove completed ones
-                for (int i = _activeOperations.Count - 1; i >= 0; i--)
-                {
-                    var operation = _activeOperations[i];
-                    
-                    if (operation.Request != null && operation.Request.IsCompleted)
-                    {
-                        // Process the completed operation
-                        ProcessCompletedOperation(operation);
-                        
-                        // Remove it from the active operations list
-                        _activeOperations.RemoveAt(i);
-                    }
-                }
-                
-                // If all operations are completed, unregister the update callback
-                if (_activeOperations.Count == 0 && _updateCallbackRegistered)
-                {
-                    EditorApplication.update -= CheckOperationsCompletion;
-                    _updateCallbackRegistered = false;
-                }
-            }
-            
-            // If any operations completed, force a GC collection to clean up UPM request objects
-            if (initialCount != _activeOperations.Count)
-            {
-                GC.Collect();
-            }
-        }
-        
-        /// 
-        /// Process a completed package operation
-        /// 
-        private void ProcessCompletedOperation(PackageOperation operation)
-        {
-            if (operation.CompletionSource == null)
-            {
-                McpLogger.LogError("TaskCompletionSource is null when processing completed operation");
-                return;
-            }
-            
-            // Check request status
-            if (operation.Request.Status == StatusCode.Success)
-            {
-                var result = operation.Request.Result;
-                if (result != null)
-                {
-                    operation.CompletionSource.SetResult(new JObject
-                    {
-                        ["success"] = true,
-                        ["type"] = "text",
-                        ["message"] = $"Successfully added package: {result.displayName} ({result.name}) version {result.version}",
-                        ["packageInfo"] = JObject.FromObject(new
-                        {
-                            name = result.name,
-                            displayName = result.displayName,
-                            version = result.version
-                        })
-                    });
-                }
-                else
-                {
-                    operation.CompletionSource.SetResult(new JObject
-                    {
-                        ["success"] = true,
-                        ["type"] = "text",
-                        ["message"] = $"Package operation completed successfully, but no package information was returned."
-                    });
-                }
-                
-                McpLogger.LogInfo($"Added package {result.displayName} ({result.name}) version {result.version}");
-            }
-            else if (operation.Request.Status == StatusCode.Failure)
-            {
-                operation.CompletionSource.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    $"Failed to add package: {operation.Request.Error.message}",
-                    "package_manager_error"
-                ));
-            }
-            else
-            {
-                operation.CompletionSource.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    $"Unknown package manager status: {operation.Request.Status}",
-                    "package_manager_error"
-                ));
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/AddPackageTool.cs.meta b/Editor/Tools/AddPackageTool.cs.meta
deleted file mode 100644
index a6191efe..00000000
--- a/Editor/Tools/AddPackageTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 3d0262aa8c7e0e64881ea66fde6bbc79
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/BatchExecuteTool.cs b/Editor/Tools/BatchExecuteTool.cs
deleted file mode 100644
index 3abef13a..00000000
--- a/Editor/Tools/BatchExecuteTool.cs
+++ /dev/null
@@ -1,312 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using UnityEngine;
-using UnityEditor;
-using McpUnity.Unity;
-using Newtonsoft.Json.Linq;
-using Unity.EditorCoroutines.Editor;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for executing multiple operations in a single batch request.
-    /// Supports sequential execution, stop-on-error, and atomic rollback.
-    /// 
-    public class BatchExecuteTool : McpToolBase
-    {
-        private readonly Func _getTool;
-
-        public BatchExecuteTool(McpUnityServer server)
-            : this(name => server != null && server.TryGetTool(name, out McpToolBase tool) ? tool : null)
-        {
-        }
-
-        public BatchExecuteTool(Func getTool)
-        {
-            _getTool = getTool ?? (_ => null);
-            Name = "batch_execute";
-            Description = "Executes multiple tool operations in a single batch request. Reduces round-trips and enables atomic operations.";
-            IsAsync = true;
-        }
-
-        public override void ExecuteAsync(JObject parameters, TaskCompletionSource tcs)
-        {
-            EditorCoroutineUtility.StartCoroutineOwnerless(ExecuteBatchCoroutine(parameters, tcs));
-        }
-
-        private IEnumerator ExecuteBatchCoroutine(JObject parameters, TaskCompletionSource tcs)
-        {
-            JArray operations = parameters["operations"] as JArray;
-            bool stopOnError = parameters["stopOnError"]?.ToObject() ?? true;
-            bool atomic = parameters["atomic"]?.ToObject() ?? false;
-
-            // Validate operations array
-            if (operations == null || operations.Count == 0)
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    "The 'operations' array is required and must contain at least one operation.",
-                    "validation_error"
-                ));
-                yield break;
-            }
-
-            // Validate max operations (prevent abuse)
-            if (operations.Count > 100)
-            {
-                tcs.SetResult(McpUnitySocketHandler.CreateErrorResponse(
-                    "Maximum of 100 operations allowed per batch.",
-                    "validation_error"
-                ));
-                yield break;
-            }
-
-            JArray results = new JArray();
-            int succeeded = 0;
-            int failed = 0;
-            int undoGroup = -1;
-
-            // Start undo group for atomic operations
-            if (atomic)
-            {
-                Undo.IncrementCurrentGroup();
-                undoGroup = Undo.GetCurrentGroup();
-                Undo.SetCurrentGroupName("Batch Execute");
-            }
-
-            for (int i = 0; i < operations.Count; i++)
-            {
-                JObject operation = operations[i] as JObject;
-                if (operation == null)
-                {
-                    results.Add(CreateOperationResult(i, null, false, null, "Invalid operation format"));
-                    failed++;
-
-                    if (stopOnError)
-                    {
-                        RevertIfAtomic(atomic, undoGroup);
-                        break;
-                    }
-                    continue;
-                }
-
-                string toolName = operation["tool"]?.ToString();
-                JObject toolParams = operation["params"] as JObject ?? new JObject();
-                string operationId = operation["id"]?.ToString() ?? i.ToString();
-
-                // Validate tool name
-                if (string.IsNullOrEmpty(toolName))
-                {
-                    results.Add(CreateOperationResult(i, operationId, false, null, "Missing 'tool' name in operation"));
-                    failed++;
-
-                    if (stopOnError)
-                    {
-                        RevertIfAtomic(atomic, undoGroup);
-                        break;
-                    }
-                    continue;
-                }
-
-                // Prevent recursive batch execution
-                if (toolName == Name)
-                {
-                    results.Add(CreateOperationResult(i, operationId, false, null, "Cannot nest batch_execute operations"));
-                    failed++;
-
-                    if (stopOnError)
-                    {
-                        RevertIfAtomic(atomic, undoGroup);
-                        break;
-                    }
-                    continue;
-                }
-
-                // Get the tool
-                McpToolBase tool = _getTool(toolName);
-                if (tool == null)
-                {
-                    results.Add(CreateOperationResult(i, operationId, false, null, $"Unknown tool: {toolName}"));
-                    failed++;
-
-                    if (stopOnError)
-                    {
-                        RevertIfAtomic(atomic, undoGroup);
-                        break;
-                    }
-                    continue;
-                }
-
-                // Execute the tool
-                JObject toolResult = null;
-                Exception toolException = null;
-
-                if (tool.IsAsync)
-                {
-                    var toolTcs = new TaskCompletionSource();
-
-                    try
-                    {
-                        tool.ExecuteAsync(toolParams, toolTcs);
-                    }
-                    catch (Exception ex)
-                    {
-                        toolException = ex;
-                    }
-
-                    // Wait for async tool completion (yield must be outside try-catch)
-                    if (toolException == null)
-                    {
-                        while (!toolTcs.Task.IsCompleted)
-                        {
-                            yield return null;
-                        }
-
-                        if (toolTcs.Task.IsFaulted)
-                        {
-                            toolException = toolTcs.Task.Exception?.InnerException ?? toolTcs.Task.Exception;
-                        }
-                        else
-                        {
-                            toolResult = toolTcs.Task.Result;
-                        }
-                    }
-                }
-                else
-                {
-                    try
-                    {
-                        toolResult = tool.Execute(toolParams);
-                    }
-                    catch (Exception ex)
-                    {
-                        toolException = ex;
-                    }
-                }
-
-                // Process result
-                if (toolException != null)
-                {
-                    results.Add(CreateOperationResult(i, operationId, false, null, toolException.Message));
-                    failed++;
-
-                    if (stopOnError)
-                    {
-                        RevertIfAtomic(atomic, undoGroup);
-                        break;
-                    }
-                }
-                else if (toolResult != null)
-                {
-                    // Check if the result indicates an error
-                    bool isError = toolResult["error"] != null;
-                    bool isSuccess = toolResult["success"]?.ToObject() ?? !isError;
-
-                    if (isSuccess && !isError)
-                    {
-                        results.Add(CreateOperationResult(i, operationId, true, toolResult, null));
-                        succeeded++;
-                    }
-                    else
-                    {
-                        string errorMessage = toolResult["error"]?["message"]?.ToString()
-                            ?? toolResult["message"]?.ToString()
-                            ?? "Tool execution failed";
-                        results.Add(CreateOperationResult(i, operationId, false, toolResult, errorMessage));
-                        failed++;
-
-                        if (stopOnError)
-                        {
-                            RevertIfAtomic(atomic, undoGroup);
-                            break;
-                        }
-                    }
-                }
-                else
-                {
-                    results.Add(CreateOperationResult(i, operationId, false, null, "Tool returned null result"));
-                    failed++;
-
-                    if (stopOnError)
-                    {
-                        RevertIfAtomic(atomic, undoGroup);
-                        break;
-                    }
-                }
-
-                // Yield to allow Unity to process other events
-                yield return null;
-            }
-
-            // Collapse undo group
-            if (atomic && undoGroup >= 0 && failed == 0)
-            {
-                Undo.CollapseUndoOperations(undoGroup);
-            }
-
-            // Build response
-            string message;
-            if (failed == 0)
-            {
-                message = $"Successfully executed {succeeded}/{operations.Count} operations.";
-            }
-            else if (atomic && stopOnError)
-            {
-                message = $"Batch execution failed and rolled back. {succeeded} operations succeeded before failure.";
-            }
-            else if (stopOnError)
-            {
-                message = $"Batch execution stopped on error. {succeeded}/{operations.Count} operations succeeded.";
-            }
-            else
-            {
-                message = $"Batch execution completed with errors. {succeeded}/{operations.Count} operations succeeded, {failed} failed.";
-            }
-
-            tcs.SetResult(new JObject
-            {
-                ["success"] = failed == 0,
-                ["type"] = "text",
-                ["message"] = message,
-                ["results"] = results,
-                ["summary"] = new JObject
-                {
-                    ["total"] = operations.Count,
-                    ["succeeded"] = succeeded,
-                    ["failed"] = failed,
-                    ["executed"] = succeeded + failed
-                }
-            });
-        }
-
-        private void RevertIfAtomic(bool atomic, int undoGroup)
-        {
-            if (atomic && undoGroup >= 0)
-            {
-                Undo.RevertAllDownToGroup(undoGroup);
-            }
-        }
-
-        private JObject CreateOperationResult(int index, string id, bool success, JObject result, string error)
-        {
-            var operationResult = new JObject
-            {
-                ["index"] = index,
-                ["id"] = id ?? index.ToString(),
-                ["success"] = success
-            };
-
-            if (success && result != null)
-            {
-                operationResult["result"] = result;
-            }
-            else if (!success)
-            {
-                operationResult["error"] = error ?? "Unknown error";
-            }
-
-            return operationResult;
-        }
-    }
-}
diff --git a/Editor/Tools/BatchExecuteTool.cs.meta b/Editor/Tools/BatchExecuteTool.cs.meta
deleted file mode 100644
index a52d6609..00000000
--- a/Editor/Tools/BatchExecuteTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: ff9afbf78278c4235b9d82d2cb8b824e
\ No newline at end of file
diff --git a/Editor/Tools/CreatePrefabTool.cs b/Editor/Tools/CreatePrefabTool.cs
deleted file mode 100644
index 5204f273..00000000
--- a/Editor/Tools/CreatePrefabTool.cs
+++ /dev/null
@@ -1,175 +0,0 @@
-using System;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for creating prefabs with optional MonoBehaviour scripts
-    /// 
-    public class CreatePrefabTool : McpToolBase
-    {
-        public CreatePrefabTool()
-        {
-            Name = "create_prefab";
-            Description = "Creates a prefab with optional MonoBehaviour script and serialized field values";
-        }
-        
-        /// 
-        /// Execute the CreatePrefab tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            string componentName = parameters["componentName"]?.ToObject();
-            string prefabName = parameters["prefabName"]?.ToObject();
-            JObject fieldValues = parameters["fieldValues"]?.ToObject();
-            
-            // Validate required parameters
-            if (string.IsNullOrEmpty(prefabName))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'prefabName' not provided", 
-                    "validation_error"
-                );
-            }
-            
-            // Create a temporary GameObject
-            GameObject tempObject = new GameObject(prefabName);
-
-            // Add component if provided
-            if (!string.IsNullOrEmpty(componentName))
-            {
-                try
-                {
-                    // Add component
-                    Component component = AddComponent(tempObject, componentName);
-            
-                    // Apply field values if provided and component exists
-                    ApplyFieldValues(fieldValues, component);
-                }
-                catch (Exception)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Failed to add component '{componentName}' to GameObject", 
-                        "component_error"
-                    );
-                }
-            }
-            
-            // For safety, we'll create a unique name if prefab already exists
-            int counter = 1;
-            string prefabPath = $"{prefabName}.prefab";
-            while (AssetDatabase.AssetPathToGUID(prefabPath) != "")
-            {
-                prefabPath = $"{prefabName}_{counter}.prefab";
-                counter++;
-            }
-            
-            // Create the prefab
-            bool success = false;
-            PrefabUtility.SaveAsPrefabAsset(tempObject, prefabPath, out success);
-            
-            // Clean up temporary object
-            UnityEngine.Object.DestroyImmediate(tempObject);
-            
-            // Refresh the asset database
-            AssetDatabase.Refresh();
-            
-            // Log the action
-            McpLogger.LogInfo($"Created prefab '{prefabName}' at path '{prefabPath}' from script '{componentName}'");
-
-            string message = success ? $"Successfully created prefab '{prefabName}' at path '{prefabPath}'" : $"Failed to create prefab '{prefabName}' at path '{prefabPath}'";
-            
-            // Create the response
-            return new JObject
-            {
-                ["success"] = success,
-                ["type"] = "text",
-                ["message"] = message,
-                ["prefabPath"] = prefabPath
-            };
-        }
-
-        private Component AddComponent(GameObject gameObject, string componentName)
-        {
-            // Find the script type
-            Type scriptType = Type.GetType($"{componentName}, Assembly-CSharp");
-            if (scriptType == null)
-            {
-                // Try with just the class name
-                scriptType = Type.GetType(componentName);
-            }
-                
-            if (scriptType == null)
-            {
-                // Try to find the type using AppDomain
-                foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
-                {
-                    scriptType = assembly.GetType(componentName);
-                    if (scriptType != null)
-                        break;
-                }
-            }
-                
-            // Throw an error if the type was not found
-            if (scriptType == null)
-            {
-                return null;
-            }
-                
-            // Check if the type is a MonoBehaviour
-            if (!typeof(MonoBehaviour).IsAssignableFrom(scriptType))
-            {
-                return null;
-            }
-            
-            return gameObject.AddComponent(scriptType);
-        }
-
-        private void ApplyFieldValues(JObject fieldValues, Component component)
-        {
-            // Apply field values if provided and component exists
-            if (fieldValues == null || fieldValues.Count == 0)
-            {
-                return;
-            }
-            
-            Undo.RecordObject(component, "Set field values");
-                
-            foreach (var property in fieldValues.Properties())
-            {
-                // Get the field/property info
-                var fieldInfo = component.GetType().GetField(property.Name, 
-                    System.Reflection.BindingFlags.Public | 
-                    System.Reflection.BindingFlags.NonPublic | 
-                    System.Reflection.BindingFlags.Instance);
-                            
-                if (fieldInfo != null)
-                {
-                    // Set field value
-                    object value = property.Value.ToObject(fieldInfo.FieldType);
-                    fieldInfo.SetValue(component, value);
-                }
-                else
-                {
-                    // Try property
-                    var propInfo = component.GetType().GetProperty(property.Name, 
-                        System.Reflection.BindingFlags.Public | 
-                        System.Reflection.BindingFlags.NonPublic | 
-                        System.Reflection.BindingFlags.Instance);
-                                
-                    if (propInfo != null && propInfo.CanWrite)
-                    {
-                        object value = property.Value.ToObject(propInfo.PropertyType);
-                        propInfo.SetValue(component, value);
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/CreatePrefabTool.cs.meta b/Editor/Tools/CreatePrefabTool.cs.meta
deleted file mode 100644
index 9b3afce5..00000000
--- a/Editor/Tools/CreatePrefabTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 1fe1226cc07d8a149aa31786552a43ca
\ No newline at end of file
diff --git a/Editor/Tools/CreateSceneTool.cs b/Editor/Tools/CreateSceneTool.cs
deleted file mode 100644
index 267c0118..00000000
--- a/Editor/Tools/CreateSceneTool.cs
+++ /dev/null
@@ -1,139 +0,0 @@
-using System;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for creating and saving a new Unity scene
-    /// 
-    public class CreateSceneTool : McpToolBase
-    {
-        public CreateSceneTool()
-        {
-            Name = "create_scene";
-            Description = "Creates a new scene and saves it to the specified path";
-        }
-
-        /// 
-        /// Execute the CreateScene tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            // Parameters
-            string sceneName = parameters["sceneName"]?.ToObject();
-            string folderPath = parameters["folderPath"]?.ToObject();
-            bool addToBuildSettings = parameters["addToBuildSettings"]?.ToObject() ?? false;
-            bool makeActive = parameters["makeActive"]?.ToObject() ?? true;
-
-            if (string.IsNullOrEmpty(sceneName))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'sceneName' not provided",
-                    "validation_error"
-                );
-            }
-
-            // Default folder path
-            if (string.IsNullOrEmpty(folderPath))
-            {
-                folderPath = "Assets";
-            }
-
-            // Ensure folder exists
-            if (!AssetDatabase.IsValidFolder(folderPath))
-            {
-                // Attempt to create nested folders as needed
-                string[] parts = folderPath.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
-                string current = parts.Length > 0 && parts[0] == "Assets" ? "Assets" : "Assets";
-                for (int i = 0; i < parts.Length; i++)
-                {
-                    if (i == 0 && parts[i] == "Assets") continue;
-                    string next = current + "/" + parts[i];
-                    if (!AssetDatabase.IsValidFolder(next))
-                    {
-                        AssetDatabase.CreateFolder(current, parts[i]);
-                    }
-                    current = next;
-                }
-            }
-
-            // Create unique path for the scene
-            string basePath = folderPath.TrimEnd('/');
-            string scenePath = AssetDatabase.GenerateUniqueAssetPath($"{basePath}/{sceneName}.unity");
-
-            try
-            {
-                var newScene = UnityEditor.SceneManagement.EditorSceneManager.NewScene(UnityEditor.SceneManagement.NewSceneSetup.DefaultGameObjects, UnityEditor.SceneManagement.NewSceneMode.Single);
-
-                bool saved = UnityEditor.SceneManagement.EditorSceneManager.SaveScene(newScene, scenePath);
-                if (!saved)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Failed to save scene at '{scenePath}'",
-                        "save_error"
-                    );
-                }
-
-                AssetDatabase.Refresh();
-
-                // Make the scene active if requested
-                if (makeActive)
-                {
-                    UnityEditor.SceneManagement.EditorSceneManager.OpenScene(scenePath, UnityEditor.SceneManagement.OpenSceneMode.Single);
-                }
-
-                // Optionally add to build settings
-                if (addToBuildSettings)
-                {
-                    AddSceneToBuildSettings(scenePath);
-                }
-
-                McpLogger.LogInfo($"Created scene '{sceneName}' at path '{scenePath}'");
-
-                return new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"Successfully created scene '{sceneName}' at path '{scenePath}'",
-                    ["scenePath"] = scenePath
-                };
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error creating scene: {ex.Message}",
-                    "scene_creation_error"
-                );
-            }
-        }
-
-        private void AddSceneToBuildSettings(string scenePath)
-        {
-            var scenes = UnityEditor.EditorBuildSettings.scenes;
-
-            // Check if already present
-            foreach (var s in scenes)
-            {
-                if (s.path == scenePath)
-                {
-                    return;
-                }
-            }
-
-            var newList = new UnityEditor.EditorBuildSettingsScene[scenes.Length + 1];
-            for (int i = 0; i < scenes.Length; i++)
-            {
-                newList[i] = scenes[i];
-            }
-            newList[newList.Length - 1] = new UnityEditor.EditorBuildSettingsScene(scenePath, true);
-            UnityEditor.EditorBuildSettings.scenes = newList;
-        }
-    }
-}
-
-
diff --git a/Editor/Tools/CreateSceneTool.cs.meta b/Editor/Tools/CreateSceneTool.cs.meta
deleted file mode 100644
index 629016d6..00000000
--- a/Editor/Tools/CreateSceneTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 9d6810880891ad34eacebdf1130b3de2
\ No newline at end of file
diff --git a/Editor/Tools/DeleteSceneTool.cs b/Editor/Tools/DeleteSceneTool.cs
deleted file mode 100644
index 3241cb2a..00000000
--- a/Editor/Tools/DeleteSceneTool.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-using System;
-using System.Linq;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for deleting a Unity scene and removing it from Build Settings
-    /// 
-    public class DeleteSceneTool : McpToolBase
-    {
-        public DeleteSceneTool()
-        {
-            Name = "delete_scene";
-            Description = "Deletes a scene by path or name and removes it from Build Settings";
-        }
-
-        /// 
-        /// Execute the DeleteScene tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            string scenePath = parameters["scenePath"]?.ToObject();
-            string sceneName = parameters["sceneName"]?.ToObject();
-            string folderPath = parameters["folderPath"]?.ToObject();
-
-            if (string.IsNullOrEmpty(scenePath))
-            {
-                if (string.IsNullOrEmpty(sceneName))
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "Provide either 'scenePath' or 'sceneName'",
-                        "validation_error"
-                    );
-                }
-
-                // Resolve scene path by name (optionally within folderPath)
-                string filter = $"{sceneName} t:Scene";
-                string[] searchInFolders = null;
-                if (!string.IsNullOrEmpty(folderPath))
-                {
-                    // Ensure folder exists
-                    if (!AssetDatabase.IsValidFolder(folderPath))
-                    {
-                        return McpUnitySocketHandler.CreateErrorResponse(
-                            $"Folder '{folderPath}' does not exist",
-                            "not_found_error"
-                        );
-                    }
-                    searchInFolders = new[] { folderPath };
-                }
-
-                var guids = AssetDatabase.FindAssets(filter, searchInFolders);
-                foreach (var guid in guids)
-                {
-                    var path = AssetDatabase.GUIDToAssetPath(guid);
-                    if (System.IO.Path.GetFileNameWithoutExtension(path) == sceneName)
-                    {
-                        scenePath = path;
-                        break;
-                    }
-                }
-
-                if (string.IsNullOrEmpty(scenePath))
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Scene named '{sceneName}' not found",
-                        "not_found_error"
-                    );
-                }
-            }
-
-            try
-            {
-                // If the scene is open, close it without saving changes
-                var scene = UnityEditor.SceneManagement.EditorSceneManager.GetSceneByPath(scenePath);
-                if (scene.IsValid() && scene.isLoaded)
-                {
-                    UnityEditor.SceneManagement.EditorSceneManager.CloseScene(scene, true);
-                }
-
-                // Remove from Build Settings
-                RemoveSceneFromBuildSettings(scenePath);
-
-                // Delete asset
-                bool deleted = AssetDatabase.DeleteAsset(scenePath);
-                AssetDatabase.Refresh();
-
-                if (!deleted)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Failed to delete scene at '{scenePath}'",
-                        "delete_error"
-                    );
-                }
-
-                McpLogger.LogInfo($"Deleted scene at path '{scenePath}' and removed from Build Settings");
-
-                return new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"Successfully deleted scene at path '{scenePath}' and removed from Build Settings",
-                    ["scenePath"] = scenePath
-                };
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error deleting scene: {ex.Message}",
-                    "scene_delete_error"
-                );
-            }
-        }
-
-        private void RemoveSceneFromBuildSettings(string scenePath)
-        {
-            var scenes = UnityEditor.EditorBuildSettings.scenes;
-            var filtered = scenes.Where(s => s.path != scenePath).ToArray();
-            if (filtered.Length != scenes.Length)
-            {
-                UnityEditor.EditorBuildSettings.scenes = filtered;
-            }
-        }
-    }
-}
-
-
diff --git a/Editor/Tools/DeleteSceneTool.cs.meta b/Editor/Tools/DeleteSceneTool.cs.meta
deleted file mode 100644
index 4bdf1b31..00000000
--- a/Editor/Tools/DeleteSceneTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: f531906872d66084086a3f0d8f7f3d3e
\ No newline at end of file
diff --git a/Editor/Tools/GameObjectTools.cs b/Editor/Tools/GameObjectTools.cs
deleted file mode 100644
index 9f9fa5fa..00000000
--- a/Editor/Tools/GameObjectTools.cs
+++ /dev/null
@@ -1,430 +0,0 @@
-using System;
-using UnityEngine;
-using UnityEditor;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Utility class for common GameObject operations
-    /// 
-    public static class GameObjectToolUtils
-    {
-        /// 
-        /// Find a GameObject by instance ID or hierarchy path
-        /// 
-        /// Optional instance ID
-        /// Optional hierarchy path
-        /// Output GameObject if found
-        /// Description of how the object was identified
-        /// Error JObject if not found, null if successful
-        public static JObject FindGameObject(int? instanceId, string objectPath, out GameObject gameObject, out string identifierInfo)
-        {
-            gameObject = null;
-            identifierInfo = "";
-
-            if (instanceId.HasValue)
-            {
-                gameObject = UnityObjectId.ObjectFromId(instanceId.Value) as GameObject;
-                identifierInfo = $"instance ID {instanceId.Value}";
-            }
-            else if (!string.IsNullOrEmpty(objectPath))
-            {
-                gameObject = GameObject.Find(objectPath);
-                if (gameObject == null)
-                {
-                    // Try finding by traversing hierarchy
-                    gameObject = FindGameObjectByPath(objectPath);
-                }
-                identifierInfo = $"path '{objectPath}'";
-            }
-            else
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Either 'instanceId' or 'objectPath' must be provided.",
-                    "validation_error"
-                );
-            }
-
-            if (gameObject == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"GameObject not found using {identifierInfo}.",
-                    "not_found_error"
-                );
-            }
-
-            return null; // Success
-        }
-
-        /// 
-        /// Find a GameObject by its hierarchy path
-        /// 
-        private static GameObject FindGameObjectByPath(string path)
-        {
-            if (string.IsNullOrEmpty(path)) return null;
-
-            path = path.TrimStart('/');
-            string[] parts = path.Split('/');
-
-            if (parts.Length == 0) return null;
-
-            // Find root object
-            GameObject current = null;
-            GameObject[] rootObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
-
-            foreach (var root in rootObjects)
-            {
-                if (root.name == parts[0])
-                {
-                    current = root;
-                    break;
-                }
-            }
-
-            if (current == null) return null;
-
-            // Traverse children
-            for (int i = 1; i < parts.Length; i++)
-            {
-                Transform child = current.transform.Find(parts[i]);
-                if (child == null) return null;
-                current = child.gameObject;
-            }
-
-            return current;
-        }
-
-        /// 
-        /// Get the full hierarchy path of a GameObject
-        /// 
-        public static string GetGameObjectPath(GameObject obj)
-        {
-            if (obj == null) return null;
-            string path = "/" + obj.name;
-            while (obj.transform.parent != null)
-            {
-                obj = obj.transform.parent.gameObject;
-                path = "/" + obj.name + path;
-            }
-            return path;
-        }
-    }
-
-    /// 
-    /// Tool for duplicating GameObjects in the Unity Editor
-    /// 
-    public class DuplicateGameObjectTool : McpToolBase
-    {
-        public DuplicateGameObjectTool()
-        {
-            Name = "duplicate_gameobject";
-            Description = "Duplicates a GameObject in the Unity scene. Can create multiple copies and optionally rename or reparent them.";
-            IsAsync = false;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            string objectPath = parameters["objectPath"]?.ToObject();
-            string newName = parameters["newName"]?.ToObject();
-            string newParentPath = parameters["newParent"]?.ToObject();
-            int? newParentId = parameters["newParentId"]?.ToObject();
-            int count = parameters["count"]?.ToObject() ?? 1;
-
-            // Validate count
-            if (count < 1 || count > 100)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Count must be between 1 and 100.",
-                    "validation_error"
-                );
-            }
-
-            // Find source GameObject
-            JObject error = GameObjectToolUtils.FindGameObject(instanceId, objectPath, out GameObject sourceObject, out string identifierInfo);
-            if (error != null) return error;
-
-            // Find new parent if specified
-            GameObject newParent = null;
-            if (newParentId.HasValue)
-            {
-                newParent = UnityObjectId.ObjectFromId(newParentId.Value) as GameObject;
-                if (newParent == null)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"New parent GameObject not found with instance ID {newParentId.Value}.",
-                        "not_found_error"
-                    );
-                }
-            }
-            else if (!string.IsNullOrEmpty(newParentPath))
-            {
-                newParent = GameObject.Find(newParentPath);
-                if (newParent == null)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"New parent GameObject not found at path '{newParentPath}'.",
-                        "not_found_error"
-                    );
-                }
-            }
-
-            // Create duplicates
-            JArray duplicatedObjects = new JArray();
-
-            for (int i = 0; i < count; i++)
-            {
-                GameObject duplicate = UnityEngine.Object.Instantiate(sourceObject);
-                Undo.RegisterCreatedObjectUndo(duplicate, $"Duplicate {sourceObject.name}");
-
-                // Set name
-                if (!string.IsNullOrEmpty(newName))
-                {
-                    duplicate.name = count > 1 ? $"{newName} ({i + 1})" : newName;
-                }
-                else
-                {
-                    // Remove "(Clone)" suffix and optionally add number
-                    string baseName = sourceObject.name;
-                    duplicate.name = count > 1 ? $"{baseName} ({i + 1})" : baseName;
-                }
-
-                // Set parent
-                Transform targetParent = newParent != null ? newParent.transform : sourceObject.transform.parent;
-                if (targetParent != null)
-                {
-                    duplicate.transform.SetParent(targetParent, true);
-                }
-
-                duplicatedObjects.Add(new JObject
-                {
-                    ["instanceId"] = UnityObjectId.GetObjectId(duplicate),
-                    ["name"] = duplicate.name,
-                    ["path"] = GameObjectToolUtils.GetGameObjectPath(duplicate)
-                });
-            }
-
-            EditorUtility.SetDirty(sourceObject.scene.GetRootGameObjects()[0]);
-
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = count == 1
-                    ? $"Successfully duplicated GameObject '{sourceObject.name}'."
-                    : $"Successfully created {count} duplicates of GameObject '{sourceObject.name}'.",
-                ["duplicatedObjects"] = duplicatedObjects
-            };
-        }
-    }
-
-    /// 
-    /// Tool for deleting GameObjects in the Unity Editor
-    /// 
-    public class DeleteGameObjectTool : McpToolBase
-    {
-        public DeleteGameObjectTool()
-        {
-            Name = "delete_gameobject";
-            Description = "Deletes a GameObject from the Unity scene. By default, also deletes all children.";
-            IsAsync = false;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            string objectPath = parameters["objectPath"]?.ToObject();
-            bool includeChildren = parameters["includeChildren"]?.ToObject() ?? true;
-
-            // Find target GameObject
-            JObject error = GameObjectToolUtils.FindGameObject(instanceId, objectPath, out GameObject targetObject, out string identifierInfo);
-            if (error != null) return error;
-
-            string deletedName = targetObject.name;
-            string deletedPath = GameObjectToolUtils.GetGameObjectPath(targetObject);
-            int childCount = targetObject.transform.childCount;
-
-            if (!includeChildren && childCount > 0)
-            {
-                // Move children to parent before deleting
-                Transform parent = targetObject.transform.parent;
-                Transform[] children = new Transform[childCount];
-
-                for (int i = 0; i < childCount; i++)
-                {
-                    children[i] = targetObject.transform.GetChild(i);
-                }
-
-                foreach (Transform child in children)
-                {
-                    Undo.SetTransformParent(child, parent, "Reparent before delete");
-                }
-            }
-
-            // Delete the GameObject
-            Undo.DestroyObjectImmediate(targetObject);
-
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = includeChildren && childCount > 0
-                    ? $"Successfully deleted GameObject '{deletedName}' and {childCount} children."
-                    : $"Successfully deleted GameObject '{deletedName}'.",
-                ["deletedPath"] = deletedPath,
-                ["childrenPreserved"] = !includeChildren && childCount > 0 ? childCount : 0
-            };
-        }
-    }
-
-    /// 
-    /// Tool for changing the parent of GameObjects in the Unity Editor
-    /// 
-    public class ReparentGameObjectTool : McpToolBase
-    {
-        public ReparentGameObjectTool()
-        {
-            Name = "reparent_gameobject";
-            Description = "Changes the parent of a GameObject. Can move to a new parent or to the root level (null parent).";
-            IsAsync = false;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            string objectPath = parameters["objectPath"]?.ToObject();
-            string newParentPath = parameters["newParent"]?.ToObject();
-            int? newParentId = parameters["newParentId"]?.ToObject();
-            bool worldPositionStays = parameters["worldPositionStays"]?.ToObject() ?? true;
-
-            // Find target GameObject
-            JObject error = GameObjectToolUtils.FindGameObject(instanceId, objectPath, out GameObject targetObject, out string identifierInfo);
-            if (error != null) return error;
-
-            string oldPath = GameObjectToolUtils.GetGameObjectPath(targetObject);
-            Transform oldParent = targetObject.transform.parent;
-
-            // Find new parent (null means root level)
-            Transform newParentTransform = null;
-            bool moveToRoot = false;
-
-            // Check if explicitly moving to root (newParent is null or empty string)
-            if (parameters["newParent"] != null && parameters["newParent"].Type == JTokenType.Null)
-            {
-                moveToRoot = true;
-            }
-            else if (newParentId.HasValue)
-            {
-                GameObject newParent = UnityObjectId.ObjectFromId(newParentId.Value) as GameObject;
-                if (newParent == null)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"New parent GameObject not found with instance ID {newParentId.Value}.",
-                        "not_found_error"
-                    );
-                }
-                newParentTransform = newParent.transform;
-            }
-            else if (!string.IsNullOrEmpty(newParentPath))
-            {
-                GameObject newParent = GameObject.Find(newParentPath);
-                if (newParent == null)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"New parent GameObject not found at path '{newParentPath}'.",
-                        "not_found_error"
-                    );
-                }
-                newParentTransform = newParent.transform;
-            }
-            else if (parameters["newParent"] == null && parameters["newParentId"] == null)
-            {
-                // Neither specified - move to root
-                moveToRoot = true;
-            }
-
-            // Prevent parenting to self or descendants
-            if (newParentTransform != null)
-            {
-                if (newParentTransform == targetObject.transform)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "Cannot parent a GameObject to itself.",
-                        "validation_error"
-                    );
-                }
-
-                if (newParentTransform.IsChildOf(targetObject.transform))
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "Cannot parent a GameObject to one of its descendants.",
-                        "validation_error"
-                    );
-                }
-            }
-
-            // Check if already at target parent
-            if (moveToRoot && oldParent == null)
-            {
-                return new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"GameObject '{targetObject.name}' is already at the root level.",
-                    ["instanceId"] = UnityObjectId.GetObjectId(targetObject),
-                    ["name"] = targetObject.name,
-                    ["path"] = oldPath,
-                    ["changed"] = false
-                };
-            }
-
-            if (!moveToRoot && newParentTransform == oldParent)
-            {
-                return new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"GameObject '{targetObject.name}' is already a child of the specified parent.",
-                    ["instanceId"] = UnityObjectId.GetObjectId(targetObject),
-                    ["name"] = targetObject.name,
-                    ["path"] = oldPath,
-                    ["changed"] = false
-                };
-            }
-
-            // Perform reparenting
-            Undo.SetTransformParent(targetObject.transform, newParentTransform, "Reparent GameObject");
-
-            if (!worldPositionStays)
-            {
-                // Reset local position when worldPositionStays is false
-                Undo.RecordObject(targetObject.transform, "Reset Local Position");
-                targetObject.transform.localPosition = Vector3.zero;
-                targetObject.transform.localRotation = Quaternion.identity;
-                targetObject.transform.localScale = Vector3.one;
-            }
-
-            string newPath = GameObjectToolUtils.GetGameObjectPath(targetObject);
-            string parentDescription = newParentTransform != null
-                ? $"'{newParentTransform.gameObject.name}'"
-                : "root level";
-
-            EditorUtility.SetDirty(targetObject);
-
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Successfully reparented GameObject '{targetObject.name}' to {parentDescription}.",
-                ["instanceId"] = UnityObjectId.GetObjectId(targetObject),
-                ["name"] = targetObject.name,
-                ["oldPath"] = oldPath,
-                ["newPath"] = newPath,
-                ["changed"] = true
-            };
-        }
-    }
-}
diff --git a/Editor/Tools/GameObjectTools.cs.meta b/Editor/Tools/GameObjectTools.cs.meta
deleted file mode 100644
index b42223c5..00000000
--- a/Editor/Tools/GameObjectTools.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: e32bc6751ab884d03a051a1eb83ac276
\ No newline at end of file
diff --git a/Editor/Tools/GetConsoleLogsTool.cs b/Editor/Tools/GetConsoleLogsTool.cs
deleted file mode 100644
index 7a6cd917..00000000
--- a/Editor/Tools/GetConsoleLogsTool.cs
+++ /dev/null
@@ -1,79 +0,0 @@
-using System;
-using Newtonsoft.Json.Linq;
-using McpUnity.Services;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for retrieving logs from the Unity console with pagination support
-    /// 
-    public class GetConsoleLogsTool : McpToolBase
-    {
-        private readonly IConsoleLogsService _consoleLogsService;
-
-        public GetConsoleLogsTool(IConsoleLogsService consoleLogsService)
-        {
-            Name = "get_console_logs";
-            Description = "Retrieves logs from the Unity console with pagination support to avoid token limits";
-            _consoleLogsService = consoleLogsService;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            try
-            {
-                string logType = parameters?["logType"]?.ToString();
-                if (string.IsNullOrWhiteSpace(logType)) logType = null;
-
-                int offset = Math.Max(0, GetIntParameter(parameters, "offset", 0));
-                int limit = Math.Max(1, Math.Min(500, GetIntParameter(parameters, "limit", 50)));
-                bool includeStackTrace = GetBoolParameter(parameters, "includeStackTrace", true);
-
-                // Use the console logs service to get logs
-                JObject result = _consoleLogsService.GetLogsAsJson(logType, offset, limit, includeStackTrace);
-
-                // Add formatted message with pagination info
-                string typeFilter = logType != null ? $" of type '{logType}'" : "";
-                int returnedCount = result["_returnedCount"]?.Value() ?? 0;
-                int filteredCount = result["_filteredCount"]?.Value() ?? 0;
-                int totalCount = result["_totalCount"]?.Value() ?? 0;
-
-                result["message"] = $"Retrieved {returnedCount} of {filteredCount} log entries{typeFilter} (offset: {offset}, limit: {limit}, total: {totalCount})";
-                result["success"] = true;
-                result["type"] = "text";
-
-                // Remove internal count fields
-                result.Remove("_totalCount");
-                result.Remove("_filteredCount");
-                result.Remove("_returnedCount");
-
-                McpLogger.LogInfo($"Console logs retrieved: {returnedCount} entries (logType={logType}, offset={offset}, limit={limit}, includeStackTrace={includeStackTrace})");
-
-                return result;
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error retrieving console logs: {ex.Message}",
-                    "console_logs_error"
-                );
-            }
-        }
-
-        private static int GetIntParameter(JObject parameters, string key, int defaultValue)
-        {
-            if (parameters?[key] != null && int.TryParse(parameters[key].ToString(), out int value))
-                return value;
-            return defaultValue;
-        }
-
-        private static bool GetBoolParameter(JObject parameters, string key, bool defaultValue)
-        {
-            if (parameters?[key] != null && bool.TryParse(parameters[key].ToString(), out bool value))
-                return value;
-            return defaultValue;
-        }
-    }
-}
diff --git a/Editor/Tools/GetConsoleLogsTool.cs.meta b/Editor/Tools/GetConsoleLogsTool.cs.meta
deleted file mode 100644
index 60a74155..00000000
--- a/Editor/Tools/GetConsoleLogsTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 3f8a9c2d5e6f7a8b9c0d1e2f3a4b5c6d
diff --git a/Editor/Tools/GetGameObjectTool.cs b/Editor/Tools/GetGameObjectTool.cs
deleted file mode 100644
index 6402895b..00000000
--- a/Editor/Tools/GetGameObjectTool.cs
+++ /dev/null
@@ -1,94 +0,0 @@
-using McpUnity.Resources;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for retrieving detailed information about a specific GameObject.
-    /// This tool provides the same functionality as the get_gameobject resource,
-    /// but as a tool that can be invoked directly without URI template parameters.
-    /// 
-    public class GetGameObjectTool : McpToolBase
-    {
-        public GetGameObjectTool()
-        {
-            Name = "get_gameobject";
-            Description = "Retrieves detailed information about a specific GameObject by instance ID, name, or hierarchical path (e.g., \"Parent/Child/MyObject\"). Returns component properties (Transform position/rotation/scale, etc.) plus a scoped hierarchy of children. Use optional 'maxDepth' (default 2), 'includeComponents', and 'includeComponentProperties' to control response size; when limits are hit, nodes carry a '_truncated' marker so you can re-query a narrower subtree.";
-        }
-
-        /// 
-        /// Execute the GetGameObject tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject. Required 'idOrName' (instance ID, name, or path).
-        /// Optional 'maxDepth' (int, default 2), 'includeComponents' (bool, default true),
-        /// 'includeComponentProperties' (bool, default true).
-        /// A JObject containing the GameObject data
-        public override JObject Execute(JObject parameters)
-        {
-            // Validate parameters
-            if (parameters == null || !parameters.ContainsKey("idOrName"))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Missing required parameter: idOrName",
-                    "validation_error"
-                );
-            }
-
-            string idOrName = parameters["idOrName"]?.ToObject();
-
-            if (string.IsNullOrEmpty(idOrName))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Parameter 'idOrName' cannot be null or empty",
-                    "validation_error"
-                );
-            }
-
-            GameObject gameObject = null;
-
-            // Try to parse as an instance ID first
-            if (int.TryParse(idOrName, out int instanceId))
-            {
-                // Unity Instance IDs are typically negative, but we'll accept any integer
-                UnityEngine.Object unityObject = UnityObjectId.ObjectFromId(instanceId);
-                gameObject = unityObject as GameObject;
-            }
-            else
-            {
-                // Otherwise, treat it as a name or hierarchical path
-                gameObject = GameObject.Find(idOrName);
-            }
-
-            // Check if the GameObject was found
-            if (gameObject == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"GameObject with '{idOrName}' reference not found. Make sure the GameObject exists and is loaded in the current scene(s).",
-                    "not_found_error"
-                );
-            }
-
-            int maxDepth = parameters["maxDepth"]?.ToObject() ?? GetGameObjectResource.DefaultMaxChildDepth;
-            bool includeComponents = parameters["includeComponents"]?.ToObject() ?? true;
-            bool includeComponentProperties = parameters["includeComponentProperties"]?.ToObject() ?? true;
-
-            // Convert the GameObject to a JObject using the resource's static method
-            JObject gameObjectData = GetGameObjectResource.GameObjectToJObject(
-                gameObject, true, maxDepth, includeComponents, includeComponentProperties);
-
-            // Create the response
-            return new JObject
-            {
-                ["success"] = true,
-                ["message"] = $"Retrieved GameObject data for '{gameObject.name}'",
-                ["gameObject"] = gameObjectData,
-                ["instanceId"] = UnityObjectId.GetObjectId(gameObject)
-            };
-        }
-    }
-}
-
diff --git a/Editor/Tools/GetPlayModeStatusTool.cs b/Editor/Tools/GetPlayModeStatusTool.cs
deleted file mode 100644
index 7010389e..00000000
--- a/Editor/Tools/GetPlayModeStatusTool.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System;
-using Newtonsoft.Json.Linq;
-using UnityEditor;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for getting Unity play mode status
-    /// 
-    public class GetPlayModeStatusTool : McpToolBase
-    {
-        public GetPlayModeStatusTool()
-        {
-            Name = "get_play_mode_status";
-            Description = "Gets Unity play mode status (isPlaying, isPaused).";
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            try
-            {
-                bool isPlaying = EditorApplication.isPlaying;
-                bool isPaused = EditorApplication.isPaused;
-
-                var result = new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = isPlaying ? (isPaused ? "Play mode (paused)" : "Play mode") : "Edit mode",
-                    ["isPlaying"] = isPlaying,
-                    ["isPaused"] = isPaused
-                };
-
-                McpLogger.LogInfo($"Play mode status requested: isPlaying={isPlaying}, isPaused={isPaused}");
-
-                return result;
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error getting play mode status: {ex.Message}",
-                    "play_mode_error"
-                );
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/GetPlayModeStatusTool.cs.meta b/Editor/Tools/GetPlayModeStatusTool.cs.meta
deleted file mode 100644
index 631b4b58..00000000
--- a/Editor/Tools/GetPlayModeStatusTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: dffebe6143c5cbb4bae56ce6088a4b91
\ No newline at end of file
diff --git a/Editor/Tools/GetSceneInfoTool.cs b/Editor/Tools/GetSceneInfoTool.cs
deleted file mode 100644
index 09698162..00000000
--- a/Editor/Tools/GetSceneInfoTool.cs
+++ /dev/null
@@ -1,89 +0,0 @@
-using System;
-using UnityEditor.SceneManagement;
-using UnityEngine.SceneManagement;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for getting information about the active Unity scene
-    /// 
-    public class GetSceneInfoTool : McpToolBase
-    {
-        public GetSceneInfoTool()
-        {
-            Name = "get_scene_info";
-            Description = "Gets information about the active scene including name, path, dirty state, root object count, and loaded state";
-        }
-
-        /// 
-        /// Execute the GetSceneInfo tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            try
-            {
-                Scene activeScene = SceneManager.GetActiveScene();
-
-                if (!activeScene.IsValid())
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "No valid active scene",
-                        "validation_error"
-                    );
-                }
-
-                // Get all loaded scenes info
-                int loadedSceneCount = SceneManager.sceneCount;
-                var loadedScenes = new JArray();
-
-                for (int i = 0; i < loadedSceneCount; i++)
-                {
-                    Scene scene = SceneManager.GetSceneAt(i);
-                    loadedScenes.Add(new JObject
-                    {
-                        ["name"] = scene.name,
-                        ["path"] = scene.path,
-                        ["buildIndex"] = scene.buildIndex,
-                        ["isLoaded"] = scene.isLoaded,
-                        ["isDirty"] = scene.isDirty,
-                        ["rootCount"] = scene.isLoaded ? scene.rootCount : 0,
-                        ["isActive"] = scene == activeScene
-                    });
-                }
-
-                var result = new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"Active scene: '{activeScene.name}'",
-                    ["activeScene"] = new JObject
-                    {
-                        ["name"] = activeScene.name,
-                        ["path"] = activeScene.path,
-                        ["buildIndex"] = activeScene.buildIndex,
-                        ["isDirty"] = activeScene.isDirty,
-                        ["isLoaded"] = activeScene.isLoaded,
-                        ["rootCount"] = activeScene.isLoaded ? activeScene.rootCount : 0
-                    },
-                    ["loadedSceneCount"] = loadedSceneCount,
-                    ["loadedScenes"] = loadedScenes
-                };
-
-                McpLogger.LogInfo($"Retrieved scene info for active scene '{activeScene.name}'");
-
-                return result;
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error getting scene info: {ex.Message}",
-                    "scene_info_error"
-                );
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/GetSceneInfoTool.cs.meta b/Editor/Tools/GetSceneInfoTool.cs.meta
deleted file mode 100644
index a0cc6471..00000000
--- a/Editor/Tools/GetSceneInfoTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 43eb7dc38d4aa46118d8607c3246f423
\ No newline at end of file
diff --git a/Editor/Tools/LoadSceneTool.cs b/Editor/Tools/LoadSceneTool.cs
deleted file mode 100644
index df9676d3..00000000
--- a/Editor/Tools/LoadSceneTool.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-using System;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for loading a Unity scene, optionally additively
-    /// 
-    public class LoadSceneTool : McpToolBase
-    {
-        public LoadSceneTool()
-        {
-            Name = "load_scene";
-            Description = "Loads a scene by path or name. Supports additive loading (default: false)";
-        }
-
-        /// 
-        /// Execute the LoadScene tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            string scenePath = parameters["scenePath"]?.ToObject();
-            string sceneName = parameters["sceneName"]?.ToObject();
-            string folderPath = parameters["folderPath"]?.ToObject();
-            bool additive = parameters["additive"]?.ToObject() ?? false;
-
-            if (string.IsNullOrEmpty(scenePath))
-            {
-                if (string.IsNullOrEmpty(sceneName))
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "Provide either 'scenePath' or 'sceneName'",
-                        "validation_error"
-                    );
-                }
-
-                // Resolve scene path by name (optionally within folderPath)
-                string filter = $"{sceneName} t:Scene";
-                string[] searchInFolders = null;
-                if (!string.IsNullOrEmpty(folderPath))
-                {
-                    if (!AssetDatabase.IsValidFolder(folderPath))
-                    {
-                        return McpUnitySocketHandler.CreateErrorResponse(
-                            $"Folder '{folderPath}' does not exist",
-                            "not_found_error"
-                        );
-                    }
-                    searchInFolders = new[] { folderPath };
-                }
-
-                var guids = AssetDatabase.FindAssets(filter, searchInFolders);
-                foreach (var guid in guids)
-                {
-                    var path = AssetDatabase.GUIDToAssetPath(guid);
-                    if (System.IO.Path.GetFileNameWithoutExtension(path) == sceneName)
-                    {
-                        scenePath = path;
-                        break;
-                    }
-                }
-
-                if (string.IsNullOrEmpty(scenePath))
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Scene named '{sceneName}' not found",
-                        "not_found_error"
-                    );
-                }
-            }
-
-            try
-            {
-                // Avoid any save prompts: save open scenes before replacing them (non-additive)
-                if (!additive)
-                {
-                    UnityEditor.SceneManagement.EditorSceneManager.SaveOpenScenes();
-                }
-
-                var mode = additive
-                    ? UnityEditor.SceneManagement.OpenSceneMode.Additive
-                    : UnityEditor.SceneManagement.OpenSceneMode.Single;
-
-                var openedScene = UnityEditor.SceneManagement.EditorSceneManager.OpenScene(scenePath, mode);
-
-                // For non-additive, scene becomes active automatically. For additive, we do not change active scene.
-
-                McpLogger.LogInfo($"Loaded scene at path '{scenePath}' (additive={additive})");
-
-                return new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"Successfully loaded scene at path '{scenePath}' (additive={additive.ToString().ToLower()})",
-                    ["scenePath"] = scenePath,
-                    ["additive"] = additive
-                };
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error loading scene: {ex.Message}",
-                    "scene_load_error"
-                );
-            }
-        }
-    }
-}
-
-
diff --git a/Editor/Tools/LoadSceneTool.cs.meta b/Editor/Tools/LoadSceneTool.cs.meta
deleted file mode 100644
index 11676395..00000000
--- a/Editor/Tools/LoadSceneTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 77c0c977efd3f724590ee3d7891f042b
\ No newline at end of file
diff --git a/Editor/Tools/MaterialTools.cs b/Editor/Tools/MaterialTools.cs
deleted file mode 100644
index 7beb7ffc..00000000
--- a/Editor/Tools/MaterialTools.cs
+++ /dev/null
@@ -1,814 +0,0 @@
-using System;
-using System.Collections.Generic;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEditor;
-using UnityEngine.Rendering;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Utility class for Material tool operations
-    /// 
-    public static class MaterialToolUtils
-    {
-        /// 
-        /// Get the default lit shader based on the current render pipeline
-        /// 
-        public static string GetDefaultShaderName()
-        {
-            // Check for URP
-            if (UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline != null)
-            {
-                string pipelineName = UnityEngine.Rendering.GraphicsSettings.currentRenderPipeline.GetType().Name;
-
-                if (pipelineName.Contains("Universal") || pipelineName.Contains("URP"))
-                {
-                    return "Universal Render Pipeline/Lit";
-                }
-                else if (pipelineName.Contains("HD") || pipelineName.Contains("HDRP"))
-                {
-                    return "HDRP/Lit";
-                }
-                else
-                {
-                    McpLogger.LogWarning("Unknown render pipeline, defaulting to Standard shader");
-                }
-            }
-
-            // Default to Standard (Built-in Render Pipeline)
-            return "Standard";
-        }
-
-        /// 
-        /// Find a shader by name, searching common Unity shader paths
-        /// 
-        public static Shader FindShader(string shaderName)
-        {
-            // Try direct lookup first
-            Shader shader = Shader.Find(shaderName);
-            if (shader != null)
-            {
-                return shader;
-            }
-
-            // Common shader path prefixes to try
-            string[] prefixes = new string[]
-            {
-                "",
-                "Standard",
-                "Universal Render Pipeline/",
-                "URP/",
-                "HDRP/",
-                "Hidden/",
-                "Legacy Shaders/",
-                "Mobile/",
-                "Particles/",
-                "Skybox/",
-                "Sprites/",
-                "UI/",
-                "Unlit/"
-            };
-
-            foreach (string prefix in prefixes)
-            {
-                shader = Shader.Find(prefix + shaderName);
-                if (shader != null)
-                {
-                    return shader;
-                }
-            }
-
-            return null;
-        }
-
-        /// 
-        /// Load a material from an asset path
-        /// 
-        public static Material LoadMaterial(string materialPath)
-        {
-            if (string.IsNullOrEmpty(materialPath))
-            {
-                return null;
-            }
-
-            // Ensure path starts with Assets/
-            if (!materialPath.StartsWith("Assets/"))
-            {
-                materialPath = "Assets/" + materialPath;
-            }
-
-            // Ensure .mat extension
-            if (!materialPath.EndsWith(".mat"))
-            {
-                materialPath += ".mat";
-            }
-
-            return AssetDatabase.LoadAssetAtPath(materialPath);
-        }
-
-        /// 
-        /// Convert a JToken to a shader property value
-        /// 
-        public static object ConvertPropertyValue(JToken token, ShaderPropertyType propertyType)
-        {
-            if (token == null)
-            {
-                return null;
-            }
-
-            switch (propertyType)
-            {
-                case ShaderPropertyType.Color:
-                    if (token.Type == JTokenType.Object)
-                    {
-                        JObject color = (JObject)token;
-                        return new Color(
-                            color["r"]?.ToObject() ?? 0f,
-                            color["g"]?.ToObject() ?? 0f,
-                            color["b"]?.ToObject() ?? 0f,
-                            color["a"]?.ToObject() ?? 1f
-                        );
-                    }
-                    break;
-
-                case ShaderPropertyType.Vector:
-                    if (token.Type == JTokenType.Object)
-                    {
-                        JObject vec = (JObject)token;
-                        return new Vector4(
-                            vec["x"]?.ToObject() ?? 0f,
-                            vec["y"]?.ToObject() ?? 0f,
-                            vec["z"]?.ToObject() ?? 0f,
-                            vec["w"]?.ToObject() ?? 0f
-                        );
-                    }
-                    break;
-
-                case ShaderPropertyType.Float:
-                case ShaderPropertyType.Range:
-                    return token.ToObject();
-
-                case ShaderPropertyType.Texture:
-                    // Texture path
-                    string texPath = token.ToObject();
-                    if (!string.IsNullOrEmpty(texPath))
-                    {
-                        if (!texPath.StartsWith("Assets/"))
-                        {
-                            texPath = "Assets/" + texPath;
-                        }
-                        return AssetDatabase.LoadAssetAtPath(texPath);
-                    }
-                    break;
-
-                case ShaderPropertyType.Int:
-                    return token.ToObject();
-            }
-
-            return null;
-        }
-
-        /// 
-        /// Find a GameObject by instance ID or path
-        /// 
-        public static GameObject FindGameObject(int? instanceId, string objectPath)
-        {
-            GameObject gameObject = null;
-
-            if (instanceId.HasValue)
-            {
-                gameObject = UnityObjectId.ObjectFromId(instanceId.Value) as GameObject;
-            }
-            else if (!string.IsNullOrEmpty(objectPath))
-            {
-                gameObject = GameObject.Find(objectPath);
-
-                if (gameObject == null)
-                {
-                    // Try to find using hierarchy path
-                    gameObject = FindGameObjectByPath(objectPath);
-                }
-            }
-
-            return gameObject;
-        }
-
-        /// 
-        /// Find a GameObject by its hierarchy path
-        /// 
-        private static GameObject FindGameObjectByPath(string path)
-        {
-            string[] pathParts = path.Split('/');
-            GameObject[] rootGameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
-
-            if (pathParts.Length == 0)
-            {
-                return null;
-            }
-
-            foreach (GameObject rootObj in rootGameObjects)
-            {
-                if (rootObj.name == pathParts[0])
-                {
-                    GameObject current = rootObj;
-
-                    for (int i = 1; i < pathParts.Length; i++)
-                    {
-                        Transform child = current.transform.Find(pathParts[i]);
-                        if (child == null)
-                        {
-                            return null;
-                        }
-                        current = child.gameObject;
-                    }
-
-                    return current;
-                }
-            }
-
-            return null;
-        }
-    }
-
-    /// 
-    /// Tool for creating new materials
-    /// 
-    public class CreateMaterialTool : McpToolBase
-    {
-        public CreateMaterialTool()
-        {
-            Name = "create_material";
-            Description = "Creates a new material with the specified shader and saves it to the project";
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            string name = parameters["name"]?.ToObject();
-            string shaderName = parameters["shader"]?.ToObject();
-            string savePath = parameters["savePath"]?.ToObject();
-            JObject properties = parameters["properties"] as JObject;
-            JObject colorParam = parameters["color"] as JObject;
-
-            // Validate required parameters
-            if (string.IsNullOrEmpty(name))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'name' not provided",
-                    "validation_error"
-                );
-            }
-
-            if (string.IsNullOrEmpty(savePath))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'savePath' not provided",
-                    "validation_error"
-                );
-            }
-
-            // Default shader based on current render pipeline
-            if (string.IsNullOrEmpty(shaderName))
-            {
-                shaderName = MaterialToolUtils.GetDefaultShaderName();
-            }
-
-            // Find the shader
-            Shader shader = MaterialToolUtils.FindShader(shaderName);
-            if (shader == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Shader '{shaderName}' not found in Unity",
-                    "not_found_error"
-                );
-            }
-
-            // Ensure save path has proper format
-            if (!savePath.StartsWith("Assets/"))
-            {
-                savePath = "Assets/" + savePath;
-            }
-            if (!savePath.EndsWith(".mat"))
-            {
-                savePath += ".mat";
-            }
-
-            // Ensure directory exists
-            string directory = System.IO.Path.GetDirectoryName(savePath);
-            if (!System.IO.Directory.Exists(directory))
-            {
-                System.IO.Directory.CreateDirectory(directory);
-            }
-
-            // Create the material
-            Material material = new Material(shader);
-            material.name = name;
-
-            // Apply color if provided (auto-detect correct property name)
-            if (colorParam != null)
-            {
-                Color color = new Color(
-                    colorParam["r"]?.ToObject() ?? 1f,
-                    colorParam["g"]?.ToObject() ?? 1f,
-                    colorParam["b"]?.ToObject() ?? 1f,
-                    colorParam["a"]?.ToObject() ?? 1f
-                );
-                ApplyBaseColor(material, color);
-            }
-
-            // Apply initial properties if provided
-            if (properties != null && properties.Count > 0)
-            {
-                ApplyMaterialProperties(material, properties);
-            }
-
-            // Save the material as an asset
-            AssetDatabase.CreateAsset(material, savePath);
-            AssetDatabase.SaveAssets();
-            AssetDatabase.Refresh();
-
-            McpLogger.LogInfo($"[MCP Unity] Created material '{name}' with shader '{shaderName}' at '{savePath}'");
-
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Successfully created material '{name}' with shader '{shaderName}'",
-                ["materialPath"] = savePath,
-                ["materialName"] = name,
-                ["shaderName"] = shader.name
-            };
-        }
-
-        private void ApplyMaterialProperties(Material material, JObject properties)
-        {
-            Shader shader = material.shader;
-            int propertyCount = shader.GetPropertyCount();
-
-            foreach (var prop in properties.Properties())
-            {
-                string propName = prop.Name;
-                JToken propValue = prop.Value;
-
-                // Find the property in the shader
-                for (int i = 0; i < propertyCount; i++)
-                {
-                    string shaderPropName = shader.GetPropertyName(i);
-                    if (shaderPropName == propName)
-                    {
-                        ShaderPropertyType propType = shader.GetPropertyType(i);
-                        object value = MaterialToolUtils.ConvertPropertyValue(propValue, propType);
-
-                        if (value != null)
-                        {
-                            SetMaterialProperty(material, propName, propType, value);
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-
-        private void SetMaterialProperty(Material material, string propName, ShaderPropertyType propType, object value)
-        {
-            switch (propType)
-            {
-                case ShaderPropertyType.Color:
-                    material.SetColor(propName, (Color)value);
-                    break;
-                case ShaderPropertyType.Vector:
-                    material.SetVector(propName, (Vector4)value);
-                    break;
-                case ShaderPropertyType.Float:
-                case ShaderPropertyType.Range:
-                    material.SetFloat(propName, (float)value);
-                    break;
-                case ShaderPropertyType.Texture:
-                    material.SetTexture(propName, (Texture)value);
-                    break;
-                case ShaderPropertyType.Int:
-                    material.SetInt(propName, (int)value);
-                    break;
-            }
-        }
-
-        /// 
-        /// Apply base color to material, auto-detecting the correct property name
-        /// 
-        private void ApplyBaseColor(Material material, Color color)
-        {
-            // Common color property names in order of preference
-            string[] colorPropertyNames = new string[]
-            {
-                "_BaseColor",    // URP Lit, HDRP Lit
-                "_Color",        // Standard, Legacy shaders
-                "_TintColor",    // Particle shaders
-                "_MainColor"     // Some custom shaders
-            };
-
-            foreach (string propName in colorPropertyNames)
-            {
-                if (material.HasProperty(propName))
-                {
-                    material.SetColor(propName, color);
-                    return;
-                }
-            }
-
-            // Fallback: try to find any color property
-            Shader shader = material.shader;
-            int propertyCount = shader.GetPropertyCount();
-            for (int i = 0; i < propertyCount; i++)
-            {
-                if (shader.GetPropertyType(i) == ShaderPropertyType.Color)
-                {
-                    string propName = shader.GetPropertyName(i);
-                    material.SetColor(propName, color);
-                    return;
-                }
-            }
-        }
-    }
-
-    /// 
-    /// Tool for assigning materials to GameObjects
-    /// 
-    public class AssignMaterialTool : McpToolBase
-    {
-        public AssignMaterialTool()
-        {
-            Name = "assign_material";
-            Description = "Assigns a material to a GameObject's Renderer component";
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            string objectPath = parameters["objectPath"]?.ToObject();
-            string materialPath = parameters["materialPath"]?.ToObject();
-            int slot = parameters["slot"]?.ToObject() ?? 0;
-
-            // Validate parameters
-            if (!instanceId.HasValue && string.IsNullOrEmpty(objectPath))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Either 'instanceId' or 'objectPath' must be provided",
-                    "validation_error"
-                );
-            }
-
-            if (string.IsNullOrEmpty(materialPath))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'materialPath' not provided",
-                    "validation_error"
-                );
-            }
-
-            // Find the GameObject
-            GameObject gameObject = MaterialToolUtils.FindGameObject(instanceId, objectPath);
-            if (gameObject == null)
-            {
-                string identifier = instanceId.HasValue ? $"ID {instanceId.Value}" : $"path '{objectPath}'";
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"GameObject with {identifier} not found",
-                    "not_found_error"
-                );
-            }
-
-            // Get the Renderer component
-            Renderer renderer = gameObject.GetComponent();
-            if (renderer == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"GameObject '{gameObject.name}' does not have a Renderer component",
-                    "component_error"
-                );
-            }
-
-            // Load the material
-            Material material = MaterialToolUtils.LoadMaterial(materialPath);
-            if (material == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Material at path '{materialPath}' not found",
-                    "not_found_error"
-                );
-            }
-
-            // Validate slot index
-            Material[] materials = renderer.sharedMaterials;
-            if (slot < 0 || slot >= materials.Length)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Material slot {slot} is out of range. GameObject has {materials.Length} material slot(s) (0-{materials.Length - 1})",
-                    "validation_error"
-                );
-            }
-
-            // Record for undo
-            Undo.RecordObject(renderer, $"Assign Material to {gameObject.name}");
-
-            // Assign the material
-            materials[slot] = material;
-            renderer.sharedMaterials = materials;
-
-            // Mark as dirty
-            EditorUtility.SetDirty(renderer);
-            if (PrefabUtility.IsPartOfAnyPrefab(gameObject))
-            {
-                PrefabUtility.RecordPrefabInstancePropertyModifications(renderer);
-            }
-
-            McpLogger.LogInfo($"[MCP Unity] Assigned material '{material.name}' to '{gameObject.name}' at slot {slot}");
-
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Successfully assigned material '{material.name}' to '{gameObject.name}' at slot {slot}",
-                ["gameObjectName"] = gameObject.name,
-                ["materialName"] = material.name,
-                ["slot"] = slot
-            };
-        }
-    }
-
-    /// 
-    /// Tool for modifying material properties
-    /// 
-    public class ModifyMaterialTool : McpToolBase
-    {
-        public ModifyMaterialTool()
-        {
-            Name = "modify_material";
-            Description = "Modifies properties of an existing material";
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            string materialPath = parameters["materialPath"]?.ToObject();
-            JObject properties = parameters["properties"] as JObject;
-
-            // Validate parameters
-            if (string.IsNullOrEmpty(materialPath))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'materialPath' not provided",
-                    "validation_error"
-                );
-            }
-
-            if (properties == null || properties.Count == 0)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'properties' not provided or empty",
-                    "validation_error"
-                );
-            }
-
-            // Load the material
-            Material material = MaterialToolUtils.LoadMaterial(materialPath);
-            if (material == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Material at path '{materialPath}' not found",
-                    "not_found_error"
-                );
-            }
-
-            // Record for undo
-            Undo.RecordObject(material, $"Modify Material {material.name}");
-
-            // Apply properties
-            Shader shader = material.shader;
-            int propertyCount = shader.GetPropertyCount();
-            List modifiedProperties = new List();
-            List unknownProperties = new List();
-
-            foreach (var prop in properties.Properties())
-            {
-                string propName = prop.Name;
-                JToken propValue = prop.Value;
-                bool found = false;
-
-                // Find the property in the shader
-                for (int i = 0; i < propertyCount; i++)
-                {
-                    string shaderPropName = shader.GetPropertyName(i);
-                    if (shaderPropName == propName)
-                    {
-                        found = true;
-                        ShaderPropertyType propType = shader.GetPropertyType(i);
-                        object value = MaterialToolUtils.ConvertPropertyValue(propValue, propType);
-
-                        if (value != null)
-                        {
-                            SetMaterialProperty(material, propName, propType, value);
-                            modifiedProperties.Add(propName);
-                        }
-                        break;
-                    }
-                }
-
-                if (!found)
-                {
-                    unknownProperties.Add(propName);
-                }
-            }
-
-            // Mark as dirty and save
-            EditorUtility.SetDirty(material);
-            AssetDatabase.SaveAssets();
-            AssetDatabase.Refresh();
-
-            McpLogger.LogInfo($"[MCP Unity] Modified material '{material.name}': {string.Join(", ", modifiedProperties)}");
-
-            JObject result = new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Successfully modified material '{material.name}'",
-                ["materialName"] = material.name,
-                ["modifiedProperties"] = new JArray(modifiedProperties)
-            };
-
-            if (unknownProperties.Count > 0)
-            {
-                result["unknownProperties"] = new JArray(unknownProperties);
-                result["message"] = $"Modified material '{material.name}'. Some properties were not found: {string.Join(", ", unknownProperties)}";
-            }
-
-            return result;
-        }
-
-        private void SetMaterialProperty(Material material, string propName, ShaderPropertyType propType, object value)
-        {
-            switch (propType)
-            {
-                case ShaderPropertyType.Color:
-                    material.SetColor(propName, (Color)value);
-                    break;
-                case ShaderPropertyType.Vector:
-                    material.SetVector(propName, (Vector4)value);
-                    break;
-                case ShaderPropertyType.Float:
-                case ShaderPropertyType.Range:
-                    material.SetFloat(propName, (float)value);
-                    break;
-                case ShaderPropertyType.Texture:
-                    material.SetTexture(propName, (Texture)value);
-                    break;
-                case ShaderPropertyType.Int:
-                    material.SetInt(propName, (int)value);
-                    break;
-            }
-        }
-    }
-
-    /// 
-    /// Tool for getting material information
-    /// 
-    public class GetMaterialInfoTool : McpToolBase
-    {
-        public GetMaterialInfoTool()
-        {
-            Name = "get_material_info";
-            Description = "Gets detailed information about a material including its shader and all properties";
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            string materialPath = parameters["materialPath"]?.ToObject();
-
-            // Validate parameters
-            if (string.IsNullOrEmpty(materialPath))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'materialPath' not provided",
-                    "validation_error"
-                );
-            }
-
-            // Load the material
-            Material material = MaterialToolUtils.LoadMaterial(materialPath);
-            if (material == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Material at path '{materialPath}' not found",
-                    "not_found_error"
-                );
-            }
-
-            // Get shader info
-            Shader shader = material.shader;
-            int propertyCount = shader.GetPropertyCount();
-
-            // Build properties array
-            JArray propertiesArray = new JArray();
-            for (int i = 0; i < propertyCount; i++)
-            {
-                string propName = shader.GetPropertyName(i);
-                string propDescription = shader.GetPropertyDescription(i);
-                ShaderPropertyType propType = shader.GetPropertyType(i);
-
-                JObject propInfo = new JObject
-                {
-                    ["name"] = propName,
-                    ["description"] = propDescription,
-                    ["type"] = propType.ToString()
-                };
-
-                // Get current value
-                propInfo["value"] = GetPropertyValue(material, propName, propType);
-
-                // Add range info if applicable
-                if (propType == ShaderPropertyType.Range)
-                {
-                    Vector2 rangeLimits = shader.GetPropertyRangeLimits(i);
-                    propInfo["rangeMin"] = rangeLimits.x;
-                    propInfo["rangeMax"] = rangeLimits.y;
-                }
-
-                propertiesArray.Add(propInfo);
-            }
-
-            // Build render queue info
-            string renderQueueName = "Custom";
-            int renderQueue = material.renderQueue;
-            if (renderQueue <= 2000) renderQueueName = "Background";
-            else if (renderQueue <= 2450) renderQueueName = "Geometry";
-            else if (renderQueue <= 2500) renderQueueName = "AlphaTest";
-            else if (renderQueue <= 3000) renderQueueName = "Transparent";
-            else renderQueueName = "Overlay";
-
-            McpLogger.LogInfo($"[MCP Unity] Retrieved info for material '{material.name}'");
-
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Material info for '{material.name}'",
-                ["materialName"] = material.name,
-                ["materialPath"] = materialPath,
-                ["shaderName"] = shader.name,
-                ["renderQueue"] = renderQueue,
-                ["renderQueueCategory"] = renderQueueName,
-                ["enableInstancing"] = material.enableInstancing,
-                ["doubleSidedGI"] = material.doubleSidedGI,
-                ["passCount"] = material.passCount,
-                ["properties"] = propertiesArray
-            };
-        }
-
-        private JToken GetPropertyValue(Material material, string propName, ShaderPropertyType propType)
-        {
-            switch (propType)
-            {
-                case ShaderPropertyType.Color:
-                    Color color = material.GetColor(propName);
-                    return new JObject
-                    {
-                        ["r"] = color.r,
-                        ["g"] = color.g,
-                        ["b"] = color.b,
-                        ["a"] = color.a
-                    };
-
-                case ShaderPropertyType.Vector:
-                    Vector4 vec = material.GetVector(propName);
-                    return new JObject
-                    {
-                        ["x"] = vec.x,
-                        ["y"] = vec.y,
-                        ["z"] = vec.z,
-                        ["w"] = vec.w
-                    };
-
-                case ShaderPropertyType.Float:
-                case ShaderPropertyType.Range:
-                    return material.GetFloat(propName);
-
-                case ShaderPropertyType.Texture:
-                    Texture tex = material.GetTexture(propName);
-                    if (tex != null)
-                    {
-                        return AssetDatabase.GetAssetPath(tex);
-                    }
-                    return null;
-
-                case ShaderPropertyType.Int:
-                    return material.GetInt(propName);
-
-                default:
-                    return null;
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/McpToolBase.cs b/Editor/Tools/McpToolBase.cs
deleted file mode 100644
index 700033e3..00000000
--- a/Editor/Tools/McpToolBase.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using UnityEngine;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Base class for MCP Unity tools that interact with the Unity Editor
-    /// 
-    public abstract class McpToolBase
-    {
-        /// 
-        /// The name of the tool as used in API calls
-        /// 
-        public string Name { get; protected set; }
-        
-        /// 
-        /// Description of the tool's functionality
-        /// 
-        public string Description { get; protected set; }
-
-        /// 
-        /// Flag indicating if the tool executes asynchronously on the main thread.
-        /// If true, ExecuteAsync should be overridden.
-        /// If false, Execute should be overridden.
-        /// 
-        public bool IsAsync { get; protected set; } = false;
-        
-        /// 
-        /// Execute the tool asynchronously with the provided parameters.
-        /// This should be overridden by tools that need to run on the Unity main thread 
-        /// or perform long-running operations without blocking the WebSocket handler.
-        /// 
-        /// Tool parameters as a JObject
-        /// TaskCompletionSource to set the result or exception of the execution
-        public virtual void ExecuteAsync(JObject parameters, TaskCompletionSource tcs)
-        {
-            // Default implementation for tools that don't override this.
-            // Indicate that this method should have been overridden if IsAsync is true.
-            tcs.TrySetException(new NotImplementedException("ExecuteAsync must be overridden if IsAsync is true."));
-        }
-        
-        /// 
-        /// Execute the tool synchronously with the provided parameters.
-        /// This should be overridden by tools that can execute quickly and directly 
-        /// within the WebSocket message handler thread.
-        /// 
-        /// Tool parameters as a JObject
-        /// The result of the tool execution as a JObject, or an error JObject
-        public virtual JObject Execute(JObject parameters)
-        {
-            // Default implementation for tools that don't override this.
-            // Indicate that this method should have been overridden if IsAsync is false.
-            return McpUnity.Unity.McpUnitySocketHandler.CreateErrorResponse(
-                "Execute must be overridden if IsAsync is false.", 
-                "implementation_error"
-            );
-        }
-    }
-}
diff --git a/Editor/Tools/McpToolBase.cs.meta b/Editor/Tools/McpToolBase.cs.meta
deleted file mode 100644
index a3d089b8..00000000
--- a/Editor/Tools/McpToolBase.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: a66a1809c0e92834781bc2b80f061958
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/MenuItemTool.cs b/Editor/Tools/MenuItemTool.cs
deleted file mode 100644
index d818d745..00000000
--- a/Editor/Tools/MenuItemTool.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for executing Unity Editor menu items
-    /// 
-    public class MenuItemTool : McpToolBase
-    {
-        public MenuItemTool()
-        {
-            Name = "execute_menu_item";
-            Description = "Executes functions tagged with the MenuItem attribute";
-        }
-        
-        /// 
-        /// Execute the MenuItem tool with the provided parameters synchronously
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters with defaults
-            string menuPath = parameters["menuPath"]?.ToObject();
-            if (string.IsNullOrEmpty(menuPath))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'menuPath' not provided", 
-                    "validation_error"
-                );
-            }
-                
-            // Log the execution
-            McpLogger.LogInfo($"[MCP Unity] Executing menu item: {menuPath}");
-                
-            // Execute the menu item
-            bool success = EditorApplication.ExecuteMenuItem(menuPath);
-                
-            // Create the response
-            return new JObject
-            {
-                ["success"] = success,
-                ["type"] = "text",
-                ["message"] = success 
-                    ? $"Successfully executed menu item: {menuPath}" 
-                    : $"Failed to execute menu item: {menuPath}"
-            };
-        }
-    }
-}
diff --git a/Editor/Tools/MenuItemTool.cs.meta b/Editor/Tools/MenuItemTool.cs.meta
deleted file mode 100644
index 68112ec4..00000000
--- a/Editor/Tools/MenuItemTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 873e30cee2cfdaf4e99fc088be3d00d6
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/RecompileScriptsTool.cs b/Editor/Tools/RecompileScriptsTool.cs
deleted file mode 100644
index 5ea87ab0..00000000
--- a/Editor/Tools/RecompileScriptsTool.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using McpUnity.Utils;
-using Newtonsoft.Json.Linq;
-using UnityEditor;
-using UnityEditor.Compilation;
-using UnityEngine;
-
-namespace McpUnity.Tools {
-    /// 
-    /// Tool to recompile all scripts in the Unity project
-    /// 
-    public class RecompileScriptsTool : McpToolBase
-    {
-        private class CompilationRequest 
-        {
-            public readonly bool ReturnWithLogs;
-            public readonly int LogsLimit;
-            public readonly TaskCompletionSource CompletionSource;
-            
-            public CompilationRequest(bool returnWithLogs, int logsLimit, TaskCompletionSource completionSource)
-            {
-                ReturnWithLogs = returnWithLogs;
-                LogsLimit = logsLimit;
-                CompletionSource = completionSource;
-            }
-        }
-        
-        private class CompilationResult 
-        {
-            public readonly List SortedLogs;
-            public readonly int WarningsCount;
-            public readonly int ErrorsCount;
-            
-            public bool HasErrors => ErrorsCount > 0;
-            
-            public CompilationResult(List sortedLogs, int warningsCount, int errorsCount) 
-            {
-                SortedLogs = sortedLogs;
-                WarningsCount = warningsCount;
-                ErrorsCount = errorsCount;
-            }
-        }
-        
-        private readonly List _pendingRequests = new List();
-        private readonly List _compilationLogs = new List();
-        private int _processedAssemblies = 0;
-
-        public RecompileScriptsTool()
-        {
-            Name = "recompile_scripts";
-            Description = "Recompiles all scripts in the Unity project";
-            IsAsync = true; // Compilation is asynchronous
-        }
-
-        /// 
-        /// Execute the Recompile tool asynchronously
-        /// 
-        /// Tool parameters as a JObject
-        /// TaskCompletionSource to set the result or exception
-        public override void ExecuteAsync(JObject parameters, TaskCompletionSource tcs)
-        {
-            // Extract and store parameters
-            var returnWithLogs = GetBoolParameter(parameters, "returnWithLogs", true);
-            var logsLimit = Mathf.Clamp(GetIntParameter(parameters, "logsLimit", 100), 0, 1000);
-            var request = new CompilationRequest(returnWithLogs, logsLimit, tcs);
-            
-            bool hasActiveRequest = false;
-            lock (_pendingRequests)
-            {
-                hasActiveRequest = _pendingRequests.Count > 0;
-                _pendingRequests.Add(request);
-            }
-
-            if (hasActiveRequest)
-            {
-                McpLogger.LogInfo("Recompilation already in progress. Waiting for completion...");
-                return;
-            }
-            
-            // On first request, initialize compilation listeners and start compilation
-            StartCompilationTracking();
-                
-            if (EditorApplication.isCompiling == false)
-            {
-                McpLogger.LogInfo("Recompiling all scripts in the Unity project");
-                CompilationPipeline.RequestScriptCompilation();
-            }
-        }
-
-        /// 
-        /// Subscribe to compilation events, reset tracked state
-        /// 
-        private void StartCompilationTracking()
-        {
-            _compilationLogs.Clear();
-            _processedAssemblies = 0;
-            CompilationPipeline.assemblyCompilationFinished += OnAssemblyCompilationFinished;
-            CompilationPipeline.compilationFinished += OnCompilationFinished;
-        }
-        
-        /// 
-        /// Unsubscribe from compilation events
-        /// 
-        private void StopCompilationTracking()
-        {
-            CompilationPipeline.assemblyCompilationFinished -= OnAssemblyCompilationFinished;
-            CompilationPipeline.compilationFinished -= OnCompilationFinished;
-        }
-
-        /// 
-        /// Record compilation logs for every single assembly
-        /// 
-        private void OnAssemblyCompilationFinished(string assemblyPath, CompilerMessage[] messages)
-        {
-            _processedAssemblies++;
-            _compilationLogs.AddRange(messages);
-        }
-
-        /// 
-        /// Stop tracking and complete all pending requests
-        /// 
-        private void OnCompilationFinished(object _)
-        {
-            McpLogger.LogInfo($"Recompilation completed. Processed {_processedAssemblies} assemblies with {_compilationLogs.Count} compiler messages");
-
-            // Sort logs by type: first errors, then warnings and info
-            List sortedLogs = _compilationLogs.OrderBy(x => x.type).ToList();
-            int errorsCount = _compilationLogs.Count(l => l.type == CompilerMessageType.Error);
-            int warningsCount = _compilationLogs.Count(l => l.type == CompilerMessageType.Warning);
-            CompilationResult result = new CompilationResult(sortedLogs, warningsCount, errorsCount);
-            
-            // Stop tracking before completing requests
-            StopCompilationTracking();
-            
-            // Complete all requests received before compilation end, the next received request will start a new compilation
-            List requestsToComplete = new List();
-            
-            lock (_pendingRequests)
-            {
-                requestsToComplete.AddRange(_pendingRequests);
-                _pendingRequests.Clear();
-            }
-
-            foreach (var request in requestsToComplete)
-            {
-                CompleteRequest(request, result);
-            }
-        }
-
-        /// 
-        /// Process a completed compilation request
-        /// 
-        private static void CompleteRequest(CompilationRequest request, CompilationResult result)
-        {
-            JArray logsArray = new JArray();
-            IEnumerable logsToReturn = request.ReturnWithLogs ? result.SortedLogs.Take(request.LogsLimit) : Enumerable.Empty();
-
-            foreach (var message in logsToReturn)
-            {
-                var logObject = new JObject 
-                {
-                    ["message"] = message.message,
-                    ["type"] = message.type.ToString()
-                };
-
-                // Add file information if available
-                if (!string.IsNullOrEmpty(message.file))
-                {
-                    logObject["file"] = message.file;
-                    logObject["line"] = message.line;
-                    logObject["column"] = message.column;
-                }
-
-                logsArray.Add(logObject);
-            }
-
-            string summaryMessage = result.HasErrors
-                                        ? $"Recompilation completed with {result.ErrorsCount} error(s) and {result.WarningsCount} warning(s)"
-                                        : $"Successfully recompiled all scripts with {result.WarningsCount} warning(s)";
-
-            summaryMessage += $" (returnWithLogs: {request.ReturnWithLogs}, logsLimit: {request.LogsLimit})";
-
-            var response = new JObject 
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = summaryMessage,
-                ["logs"] = logsArray
-            };
-
-            request.CompletionSource.SetResult(response);
-        }
-
-        /// 
-        /// Helper method to safely extract integer parameters with default values
-        /// 
-        /// JObject containing parameters
-        /// Parameter key to extract
-        /// Default value if parameter is missing or invalid
-        /// Extracted integer value or default
-        private static int GetIntParameter(JObject parameters, string key, int defaultValue)
-        {
-            if (parameters?[key] != null && int.TryParse(parameters[key].ToString(), out int value))
-                return value;
-            return defaultValue;
-        }
-
-        /// 
-        /// Helper method to safely extract boolean parameters with default values
-        /// 
-        /// JObject containing parameters
-        /// Parameter key to extract
-        /// Default value if parameter is missing or invalid
-        /// Extracted boolean value or default
-        private static bool GetBoolParameter(JObject parameters, string key, bool defaultValue)
-        {
-            if (parameters?[key] != null && bool.TryParse(parameters[key].ToString(), out bool value))
-                return value;
-            return defaultValue;
-        }
-    }
-}
\ No newline at end of file
diff --git a/Editor/Tools/RecompileScriptsTool.cs.meta b/Editor/Tools/RecompileScriptsTool.cs.meta
deleted file mode 100644
index 98c53a52..00000000
--- a/Editor/Tools/RecompileScriptsTool.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: 9db4c0c982944b9da2e14745cf799f99
-timeCreated: 1758591273
\ No newline at end of file
diff --git a/Editor/Tools/RunTestsTool.cs b/Editor/Tools/RunTestsTool.cs
deleted file mode 100644
index d8997194..00000000
--- a/Editor/Tools/RunTestsTool.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using System;
-using System.Threading;
-using System.Threading.Tasks;
-using McpUnity.Unity;
-using UnityEngine;
-using Newtonsoft.Json.Linq;
-using System.Collections.Generic;
-using UnityEditor.TestTools.TestRunner.Api;
-using McpUnity.Services;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for running Unity Test Runner tests
-    /// 
-    public class RunTestsTool : McpToolBase
-    {
-        private readonly ITestRunnerService _testRunnerService;
-
-        public RunTestsTool(ITestRunnerService testRunnerService)
-        {
-            Name = "run_tests";
-            Description = "Runs tests using Unity's Test Runner";
-            IsAsync = true;
-            _testRunnerService = testRunnerService;
-        }
-        
-        /// 
-        /// Executes the RunTests tool asynchronously on the main thread.
-        /// 
-        /// Tool parameters, including optional 'testMode' and 'testFilter'.
-        /// TaskCompletionSource to set the result or exception.
-        public override async void ExecuteAsync(JObject parameters, TaskCompletionSource tcs)
-        {
-            // Parse parameters
-            string testModeStr = parameters?["testMode"]?.ToObject() ?? "EditMode";
-            string testFilter = parameters?["testFilter"]?.ToObject(); // Optional
-            bool returnOnlyFailures = parameters?["returnOnlyFailures"]?.ToObject() ?? false; // Optional
-            bool returnWithLogs = parameters?["returnWithLogs"]?.ToObject() ?? false; // Optional
-
-            TestMode testMode = TestMode.EditMode;
-            
-            if (Enum.TryParse(testModeStr, true, out TestMode parsedMode))
-            {
-                testMode = parsedMode;
-            }
-
-            McpLogger.LogInfo($"Executing RunTestsTool: Mode={testMode}, Filter={testFilter ?? "(none)"}");
-
-            // Call the service to run tests
-            JObject result = await _testRunnerService.ExecuteTestsAsync(testMode, returnOnlyFailures, returnWithLogs, testFilter);
-            tcs.SetResult(result);
-        }
-    }
-}
diff --git a/Editor/Tools/RunTestsTool.cs.meta b/Editor/Tools/RunTestsTool.cs.meta
deleted file mode 100644
index 1c172328..00000000
--- a/Editor/Tools/RunTestsTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 2927ec83161e2f34cb555e0521b7bb1e
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/SaveSceneTool.cs b/Editor/Tools/SaveSceneTool.cs
deleted file mode 100644
index c5e43ada..00000000
--- a/Editor/Tools/SaveSceneTool.cs
+++ /dev/null
@@ -1,144 +0,0 @@
-using System;
-using UnityEditor;
-using UnityEditor.SceneManagement;
-using UnityEngine.SceneManagement;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for saving a Unity scene
-    /// 
-    public class SaveSceneTool : McpToolBase
-    {
-        public SaveSceneTool()
-        {
-            Name = "save_scene";
-            Description = "Saves the current active scene. Optionally saves to a new path (Save As)";
-        }
-
-        /// 
-        /// Execute the SaveScene tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            string scenePath = parameters["scenePath"]?.ToObject();
-            bool saveAs = parameters["saveAs"]?.ToObject() ?? false;
-
-            try
-            {
-                Scene activeScene = SceneManager.GetActiveScene();
-
-                if (!activeScene.IsValid())
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "No valid active scene to save",
-                        "validation_error"
-                    );
-                }
-
-                string targetPath;
-
-                if (saveAs || !string.IsNullOrEmpty(scenePath))
-                {
-                    // Save As mode - need a path
-                    if (string.IsNullOrEmpty(scenePath))
-                    {
-                        return McpUnitySocketHandler.CreateErrorResponse(
-                            "Parameter 'scenePath' is required when 'saveAs' is true",
-                            "validation_error"
-                        );
-                    }
-
-                    // Ensure the path has .unity extension
-                    if (!scenePath.EndsWith(".unity", StringComparison.OrdinalIgnoreCase))
-                    {
-                        scenePath += ".unity";
-                    }
-
-                    // Ensure the path starts with Assets/
-                    if (!scenePath.StartsWith("Assets/", StringComparison.OrdinalIgnoreCase))
-                    {
-                        scenePath = "Assets/" + scenePath;
-                    }
-
-                    // Ensure the directory exists
-                    string directory = System.IO.Path.GetDirectoryName(scenePath);
-                    if (!string.IsNullOrEmpty(directory) && !AssetDatabase.IsValidFolder(directory))
-                    {
-                        CreateFolderHierarchy(directory);
-                    }
-
-                    targetPath = scenePath;
-                }
-                else
-                {
-                    // Save to current path
-                    targetPath = activeScene.path;
-
-                    if (string.IsNullOrEmpty(targetPath))
-                    {
-                        return McpUnitySocketHandler.CreateErrorResponse(
-                            "Scene has no path. Use 'scenePath' parameter to specify where to save the scene",
-                            "validation_error"
-                        );
-                    }
-                }
-
-                bool saved = EditorSceneManager.SaveScene(activeScene, targetPath);
-
-                if (!saved)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Failed to save scene to '{targetPath}'",
-                        "save_error"
-                    );
-                }
-
-                AssetDatabase.Refresh();
-
-                McpLogger.LogInfo($"Saved scene '{activeScene.name}' to path '{targetPath}'");
-
-                return new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"Successfully saved scene '{activeScene.name}' to '{targetPath}'",
-                    ["scenePath"] = targetPath,
-                    ["sceneName"] = activeScene.name
-                };
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error saving scene: {ex.Message}",
-                    "scene_save_error"
-                );
-            }
-        }
-
-        /// 
-        /// Creates folder hierarchy for the given path
-        /// 
-        private void CreateFolderHierarchy(string folderPath)
-        {
-            string[] parts = folderPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
-            string current = parts.Length > 0 && parts[0] == "Assets" ? "Assets" : "Assets";
-
-            for (int i = 0; i < parts.Length; i++)
-            {
-                if (i == 0 && parts[i] == "Assets") continue;
-
-                string next = current + "/" + parts[i];
-                if (!AssetDatabase.IsValidFolder(next))
-                {
-                    AssetDatabase.CreateFolder(current, parts[i]);
-                }
-                current = next;
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/SaveSceneTool.cs.meta b/Editor/Tools/SaveSceneTool.cs.meta
deleted file mode 100644
index 0e1b2dd3..00000000
--- a/Editor/Tools/SaveSceneTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 40d0c05b18b8d411f829c4b845ee98e0
\ No newline at end of file
diff --git a/Editor/Tools/SelectGameObjectTool.cs b/Editor/Tools/SelectGameObjectTool.cs
deleted file mode 100644
index 2c14055c..00000000
--- a/Editor/Tools/SelectGameObjectTool.cs
+++ /dev/null
@@ -1,75 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for selecting GameObjects in the Unity Editor
-    /// 
-    public class SelectGameObjectTool : McpToolBase
-    {
-        public SelectGameObjectTool()
-        {
-            Name = "select_gameobject";
-            Description = "Sets the selected GameObject in the Unity editor by path, name or instance ID";
-        }
-        
-        /// 
-        /// Execute the SelectGameObject tool with the provided parameters synchronously
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            string objectPath = parameters["objectPath"]?.ToObject();
-            string objectName = parameters["objectName"]?.ToObject();
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            GameObject selectedGameObject = null;
-            
-            // Validate parameters - require either objectPath or instanceId
-            if (string.IsNullOrEmpty(objectPath) && string.IsNullOrEmpty(objectName) && !instanceId.HasValue)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'objectPath', 'objectName' or 'instanceId' not provided", 
-                    "validation_error"
-                );
-            }
-            
-            // First try to find by instance ID if provided
-            if (instanceId.HasValue)
-            {
-                selectedGameObject = UnityObjectId.ObjectFromId(instanceId.Value) as GameObject;
-            }
-            else if (!string.IsNullOrEmpty(objectPath))
-            {
-                // Try to find the object by path in the hierarchy
-                selectedGameObject = GameObject.Find(objectPath);
-            }
-            else
-            {
-                // Try to find the object by name in the hierarchy
-                selectedGameObject = GameObject.Find(objectName);
-            }
-            
-            Selection.activeGameObject = selectedGameObject;
-
-            // Ping the selected object
-            EditorGUIUtility.PingObject(selectedGameObject);
-            
-            McpLogger.LogInfo($"[MCP Unity] Selected GameObject: {selectedGameObject?.name}");
-            
-            // Create the response
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Successfully selected GameObject {selectedGameObject?.name}"
-            };
-        }
-    }
-}
diff --git a/Editor/Tools/SelectGameObjectTool.cs.meta b/Editor/Tools/SelectGameObjectTool.cs.meta
deleted file mode 100644
index 7dfa99ff..00000000
--- a/Editor/Tools/SelectGameObjectTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 9165cb1f07ae1a34cbcdb180f757a767
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/SendConsoleLogTool.cs b/Editor/Tools/SendConsoleLogTool.cs
deleted file mode 100644
index 25757fc0..00000000
--- a/Editor/Tools/SendConsoleLogTool.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-using System.Threading.Tasks;
-using McpUnity.Unity;
-using UnityEngine;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for sending notification messages to the Unity console
-    /// 
-    public class SendConsoleLogTool : McpToolBase
-    {
-        public SendConsoleLogTool()
-        {
-            Name = "send_console_log";
-            Description = "Sends a message to the Unity console";
-        }
-        
-        /// 
-        /// Execute the NotifyMessage tool with the provided parameters synchronously
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            string message = parameters["message"]?.ToObject();
-            string type = parameters["type"]?.ToObject()?.ToLower() ?? "info";
- 
-            if (string.IsNullOrEmpty(message))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'message' not provided", 
-                    "validation_error"
-                );
-            }
- 
-            // Log the message based on type
-            switch (type)
-            {
-                case "error":
-                    Debug.LogError($"[MCP]: {message}");
-                    break;
-                case "warning":
-                    Debug.LogWarning($"[MCP]: {message}");
-                    break;
-                default:
-                    Debug.Log($"[MCP]: {message}");
-                    break;
-            }
- 
-            // Create the response
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Message displayed: {message}"
-            };
-        }
-    }
-}
diff --git a/Editor/Tools/SendConsoleLogTool.cs.meta b/Editor/Tools/SendConsoleLogTool.cs.meta
deleted file mode 100644
index 59b80e28..00000000
--- a/Editor/Tools/SendConsoleLogTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 35bb1bb57a358064c9ca8f2c8ba3caf2
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/SetPlayModeStatusTool.cs b/Editor/Tools/SetPlayModeStatusTool.cs
deleted file mode 100644
index f0193c76..00000000
--- a/Editor/Tools/SetPlayModeStatusTool.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-using System;
-using Newtonsoft.Json.Linq;
-using UnityEditor;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for controlling Unity play mode (play, pause, step)
-    /// 
-    public class SetPlayModeStatusTool : McpToolBase
-    {
-        public SetPlayModeStatusTool()
-        {
-            Name = "set_play_mode_status";
-            Description = "Controls Unity play mode. Actions: 'play', 'pause', 'stop', 'step'.";
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            try
-            {
-                string action = parameters?["action"]?.ToString()?.ToLowerInvariant();
-                
-                if (string.IsNullOrEmpty(action))
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "Missing required parameter 'action'. Valid actions: 'play', 'pause', 'stop', 'step'",
-                        "missing_parameter"
-                    );
-                }
-
-                bool wasPlaying = EditorApplication.isPlaying;
-                bool wasPaused = EditorApplication.isPaused;
-
-                switch (action)
-                {
-                    case "play":
-                        if (!EditorApplication.isPlaying)
-                        {
-                            // Start play mode
-                            EditorApplication.isPlaying = true;
-                        }
-                        else if (EditorApplication.isPaused)
-                        {
-                            // Unpause if already playing
-                            EditorApplication.isPaused = false;
-                        }
-                        break;
-
-                    case "pause":
-                        if (EditorApplication.isPlaying)
-                        {
-                            EditorApplication.isPaused = !EditorApplication.isPaused;
-                        }
-                        else
-                        {
-                            return McpUnitySocketHandler.CreateErrorResponse(
-                                "Cannot pause: Editor is not in play mode",
-                                "invalid_state"
-                            );
-                        }
-                        break;
-
-                    case "stop":
-                        if (EditorApplication.isPlaying)
-                        {
-                            EditorApplication.isPlaying = false;
-                        }
-                        break;
-
-                    case "step":
-                        if (EditorApplication.isPlaying)
-                        {
-                            EditorApplication.Step();
-                        }
-                        else
-                        {
-                            return McpUnitySocketHandler.CreateErrorResponse(
-                                "Cannot step: Editor is not in play mode",
-                                "invalid_state"
-                            );
-                        }
-                        break;
-
-                    default:
-                        return McpUnitySocketHandler.CreateErrorResponse(
-                            $"Invalid action '{action}'. Valid actions: 'play', 'pause', 'stop', 'step'",
-                            "invalid_parameter"
-                        );
-                }
-
-                // Give Unity a moment to update state
-                bool isPlaying = EditorApplication.isPlaying;
-                bool isPaused = EditorApplication.isPaused;
-
-                var result = new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"Action '{action}' executed. State: {(isPlaying ? (isPaused ? "Playing (paused)" : "Playing") : "Edit mode")}",
-                    ["action"] = action,
-                    ["wasPlaying"] = wasPlaying,
-                    ["wasPaused"] = wasPaused,
-                    ["isPlaying"] = isPlaying,
-                    ["isPaused"] = isPaused
-                };
-
-                McpLogger.LogInfo($"Play mode action '{action}' executed. isPlaying={isPlaying}, isPaused={isPaused}");
-
-                return result;
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error controlling play mode: {ex.Message}",
-                    "play_mode_error"
-                );
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/SetPlayModeStatusTool.cs.meta b/Editor/Tools/SetPlayModeStatusTool.cs.meta
deleted file mode 100644
index e3561552..00000000
--- a/Editor/Tools/SetPlayModeStatusTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 8c3f7e9a2d4b1c5e8f9a0b1c2d3e4f5a
diff --git a/Editor/Tools/TransformTools.cs b/Editor/Tools/TransformTools.cs
deleted file mode 100644
index a9112edd..00000000
--- a/Editor/Tools/TransformTools.cs
+++ /dev/null
@@ -1,484 +0,0 @@
-using UnityEngine;
-using UnityEditor;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for moving a GameObject's position in the Unity Editor.
-    /// Supports world/local space and absolute/relative positioning.
-    /// 
-    public class MoveGameObjectTool : McpToolBase
-    {
-        public MoveGameObjectTool()
-        {
-            Name = "move_gameobject";
-            Description = "Moves a GameObject to a new position. Supports world/local space and absolute/relative positioning.";
-            IsAsync = false;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Find the GameObject
-            var findResult = TransformToolUtils.FindGameObject(parameters);
-            if (findResult.Error != null)
-                return findResult.Error;
-
-            GameObject gameObject = findResult.GameObject;
-            Transform transform = gameObject.transform;
-
-            // Extract position
-            JObject positionObj = parameters["position"] as JObject;
-            if (positionObj == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'position' not provided",
-                    "validation_error"
-                );
-            }
-
-            Vector3 position = new Vector3(
-                positionObj["x"]?.ToObject() ?? 0f,
-                positionObj["y"]?.ToObject() ?? 0f,
-                positionObj["z"]?.ToObject() ?? 0f
-            );
-
-            // Get space and relative flags
-            string space = parameters["space"]?.ToObject() ?? "world";
-            bool relative = parameters["relative"]?.ToObject() ?? false;
-
-            // Record undo
-            Undo.RecordObject(transform, "Move GameObject");
-
-            // Apply the position change
-            if (space.ToLower() == "local")
-            {
-                if (relative)
-                    transform.localPosition += position;
-                else
-                    transform.localPosition = position;
-            }
-            else // world space
-            {
-                if (relative)
-                    transform.position += position;
-                else
-                    transform.position = position;
-            }
-
-            EditorUtility.SetDirty(gameObject);
-
-            // Return result with new position
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"GameObject '{gameObject.name}' moved successfully.",
-                ["instanceId"] = UnityObjectId.GetObjectId(gameObject),
-                ["name"] = gameObject.name,
-                ["path"] = TransformToolUtils.GetGameObjectPath(gameObject),
-                ["position"] = new JObject
-                {
-                    ["world"] = new JObject
-                    {
-                        ["x"] = transform.position.x,
-                        ["y"] = transform.position.y,
-                        ["z"] = transform.position.z
-                    },
-                    ["local"] = new JObject
-                    {
-                        ["x"] = transform.localPosition.x,
-                        ["y"] = transform.localPosition.y,
-                        ["z"] = transform.localPosition.z
-                    }
-                }
-            };
-        }
-    }
-
-    /// 
-    /// Tool for rotating a GameObject in the Unity Editor.
-    /// Supports world/local space and absolute/relative rotation using Euler angles.
-    /// 
-    public class RotateGameObjectTool : McpToolBase
-    {
-        public RotateGameObjectTool()
-        {
-            Name = "rotate_gameobject";
-            Description = "Rotates a GameObject using Euler angles. Supports world/local space and absolute/relative rotation.";
-            IsAsync = false;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Find the GameObject
-            var findResult = TransformToolUtils.FindGameObject(parameters);
-            if (findResult.Error != null)
-                return findResult.Error;
-
-            GameObject gameObject = findResult.GameObject;
-            Transform transform = gameObject.transform;
-
-            // Extract rotation (Euler angles)
-            JObject rotationObj = parameters["rotation"] as JObject;
-            if (rotationObj == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'rotation' not provided",
-                    "validation_error"
-                );
-            }
-
-            Vector3 eulerAngles = new Vector3(
-                rotationObj["x"]?.ToObject() ?? 0f,
-                rotationObj["y"]?.ToObject() ?? 0f,
-                rotationObj["z"]?.ToObject() ?? 0f
-            );
-
-            // Get space and relative flags
-            string space = parameters["space"]?.ToObject() ?? "world";
-            bool relative = parameters["relative"]?.ToObject() ?? false;
-
-            // Record undo
-            Undo.RecordObject(transform, "Rotate GameObject");
-
-            // Apply the rotation
-            if (relative)
-            {
-                // Relative rotation - add to current rotation
-                Space unitySpace = space.ToLower() == "local" ? Space.Self : Space.World;
-                transform.Rotate(eulerAngles, unitySpace);
-            }
-            else
-            {
-                // Absolute rotation - set directly
-                if (space.ToLower() == "local")
-                    transform.localEulerAngles = eulerAngles;
-                else
-                    transform.eulerAngles = eulerAngles;
-            }
-
-            EditorUtility.SetDirty(gameObject);
-
-            // Return result with new rotation
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"GameObject '{gameObject.name}' rotated successfully.",
-                ["instanceId"] = UnityObjectId.GetObjectId(gameObject),
-                ["name"] = gameObject.name,
-                ["path"] = TransformToolUtils.GetGameObjectPath(gameObject),
-                ["rotation"] = new JObject
-                {
-                    ["world"] = new JObject
-                    {
-                        ["x"] = transform.eulerAngles.x,
-                        ["y"] = transform.eulerAngles.y,
-                        ["z"] = transform.eulerAngles.z
-                    },
-                    ["local"] = new JObject
-                    {
-                        ["x"] = transform.localEulerAngles.x,
-                        ["y"] = transform.localEulerAngles.y,
-                        ["z"] = transform.localEulerAngles.z
-                    }
-                }
-            };
-        }
-    }
-
-    /// 
-    /// Tool for scaling a GameObject in the Unity Editor.
-    /// Supports absolute and relative (multiplicative) scaling.
-    /// 
-    public class ScaleGameObjectTool : McpToolBase
-    {
-        public ScaleGameObjectTool()
-        {
-            Name = "scale_gameobject";
-            Description = "Scales a GameObject. Supports absolute and relative (multiplicative) scaling.";
-            IsAsync = false;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Find the GameObject
-            var findResult = TransformToolUtils.FindGameObject(parameters);
-            if (findResult.Error != null)
-                return findResult.Error;
-
-            GameObject gameObject = findResult.GameObject;
-            Transform transform = gameObject.transform;
-
-            // Extract scale
-            JObject scaleObj = parameters["scale"] as JObject;
-            if (scaleObj == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'scale' not provided",
-                    "validation_error"
-                );
-            }
-
-            Vector3 scale = new Vector3(
-                scaleObj["x"]?.ToObject() ?? 1f,
-                scaleObj["y"]?.ToObject() ?? 1f,
-                scaleObj["z"]?.ToObject() ?? 1f
-            );
-
-            // Get relative flag
-            bool relative = parameters["relative"]?.ToObject() ?? false;
-
-            // Record undo
-            Undo.RecordObject(transform, "Scale GameObject");
-
-            // Apply the scale
-            if (relative)
-            {
-                // Relative scale - multiply current scale
-                transform.localScale = Vector3.Scale(transform.localScale, scale);
-            }
-            else
-            {
-                // Absolute scale - set directly
-                transform.localScale = scale;
-            }
-
-            EditorUtility.SetDirty(gameObject);
-
-            // Return result with new scale
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"GameObject '{gameObject.name}' scaled successfully.",
-                ["instanceId"] = UnityObjectId.GetObjectId(gameObject),
-                ["name"] = gameObject.name,
-                ["path"] = TransformToolUtils.GetGameObjectPath(gameObject),
-                ["scale"] = new JObject
-                {
-                    ["x"] = transform.localScale.x,
-                    ["y"] = transform.localScale.y,
-                    ["z"] = transform.localScale.z
-                }
-            };
-        }
-    }
-
-    /// 
-    /// Tool for setting a GameObject's full transform (position, rotation, scale) in one operation.
-    /// All parameters are optional - only provided values will be changed.
-    /// 
-    public class SetTransformTool : McpToolBase
-    {
-        public SetTransformTool()
-        {
-            Name = "set_transform";
-            Description = "Sets a GameObject's transform (position, rotation, scale) in one operation. All transform properties are optional.";
-            IsAsync = false;
-        }
-
-        public override JObject Execute(JObject parameters)
-        {
-            // Find the GameObject
-            var findResult = TransformToolUtils.FindGameObject(parameters);
-            if (findResult.Error != null)
-                return findResult.Error;
-
-            GameObject gameObject = findResult.GameObject;
-            Transform transform = gameObject.transform;
-
-            // Check that at least one transform property is provided
-            JObject positionObj = parameters["position"] as JObject;
-            JObject rotationObj = parameters["rotation"] as JObject;
-            JObject scaleObj = parameters["scale"] as JObject;
-
-            if (positionObj == null && rotationObj == null && scaleObj == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "At least one of 'position', 'rotation', or 'scale' must be provided",
-                    "validation_error"
-                );
-            }
-
-            // Get space flag (applies to position and rotation)
-            string space = parameters["space"]?.ToObject() ?? "world";
-            bool isLocal = space.ToLower() == "local";
-
-            // Record undo
-            Undo.RecordObject(transform, "Set Transform");
-
-            // Apply position if provided
-            if (positionObj != null)
-            {
-                Vector3 position = new Vector3(
-                    positionObj["x"]?.ToObject() ?? (isLocal ? transform.localPosition.x : transform.position.x),
-                    positionObj["y"]?.ToObject() ?? (isLocal ? transform.localPosition.y : transform.position.y),
-                    positionObj["z"]?.ToObject() ?? (isLocal ? transform.localPosition.z : transform.position.z)
-                );
-
-                if (isLocal)
-                    transform.localPosition = position;
-                else
-                    transform.position = position;
-            }
-
-            // Apply rotation if provided
-            if (rotationObj != null)
-            {
-                Vector3 eulerAngles = new Vector3(
-                    rotationObj["x"]?.ToObject() ?? (isLocal ? transform.localEulerAngles.x : transform.eulerAngles.x),
-                    rotationObj["y"]?.ToObject() ?? (isLocal ? transform.localEulerAngles.y : transform.eulerAngles.y),
-                    rotationObj["z"]?.ToObject() ?? (isLocal ? transform.localEulerAngles.z : transform.eulerAngles.z)
-                );
-
-                if (isLocal)
-                    transform.localEulerAngles = eulerAngles;
-                else
-                    transform.eulerAngles = eulerAngles;
-            }
-
-            // Apply scale if provided
-            if (scaleObj != null)
-            {
-                Vector3 scale = new Vector3(
-                    scaleObj["x"]?.ToObject() ?? transform.localScale.x,
-                    scaleObj["y"]?.ToObject() ?? transform.localScale.y,
-                    scaleObj["z"]?.ToObject() ?? transform.localScale.z
-                );
-                transform.localScale = scale;
-            }
-
-            EditorUtility.SetDirty(gameObject);
-
-            // Return result with full transform data
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"GameObject '{gameObject.name}' transform updated successfully.",
-                ["instanceId"] = UnityObjectId.GetObjectId(gameObject),
-                ["name"] = gameObject.name,
-                ["path"] = TransformToolUtils.GetGameObjectPath(gameObject),
-                ["transform"] = new JObject
-                {
-                    ["position"] = new JObject
-                    {
-                        ["world"] = new JObject
-                        {
-                            ["x"] = transform.position.x,
-                            ["y"] = transform.position.y,
-                            ["z"] = transform.position.z
-                        },
-                        ["local"] = new JObject
-                        {
-                            ["x"] = transform.localPosition.x,
-                            ["y"] = transform.localPosition.y,
-                            ["z"] = transform.localPosition.z
-                        }
-                    },
-                    ["rotation"] = new JObject
-                    {
-                        ["world"] = new JObject
-                        {
-                            ["x"] = transform.eulerAngles.x,
-                            ["y"] = transform.eulerAngles.y,
-                            ["z"] = transform.eulerAngles.z
-                        },
-                        ["local"] = new JObject
-                        {
-                            ["x"] = transform.localEulerAngles.x,
-                            ["y"] = transform.localEulerAngles.y,
-                            ["z"] = transform.localEulerAngles.z
-                        }
-                    },
-                    ["scale"] = new JObject
-                    {
-                        ["x"] = transform.localScale.x,
-                        ["y"] = transform.localScale.y,
-                        ["z"] = transform.localScale.z
-                    }
-                }
-            };
-        }
-    }
-
-    /// 
-    /// Utility class for common transform tool operations
-    /// 
-    internal static class TransformToolUtils
-    {
-        /// 
-        /// Result of finding a GameObject
-        /// 
-        public struct FindResult
-        {
-            public GameObject GameObject;
-            public JObject Error;
-        }
-
-        /// 
-        /// Find a GameObject by instanceId or objectPath from parameters
-        /// 
-        public static FindResult FindGameObject(JObject parameters)
-        {
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            string objectPath = parameters["objectPath"]?.ToObject();
-
-            GameObject gameObject = null;
-            string identifierInfo = "";
-
-            if (instanceId.HasValue)
-            {
-                gameObject = UnityObjectId.ObjectFromId(instanceId.Value) as GameObject;
-                identifierInfo = $"instance ID {instanceId.Value}";
-            }
-            else if (!string.IsNullOrEmpty(objectPath))
-            {
-                gameObject = GameObject.Find(objectPath);
-                identifierInfo = $"path '{objectPath}'";
-            }
-            else
-            {
-                return new FindResult
-                {
-                    Error = McpUnitySocketHandler.CreateErrorResponse(
-                        "Either 'instanceId' or 'objectPath' must be provided",
-                        "validation_error"
-                    )
-                };
-            }
-
-            if (gameObject == null)
-            {
-                return new FindResult
-                {
-                    Error = McpUnitySocketHandler.CreateErrorResponse(
-                        $"GameObject not found with {identifierInfo}",
-                        "not_found_error"
-                    )
-                };
-            }
-
-            return new FindResult { GameObject = gameObject };
-        }
-
-        /// 
-        /// Get the hierarchy path of a GameObject
-        /// 
-        public static string GetGameObjectPath(GameObject obj)
-        {
-            if (obj == null) return null;
-            string path = "/" + obj.name;
-            while (obj.transform.parent != null)
-            {
-                obj = obj.transform.parent.gameObject;
-                path = "/" + obj.name + path;
-            }
-            return path;
-        }
-    }
-}
diff --git a/Editor/Tools/TransformTools.cs.meta b/Editor/Tools/TransformTools.cs.meta
deleted file mode 100644
index 521c9a41..00000000
--- a/Editor/Tools/TransformTools.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 45ce397497b614ef2ba5df0d2031e3c9
\ No newline at end of file
diff --git a/Editor/Tools/UnloadSceneTool.cs b/Editor/Tools/UnloadSceneTool.cs
deleted file mode 100644
index fd3342f3..00000000
--- a/Editor/Tools/UnloadSceneTool.cs
+++ /dev/null
@@ -1,118 +0,0 @@
-using System;
-using UnityEditor;
-using UnityEditor.SceneManagement;
-using UnityEngine.SceneManagement;
-using Newtonsoft.Json.Linq;
-using McpUnity.Unity;
-using McpUnity.Utils;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for unloading a Unity scene (without deleting the asset)
-    /// 
-    public class UnloadSceneTool : McpToolBase
-    {
-        public UnloadSceneTool()
-        {
-            Name = "unload_scene";
-            Description = "Unloads a scene by path or name (does not delete the scene asset, just closes it from the hierarchy)";
-        }
-
-        /// 
-        /// Execute the UnloadScene tool with the provided parameters
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            string scenePath = parameters["scenePath"]?.ToObject();
-            string sceneName = parameters["sceneName"]?.ToObject();
-            bool removeScene = parameters["removeScene"]?.ToObject() ?? true;
-
-            if (string.IsNullOrEmpty(scenePath) && string.IsNullOrEmpty(sceneName))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Provide either 'scenePath' or 'sceneName'",
-                    "validation_error"
-                );
-            }
-
-            try
-            {
-                Scene sceneToUnload;
-
-                if (!string.IsNullOrEmpty(scenePath))
-                {
-                    sceneToUnload = SceneManager.GetSceneByPath(scenePath);
-                }
-                else
-                {
-                    sceneToUnload = SceneManager.GetSceneByName(sceneName);
-                }
-
-                if (!sceneToUnload.IsValid())
-                {
-                    string identifier = !string.IsNullOrEmpty(scenePath) ? $"path '{scenePath}'" : $"name '{sceneName}'";
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Scene with {identifier} is not currently loaded",
-                        "not_found_error"
-                    );
-                }
-
-                // Check if this is the only loaded scene
-                if (SceneManager.sceneCount <= 1)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        "Cannot unload the only loaded scene. Load another scene first or create a new scene",
-                        "validation_error"
-                    );
-                }
-
-                string unloadedSceneName = sceneToUnload.name;
-                string unloadedScenePath = sceneToUnload.path;
-                bool wasDirty = sceneToUnload.isDirty;
-
-                // If scene has unsaved changes, save it first
-                if (wasDirty)
-                {
-                    bool savePrompt = parameters["saveIfDirty"]?.ToObject() ?? true;
-                    if (savePrompt && !string.IsNullOrEmpty(unloadedScenePath))
-                    {
-                        EditorSceneManager.SaveScene(sceneToUnload);
-                    }
-                }
-
-                // Close/unload the scene
-                bool success = EditorSceneManager.CloseScene(sceneToUnload, removeScene);
-
-                if (!success)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Failed to unload scene '{unloadedSceneName}'",
-                        "unload_error"
-                    );
-                }
-
-                McpLogger.LogInfo($"Unloaded scene '{unloadedSceneName}' (path: '{unloadedScenePath}')");
-
-                return new JObject
-                {
-                    ["success"] = true,
-                    ["type"] = "text",
-                    ["message"] = $"Successfully unloaded scene '{unloadedSceneName}'",
-                    ["sceneName"] = unloadedSceneName,
-                    ["scenePath"] = unloadedScenePath,
-                    ["wasDirty"] = wasDirty,
-                    ["removed"] = removeScene
-                };
-            }
-            catch (Exception ex)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"Error unloading scene: {ex.Message}",
-                    "scene_unload_error"
-                );
-            }
-        }
-    }
-}
diff --git a/Editor/Tools/UnloadSceneTool.cs.meta b/Editor/Tools/UnloadSceneTool.cs.meta
deleted file mode 100644
index 3f8eecd9..00000000
--- a/Editor/Tools/UnloadSceneTool.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: 74d85884c08c2427ea9a470bf0ab6bae
\ No newline at end of file
diff --git a/Editor/Tools/UpdateComponentTool.cs b/Editor/Tools/UpdateComponentTool.cs
deleted file mode 100644
index 8e019881..00000000
--- a/Editor/Tools/UpdateComponentTool.cs
+++ /dev/null
@@ -1,858 +0,0 @@
-using System;
-using System.Reflection;
-using McpUnity.Unity;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for updating component data in the Unity Editor
-    /// 
-    public class UpdateComponentTool : McpToolBase
-    {
-        public UpdateComponentTool()
-        {
-            Name = "update_component";
-            Description = "Updates component fields on a GameObject or adds it to the GameObject if it does not contain the component";
-        }
-        
-        /// 
-        /// Execute the UpdateComponent tool with the provided parameters synchronously
-        /// 
-        /// Tool parameters as a JObject
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            string objectPath = parameters["objectPath"]?.ToObject();
-            string componentName = parameters["componentName"]?.ToObject();
-            JObject componentData = parameters["componentData"] as JObject;
-            
-            // Validate parameters - require either instanceId or objectPath
-            if (!instanceId.HasValue && string.IsNullOrEmpty(objectPath))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Either 'instanceId' or 'objectPath' must be provided", 
-                    "validation_error"
-                );
-            }
-            
-            if (string.IsNullOrEmpty(componentName))
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    "Required parameter 'componentName' not provided", 
-                    "validation_error"
-                );
-            }
-            
-            // Find the GameObject by instance ID or path
-            GameObject gameObject = null;
-            string identifier = "unknown";
-            
-            if (instanceId.HasValue)
-            {
-                gameObject = UnityObjectId.ObjectFromId(instanceId.Value) as GameObject;
-                identifier = $"ID {instanceId.Value}";
-            }
-            else
-            {
-                // Find by path
-                gameObject = GameObject.Find(objectPath);
-                identifier = $"path '{objectPath}'";
-                
-                if (gameObject == null)
-                {
-                    // Try to find using the Unity Scene hierarchy path
-                    gameObject = FindGameObjectByPath(objectPath);
-                }
-            }
-                    
-            if (gameObject == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse(
-                    $"GameObject with path '{objectPath}' or instance ID {instanceId} not found", 
-                    "not_found_error"
-                );
-            }
-            
-            McpLogger.LogInfo($"[MCP Unity] Updating component '{componentName}' on GameObject '{gameObject.name}' (found by {identifier})");
-            
-            // Try to find the component by name
-            Component component = gameObject.GetComponent(componentName);
-            
-            // If component not found, try to add it
-            if (component == null)
-            {
-                Type componentType = FindComponentType(componentName);
-                if (componentType == null)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(
-                        $"Component type '{componentName}' not found in Unity", 
-                        "component_error"
-                    );
-                }
-                
-                component = Undo.AddComponent(gameObject, componentType);
-
-                // Ensure changes are saved
-                EditorUtility.SetDirty(gameObject);
-                if (PrefabUtility.IsPartOfAnyPrefab(gameObject))
-                {
-                    PrefabUtility.RecordPrefabInstancePropertyModifications(component);
-                }
-                
-                McpLogger.LogInfo($"[MCP Unity] Added component '{componentName}' to GameObject '{gameObject.name}'");
-            }
-            // Update component fields
-            if (componentData != null && componentData.Count > 0)
-            {
-                bool success = UpdateComponentData(component, componentData, out string errorMessage);
-                // If update failed, return error
-                if (!success)
-                {
-                    return McpUnitySocketHandler.CreateErrorResponse(errorMessage, "update_error");
-                }
-
-                // Ensure field changes are saved
-                EditorUtility.SetDirty(gameObject);
-                if (PrefabUtility.IsPartOfAnyPrefab(gameObject))
-                {
-                    PrefabUtility.RecordPrefabInstancePropertyModifications(component);
-                }
-
-            }
-
-            // Create the response
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = $"Successfully updated component '{componentName}' on GameObject '{gameObject.name}'"
-            };
-        }
-        
-        /// 
-        /// Find a GameObject by its hierarchy path
-        /// 
-        /// The path to the GameObject (e.g. "Canvas/Panel/Button")
-        /// The GameObject if found, null otherwise
-        private GameObject FindGameObjectByPath(string path)
-        {
-            // Split the path by '/'
-            string[] pathParts = path.Split('/');
-            GameObject[] rootGameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
-            
-            // If the path is empty, return null
-            if (pathParts.Length == 0)
-            {
-                return null;
-            }
-            
-            // Search through all root GameObjects in all scenes
-            foreach (GameObject rootObj in rootGameObjects)
-            {
-                if (rootObj.name == pathParts[0])
-                {
-                    // Found the root object, now traverse down the path
-                    GameObject current = rootObj;
-                    
-                    // Start from index 1 since we've already matched the root
-                    for (int i = 1; i < pathParts.Length; i++)
-                    {
-                        Transform child = current.transform.Find(pathParts[i]);
-                        if (child == null)
-                        {
-                            // Path segment not found
-                            return null;
-                        }
-                        
-                        // Move to the next level
-                        current = child.gameObject;
-                    }
-                    
-                    // If we got here, we found the full path
-                    return current;
-                }
-            }
-            
-            // Not found
-            return null;
-        }
-        
-        /// 
-        /// Find a component type by name
-        /// 
-        /// The name of the component type
-        /// The component type, or null if not found
-        private Type FindComponentType(string componentName)
-        {
-            // First try direct match
-            Type type = Type.GetType(componentName);
-            if (type != null && typeof(Component).IsAssignableFrom(type))
-            {
-                return type;
-            }
-            
-            // Try common Unity namespaces
-            string[] commonNamespaces = new string[] 
-            {
-                "UnityEngine",
-                "UnityEngine.UI",
-                "UnityEngine.EventSystems",
-                "UnityEngine.Animations",
-                "UnityEngine.Rendering",
-                "TMPro"
-            };
-            
-            foreach (string ns in commonNamespaces)
-            {
-                type = Type.GetType($"{ns}.{componentName}, UnityEngine");
-                if (type != null && typeof(Component).IsAssignableFrom(type))
-                {
-                    return type;
-                }
-            }
-            
-            // Try assemblies search
-            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
-            {
-                try
-                {
-                    foreach (Type t in assembly.GetTypes())
-                    {
-                        if (t.Name == componentName && typeof(Component).IsAssignableFrom(t))
-                        {
-                            return t;
-                        }
-                    }
-                }
-                catch (Exception)
-                {
-                    // Some assemblies might throw exceptions when getting types
-                    continue;
-                }
-            }
-            
-            return null;
-        }
-
-        private FieldInfo GetFieldRecursive(Type type, string fieldName)
-        {
-            while (type != null && type != typeof(object))
-            {
-                FieldInfo field = type.GetField(fieldName,
-                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
-                if (field != null)
-                {
-                    return field;
-                }
-
-                type = type.BaseType;
-            }
-
-            return null;
-        }
-
-        private PropertyInfo GetPropertyRecursive(Type type, string propertyName)
-        {
-            while (type != null && type != typeof(object))
-            {
-                PropertyInfo prop = type.GetProperty(propertyName,
-                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
-                if (prop != null)
-                {
-                    return prop;
-                }
-
-                type = type.BaseType;
-            }
-
-            return null;
-        }
-
-        /// 
-        /// Update component data based on the provided JObject
-        /// Uses SerializedObject API as primary method (handles base class fields and nested paths),
-        /// with reflection as fallback for non-serialized properties.
-        /// 
-        /// The component to update
-        /// The data to apply to the component
-        /// Error message if any fields failed to update
-        /// True if all fields were updated successfully
-        private bool UpdateComponentData(Component component, JObject componentData, out string errorMessage)
-        {
-            errorMessage = "";
-            
-            if (component == null || componentData == null)
-            {
-                errorMessage = "Component or component data is null";
-                return false;
-            }
-
-            Type componentType = component.GetType();
-            bool fullSuccess = true;
-
-            // Record object for undo
-            Undo.RecordObject(component, $"Update {componentType.Name} fields");
-
-            SerializedObject serializedObject = new SerializedObject(component);
-
-            foreach (var property in componentData.Properties())
-            {
-                string fieldName = property.Name;
-                JToken fieldValue = property.Value;
-
-                if (string.IsNullOrEmpty(fieldName))
-                {
-                    continue;
-                }
-
-                SerializedProperty serializedProperty = serializedObject.FindProperty(fieldName);
-                if (serializedProperty != null)
-                {
-                    if (!TrySetSerializedPropertyValue(serializedProperty, fieldValue, out string setError))
-                    {
-                        fullSuccess = false;
-                        errorMessage = setError;
-                        McpLogger.LogWarning($"[MCP Unity] {errorMessage}");
-                        break;
-                    }
-
-                    continue;
-                }
-
-                FieldInfo fieldInfo = GetFieldRecursive(componentType, fieldName);
-                if (fieldInfo != null)
-                {
-                    if (!TryConvertJTokenToValue(fieldValue, fieldInfo.FieldType, out object value, out string convertError))
-                    {
-                        fullSuccess = false;
-                        errorMessage = $"Could not convert field '{fieldName}' on component '{componentType.Name}': {convertError}";
-                        McpLogger.LogWarning($"[MCP Unity] {errorMessage}");
-                        break;
-                    }
-
-                    fieldInfo.SetValue(component, value);
-                    continue;
-                }
-
-                PropertyInfo propertyInfo = GetPropertyRecursive(componentType, fieldName);
-                if (propertyInfo != null)
-                {
-                    if (!propertyInfo.CanWrite)
-                    {
-                        fullSuccess = false;
-                        errorMessage = $"Property '{fieldName}' on component '{componentType.Name}' is read-only";
-                        McpLogger.LogWarning($"[MCP Unity] {errorMessage}");
-                        break;
-                    }
-
-                    if (!TryConvertJTokenToValue(fieldValue, propertyInfo.PropertyType, out object value, out string convertError))
-                    {
-                        fullSuccess = false;
-                        errorMessage = $"Could not convert property '{fieldName}' on component '{componentType.Name}': {convertError}";
-                        McpLogger.LogWarning($"[MCP Unity] {errorMessage}");
-                        break;
-                    }
-
-                    propertyInfo.SetValue(component, value);
-                    continue;
-                }
-
-                fullSuccess = false;
-                errorMessage = $"Field or Property with name '{fieldName}' not found on component '{componentType.Name}'";
-                McpLogger.LogWarning($"[MCP Unity] {errorMessage}");
-                break;
-            }
-
-            if (fullSuccess)
-            {
-                serializedObject.ApplyModifiedProperties();
-            }
-
-            return fullSuccess;
-        }
-
-        private bool TrySetSerializedPropertyValue(SerializedProperty prop, JToken value, out string errorMessage)
-        {
-            errorMessage = "";
-
-            try
-            {
-                switch (prop.propertyType)
-                {
-                    case SerializedPropertyType.Integer:
-                        prop.intValue = value.ToObject();
-                        return true;
-                    case SerializedPropertyType.Boolean:
-                        prop.boolValue = value.ToObject();
-                        return true;
-                    case SerializedPropertyType.Float:
-                        prop.floatValue = value.ToObject();
-                        return true;
-                    case SerializedPropertyType.String:
-                        prop.stringValue = value.Type == JTokenType.Null ? null : value.ToObject();
-                        return true;
-                    case SerializedPropertyType.Color:
-                        if (!TryReadColor(value, out Color color, out errorMessage)) return false;
-                        prop.colorValue = color;
-                        return true;
-                    case SerializedPropertyType.Vector2:
-                        if (!TryReadVector2(value, out Vector2 vector2, out errorMessage)) return false;
-                        prop.vector2Value = vector2;
-                        return true;
-                    case SerializedPropertyType.Vector3:
-                        if (!TryReadVector3(value, out Vector3 vector3, out errorMessage)) return false;
-                        prop.vector3Value = vector3;
-                        return true;
-                    case SerializedPropertyType.Vector4:
-                        if (!TryReadVector4(value, out Vector4 vector4, out errorMessage)) return false;
-                        prop.vector4Value = vector4;
-                        return true;
-                    case SerializedPropertyType.Quaternion:
-                        if (!TryReadQuaternion(value, out Quaternion quaternion, out errorMessage)) return false;
-                        prop.quaternionValue = quaternion;
-                        return true;
-                    case SerializedPropertyType.Rect:
-                        if (!TryReadRect(value, out Rect rect, out errorMessage)) return false;
-                        prop.rectValue = rect;
-                        return true;
-                    case SerializedPropertyType.Bounds:
-                        if (!TryReadBounds(value, out Bounds bounds, out errorMessage)) return false;
-                        prop.boundsValue = bounds;
-                        return true;
-                    case SerializedPropertyType.Enum:
-                        return TrySetEnumProperty(prop, value, out errorMessage);
-                    case SerializedPropertyType.ObjectReference:
-                        if (!TryLoadObjectReference(value, typeof(UnityEngine.Object), out UnityEngine.Object asset, out errorMessage))
-                        {
-                            return false;
-                        }
-
-                        prop.objectReferenceValue = asset;
-                        return true;
-                    case SerializedPropertyType.LayerMask:
-                        prop.intValue = value.ToObject();
-                        return true;
-                    case SerializedPropertyType.Vector2Int:
-                        if (!TryReadVector2Int(value, out Vector2Int vector2Int, out errorMessage)) return false;
-                        prop.vector2IntValue = vector2Int;
-                        return true;
-                    case SerializedPropertyType.Vector3Int:
-                        if (!TryReadVector3Int(value, out Vector3Int vector3Int, out errorMessage)) return false;
-                        prop.vector3IntValue = vector3Int;
-                        return true;
-                    case SerializedPropertyType.RectInt:
-                        if (!TryReadRectInt(value, out RectInt rectInt, out errorMessage)) return false;
-                        prop.rectIntValue = rectInt;
-                        return true;
-                    case SerializedPropertyType.BoundsInt:
-                        if (!TryReadBoundsInt(value, out BoundsInt boundsInt, out errorMessage)) return false;
-                        prop.boundsIntValue = boundsInt;
-                        return true;
-                    case SerializedPropertyType.Generic:
-                        return TrySetGenericProperty(prop, value, out errorMessage);
-                    case SerializedPropertyType.ArraySize:
-                        prop.intValue = value.ToObject();
-                        return true;
-                    default:
-                        errorMessage = $"Unsupported property type '{prop.propertyType}' for '{prop.propertyPath}'";
-                        return false;
-                }
-            }
-            catch (Exception ex)
-            {
-                errorMessage = $"Could not set '{prop.propertyPath}' ({prop.propertyType}): {ex.Message}";
-                return false;
-            }
-        }
-
-        /// 
-        /// Convert a JToken to a value of the specified type
-        /// 
-        /// The JToken to convert
-        /// The target type to convert to
-        /// The converted value
-        private bool TryConvertJTokenToValue(JToken token, Type targetType, out object value, out string errorMessage)
-        {
-            value = null;
-            errorMessage = "";
-
-            if (token == null || token.Type == JTokenType.Null)
-            {
-                if (targetType.IsValueType && Nullable.GetUnderlyingType(targetType) == null)
-                {
-                    errorMessage = $"Cannot assign null to value type '{targetType.Name}'";
-                    return false;
-                }
-
-                return true;
-            }
-
-            // Handle Unity Vector types
-            if (targetType == typeof(Vector2) && token.Type == JTokenType.Object)
-            {
-                bool success = TryReadVector2(token, out Vector2 vector, out errorMessage);
-                value = vector;
-                return success;
-            }
-
-            if (targetType == typeof(Vector3) && token.Type == JTokenType.Object)
-            {
-                bool success = TryReadVector3(token, out Vector3 vector, out errorMessage);
-                value = vector;
-                return success;
-            }
-
-            if (targetType == typeof(Vector4) && token.Type == JTokenType.Object)
-            {
-                bool success = TryReadVector4(token, out Vector4 vector, out errorMessage);
-                value = vector;
-                return success;
-            }
-
-            if (targetType == typeof(Quaternion) && token.Type == JTokenType.Object)
-            {
-                bool success = TryReadQuaternion(token, out Quaternion quaternion, out errorMessage);
-                value = quaternion;
-                return success;
-            }
-
-            if (targetType == typeof(Color) && token.Type == JTokenType.Object)
-            {
-                bool success = TryReadColor(token, out Color color, out errorMessage);
-                value = color;
-                return success;
-            }
-
-            if (targetType == typeof(Bounds) && token.Type == JTokenType.Object)
-            {
-                bool success = TryReadBounds(token, out Bounds bounds, out errorMessage);
-                value = bounds;
-                return success;
-            }
-
-            if (targetType == typeof(Rect) && token.Type == JTokenType.Object)
-            {
-                bool success = TryReadRect(token, out Rect rect, out errorMessage);
-                value = rect;
-                return success;
-            }
-
-            // Handle UnityEngine.Object types (assets) by path or GUID
-            if (typeof(UnityEngine.Object).IsAssignableFrom(targetType))
-            {
-                bool success = TryLoadObjectReference(token, targetType, out UnityEngine.Object asset, out errorMessage);
-                value = asset;
-                return success;
-            }
-
-            // Handle enum types
-            if (targetType.IsEnum)
-            {
-                if (token.Type == JTokenType.String)
-                {
-                    string enumName = token.ToObject();
-                    if (Enum.TryParse(targetType, enumName, true, out object result))
-                    {
-                        value = result;
-                        return true;
-                    }
-
-                    if (int.TryParse(enumName, out int enumValue))
-                    {
-                        value = Enum.ToObject(targetType, enumValue);
-                        return true;
-                    }
-
-                    errorMessage = $"'{enumName}' is not a valid value for enum '{targetType.Name}'";
-                    return false;
-                }
-
-                if (token.Type == JTokenType.Integer)
-                {
-                    value = Enum.ToObject(targetType, token.ToObject());
-                    return true;
-                }
-
-                errorMessage = $"Expected string or integer for enum '{targetType.Name}'";
-                return false;
-            }
-
-            try
-            {
-                value = token.ToObject(targetType);
-                return true;
-            }
-            catch (Exception ex)
-            {
-                errorMessage = $"Error converting value to type {targetType.Name}: {ex.Message}";
-                return false;
-            }
-        }
-
-        private bool TrySetGenericProperty(SerializedProperty prop, JToken value, out string errorMessage)
-        {
-            errorMessage = "";
-
-            if (value.Type != JTokenType.Object)
-            {
-                errorMessage = $"Expected object value for '{prop.propertyPath}'";
-                return false;
-            }
-
-            foreach (var child in ((JObject)value).Properties())
-            {
-                SerializedProperty childProp = prop.FindPropertyRelative(child.Name);
-                if (childProp == null)
-                {
-                    errorMessage = $"Nested property '{child.Name}' not found under '{prop.propertyPath}'";
-                    return false;
-                }
-
-                if (!TrySetSerializedPropertyValue(childProp, child.Value, out errorMessage))
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        private bool TrySetEnumProperty(SerializedProperty prop, JToken value, out string errorMessage)
-        {
-            errorMessage = "";
-
-            if (value.Type == JTokenType.String)
-            {
-                string enumValue = value.ToObject();
-                string[] enumNames = prop.enumNames;
-
-                for (int i = 0; i < enumNames.Length; i++)
-                {
-                    if (string.Equals(enumNames[i], enumValue, StringComparison.OrdinalIgnoreCase))
-                    {
-                        prop.enumValueIndex = i;
-                        return true;
-                    }
-                }
-
-                errorMessage = $"'{enumValue}' is not a valid value for '{prop.propertyPath}'";
-                return false;
-            }
-
-            if (value.Type == JTokenType.Integer)
-            {
-                int index = value.ToObject();
-                if (index < 0 || index >= prop.enumNames.Length)
-                {
-                    errorMessage = $"Enum index {index} is out of range for '{prop.propertyPath}'";
-                    return false;
-                }
-
-                prop.enumValueIndex = index;
-                return true;
-            }
-
-            errorMessage = $"Expected string or integer enum value for '{prop.propertyPath}'";
-            return false;
-        }
-
-        private bool TryLoadObjectReference(JToken token, Type targetType, out UnityEngine.Object asset, out string errorMessage)
-        {
-            asset = null;
-            errorMessage = "";
-
-            if (token == null || token.Type == JTokenType.Null)
-            {
-                return true;
-            }
-
-            string assetPath = null;
-
-            if (token.Type == JTokenType.String)
-            {
-                string input = token.ToObject();
-                if (string.IsNullOrEmpty(input))
-                {
-                    return true;
-                }
-
-                if (input.StartsWith("Assets/") || input.StartsWith("Packages/"))
-                {
-                    assetPath = input;
-                }
-                else
-                {
-                    string[] guids = AssetDatabase.FindAssets($"{input} t:{targetType.Name}");
-                    if (guids.Length == 0)
-                    {
-                        guids = AssetDatabase.FindAssets(input);
-                    }
-
-                    if (guids.Length > 0)
-                    {
-                        assetPath = AssetDatabase.GUIDToAssetPath(guids[0]);
-                    }
-                }
-            }
-            else if (token.Type == JTokenType.Object)
-            {
-                JObject obj = (JObject)token;
-                string guid = obj["guid"]?.ToObject();
-                string path = obj["path"]?.ToObject();
-
-                if (!string.IsNullOrEmpty(guid))
-                {
-                    assetPath = AssetDatabase.GUIDToAssetPath(guid);
-                    if (string.IsNullOrEmpty(assetPath))
-                    {
-                        errorMessage = $"Could not find asset with GUID '{guid}'";
-                        return false;
-                    }
-                }
-                else
-                {
-                    assetPath = path;
-                }
-            }
-            else
-            {
-                errorMessage = "Object references must be null, an asset path, an asset name, or an object with guid/path";
-                return false;
-            }
-
-            if (string.IsNullOrEmpty(assetPath))
-            {
-                errorMessage = $"Could not find asset '{token}'";
-                return false;
-            }
-
-            asset = AssetDatabase.LoadAssetAtPath(assetPath, targetType);
-            if (asset == null)
-            {
-                errorMessage = $"Could not find asset at path '{assetPath}'";
-                return false;
-            }
-
-            return true;
-        }
-
-        private static bool TryReadObject(JToken value, string expectedType, out JObject obj, out string errorMessage)
-        {
-            obj = value as JObject;
-            errorMessage = "";
-
-            if (obj != null)
-            {
-                return true;
-            }
-
-            errorMessage = $"Expected object value for {expectedType}";
-            return false;
-        }
-
-        private static bool TryReadVector2(JToken value, out Vector2 vector, out string errorMessage)
-        {
-            vector = Vector2.zero;
-            if (!TryReadObject(value, "Vector2", out JObject obj, out errorMessage)) return false;
-            vector = new Vector2(obj["x"]?.ToObject() ?? 0f, obj["y"]?.ToObject() ?? 0f);
-            return true;
-        }
-
-        private static bool TryReadVector3(JToken value, out Vector3 vector, out string errorMessage)
-        {
-            vector = Vector3.zero;
-            if (!TryReadObject(value, "Vector3", out JObject obj, out errorMessage)) return false;
-            vector = new Vector3(obj["x"]?.ToObject() ?? 0f, obj["y"]?.ToObject() ?? 0f, obj["z"]?.ToObject() ?? 0f);
-            return true;
-        }
-
-        private static bool TryReadVector4(JToken value, out Vector4 vector, out string errorMessage)
-        {
-            vector = Vector4.zero;
-            if (!TryReadObject(value, "Vector4", out JObject obj, out errorMessage)) return false;
-            vector = new Vector4(obj["x"]?.ToObject() ?? 0f, obj["y"]?.ToObject() ?? 0f, obj["z"]?.ToObject() ?? 0f, obj["w"]?.ToObject() ?? 0f);
-            return true;
-        }
-
-        private static bool TryReadQuaternion(JToken value, out Quaternion quaternion, out string errorMessage)
-        {
-            quaternion = Quaternion.identity;
-            if (!TryReadObject(value, "Quaternion", out JObject obj, out errorMessage)) return false;
-            quaternion = new Quaternion(obj["x"]?.ToObject() ?? 0f, obj["y"]?.ToObject() ?? 0f, obj["z"]?.ToObject() ?? 0f, obj["w"]?.ToObject() ?? 1f);
-            return true;
-        }
-
-        private static bool TryReadColor(JToken value, out Color color, out string errorMessage)
-        {
-            color = Color.clear;
-            if (!TryReadObject(value, "Color", out JObject obj, out errorMessage)) return false;
-            color = new Color(obj["r"]?.ToObject() ?? 0f, obj["g"]?.ToObject() ?? 0f, obj["b"]?.ToObject() ?? 0f, obj["a"]?.ToObject() ?? 1f);
-            return true;
-        }
-
-        private static bool TryReadRect(JToken value, out Rect rect, out string errorMessage)
-        {
-            rect = new Rect();
-            if (!TryReadObject(value, "Rect", out JObject obj, out errorMessage)) return false;
-            rect = new Rect(obj["x"]?.ToObject() ?? 0f, obj["y"]?.ToObject() ?? 0f, obj["width"]?.ToObject() ?? 0f, obj["height"]?.ToObject() ?? 0f);
-            return true;
-        }
-
-        private static bool TryReadBounds(JToken value, out Bounds bounds, out string errorMessage)
-        {
-            bounds = new Bounds(Vector3.zero, Vector3.one);
-            if (!TryReadObject(value, "Bounds", out JObject obj, out errorMessage)) return false;
-
-            Vector3 center = Vector3.zero;
-            Vector3 size = Vector3.one;
-            if (obj["center"] != null && !TryReadVector3(obj["center"], out center, out errorMessage)) return false;
-            if (obj["size"] != null && !TryReadVector3(obj["size"], out size, out errorMessage)) return false;
-
-            bounds = new Bounds(center, size);
-            return true;
-        }
-
-        private static bool TryReadVector2Int(JToken value, out Vector2Int vector, out string errorMessage)
-        {
-            vector = Vector2Int.zero;
-            if (!TryReadObject(value, "Vector2Int", out JObject obj, out errorMessage)) return false;
-            vector = new Vector2Int(obj["x"]?.ToObject() ?? 0, obj["y"]?.ToObject() ?? 0);
-            return true;
-        }
-
-        private static bool TryReadVector3Int(JToken value, out Vector3Int vector, out string errorMessage)
-        {
-            vector = Vector3Int.zero;
-            if (!TryReadObject(value, "Vector3Int", out JObject obj, out errorMessage)) return false;
-            vector = new Vector3Int(obj["x"]?.ToObject() ?? 0, obj["y"]?.ToObject() ?? 0, obj["z"]?.ToObject() ?? 0);
-            return true;
-        }
-
-        private static bool TryReadRectInt(JToken value, out RectInt rect, out string errorMessage)
-        {
-            rect = new RectInt();
-            if (!TryReadObject(value, "RectInt", out JObject obj, out errorMessage)) return false;
-            rect = new RectInt(obj["x"]?.ToObject() ?? 0, obj["y"]?.ToObject() ?? 0, obj["width"]?.ToObject() ?? 0, obj["height"]?.ToObject() ?? 0);
-            return true;
-        }
-
-        private static bool TryReadBoundsInt(JToken value, out BoundsInt bounds, out string errorMessage)
-        {
-            bounds = new BoundsInt(Vector3Int.zero, Vector3Int.one);
-            if (!TryReadObject(value, "BoundsInt", out JObject obj, out errorMessage)) return false;
-
-            Vector3Int position = Vector3Int.zero;
-            Vector3Int size = Vector3Int.one;
-            if (obj["position"] != null && !TryReadVector3Int(obj["position"], out position, out errorMessage)) return false;
-            if (obj["size"] != null && !TryReadVector3Int(obj["size"], out size, out errorMessage)) return false;
-
-            bounds = new BoundsInt(position, size);
-            return true;
-        }
-    }
-}
diff --git a/Editor/Tools/UpdateComponentTool.cs.meta b/Editor/Tools/UpdateComponentTool.cs.meta
deleted file mode 100644
index 998bb368..00000000
--- a/Editor/Tools/UpdateComponentTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: da399442b5700bb448f52ad4c015d266
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Tools/UpdateGameObjectTool.cs b/Editor/Tools/UpdateGameObjectTool.cs
deleted file mode 100644
index c68dc5e4..00000000
--- a/Editor/Tools/UpdateGameObjectTool.cs
+++ /dev/null
@@ -1,161 +0,0 @@
-using System;
-using UnityEngine;
-using UnityEditor;
-using McpUnity.Utils; // For GameObjectHierarchyCreator and McpLogger
-using McpUnity.Unity; // For McpUnitySocketHandler
-using Newtonsoft.Json.Linq; // For JObject
-
-namespace McpUnity.Tools
-{
-    /// 
-    /// Tool for updating or creating a GameObject in the Unity Editor.
-    /// Supports setting name, tag, layer, active state, and static state by instance ID or hierarchy path.
-    /// Returns a JObject result similar to UpdateComponentTool for consistency.
-    /// 
-    public class UpdateGameObjectTool : McpToolBase
-    {
-        public UpdateGameObjectTool()
-        {
-            Name = "update_gameobject";
-            Description = "Updates or creates a GameObject and its properties (name, tag, layer, active state, static state) based on instance ID or object path.";
-            IsAsync = false; // Operations are expected to be quick
-        }
-
-        /// 
-        /// Executes the update or creation of a GameObject based on the provided parameters.
-        /// 
-        /// A JObject containing: instanceId (int?), objectPath (string), name (string), tag (string), layer (int?), isActiveSelf (bool?), isStatic (bool?)
-        /// JObject with success, message, instanceId, name, and path fields (see UpdateComponentTool for format)
-        public override JObject Execute(JObject parameters)
-        {
-            // Extract parameters from JObject
-            int? instanceId = parameters["instanceId"]?.ToObject();
-            string objectPath = parameters["objectPath"]?.ToObject();
-            JObject gameObjectData = parameters["gameObjectData"] as JObject;
-
-            string newName = gameObjectData? ["name"]?.ToObject();
-            string newTag = gameObjectData? ["tag"]?.ToObject();
-            int? newLayer = gameObjectData? ["layer"]?.ToObject();
-            bool? newIsActiveSelf = (gameObjectData?["activeSelf"] ?? gameObjectData?["isActiveSelf"])?.ToObject();
-            bool? newIsStatic = (gameObjectData?["isStatic"] ?? gameObjectData?["static"])?.ToObject();
-
-            GameObject targetGameObject = null;
-            string identifierInfo = "";
-
-            // Identify or create the GameObject by instanceId or objectPath
-            if (instanceId.HasValue)
-            {
-                targetGameObject = UnityObjectId.ObjectFromId(instanceId.Value) as GameObject;
-                identifierInfo = $"instance ID {instanceId.Value}";
-            }
-            else if (!string.IsNullOrEmpty(objectPath))
-            {
-                // Will create the GameObject if it doesn't exist
-                targetGameObject = GameObjectHierarchyCreator.FindOrCreateHierarchicalGameObject(objectPath);
-                identifierInfo = $"path '{objectPath}'";
-            }
-            else
-            {
-                // Neither instanceId nor objectPath was provided
-                return McpUnitySocketHandler.CreateErrorResponse("Either 'instanceId' or 'objectPath' must be provided.", "validation_error");
-            }
-
-            // Check if we could not identify or create the GameObject
-            if (targetGameObject == null)
-            {
-                return McpUnitySocketHandler.CreateErrorResponse($"Target GameObject could not be identified or created using {identifierInfo}.", "unknown_error");
-            }
-
-            // Record for undo in Unity Editor
-            Undo.RecordObject(targetGameObject, "Update GameObject Properties");
-            bool propertiesUpdated = false;
-            string originalNameForLog = targetGameObject.name;
-
-            // Update name if provided and different
-            if (!string.IsNullOrEmpty(newName) && targetGameObject.name != newName)
-            {
-                targetGameObject.name = newName;
-                propertiesUpdated = true;
-            }
-
-            // Update tag if provided and different, warn if tag doesn't exist
-            if (!string.IsNullOrEmpty(newTag))
-            {
-                bool tagExists = Array.Exists(UnityEditorInternal.InternalEditorUtility.tags, t => t.Equals(newTag));
-                if (!tagExists)
-                {
-                    McpLogger.LogWarning($"UpdateGameObjectTool: Tag '{newTag}' does not exist for GameObject '{originalNameForLog}'. Tag not changed. Please create the tag in Unity's Tag Manager.");
-                }
-                else if (!targetGameObject.CompareTag(newTag))
-                {
-                    targetGameObject.tag = newTag;
-                    propertiesUpdated = true;
-                }
-            }
-
-            // Update layer if provided and valid
-            if (newLayer.HasValue)
-            {
-                if (newLayer.Value < 0 || newLayer.Value > 31)
-                {
-                    McpLogger.LogWarning($"UpdateGameObjectTool: Invalid layer value {newLayer.Value} for GameObject '{originalNameForLog}'. Layer must be between 0 and 31. Layer not changed.");
-                }
-                else if (targetGameObject.layer != newLayer.Value)
-                {
-                    targetGameObject.layer = newLayer.Value;
-                    propertiesUpdated = true;
-                }
-            }
-
-            // Update active state if provided and different
-            if (newIsActiveSelf.HasValue && targetGameObject.activeSelf != newIsActiveSelf.Value)
-            {
-                targetGameObject.SetActive(newIsActiveSelf.Value);
-                propertiesUpdated = true;
-            }
-
-            // Update static state if provided and different
-            if (newIsStatic.HasValue && targetGameObject.isStatic != newIsStatic.Value)
-            {
-                targetGameObject.isStatic = newIsStatic.Value;
-                propertiesUpdated = true;
-            }
-
-            // Mark as dirty if any property was changed
-            if (propertiesUpdated)
-            {
-                EditorUtility.SetDirty(targetGameObject);
-            }
-
-            // Compose result message and return as JObject (like UpdateComponentTool)
-            return new JObject
-            {
-                ["success"] = true,
-                ["type"] = "text",
-                ["message"] = propertiesUpdated
-                    ? $"GameObject '{targetGameObject.name}' (identified by {identifierInfo}) updated successfully."
-                    : $"No properties were changed for GameObject '{targetGameObject.name}' (identified by {identifierInfo}).",
-                ["instanceId"] = UnityObjectId.GetObjectId(targetGameObject),
-                ["name"] = targetGameObject.name,
-                ["path"] = GetGameObjectPath(targetGameObject)
-            };
-        }
-
-        /// 
-        /// Utility to get the hierarchy path of a GameObject (root/child/.../target)
-        /// 
-        /// The GameObject to get the path for
-        /// Hierarchy path as string
-        private static string GetGameObjectPath(GameObject obj)
-        {
-            if (obj == null) return null;
-            string path = "/" + obj.name;
-            while (obj.transform.parent != null)
-            {
-                obj = obj.transform.parent.gameObject;
-                path = "/" + obj.name + path;
-            }
-            return path;
-        }
-    }
-}
diff --git a/Editor/Tools/UpdateGameObjectTool.cs.meta b/Editor/Tools/UpdateGameObjectTool.cs.meta
deleted file mode 100644
index 8a212615..00000000
--- a/Editor/Tools/UpdateGameObjectTool.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 63e86e1bcdcf1a440a9c171c2d385629
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/UnityBridge.meta b/Editor/UnityBridge.meta
deleted file mode 100644
index 8e6c6da5..00000000
--- a/Editor/UnityBridge.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 141d6bf30581c964d8b9b5ebbc841e16
-folderAsset: yes
-DefaultImporter:
-  externalObjects: {}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/UnityBridge/McpUnityEditorWindow.cs b/Editor/UnityBridge/McpUnityEditorWindow.cs
deleted file mode 100644
index b3a30963..00000000
--- a/Editor/UnityBridge/McpUnityEditorWindow.cs
+++ /dev/null
@@ -1,690 +0,0 @@
-using System;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEditor;
-
-namespace McpUnity.Unity
-{
-    /// 
-    /// Editor window for controlling the MCP Unity Server.
-    /// Provides UI for starting/stopping the server and configuring settings.
-    /// 
-    public class McpUnityEditorWindow : EditorWindow
-    {
-        private GUIStyle _headerStyle;
-        private GUIStyle _subHeaderStyle;
-        private GUIStyle _boxStyle;
-        private GUIStyle _wrappedLabelStyle;
-        private GUIStyle _connectedClientBoxStyle; // Style for individual connected clients
-        private GUIStyle _connectedClientLabelStyle; // Style for labels in connected client boxes
-        private int _selectedTab = 0;
-        private readonly string[] _tabNames = { "Server", "Help" };
-        private bool _isInitialized = false;
-        private string _mcpConfigJson = "";
-        private bool _tabsIndentationJson = false;
-        private bool _useRelativePathJson = false;
-        private Vector2 _helpTabScrollPosition = Vector2.zero;
-        private Vector2 _serverTabScrollPosition = Vector2.zero;
-
-        [MenuItem("Tools/MCP Unity/Server Window", false, 1)]
-        public static void ShowWindow()
-        {
-            var window = GetWindow("MCP Unity");
-            window.minSize = new Vector2(600, 400);
-        }
-
-        private void OnGUI()
-        {
-            InitializeStyles();
-
-            EditorGUILayout.BeginVertical();
-            
-            // Header
-            EditorGUILayout.Space();
-            WrappedLabel("MCP Unity", _headerStyle);
-            EditorGUILayout.Space();
-            
-            // Tabs
-            _selectedTab = GUILayout.Toolbar(_selectedTab, _tabNames);
-            EditorGUILayout.Space();
-            
-            switch (_selectedTab)
-            {
-                case 0: // Server tab
-                    DrawServerTab();
-                    break;
-                case 1: // Help tab
-                    DrawHelpTab();
-                    break;
-            }
-
-            // Version info at the bottom
-            GUILayout.FlexibleSpace();
-            WrappedLabel($"MCP Unity v{McpUnitySettings.ServerVersion}", EditorStyles.miniLabel, GUILayout.Width(150));
-            
-            EditorGUILayout.EndVertical();
-        }
-
-        #region Tab Drawing Methods
-
-        private void DrawServerTab()
-        {
-            _serverTabScrollPosition = EditorGUILayout.BeginScrollView(_serverTabScrollPosition);
-            EditorGUILayout.BeginVertical("box");
-            
-            // Server status
-            EditorGUILayout.BeginHorizontal();
-            EditorGUILayout.LabelField("Status:", GUILayout.Width(120));
-            
-            McpUnitySettings settings = McpUnitySettings.Instance;
-            McpUnityServer mcpUnityServer = McpUnityServer.Instance;
-            bool hasScheduledStart = mcpUnityServer.HasScheduledStart;
-            string statusText = hasScheduledStart ? mcpUnityServer.ScheduledStartStatus : (mcpUnityServer.IsListening ? "Server Online" : "Server Offline");
-            Color statusColor = hasScheduledStart ? Color.yellow : (mcpUnityServer.IsListening ? Color.green : Color.red);
-            
-            GUIStyle statusStyle = new GUIStyle(EditorStyles.boldLabel);
-            statusStyle.normal.textColor = statusColor;
-            
-            EditorGUILayout.LabelField(statusText, statusStyle);
-            EditorGUILayout.EndHorizontal();
-            
-            EditorGUILayout.Space();
-            
-            // Port configuration
-            EditorGUILayout.BeginHorizontal();
-            int newPort = EditorGUILayout.IntField("Connection Port", settings.Port);
-            if (newPort < 1 || newPort > 65536)
-            {
-                newPort = settings.Port;
-                Debug.LogError($"{newPort} is an invalid port number. Please enter a number between 1 and 65535.");
-            }
-            
-            if (newPort != settings.Port)
-            {
-                settings.Port = newPort;
-                settings.SaveSettings();
-                mcpUnityServer.RestartServer();
-            }
-            EditorGUILayout.EndHorizontal();
-            
-            EditorGUILayout.Space();
-            
-            // Test timeout setting
-            EditorGUILayout.BeginHorizontal();
-            int newTimeout = EditorGUILayout.IntField(new GUIContent("Request Timeout (seconds)", "Timeout in seconds for tool request"), settings.RequestTimeoutSeconds);
-            if (newTimeout < McpUnitySettings.RequestTimeoutMinimum)
-            {
-                newTimeout = McpUnitySettings.RequestTimeoutMinimum;
-                Debug.LogError($"Request timeout must be at least {McpUnitySettings.RequestTimeoutMinimum} seconds.");
-            }
-            
-            if (newTimeout != settings.RequestTimeoutSeconds)
-            {
-                settings.RequestTimeoutSeconds = newTimeout;
-                settings.SaveSettings();
-            }
-            EditorGUILayout.EndHorizontal();
-            
-            EditorGUILayout.Space();
-            
-            // Auto start server toggle
-            bool autoStartServer = EditorGUILayout.Toggle(new GUIContent("Auto Start Server", "Automatically starts the MCP server when Unity opens"), settings.AutoStartServer);
-            if (autoStartServer != settings.AutoStartServer)
-            {
-                settings.AutoStartServer = autoStartServer;
-                settings.SaveSettings();
-            }
-            
-            EditorGUILayout.Space();
-            
-            // Allow remote connections toggle
-            bool allowRemoteConnections = EditorGUILayout.Toggle(new GUIContent("Allow Remote Connections", "Allow connections from remote MCP bridges. When disabled, only localhost connections are allowed (default)."), settings.AllowRemoteConnections);
-            if (allowRemoteConnections != settings.AllowRemoteConnections)
-            {
-                settings.AllowRemoteConnections = allowRemoteConnections;
-                settings.SaveSettings();
-                // Restart server to apply binding change
-                mcpUnityServer.RestartServer();
-            }
-            
-            EditorGUILayout.Space();
-            
-            // Enable info logs toggle
-            bool enableInfoLogs = EditorGUILayout.Toggle(new GUIContent("Enable Info Logs", "Show informational logs in the Unity console"), settings.EnableInfoLogs);
-            if (enableInfoLogs != settings.EnableInfoLogs)
-            {
-                settings.EnableInfoLogs = enableInfoLogs;
-                settings.SaveSettings();
-            }
-            
-            EditorGUILayout.Space();
-
-            // Server control buttons
-            EditorGUILayout.BeginHorizontal();
-            
-            // Connect button - enabled only when disconnected
-            GUI.enabled = !mcpUnityServer.IsListening && !hasScheduledStart;
-            if (GUILayout.Button("Start Server", GUILayout.Height(30)))
-            {
-                mcpUnityServer.StartServer();
-            }
-            
-            // Disconnect button - enabled only when connected
-            GUI.enabled = mcpUnityServer.IsListening || hasScheduledStart;
-            if (GUILayout.Button("Stop Server", GUILayout.Height(30)))
-            {
-                mcpUnityServer.StopServer();
-            }
-
-            GUI.enabled = true;
-            if (GUILayout.Button("Restart Server", GUILayout.Height(30)))
-            {
-                mcpUnityServer.RestartServer();
-            }
-            
-            //Repaint();
-            
-            GUI.enabled = true;
-            EditorGUILayout.EndHorizontal();
-
-            EditorGUILayout.Space(); 
-            
-            EditorGUILayout.LabelField("Connected Clients", EditorStyles.boldLabel);
-            EditorGUILayout.BeginVertical("box"); // Keep the default gray box for the container
-
-            var clients = mcpUnityServer.Clients;
-            
-            if (clients.Count > 0)
-            {
-                foreach (var client in clients)
-                {
-                    EditorGUILayout.BeginVertical(_connectedClientBoxStyle); // Use green background for each client
-                    
-                    // Check if we have a meaningful client name (not empty and not the fallback)
-                    string clientName = client.Value;
-                    bool hasMeaningfulName = !string.IsNullOrEmpty(clientName) 
-                        && !clientName.Equals("Unknown MCP Client", StringComparison.OrdinalIgnoreCase);
-                    
-                    if (hasMeaningfulName)
-                    {
-                        // Show name prominently when available
-                        EditorGUILayout.LabelField(clientName, EditorStyles.boldLabel);
-                        EditorGUILayout.LabelField($"ID: {client.Key}", _connectedClientLabelStyle);
-                    }
-                    else
-                    {
-                        // Show just the ID when no meaningful name is available
-                        EditorGUILayout.LabelField($"Client: {client.Key}", EditorStyles.boldLabel);
-                    }
-                    
-                    EditorGUILayout.EndVertical();
-                    EditorGUILayout.Space();
-                }
-            }
-            else
-            {
-                GUIStyle wrapStyle = new GUIStyle(EditorStyles.centeredGreyMiniLabel);
-                wrapStyle.wordWrap = true;
-                GUILayout.Label("No clients connected\nInvoke a tool from the MCP Client to connect", wrapStyle, GUILayout.ExpandWidth(true));
-            }
-                
-            EditorGUILayout.EndVertical();
-
-            // NPM Executable Path
-            string newNpmPath = EditorGUILayout.TextField(new GUIContent("NPM Executable Path", "Optional: Full path to the npm executable (e.g., /Users/user/.asdf/shims/npm or C:\\path\\to\\npm.cmd). If not set, 'npm' from the system PATH will be used."), settings.NpmExecutablePath);
-            if (newNpmPath != settings.NpmExecutablePath)
-            {
-                settings.NpmExecutablePath = newNpmPath;
-                settings.SaveSettings();
-            }
-            
-            EditorGUILayout.Space();
-            
-            // MCP Config generation section
-            EditorGUILayout.Space();
-            EditorGUILayout.LabelField("MCP Configuration", EditorStyles.boldLabel);
-
-            var beforeTabs = _tabsIndentationJson;
-            var beforeRelative = _useRelativePathJson;
-            _tabsIndentationJson = EditorGUILayout.Toggle("Use Tabs indentation", _tabsIndentationJson);
-            _useRelativePathJson = EditorGUILayout.Toggle(
-                new GUIContent(
-                    "Use relative path",
-                    "Emit a path relative to the Unity project root. Use this when pasting into workspace-scoped configs (e.g. .vscode/mcp.json) that are shared via git."),
-                _useRelativePathJson);
-
-            if (string.IsNullOrEmpty(_mcpConfigJson) || beforeTabs != _tabsIndentationJson || beforeRelative != _useRelativePathJson)
-            {
-                var pathMode = _useRelativePathJson ? PathMode.ProjectRelative : PathMode.Absolute;
-                _mcpConfigJson = McpUtils.GenerateMcpConfigJson(_tabsIndentationJson, pathMode);
-            }
-                
-            if (GUILayout.Button("Copy to Clipboard", GUILayout.Height(30)))
-            {
-                EditorGUIUtility.systemCopyBuffer = _mcpConfigJson;
-            }
-            
-            EditorGUILayout.TextArea(_mcpConfigJson, GUILayout.Height(200));
-
-            EditorGUILayout.Space();
-            
-            ShowConfigButton("Windsurf", McpUtils.AddToWindsurfIdeConfig);
-            
-            EditorGUILayout.Space();
-            
-            ShowConfigButton("Claude Desktop", McpUtils.AddToClaudeDesktopConfig);
-            
-            EditorGUILayout.Space();
-            
-            ShowConfigButton("Cursor", McpUtils.AddToCursorConfig);
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton("Cursor (Project)", McpUtils.AddToCursorProjectConfig);
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton("Claude Code", McpUtils.AddToClaudeCodeConfig);
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton("Claude Code (Project)", McpUtils.AddToClaudeCodeProjectConfig);
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton("GitHub Copilot", McpUtils.AddToGitHubCopilotConfig);
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton("Codex CLI", McpUtils.AddToCodexCliConfig);
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton(
-                "Codex CLI (Project)",
-                McpUtils.AddToCodexCliProjectConfig,
-                "Codex only loads this project config after you mark the project as trusted. The first time you run `codex` from this project's root, approve the trust prompt.");
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton("Google Antigravity", McpUtils.AddToAntigravityConfig);
-
-            EditorGUILayout.Space();
-
-            ShowConfigButton("OpenCode", McpUtils.AddToOpenCodeConfig);
-
-            EditorGUILayout.Separator();
-            EditorGUILayout.Separator();
-
-            EditorGUILayout.Space(); 
-
-            // Force Install Server button
-            if (GUILayout.Button("Force Install Server", GUILayout.Height(30)))
-            {
-                McpUnityServer.Instance.InstallServer();
-                McpLogger.LogInfo("MCP Unity Server installed successfully.");
-            }
-            
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.EndScrollView();
-        }
-        
-        private void DrawHelpTab()
-        {
-            // Begin scrollable area
-            _helpTabScrollPosition = EditorGUILayout.BeginScrollView(_helpTabScrollPosition);
-            
-            WrappedLabel("About MCP Unity", _subHeaderStyle);
-            EditorGUILayout.BeginVertical(_boxStyle);
-            WrappedLabel("MCP Unity is a Unity Editor integration of the Model Context Protocol (MCP), which enables standardized communication between AI models and applications.");
-            EditorGUILayout.Space();
-            
-            if (GUILayout.Button("Open MCP Protocol Documentation"))
-            {
-                Application.OpenURL("https://modelcontextprotocol.io");
-            }
-            
-            EditorGUILayout.EndVertical();
-            
-            // IDE Integration settings
-            EditorGUILayout.Space();
-            WrappedLabel("IDE Integration Settings", _subHeaderStyle);
-            
-            EditorGUILayout.BeginVertical(_boxStyle);
-            string ideIntegrationTooltip = "Add the Library/PackedCache folder to VSCode-like IDE workspaces so code can be indexed for the AI to access it. This improves code intelligence for Unity packages in VSCode, Cursor, and similar IDEs.";
-            
-            WrappedLabel("These settings help improve code intelligence in VSCode-like IDEs by adding the Unity Package Cache to your workspace. This is automatically configured when the MCP Unity tool is opened in Unity.");
-            EditorGUILayout.Space();
-            
-            // Add button to manually update workspace
-            if (GUILayout.Button(new GUIContent("Update Workspace Cache Now", ideIntegrationTooltip), GUILayout.Height(24)))
-            {
-                bool updated = VsCodeWorkspaceUtils.AddPackageCacheToWorkspace();
-                if (updated)
-                {
-                    EditorUtility.DisplayDialog("Workspace Updated", "Successfully added Library/PackedCache to workspace files. Please restart your IDE and open the workspace.", "OK");
-                }
-                else
-                {
-                    EditorUtility.DisplayDialog("Workspace Update Failed", "No workspace files were found or needed updating.", "OK");
-                }
-            }
-            
-            EditorGUILayout.EndVertical();
-            
-            EditorGUILayout.Space();
-            WrappedLabel("Available Tools", _subHeaderStyle);
-            
-            EditorGUILayout.BeginVertical(_boxStyle);
-            
-            // execute_menu_item
-            WrappedLabel("execute_menu_item", EditorStyles.boldLabel);
-            WrappedLabel("Executes a function that is currently tagged with MenuItem attribute in the project or in the Unity Editor's menu path");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Execute the menu item 'GameObject/Create Empty' to create a new empty GameObject", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // select_gameobject
-            WrappedLabel("select_gameobject", EditorStyles.boldLabel);
-            WrappedLabel("Selects game objects in the Unity hierarchy by path or instance ID");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Select the Main Camera object in my scene", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // update_gameobject
-            WrappedLabel("update_gameobject", EditorStyles.boldLabel);
-            WrappedLabel("Updates a GameObject's core properties (name, tag, layer, active/static state), or creates the GameObject if it does not exist");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Set the Player object's tag to 'Enemy' and make it inactive", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // update_component
-            WrappedLabel("update_component", EditorStyles.boldLabel);
-            WrappedLabel("Updates component fields on a GameObject or adds it to the GameObject if it does not contain the component");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Add a Rigidbody component to the Player object and set its mass to 5", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // add_package
-            WrappedLabel("add_package", EditorStyles.boldLabel);
-            WrappedLabel("Installs new packages in the Unity Package Manager");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Add the TextMeshPro package to my project", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // run_tests
-            WrappedLabel("run_tests", EditorStyles.boldLabel);
-            WrappedLabel("Runs tests using the Unity Test Runner");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Run all the EditMode tests in my project", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // send_console_log
-            WrappedLabel("send_console_log", EditorStyles.boldLabel);
-            WrappedLabel("Sends console logs to the Unity Editor console");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Send a console log to Unity that the task has been completed", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // add_asset_to_scene
-            WrappedLabel("add_asset_to_scene", EditorStyles.boldLabel);
-            WrappedLabel("Adds an asset from the AssetDatabase to the Unity scene");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Add the Player prefab from my project to the current scene", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            
-            // recompile_scripts
-            WrappedLabel("recompile_scripts", EditorStyles.boldLabel);
-            WrappedLabel("Recompiles all scripts in the Unity project");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Recompile scripts in my project", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            
-            EditorGUILayout.EndVertical();
-            
-            // Available Resources section
-            EditorGUILayout.Space();
-            WrappedLabel("Available Resources", _subHeaderStyle);
-            
-            EditorGUILayout.BeginVertical(_boxStyle);
-            
-            // unity://menu-items
-            WrappedLabel("unity://menu-items", EditorStyles.boldLabel);
-            WrappedLabel("Retrieves a list of all available menu items in the Unity Editor");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Show me all available menu items related to GameObject creation", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // unity://hierarchy
-            WrappedLabel("unity://hierarchy", EditorStyles.boldLabel);
-            WrappedLabel("Retrieves a list of all game objects in the Unity hierarchy");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Show me the current scene hierarchy structure", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // unity://gameobject/{id}
-            WrappedLabel("unity://gameobject/{id}", EditorStyles.boldLabel);
-            WrappedLabel("Retrieves detailed information about a specific GameObject, including all components with serialized properties and fields");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Get me detailed information about the Player GameObject", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // unity://logs
-            WrappedLabel("unity://logs", EditorStyles.boldLabel);
-            WrappedLabel("Retrieves a list of all logs from the Unity console");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Show me the recent error messages from the Unity console", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // unity://packages
-            WrappedLabel("unity://packages", EditorStyles.boldLabel);
-            WrappedLabel("Retrieves information about installed and available packages from the Unity Package Manager");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("List all the packages currently installed in my Unity project", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // unity://assets
-            WrappedLabel("unity://assets", EditorStyles.boldLabel);
-            WrappedLabel("Retrieves information about assets in the Unity Asset Database");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("Find all texture assets in my project", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            // unity://tests/{testMode}
-            WrappedLabel("unity://tests/{testMode}", EditorStyles.boldLabel);
-            WrappedLabel("Retrieves information about tests in the Unity Test Runner");
-            EditorGUILayout.BeginVertical(EditorStyles.helpBox);
-            EditorGUILayout.LabelField("Example prompt:", EditorStyles.miniLabel);
-            WrappedLabel("List all available tests in my Unity project", new GUIStyle(EditorStyles.miniLabel) { fontStyle = FontStyle.Italic });
-            EditorGUILayout.EndVertical();
-            EditorGUILayout.Space();
-            
-            EditorGUILayout.EndVertical();
-            
-            // Author information
-            EditorGUILayout.Space();
-            WrappedLabel("Author", _subHeaderStyle);
-            
-            EditorGUILayout.BeginVertical(_boxStyle);
-            
-            WrappedLabel("Created by CoderGamester", EditorStyles.boldLabel);
-            EditorGUILayout.Space();
-            
-            WrappedLabel("For issues, feedback, or contributions, please visit:");
-            
-            // Begin horizontal layout for buttons
-            EditorGUILayout.BeginHorizontal();
-            
-            if (GUILayout.Button("GitHub: https://github.com/CoderGamester", GUILayout.Height(30)))
-            {
-                Application.OpenURL("https://github.com/CoderGamester");
-            }
-            
-            if (GUILayout.Button("LinkedIn: Miguel Tomás", GUILayout.Height(30)))
-            {
-                Application.OpenURL("https://www.linkedin.com/in/miguel-tomas/");
-            }
-            
-            // End horizontal layout
-            EditorGUILayout.EndHorizontal();
-            
-            EditorGUILayout.EndVertical();
-            
-            // End scrollable area
-            EditorGUILayout.EndScrollView();
-        }
-
-        #endregion
-
-        #region Helper Methods
-
-        private void InitializeStyles()
-        {
-            if (_isInitialized) return;
-            
-            // Window title
-            titleContent = new GUIContent("MCP Unity");
-            
-            // Header style
-            _headerStyle = new GUIStyle(EditorStyles.largeLabel)
-            {
-                fontSize = 20,
-                fontStyle = FontStyle.Bold,
-                alignment = TextAnchor.MiddleCenter,
-                margin = new RectOffset(0, 0, 10, 10)
-            };
-            
-            // Sub-header style
-            _subHeaderStyle = new GUIStyle(EditorStyles.boldLabel)
-            {
-                fontSize = 14,
-                margin = new RectOffset(0, 0, 10, 5)
-            };
-            
-            // Box style
-            _boxStyle = new GUIStyle(EditorStyles.helpBox)
-            {
-                padding = new RectOffset(10, 10, 10, 10),
-                margin = new RectOffset(0, 0, 10, 10)
-            };
-            
-            // Connected client box style with green background
-            _connectedClientBoxStyle = new GUIStyle(EditorStyles.helpBox)
-            {
-                padding = new RectOffset(10, 10, 10, 10),
-                margin = new RectOffset(0, 0, 5, 5)
-            };
-            
-            // Create a light green texture for the background
-            Texture2D greenTexture = new Texture2D(1, 1);
-            greenTexture.SetPixel(0, 0, new Color(0.8f, 1.0f, 0.8f, 1.0f)); // Light green color
-            greenTexture.Apply();
-            
-            _connectedClientBoxStyle.normal.background = greenTexture;
-            
-            // Label style for text in connected client boxes (black text for contrast)
-            _connectedClientLabelStyle = new GUIStyle(EditorStyles.label)
-            {
-                normal = { textColor = Color.black }
-            };
-            
-            // Wrapped label style for text that needs to wrap
-            _wrappedLabelStyle = new GUIStyle(EditorStyles.label)
-            {
-                wordWrap = true,
-                richText = true
-            };
-            
-            _isInitialized = true;
-        }
-        
-        /// 
-        /// Creates a label with text that properly wraps based on available width
-        /// 
-        /// The text to display
-        /// Optional style override (wordWrap will be forced true)
-        /// Layout options
-        private void WrappedLabel(string text, GUIStyle style = null, params GUILayoutOption[] options)
-        {
-            if (style == null)
-            {
-                // Use our predefined wrapped label style
-                EditorGUILayout.LabelField(text, _wrappedLabelStyle, options);
-                return;
-            }
-            
-            // Create a temporary style with wordWrap enabled based on the provided style
-            GUIStyle wrappedStyle = new GUIStyle(style)
-            {
-                wordWrap = true
-            };
-            
-            EditorGUILayout.LabelField(text, wrappedStyle, options);
-        }
-
-        
-            
-        // Helper to show a config button with unified logic
-        private void ShowConfigButton(string configLabel, Func configAction, string successFollowUp = null)
-        {
-            bool isSupported = McpUtils.IsAutoConfigSupported(configLabel);
-
-            using (new EditorGUI.DisabledScope(!isSupported))
-            {
-                if (GUILayout.Button($"Configure {configLabel}", GUILayout.Height(30)))
-                {
-                    bool added = configAction(_tabsIndentationJson);
-                    if (added)
-                    {
-                        string message = $"The MCP configuration was successfully added to the {configLabel} config file.";
-                        if (!string.IsNullOrEmpty(successFollowUp))
-                        {
-                            message += "\n\n" + successFollowUp;
-                        }
-                        EditorUtility.DisplayDialog("Success", message, "OK");
-                    }
-                    else
-                    {
-                        EditorUtility.DisplayDialog("Error", $"The MCP configuration could not be added to the {configLabel} config file.", "OK");
-                    }
-                }
-            }
-
-            if (!isSupported)
-            {
-                WrappedLabel(McpUtils.GetAutoConfigUnsupportedReason(configLabel), EditorStyles.miniLabel);
-            }
-        }
-
-        
-        #endregion
-    }
-}
diff --git a/Editor/UnityBridge/McpUnityEditorWindow.cs.meta b/Editor/UnityBridge/McpUnityEditorWindow.cs.meta
deleted file mode 100644
index 1490c5bf..00000000
--- a/Editor/UnityBridge/McpUnityEditorWindow.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: b37b7af2843115b4592e82f0a4445d47
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/UnityBridge/McpUnityServer.cs b/Editor/UnityBridge/McpUnityServer.cs
deleted file mode 100644
index 499ba475..00000000
--- a/Editor/UnityBridge/McpUnityServer.cs
+++ /dev/null
@@ -1,795 +0,0 @@
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Threading;
-using UnityEditor;
-using UnityEngine;
-using McpUnity.Tools;
-using McpUnity.Resources;
-using McpUnity.Services;
-using McpUnity.Utils;
-using WebSocketSharp.Server;
-using System.IO;
-using System.Net.Sockets;
-using UnityEditor.Callbacks;
-
-namespace McpUnity.Unity
-{
-    /// 
-    /// Custom WebSocket close codes for Unity-specific events.
-    /// Range 4000-4999 is reserved for application use.
-    /// 
-    public static class UnityCloseCode
-    {
-        /// 
-        /// Unity is entering Play mode - clients should use fast polling instead of backoff
-        /// 
-        public const ushort PlayMode = 4001;
-    }
-
-    /// 
-    /// MCP Unity Server to communicate Node.js MCP server.
-    /// Uses WebSockets to communicate with Node.js.
-    /// 
-    [InitializeOnLoad]
-    public class McpUnityServer : IDisposable
-    {
-        private static McpUnityServer _instance;
-
-        private readonly Dictionary _tools = new Dictionary();
-        private readonly Dictionary _resources = new Dictionary();
-
-        private const int DelayedStartMaxAttempts = 10;
-        private static readonly double[] DelayedStartRetryDelaySeconds = { 0.25d, 0.5d, 1d, 2d, 3d, 5d };
-
-        private WebSocketServer _webSocketServer;
-        private CancellationTokenSource _cts;
-        private TestRunnerService _testRunnerService;
-        private ConsoleLogsService _consoleLogsService;
-        private bool _delayedStartScheduled;
-        private bool _delayedStartRequiresAutoStart;
-        private int _delayedStartAttempt;
-        private double _delayedStartEarliestTime;
-        private string _delayedStartReason;
-        private int _connectionGeneration;
-        private int _activeConnectionGeneration;
-
-        private enum StartServerResult
-        {
-            Started,
-            AlreadyListening,
-            Skipped,
-            AddressAlreadyInUse,
-            Failed
-        }
-
-        /// 
-        /// Singleton instance accessor. Returns null in batch mode.
-        /// 
-        public static McpUnityServer Instance
-        {
-            get
-            {
-                // Don't create instance in batch mode to avoid hanging builds
-                if (Application.isBatchMode)
-                {
-                    return null;
-                }
-                
-                if (_instance == null)
-                {
-                    _instance = new McpUnityServer();
-                }
-                return _instance;
-            }
-        }
-
-        /// 
-        /// Current Listening state
-        /// 
-        public bool IsListening => _webSocketServer?.IsListening ?? false;
-
-        /// 
-        /// True when a delayed start or retry is waiting for Unity/editor socket cleanup.
-        /// 
-        public bool HasScheduledStart => _delayedStartScheduled;
-
-        /// 
-        /// Human-readable status for scheduled restart attempts.
-        /// 
-        public string ScheduledStartStatus
-        {
-            get
-            {
-                if (!_delayedStartScheduled)
-                {
-                    return string.Empty;
-                }
-
-                return $"Retrying port {McpUnitySettings.Instance.Port} (attempt {_delayedStartAttempt}/{DelayedStartMaxAttempts})";
-            }
-        }
-
-        /// 
-        /// Thread-safe dictionary of connected clients with this server.
-        /// WebSocketSharp dispatches OnOpen/OnClose on thread pool threads,
-        /// so concurrent access must be safe.
-        /// 
-        public ConcurrentDictionary Clients { get; } = new ConcurrentDictionary();
-
-        /// 
-        /// Disposes the McpUnityServer instance, stopping the WebSocket server and unsubscribing from Unity Editor events.
-        /// This method ensures proper cleanup of resources and prevents memory leaks or unexpected behavior during domain reloads or editor shutdown.
-        /// 
-        public void Dispose()
-        {
-            StopServer();
-
-            EditorApplication.quitting -= OnEditorQuitting;
-            AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
-            AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload;
-            EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
-
-            GC.SuppressFinalize(this);
-        }
-
-        /// 
-        /// Start the WebSocket Server to communicate with Node.js
-        /// 
-        public void StartServer()
-        {
-            CancelScheduledStart();
-            ScheduleStartServer(requireAutoStart: false, reason: "manual start");
-        }
-
-        /// 
-        /// Stop the current server and start it again after Unity has had a chance to release the socket.
-        /// 
-        public void RestartServer()
-        {
-            StopServer();
-            ScheduleStartServer(requireAutoStart: false, reason: "manual restart");
-        }
-
-        /// 
-        /// Stop the WebSocket server
-        /// 
-        /// Optional custom close code to send to clients before stopping
-        /// Optional reason message for the close
-        public void StopServer(ushort? closeCode = null, string closeReason = null)
-        {
-            CancelScheduledStart();
-            _activeConnectionGeneration = 0;
-            McpBackgroundTick.Stop();
-
-            if (_webSocketServer == null)
-            {
-                Clients.Clear();
-                return;
-            }
-
-            try
-            {
-                CloseAllClients(closeCode ?? 1000, closeReason ?? "Server stopping");
-
-                _webSocketServer?.Stop();
-
-                McpLogger.LogInfo("WebSocket server stopped");
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogError($"Error during WebSocketServer.Stop(): {ex.Message}\n{ex.StackTrace}");
-            }
-            finally
-            {
-                _webSocketServer = null;
-                Clients.Clear();
-                McpLogger.LogInfo("WebSocket server stopped and resources cleaned up.");
-            }
-        }
-
-        /// 
-        /// Try to get a tool by name
-        /// 
-        public bool TryGetTool(string name, out McpToolBase tool)
-        {
-            return _tools.TryGetValue(name, out tool);
-        }
-
-        /// 
-        /// Try to get a resource by name
-        /// 
-        public bool TryGetResource(string name, out McpResourceBase resource)
-        {
-            return _resources.TryGetValue(name, out resource);
-        }
-
-        /// 
-        /// Installs the MCP Node.js server by running 'npm install' and 'npm run build'
-        /// in the server directory if 'node_modules' or 'build' folders are missing.
-        /// 
-        public void InstallServer()
-        {
-            string serverPath = McpUtils.GetServerPath();
-
-            if (string.IsNullOrEmpty(serverPath) || !Directory.Exists(serverPath))
-            {
-                McpLogger.LogError($"Server path not found or invalid: {serverPath}. Make sure that MCP Node.js server is installed.");
-                return;
-            }
-
-            // Validate server path and warn about potential issues (spaces, special characters)
-            if (!McpUtils.ValidateServerPath(serverPath))
-            {
-                McpLogger.LogError("Server path validation failed. See previous errors for details.");
-                return;
-            }
-
-            string nodeModulesPath = Path.Combine(serverPath, "node_modules");
-            if (!Directory.Exists(nodeModulesPath))
-            {
-                McpUtils.RunNpmCommand("install", serverPath);
-            }
-
-            string buildPath = Path.Combine(serverPath, "build");
-            if (!Directory.Exists(buildPath))
-            {
-                McpUtils.RunNpmCommand("run build", serverPath);
-            }
-        }
-
-        internal bool ShouldTrackClient(int connectionGeneration)
-        {
-            return connectionGeneration == _activeConnectionGeneration && IsListening;
-        }
-
-        /// 
-        /// Private constructor to enforce singleton pattern
-        /// 
-        private McpUnityServer()
-        {
-            // Skip all initialization in batch mode (Unity Cloud Build, CI, headless builds)
-            // The npm install/build commands can hang indefinitely without node.js available
-            if (Application.isBatchMode)
-            {
-                McpLogger.LogInfo("MCP Unity server disabled: Running in batch mode (Unity Cloud Build or CI)");
-                return;
-            }
-
-            EditorApplication.quitting -= OnEditorQuitting; // Prevent multiple subscriptions on domain reload
-            EditorApplication.quitting += OnEditorQuitting;
-
-            AssemblyReloadEvents.beforeAssemblyReload -= OnBeforeAssemblyReload;
-            AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
-
-            AssemblyReloadEvents.afterAssemblyReload -= OnAfterAssemblyReload;
-            AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
-
-            EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
-            EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
-
-            InstallServer();
-            InitializeServices();
-            RegisterResources();
-            RegisterTools();
-
-            // Initial start if auto-start is enabled and not recovering from a reload where it was off
-            if (McpUnitySettings.Instance.AutoStartServer)
-            {
-                ScheduleStartServer(requireAutoStart: true, reason: "auto-start");
-            }
-        }
-
-        private StartServerResult StartServerInternal(bool logAddressInUseAsError, int attempt = 1, double nextRetryDelaySeconds = 0)
-        {
-            // Skip starting server if this is a Multiplayer Play Mode clone instance
-            // Only the main editor should run the WebSocket server to avoid port conflicts
-            if (McpUtils.IsMultiplayerPlayModeClone())
-            {
-                McpLogger.LogInfo("Server startup skipped: Running as Multiplayer Play Mode clone instance. Only the main editor runs the MCP server.");
-                return StartServerResult.Skipped;
-            }
-
-            if (IsListening)
-            {
-                McpLogger.LogInfo($"Server start requested, but already listening on port {McpUnitySettings.Instance.Port}.");
-                return StartServerResult.AlreadyListening;
-            }
-
-            if (_webSocketServer != null)
-            {
-                StopServer();
-            }
-
-            WebSocketServer webSocketServer = null;
-            try
-            {
-                int connectionGeneration = Interlocked.Increment(ref _connectionGeneration);
-                var host = McpUnitySettings.Instance.AllowRemoteConnections ? "0.0.0.0" : "localhost";
-                webSocketServer = new WebSocketServer($"ws://{host}:{McpUnitySettings.Instance.Port}");
-                webSocketServer.Log.Output = (data, path) => { };
-                webSocketServer.AddWebSocketService("/McpUnity", () => new McpUnitySocketHandler(this, connectionGeneration));
-                webSocketServer.Start();
-                _webSocketServer = webSocketServer;
-                _activeConnectionGeneration = connectionGeneration;
-                McpBackgroundTick.Start();
-                McpLogger.LogInfo($"WebSocket server started successfully on {host}:{McpUnitySettings.Instance.Port}.");
-                return StartServerResult.Started;
-            }
-            catch (SocketException ex) when (ex.SocketErrorCode == SocketError.AddressAlreadyInUse)
-            {
-                CleanupFailedStart(webSocketServer);
-                string message = $"Failed to start WebSocket server: Port {McpUnitySettings.Instance.Port} is already in use. {ex.Message}";
-                if (logAddressInUseAsError)
-                {
-                    McpLogger.LogError(message);
-                }
-                else
-                {
-                    McpLogger.LogWarning($"{message} Attempt {attempt}/{DelayedStartMaxAttempts}; retrying in {nextRetryDelaySeconds:0.##}s.");
-                }
-
-                return StartServerResult.AddressAlreadyInUse;
-            }
-            catch (Exception ex)
-            {
-                CleanupFailedStart(webSocketServer);
-                McpLogger.LogError($"Failed to start WebSocket server: {ex.Message}\n{ex.StackTrace}");
-                return StartServerResult.Failed;
-            }
-        }
-
-        private static double GetDelayedStartDelaySeconds(int attempt)
-        {
-            int normalizedAttempt = Math.Max(attempt, 1);
-            int delayIndex = Math.Min(normalizedAttempt - 1, DelayedStartRetryDelaySeconds.Length - 1);
-            return DelayedStartRetryDelaySeconds[delayIndex];
-        }
-
-        private void ScheduleStartServer(bool requireAutoStart, string reason, int attempt = 1)
-        {
-            int normalizedAttempt = Math.Min(Math.Max(attempt, 1), DelayedStartMaxAttempts);
-            if (_delayedStartScheduled)
-            {
-                _delayedStartRequiresAutoStart = _delayedStartRequiresAutoStart && requireAutoStart;
-                _delayedStartAttempt = Math.Max(_delayedStartAttempt, normalizedAttempt);
-                _delayedStartReason = reason;
-                return;
-            }
-
-            _delayedStartScheduled = true;
-            _delayedStartRequiresAutoStart = requireAutoStart;
-            _delayedStartAttempt = normalizedAttempt;
-            _delayedStartReason = reason;
-            double delaySeconds = GetDelayedStartDelaySeconds(normalizedAttempt);
-            _delayedStartEarliestTime = EditorApplication.timeSinceStartup + delaySeconds;
-            McpLogger.LogInfo($"WebSocket server start scheduled in {delaySeconds:0.##}s ({reason}, attempt {normalizedAttempt}/{DelayedStartMaxAttempts}).");
-            EditorApplication.delayCall += StartServerAfterDelay;
-            EditorApplication.update += StartServerAfterDelayOnUpdate;
-        }
-
-        private void CancelScheduledStart()
-        {
-            if (!_delayedStartScheduled)
-            {
-                return;
-            }
-
-            EditorApplication.delayCall -= StartServerAfterDelay;
-            EditorApplication.update -= StartServerAfterDelayOnUpdate;
-            _delayedStartScheduled = false;
-            _delayedStartRequiresAutoStart = false;
-            _delayedStartAttempt = 0;
-            _delayedStartEarliestTime = 0;
-            _delayedStartReason = null;
-        }
-
-        private void StartServerAfterDelay()
-        {
-            if (!_delayedStartScheduled)
-            {
-                return;
-            }
-
-            if (EditorApplication.timeSinceStartup < _delayedStartEarliestTime)
-            {
-                EditorApplication.delayCall += StartServerAfterDelay;
-                return;
-            }
-
-            RunScheduledStart();
-        }
-
-        private void StartServerAfterDelayOnUpdate()
-        {
-            if (!_delayedStartScheduled)
-            {
-                EditorApplication.update -= StartServerAfterDelayOnUpdate;
-                return;
-            }
-
-            if (EditorApplication.timeSinceStartup < _delayedStartEarliestTime)
-            {
-                return;
-            }
-
-            RunScheduledStart();
-        }
-
-        private void RunScheduledStart()
-        {
-            _delayedStartScheduled = false;
-            EditorApplication.delayCall -= StartServerAfterDelay;
-            EditorApplication.update -= StartServerAfterDelayOnUpdate;
-
-            bool requireAutoStart = _delayedStartRequiresAutoStart;
-            int attempt = Math.Min(Math.Max(_delayedStartAttempt, 1), DelayedStartMaxAttempts);
-            string reason = _delayedStartReason;
-            _delayedStartRequiresAutoStart = false;
-            _delayedStartAttempt = 0;
-            _delayedStartEarliestTime = 0;
-            _delayedStartReason = null;
-
-            if (Application.isBatchMode || _instance != this)
-            {
-                return;
-            }
-
-            if (requireAutoStart && !McpUnitySettings.Instance.AutoStartServer)
-            {
-                McpLogger.LogInfo("Scheduled WebSocket server start skipped because auto-start is disabled.");
-                return;
-            }
-
-            if (IsListening)
-            {
-                return;
-            }
-
-            bool isFinalAttempt = attempt >= DelayedStartMaxAttempts;
-            double nextRetryDelaySeconds = isFinalAttempt ? 0 : GetDelayedStartDelaySeconds(attempt + 1);
-            StartServerResult result = StartServerInternal(isFinalAttempt, attempt, nextRetryDelaySeconds);
-            if (result == StartServerResult.AddressAlreadyInUse && !isFinalAttempt)
-            {
-                ScheduleStartServer(requireAutoStart, reason ?? "port still in use", attempt + 1);
-            }
-        }
-
-        private void CleanupFailedStart(WebSocketServer webSocketServer)
-        {
-            McpBackgroundTick.Stop();
-
-            if (webSocketServer == null)
-            {
-                Clients.Clear();
-                return;
-            }
-
-            try
-            {
-                if (webSocketServer.IsListening)
-                {
-                    webSocketServer.Stop();
-                }
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogWarning($"Error cleaning up failed WebSocket server start: {ex.Message}");
-            }
-            finally
-            {
-                if (ReferenceEquals(_webSocketServer, webSocketServer))
-                {
-                    _webSocketServer = null;
-                }
-
-                Clients.Clear();
-            }
-        }
-
-        /// 
-        /// Close all connected clients with a specific close code
-        /// 
-        /// WebSocket close code
-        /// Reason message for the close
-        private void CloseAllClients(ushort closeCode, string reason)
-        {
-            if (_webSocketServer == null)
-            {
-                return;
-            }
-
-            try
-            {
-                var service = _webSocketServer.WebSocketServices["/McpUnity"];
-                if (service?.Sessions != null)
-                {
-                    // Get all active session IDs and close each with the custom code
-                    var sessionIds = new List(service.Sessions.IDs);
-                    foreach (var sessionId in sessionIds)
-                    {
-                        service.Sessions.CloseSession(sessionId, closeCode, reason);
-                    }
-                    McpLogger.LogInfo($"Closed {sessionIds.Count} client connection(s) with code {closeCode}: {reason}");
-                }
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogError($"Error closing client connections: {ex.Message}");
-            }
-        }
-        
-        /// 
-        /// Register all available tools
-        /// 
-        private void RegisterTools()
-        {
-            // Register MenuItemTool
-            MenuItemTool menuItemTool = new MenuItemTool();
-            _tools.Add(menuItemTool.Name, menuItemTool);
-
-            // Register SelectGameObjectTool
-            SelectGameObjectTool selectGameObjectTool = new SelectGameObjectTool();
-            _tools.Add(selectGameObjectTool.Name, selectGameObjectTool);
-
-            // Register UpdateGameObjectTool
-            UpdateGameObjectTool updateGameObjectTool = new UpdateGameObjectTool();
-            _tools.Add(updateGameObjectTool.Name, updateGameObjectTool);
-            
-            // Register PackageManagerTool
-            AddPackageTool addPackageTool = new AddPackageTool();
-            _tools.Add(addPackageTool.Name, addPackageTool);
-            
-            // Register RunTestsTool
-            RunTestsTool runTestsTool = new RunTestsTool(_testRunnerService);
-            _tools.Add(runTestsTool.Name, runTestsTool);
-            
-            // Register SendConsoleLogTool
-            SendConsoleLogTool sendConsoleLogTool = new SendConsoleLogTool();
-            _tools.Add(sendConsoleLogTool.Name, sendConsoleLogTool);
-            
-            // Register GetConsoleLogsTool
-            GetConsoleLogsTool getConsoleLogsTool = new GetConsoleLogsTool(_consoleLogsService);
-            _tools.Add(getConsoleLogsTool.Name, getConsoleLogsTool);
-            
-            // Register UpdateComponentTool
-            UpdateComponentTool updateComponentTool = new UpdateComponentTool();
-            _tools.Add(updateComponentTool.Name, updateComponentTool);
-            
-            // Register AddAssetToSceneTool
-            AddAssetToSceneTool addAssetToSceneTool = new AddAssetToSceneTool();
-            _tools.Add(addAssetToSceneTool.Name, addAssetToSceneTool);
-            
-            // Register CreatePrefabTool
-            CreatePrefabTool createPrefabTool = new CreatePrefabTool();
-            _tools.Add(createPrefabTool.Name, createPrefabTool);
-
-            // Register CreateSceneTool
-            CreateSceneTool createSceneTool = new CreateSceneTool();
-            _tools.Add(createSceneTool.Name, createSceneTool);
-
-            // Register DeleteSceneTool
-            DeleteSceneTool deleteSceneTool = new DeleteSceneTool();
-            _tools.Add(deleteSceneTool.Name, deleteSceneTool);
-
-            // Register LoadSceneTool
-            LoadSceneTool loadSceneTool = new LoadSceneTool();
-            _tools.Add(loadSceneTool.Name, loadSceneTool);
-
-            // Register SaveSceneTool
-            SaveSceneTool saveSceneTool = new SaveSceneTool();
-            _tools.Add(saveSceneTool.Name, saveSceneTool);
-
-            // Register GetSceneInfoTool
-            GetSceneInfoTool getSceneInfoTool = new GetSceneInfoTool();
-            _tools.Add(getSceneInfoTool.Name, getSceneInfoTool);
-
-            // Register GetPlayModeStatusTool
-            GetPlayModeStatusTool getPlayModeStatusTool = new GetPlayModeStatusTool();
-            _tools.Add(getPlayModeStatusTool.Name, getPlayModeStatusTool);
-
-            // Register SetPlayModeStatusTool
-            SetPlayModeStatusTool setPlayModeStatusTool = new SetPlayModeStatusTool();
-            _tools.Add(setPlayModeStatusTool.Name, setPlayModeStatusTool);
-
-            // Register UnloadSceneTool
-            UnloadSceneTool unloadSceneTool = new UnloadSceneTool();
-            _tools.Add(unloadSceneTool.Name, unloadSceneTool);
-
-            // Register RecompileScriptsTool
-            RecompileScriptsTool recompileScriptsTool = new RecompileScriptsTool();
-            _tools.Add(recompileScriptsTool.Name, recompileScriptsTool);
-            
-            // Register GetGameObjectTool
-            GetGameObjectTool getGameObjectTool = new GetGameObjectTool();
-            _tools.Add(getGameObjectTool.Name, getGameObjectTool);
-
-            // Register DuplicateGameObjectTool
-            DuplicateGameObjectTool duplicateGameObjectTool = new DuplicateGameObjectTool();
-            _tools.Add(duplicateGameObjectTool.Name, duplicateGameObjectTool);
-
-            // Register DeleteGameObjectTool
-            DeleteGameObjectTool deleteGameObjectTool = new DeleteGameObjectTool();
-            _tools.Add(deleteGameObjectTool.Name, deleteGameObjectTool);
-
-            // Register ReparentGameObjectTool
-            ReparentGameObjectTool reparentGameObjectTool = new ReparentGameObjectTool();
-            _tools.Add(reparentGameObjectTool.Name, reparentGameObjectTool);
-
-            // Register Transform Tools
-            MoveGameObjectTool moveGameObjectTool = new MoveGameObjectTool();
-            _tools.Add(moveGameObjectTool.Name, moveGameObjectTool);
-
-            RotateGameObjectTool rotateGameObjectTool = new RotateGameObjectTool();
-            _tools.Add(rotateGameObjectTool.Name, rotateGameObjectTool);
-
-            ScaleGameObjectTool scaleGameObjectTool = new ScaleGameObjectTool();
-            _tools.Add(scaleGameObjectTool.Name, scaleGameObjectTool);
-
-            SetTransformTool setTransformTool = new SetTransformTool();
-            _tools.Add(setTransformTool.Name, setTransformTool);
-
-            // Register Material Tools
-            CreateMaterialTool createMaterialTool = new CreateMaterialTool();
-            _tools.Add(createMaterialTool.Name, createMaterialTool);
-
-            AssignMaterialTool assignMaterialTool = new AssignMaterialTool();
-            _tools.Add(assignMaterialTool.Name, assignMaterialTool);
-
-            ModifyMaterialTool modifyMaterialTool = new ModifyMaterialTool();
-            _tools.Add(modifyMaterialTool.Name, modifyMaterialTool);
-
-            GetMaterialInfoTool getMaterialInfoTool = new GetMaterialInfoTool();
-            _tools.Add(getMaterialInfoTool.Name, getMaterialInfoTool);
-
-            // Register BatchExecuteTool (must be registered last as it needs access to other tools)
-            BatchExecuteTool batchExecuteTool = new BatchExecuteTool(this);
-            _tools.Add(batchExecuteTool.Name, batchExecuteTool);
-        }
-        
-        /// 
-        /// Register all available resources
-        /// 
-        private void RegisterResources()
-        {
-            // Register GetMenuItemsResource
-            GetMenuItemsResource getMenuItemsResource = new GetMenuItemsResource();
-            _resources.Add(getMenuItemsResource.Name, getMenuItemsResource);
-            
-            // Register GetConsoleLogsResource
-            GetConsoleLogsResource getConsoleLogsResource = new GetConsoleLogsResource(_consoleLogsService);
-            _resources.Add(getConsoleLogsResource.Name, getConsoleLogsResource);
-            
-            // Register GetScenesHierarchyResource
-            GetScenesHierarchyResource getScenesHierarchyResource = new GetScenesHierarchyResource();
-            _resources.Add(getScenesHierarchyResource.Name, getScenesHierarchyResource);
-            
-            // Register GetPackagesResource
-            GetPackagesResource getPackagesResource = new GetPackagesResource();
-            _resources.Add(getPackagesResource.Name, getPackagesResource);
-            
-            // Register GetAssetsResource
-            GetAssetsResource getAssetsResource = new GetAssetsResource();
-            _resources.Add(getAssetsResource.Name, getAssetsResource);
-            
-            // Register GetTestsResource
-            GetTestsResource getTestsResource = new GetTestsResource(_testRunnerService);
-            _resources.Add(getTestsResource.Name, getTestsResource);
-            
-            // Register GetGameObjectResource
-            GetGameObjectResource getGameObjectResource = new GetGameObjectResource();
-            _resources.Add(getGameObjectResource.Name, getGameObjectResource);
-        }
-        
-        /// 
-        /// Initialize services used by the server
-        /// 
-        private void InitializeServices()
-        {
-            // Initialize the test runner service
-            _testRunnerService = new TestRunnerService();
-            
-            // Initialize the console logs service
-            _consoleLogsService = new ConsoleLogsService();
-        }
-
-        /// 
-        /// Called after every domain reload
-        /// 
-        [DidReloadScripts]
-        private static void AfterReload()
-        {
-            // Skip initialization in batch mode (Unity Cloud Build, CI, headless builds)
-            // This prevents npm commands from hanging the build process
-            if (Application.isBatchMode)
-            {
-                return;
-            }
-
-            // Ensure Instance is created and hooks are set up after initial domain load
-            var currentInstance = Instance;
-        }
-
-        /// 
-        /// Handles the Unity Editor quitting event. Ensures the server is properly stopped and disposed.
-        /// 
-        private static void OnEditorQuitting()
-        {
-            if (Application.isBatchMode || _instance == null) return;
-            
-            McpLogger.LogInfo("Editor is quitting. Ensuring server is stopped.");
-            _instance.Dispose();
-        }
-
-        /// 
-        /// Handles the Unity Editor's 'before assembly reload' event.
-        /// Stops the WebSocket server to prevent port conflicts and ensure a clean state before scripts are recompiled.
-        /// 
-        private static void OnBeforeAssemblyReload()
-        {
-            if (Application.isBatchMode || _instance == null) return;
-            
-            _instance.StopServer();
-        }
-
-        /// 
-        /// Handles the Unity Editor's 'after assembly reload' event.
-        /// If auto-start is enabled, attempts to restart the WebSocket server if it's not already listening.
-        /// This ensures the server is operational after script recompilation.
-        /// 
-        private static void OnAfterAssemblyReload()
-        {
-            if (Application.isBatchMode || _instance == null) return;
-            
-            if (McpUnitySettings.Instance.AutoStartServer && !_instance.IsListening)
-            {
-                _instance.ScheduleStartServer(requireAutoStart: true, reason: "assembly reload");
-            }
-        }
-
-        /// 
-        /// Handles changes in Unity Editor's play mode state.
-        /// Stops the server when exiting Edit Mode if configured, and restarts it when entering Play Mode or returning to Edit Mode if auto-start is enabled.
-        /// 
-        /// The current play mode state change.
-        private static void OnPlayModeStateChanged(PlayModeStateChange state)
-        {
-            if (Application.isBatchMode || _instance == null) return;
-            
-            switch (state)
-            {
-                case PlayModeStateChange.ExitingEditMode:
-                    // About to enter Play Mode - use custom close code so clients use fast polling
-                    _instance.StopServer(UnityCloseCode.PlayMode, "Unity entering Play mode");
-                    break;
-                case PlayModeStateChange.EnteredPlayMode:
-                    // The assumption above (server stays down through Play, a domain reload on exit
-                    // will restart it) only holds when Enter Play Mode Options are OFF or don't disable
-                    // domain reload. With "Reload Scene without Reload Domain" -- Project Settings >
-                    // Editor > Enter Play Mode Options, a real, supported Unity profile some projects
-                    // pick specifically for iteration speed -- NO domain reload happens on either side
-                    // of Play Mode, so nothing was ever going to bring the server back. That leaves it
-                    // down for the entire Play session, with no way for a client to even ask Unity to
-                    // exit Play, since that request needs this same server. Confirmed live: a client
-                    // got locked in Play with no way out until the Editor was closed by hand.
-                    // Restarting here covers both profiles: if a domain reload already restarted it via
-                    // OnAfterAssemblyReload, IsListening is already true and this is a no-op; if it
-                    // didn't, this is the only thing that brings it back.
-                    if (!_instance.IsListening && McpUnitySettings.Instance.AutoStartServer)
-                    {
-                        _instance.ScheduleStartServer(requireAutoStart: true, reason: "entered play mode");
-                    }
-                    break;
-                case PlayModeStateChange.ExitingPlayMode:
-                    break;
-                case PlayModeStateChange.EnteredEditMode:
-                    // Returned to Edit Mode
-                    if (!_instance.IsListening && McpUnitySettings.Instance.AutoStartServer)
-                    {
-                        _instance.ScheduleStartServer(requireAutoStart: true, reason: "entered edit mode");
-                    }
-                    break;
-            }
-        }
-    }
-}
diff --git a/Editor/UnityBridge/McpUnityServer.cs.meta b/Editor/UnityBridge/McpUnityServer.cs.meta
deleted file mode 100644
index 48ce2f1a..00000000
--- a/Editor/UnityBridge/McpUnityServer.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: ca1c774925b38a149b67fa9318aba113
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/UnityBridge/McpUnitySettings.cs b/Editor/UnityBridge/McpUnitySettings.cs
deleted file mode 100644
index 2223431e..00000000
--- a/Editor/UnityBridge/McpUnitySettings.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-using System;
-using System.IO;
-using McpUnity.Utils;
-using UnityEngine;
-using UnityEditor;
-
-namespace McpUnity.Unity
-{
-    /// 
-    /// Handles persistence of MCP Unity settings
-    /// 
-    [Serializable]
-    public class McpUnitySettings
-    {
-        // Constants
-        public const string ServerVersion = "1.2.0";
-        public const string PackageName = "com.gamelovers.mcp-unity";
-        public const int RequestTimeoutMinimum = 10;
-        
-        // Paths
-        private const string SettingsPath = "ProjectSettings/McpUnitySettings.json";
-        
-        private static McpUnitySettings _instance;
-
-        [Tooltip("Port number for MCP server")]
-        public int Port = 8090;
-        
-        [Tooltip("Timeout in seconds for tool request")]
-        public int RequestTimeoutSeconds = RequestTimeoutMinimum;
-        
-        [Tooltip("Whether to automatically start the MCP server when Unity opens")]
-        public bool AutoStartServer = true;
-        
-        [Tooltip("Whether to show info logs in the Unity console")]
-        public bool EnableInfoLogs = false;
-
-        [Tooltip("Optional: Full path to the npm executable (e.g., /Users/user/.asdf/shims/npm or C:\\path\\to\\npm.cmd). If not set, 'npm' from the system PATH will be used.")]
-        public string NpmExecutablePath = string.Empty;
-        
-        [Tooltip("Allow connections from remote MCP bridges. When disabled, only localhost connections are allowed (default).")]
-        public bool AllowRemoteConnections = false;
-
-        /// 
-        /// Singleton instance of settings
-        /// 
-        public static McpUnitySettings Instance
-        {
-            get
-            {
-                if (_instance == null)
-                {
-                    _instance = new McpUnitySettings();
-                }
-                return _instance;
-            }
-        }
-
-        /// 
-        /// Private constructor for singleton
-        /// 
-        private McpUnitySettings() 
-        { 
-            LoadSettings();
-        }
-
-        /// 
-        /// Load settings from disk
-        /// 
-        public void LoadSettings()
-        {
-            try
-            {
-                // Load settings from McpUnitySettings.json
-                if (File.Exists(SettingsPath))
-                {
-                    string json = File.ReadAllText(SettingsPath);
-                    JsonUtility.FromJsonOverwrite(json, this);
-                }
-                else
-                {
-                    // Create default settings file on the first time initialization
-                    SaveSettings();
-                }
-            }
-            catch (Exception ex)
-            {
-                // Can't use LoggerService here as it depends on settings
-                Debug.LogError($"[MCP Unity] Failed to load settings: {ex.Message}");
-            }
-        }
-
-        /// 
-        /// Save settings to disk
-        /// 
-        /// 
-        /// WARNING: This file is also read by the MCP server. Changes here will require updates to it. See mcpUnity.ts
-        /// 
-        public void SaveSettings()
-        {
-            try
-            {
-                // Save settings to McpUnitySettings.json
-                string json = JsonUtility.ToJson(this, true);
-                File.WriteAllText(SettingsPath, json);
-            }
-            catch (Exception ex)
-            {
-                // Can't use LoggerService here as it might create circular dependency
-                Debug.LogError($"[MCP Unity] Failed to save settings: {ex.Message}");
-            }
-        }
-    }
-}
diff --git a/Editor/UnityBridge/McpUnitySettings.cs.meta b/Editor/UnityBridge/McpUnitySettings.cs.meta
deleted file mode 100644
index f4a1774f..00000000
--- a/Editor/UnityBridge/McpUnitySettings.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 36f9e0452348eb44cad23688666e32c3
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/UnityBridge/McpUnitySocketHandler.cs b/Editor/UnityBridge/McpUnitySocketHandler.cs
deleted file mode 100644
index e0c90612..00000000
--- a/Editor/UnityBridge/McpUnitySocketHandler.cs
+++ /dev/null
@@ -1,376 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using WebSocketSharp;
-using WebSocketSharp.Server;
-using McpUnity.Tools;
-using McpUnity.Resources;
-using Unity.EditorCoroutines.Editor;
-using System.Collections;
-using System.Collections.Specialized;
-using System.Collections.Concurrent;
-using McpUnity.Utils;
-
-namespace McpUnity.Unity
-{
-    /// 
-    /// Drains work queued from background WebSocket threads on the Unity main thread via
-    /// EditorApplication.update, which keeps firing even when the Editor is unfocused.
-    ///
-    /// This replaces dispatching through EditorApplication.delayCall: delayCall is a plain
-    /// static delegate, and a "+=" performed from the WebSocketSharp background thread is not
-    /// reliably observed/drained by the main thread while the Editor is idle in the
-    /// background. The result was that requests received while Unity was not the foreground
-    /// app were never processed, and the MCP client timed out. Draining a thread-safe queue
-    /// from EditorApplication.update fixes this without relying on cross-thread delegate
-    /// mutation.
-    /// 
-    [InitializeOnLoad]
-    internal static class McpMainThreadDispatcher
-    {
-        private static readonly ConcurrentQueue _queue = new ConcurrentQueue();
-
-        static McpMainThreadDispatcher()
-        {
-            EditorApplication.update -= Drain;
-            EditorApplication.update += Drain;
-        }
-
-        public static void Enqueue(Action action)
-        {
-            if (action != null) _queue.Enqueue(action);
-        }
-
-        private static void Drain()
-        {
-            while (_queue.TryDequeue(out var action))
-            {
-                try { action(); }
-                catch (Exception ex) { McpLogger.LogError($"MainThreadDispatcher action failed: {ex}"); }
-            }
-        }
-    }
-
-    /// 
-    /// WebSocket handler for MCP Unity communications
-    /// 
-    public class McpUnitySocketHandler : WebSocketBehavior
-    {
-        private readonly McpUnityServer _server;
-        private readonly int _connectionGeneration;
-
-        /// 
-        /// Creates a WebSocket handler for the active server generation.
-        /// 
-        public McpUnitySocketHandler(McpUnityServer server, int connectionGeneration)
-        {
-            _server = server;
-            _connectionGeneration = connectionGeneration;
-        }
-
-        /// 
-        /// Create a standardized error response
-        /// 
-        /// Error message
-        /// Type of error
-        /// A JObject containing the error information
-        public static JObject CreateErrorResponse(string message, string errorType)
-        {
-            return new JObject
-            {
-                ["error"] = new JObject
-                {
-                    ["type"] = errorType,
-                    ["message"] = message
-                }
-            };
-        }
-        
-        /// 
-        /// Handle incoming messages from WebSocket clients.
-        /// WebSocketSharp invokes this on a background thread; we marshal the entire
-        /// message-handling body onto Unity's main thread via EditorApplication.delayCall
-        /// before touching any Editor APIs.
-        ///
-        /// Why this matters: accessing EditorStyles or scheduling EditorCoroutines from
-        /// a background thread can NRE inside PropertyEditor+Styles..cctor, which under
-        /// CLR rules permanently bricks that type for the rest of the AppDomain and
-        /// turns the Inspector black until Unity is restarted.
-        /// 
-        protected override void OnMessage(MessageEventArgs e)
-        {
-            if (!_server.ShouldTrackClient(_connectionGeneration))
-            {
-                CloseUntrackedConnection();
-                return;
-            }
-
-            string data = e.Data;
-            // Dispatch via a thread-safe queue drained in EditorApplication.update rather than
-            // EditorApplication.delayCall. A delayCall "+=" from this background thread is not
-            // reliably drained by the main thread while the Editor is unfocused/idle, so the
-            // request would never run and the client would time out. See McpMainThreadDispatcher.
-            McpMainThreadDispatcher.Enqueue(() => HandleMessageAsync(data));
-        }
-
-        /// 
-        /// Handle WebSocket connection open.
-        /// Supports multiple concurrent MCP clients (e.g. multiple Claude Code instances).
-        /// Cleans up only inactive (dead) sessions to prevent file descriptor accumulation
-        /// while keeping other active clients connected.
-        /// websocket-sharp uses Mono's IOSelector/select(), which can crash when FD
-        /// values exceed ~1024, so stale session cleanup is important.
-        /// See: https://github.com/CoderGamester/mcp-unity/issues/110
-        /// 
-        protected override void OnOpen()
-        {
-            if (!_server.ShouldTrackClient(_connectionGeneration))
-            {
-                CloseUntrackedConnection();
-                return;
-            }
-
-            // Clean up inactive (dead) sessions to prevent file descriptor accumulation.
-            // Only removes sessions that are no longer connected — active clients are preserved.
-            // Note: Do NOT use ActiveIDs here — it pings every client and blocks.
-            var inactiveIds = Sessions.InactiveIDs.ToList();
-            if (inactiveIds.Count > 0)
-            {
-                foreach (var oldId in inactiveIds)
-                {
-                    // Also remove from our tracking dictionary
-                    _server.Clients.TryRemove(oldId, out _);
-                    try
-                    {
-                        Sessions.CloseSession(oldId, CloseStatusCode.Normal, "Stale session cleanup");
-                    }
-                    catch (Exception ex)
-                    {
-                        McpLogger.LogWarning($"Error closing stale session {oldId}: {ex.Message}");
-                    }
-                }
-                McpLogger.LogInfo($"Cleaned up {inactiveIds.Count} inactive session(s)");
-            }
-
-            // Extract client name from the X-Client-Name header (if available)
-            string clientName = "";
-            NameValueCollection headers = Context.Headers;
-            if (headers != null && headers.Contains("X-Client-Name"))
-            {
-                clientName = headers["X-Client-Name"];
-            }
-
-            if (!_server.ShouldTrackClient(_connectionGeneration))
-            {
-                CloseUntrackedConnection();
-                return;
-            }
-
-            // Add the client to the server's tracking dictionary
-            _server.Clients[ID] = clientName;
-
-            McpLogger.LogInfo($"WebSocket client connected (ID: {ID}, Name: {(string.IsNullOrEmpty(clientName) ? "Unknown" : clientName)}, Total clients: {_server.Clients.Count})");
-        }
-
-        /// 
-        /// Handle WebSocket connection close
-        /// 
-        protected override void OnClose(CloseEventArgs e)
-        {
-            _server.Clients.TryGetValue(ID, out string clientName);
-
-            // Remove the client from the server
-            _server.Clients.TryRemove(ID, out _);
-
-            string reason = e.Reason;
-            if (reason == "An exception has occurred while receiving.")
-            {
-                reason = "connection closed by client";
-            }
-
-            McpLogger.LogInfo($"WebSocket client '{clientName}' disconnected: {reason} (Remaining clients: {_server.Clients.Count})");
-        }
-
-        /// 
-        /// Handle WebSocket errors
-        /// 
-        protected override void OnError(ErrorEventArgs e)
-        {
-            McpLogger.LogError($"WebSocket error: {e.Message}");
-        }
-
-        /// 
-        /// Process a WebSocket message on the Unity main thread.
-        /// Safe to call EditorCoroutineUtility, Selection, and other Editor APIs from here.
-        /// 
-        private async void HandleMessageAsync(string data)
-        {
-            try
-            {
-                if (!_server.ShouldTrackClient(_connectionGeneration))
-                {
-                    CloseUntrackedConnection();
-                    return;
-                }
-
-                McpLogger.LogInfo($"WebSocket message received: {data}");
-                JObject requestJson;
-                try
-                {
-                    requestJson = JObject.Parse(data);
-                }
-                catch (JsonReaderException jre)
-                {
-                    McpLogger.LogError($"Invalid JSON received: {jre.Message}. Data: {data}");
-                    // Attempt to send a parse error response. No requestId is available yet.
-                    Send(CreateResponse(null, CreateErrorResponse($"Invalid JSON format: {jre.Message}", "invalid_json")).ToString(Formatting.None));
-                    return;
-                }
-
-                var method = requestJson["method"]?.ToString();
-                var parameters = requestJson["params"] as JObject ?? new JObject();
-                var requestId = requestJson["id"]?.ToString();
-                // We need to dispatch to Unity's main thread and wait for completion
-                var tcs = new TaskCompletionSource();
-
-                if (string.IsNullOrEmpty(method))
-                {
-                    tcs.SetResult(CreateErrorResponse("Missing method in request", "invalid_request"));
-                }
-                else if (_server.TryGetTool(method, out var tool))
-                {
-                    EditorCoroutineUtility.StartCoroutineOwnerless(ExecuteTool(tool, parameters, tcs));
-                }
-                else if (_server.TryGetResource(method, out var resource))
-                {
-                    EditorCoroutineUtility.StartCoroutineOwnerless(FetchResourceCoroutine(resource, parameters, tcs));
-                }
-                else
-                {
-                    tcs.SetResult(CreateErrorResponse($"Unknown method: {method}", "unknown_method"));
-                }
-
-                JObject responseJson = await tcs.Task;
-                JObject jsonRpcResponse = CreateResponse(requestId, responseJson);
-                string responseStr = jsonRpcResponse.ToString(Formatting.None);
-
-                McpLogger.LogInfo($"WebSocket message response for request ID '{requestId}': {responseStr}");
-
-                // Send the response back to the client
-                Send(responseStr);
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogError($"Error processing message: {ex.Message}");
-
-                Send(CreateErrorResponse($"Internal server error: {ex.Message}", "internal_error").ToString(Formatting.None));
-            }
-        }
-
-        private void CloseUntrackedConnection()
-        {
-            try
-            {
-                WebSocket webSocket = Context?.WebSocket;
-                if (webSocket?.ReadyState == WebSocketState.Open)
-                {
-                    webSocket.Close(CloseStatusCode.Away, "Server is restarting");
-                }
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogWarning($"Error closing untracked WebSocket connection: {ex.Message}");
-            }
-        }
-        
-        /// 
-        /// Execute a tool with the provided parameters
-        /// 
-        private IEnumerator ExecuteTool(McpToolBase tool, JObject parameters, TaskCompletionSource tcs)
-        {
-            try
-            {
-                if (tool.IsAsync)
-                {
-                    tool.ExecuteAsync(parameters, tcs);
-                }
-                else
-                {
-                    var result = tool.Execute(parameters);
-                    tcs.SetResult(result);
-                }
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogError($"Error executing tool {tool.Name}: {ex.Message}\n{ex.StackTrace}");
-                tcs.SetResult(CreateErrorResponse(
-                    $"Failed to execute tool {tool.Name}: {ex.Message}",
-                    "tool_execution_error"
-                ));
-            }
-            
-            yield return null;
-        }
-        
-        /// 
-        /// Fetch a resource with the provided parameters
-        /// 
-        private IEnumerator FetchResourceCoroutine(McpResourceBase resource, JObject parameters, TaskCompletionSource tcs)
-        {
-            try
-            {
-                if (resource.IsAsync)
-                {
-                    resource.FetchAsync(parameters, tcs);
-                }
-                else
-                {
-                    var result = resource.Fetch(parameters);
-                    tcs.SetResult(result);
-                }
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogError($"Error fetching resource {resource.Name}: {ex.Message}\n{ex.StackTrace}");
-                tcs.SetResult(CreateErrorResponse(
-                    $"Failed to fetch resource {resource.Name}: {ex.Message}",
-                    "resource_fetch_error"
-                ));
-            }
-            yield return null;
-        }
-        
-        /// 
-        /// Create a JSON-RPC 2.0 response
-        /// 
-        /// Request ID
-        /// Result object
-        /// JSON-RPC 2.0 response
-        private JObject CreateResponse(string requestId, JObject result)
-        {
-            // Format as JSON-RPC 2.0 response
-            JObject jsonRpcResponse = new JObject
-            {
-                ["id"] = requestId
-            };
-            
-            // Add result or error
-            if (result.TryGetValue("error", out var errorObj))
-            {
-                jsonRpcResponse["error"] = errorObj;
-            }
-            else
-            {
-                jsonRpcResponse["result"] = result;
-            }
-            
-            return jsonRpcResponse;
-        }
-    }
-}
diff --git a/Editor/UnityBridge/McpUnitySocketHandler.cs.meta b/Editor/UnityBridge/McpUnitySocketHandler.cs.meta
deleted file mode 100644
index 3c132433..00000000
--- a/Editor/UnityBridge/McpUnitySocketHandler.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 6cdc25bb9ba16374c86e42480ff8e3b9
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Utils.meta b/Editor/Utils.meta
deleted file mode 100644
index 508c7dcc..00000000
--- a/Editor/Utils.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: 653d639cd0bb4a88a40d33ae3f57571b
-timeCreated: 1743635743
\ No newline at end of file
diff --git a/Editor/Utils/GameObjectHierarchyCreator.cs b/Editor/Utils/GameObjectHierarchyCreator.cs
deleted file mode 100644
index 39c3b5a9..00000000
--- a/Editor/Utils/GameObjectHierarchyCreator.cs
+++ /dev/null
@@ -1,71 +0,0 @@
-using System;
-using UnityEngine;
-using UnityEditor; // Required for Undo operations
-
-namespace McpUnity.Utils
-{
-    public static class GameObjectHierarchyCreator
-    {
-        public static GameObject FindOrCreateHierarchicalGameObject(string path)
-        {
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentException("GameObject path cannot be null or empty.", nameof(path));
-            }
-
-            path = path.Trim('/');
-            if (string.IsNullOrEmpty(path))
-            {
-                throw new ArgumentException("GameObject path cannot consist only of slashes.", nameof(path));
-            }
-
-            string[] parts = path.Split('/');
-            GameObject currentParent = null;
-            GameObject foundOrCreatedObject = null;
-
-            for (int i = 0; i < parts.Length; i++)
-            {
-                string name = parts[i];
-                if (string.IsNullOrEmpty(name))
-                {
-                    throw new ArgumentException($"Invalid path: empty segment at part {i + 1} in path '{path}'. Ensure segments are not empty.");
-                }
-
-                Transform childTransform;
-                if (currentParent == null)
-                {
-                    GameObject rootObj = GameObject.Find(name);
-                    childTransform = rootObj?.transform;
-                }
-                else
-                {
-                    childTransform = currentParent.transform.Find(name);
-                }
-
-                if (childTransform == null)
-                {
-                    GameObject newObj = new GameObject(name);
-                    Undo.RegisterCreatedObjectUndo(newObj, $"Create {name}");
-                    if (currentParent != null)
-                    {
-                        newObj.transform.SetParent(currentParent.transform, false);
-                    }
-                    foundOrCreatedObject = newObj;
-                    currentParent = newObj;
-                }
-                else
-                {
-                    foundOrCreatedObject = childTransform.gameObject;
-                    currentParent = foundOrCreatedObject;
-                }
-            }
-
-            if (foundOrCreatedObject == null)
-            {
-                throw new InvalidOperationException($"Failed to find or create GameObject for path '{path}'. This indicates an unexpected state.");
-            }
-
-            return foundOrCreatedObject;
-        }
-    }
-}
diff --git a/Editor/Utils/GameObjectHierarchyCreator.cs.meta b/Editor/Utils/GameObjectHierarchyCreator.cs.meta
deleted file mode 100644
index 04e489b7..00000000
--- a/Editor/Utils/GameObjectHierarchyCreator.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 3697ac47725de644d8b35a23fea536c2
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Utils/Logger.cs b/Editor/Utils/Logger.cs
deleted file mode 100644
index 97ce39c4..00000000
--- a/Editor/Utils/Logger.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using UnityEngine;
-using McpUnity.Unity;
-
-namespace McpUnity.Utils
-{
-    /// 
-    /// Special logger to use inside the MCP Unity Editor project
-    /// 
-    public static class McpLogger
-    {
-        private const string LogPrefix = "[MCP Unity] ";
-        
-        /// 
-        /// Log an info message if info logs are enabled
-        /// 
-        /// Message to log
-        public static void LogInfo(string message)
-        {
-            if (McpUnitySettings.Instance.EnableInfoLogs)
-            {
-                Debug.LogFormat(LogType.Log, LogOption.NoStacktrace, null, "{0}{1}", LogPrefix, message);
-            }
-        }
-        
-        /// 
-        /// Log a warning message
-        /// 
-        /// Message to log
-        public static void LogWarning(string message)
-        {
-            Debug.LogWarning($"{LogPrefix}{message}");
-        }
-        
-        /// 
-        /// Log an error message
-        /// 
-        /// Message to log
-        public static void LogError(string message)
-        {
-            Debug.LogError($"{LogPrefix}{message}");
-        }
-    }
-}
diff --git a/Editor/Utils/Logger.cs.meta b/Editor/Utils/Logger.cs.meta
deleted file mode 100644
index 1dcc4b91..00000000
--- a/Editor/Utils/Logger.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: d9f2327ac25b8994498b269be0b5a68e
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Utils/McpBackgroundTick.cs b/Editor/Utils/McpBackgroundTick.cs
deleted file mode 100644
index 13de6728..00000000
--- a/Editor/Utils/McpBackgroundTick.cs
+++ /dev/null
@@ -1,110 +0,0 @@
-#if UNITY_EDITOR
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-
-namespace McpUnity.Utils
-{
-    /// 
-    /// Keeps the Unity Editor loop ticking while the editor window is unfocused on Windows.
-    /// 
-    internal static class McpBackgroundTick
-    {
-#if UNITY_EDITOR_WIN
-        private delegate void TimerProc(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime);
-
-        [DllImport("user32.dll", SetLastError = true)]
-        private static extern UIntPtr SetTimer(IntPtr hWnd, UIntPtr nIDEvent, uint uElapse, TimerProc lpTimerFunc);
-
-        [DllImport("user32.dll", SetLastError = true)]
-        private static extern bool KillTimer(IntPtr hWnd, UIntPtr uIDEvent);
-
-        private const uint TickIntervalMs = 100;
-
-        // Rooted for the complete native timer lifetime so the callback cannot be collected.
-        private static readonly TimerProc Callback = OnTimer;
-        private static UIntPtr _timerId;
-        private static int _callbackRunning;
-#endif
-
-        /// 
-        /// Starts the Windows background timer after the WebSocket server is listening.
-        /// 
-        public static void Start()
-        {
-#if UNITY_EDITOR_WIN
-            if (_timerId != UIntPtr.Zero)
-            {
-                return;
-            }
-
-            _timerId = SetTimer(IntPtr.Zero, UIntPtr.Zero, TickIntervalMs, Callback);
-            if (_timerId == UIntPtr.Zero)
-            {
-                McpLogger.LogError($"Failed to start background editor tick timer. Win32 error: {Marshal.GetLastWin32Error()}.");
-            }
-#endif
-        }
-
-        /// 
-        /// Stops the Windows background timer. Safe to call repeatedly.
-        /// 
-        public static void Stop()
-        {
-#if UNITY_EDITOR_WIN
-            UIntPtr timerId = _timerId;
-            if (timerId == UIntPtr.Zero)
-            {
-                return;
-            }
-
-            try
-            {
-                if (!KillTimer(IntPtr.Zero, timerId))
-                {
-                    McpLogger.LogWarning($"Failed to stop background editor tick timer. Win32 error: {Marshal.GetLastWin32Error()}.");
-                }
-            }
-            catch (Exception ex)
-            {
-                McpLogger.LogError($"Error stopping background editor tick timer: {ex.Message}");
-            }
-            finally
-            {
-                _timerId = UIntPtr.Zero;
-            }
-#endif
-        }
-
-#if UNITY_EDITOR_WIN
-        private static void OnTimer(IntPtr hWnd, uint uMsg, UIntPtr nIDEvent, uint dwTime)
-        {
-            if (Interlocked.Exchange(ref _callbackRunning, 1) != 0)
-            {
-                return;
-            }
-
-            try
-            {
-                UnityEditor.EditorApplication.QueuePlayerLoopUpdate();
-            }
-            catch (Exception ex)
-            {
-                try
-                {
-                    McpLogger.LogError($"Error during background editor tick: {ex.Message}");
-                }
-                catch
-                {
-                    // Nothing may escape the native callback boundary.
-                }
-            }
-            finally
-            {
-                Volatile.Write(ref _callbackRunning, 0);
-            }
-        }
-#endif
-    }
-}
-#endif
diff --git a/Editor/Utils/McpBackgroundTick.cs.meta b/Editor/Utils/McpBackgroundTick.cs.meta
deleted file mode 100644
index 5994d4aa..00000000
--- a/Editor/Utils/McpBackgroundTick.cs.meta
+++ /dev/null
@@ -1,2 +0,0 @@
-fileFormatVersion: 2
-guid: c6a75d0993683af4f8d6b111db870d9c
\ No newline at end of file
diff --git a/Editor/Utils/McpUtils.cs b/Editor/Utils/McpUtils.cs
deleted file mode 100644
index bd51e421..00000000
--- a/Editor/Utils/McpUtils.cs
+++ /dev/null
@@ -1,1165 +0,0 @@
-using System;
-using System.IO;
-using System.Collections.Generic;
-using System.Reflection;
-using McpUnity.Unity;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace McpUnity.Utils
-{
-    /// 
-    /// Controls how the path to Server~/build/index.js is rendered in generated MCP configs.
-    /// 
-    public enum PathMode
-    {
-        /// Absolute filesystem path. Required for per-user/global configs (Cursor, Windsurf, etc.).
-        Absolute,
-        /// Path relative to the Unity project root. Used for OpenCode's opencode.json.
-        ProjectRelative,
-        /// Project-relative path prefixed with ${workspaceFolder}/. Used for VS Code / GitHub Copilot's .vscode/mcp.json.
-        VSCodeWorkspaceFolder
-    }
-
-    /// 
-    /// Utility class for MCP configuration and system operations
-    /// 
-    public static class McpUtils
-    {
-
-        // Cached result for Multiplayer Play Mode clone detection
-        private static bool? _isMultiplayerPlayModeClone;
-        
-        /// 
-        /// Generates the MCP configuration JSON to setup the Unity MCP server in different AI Clients
-        /// 
-        public static string GenerateMcpConfigJson(bool useTabsIndentation, PathMode pathMode = PathMode.Absolute)
-        {
-            var config = new Dictionary
-            {
-                { "mcpServers", new Dictionary
-                    {
-                        { "mcp-unity", new Dictionary
-                            {
-                                { "command", "node" },
-                                { "args", new[] { GetIndexJsPath(pathMode) } }
-                            }
-                        }
-                    }
-                }
-            };
-
-            // Initialize string writer with proper indentation
-            var stringWriter = new StringWriter();
-            using (var jsonWriter = new JsonTextWriter(stringWriter))
-            {
-                jsonWriter.Formatting = Formatting.Indented;
-
-                // Set indentation character and count
-                if (useTabsIndentation)
-                {
-                    jsonWriter.IndentChar = '\t';
-                    jsonWriter.Indentation = 1;
-                }
-                else
-                {
-                    jsonWriter.IndentChar = ' ';
-                    jsonWriter.Indentation = 2;
-                }
-
-                // Serialize directly to the JsonTextWriter
-                var serializer = new JsonSerializer();
-                serializer.Serialize(jsonWriter, config);
-            }
-
-            return stringWriter.ToString().Replace("\\", "/").Replace("//", "/");
-        }
-
-        /// 
-        /// Generates the MCP configuration JSON for OpenCode (https://opencode.ai/).
-        /// OpenCode uses a different schema than the standard `mcpServers` shape:
-        ///   { "$schema": ..., "mcp": { "mcp-unity": { "type": "local", "enabled": true, "command": [...], "environment": {} } } }
-        /// 
-        public static string GenerateOpenCodeConfigJson(bool useTabsIndentation, PathMode pathMode = PathMode.Absolute)
-        {
-            string indexJsPath = GetIndexJsPath(pathMode);
-
-            var config = new Dictionary
-            {
-                { "$schema", "https://opencode.ai/config.json" },
-                { "mcp", new Dictionary
-                    {
-                        { "mcp-unity", new Dictionary
-                            {
-                                { "type", "local" },
-                                { "enabled", true },
-                                { "command", new[] { "node", indexJsPath } },
-                                { "environment", new Dictionary() }
-                            }
-                        }
-                    }
-                }
-            };
-
-            var stringWriter = new StringWriter();
-            using (var jsonWriter = new JsonTextWriter(stringWriter))
-            {
-                jsonWriter.Formatting = Formatting.Indented;
-
-                if (useTabsIndentation)
-                {
-                    jsonWriter.IndentChar = '\t';
-                    jsonWriter.Indentation = 1;
-                }
-                else
-                {
-                    jsonWriter.IndentChar = ' ';
-                    jsonWriter.Indentation = 2;
-                }
-
-                var serializer = new JsonSerializer();
-                serializer.Serialize(jsonWriter, config);
-            }
-
-            return stringWriter.ToString().Replace("\\", "/").Replace("//", "/");
-        }
-
-        /// 
-        /// Generates the MCP configuration TOML to setup the Unity MCP server in TOML-based AI Clients (e.g., Codex CLI)
-        /// 
-        /// The TOML configuration string for mcp-unity server
-        public static string GenerateMcpConfigToml(PathMode pathMode = PathMode.Absolute)
-        {
-            string indexJsPath = GetIndexJsPath(pathMode);
-
-            var sb = new System.Text.StringBuilder();
-            sb.AppendLine("[mcp_servers.mcp-unity]");
-            sb.AppendLine("command = \"node\"");
-            sb.AppendLine($"args = [\"{indexJsPath}\"]");
-            return sb.ToString();
-        }
-
-        /// 
-        /// Returns the path to Server~/build/index.js rendered according to the given .
-        /// All returned paths use forward slashes.
-        /// 
-        private static string GetIndexJsPath(PathMode mode)
-        {
-            string absoluteIndexJs = Path.Combine(GetServerPath(), "build", "index.js").Replace("\\", "/");
-
-            if (mode == PathMode.Absolute)
-            {
-                return absoluteIndexJs;
-            }
-
-            string projectRoot = Directory.GetParent(Application.dataPath).FullName.Replace("\\", "/");
-            string relativeIndexJs = Path.GetRelativePath(projectRoot, absoluteIndexJs).Replace("\\", "/");
-
-            if (mode == PathMode.VSCodeWorkspaceFolder)
-            {
-                return "${workspaceFolder}/" + relativeIndexJs;
-            }
-
-            return relativeIndexJs;
-        }
-
-        /// 
-        /// Gets the absolute path to the Server directory containing package.json (root server dir).
-        /// Works whether MCP Unity is installed via Package Manager or directly in the Assets folder
-        /// 
-        public static string GetServerPath()
-        {
-            // First, try to find the package info via Package Manager
-            var packageInfo = UnityEditor.PackageManager.PackageInfo.FindForAssetPath($"Packages/{McpUnitySettings.PackageName}");
-                
-            if (packageInfo != null && !string.IsNullOrEmpty(packageInfo.resolvedPath))
-            {
-                string serverPath = Path.Combine(packageInfo.resolvedPath, "Server~");
-
-                return CleanPathPrefix(serverPath);
-            }
-
-            string[] dirs = System.IO.Directory.GetDirectories("Assets", "Server~", System.IO.SearchOption.AllDirectories);
-
-            for (int n=0; n
-        /// Cleans the path prefix by removing a leading "~" character if present on macOS.
-        /// 
-        /// The path to clean.
-        /// The cleaned path.
-        private static string CleanPathPrefix(string path)
-        {
-            if (path.StartsWith("~"))
-            {
-                return path.Substring(1);
-            }
-            return path;
-        }
-
-        /// 
-        /// Encodes a file path for use in file:// URLs by replacing spaces with %20.
-        /// 
-        /// The path to encode.
-        /// The encoded path suitable for file:// URLs.
-        public static string EncodePathForFileUrl(string path)
-        {
-            if (string.IsNullOrEmpty(path))
-                return path;
-
-            return path.Replace(" ", "%20");
-        }
-
-        /// 
-        /// Validates the server path and returns true if valid.
-        /// 
-        /// The server path to validate.
-        /// True if path is valid and usable, false if path has critical issues.
-        public static bool ValidateServerPath(string serverPath)
-        {
-            if (string.IsNullOrEmpty(serverPath))
-            {
-                Debug.LogError("[MCP Unity] Server path is null or empty. Cannot validate.");
-                return false;
-            }
-
-            // Verify the path exists
-            if (!Directory.Exists(serverPath))
-            {
-                Debug.LogError($"[MCP Unity] Server path does not exist: {serverPath}");
-                return false;
-            }
-
-            // Verify required files exist
-            string packageJsonPath = Path.Combine(serverPath, "package.json");
-            if (!File.Exists(packageJsonPath))
-            {
-                Debug.LogError($"[MCP Unity] package.json not found in server path: {serverPath}");
-                return false;
-            }
-
-            return true;
-        }
-
-        /// 
-        /// Adds the MCP configuration to the Windsurf MCP config file
-        /// 
-        public static bool AddToWindsurfIdeConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetWindsurfMcpConfigPath();
-            return AddToConfigFile(configFilePath, useTabsIndentation, "Windsurf");
-        }
-        
-        /// 
-        /// Adds the MCP configuration to the Claude Desktop config file
-        /// 
-        public static bool AddToClaudeDesktopConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetClaudeDesktopConfigPath();
-            return AddToConfigFile(configFilePath, useTabsIndentation, "Claude Desktop");
-        }
-        
-        /// 
-        /// Adds the MCP configuration to the Cursor config file
-        /// 
-        public static bool AddToCursorConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetCursorConfigPath();
-            return AddToConfigFile(configFilePath, useTabsIndentation, "Cursor");
-        }
-        
-        /// 
-        /// Adds the MCP configuration to the Claude Code config file
-        /// 
-        public static bool AddToClaudeCodeConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetClaudeCodeConfigPath();
-            return AddToConfigFile(configFilePath, useTabsIndentation, "Claude Code");
-        }
-
-        /// 
-        /// Adds the MCP configuration to the Google Antigravity config file
-        /// 
-        public static bool AddToAntigravityConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetAntigravityConfigPath();
-            return AddToConfigFile(configFilePath, useTabsIndentation, "Google Antigravity");
-        }
-
-        /// 
-        /// Adds the MCP configuration to the GitHub Copilot config file.
-        /// Uses ${workspaceFolder}-prefixed path so the config is portable across machines when committed to git.
-        /// 
-        public static bool AddToGitHubCopilotConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetGitHubCopilotConfigPath();
-            return AddToConfigFile(configFilePath, useTabsIndentation, "GitHub Copilot", PathMode.VSCodeWorkspaceFolder);
-        }
-
-        /// 
-        /// Adds the MCP configuration to the Codex CLI config file (TOML format)
-        /// 
-        public static bool AddToCodexCliConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetCodexCliConfigPath();
-            return AddToTomlConfigFile(configFilePath, "Codex CLI");
-        }
-
-        /// 
-        /// Adds the MCP configuration to the OpenCode config file (opencode.json in project root).
-        /// OpenCode uses a custom JSON schema, so this does not reuse the standard mcpServers helpers.
-        /// Uses a project-relative path so the config is portable across machines when committed to git.
-        /// 
-        public static bool AddToOpenCodeConfig(bool useTabsIndentation)
-        {
-            string configFilePath = GetOpenCodeConfigPath();
-            return AddToOpenCodeConfigFile(configFilePath, useTabsIndentation, PathMode.ProjectRelative);
-        }
-
-        /// 
-        /// Adds the MCP configuration to the project-local Cursor config (/.cursor/mcp.json).
-        /// Uses a project-relative path so the config is portable across machines when committed to git.
-        /// 
-        public static bool AddToCursorProjectConfig(bool useTabsIndentation)
-        {
-            return AddToConfigFile(GetCursorProjectConfigPath(), useTabsIndentation, "Cursor (Project)", PathMode.ProjectRelative);
-        }
-
-        /// 
-        /// Adds the MCP configuration to the project-local Claude Code config (/.mcp.json).
-        /// This file is Claude Code's team-shared MCP config and is intended to be committed to git.
-        /// Uses a project-relative path so the config is portable across machines.
-        /// 
-        public static bool AddToClaudeCodeProjectConfig(bool useTabsIndentation)
-        {
-            return AddToConfigFile(GetClaudeCodeProjectConfigPath(), useTabsIndentation, "Claude Code (Project)", PathMode.ProjectRelative);
-        }
-
-        /// 
-        /// Adds the MCP configuration to the project-local Codex CLI config (/.codex/config.toml).
-        /// Codex layers this over the global ~/.codex/config.toml only when the project is marked trusted
-        /// (Codex prompts the user the first time they run `codex` from the project root).
-        /// Uses a project-relative path so the config is portable across machines.
-        /// 
-        public static bool AddToCodexCliProjectConfig(bool useTabsIndentation)
-        {
-            return AddToTomlConfigFile(GetCodexCliProjectConfigPath(), "Codex CLI (Project)", PathMode.ProjectRelative);
-        }
-
-        /// 
-        /// Returns whether automatic MCP configuration is supported for the given product on the current platform.
-        /// 
-        public static bool IsAutoConfigSupported(string productName)
-        {
-            switch (productName)
-            {
-                case "Claude Code":
-                case "Claude Code (Project)":
-                case "Codex CLI":
-                case "Codex CLI (Project)":
-                case "Cursor (Project)":
-                case "GitHub Copilot":
-                case "OpenCode":
-                    return Application.platform == RuntimePlatform.WindowsEditor
-                        || Application.platform == RuntimePlatform.OSXEditor
-                        || Application.platform == RuntimePlatform.LinuxEditor;
-                case "Windsurf":
-                case "Claude Desktop":
-                case "Cursor":
-                case "Google Antigravity":
-                    return Application.platform == RuntimePlatform.WindowsEditor
-                        || Application.platform == RuntimePlatform.OSXEditor;
-                default:
-                    return false;
-            }
-        }
-
-        /// 
-        /// Returns a human-readable reason when automatic MCP configuration is unsupported.
-        /// 
-        public static string GetAutoConfigUnsupportedReason(string productName)
-        {
-            if (IsAutoConfigSupported(productName))
-            {
-                return null;
-            }
-
-            if (Application.platform == RuntimePlatform.LinuxEditor)
-            {
-                return $"Automatic {productName} configuration is currently available on Linux only for Claude Code, Codex CLI, Cursor (Project), GitHub Copilot, and OpenCode.";
-            }
-
-            return $"Automatic {productName} configuration is not supported on {Application.platform}.";
-        }
-
-        /// 
-        /// Common method to add MCP configuration to a specified config file
-        /// 
-        /// Path to the config file
-        /// Whether to use tabs for indentation
-        /// Name of the product (for error messages)
-        /// How to render the path to Server~/build/index.js
-        /// True if successfuly added the config, false otherwise
-        private static bool AddToConfigFile(string configFilePath, bool useTabsIndentation, string productName, PathMode pathMode = PathMode.Absolute)
-        {
-            if (string.IsNullOrEmpty(configFilePath))
-            {
-                Debug.LogError($"{productName} config file not found. Please make sure {productName} is installed.");
-                return false;
-            }
-
-            // Generate fresh MCP config JSON
-            string mcpConfigJson = GenerateMcpConfigJson(useTabsIndentation, pathMode);
-            
-            try
-            {
-                // Parse the MCP config JSON
-                JObject mcpConfig = JObject.Parse(mcpConfigJson);
-
-                // Check if the file exists
-                if (File.Exists(configFilePath))
-                {
-                    return TryMergeMcpServers(configFilePath, mcpConfig, productName);
-                }
-                else if(Directory.Exists(Path.GetDirectoryName(configFilePath)))
-                {
-                    // Create a new config file with just our config
-                    File.WriteAllText(configFilePath, mcpConfigJson);
-                    return true;
-                }
-                else
-                {
-                    Debug.LogError($"Cannot find {productName} config file or {productName} is currently not installed. Expecting {productName} to be installed in the {configFilePath} path");
-                }
-            }
-            catch (Exception ex)
-            {
-                Debug.LogError($"Failed to add MCP configuration to {productName}: {ex}");
-            }
-
-            return false;
-        }
-        
-        /// 
-        /// Gets the path to the Windsurf MCP config file based on the current OS
-        /// 
-        /// The path to the Windsurf MCP config file
-        private static string GetWindsurfMcpConfigPath()
-        {
-            // Base path depends on the OS
-            string basePath;
-            
-            if (Application.platform == RuntimePlatform.WindowsEditor)
-            {
-                // Windows: %USERPROFILE%/.codeium/windsurf
-                basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".codeium/windsurf");
-            }
-            else if (Application.platform == RuntimePlatform.OSXEditor)
-            {
-                // macOS: ~/Library/Application Support/.codeium/windsurf
-                string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
-                basePath = Path.Combine(homeDir, ".codeium/windsurf");
-            }
-            else
-            {
-                // Unsupported platform
-                Debug.LogError("Unsupported platform for Windsurf MCP config");
-                return null;
-            }
-            
-            // Return the path to the mcp_config.json file
-            return Path.Combine(basePath, "mcp_config.json");
-        }
-        
-        /// 
-        /// Gets the path to the Claude Desktop config file based on the current OS
-        /// 
-        /// The path to the Claude Desktop config file
-        private static string GetClaudeDesktopConfigPath()
-        {
-            // Base path depends on the OS
-            string basePath;
-            
-            if (Application.platform == RuntimePlatform.WindowsEditor)
-            {
-                // Windows: %USERPROFILE%/AppData/Roaming/Claude
-                basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Claude");
-            }
-            else if (Application.platform == RuntimePlatform.OSXEditor)
-            {
-                // macOS: ~/Library/Application Support/Claude
-                string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
-                basePath = Path.Combine(homeDir, "Library", "Application Support", "Claude");
-            }
-            else
-            {
-                // Unsupported platform
-                Debug.LogError("Unsupported platform for Claude Desktop config");
-                return null;
-            }
-            
-            // Return the path to the claude_desktop_config.json file
-            return Path.Combine(basePath, "claude_desktop_config.json");
-        }
-
-        /// 
-        /// Gets the path to the Cursor config file based on the current OS
-        /// 
-        /// The path to the Cursor config file
-        private static string GetCursorConfigPath()
-        {
-            // Base path depends on the OS
-            string basePath;
-            
-            if (Application.platform == RuntimePlatform.WindowsEditor)
-            {
-                // Windows: %USERPROFILE%/.cursor
-                basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".cursor");
-            }
-            else if (Application.platform == RuntimePlatform.OSXEditor)
-            {
-                // macOS: ~/.cursor
-                string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
-                basePath = Path.Combine(homeDir, ".cursor");
-            }
-            else
-            {
-                // Unsupported platform
-                Debug.LogError("Unsupported platform for Cursor MCP config");
-                return null;
-            }
-            
-            // Return the path to the mcp_config.json file
-            return Path.Combine(basePath, "mcp.json");
-        }
-
-        /// 
-        /// Gets the path to the Claude Code config file based on the current OS
-        /// 
-        /// The path to the Claude Code config file
-        private static string GetClaudeCodeConfigPath()
-        {
-            // Returns the absolute path to the global Claude configuration file.
-            // Windows: %USERPROFILE%\.claude.json
-            // macOS/Linux: $HOME/.claude.json
-            if (!TryGetUserHomeDirectory("Claude Code", out string homeDir))
-            {
-                return null;
-            }
-
-            return Path.Combine(homeDir, ".claude.json");
-        }
-
-        /// 
-        /// Gets the path to the Google Antigravity MCP config file based on the current OS
-        /// 
-        /// The path to the Google Antigravity MCP config file
-        private static string GetAntigravityConfigPath()
-        {
-            // Base path depends on the OS
-            string basePath;
-
-            if (Application.platform == RuntimePlatform.WindowsEditor)
-            {
-                // Windows: %USERPROFILE%/.gemini/antigravity/mcp_config.json
-                basePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".gemini", "antigravity");
-            }
-            else if (Application.platform == RuntimePlatform.OSXEditor)
-            {
-                // macOS: ~/Library/Application Support/.gemini/antigravity/mcp_config.json
-                string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
-                basePath = Path.Combine(homeDir, "Library", "Application Support", ".gemini", "antigravity");
-            }
-            else
-            {
-                // Unsupported platform
-                Debug.LogError("Unsupported platform for Google Antigravity MCP config");
-                return null;
-            }
-
-            // Return the path to the mcp_config.json file
-            return Path.Combine(basePath, "mcp_config.json");
-        }
-
-        /// 
-        /// Gets the path to the GitHub Copilot config file (workspace .vscode/mcp.json)
-        /// 
-        /// The path to the GitHub Copilot config file
-        private static string GetGitHubCopilotConfigPath()
-        {
-            // Default to current Unity project root/.vscode/mcp.json
-            string projectRoot = Directory.GetParent(Application.dataPath).FullName;
-            string vscodeDir = Path.Combine(projectRoot, ".vscode");
-            return Path.Combine(vscodeDir, "mcp.json");
-        }
-
-        /// 
-        /// Gets the path to the OpenCode config file (opencode.json in the Unity project root).
-        /// OpenCode reads its config per-project, not per-user, so this path is OS-independent.
-        /// 
-        /// The path to the OpenCode config file
-        private static string GetOpenCodeConfigPath()
-        {
-            string projectRoot = Directory.GetParent(Application.dataPath).FullName;
-            return Path.Combine(projectRoot, "opencode.json");
-        }
-
-        /// 
-        /// Adds the MCP configuration to the OpenCode config file. Preserves existing
-        /// `$schema` and any unrelated entries under `mcp`, only upserting `mcp["mcp-unity"]`.
-        /// 
-        private static bool AddToOpenCodeConfigFile(string configFilePath, bool useTabsIndentation, PathMode pathMode = PathMode.Absolute)
-        {
-            const string productName = "OpenCode";
-
-            if (string.IsNullOrEmpty(configFilePath))
-            {
-                Debug.LogError($"{productName} config file path could not be resolved.");
-                return false;
-            }
-
-            try
-            {
-                string mcpConfigJson = GenerateOpenCodeConfigJson(useTabsIndentation, pathMode);
-                JObject mcpConfig = JObject.Parse(mcpConfigJson);
-                JToken newServerEntry = mcpConfig["mcp"]?["mcp-unity"];
-
-                if (newServerEntry == null)
-                {
-                    Debug.LogError($"Failed to generate {productName} configuration: missing mcp-unity entry.");
-                    return false;
-                }
-
-                if (!File.Exists(configFilePath))
-                {
-                    File.WriteAllText(configFilePath, mcpConfigJson);
-                    return true;
-                }
-
-                string existingJson = File.ReadAllText(configFilePath);
-                JObject existingConfig = string.IsNullOrWhiteSpace(existingJson)
-                    ? new JObject()
-                    : JObject.Parse(existingJson);
-
-                JObject mcpSection = existingConfig["mcp"] as JObject;
-                if (mcpSection == null)
-                {
-                    mcpSection = new JObject();
-                    existingConfig["mcp"] = mcpSection;
-                }
-
-                mcpSection["mcp-unity"] = newServerEntry;
-
-                File.WriteAllText(configFilePath, existingConfig.ToString(Formatting.Indented));
-                return true;
-            }
-            catch (Exception ex)
-            {
-                Debug.LogError($"Failed to add MCP configuration to {productName}: {ex}");
-                return false;
-            }
-        }
-
-        /// 
-        /// Gets the path to the Codex CLI config file based on the current OS
-        /// 
-        /// The path to the Codex CLI config file
-        private static string GetCodexCliConfigPath()
-        {
-            // Codex CLI uses ~/.codex/config.toml on all platforms
-            if (!TryGetUserHomeDirectory("Codex CLI", out string homeDir))
-            {
-                return null;
-            }
-
-            return Path.Combine(homeDir, ".codex", "config.toml");
-        }
-
-        /// 
-        /// Gets the path to the project-local Cursor MCP config (/.cursor/mcp.json).
-        /// 
-        private static string GetCursorProjectConfigPath()
-        {
-            string projectRoot = Directory.GetParent(Application.dataPath).FullName;
-            return Path.Combine(projectRoot, ".cursor", "mcp.json");
-        }
-
-        /// 
-        /// Gets the path to the project-local Claude Code MCP config (/.mcp.json).
-        /// This is the team-shared config that Claude Code reads in addition to ~/.claude.json.
-        /// 
-        private static string GetClaudeCodeProjectConfigPath()
-        {
-            string projectRoot = Directory.GetParent(Application.dataPath).FullName;
-            return Path.Combine(projectRoot, ".mcp.json");
-        }
-
-        /// 
-        /// Gets the path to the project-local Codex CLI config (/.codex/config.toml).
-        /// Codex layers this over ~/.codex/config.toml only when the project is marked trusted.
-        /// 
-        private static string GetCodexCliProjectConfigPath()
-        {
-            string projectRoot = Directory.GetParent(Application.dataPath).FullName;
-            return Path.Combine(projectRoot, ".codex", "config.toml");
-        }
-
-        /// 
-        /// Resolves the current user's home directory across supported Unity Editor platforms.
-        /// 
-        private static bool TryGetUserHomeDirectory(string productName, out string homeDir)
-        {
-            if (Application.platform == RuntimePlatform.WindowsEditor)
-            {
-                homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
-                return true;
-            }
-
-            if (Application.platform == RuntimePlatform.OSXEditor
-                || Application.platform == RuntimePlatform.LinuxEditor)
-            {
-                homeDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
-                return true;
-            }
-
-            Debug.LogError($"Unsupported platform for {productName} config");
-            homeDir = null;
-            return false;
-        }
-
-        /// 
-        /// Common method to add MCP configuration to a TOML-based config file
-        /// 
-        /// Path to the TOML config file
-        /// Name of the product (for error messages)
-        /// How to render the path to Server~/build/index.js
-        /// True if successfully added the config, false otherwise
-        private static bool AddToTomlConfigFile(string configFilePath, string productName, PathMode pathMode = PathMode.Absolute)
-        {
-            if (string.IsNullOrEmpty(configFilePath))
-            {
-                Debug.LogError($"{productName} config file path not found. Please make sure {productName} is installed.");
-                return false;
-            }
-
-            try
-            {
-                // Generate fresh MCP config TOML
-                string mcpServerConfig = "\n" + GenerateMcpConfigToml(pathMode);
-                
-                string directoryPath = Path.GetDirectoryName(configFilePath);
-                
-                // Check if the config file exists
-                if (File.Exists(configFilePath))
-                {
-                    return TryMergeMcpServersToml(configFilePath, mcpServerConfig, productName);
-                }
-                else if (Directory.Exists(directoryPath))
-                {
-                    // Create a new config file
-                    File.WriteAllText(configFilePath, mcpServerConfig.TrimStart());
-                    return true;
-                }
-                else
-                {
-                    // Create directory and file
-                    Directory.CreateDirectory(directoryPath);
-                    File.WriteAllText(configFilePath, mcpServerConfig.TrimStart());
-                    return true;
-                }
-            }
-            catch (Exception ex)
-            {
-                Debug.LogError($"Failed to add MCP configuration to {productName}: {ex}");
-                return false;
-            }
-        }
-
-        /// 
-        /// Helper to merge mcp_servers.mcp-unity section into an existing TOML config file.
-        /// 
-        /// Path to the existing TOML config file
-        /// The new mcp-unity TOML configuration to merge
-        /// Name of the product (for error messages)
-        /// True if successfully merged, false otherwise
-        private static bool TryMergeMcpServersToml(string configFilePath, string mcpServerConfig, string productName)
-        {
-            string existingContent = File.ReadAllText(configFilePath);
-            
-            // Check if mcp-unity is already configured
-            if (existingContent.Contains("[mcp_servers.mcp-unity]"))
-            {
-                // Update existing configuration
-                // Find the start of the mcp-unity section
-                int startIndex = existingContent.IndexOf("[mcp_servers.mcp-unity]", StringComparison.Ordinal);
-                
-                // Find the end of this section (next section header or end of file)
-                int endIndex = FindNextTomlSectionIndex(existingContent, startIndex + 23);
-                
-                string newContent = existingContent.Substring(0, startIndex) + 
-                                  mcpServerConfig.TrimStart() + 
-                                  existingContent.Substring(endIndex);
-                File.WriteAllText(configFilePath, newContent);
-            }
-            else
-            {
-                // Append the new configuration
-                File.AppendAllText(configFilePath, mcpServerConfig);
-            }
-            
-            return true;
-        }
-
-        /// 
-        /// Finds the index of the next TOML section header starting from the given position.
-        /// Returns the length of the content if no next section is found.
-        /// 
-        /// The TOML content to search
-        /// The position to start searching from
-        /// The index of the next section header, or content length if not found
-        private static int FindNextTomlSectionIndex(string content, int startPosition)
-        {
-            // Look for patterns like [section] or [section.subsection]
-            int nextSectionIndex = content.IndexOf("\n[", startPosition, StringComparison.Ordinal);
-            
-            if (nextSectionIndex == -1)
-            {
-                // No more sections, return end of content
-                return content.Length;
-            }
-            
-            return nextSectionIndex;
-        }
-
-        /// 
-        /// Runs an npm command (such as install or build) in the specified working directory.
-        /// Handles cross-platform compatibility (Windows/macOS/Linux) for invoking npm.
-        /// Logs output and errors to the Unity console.
-        /// 
-        /// Arguments to pass to npm (e.g., "install" or "run build").
-        /// The working directory where the npm command should be executed.
-        public static void RunNpmCommand(string arguments, string workingDirectory)
-        {
-            string npmExecutable = McpUnitySettings.Instance.NpmExecutablePath;
-            bool useCustomNpmPath = !string.IsNullOrWhiteSpace(npmExecutable);
-
-            System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo
-            {
-                WorkingDirectory = workingDirectory,
-                RedirectStandardOutput = true,
-                RedirectStandardError = true,
-                UseShellExecute = false, // Important for redirection and direct execution
-                CreateNoWindow = true
-            };
-
-            if (useCustomNpmPath)
-            {
-                // Use the custom path directly
-                startInfo.FileName = npmExecutable;
-                startInfo.Arguments = arguments;
-            }
-            else if (Application.platform == RuntimePlatform.WindowsEditor)
-            {
-                // Fallback to cmd.exe to find 'npm' in PATH
-                startInfo.FileName = "cmd.exe";
-                startInfo.Arguments = $"/c npm {arguments}";
-            }
-            else // macOS / Linux
-            {
-                string userShell = Environment.GetEnvironmentVariable("SHELL") ?? "/bin/bash";
-                string shellName = Path.GetFileName(userShell);
-                
-                // Source rc file to init version managers (nvm, fnm, volta) - GUI apps don't inherit shell env
-                string rcFile = shellName == "zsh" ? ".zshrc" : ".bashrc";
-                
-                startInfo.FileName = userShell;
-                startInfo.Arguments = $"-c \"source ~/{rcFile} 2>/dev/null || true; npm {arguments}\"";
-
-                // Fallback PATH for common npm locations
-                string currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty;
-                string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
-                string extraPaths = string.Join(":",
-                    "/usr/local/bin",
-                    "/opt/homebrew/bin",
-                    $"{homeDir}/.nvm/versions/node/default/bin"  // nvm default alias
-                );
-                startInfo.EnvironmentVariables["PATH"] = $"{extraPaths}:{currentPath}";
-            }
-
-            try
-            {
-                using (var process = System.Diagnostics.Process.Start(startInfo))
-                {
-                    if (process == null)
-                    {
-                        Debug.LogError($"[MCP Unity] Failed to start npm process with arguments: {arguments} in {workingDirectory}. Process object is null.");
-                        return;
-                    }
-
-                    string output = process.StandardOutput.ReadToEnd();
-                    string error = process.StandardError.ReadToEnd();
-
-                    process.WaitForExit();
-
-                    if (process.ExitCode == 0)
-                    {
-                        Debug.Log($"[MCP Unity] npm {arguments} completed successfully in {workingDirectory}.\n{output}");
-                    }
-                    else
-                    {
-                        Debug.LogError($"[MCP Unity] npm {arguments} failed in {workingDirectory}. Exit Code: {process.ExitCode}. Error: {error}");
-                    }
-                }
-            }
-            catch (Exception ex)
-            {
-                // Use commandToLog here
-                Debug.LogError($"[MCP Unity] Exception while running npm {arguments} in {workingDirectory}. Error: {ex.Message}");
-            }
-        }
-
-        /// 
-        /// Returns the appropriate config JObject for merging MCP server settings,
-        /// with special handling for "Claude Code":
-        /// - For most products, returns the root config object.
-        /// - For "Claude Code", returns the project-specific config under "projects/[serverPathParent]".
-        /// Throws a MissingMemberException if the expected project entry does not exist.
-        /// 
-        private static JObject GetMcpServersConfig(JObject existingConfig, string productName)
-        {
-            // For most products, use the root config object.
-            if (productName != "Claude Code")
-            {
-                return existingConfig;
-            }
-
-            // For Claude Code, use the project-specific config.
-            if (existingConfig["projects"] == null)
-            {
-                throw new MissingMemberException("Claude Code config error: Could not find 'projects' entry in existing config.");
-            }
-
-            string serverPath = GetServerPath();
-            string serverPathParent = Path.GetDirectoryName(serverPath)?.Replace("\\", "/");
-            var projectConfig = existingConfig["projects"][serverPathParent];
-
-            if (projectConfig == null)
-            {
-                throw new MissingMemberException(
-                    $"Claude Code config error: Could not find project entry for parent directory '{serverPathParent}' in existing config."
-                );
-            }
-
-            return (JObject)projectConfig;
-        }
-
-        /// 
-        /// Helper to merge mcpServers from mcpConfig into the existing config file.
-        /// 
-        private static bool TryMergeMcpServers(string configFilePath, JObject mcpConfig, string productName)
-        {
-            // Read the existing config
-            string existingConfigJson = File.ReadAllText(configFilePath);
-            JObject existingConfig = string.IsNullOrEmpty(existingConfigJson) ? new JObject() : JObject.Parse(existingConfigJson);
-            JObject mcpServersConfig = GetMcpServersConfig(existingConfig, productName);
-
-            // Merge the mcpServers from our config into the existing config
-            if (mcpConfig["mcpServers"] != null && mcpConfig["mcpServers"] is JObject mcpServers)
-            {
-                // Create mcpServers object if it doesn't exist
-                if (mcpServersConfig["mcpServers"] == null)
-                {
-                    mcpServersConfig["mcpServers"] = new JObject();
-                }
-
-                // Add or update the mcp-unity server config
-                if (mcpServers["mcp-unity"] != null)
-                {
-                    ((JObject)mcpServersConfig["mcpServers"])["mcp-unity"] = mcpServers["mcp-unity"];
-                }
-
-                // Write the updated config back to the file
-                File.WriteAllText(configFilePath, existingConfig.ToString(Formatting.Indented));
-                return true;
-            }
-
-            return false;
-        }
-
-        /// 
-        /// Detects if the current Unity Editor instance is a Multiplayer Play Mode clone (additional editor).
-        /// Uses multiple detection methods in order of reliability:
-        /// 1. Command line arguments (-name Player2/3/4 indicates clone)
-        /// 2. Reflection on CurrentPlayer.IsMainEditor property
-        /// 3. Library path heuristics
-        /// Returns false if not a clone or detection fails (allowing normal operation).
-        /// 
-        /// True if running as a clone instance, false if main editor or detection fails
-        public static bool IsMultiplayerPlayModeClone()
-        {
-            // Return cached result if available
-            if (_isMultiplayerPlayModeClone.HasValue)
-            {
-                return _isMultiplayerPlayModeClone.Value;
-            }
-
-            try
-            {
-                // Method 1: Check command line arguments (most reliable)
-                // Unity MPPM passes "-name PlayerX" where X > 1 for clones
-                string[] args = Environment.GetCommandLineArgs();
-                for (int i = 0; i < args.Length - 1; i++)
-                {
-                    if (args[i] == "-name" || args[i] == "--name")
-                    {
-                        string playerName = args[i + 1];
-                        // Player1 is the main editor, Player2/3/4 are clones
-                        if (playerName.StartsWith("Player") && playerName != "Player1")
-                        {
-                            _isMultiplayerPlayModeClone = true;
-                            return true;
-                        }
-                        // Found -name argument but it's Player1 (main editor)
-                        if (playerName == "Player1")
-                        {
-                            _isMultiplayerPlayModeClone = false;
-                            return false;
-                        }
-                    }
-                }
-
-                // Method 2: Check for MPPM-specific command line flags
-                foreach (string arg in args)
-                {
-                    // Check for clone-specific flags that Unity might pass
-                    if (arg.Contains("mppm") && arg.Contains("clone"))
-                    {
-                        _isMultiplayerPlayModeClone = true;
-                        return true;
-                    }
-                }
-
-                // Method 3: Try reflection on CurrentPlayer.IsMainEditor (MPPM 1.4+)
-                Assembly mppmAssembly = null;
-                foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
-                {
-                    string assemblyName = assembly.GetName().Name;
-                    if (assemblyName == "Unity.Multiplayer.Playmode" || 
-                        assemblyName == "Unity.Multiplayer.Playmode.Editor")
-                    {
-                        mppmAssembly = assembly;
-                        break;
-                    }
-                }
-
-                if (mppmAssembly != null)
-                {
-                    // Try to find CurrentPlayer class
-                    Type currentPlayerType = mppmAssembly.GetType("Unity.Multiplayer.Playmode.CurrentPlayer");
-                    if (currentPlayerType != null)
-                    {
-                        // Try IsMainEditor property
-                        PropertyInfo isMainEditorProperty = currentPlayerType.GetProperty(
-                            "IsMainEditor", 
-                            BindingFlags.Public | BindingFlags.Static);
-                        
-                        if (isMainEditorProperty != null)
-                        {
-                            bool isMainEditor = (bool)isMainEditorProperty.GetValue(null);
-                            _isMultiplayerPlayModeClone = !isMainEditor;
-                            return !isMainEditor;
-                        }
-                    }
-                }
-
-                // Method 4: Check if Unity's Library path indicates a VP (Virtual Player) subfolder
-                // Clone instances may use a modified library path
-                string libraryPath = Path.GetFullPath(Path.Combine(Application.dataPath, "..", "Library"));
-                if (IsVirtualPlayerLibraryPath(libraryPath))
-                {
-                    // Looks like we're in a virtual player's library folder
-                    _isMultiplayerPlayModeClone = true;
-                    return true;
-                }
-
-                // Default: not a clone (or couldn't detect MPPM)
-                _isMultiplayerPlayModeClone = false;
-                return false;
-            }
-            catch (Exception ex)
-            {
-                // On any error, assume not a clone to avoid breaking functionality
-                Debug.LogWarning($"[MCP Unity] Error detecting Multiplayer Play Mode clone status: {ex.Message}");
-                _isMultiplayerPlayModeClone = false;
-                return false;
-            }
-        }
-
-        /// 
-        /// Returns true when the path contains a "Library" segment followed by a "VP" segment.
-        /// This avoids false positives from names like "MVP" or "CountyLibraryApp".
-        /// 
-        private static bool IsVirtualPlayerLibraryPath(string libraryPath)
-        {
-            if (string.IsNullOrEmpty(libraryPath))
-            {
-                return false;
-            }
-
-            string normalizedPath = libraryPath.Replace('\\', '/');
-            string[] segments = normalizedPath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
-
-            int libraryIndex = -1;
-            for (int i = 0; i < segments.Length; i++)
-            {
-                if (string.Equals(segments[i], "Library", StringComparison.OrdinalIgnoreCase))
-                {
-                    libraryIndex = i;
-                    break;
-                }
-            }
-
-            if (libraryIndex < 0)
-            {
-                return false;
-            }
-
-            for (int i = libraryIndex + 1; i < segments.Length; i++)
-            {
-                if (string.Equals(segments[i], "VP", StringComparison.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        /// 
-        /// Resets the cached Multiplayer Play Mode clone detection result.
-        /// Useful for testing or when the state might have changed.
-        /// 
-        public static void ResetMultiplayerPlayModeCloneCache()
-        {
-            _isMultiplayerPlayModeClone = null;
-        }
-    }
-}
diff --git a/Editor/Utils/McpUtils.cs.meta b/Editor/Utils/McpUtils.cs.meta
deleted file mode 100644
index 7d4bdb1a..00000000
--- a/Editor/Utils/McpUtils.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: eb8f80389be8b844799e9e8ab5abc86f
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Utils/UnityObjectId.cs b/Editor/Utils/UnityObjectId.cs
deleted file mode 100644
index 02247c23..00000000
--- a/Editor/Utils/UnityObjectId.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System.Collections.Generic;
-using UnityEditor;
-using UnityEngine;
-
-namespace McpUnity.Utils
-{
-    public static class UnityObjectId
-    {
-#if MCP_UNITY_ENTITY_ID_API
-        private static int _nextSyntheticId = 1;
-        private static readonly Dictionary PublicIdsByEntityId = new Dictionary();
-        private static readonly Dictionary EntityIdsByPublicId = new Dictionary();
-#endif
-
-        public static int GetObjectId(Object unityObject)
-        {
-            if (unityObject == null)
-            {
-                return 0;
-            }
-
-#if MCP_UNITY_ENTITY_ID_API
-            EntityId entityId = unityObject.GetEntityId();
-            if (!PublicIdsByEntityId.TryGetValue(entityId, out int publicId))
-            {
-                publicId = _nextSyntheticId++;
-                PublicIdsByEntityId[entityId] = publicId;
-                EntityIdsByPublicId[publicId] = entityId;
-            }
-
-            return publicId;
-#else
-            return unityObject.GetInstanceID();
-#endif
-        }
-
-        public static Object ObjectFromId(int objectId)
-        {
-#if MCP_UNITY_ENTITY_ID_API
-            return EntityIdsByPublicId.TryGetValue(objectId, out EntityId entityId)
-                ? EditorUtility.EntityIdToObject(entityId)
-                : null;
-#else
-            return EditorUtility.InstanceIDToObject(objectId);
-#endif
-        }
-    }
-}
diff --git a/Editor/Utils/UnityObjectId.cs.meta b/Editor/Utils/UnityObjectId.cs.meta
deleted file mode 100644
index ec3ab4be..00000000
--- a/Editor/Utils/UnityObjectId.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: e59c20bd238645158de51b1e7076cc51
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/Editor/Utils/VsCodeWorkspaceUtils.cs b/Editor/Utils/VsCodeWorkspaceUtils.cs
deleted file mode 100644
index 124dc554..00000000
--- a/Editor/Utils/VsCodeWorkspaceUtils.cs
+++ /dev/null
@@ -1,140 +0,0 @@
-using System;
-using System.IO;
-using System.Collections.Generic;
-using UnityEngine;
-using UnityEditor;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using System.Linq;
-
-namespace McpUnity.Utils
-{
-    /// 
-    /// Manages VSCode-like IDE workspace integration for Unity projects
-    /// 
-    public class VsCodeWorkspaceUtils
-    {
-        /// 
-        /// The default folder structure for code-workspace files
-        /// 
-        private static readonly JArray DefaultFolders = JArray.Parse(@"[
-            {
-                ""path"": ""Assets""
-            },
-            {
-                ""path"": ""Packages""
-            },
-            {
-                ""path"": ""Library/PackageCache""
-            }
-        ]");
-        
-        /// 
-        /// Add the Library/PackageCache folder to the .code-workspace file if not already present
-        /// This ensures that the Unity cache is available to code intelligence tools
-        /// 
-        public static bool AddPackageCacheToWorkspace()
-        {
-            try
-            {
-                // Get the project root directory
-                string projectRoot = Directory.GetParent(Application.dataPath).FullName;
-                
-                // Determine the workspace filename based on the project directory name
-                string projectDirName = new DirectoryInfo(projectRoot).Name;
-                string workspaceFilename = $"{projectDirName}.code-workspace";
-                string workspacePath = Path.Combine(projectRoot, workspaceFilename);
-                JObject workspaceConfig = new JObject
-                {
-                    ["folders"] = DefaultFolders.DeepClone(),
-                    ["settings"] = new JObject()
-                };
-                
-                // If file exists, update it rather than overwriting
-                if (File.Exists(workspacePath))
-                {
-                    string existingContent = File.ReadAllText(workspacePath);
-                    JObject existingWorkspace = JObject.Parse(existingContent);
-                    
-                    // Merge the new config with the existing one
-                    MergeWorkspaceConfigs(existingWorkspace, workspaceConfig);
-                    workspaceConfig = existingWorkspace;
-                }
-                
-                // Write the updated workspace file
-                File.WriteAllText(workspacePath, workspaceConfig.ToString(Formatting.Indented));
-                Debug.Log($"[MCP Unity] Updated workspace configuration in {workspacePath}");
-                return true;
-            }
-            catch (Exception ex)
-            {
-                Debug.LogError($"[MCP Unity] Error updating workspace file: {ex.Message}");
-                return false;
-            }
-        }
-        
-        /// 
-        /// Merges a source workspace config into a target workspace config
-        /// Ensures folders are uniquely added based on path properties
-        /// 
-        private static void MergeWorkspaceConfigs(JObject target, JObject source)
-        {
-            // Merge folders array if both exist
-            if (source["folders"] != null && source["folders"].Type == JTokenType.Array)
-            {
-                if (target["folders"] == null || target["folders"].Type != JTokenType.Array)
-                {
-                    target["folders"] = new JArray();
-                }
-                
-                // Get existing folder paths
-                var existingPaths = new HashSet();
-                foreach (var folder in target["folders"])
-                {
-                    if (folder.Type == JTokenType.Object && folder["path"] != null)
-                    {
-                        existingPaths.Add(folder["path"].ToString());
-                    }
-                }
-                
-                // Add new folders if they don't exist
-                foreach (var folder in source["folders"])
-                {
-                    if (folder.Type == JTokenType.Object && folder["path"] != null)
-                    {
-                        string path = folder["path"].ToString();
-                        if (!existingPaths.Contains(path))
-                        {
-                            ((JArray)target["folders"]).Add(folder.DeepClone());
-                            existingPaths.Add(path);
-                        }
-                    }
-                }
-            }
-            
-            // Merge settings if both exist
-            if (source["settings"] != null && source["settings"].Type == JTokenType.Object)
-            {
-                if (target["settings"] == null || target["settings"].Type != JTokenType.Object)
-                {
-                    target["settings"] = new JObject();
-                }
-                
-                // Deep merge settings
-                foreach (var property in (JObject)source["settings"])
-                {
-                    target["settings"][property.Key] = property.Value.DeepClone();
-                }
-            }
-            
-            // Merge any other top-level properties
-            foreach (var property in source)
-            {
-                if (property.Key != "folders" && property.Key != "settings")
-                {
-                    target[property.Key] = property.Value.DeepClone();
-                }
-            }
-        }
-    }
-}
diff --git a/Editor/Utils/VsCodeWorkspaceUtils.cs.meta b/Editor/Utils/VsCodeWorkspaceUtils.cs.meta
deleted file mode 100644
index e6c3ef96..00000000
--- a/Editor/Utils/VsCodeWorkspaceUtils.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: e7f91a2b8d6f343c1b54a71d59e8fa12
-MonoImporter:
-  externalObjects: {}
-  serializedVersion: 2
-  defaultReferences: []
-  executionOrder: 0
-  icon: {instanceID: 0}
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 
diff --git a/README-ja.md b/README-ja.md
index 2d28351f..8c7960b5 100644
--- a/README-ja.md
+++ b/README-ja.md
@@ -1,5 +1,7 @@
 # MCP Unity Editor(ゲームエンジン)
 
+> **MCP Unity 2.0 documentation notice:** この翻訳は旧 1.4 WebSocket アーキテクチャを説明しています。2.0 の Unity CLI / Pipeline セットアップと移行手順については、最新の [README.md](README.md) を参照してください。
+
 [![](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction)
 [![](https://img.shields.io/badge/Unity-000000?style=flat&logo=unity&logoColor=white 'Unity')](https://unity.com/releases/editor/archive)
 [![](https://img.shields.io/badge/Node.js-339933?style=flat&logo=nodedotjs&logoColor=white 'Node.js')](https://nodejs.org/en/download/)
diff --git a/README.md b/README.md
index f09cf2f2..e4a8efef 100644
--- a/README.md
+++ b/README.md
@@ -1,726 +1,298 @@
-# MCP Unity Editor (Game Engine)
+# MCP Unity 2.0
 
-[![](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction)
-[![](https://img.shields.io/badge/Unity-000000?style=flat&logo=unity&logoColor=white 'Unity')](https://unity.com/releases/editor/archive)
-[![](https://img.shields.io/badge/Node.js-339933?style=flat&logo=nodedotjs&logoColor=white 'Node.js')](https://nodejs.org/en/download/)
-[![](https://img.shields.io/github/stars/CoderGamester/mcp-unity 'Stars')](https://github.com/CoderGamester/mcp-unity/stargazers)
-[![](https://img.shields.io/github/last-commit/CoderGamester/mcp-unity 'Last Commit')](https://github.com/CoderGamester/mcp-unity/commits/main)
-[![](https://img.shields.io/badge/License-MIT-red.svg 'MIT License')](https://opensource.org/licenses/MIT)
+MCP Unity 2.0 is a Unity CLI extension package for focused Editor authoring workflows. Unity Pipeline supplies the broad command catalog; this package adds five commands where project-specific safety or bounded inspection is valuable. An optional private Node companion adds read-oriented MCP resources and a dashboard.
 
-| [🇺🇸English](README.md) | [🇨🇳简体中文](README_zh-CN.md) | [🇯🇵日本語](README-ja.md) |
-|----------------------|---------------------------------|----------------------|
+Version 2.0.0 supports Unity 6000.0, Unity 6000.3, and Unity 6000.5. It requires Unity CLI 1.0.0-beta.2 or newer. Unity CLI and Pipeline are experimental products, so keep the pins in this repository synchronized when upgrading.
 
-```        
-                              ,/(/.   *(/,                                  
-                          */(((((/.   *((((((*.                             
-                     .*((((((((((/.   *((((((((((/.                         
-                 ./((((((((((((((/    *((((((((((((((/,                     
+> [!IMPORTANT]
+> This is a breaking architecture change from 1.4.0. There is no custom WebSocket bridge, no listener on port 8090, and no `ProjectSettings/McpUnitySettings.json`. See [Migration from 1.4.0](#migration-from-140).
 
-             ,/(((((((((((((/*.           */(((((((((((((/*.                
-            ,%%#((/((((((*                    ,/(((((/(#&@@(                
-            ,%%##%%##((((((/*.             ,/((((/(#&@@@@@@(                
-            ,%%######%%##((/(((/*.    .*/(((//(%@@@@@@@@@@@(                
-            ,%%####%#(%%#%%##((/((((((((//#&@@@@@@&@@@@@@@@(                
-            ,%%####%(    /#%#%%%##(//(#@@@@@@@%,   #@@@@@@@(                
-            ,%%####%(        *#%###%@@@@@@(        #@@@@@@@(                
-            ,%%####%(           #%#%@@@@,          #@@@@@@@(                
-            ,%%##%%%(           #%#%@@@@,          #@@@@@@@(                
-            ,%%%#*              #%#%@@@@,             *%@@@(                
-            .,      ,/##*.      #%#%@@@@,     ./&@#*      *`                
-                ,/#%#####%%#/,  #%#%@@@@, ,/&@@@@@@@@@&\.                    
-                 `*#########%%%%###%@@@@@@@@@@@@@@@@@@&*´                   
-                    `*%%###########%@@@@@@@@@@@@@@&*´                        
-                        `*%%%######%@@@@@@@@@@&*´                            
-                            `*#%%##%@@@@@&*´                                 
-                               `*%#%@&*´                                     
-                                                       
-     ███╗   ███╗ ██████╗██████╗         ██╗   ██╗███╗   ██╗██╗████████╗██╗   ██╗
-     ████╗ ████║██╔════╝██╔══██╗        ██║   ██║████╗  ██║██║╚══██╔══╝╚██╗ ██╔╝
-     ██╔████╔██║██║     ██████╔╝        ██║   ██║██╔██╗ ██║██║   ██║    ╚████╔╝ 
-     ██║╚██╔╝██║██║     ██╔═══╝         ██║   ██║██║╚██╗██║██║   ██║     ╚██╔╝  
-     ██║ ╚═╝ ██║╚██████╗██║             ╚██████╔╝██║ ╚████║██║   ██║      ██║   
-     ╚═╝     ╚═╝ ╚═════╝╚═╝              ╚═════╝ ╚═╝  ╚═══╝╚═╝   ╚═╝      ╚═╝   
-```       
+## Architecture
 
-MCP Unity is an implementation of the Model Context Protocol for Unity Editor, allowing AI assistants to interact with your Unity projects. This package provides a bridge between Unity and a Node.js server that implements the MCP protocol, enabling AI agents like Cursor, Windsurf, Claude Code, Codex CLI, GitHub Copilot, Google Antigravity, and OpenCode to execute operations within the Unity Editor.
+The normal data flow is:
 
-## Features
-
-### IDE Integration - Package Cache Access
-
-MCP Unity provides automatic integration with VSCode-like IDEs (Visual Studio Code, Cursor, Windsurf, Google Antigravity) by adding the Unity `Library/PackedCache` folder to your workspace. This feature:
-
-- Improves code intelligence for Unity packages
-- Enables better autocompletion and type information for Unity packages
-- Helps AI coding assistants understand your project's dependencies
-
-### MCP Server Tools
-
-The following tools are available for manipulating and querying Unity scenes and GameObjects via MCP:
-
-- `execute_menu_item`: Executes Unity menu items (functions tagged with the MenuItem attribute)
-  > **Example prompt:** "Execute the menu item 'GameObject/Create Empty' to create a new empty GameObject"
-
-- `select_gameobject`: Selects game objects in the Unity hierarchy by path or instance ID
-  > **Example prompt:** "Select the Main Camera object in my scene"
-
-- `update_gameobject`: Updates a GameObject's core properties (name, tag, layer, active/static state), or creates the GameObject if it does not exist
-  > **Example prompt:** "Set the Player object's tag to 'Enemy' and make it inactive"
-
-- `update_component`: Updates component fields on a GameObject or adds it to the GameObject if it does not contain the component
-  > **Example prompt:** "Add a Rigidbody component to the Player object and set its mass to 5"
-
-- `add_package`: Installs new packages in the Unity Package Manager
-  > **Example prompt:** "Add the TextMeshPro package to my project"
-
-- `run_tests`: Runs tests using the Unity Test Runner
-  > **Example prompt:** "Run all the EditMode tests in my project"
-
-- `send_console_log`: Send a console log to Unity
-  > **Example prompt:** "Send a console log to Unity Editor"
-
-- `add_asset_to_scene`: Adds an asset from the AssetDatabase to the Unity scene
-  > **Example prompt:** "Add the Player prefab from my project to the current scene"
-
-- `create_prefab`: Creates a prefab with optional MonoBehaviour script and serialized field values
-  > **Example prompt:** "Create a prefab named 'Player' from the 'PlayerController' script"
-
-- `create_scene`: Creates a new scene and saves it to the specified path
-  > **Example prompt:** "Create a new scene called 'Level1' in the Scenes folder"
-
-- `load_scene`: Loads a scene by path or name, with optional additive loading
-  > **Example prompt:** "Load the MainMenu scene"
-
-- `delete_scene`: Deletes a scene by path or name and removes it from Build Settings
-  > **Example prompt:** "Delete the old TestScene from my project"
-
-- `get_gameobject`: Gets detailed information about a specific GameObject including all components
-  > **Example prompt:** "Get the details of the Player GameObject"
-
-- `get_console_logs`: Retrieves logs from the Unity console with pagination support
-  > **Example prompt:** "Show me the last 20 error logs from the Unity console"
-
-- `recompile_scripts`: Recompiles all scripts in the Unity project
-  > **Example prompt:** "Recompile scripts in my Unity project"
-
-- `save_scene`: Saves the current active scene, with optional Save As to a new path
-  > **Example prompt:** "Save the current scene" or "Save the scene as 'Assets/Scenes/Level2.unity'"
-
-- `get_scene_info`: Gets information about the active scene including name, path, dirty state, and all loaded scenes
-  > **Example prompt:** "What scenes are currently loaded in my project?"
-
-- `unload_scene`: Unloads a scene from the hierarchy (does not delete the scene asset)
-  > **Example prompt:** "Unload the UI scene from the hierarchy"
-
-- `duplicate_gameobject`: Duplicates a GameObject in the scene with optional renaming and reparenting
-  > **Example prompt:** "Duplicate the Enemy prefab 5 times and rename them Enemy_1 through Enemy_5"
-
-- `delete_gameobject`: Deletes a GameObject from the scene
-  > **Example prompt:** "Delete the old Player object from the scene"
-
-- `reparent_gameobject`: Changes the parent of a GameObject in the hierarchy
-  > **Example prompt:** "Move the HealthBar object to be a child of the UI Canvas"
-
-- `move_gameobject`: Moves a GameObject to a new position (local or world space)
-  > **Example prompt:** "Move the Player object to position (10, 0, 5) in world space"
-
-- `rotate_gameobject`: Rotates a GameObject to a new rotation (local or world space, Euler angles or quaternion)
-  > **Example prompt:** "Rotate the Camera 45 degrees on the Y axis"
-
-- `scale_gameobject`: Scales a GameObject to a new local scale
-  > **Example prompt:** "Scale the Enemy object to twice its size"
-
-- `set_transform`: Sets position, rotation, and scale of a GameObject in a single operation
-  > **Example prompt:** "Set the Cube's position to (0, 5, 0), rotation to (0, 90, 0), and scale to (2, 2, 2)"
-
-- `create_material`: Creates a new material with specified shader and saves it to the project
-  > **Example prompt:** "Create a red material called 'EnemyMaterial' using the URP Lit shader"
-
-- `assign_material`: Assigns a material to a GameObject's Renderer component
-  > **Example prompt:** "Assign the 'EnemyMaterial' to the Enemy GameObject"
-
-- `modify_material`: Modifies properties of an existing material (colors, floats, textures)
-  > **Example prompt:** "Change the color of 'EnemyMaterial' to blue and set metallic to 0.8"
-
-- `get_material_info`: Gets detailed information about a material including shader and all properties
-  > **Example prompt:** "Show me all the properties of the 'PlayerMaterial'"
-
-- `batch_execute`: Executes multiple tool operations in a single batch request, reducing round-trips and enabling atomic operations with optional rollback on failure
-  > **Example prompt:** "Create 10 empty GameObjects named Enemy_1 through Enemy_10 in a single batch operation"
-
-### MCP App tools
-
-- `show_unity_dashboard`: Opens the Unity dashboard MCP App in VS Code (requires VS Code 1.109+)
-  > **Example prompt:** "Open the Unity dashboard app"
-
-- `get_play_mode_status`: Gets Unity play mode status (isPlaying, isPaused)
-  > **Example prompt:** "Is Unity in play mode?"
-
-- `set_play_mode_status`: Controls Unity play mode with actions: 'play' (start or unpause), 'pause' (toggle pause), 'stop' (exit play mode), 'step' (advance one frame)
-  > **Example prompt:** "Start Unity play mode" or "Pause the game" or "Step forward one frame"
-
-### MCP Server Resources
-
-- `unity://menu-items`: Retrieves a list of all available menu items in the Unity Editor to facilitate `execute_menu_item` tool
-  > **Example prompt:** "Show me all available menu items related to GameObject creation"
-
-- `unity://scenes-hierarchy`: Retrieves a list of all game objects in the current Unity scene hierarchy
-  > **Example prompt:** "Show me the current scenes hierarchy structure"
-
-- `unity://gameobject/{id}`: Retrieves detailed information about a specific GameObject by instance ID or object path in the scene hierarchy, including all GameObject components with it's serialized properties and fields
-  > **Example prompt:** "Get me detailed information about the Player GameObject"
-
-- `unity://logs`: Retrieves a list of all logs from the Unity console
-  > **Example prompt:** "Show me the recent error messages from the Unity console"
-
-- `unity://packages`: Retrieves information about installed and available packages from the Unity Package Manager
-  > **Example prompt:** "List all the packages currently installed in my Unity project"
-
-- `unity://assets`: Retrieves information about assets in the Unity Asset Database
-  > **Example prompt:** "Find all texture assets in my project"
-
-- `unity://tests/{testMode}`: Retrieves information about tests in the Unity Test Runner
-  > **Example prompt:** "List all available tests in my Unity project"
-
-- `ui://unity-dashboard`: Unity dashboard MCP App UI
-  > **Example prompt:** "Open the Unity dashboard app"
-
-### MCP Server Prompts
-
-Prompts are pre-configured templates that provide guided workflows for common Unity tasks. They help AI assistants understand the proper sequence of operations and available tools for specific scenarios.
-
-- `unity_dashboard`: Opens the Unity dashboard MCP app with contextual information about its features
-  > **Usage:** In your AI assistant, use the prompt "unity_dashboard" to get guided access to the Unity dashboard
-
-- `gameobject_handling_strategy`: Provides a structured workflow for working with GameObjects, including which tools and resources to use
-  > **Usage:** In your AI assistant, use the prompt "gameobject_handling_strategy" with a GameObject ID, name, or path to get step-by-step guidance
-
-## Requirements
-- Unity 6 or later - to [install the server](#install-server)
-- Node.js 18 or later - to [start the server](#start-server)
-- npm 9 or later - to [debug the server](#debug-server)
-
-> [!NOTE]
-> **Project Paths with Spaces**
->
-> MCP Unity supports project paths containing spaces. However, if you experience connection issues, try moving your project to a path without spaces as a troubleshooting step.
->
-> **Examples:**
-> -   ✅ **Recommended:** `C:\Users\YourUser\Documents\UnityProjects\MyAwesomeGame`
-> -   ✅ **Supported:** `C:\Users\Your User\Documents\Unity Projects\My Awesome Game`
-
-## Installation
+```text
+MCP client <-> Unity CLI (`unity mcp`) <-> Pipeline <-> Unity Editor
+                                              ^
+                                              |
+                                  MCP Unity extension commands
+```
 
-Installing this MCP Unity Server is a multi-step process:
+The optional companion is a second MCP server:
 
-### Step 1: Install Node.js 
-> To run MCP Unity server, you'll need to have Node.js 18 or later installed on your computer:
+```text
+MCP client <-> private Node companion <-> `unity mcp` <-> Pipeline <-> Unity Editor
+```
 
-![node](docs/node.jpg)
+Unity Package Manager installs the exact dependency `com.unity.pipeline@0.3.1-exp.1` automatically from this package's manifest. MCP Unity does not run `unity pipeline install`, mutate a project manifest, or vendor Pipeline.
 
-
-Windows +Unity CLI is machine-level software and is never downloaded or installed by this package. A developer or CI image must install it explicitly. -1. Visit the [Node.js download page](https://nodejs.org/en/download/) -2. Download the Windows Installer (.msi) for the LTS version (recommended) -3. Run the installer and follow the installation wizard -4. Verify the installation by opening PowerShell and running: - ```bash - node --version - ``` -
+## Install -
-macOS +1. Use Unity 6000.0, Unity 6000.3, or Unity 6000.5. +2. In Package Manager, choose **Add package from git URL** and enter: -1. Visit the [Node.js download page](https://nodejs.org/en/download/) -2. Download the macOS Installer (.pkg) for the LTS version (recommended) -3. Run the installer and follow the installation wizard -4. Alternatively, if you have Homebrew installed, you can run: - ```bash - brew install node@18 + ```text + https://github.com/CoderGamester/mcp-unity.git#2.0.0 ``` -5. Verify the installation by opening Terminal and running: - ```bash - node --version - ``` -
- -### Step 2: Install Unity MCP Server package via Unity Package Manager -1. Open the Unity Package Manager (Window > Package Manager) -2. Click the "+" button in the top-left corner -3. Select "Add package from git URL..." -4. Enter: `https://github.com/CoderGamester/mcp-unity.git` -5. Click "Add" - -![package manager](https://github.com/user-attachments/assets/a72bfca4-ae52-48e7-a876-e99c701b0497) - -### Step 3: Configure AI LLM Client - -
-Option 1: Configure using Unity Editor - -1. Open the Unity Editor -2. Navigate to Tools > MCP Unity > Server Window -3. Click on the "Configure" button for your AI LLM client as shown in the image below - -![image](docs/configure.jpg) - -> **Global vs. Project configuration:** - > - **Configure \[Client\]** — writes to your global user config file (e.g. `~/.claude.json`). Uses an absolute path. Applies to all projects on your machine. Best for personal, single-developer setups. - > - **Configure \[Client\] (Project)** — writes to a `.mcp.json` file (or equivalent) in the Unity project root. Uses a relative path, so it works across machines. Intended to be committed to git and shared with the team. Best for collaborative projects or when you want the config to travel with the project. - > - > If in doubt, prefer the **(Project)** variant — the relative path is more portable and won't break if you move your project folder. +3. Let UPM resolve `com.unity.pipeline@0.3.1-exp.1`. +4. Install Unity CLI by following the [official Unity CLI documentation](https://docs.unity.com/en-us/unity-cli/use-unity-cli). +5. Open `Window > MCP Unity > Setup`. -4. Confirm the configuration installation with the given popup +The Setup window is user-initiated and never opens on import. It displays the current project path and the resolved Pipeline version and compatibility state, checks only `unity --version`, and can copy official installation or MCP configuration text. It does not display the resolved package filesystem path; it uses the package resolver path internally when it generates companion configuration. It does not execute installers, request elevation, modify PATH, change shell files, run upgrades, write client configuration, or store a machine-specific CLI path in project settings. -![image](https://github.com/user-attachments/assets/b1f05d33-3694-4256-a57b-8556005021ba) +CLI lookup order is: -
+1. the path entered in the Setup window; +2. `UNITY_CLI_PATH`; +3. `unity` from `PATH`. -
-Option 2: Configure Manually +CLI 1.x versions at or above Unity CLI 1.0.0-beta.2 are compatible. A newer major version is reported as untested rather than silently accepted as tested. -Open the MCP configuration file of your AI client and add the MCP Unity server configuration: +If UPM cannot resolve Pipeline, treat it as a normal package-resolution failure: confirm the Unity version, registry/network access, and the exact dependency pin in `package.json`, then retry Package Manager resolution. -> Replace `ABSOLUTE/PATH/TO` with the absolute path to your MCP Unity installation or just copy the text from the Unity Editor MCP Server window (Tools > MCP Unity > Server Window). -> -> For configs that live inside the Unity project tree and get committed to git (e.g. `/.vscode/mcp.json`, `/opencode.json`, `/.cursor/mcp.json`, `/.mcp.json`, `/.codex/config.toml`), prefer a project-relative path so the same file works across machines. Toggle **"Use relative path"** in the Server Window to switch the copy-paste snippet between absolute and project-relative forms. The **Configure GitHub Copilot**, **Configure OpenCode**, **Configure Cursor (Project)**, **Configure Claude Code (Project)**, and **Configure Codex CLI (Project)** buttons already emit relative paths automatically. -> -> Project-local buttons (Cursor / Claude Code / Codex CLI) write the MCP server entry into the Unity project directory instead of your global user config, so other (non-Unity) projects don't see MCP connection-failure warnings. For **Codex CLI (Project)** specifically, you must approve the project trust prompt the first time you run `codex` from the project root, otherwise Codex ignores `/.codex/config.toml`. +## Configure the primary MCP server -**For JSON-based clients** (Cursor, Windsurf, Claude Code, GitHub Copilot, etc.): +The supported primary entrypoint is: -```json -{ - "mcpServers": { - "mcp-unity": { - "command": "node", - "args": [ - "ABSOLUTE/PATH/TO/mcp-unity/Server~/build/index.js" - ] - } - } -} +```bash +unity mcp --project-path "/absolute/path/to/UnityProject" ``` -For workspace-scoped VS Code / GitHub Copilot (`.vscode/mcp.json`), use `${workspaceFolder}` so the path is portable across machines: +A JSON-based MCP client can use: ```json { - "mcpServers": { - "mcp-unity": { - "command": "node", - "args": [ - "${workspaceFolder}/Library/PackageCache/com.gamelovers.mcp-unity@/Server~/build/index.js" - ] - } - } + "mcpServers": { + "unity": { + "command": "unity", + "args": [ + "mcp", + "--project-path", + "/absolute/path/to/UnityProject" + ] + } + } } ``` -**For Codex CLI** (`~/.codex/config.toml`): +If Unity CLI is not on `PATH`, set the MCP client's `command` to the absolute executable path. Otherwise, ensure `unity` is available on the MCP client's `PATH`. The Setup window copies, but never writes, this configuration. -```toml -[mcp_servers.mcp-unity] -command = "node" -args = ["ABSOLUTE/PATH/TO/mcp-unity/Server~/build/index.js"] -``` +## MCP Unity extension commands -**For Cursor — project-local** (`.cursor/mcp.json` in the Unity project root, project-relative path): +Pipeline discovers these commands through `[CliCommand]`: -```json -{ - "mcpServers": { - "mcp-unity": { - "command": "node", - "args": [ - "Library/PackageCache/com.gamelovers.mcp-unity@/Server~/build/index.js" - ] - } - } -} -``` +- `inspect_gameobject` — bounded GameObject, hierarchy, component, and serialized-property inspection. Depth defaults to 2 and caps at 8; nodes default to 200 and cap at 1,000. Each GameObject returns at most 32 components, the whole inspection returns at most 128 components, and one shared aggregate conversion-work budget bounds property scanning and lazy value conversion across every component. Serialized readers, iterators, wrappers, and values reserve work/content before allocation; exact exhaustion blocks later work without claiming conversion truncation until something is actually omitted. The serialized result stays at or below 512 KiB and reports stable work, content, conversion, component, property, and payload truncation metadata. +- `duplicate_gameobject` — duplicate a source with optional parent/name and `world_position_stays`; records Unity Undo and returns the new identity. +- `unload_scene` — unload a scene by path; protects dirty scenes unless `force=true` and never unloads the only active scene. +- `editor_step` — advance one Editor frame; requires play mode and returns the resulting Editor state. +- `assign_material` — assign a material to a Renderer slot with validation, Undo, and prefab-modification recording. -**For Claude Code — project-local** (`.mcp.json` in the Unity project root, project-relative path — Claude Code's team-shared MCP config): +All other Editor operations use commands supplied by `com.unity.pipeline@0.3.1-exp.1`. Run the CLI/MCP command discovery flow to inspect the full official catalog. -```json -{ - "mcpServers": { - "mcp-unity": { - "command": "node", - "args": [ - "Library/PackageCache/com.gamelovers.mcp-unity@/Server~/build/index.js" - ] - } - } -} -``` +## Optional MCP companion -**For Codex CLI — project-local** (`.codex/config.toml` in the Unity project root, project-relative path): +`Server~` is a private, self-contained Node 20+ package. Its tracked `build/index.js` bundles every runtime npm dependency and its copied dashboard, so a UPM or Git installation can launch it directly without `npm install`; bundled licenses are retained in `Server~/THIRD_PARTY_NOTICES.md`. Release verification initializes that shipped entrypoint from a clean archive with no reachable `node_modules`, uses Node itself with an extensionless cross-platform fake `mcp` child (no shell, chmod, or `.cmd` dependency), and reads `ui://unity-dashboard` over stdio. It is optional: the Unity package and all five extension commands remain usable through the primary `unity mcp` server without Node. -```toml -[mcp_servers.mcp-unity] -command = "node" -args = ["Library/PackageCache/com.gamelovers.mcp-unity@/Server~/build/index.js"] -``` +Every Unity-backed companion resource is projected to at most 512 KiB. Strings, collections, object keys, depth, and aggregate values are bounded without recursive traversal, and the top-level `projection` metadata reports whether data was omitted. The scene hierarchy uses equivalent specialized `truncation` metadata. Companion error details have a centralized 4 KiB UTF-8-safe ceiling; oversized errors end with `[truncated]`, including outer MCP resource and dashboard-read errors. -> Codex layers this file over the global `~/.codex/config.toml`, but only when the project is marked trusted. The first time you `cd` into the project and run `codex`, approve the trust prompt — otherwise Codex ignores `.codex/config.toml`. +The companion requires `--project-path ` and accepts `--unity-cli-path `. Its lookup order is the argument, `UNITY_CLI_PATH`, then `unity` from `PATH`. It validates Unity CLI 1.0.0-beta.2 or newer and never installs it. -**For OpenCode** (`opencode.json` in the Unity project root): +Open `Window > MCP Unity > Setup` and use its copy action to generate this configuration from the package path it resolves internally: ```json { - "$schema": "https://opencode.ai/config.json", - "mcp": { - "mcp-unity": { - "type": "local", - "enabled": true, - "command": ["node", "Library/PackageCache/com.gamelovers.mcp-unity@/Server~/build/index.js"], - "environment": {} + "mcpServers": { + "mcp-unity-companion": { + "command": "node", + "args": [ + "/resolved/upm/package/path/Server~/build/index.js", + "--project-path", + "/absolute/path/to/UnityProject" + ] } } } ``` -> Note: the `@` segment in the UPM package cache path changes when the package is updated. If you update MCP Unity, re-run the **Configure** button (or update the path manually) so the snippet points at the new cache directory. - -
- -## Start Unity Editor MCP Server -1. Open the Unity Editor -2. Navigate to Tools > MCP Unity > Server Window -3. Click "Start Server" to start the WebSocket server -4. Open your AI Coding IDE (e.g. Cursor, Windsurf, Claude Code, Codex CLI, GitHub Copilot, Google Antigravity, OpenCode, etc.) and start executing Unity tools - -![connect](https://github.com/user-attachments/assets/2e266a8b-8ba3-4902-b585-b220b11ab9a2) - -> When the AI client connects to the WebSocket server, it will automatically show in the green box in the window - -## Optional: Set WebSocket Port -By default, the WebSocket server runs on port '8090'. You can change this port in two ways: - -1. Open the Unity Editor -2. Navigate to Tools > MCP Unity > Server Window -3. Change the "WebSocket Port" value to your desired port number -4. Unity will setup the system environment variable UNITY_PORT to the new port number -5. Restart the Node.js server -6. Click again on "Start Server" to reconnect the Unity Editor web socket to the Node.js MCP Server - -## Optional: Set Timeout +To pin a CLI executable for only this companion, use the optional argument: -By default, the timeout between the MCP server and the WebSocket is 10 seconds. -You can change depending on the OS you are using: - -1. Open the Unity Editor -2. Navigate to Tools > MCP Unity > Server Window -3. Change the "Request Timeout (seconds)" value to your desired timeout seconds -4. Unity will setup the system environment variable UNITY_REQUEST_TIMEOUT to the new timeout value -5. Restart the Node.js server -6. Click again on "Start Server" to reconnect the Unity Editor web socket to the Node.js MCP Server - -> [!TIP] -> The timeout between your AI Coding IDE (e.g., Claude Desktop, Cursor IDE, Windsurf IDE) and the MCP Server depends on the IDE. - -## Optional: Allow Remote MCP Bridge Connections - -By default, the WebSocket server binds to 'localhost'. To allow MCP bridge connections from other machines: - -1. Open the Unity Editor -2. Navigate to Tools > MCP Unity > Server Window -3. Enable the "Allow Remote Connections" checkbox -4. Unity will bind the WebSocket server to '0.0.0.0' (all interfaces) -5. Restart the Node.js server to apply the new host configuration -6. Set the environment variable UNITY_HOST to your Unity machine's IP address when running the MCP bridge remotely: `UNITY_HOST=192.168.1.100 node server.js` - -## Debugging the Server - -
-Building the Node.js Server - -The MCP Unity server is built using Node.js . It requires to compile the TypeScript code to JavaScript in the `build` directory. -In case of issues, you can force install it in by: - -1. Open the Unity Editor -2. Navigate to Tools > MCP Unity > Server Window -3. Click on "Force Install Server" button - -![install](docs/install.jpg) - -If you want to build it manually, you can follow these steps: - -1. Open a terminal/PowerShell/Command Prompt - -2. Navigate to the Server directory: - ```bash - cd ABSOLUTE/PATH/TO/mcp-unity/Server~ - ``` - -3. Install dependencies: - ```bash - npm install - ``` - -4. Build the server: - ```bash - npm run build - ``` - -5. Run the server: - ```bash - node build/index.js - ``` - -
- -
-Debugging with MCP Inspector - -Debug the server with [@modelcontextprotocol/inspector](https://github.com/modelcontextprotocol/inspector): - - Powershell - ```powershell - npx @modelcontextprotocol/inspector node Server~/build/index.js - ``` - - Command Prompt/Terminal - ```cmd - npx @modelcontextprotocol/inspector node Server~/build/index.js - ``` - -Don't forget to shutdown the server with `Ctrl + C` before closing the terminal or debugging it with the [MCP Inspector](https://github.com/modelcontextprotocol/inspector). - -
- -
-Enable Console Logs - -1. Enable logging on your terminal or into a log.txt file: - - Powershell - ```powershell - $env:LOGGING = "true" - $env:LOGGING_FILE = "true" - ``` - - Command Prompt/Terminal - ```cmd - set LOGGING=true - set LOGGING_FILE=true - ``` - -
- -## Frequently Asked Questions - -
-What is MCP Unity? - -MCP Unity is a powerful bridge that connects your Unity Editor environment to AI assistants LLM tools using the Model Context Protocol (MCP). - -In essence, MCP Unity: -- Exposes Unity Editor functionalities (like creating objects, modifying components, running tests, etc.) as "tools" and "resources" that an AI can understand and use. -- Runs a WebSocket server inside Unity and a Node.js server (acting as a WebSocket client to Unity) that implements the MCP. This allows AI assistants to send commands to Unity and receive information back. -- Enables you to use natural language prompts with your AI assistant to perform complex tasks within your Unity project, significantly speeding up development workflows. - -
- -
-Why use MCP Unity? - -MCP Unity offers several compelling advantages for developers, artists, and project managers: - -- **Accelerated Development:** Automate repetitive tasks, generate boilerplate code, and manage assets using AI prompts. This frees up your time to focus on creative and complex problem-solving. -- **Enhanced Productivity:** Interact with Unity Editor features without needing to manually click through menus or write scripts for simple operations. Your AI assistant becomes a direct extension of your capabilities within Unity. -- **Improved Accessibility:** Allows users who are less familiar with the deep intricacies of the Unity Editor or C# scripting to still make meaningful contributions and modifications to a project through AI guidance. -- **Seamless Integration:** Designed to work with various AI assistants and IDEs that support MCP, providing a consistent way to leverage AI across your development toolkit. -- **Extensibility:** The protocol and the toolset can be expanded. You can define new tools and resources to expose more of your project-specific or Unity's functionality to AI. -- **Collaborative Potential:** Facilitates a new way of collaborating where AI can assist in tasks traditionally done by team members, or help in onboarding new developers by guiding them through project structures and operations. - -
- -
-How does MCP Unity compare with the upcoming Unity 6.2 AI features? - -Unity 6.2 is set to introduce new built-in AI tools, including the previous Unity Muse (for generative AI capabilities like texture and animation generation) and Unity Sentis (for running neural networks in Unity runtime). As Unity 6.2 is not yet fully released, this comparison is based on publicly available information and anticipated functionalities: - -- **Focus:** - - **MCP Unity:** Primarily focuses on **Editor automation and interaction**. It allows external AI (like LLM-based coding assistants) to *control and query the Unity Editor itself* to manipulate scenes, assets, and project settings. It's about augmenting the *developer's workflow* within the Editor. - - **Unity 6.2 AI:** - - Aims at in-Editor content creation (generating textures, sprites, animations, behaviors, scripts) and AI-powered assistance for common tasks, directly integrated into the Unity Editor interface. - - A fine-tuned model to ask any question about Unity's documentation and API structure, with customized examples more accurate to Unity's environment. - - Adds the functionality to run AI model inference, allowing developers to deploy and run pre-trained neural networks *within your game or application* for features like NPC behavior, image recognition, etc. - -- **Use Cases:** - - **MCP Unity:** "Create a new 3D object, name it 'Player', add a Rigidbody, and set its mass to 10." "Run all Play Mode tests." "Ask to fix the error on the console log." "Execute the custom menu item 'Prepare build for iOS' and fix any errors that may occur." - - **Unity 6.2 AI:** "Generate a sci-fi texture for this material." "Update all trees position in the scene to be placed inside of terrain zones tagged with 'forest'." "Create a walking animation for this character." "Generate 2D sprites to complete the character." "Ask details about the error on the console log." - -- **Complementary, Not Mutually Exclusive:** - MCP Unity and Unity's native AI tools can be seen as complementary. You might use MCP Unity with your AI coding assistant to set up a scene or batch-modify assets, and then use Unity AI tools to generate a specific texture, or to create animations, or 2D sprites for one of those assets. MCP Unity provides a flexible, protocol-based way to interact with the Editor, which can be powerful for developers who want to integrate with a broader range of external AI services or build custom automation workflows. - -
- -
-What MCP hosts and IDEs currently support MCP Unity? - -MCP Unity is designed to work with any AI assistant or development environment that can act as an MCP client. The ecosystem is growing, but current known integrations or compatible platforms include: -- Cursor -- Windsurf -- Claude Desktop -- Claude Code -- Codex CLI -- GitHub Copilot -- Google Antigravity -- OpenCode - -
- -
-Can I extend MCP Unity with custom tools for my project? - -Yes, absolutely! One of the significant benefits of the MCP Unity architecture is its extensibility. -- **In Unity (C#):** You can create new C# classes that inherit from `McpToolBase` (or a similar base for resources) to expose custom Unity Editor functionality. These tools would then be registered in `McpUnityServer.cs`. For example, you could write a tool to automate a specific asset import pipeline unique to your project. -- **In Node.js Server (TypeScript):** You would then define the corresponding TypeScript tool handler in the `Server/src/tools/` directory, including its Zod schema for inputs/outputs, and register it in `Server/src/index.ts`. This Node.js part will forward the request to your new C# tool in Unity. - -This allows you to tailor the AI's capabilities to the specific needs and workflows of your game or application. - -
- -
-Is MCP Unity free to use? - -Yes, MCP Unity is an open-source project distributed under the MIT License. You are free to use, modify, and distribute it according to the license terms. - -
- -
-Why am I unable to connect to MCP Unity? - -- Ensure the WebSocket server is running (check the Server Window in Unity) -- Send a console log message from MCP client to force a reconnection between MCP client and Unity server -- Change the port number in the Unity Editor MCP Server window. (Tools > MCP Unity > Server Window) - -
- -
-Why won't the MCP Unity server start? - -- Check the Unity Console for error messages -- Ensure Node.js is properly installed and accessible in your PATH -- Verify that all dependencies are installed in the Server directory - -
- -
-Why do I get a connection failed error when running Play Mode tests? - -The `run_tests` tool returns the following response: -``` -Error: -Connection failed: Unknown error +```json +{ + "mcpServers": { + "mcp-unity-companion": { + "command": "node", + "args": [ + "/resolved/upm/package/path/Server~/build/index.js", + "--project-path", + "/absolute/path/to/UnityProject", + "--unity-cli-path", + "/absolute/path/to/unity" + ] + } + } +} ``` -This error occurs because the bridge connection is lost when the domain reloads upon switching to Play Mode. The workaround is to turn off **Reload Domain** in **Edit > Project Settings > Editor > "Enter Play Mode Settings"**. - -
- -
-Why do some clients fail with KeyError: 'position' during tool initialization? - -Some MCP clients may fail while parsing tool schemas when they contain local JSON pointer references such as `#/properties/position`. - -MCP Unity avoids this by registering transform tool inputs (`set_transform`, `move_gameobject`, `rotate_gameobject`, `scale_gameobject`) with fresh nested vector schemas per field, so the generated schema does not rely on local `#/properties/...` references. - -If you still see this error: -- update your MCP client to the latest version, -- rebuild the Node server (`cd Server~ && npm run build`), -- confirm your package version includes this compatibility fix. - -
- -## Troubleshooting: WSL2 (Windows 11) networking +The companion exposes exactly: -When running the MCP (Node.js) server inside WSL2 while Unity runs on Windows 11, connecting to `ws://localhost:8090/McpUnity` may fail with `ECONNREFUSED`. +- Tool: `show_unity_dashboard` +- Resources: + - `unity://logs{?severity,limit}` + - `unity://scenes-hierarchy{?path,max_nodes}` + - `unity://gameobject/{target}` + - `unity://packages{?include_indirect}` + - `unity://tests/{mode}` + - `ui://unity-dashboard` +- Prompts: + - `gameobject_handling_strategy` + - `unity_dashboard` -Cause: WSL2 and Windows have separate network namespaces — `localhost` inside WSL2 does not point to the Windows host. By default, Unity listens on `localhost:8090`. +The companion lazily starts `unity mcp`, retries one interrupted read-only resource request, and never mirrors or retries mutation commands. -### Solution 1 — Enable WSL2 Mirrored mode networking (preferred) -- Windows 11: Settings → System → For developers → WSL → Enable “Mirrored mode networking”. -- Or via `.wslconfig` (then run `wsl --shutdown` and reopen WSL): +## Remote and CI operation -```ini -[wsl2] -networkingMode=mirrored -``` +The package is local-only. Run Unity CLI on the same host as the Unity Editor. For a remote workflow, connect to that host through SSH or external agent infrastructure and launch the CLI there; do not expose an Editor socket. -After enabling, `localhost` is shared between Windows and WSL2, so the default config (`localhost:8090`) works. +CI must install Unity CLI before starting MCP Unity. The official non-interactive beta-channel commands currently surfaced by the Setup window are: -### Solution 2 — Point the Node client to the Windows host -Set in your WSL shell before starting the MCP client: +macOS/Linux: ```bash -# Use the Windows host IP detected from resolv.conf -export UNITY_HOST=$(grep -m1 nameserver /etc/resolv.conf | awk '{print $2}') +curl -fsSL https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.sh | UNITY_CLI_CHANNEL=beta bash ``` -With this, `Server~/src/unity/mcpUnity.ts` will connect to `ws://$UNITY_HOST:8090/McpUnity` instead of `localhost` (it reads `UNITY_HOST`, and may also honor a `Host` in `ProjectSettings/McpUnitySettings.json` if present). - -### Solution 3 — Allow remote connections from Unity -- Unity: Tools → MCP Unity → Server Window → enable “Allow Remote Connections” (Unity binds to `0.0.0.0`). -- Ensure Windows Firewall allows inbound TCP on your configured port (default 8090). -- From WSL2, connect to the Windows host IP (see Solution 2) or to `localhost` if mirrored mode is enabled. - -> [!NOTE] -> Default port is `8090`. You can change it in the Unity Server Window (Tools → MCP Unity → Server Window). The value maps to `McpUnitySettings` and is persisted in `ProjectSettings/McpUnitySettings.json`. +Windows PowerShell: -#### Validate connectivity - -```bash -npm i -g wscat -# After enabling mirrored networking -wscat -c ws://localhost:8090/McpUnity -# Or using the Windows host IP -wscat -c ws://$UNITY_HOST:8090/McpUnity +```powershell +$env:UNITY_CLI_CHANNEL='beta'; irm https://public-cdn.cloud.unity3d.com/hub/prod/cli/install.ps1 | iex ``` -## Running Tests - -### C# Tests (Unity) -Run tests using Unity's Test Runner: -1. Open Unity Editor -2. Navigate to Window > General > Test Runner -3. Select "EditMode" tab -4. Click "Run All" to execute all tests +After installation, CI should run `unity --version` and require Unity CLI 1.0.0-beta.2 or newer before launching `unity mcp --project-path `. + +## Migration from 1.4.0 + +The table is intentionally exhaustive. Its checked-in inventory is enforced against the actual tool constants, resource registrations, prompt registrations, URI templates, settings fields, environment variables, deployment files, and registry metadata in the `1.4.0` tag whenever that tag object is available. Shallow clones and packaged copies use the same immutable inventory snapshot. + +### Tools + +| 1.4.0 concept | 2.0 replacement | +|---|---| +| `tool:add_asset_to_scene` | Pipeline `instantiate_prefab` for prefab assets; use the relevant Pipeline asset/authoring command for other asset types. | +| `tool:add_package` | Pipeline `package_add`; poll `package_status` when needed. | +| `tool:assign_material` | MCP Unity extension `assign_material`. | +| `tool:batch_execute` | Removed. Let the MCP client sequence Pipeline commands; use Pipeline's purpose-built plural commands such as `create_gameobjects` where available. | +| `tool:create_material` | Pipeline `create_asset` with type `UnityEngine.Material`, then `set_material_properties`. | +| `tool:create_prefab` | Pipeline `create_gameobject`/`attach_script` as needed, then `create_prefab`. | +| `tool:create_scene` | Pipeline `create_scene`. | +| `tool:delete_gameobject` | Pipeline `delete_gameobject`. | +| `tool:delete_scene` | Pipeline `remove_scene_from_build` when applicable, then `delete_asset` with confirmation. | +| `tool:duplicate_gameobject` | MCP Unity extension `duplicate_gameobject`. | +| `tool:execute_menu_item` | Pipeline `menu`. | +| `tool:get_console_logs` | Pipeline `get_console_logs`, or companion `unity://logs{?severity,limit}`. | +| `tool:get_gameobject` | MCP Unity extension `inspect_gameobject`, or companion `unity://gameobject/{target}`. | +| `tool:get_material_info` | Pipeline `get_material_properties`. | +| `tool:get_play_mode_status` | Pipeline `editor_status`. | +| `tool:get_scene_info` | Pipeline `list_open_scenes`. | +| `tool:get_scenes_hierarchy` | Pipeline `get_scene_hierarchy`, or companion `unity://scenes-hierarchy{?path,max_nodes}`. | +| `tool:load_scene` | Pipeline `open_scene`. | +| `tool:modify_material` | Pipeline `set_material_properties`. | +| `tool:move_gameobject` | Pipeline `set_transform`. | +| `tool:recompile_scripts` | Pipeline `recompile`; poll `recompile_status`. | +| `tool:reparent_gameobject` | Pipeline `set_parent`. | +| `tool:rotate_gameobject` | Pipeline `set_transform`. | +| `tool:run_tests` | Pipeline `list_tests`, `run_tests`, and `test_status`. | +| `tool:save_scene` | Pipeline `save_scene` or `save_all`. | +| `tool:scale_gameobject` | Pipeline `set_transform`. | +| `tool:select_gameobject` | Pipeline `set_selection`; inspect with `get_selection`. | +| `tool:send_console_log` | Removed. Use project logging code or a project-specific `[CliCommand]`; Pipeline provides `get_console_logs` and `clear_console`, not arbitrary log injection. | +| `tool:set_play_mode_status` | Pipeline `editor_play`, `editor_pause`, and `editor_stop`; use extension `editor_step` to step. | +| `tool:set_transform` | Pipeline `set_transform`. | +| `tool:show_unity_dashboard` | Optional companion tool `show_unity_dashboard`. | +| `tool:unload_scene` | MCP Unity extension `unload_scene`. | +| `tool:update_component` | Pipeline `add_component` and `set_component_properties`. | +| `tool:update_gameobject` | Pipeline `create_gameobject`, `rename_gameobject`, `set_active`, `set_tag`, `set_layer`, `set_parent`, and `set_transform` as required. | + +### Resources, prompts, and URIs + +| 1.4.0 concept | 2.0 replacement | +|---|---| +| `resource:get_assets` | Removed as a resource; use Pipeline `find_assets`. | +| `resource:get_console_logs` | Companion `unity://logs{?severity,limit}` or Pipeline `get_console_logs`. | +| `resource:get_gameobject` | Companion `unity://gameobject/{target}` or extension `inspect_gameobject`. | +| `resource:get_menu_items` | Removed as a resource; call Pipeline `menu` without a path to list menu items. | +| `resource:get_packages` | Companion `unity://packages{?include_indirect}` or Pipeline `package_list`. | +| `resource:get_scenes_hierarchy` | Companion `unity://scenes-hierarchy{?path,max_nodes}` or Pipeline `get_scene_hierarchy`. | +| `resource:get_tests` | Companion `unity://tests/{mode}` or Pipeline `list_tests`. | +| `resource:unity_dashboard_app` | Companion `ui://unity-dashboard`. | +| `resource:unity_dashboard_app_legacy` | Removed; use companion `ui://unity-dashboard`. | +| `uri:ui://unity-dashboard` | Retained by the optional companion. | +| `uri:unity://assets` | Removed; use Pipeline `find_assets`. | +| `uri:unity://gameobject/{idOrName}` | Companion `unity://gameobject/{target}`. | +| `uri:unity://logs/{logType}?offset={offset}&limit={limit}&includeStackTrace={includeStackTrace}` | Companion `unity://logs{?severity,limit}`; pagination/stack controls are no longer public URI arguments. | +| `uri:unity://menu-items` | Removed; call Pipeline `menu` without a path. | +| `uri:unity://packages` | Companion `unity://packages{?include_indirect}`. | +| `uri:unity://scenes_hierarchy` | Renamed to companion `unity://scenes-hierarchy{?path,max_nodes}`. | +| `uri:unity://tests/{testMode}` | Renamed to companion `unity://tests/{mode}`. | +| `uri:unity://ui/dashboard` | Removed legacy alias; use `ui://unity-dashboard`. | +| `prompt:gameobject_handling_strategy` | Retained by the optional companion. | +| `prompt:unity_dashboard` | Retained by the optional companion. | + +### Configuration and integration + +| 1.4.0 concept | 2.0 replacement | +|---|---| +| `config:Port` | Removed; Unity CLI owns transport. There is no package port setting. | +| `config:RequestTimeoutSeconds` | Removed; use MCP host/CLI timeout controls. | +| `config:AutoStartServer` | Removed; the MCP host explicitly launches `unity mcp`. | +| `config:EnableInfoLogs` | Removed; use Unity CLI and Editor logging. | +| `config:NpmExecutablePath` | Removed; the Unity package does not run npm. | +| `config:AllowRemoteConnections` | Removed; run the CLI on the Unity host and connect through SSH/external agent infrastructure. | +| `concept:env:UNITY_HOST` | Removed; there is no host override for an Editor socket. Run Unity CLI on the Editor host. | +| `concept:env:LOGGING` | Removed with the legacy Node logger. Use Unity CLI, MCP-host, and Editor logging. | +| `concept:env:LOGGING_FILE` | Removed with legacy `log.txt` output. Direct logs through the MCP host/CI environment instead. | +| `concept:path:ProjectSettings/McpUnitySettings.json` | Removed and never created. | +| `concept:integration:Unity-driven npm install/build` | Removed; the bundled companion build is shipped in `Server~/build`, and maintainers build it outside Unity. | +| `concept:integration:automatic MCP-client configuration` | Removed; `Window > MCP Unity > Setup` only copies configuration after a user action. | +| `concept:integration:PackedCache mutation` | Removed; MCP Unity never edits IDE workspaces or PackedCache references. | +| `concept:integration:custom WebSocket endpoint/port` | Removed; Unity CLI/Pipeline own communication and the old endpoint and port 8090 are not opened. | +| `concept:integration:Docker deployment/Dockerfile/exposed ports` | Removed. No Docker image or exposed bridge/health ports are shipped; install and run Unity CLI on the Unity host or through external agent infrastructure. | +| `concept:integration:Smithery configuration` | Removed. Configure the primary CLI or optional companion directly in the MCP host; no Smithery manifest is shipped. | +| `concept:integration:Node npm executable/bin/publication surface` | Removed. The companion is private, has no npm `bin`, `files`, or publication configuration, and is launched from its bundled `Server~/build/index.js`. | +| `concept:integration:MCP registry server.json` | The invalid registry manifest was already absent from the final 1.4.0 tag and remains unsupported in 2.0. | +| `concept:integration:MCP registry mcpName/mcpname` | Removed from both Unity and Node package metadata; 2.0 is not an npm-published MCP server. | +| `concept:integration:Glama registry metadata` | Removed; the package no longer advertises a registry-hosted server through Glama metadata. | + +Old tool aliases are not retained. Update prompts and client automation to the replacement names above before upgrading. + +## Development + +Install and test the private companion: -### TypeScript Tests (Server) -Run tests using Jest: ```bash cd Server~ -npm test +npm ci +npm test -- --runInBand --detectOpenHandles +npm run build +npm audit --omit=dev ``` -To run tests in watch mode: +Run Unity EditMode tests from the Editor Test Runner or in batch mode on all supported lines: + ```bash -npm run test:watch +"/Applications/Unity/Hub/Editor//Unity.app/Contents/MacOS/Unity" \ + -batchmode -nographics -projectPath "/path/to/test-project" \ + -runTests -testPlatform EditMode -testResults "/tmp/results.xml" ``` -## Support & Feedback - -If you have any questions or need support, please open an [issue](https://github.com/CoderGamester/mcp-unity/issues) on this repository or alternative you can reach out on: -- Linkedin: [![](https://img.shields.io/badge/LinkedIn-0077B5?style=flat&logo=linkedin&logoColor=white 'LinkedIn')](https://www.linkedin.com/in/miguel-tomas/) -- Discord: gamester7178 -- Email: game.gamester@gmail.com - -## Contributing +See [AGENTS.md](AGENTS.md) for the maintainer architecture and release invariants, and [CHANGELOG.md](CHANGELOG.md) for release notes. -Contributions are welcome! Please feel free to submit a Pull Request or open an Issue with your request. +## Security and audit note -**Commit your changes** following the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format. +The companion uses stdio only. The exact required `@modelcontextprotocol/sdk@1.26.0` pin currently brings two moderate npm audit advisories through its unused Hono HTTP adapter. They are tracked as an inherited pinned-SDK risk; MCP Unity does not import or expose that HTTP adapter. Changing the SDK pin requires a coordinated compatibility review. ## License -This project is under [MIT License](LICENSE.md) - -## Acknowledgements - -- [Model Context Protocol](https://modelcontextprotocol.io) -- [Unity Technologies](https://unity.com) -- [Node.js](https://nodejs.org) -- [WebSocket-Sharp](https://github.com/sta/websocket-sharp) +[MIT](LICENSE.md) diff --git a/README_zh-CN.md b/README_zh-CN.md index 2d70cc9d..d82b52d9 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -1,5 +1,7 @@ # MCP Unity Editor(游戏引擎) +> **MCP Unity 2.0 documentation notice:** 此翻译仍描述旧版 1.4 WebSocket 架构。有关 2.0 Unity CLI / Pipeline 设置和迁移说明,请参阅最新的 [README.md](README.md)。 + [![](https://badge.mcpx.dev?status=on 'MCP Enabled')](https://modelcontextprotocol.io/introduction) [![](https://img.shields.io/badge/Unity-000000?style=flat&logo=unity&logoColor=white 'Unity')](https://unity.com/releases/editor/archive) [![](https://img.shields.io/badge/Node.js-339933?style=flat&logo=nodedotjs&logoColor=white 'Node.js')](https://nodejs.org/en/download/) diff --git a/Server~/.dockerignore b/Server~/.dockerignore deleted file mode 100644 index 1c77fea9..00000000 --- a/Server~/.dockerignore +++ /dev/null @@ -1,33 +0,0 @@ -# Node.js -node_modules -npm-debug.log -yarn-debug.log -yarn-error.log - -# Build outputs -build -dist -.tsbuildinfo - -# Development files -.git -.github -.vscode -.idea -*.md -.gitignore -.env -.env.* -*.log - -# Test files -test -__tests__ -*.test.ts -*.spec.ts - -# Miscellaneous -.DS_Store -Thumbs.db -*.swp -*.swo diff --git a/Server~/Dockerfile b/Server~/Dockerfile deleted file mode 100644 index 8d92ee02..00000000 --- a/Server~/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# Multi-stage build for optimized production image -FROM node:18-alpine AS builder - -# Set working directory -WORKDIR /app - -# Copy package files for dependency installation -COPY package.json package-lock.json ./ - -# Install dependencies with cache optimization -RUN --mount=type=cache,target=/root/.npm \ - npm ci - -# Copy TypeScript configuration and source code -COPY tsconfig.json ./ -COPY src ./src - -# Build the project -RUN npm run build - -# Production stage with minimal dependencies -FROM node:18-alpine AS production - -# Set working directory -WORKDIR /app - -# Set production environment -ENV NODE_ENV=production - -# Copy package files -COPY package.json package-lock.json ./ - -# Install production dependencies only -RUN --mount=type=cache,target=/root/.npm \ - npm ci --omit=dev - -# Copy built application from builder stage -COPY --from=builder /app/build ./build - -# Create a non-root user to run the app -RUN addgroup -g 1001 -S nodejs && \ - adduser -S nodejs -u 1001 -G nodejs - -# Set ownership to the non-root user -RUN chown -R nodejs:nodejs /app - -# Switch to non-root user -USER nodejs - -# Expose WebSocket and HTTP ports -EXPOSE 8090 3000 - -# Health check to ensure the application is running -HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ - CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1 - -# Command to run the MCP server -ENTRYPOINT ["node", "build/index.js"] diff --git a/Server~/THIRD_PARTY_NOTICES.md b/Server~/THIRD_PARTY_NOTICES.md new file mode 100644 index 00000000..cfbe7275 --- /dev/null +++ b/Server~/THIRD_PARTY_NOTICES.md @@ -0,0 +1,604 @@ +# Third-Party Notices + +MCP Unity bundles the following runtime dependencies into `build/index.js`. +This file is generated from the exact packages included by the companion build. + +## @modelcontextprotocol/ext-apps 1.0.1 + +License: MIT + +Source: https://github.com/modelcontextprotocol/ext-apps + +```text +The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0. + +Contributions for which relicensing consent has been obtained are licensed under Apache-2.0. Contributions made by authors who originally licensed their work under the MIT License and who have not yet granted explicit permission to relicense remain licensed under the MIT License. + +No rights beyond those granted by the applicable original license are conveyed for such contributions. + +--- + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright + owner or by an individual or Legal Entity authorized to submit on behalf + of the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +--- + +MIT License + +Copyright (c) 2024-2025 Model Context Protocol a Series of LF Projects, LLC. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +Creative Commons Attribution 4.0 International (CC-BY-4.0) + +Documentation in this project (excluding specifications) is licensed under +CC-BY-4.0. See https://creativecommons.org/licenses/by/4.0/legalcode for +the full license text. +``` + +## @modelcontextprotocol/sdk 1.26.0 + +License: MIT + +Source: git+https://github.com/modelcontextprotocol/typescript-sdk.git + +```text +MIT License + +Copyright (c) 2024 Anthropic, PBC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## ajv 8.20.0 + +License: MIT + +Source: ajv-validator/ajv + +```text +The MIT License (MIT) + +Copyright (c) 2015-2021 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## ajv-formats 3.0.1 + +License: MIT + +Source: git+https://github.com/ajv-validator/ajv-formats.git + +```text +MIT License + +Copyright (c) 2020 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## cross-spawn 7.0.6 + +License: MIT + +Source: git@github.com:moxystudio/node-cross-spawn.git + +```text +The MIT License (MIT) + +Copyright (c) 2018 Made With MOXY Lda + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +``` + +## fast-deep-equal 3.1.3 + +License: MIT + +Source: git+https://github.com/epoberezkin/fast-deep-equal.git + +```text +MIT License + +Copyright (c) 2017 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## fast-uri 3.1.4 + +License: BSD-3-Clause + +Source: git+https://github.com/fastify/fast-uri.git + +```text +Copyright (c) 2011-2021, Gary Court until https://github.com/garycourt/uri-js/commit/a1acf730b4bba3f1097c9f52e7d9d3aba8cdcaae +Copyright (c) 2021-present The Fastify team +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * The names of any contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS AND CONTRIBUTORS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + * * * + +The complete list of contributors can be found at: +- https://github.com/garycourt/uri-js/graphs/contributors +``` + +## isexe 2.0.0 + +License: ISC + +Source: git+https://github.com/isaacs/isexe.git + +```text +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` + +## json-schema-traverse 1.0.0 + +License: MIT + +Source: git+https://github.com/epoberezkin/json-schema-traverse.git + +```text +MIT License + +Copyright (c) 2017 Evgeny Poberezkin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## path-key 3.1.1 + +License: MIT + +Source: sindresorhus/path-key + +```text +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## shebang-command 2.0.0 + +License: MIT + +Source: kevva/shebang-command + +```text +MIT License + +Copyright (c) Kevin Mårtensson (github.com/kevva) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## shebang-regex 3.0.0 + +License: MIT + +Source: sindresorhus/shebang-regex + +```text +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +``` + +## which 2.0.2 + +License: ISC + +Source: git://github.com/isaacs/node-which.git + +```text +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` + +## zod 3.25.76 + +License: MIT + +Source: git+https://github.com/colinhacks/zod.git + +```text +MIT License + +Copyright (c) 2025 Colin McDonnell + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` + +## zod-to-json-schema 3.25.1 + +License: ISC + +Source: https://github.com/StefanTerdell/zod-to-json-schema + +```text +ISC License + +Copyright (c) 2020, Stefan Terdell + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +``` diff --git a/Server~/build/index.js b/Server~/build/index.js new file mode 100755 index 00000000..2c9cc852 --- /dev/null +++ b/Server~/build/index.js @@ -0,0 +1,36071 @@ +#!/usr/bin/env node +import { createRequire as __mcpCreateRequire } from 'node:module'; +const require = __mcpCreateRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x2) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x2, { + get: (a2, b2) => (typeof require !== "undefined" ? require : a2)[b2] +}) : x2)(function(x2) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x2 + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to2, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to2, key) && key !== except) + __defProp(to2, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to2; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js +var require_code = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/code.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = void 0; + var _CodeOrName = class { + }; + exports._CodeOrName = _CodeOrName; + exports.IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i; + var Name = class extends _CodeOrName { + constructor(s2) { + super(); + if (!exports.IDENTIFIER.test(s2)) + throw new Error("CodeGen: name must be a valid identifier"); + this.str = s2; + } + toString() { + return this.str; + } + emptyStr() { + return false; + } + get names() { + return { [this.str]: 1 }; + } + }; + exports.Name = Name; + var _Code = class extends _CodeOrName { + constructor(code) { + super(); + this._items = typeof code === "string" ? [code] : code; + } + toString() { + return this.str; + } + emptyStr() { + if (this._items.length > 1) + return false; + const item = this._items[0]; + return item === "" || item === '""'; + } + get str() { + var _a; + return (_a = this._str) !== null && _a !== void 0 ? _a : this._str = this._items.reduce((s2, c) => `${s2}${c}`, ""); + } + get names() { + var _a; + return (_a = this._names) !== null && _a !== void 0 ? _a : this._names = this._items.reduce((names, c) => { + if (c instanceof Name) + names[c.str] = (names[c.str] || 0) + 1; + return names; + }, {}); + } + }; + exports._Code = _Code; + exports.nil = new _Code(""); + function _(strs, ...args) { + const code = [strs[0]]; + let i = 0; + while (i < args.length) { + addCodeArg(code, args[i]); + code.push(strs[++i]); + } + return new _Code(code); + } + exports._ = _; + var plus = new _Code("+"); + function str(strs, ...args) { + const expr = [safeStringify(strs[0])]; + let i = 0; + while (i < args.length) { + expr.push(plus); + addCodeArg(expr, args[i]); + expr.push(plus, safeStringify(strs[++i])); + } + optimize(expr); + return new _Code(expr); + } + exports.str = str; + function addCodeArg(code, arg) { + if (arg instanceof _Code) + code.push(...arg._items); + else if (arg instanceof Name) + code.push(arg); + else + code.push(interpolate(arg)); + } + exports.addCodeArg = addCodeArg; + function optimize(expr) { + let i = 1; + while (i < expr.length - 1) { + if (expr[i] === plus) { + const res = mergeExprItems(expr[i - 1], expr[i + 1]); + if (res !== void 0) { + expr.splice(i - 1, 3, res); + continue; + } + expr[i++] = "+"; + } + i++; + } + } + function mergeExprItems(a2, b2) { + if (b2 === '""') + return a2; + if (a2 === '""') + return b2; + if (typeof a2 == "string") { + if (b2 instanceof Name || a2[a2.length - 1] !== '"') + return; + if (typeof b2 != "string") + return `${a2.slice(0, -1)}${b2}"`; + if (b2[0] === '"') + return a2.slice(0, -1) + b2.slice(1); + return; + } + if (typeof b2 == "string" && b2[0] === '"' && !(a2 instanceof Name)) + return `"${a2}${b2.slice(1)}`; + return; + } + function strConcat(c1, c2) { + return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str`${c1}${c2}`; + } + exports.strConcat = strConcat; + function interpolate(x2) { + return typeof x2 == "number" || typeof x2 == "boolean" || x2 === null ? x2 : safeStringify(Array.isArray(x2) ? x2.join(",") : x2); + } + function stringify(x2) { + return new _Code(safeStringify(x2)); + } + exports.stringify = stringify; + function safeStringify(x2) { + return JSON.stringify(x2).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029"); + } + exports.safeStringify = safeStringify; + function getProperty(key) { + return typeof key == "string" && exports.IDENTIFIER.test(key) ? new _Code(`.${key}`) : _`[${key}]`; + } + exports.getProperty = getProperty; + function getEsmExportName(key) { + if (typeof key == "string" && exports.IDENTIFIER.test(key)) { + return new _Code(`${key}`); + } + throw new Error(`CodeGen: invalid export name: ${key}, use explicit $id name mapping`); + } + exports.getEsmExportName = getEsmExportName; + function regexpCode(rx) { + return new _Code(rx.toString()); + } + exports.regexpCode = regexpCode; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/scope.js +var require_scope = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/scope.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.ValueScope = exports.ValueScopeName = exports.Scope = exports.varKinds = exports.UsedValueState = void 0; + var code_1 = require_code(); + var ValueError = class extends Error { + constructor(name) { + super(`CodeGen: "code" for ${name} not defined`); + this.value = name.value; + } + }; + var UsedValueState; + (function(UsedValueState2) { + UsedValueState2[UsedValueState2["Started"] = 0] = "Started"; + UsedValueState2[UsedValueState2["Completed"] = 1] = "Completed"; + })(UsedValueState || (exports.UsedValueState = UsedValueState = {})); + exports.varKinds = { + const: new code_1.Name("const"), + let: new code_1.Name("let"), + var: new code_1.Name("var") + }; + var Scope = class { + constructor({ prefixes, parent } = {}) { + this._names = {}; + this._prefixes = prefixes; + this._parent = parent; + } + toName(nameOrPrefix) { + return nameOrPrefix instanceof code_1.Name ? nameOrPrefix : this.name(nameOrPrefix); + } + name(prefix) { + return new code_1.Name(this._newName(prefix)); + } + _newName(prefix) { + const ng2 = this._names[prefix] || this._nameGroup(prefix); + return `${prefix}${ng2.index++}`; + } + _nameGroup(prefix) { + var _a, _b; + if (((_b = (_a = this._parent) === null || _a === void 0 ? void 0 : _a._prefixes) === null || _b === void 0 ? void 0 : _b.has(prefix)) || this._prefixes && !this._prefixes.has(prefix)) { + throw new Error(`CodeGen: prefix "${prefix}" is not allowed in this scope`); + } + return this._names[prefix] = { prefix, index: 0 }; + } + }; + exports.Scope = Scope; + var ValueScopeName = class extends code_1.Name { + constructor(prefix, nameStr) { + super(nameStr); + this.prefix = prefix; + } + setValue(value, { property, itemIndex }) { + this.value = value; + this.scopePath = (0, code_1._)`.${new code_1.Name(property)}[${itemIndex}]`; + } + }; + exports.ValueScopeName = ValueScopeName; + var line = (0, code_1._)`\n`; + var ValueScope = class extends Scope { + constructor(opts) { + super(opts); + this._values = {}; + this._scope = opts.scope; + this.opts = { ...opts, _n: opts.lines ? line : code_1.nil }; + } + get() { + return this._scope; + } + name(prefix) { + return new ValueScopeName(prefix, this._newName(prefix)); + } + value(nameOrPrefix, value) { + var _a; + if (value.ref === void 0) + throw new Error("CodeGen: ref must be passed in value"); + const name = this.toName(nameOrPrefix); + const { prefix } = name; + const valueKey = (_a = value.key) !== null && _a !== void 0 ? _a : value.ref; + let vs = this._values[prefix]; + if (vs) { + const _name = vs.get(valueKey); + if (_name) + return _name; + } else { + vs = this._values[prefix] = /* @__PURE__ */ new Map(); + } + vs.set(valueKey, name); + const s2 = this._scope[prefix] || (this._scope[prefix] = []); + const itemIndex = s2.length; + s2[itemIndex] = value.ref; + name.setValue(value, { property: prefix, itemIndex }); + return name; + } + getValue(prefix, keyOrRef) { + const vs = this._values[prefix]; + if (!vs) + return; + return vs.get(keyOrRef); + } + scopeRefs(scopeName, values = this._values) { + return this._reduceValues(values, (name) => { + if (name.scopePath === void 0) + throw new Error(`CodeGen: name "${name}" has no value`); + return (0, code_1._)`${scopeName}${name.scopePath}`; + }); + } + scopeCode(values = this._values, usedValues, getCode) { + return this._reduceValues(values, (name) => { + if (name.value === void 0) + throw new Error(`CodeGen: name "${name}" has no value`); + return name.value.code; + }, usedValues, getCode); + } + _reduceValues(values, valueCode, usedValues = {}, getCode) { + let code = code_1.nil; + for (const prefix in values) { + const vs = values[prefix]; + if (!vs) + continue; + const nameSet = usedValues[prefix] = usedValues[prefix] || /* @__PURE__ */ new Map(); + vs.forEach((name) => { + if (nameSet.has(name)) + return; + nameSet.set(name, UsedValueState.Started); + let c = valueCode(name); + if (c) { + const def = this.opts.es5 ? exports.varKinds.var : exports.varKinds.const; + code = (0, code_1._)`${code}${def} ${name} = ${c};${this.opts._n}`; + } else if (c = getCode === null || getCode === void 0 ? void 0 : getCode(name)) { + code = (0, code_1._)`${code}${c}${this.opts._n}`; + } else { + throw new ValueError(name); + } + nameSet.set(name, UsedValueState.Completed); + }); + } + return code; + } + }; + exports.ValueScope = ValueScope; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/index.js +var require_codegen = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/codegen/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.or = exports.and = exports.not = exports.CodeGen = exports.operators = exports.varKinds = exports.ValueScopeName = exports.ValueScope = exports.Scope = exports.Name = exports.regexpCode = exports.stringify = exports.getProperty = exports.nil = exports.strConcat = exports.str = exports._ = void 0; + var code_1 = require_code(); + var scope_1 = require_scope(); + var code_2 = require_code(); + Object.defineProperty(exports, "_", { enumerable: true, get: function() { + return code_2._; + } }); + Object.defineProperty(exports, "str", { enumerable: true, get: function() { + return code_2.str; + } }); + Object.defineProperty(exports, "strConcat", { enumerable: true, get: function() { + return code_2.strConcat; + } }); + Object.defineProperty(exports, "nil", { enumerable: true, get: function() { + return code_2.nil; + } }); + Object.defineProperty(exports, "getProperty", { enumerable: true, get: function() { + return code_2.getProperty; + } }); + Object.defineProperty(exports, "stringify", { enumerable: true, get: function() { + return code_2.stringify; + } }); + Object.defineProperty(exports, "regexpCode", { enumerable: true, get: function() { + return code_2.regexpCode; + } }); + Object.defineProperty(exports, "Name", { enumerable: true, get: function() { + return code_2.Name; + } }); + var scope_2 = require_scope(); + Object.defineProperty(exports, "Scope", { enumerable: true, get: function() { + return scope_2.Scope; + } }); + Object.defineProperty(exports, "ValueScope", { enumerable: true, get: function() { + return scope_2.ValueScope; + } }); + Object.defineProperty(exports, "ValueScopeName", { enumerable: true, get: function() { + return scope_2.ValueScopeName; + } }); + Object.defineProperty(exports, "varKinds", { enumerable: true, get: function() { + return scope_2.varKinds; + } }); + exports.operators = { + GT: new code_1._Code(">"), + GTE: new code_1._Code(">="), + LT: new code_1._Code("<"), + LTE: new code_1._Code("<="), + EQ: new code_1._Code("==="), + NEQ: new code_1._Code("!=="), + NOT: new code_1._Code("!"), + OR: new code_1._Code("||"), + AND: new code_1._Code("&&"), + ADD: new code_1._Code("+") + }; + var Node = class { + optimizeNodes() { + return this; + } + optimizeNames(_names, _constants) { + return this; + } + }; + var Def = class extends Node { + constructor(varKind, name, rhs) { + super(); + this.varKind = varKind; + this.name = name; + this.rhs = rhs; + } + render({ es5, _n: _n2 }) { + const varKind = es5 ? scope_1.varKinds.var : this.varKind; + const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`; + return `${varKind} ${this.name}${rhs};` + _n2; + } + optimizeNames(names, constants) { + if (!names[this.name.str]) + return; + if (this.rhs) + this.rhs = optimizeExpr(this.rhs, names, constants); + return this; + } + get names() { + return this.rhs instanceof code_1._CodeOrName ? this.rhs.names : {}; + } + }; + var Assign = class extends Node { + constructor(lhs, rhs, sideEffects) { + super(); + this.lhs = lhs; + this.rhs = rhs; + this.sideEffects = sideEffects; + } + render({ _n: _n2 }) { + return `${this.lhs} = ${this.rhs};` + _n2; + } + optimizeNames(names, constants) { + if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects) + return; + this.rhs = optimizeExpr(this.rhs, names, constants); + return this; + } + get names() { + const names = this.lhs instanceof code_1.Name ? {} : { ...this.lhs.names }; + return addExprNames(names, this.rhs); + } + }; + var AssignOp = class extends Assign { + constructor(lhs, op, rhs, sideEffects) { + super(lhs, rhs, sideEffects); + this.op = op; + } + render({ _n: _n2 }) { + return `${this.lhs} ${this.op}= ${this.rhs};` + _n2; + } + }; + var Label = class extends Node { + constructor(label) { + super(); + this.label = label; + this.names = {}; + } + render({ _n: _n2 }) { + return `${this.label}:` + _n2; + } + }; + var Break = class extends Node { + constructor(label) { + super(); + this.label = label; + this.names = {}; + } + render({ _n: _n2 }) { + const label = this.label ? ` ${this.label}` : ""; + return `break${label};` + _n2; + } + }; + var Throw = class extends Node { + constructor(error2) { + super(); + this.error = error2; + } + render({ _n: _n2 }) { + return `throw ${this.error};` + _n2; + } + get names() { + return this.error.names; + } + }; + var AnyCode = class extends Node { + constructor(code) { + super(); + this.code = code; + } + render({ _n: _n2 }) { + return `${this.code};` + _n2; + } + optimizeNodes() { + return `${this.code}` ? this : void 0; + } + optimizeNames(names, constants) { + this.code = optimizeExpr(this.code, names, constants); + return this; + } + get names() { + return this.code instanceof code_1._CodeOrName ? this.code.names : {}; + } + }; + var ParentNode = class extends Node { + constructor(nodes = []) { + super(); + this.nodes = nodes; + } + render(opts) { + return this.nodes.reduce((code, n) => code + n.render(opts), ""); + } + optimizeNodes() { + const { nodes } = this; + let i = nodes.length; + while (i--) { + const n = nodes[i].optimizeNodes(); + if (Array.isArray(n)) + nodes.splice(i, 1, ...n); + else if (n) + nodes[i] = n; + else + nodes.splice(i, 1); + } + return nodes.length > 0 ? this : void 0; + } + optimizeNames(names, constants) { + const { nodes } = this; + let i = nodes.length; + while (i--) { + const n = nodes[i]; + if (n.optimizeNames(names, constants)) + continue; + subtractNames(names, n.names); + nodes.splice(i, 1); + } + return nodes.length > 0 ? this : void 0; + } + get names() { + return this.nodes.reduce((names, n) => addNames(names, n.names), {}); + } + }; + var BlockNode = class extends ParentNode { + render(opts) { + return "{" + opts._n + super.render(opts) + "}" + opts._n; + } + }; + var Root = class extends ParentNode { + }; + var Else = class extends BlockNode { + }; + Else.kind = "else"; + var If = class _If extends BlockNode { + constructor(condition, nodes) { + super(nodes); + this.condition = condition; + } + render(opts) { + let code = `if(${this.condition})` + super.render(opts); + if (this.else) + code += "else " + this.else.render(opts); + return code; + } + optimizeNodes() { + super.optimizeNodes(); + const cond = this.condition; + if (cond === true) + return this.nodes; + let e = this.else; + if (e) { + const ns = e.optimizeNodes(); + e = this.else = Array.isArray(ns) ? new Else(ns) : ns; + } + if (e) { + if (cond === false) + return e instanceof _If ? e : e.nodes; + if (this.nodes.length) + return this; + return new _If(not(cond), e instanceof _If ? [e] : e.nodes); + } + if (cond === false || !this.nodes.length) + return void 0; + return this; + } + optimizeNames(names, constants) { + var _a; + this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants); + if (!(super.optimizeNames(names, constants) || this.else)) + return; + this.condition = optimizeExpr(this.condition, names, constants); + return this; + } + get names() { + const names = super.names; + addExprNames(names, this.condition); + if (this.else) + addNames(names, this.else.names); + return names; + } + }; + If.kind = "if"; + var For = class extends BlockNode { + }; + For.kind = "for"; + var ForLoop = class extends For { + constructor(iteration) { + super(); + this.iteration = iteration; + } + render(opts) { + return `for(${this.iteration})` + super.render(opts); + } + optimizeNames(names, constants) { + if (!super.optimizeNames(names, constants)) + return; + this.iteration = optimizeExpr(this.iteration, names, constants); + return this; + } + get names() { + return addNames(super.names, this.iteration.names); + } + }; + var ForRange = class extends For { + constructor(varKind, name, from, to2) { + super(); + this.varKind = varKind; + this.name = name; + this.from = from; + this.to = to2; + } + render(opts) { + const varKind = opts.es5 ? scope_1.varKinds.var : this.varKind; + const { name, from, to: to2 } = this; + return `for(${varKind} ${name}=${from}; ${name}<${to2}; ${name}++)` + super.render(opts); + } + get names() { + const names = addExprNames(super.names, this.from); + return addExprNames(names, this.to); + } + }; + var ForIter = class extends For { + constructor(loop, varKind, name, iterable) { + super(); + this.loop = loop; + this.varKind = varKind; + this.name = name; + this.iterable = iterable; + } + render(opts) { + return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts); + } + optimizeNames(names, constants) { + if (!super.optimizeNames(names, constants)) + return; + this.iterable = optimizeExpr(this.iterable, names, constants); + return this; + } + get names() { + return addNames(super.names, this.iterable.names); + } + }; + var Func = class extends BlockNode { + constructor(name, args, async) { + super(); + this.name = name; + this.args = args; + this.async = async; + } + render(opts) { + const _async = this.async ? "async " : ""; + return `${_async}function ${this.name}(${this.args})` + super.render(opts); + } + }; + Func.kind = "func"; + var Return = class extends ParentNode { + render(opts) { + return "return " + super.render(opts); + } + }; + Return.kind = "return"; + var Try = class extends BlockNode { + render(opts) { + let code = "try" + super.render(opts); + if (this.catch) + code += this.catch.render(opts); + if (this.finally) + code += this.finally.render(opts); + return code; + } + optimizeNodes() { + var _a, _b; + super.optimizeNodes(); + (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNodes(); + (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes(); + return this; + } + optimizeNames(names, constants) { + var _a, _b; + super.optimizeNames(names, constants); + (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants); + (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants); + return this; + } + get names() { + const names = super.names; + if (this.catch) + addNames(names, this.catch.names); + if (this.finally) + addNames(names, this.finally.names); + return names; + } + }; + var Catch = class extends BlockNode { + constructor(error2) { + super(); + this.error = error2; + } + render(opts) { + return `catch(${this.error})` + super.render(opts); + } + }; + Catch.kind = "catch"; + var Finally = class extends BlockNode { + render(opts) { + return "finally" + super.render(opts); + } + }; + Finally.kind = "finally"; + var CodeGen = class { + constructor(extScope, opts = {}) { + this._values = {}; + this._blockStarts = []; + this._constants = {}; + this.opts = { ...opts, _n: opts.lines ? "\n" : "" }; + this._extScope = extScope; + this._scope = new scope_1.Scope({ parent: extScope }); + this._nodes = [new Root()]; + } + toString() { + return this._root.render(this.opts); + } + // returns unique name in the internal scope + name(prefix) { + return this._scope.name(prefix); + } + // reserves unique name in the external scope + scopeName(prefix) { + return this._extScope.name(prefix); + } + // reserves unique name in the external scope and assigns value to it + scopeValue(prefixOrName, value) { + const name = this._extScope.value(prefixOrName, value); + const vs = this._values[name.prefix] || (this._values[name.prefix] = /* @__PURE__ */ new Set()); + vs.add(name); + return name; + } + getScopeValue(prefix, keyOrRef) { + return this._extScope.getValue(prefix, keyOrRef); + } + // return code that assigns values in the external scope to the names that are used internally + // (same names that were returned by gen.scopeName or gen.scopeValue) + scopeRefs(scopeName) { + return this._extScope.scopeRefs(scopeName, this._values); + } + scopeCode() { + return this._extScope.scopeCode(this._values); + } + _def(varKind, nameOrPrefix, rhs, constant) { + const name = this._scope.toName(nameOrPrefix); + if (rhs !== void 0 && constant) + this._constants[name.str] = rhs; + this._leafNode(new Def(varKind, name, rhs)); + return name; + } + // `const` declaration (`var` in es5 mode) + const(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.const, nameOrPrefix, rhs, _constant); + } + // `let` declaration with optional assignment (`var` in es5 mode) + let(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.let, nameOrPrefix, rhs, _constant); + } + // `var` declaration with optional assignment + var(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.var, nameOrPrefix, rhs, _constant); + } + // assignment code + assign(lhs, rhs, sideEffects) { + return this._leafNode(new Assign(lhs, rhs, sideEffects)); + } + // `+=` code + add(lhs, rhs) { + return this._leafNode(new AssignOp(lhs, exports.operators.ADD, rhs)); + } + // appends passed SafeExpr to code or executes Block + code(c) { + if (typeof c == "function") + c(); + else if (c !== code_1.nil) + this._leafNode(new AnyCode(c)); + return this; + } + // returns code for object literal for the passed argument list of key-value pairs + object(...keyValues) { + const code = ["{"]; + for (const [key, value] of keyValues) { + if (code.length > 1) + code.push(","); + code.push(key); + if (key !== value || this.opts.es5) { + code.push(":"); + (0, code_1.addCodeArg)(code, value); + } + } + code.push("}"); + return new code_1._Code(code); + } + // `if` clause (or statement if `thenBody` and, optionally, `elseBody` are passed) + if(condition, thenBody, elseBody) { + this._blockNode(new If(condition)); + if (thenBody && elseBody) { + this.code(thenBody).else().code(elseBody).endIf(); + } else if (thenBody) { + this.code(thenBody).endIf(); + } else if (elseBody) { + throw new Error('CodeGen: "else" body without "then" body'); + } + return this; + } + // `else if` clause - invalid without `if` or after `else` clauses + elseIf(condition) { + return this._elseNode(new If(condition)); + } + // `else` clause - only valid after `if` or `else if` clauses + else() { + return this._elseNode(new Else()); + } + // end `if` statement (needed if gen.if was used only with condition) + endIf() { + return this._endBlockNode(If, Else); + } + _for(node, forBody) { + this._blockNode(node); + if (forBody) + this.code(forBody).endFor(); + return this; + } + // a generic `for` clause (or statement if `forBody` is passed) + for(iteration, forBody) { + return this._for(new ForLoop(iteration), forBody); + } + // `for` statement for a range of values + forRange(nameOrPrefix, from, to2, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.let) { + const name = this._scope.toName(nameOrPrefix); + return this._for(new ForRange(varKind, name, from, to2), () => forBody(name)); + } + // `for-of` statement (in es5 mode replace with a normal for loop) + forOf(nameOrPrefix, iterable, forBody, varKind = scope_1.varKinds.const) { + const name = this._scope.toName(nameOrPrefix); + if (this.opts.es5) { + const arr = iterable instanceof code_1.Name ? iterable : this.var("_arr", iterable); + return this.forRange("_i", 0, (0, code_1._)`${arr}.length`, (i) => { + this.var(name, (0, code_1._)`${arr}[${i}]`); + forBody(name); + }); + } + return this._for(new ForIter("of", varKind, name, iterable), () => forBody(name)); + } + // `for-in` statement. + // With option `ownProperties` replaced with a `for-of` loop for object keys + forIn(nameOrPrefix, obj, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.const) { + if (this.opts.ownProperties) { + return this.forOf(nameOrPrefix, (0, code_1._)`Object.keys(${obj})`, forBody); + } + const name = this._scope.toName(nameOrPrefix); + return this._for(new ForIter("in", varKind, name, obj), () => forBody(name)); + } + // end `for` loop + endFor() { + return this._endBlockNode(For); + } + // `label` statement + label(label) { + return this._leafNode(new Label(label)); + } + // `break` statement + break(label) { + return this._leafNode(new Break(label)); + } + // `return` statement + return(value) { + const node = new Return(); + this._blockNode(node); + this.code(value); + if (node.nodes.length !== 1) + throw new Error('CodeGen: "return" should have one node'); + return this._endBlockNode(Return); + } + // `try` statement + try(tryBody, catchCode, finallyCode) { + if (!catchCode && !finallyCode) + throw new Error('CodeGen: "try" without "catch" and "finally"'); + const node = new Try(); + this._blockNode(node); + this.code(tryBody); + if (catchCode) { + const error2 = this.name("e"); + this._currNode = node.catch = new Catch(error2); + catchCode(error2); + } + if (finallyCode) { + this._currNode = node.finally = new Finally(); + this.code(finallyCode); + } + return this._endBlockNode(Catch, Finally); + } + // `throw` statement + throw(error2) { + return this._leafNode(new Throw(error2)); + } + // start self-balancing block + block(body, nodeCount) { + this._blockStarts.push(this._nodes.length); + if (body) + this.code(body).endBlock(nodeCount); + return this; + } + // end the current self-balancing block + endBlock(nodeCount) { + const len = this._blockStarts.pop(); + if (len === void 0) + throw new Error("CodeGen: not in self-balancing block"); + const toClose = this._nodes.length - len; + if (toClose < 0 || nodeCount !== void 0 && toClose !== nodeCount) { + throw new Error(`CodeGen: wrong number of nodes: ${toClose} vs ${nodeCount} expected`); + } + this._nodes.length = len; + return this; + } + // `function` heading (or definition if funcBody is passed) + func(name, args = code_1.nil, async, funcBody) { + this._blockNode(new Func(name, args, async)); + if (funcBody) + this.code(funcBody).endFunc(); + return this; + } + // end function definition + endFunc() { + return this._endBlockNode(Func); + } + optimize(n = 1) { + while (n-- > 0) { + this._root.optimizeNodes(); + this._root.optimizeNames(this._root.names, this._constants); + } + } + _leafNode(node) { + this._currNode.nodes.push(node); + return this; + } + _blockNode(node) { + this._currNode.nodes.push(node); + this._nodes.push(node); + } + _endBlockNode(N1, N2) { + const n = this._currNode; + if (n instanceof N1 || N2 && n instanceof N2) { + this._nodes.pop(); + return this; + } + throw new Error(`CodeGen: not in block "${N2 ? `${N1.kind}/${N2.kind}` : N1.kind}"`); + } + _elseNode(node) { + const n = this._currNode; + if (!(n instanceof If)) { + throw new Error('CodeGen: "else" without "if"'); + } + this._currNode = n.else = node; + return this; + } + get _root() { + return this._nodes[0]; + } + get _currNode() { + const ns = this._nodes; + return ns[ns.length - 1]; + } + set _currNode(node) { + const ns = this._nodes; + ns[ns.length - 1] = node; + } + }; + exports.CodeGen = CodeGen; + function addNames(names, from) { + for (const n in from) + names[n] = (names[n] || 0) + (from[n] || 0); + return names; + } + function addExprNames(names, from) { + return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names; + } + function optimizeExpr(expr, names, constants) { + if (expr instanceof code_1.Name) + return replaceName(expr); + if (!canOptimize(expr)) + return expr; + return new code_1._Code(expr._items.reduce((items, c) => { + if (c instanceof code_1.Name) + c = replaceName(c); + if (c instanceof code_1._Code) + items.push(...c._items); + else + items.push(c); + return items; + }, [])); + function replaceName(n) { + const c = constants[n.str]; + if (c === void 0 || names[n.str] !== 1) + return n; + delete names[n.str]; + return c; + } + function canOptimize(e) { + return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== void 0); + } + } + function subtractNames(names, from) { + for (const n in from) + names[n] = (names[n] || 0) - (from[n] || 0); + } + function not(x2) { + return typeof x2 == "boolean" || typeof x2 == "number" || x2 === null ? !x2 : (0, code_1._)`!${par(x2)}`; + } + exports.not = not; + var andCode = mappend(exports.operators.AND); + function and(...args) { + return args.reduce(andCode); + } + exports.and = and; + var orCode = mappend(exports.operators.OR); + function or2(...args) { + return args.reduce(orCode); + } + exports.or = or2; + function mappend(op) { + return (x2, y2) => x2 === code_1.nil ? y2 : y2 === code_1.nil ? x2 : (0, code_1._)`${par(x2)} ${op} ${par(y2)}`; + } + function par(x2) { + return x2 instanceof code_1.Name ? x2 : (0, code_1._)`(${x2})`; + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/util.js +var require_util = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/util.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.checkStrictMode = exports.getErrorPath = exports.Type = exports.useFunc = exports.setEvaluated = exports.evaluatedPropsToName = exports.mergeEvaluated = exports.eachItem = exports.unescapeJsonPointer = exports.escapeJsonPointer = exports.escapeFragment = exports.unescapeFragment = exports.schemaRefOrVal = exports.schemaHasRulesButRef = exports.schemaHasRules = exports.checkUnknownRules = exports.alwaysValidSchema = exports.toHash = void 0; + var codegen_1 = require_codegen(); + var code_1 = require_code(); + function toHash(arr) { + const hash = {}; + for (const item of arr) + hash[item] = true; + return hash; + } + exports.toHash = toHash; + function alwaysValidSchema(it2, schema) { + if (typeof schema == "boolean") + return schema; + if (Object.keys(schema).length === 0) + return true; + checkUnknownRules(it2, schema); + return !schemaHasRules(schema, it2.self.RULES.all); + } + exports.alwaysValidSchema = alwaysValidSchema; + function checkUnknownRules(it2, schema = it2.schema) { + const { opts, self } = it2; + if (!opts.strictSchema) + return; + if (typeof schema === "boolean") + return; + const rules = self.RULES.keywords; + for (const key in schema) { + if (!rules[key]) + checkStrictMode(it2, `unknown keyword: "${key}"`); + } + } + exports.checkUnknownRules = checkUnknownRules; + function schemaHasRules(schema, rules) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (rules[key]) + return true; + return false; + } + exports.schemaHasRules = schemaHasRules; + function schemaHasRulesButRef(schema, RULES) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (key !== "$ref" && RULES.all[key]) + return true; + return false; + } + exports.schemaHasRulesButRef = schemaHasRulesButRef; + function schemaRefOrVal({ topSchemaRef, schemaPath }, schema, keyword, $data) { + if (!$data) { + if (typeof schema == "number" || typeof schema == "boolean") + return schema; + if (typeof schema == "string") + return (0, codegen_1._)`${schema}`; + } + return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`; + } + exports.schemaRefOrVal = schemaRefOrVal; + function unescapeFragment(str) { + return unescapeJsonPointer(decodeURIComponent(str)); + } + exports.unescapeFragment = unescapeFragment; + function escapeFragment(str) { + return encodeURIComponent(escapeJsonPointer(str)); + } + exports.escapeFragment = escapeFragment; + function escapeJsonPointer(str) { + if (typeof str == "number") + return `${str}`; + return str.replace(/~/g, "~0").replace(/\//g, "~1"); + } + exports.escapeJsonPointer = escapeJsonPointer; + function unescapeJsonPointer(str) { + return str.replace(/~1/g, "/").replace(/~0/g, "~"); + } + exports.unescapeJsonPointer = unescapeJsonPointer; + function eachItem(xs, f2) { + if (Array.isArray(xs)) { + for (const x2 of xs) + f2(x2); + } else { + f2(xs); + } + } + exports.eachItem = eachItem; + function makeMergeEvaluated({ mergeNames, mergeToName, mergeValues: mergeValues3, resultToName }) { + return (gen, from, to2, toName) => { + const res = to2 === void 0 ? from : to2 instanceof codegen_1.Name ? (from instanceof codegen_1.Name ? mergeNames(gen, from, to2) : mergeToName(gen, from, to2), to2) : from instanceof codegen_1.Name ? (mergeToName(gen, to2, from), from) : mergeValues3(from, to2); + return toName === codegen_1.Name && !(res instanceof codegen_1.Name) ? resultToName(gen, res) : res; + }; + } + exports.mergeEvaluated = { + props: makeMergeEvaluated({ + mergeNames: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true && ${from} !== undefined`, () => { + gen.if((0, codegen_1._)`${from} === true`, () => gen.assign(to2, true), () => gen.assign(to2, (0, codegen_1._)`${to2} || {}`).code((0, codegen_1._)`Object.assign(${to2}, ${from})`)); + }), + mergeToName: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true`, () => { + if (from === true) { + gen.assign(to2, true); + } else { + gen.assign(to2, (0, codegen_1._)`${to2} || {}`); + setEvaluated(gen, to2, from); + } + }), + mergeValues: (from, to2) => from === true ? true : { ...from, ...to2 }, + resultToName: evaluatedPropsToName + }), + items: makeMergeEvaluated({ + mergeNames: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true && ${from} !== undefined`, () => gen.assign(to2, (0, codegen_1._)`${from} === true ? true : ${to2} > ${from} ? ${to2} : ${from}`)), + mergeToName: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true`, () => gen.assign(to2, from === true ? true : (0, codegen_1._)`${to2} > ${from} ? ${to2} : ${from}`)), + mergeValues: (from, to2) => from === true ? true : Math.max(from, to2), + resultToName: (gen, items) => gen.var("items", items) + }) + }; + function evaluatedPropsToName(gen, ps) { + if (ps === true) + return gen.var("props", true); + const props = gen.var("props", (0, codegen_1._)`{}`); + if (ps !== void 0) + setEvaluated(gen, props, ps); + return props; + } + exports.evaluatedPropsToName = evaluatedPropsToName; + function setEvaluated(gen, props, ps) { + Object.keys(ps).forEach((p2) => gen.assign((0, codegen_1._)`${props}${(0, codegen_1.getProperty)(p2)}`, true)); + } + exports.setEvaluated = setEvaluated; + var snippets = {}; + function useFunc(gen, f2) { + return gen.scopeValue("func", { + ref: f2, + code: snippets[f2.code] || (snippets[f2.code] = new code_1._Code(f2.code)) + }); + } + exports.useFunc = useFunc; + var Type; + (function(Type2) { + Type2[Type2["Num"] = 0] = "Num"; + Type2[Type2["Str"] = 1] = "Str"; + })(Type || (exports.Type = Type = {})); + function getErrorPath(dataProp, dataPropType, jsPropertySyntax) { + if (dataProp instanceof codegen_1.Name) { + const isNumber = dataPropType === Type.Num; + return jsPropertySyntax ? isNumber ? (0, codegen_1._)`"[" + ${dataProp} + "]"` : (0, codegen_1._)`"['" + ${dataProp} + "']"` : isNumber ? (0, codegen_1._)`"/" + ${dataProp}` : (0, codegen_1._)`"/" + ${dataProp}.replace(/~/g, "~0").replace(/\\//g, "~1")`; + } + return jsPropertySyntax ? (0, codegen_1.getProperty)(dataProp).toString() : "/" + escapeJsonPointer(dataProp); + } + exports.getErrorPath = getErrorPath; + function checkStrictMode(it2, msg, mode = it2.opts.strictSchema) { + if (!mode) + return; + msg = `strict mode: ${msg}`; + if (mode === true) + throw new Error(msg); + it2.self.logger.warn(msg); + } + exports.checkStrictMode = checkStrictMode; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/names.js +var require_names = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/names.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var names = { + // validation function arguments + data: new codegen_1.Name("data"), + // data passed to validation function + // args passed from referencing schema + valCxt: new codegen_1.Name("valCxt"), + // validation/data context - should not be used directly, it is destructured to the names below + instancePath: new codegen_1.Name("instancePath"), + parentData: new codegen_1.Name("parentData"), + parentDataProperty: new codegen_1.Name("parentDataProperty"), + rootData: new codegen_1.Name("rootData"), + // root data - same as the data passed to the first/top validation function + dynamicAnchors: new codegen_1.Name("dynamicAnchors"), + // used to support recursiveRef and dynamicRef + // function scoped variables + vErrors: new codegen_1.Name("vErrors"), + // null or array of validation errors + errors: new codegen_1.Name("errors"), + // counter of validation errors + this: new codegen_1.Name("this"), + // "globals" + self: new codegen_1.Name("self"), + scope: new codegen_1.Name("scope"), + // JTD serialize/parse name for JSON string and position + json: new codegen_1.Name("json"), + jsonPos: new codegen_1.Name("jsonPos"), + jsonLen: new codegen_1.Name("jsonLen"), + jsonPart: new codegen_1.Name("jsonPart") + }; + exports.default = names; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/errors.js +var require_errors = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/errors.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.extendErrors = exports.resetErrorsCount = exports.reportExtraError = exports.reportError = exports.keyword$DataError = exports.keywordError = void 0; + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var names_1 = require_names(); + exports.keywordError = { + message: ({ keyword }) => (0, codegen_1.str)`must pass "${keyword}" keyword validation` + }; + exports.keyword$DataError = { + message: ({ keyword, schemaType }) => schemaType ? (0, codegen_1.str)`"${keyword}" keyword must be ${schemaType} ($data)` : (0, codegen_1.str)`"${keyword}" keyword is invalid ($data)` + }; + function reportError(cxt, error2 = exports.keywordError, errorPaths, overrideAllErrors) { + const { it: it2 } = cxt; + const { gen, compositeRule, allErrors } = it2; + const errObj = errorObjectCode(cxt, error2, errorPaths); + if (overrideAllErrors !== null && overrideAllErrors !== void 0 ? overrideAllErrors : compositeRule || allErrors) { + addError(gen, errObj); + } else { + returnErrors(it2, (0, codegen_1._)`[${errObj}]`); + } + } + exports.reportError = reportError; + function reportExtraError(cxt, error2 = exports.keywordError, errorPaths) { + const { it: it2 } = cxt; + const { gen, compositeRule, allErrors } = it2; + const errObj = errorObjectCode(cxt, error2, errorPaths); + addError(gen, errObj); + if (!(compositeRule || allErrors)) { + returnErrors(it2, names_1.default.vErrors); + } + } + exports.reportExtraError = reportExtraError; + function resetErrorsCount(gen, errsCount) { + gen.assign(names_1.default.errors, errsCount); + gen.if((0, codegen_1._)`${names_1.default.vErrors} !== null`, () => gen.if(errsCount, () => gen.assign((0, codegen_1._)`${names_1.default.vErrors}.length`, errsCount), () => gen.assign(names_1.default.vErrors, null))); + } + exports.resetErrorsCount = resetErrorsCount; + function extendErrors({ gen, keyword, schemaValue, data, errsCount, it: it2 }) { + if (errsCount === void 0) + throw new Error("ajv implementation error"); + const err = gen.name("err"); + gen.forRange("i", errsCount, names_1.default.errors, (i) => { + gen.const(err, (0, codegen_1._)`${names_1.default.vErrors}[${i}]`); + gen.if((0, codegen_1._)`${err}.instancePath === undefined`, () => gen.assign((0, codegen_1._)`${err}.instancePath`, (0, codegen_1.strConcat)(names_1.default.instancePath, it2.errorPath))); + gen.assign((0, codegen_1._)`${err}.schemaPath`, (0, codegen_1.str)`${it2.errSchemaPath}/${keyword}`); + if (it2.opts.verbose) { + gen.assign((0, codegen_1._)`${err}.schema`, schemaValue); + gen.assign((0, codegen_1._)`${err}.data`, data); + } + }); + } + exports.extendErrors = extendErrors; + function addError(gen, errObj) { + const err = gen.const("err", errObj); + gen.if((0, codegen_1._)`${names_1.default.vErrors} === null`, () => gen.assign(names_1.default.vErrors, (0, codegen_1._)`[${err}]`), (0, codegen_1._)`${names_1.default.vErrors}.push(${err})`); + gen.code((0, codegen_1._)`${names_1.default.errors}++`); + } + function returnErrors(it2, errs) { + const { gen, validateName, schemaEnv } = it2; + if (schemaEnv.$async) { + gen.throw((0, codegen_1._)`new ${it2.ValidationError}(${errs})`); + } else { + gen.assign((0, codegen_1._)`${validateName}.errors`, errs); + gen.return(false); + } + } + var E2 = { + keyword: new codegen_1.Name("keyword"), + schemaPath: new codegen_1.Name("schemaPath"), + // also used in JTD errors + params: new codegen_1.Name("params"), + propertyName: new codegen_1.Name("propertyName"), + message: new codegen_1.Name("message"), + schema: new codegen_1.Name("schema"), + parentSchema: new codegen_1.Name("parentSchema") + }; + function errorObjectCode(cxt, error2, errorPaths) { + const { createErrors } = cxt.it; + if (createErrors === false) + return (0, codegen_1._)`{}`; + return errorObject(cxt, error2, errorPaths); + } + function errorObject(cxt, error2, errorPaths = {}) { + const { gen, it: it2 } = cxt; + const keyValues = [ + errorInstancePath(it2, errorPaths), + errorSchemaPath(cxt, errorPaths) + ]; + extraErrorProps(cxt, error2, keyValues); + return gen.object(...keyValues); + } + function errorInstancePath({ errorPath }, { instancePath }) { + const instPath = instancePath ? (0, codegen_1.str)`${errorPath}${(0, util_1.getErrorPath)(instancePath, util_1.Type.Str)}` : errorPath; + return [names_1.default.instancePath, (0, codegen_1.strConcat)(names_1.default.instancePath, instPath)]; + } + function errorSchemaPath({ keyword, it: { errSchemaPath } }, { schemaPath, parentSchema }) { + let schPath = parentSchema ? errSchemaPath : (0, codegen_1.str)`${errSchemaPath}/${keyword}`; + if (schemaPath) { + schPath = (0, codegen_1.str)`${schPath}${(0, util_1.getErrorPath)(schemaPath, util_1.Type.Str)}`; + } + return [E2.schemaPath, schPath]; + } + function extraErrorProps(cxt, { params, message }, keyValues) { + const { keyword, data, schemaValue, it: it2 } = cxt; + const { opts, propertyName, topSchemaRef, schemaPath } = it2; + keyValues.push([E2.keyword, keyword], [E2.params, typeof params == "function" ? params(cxt) : params || (0, codegen_1._)`{}`]); + if (opts.messages) { + keyValues.push([E2.message, typeof message == "function" ? message(cxt) : message]); + } + if (opts.verbose) { + keyValues.push([E2.schema, schemaValue], [E2.parentSchema, (0, codegen_1._)`${topSchemaRef}${schemaPath}`], [names_1.default.data, data]); + } + if (propertyName) + keyValues.push([E2.propertyName, propertyName]); + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/boolSchema.js +var require_boolSchema = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/boolSchema.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.boolOrEmptySchema = exports.topBoolOrEmptySchema = void 0; + var errors_1 = require_errors(); + var codegen_1 = require_codegen(); + var names_1 = require_names(); + var boolError = { + message: "boolean schema is false" + }; + function topBoolOrEmptySchema(it2) { + const { gen, schema, validateName } = it2; + if (schema === false) { + falseSchemaError(it2, false); + } else if (typeof schema == "object" && schema.$async === true) { + gen.return(names_1.default.data); + } else { + gen.assign((0, codegen_1._)`${validateName}.errors`, null); + gen.return(true); + } + } + exports.topBoolOrEmptySchema = topBoolOrEmptySchema; + function boolOrEmptySchema(it2, valid) { + const { gen, schema } = it2; + if (schema === false) { + gen.var(valid, false); + falseSchemaError(it2); + } else { + gen.var(valid, true); + } + } + exports.boolOrEmptySchema = boolOrEmptySchema; + function falseSchemaError(it2, overrideAllErrors) { + const { gen, data } = it2; + const cxt = { + gen, + keyword: "false schema", + data, + schema: false, + schemaCode: false, + schemaValue: false, + params: {}, + it: it2 + }; + (0, errors_1.reportError)(cxt, boolError, void 0, overrideAllErrors); + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/rules.js +var require_rules = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/rules.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getRules = exports.isJSONType = void 0; + var _jsonTypes = ["string", "number", "integer", "boolean", "null", "object", "array"]; + var jsonTypes = new Set(_jsonTypes); + function isJSONType(x2) { + return typeof x2 == "string" && jsonTypes.has(x2); + } + exports.isJSONType = isJSONType; + function getRules() { + const groups = { + number: { type: "number", rules: [] }, + string: { type: "string", rules: [] }, + array: { type: "array", rules: [] }, + object: { type: "object", rules: [] } + }; + return { + types: { ...groups, integer: true, boolean: true, null: true }, + rules: [{ rules: [] }, groups.number, groups.string, groups.array, groups.object], + post: { rules: [] }, + all: {}, + keywords: {} + }; + } + exports.getRules = getRules; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/applicability.js +var require_applicability = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/applicability.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.shouldUseRule = exports.shouldUseGroup = exports.schemaHasRulesForType = void 0; + function schemaHasRulesForType({ schema, self }, type) { + const group = self.RULES.types[type]; + return group && group !== true && shouldUseGroup(schema, group); + } + exports.schemaHasRulesForType = schemaHasRulesForType; + function shouldUseGroup(schema, group) { + return group.rules.some((rule) => shouldUseRule(schema, rule)); + } + exports.shouldUseGroup = shouldUseGroup; + function shouldUseRule(schema, rule) { + var _a; + return schema[rule.keyword] !== void 0 || ((_a = rule.definition.implements) === null || _a === void 0 ? void 0 : _a.some((kwd) => schema[kwd] !== void 0)); + } + exports.shouldUseRule = shouldUseRule; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/dataType.js +var require_dataType = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/dataType.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = void 0; + var rules_1 = require_rules(); + var applicability_1 = require_applicability(); + var errors_1 = require_errors(); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var DataType; + (function(DataType2) { + DataType2[DataType2["Correct"] = 0] = "Correct"; + DataType2[DataType2["Wrong"] = 1] = "Wrong"; + })(DataType || (exports.DataType = DataType = {})); + function getSchemaTypes(schema) { + const types = getJSONTypes(schema.type); + const hasNull = types.includes("null"); + if (hasNull) { + if (schema.nullable === false) + throw new Error("type: null contradicts nullable: false"); + } else { + if (!types.length && schema.nullable !== void 0) { + throw new Error('"nullable" cannot be used without "type"'); + } + if (schema.nullable === true) + types.push("null"); + } + return types; + } + exports.getSchemaTypes = getSchemaTypes; + function getJSONTypes(ts) { + const types = Array.isArray(ts) ? ts : ts ? [ts] : []; + if (types.every(rules_1.isJSONType)) + return types; + throw new Error("type must be JSONType or JSONType[]: " + types.join(",")); + } + exports.getJSONTypes = getJSONTypes; + function coerceAndCheckDataType(it2, types) { + const { gen, data, opts } = it2; + const coerceTo = coerceToTypes(types, opts.coerceTypes); + const checkTypes = types.length > 0 && !(coerceTo.length === 0 && types.length === 1 && (0, applicability_1.schemaHasRulesForType)(it2, types[0])); + if (checkTypes) { + const wrongType = checkDataTypes(types, data, opts.strictNumbers, DataType.Wrong); + gen.if(wrongType, () => { + if (coerceTo.length) + coerceData(it2, types, coerceTo); + else + reportTypeError(it2); + }); + } + return checkTypes; + } + exports.coerceAndCheckDataType = coerceAndCheckDataType; + var COERCIBLE = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null"]); + function coerceToTypes(types, coerceTypes) { + return coerceTypes ? types.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : []; + } + function coerceData(it2, types, coerceTo) { + const { gen, data, opts } = it2; + const dataType = gen.let("dataType", (0, codegen_1._)`typeof ${data}`); + const coerced = gen.let("coerced", (0, codegen_1._)`undefined`); + if (opts.coerceTypes === "array") { + gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data))); + } + gen.if((0, codegen_1._)`${coerced} !== undefined`); + for (const t of coerceTo) { + if (COERCIBLE.has(t) || t === "array" && opts.coerceTypes === "array") { + coerceSpecificType(t); + } + } + gen.else(); + reportTypeError(it2); + gen.endIf(); + gen.if((0, codegen_1._)`${coerced} !== undefined`, () => { + gen.assign(data, coerced); + assignParentData(it2, coerced); + }); + function coerceSpecificType(t) { + switch (t) { + case "string": + gen.elseIf((0, codegen_1._)`${dataType} == "number" || ${dataType} == "boolean"`).assign(coerced, (0, codegen_1._)`"" + ${data}`).elseIf((0, codegen_1._)`${data} === null`).assign(coerced, (0, codegen_1._)`""`); + return; + case "number": + gen.elseIf((0, codegen_1._)`${dataType} == "boolean" || ${data} === null + || (${dataType} == "string" && ${data} && ${data} == +${data})`).assign(coerced, (0, codegen_1._)`+${data}`); + return; + case "integer": + gen.elseIf((0, codegen_1._)`${dataType} === "boolean" || ${data} === null + || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))`).assign(coerced, (0, codegen_1._)`+${data}`); + return; + case "boolean": + gen.elseIf((0, codegen_1._)`${data} === "false" || ${data} === 0 || ${data} === null`).assign(coerced, false).elseIf((0, codegen_1._)`${data} === "true" || ${data} === 1`).assign(coerced, true); + return; + case "null": + gen.elseIf((0, codegen_1._)`${data} === "" || ${data} === 0 || ${data} === false`); + gen.assign(coerced, null); + return; + case "array": + gen.elseIf((0, codegen_1._)`${dataType} === "string" || ${dataType} === "number" + || ${dataType} === "boolean" || ${data} === null`).assign(coerced, (0, codegen_1._)`[${data}]`); + } + } + } + function assignParentData({ gen, parentData, parentDataProperty }, expr) { + gen.if((0, codegen_1._)`${parentData} !== undefined`, () => gen.assign((0, codegen_1._)`${parentData}[${parentDataProperty}]`, expr)); + } + function checkDataType(dataType, data, strictNums, correct = DataType.Correct) { + const EQ = correct === DataType.Correct ? codegen_1.operators.EQ : codegen_1.operators.NEQ; + let cond; + switch (dataType) { + case "null": + return (0, codegen_1._)`${data} ${EQ} null`; + case "array": + cond = (0, codegen_1._)`Array.isArray(${data})`; + break; + case "object": + cond = (0, codegen_1._)`${data} && typeof ${data} == "object" && !Array.isArray(${data})`; + break; + case "integer": + cond = numCond((0, codegen_1._)`!(${data} % 1) && !isNaN(${data})`); + break; + case "number": + cond = numCond(); + break; + default: + return (0, codegen_1._)`typeof ${data} ${EQ} ${dataType}`; + } + return correct === DataType.Correct ? cond : (0, codegen_1.not)(cond); + function numCond(_cond = codegen_1.nil) { + return (0, codegen_1.and)((0, codegen_1._)`typeof ${data} == "number"`, _cond, strictNums ? (0, codegen_1._)`isFinite(${data})` : codegen_1.nil); + } + } + exports.checkDataType = checkDataType; + function checkDataTypes(dataTypes, data, strictNums, correct) { + if (dataTypes.length === 1) { + return checkDataType(dataTypes[0], data, strictNums, correct); + } + let cond; + const types = (0, util_1.toHash)(dataTypes); + if (types.array && types.object) { + const notObj = (0, codegen_1._)`typeof ${data} != "object"`; + cond = types.null ? notObj : (0, codegen_1._)`!${data} || ${notObj}`; + delete types.null; + delete types.array; + delete types.object; + } else { + cond = codegen_1.nil; + } + if (types.number) + delete types.integer; + for (const t in types) + cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct)); + return cond; + } + exports.checkDataTypes = checkDataTypes; + var typeError = { + message: ({ schema }) => `must be ${schema}`, + params: ({ schema, schemaValue }) => typeof schema == "string" ? (0, codegen_1._)`{type: ${schema}}` : (0, codegen_1._)`{type: ${schemaValue}}` + }; + function reportTypeError(it2) { + const cxt = getTypeErrorContext(it2); + (0, errors_1.reportError)(cxt, typeError); + } + exports.reportTypeError = reportTypeError; + function getTypeErrorContext(it2) { + const { gen, data, schema } = it2; + const schemaCode = (0, util_1.schemaRefOrVal)(it2, schema, "type"); + return { + gen, + keyword: "type", + data, + schema: schema.type, + schemaCode, + schemaValue: schemaCode, + parentSchema: schema, + params: {}, + it: it2 + }; + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/defaults.js +var require_defaults = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/defaults.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.assignDefaults = void 0; + var codegen_1 = require_codegen(); + var util_1 = require_util(); + function assignDefaults(it2, ty) { + const { properties, items } = it2.schema; + if (ty === "object" && properties) { + for (const key in properties) { + assignDefault(it2, key, properties[key].default); + } + } else if (ty === "array" && Array.isArray(items)) { + items.forEach((sch, i) => assignDefault(it2, i, sch.default)); + } + } + exports.assignDefaults = assignDefaults; + function assignDefault(it2, prop, defaultValue) { + const { gen, compositeRule, data, opts } = it2; + if (defaultValue === void 0) + return; + const childData = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(prop)}`; + if (compositeRule) { + (0, util_1.checkStrictMode)(it2, `default is ignored for: ${childData}`); + return; + } + let condition = (0, codegen_1._)`${childData} === undefined`; + if (opts.useDefaults === "empty") { + condition = (0, codegen_1._)`${condition} || ${childData} === null || ${childData} === ""`; + } + gen.if(condition, (0, codegen_1._)`${childData} = ${(0, codegen_1.stringify)(defaultValue)}`); + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/code.js +var require_code2 = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/code.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.isOwnProperty = exports.hasPropFunc = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = void 0; + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var names_1 = require_names(); + var util_2 = require_util(); + function checkReportMissingProp(cxt, prop) { + const { gen, data, it: it2 } = cxt; + gen.if(noPropertyInData(gen, data, prop, it2.opts.ownProperties), () => { + cxt.setParams({ missingProperty: (0, codegen_1._)`${prop}` }, true); + cxt.error(); + }); + } + exports.checkReportMissingProp = checkReportMissingProp; + function checkMissingProp({ gen, data, it: { opts } }, properties, missing) { + return (0, codegen_1.or)(...properties.map((prop) => (0, codegen_1.and)(noPropertyInData(gen, data, prop, opts.ownProperties), (0, codegen_1._)`${missing} = ${prop}`))); + } + exports.checkMissingProp = checkMissingProp; + function reportMissingProp(cxt, missing) { + cxt.setParams({ missingProperty: missing }, true); + cxt.error(); + } + exports.reportMissingProp = reportMissingProp; + function hasPropFunc(gen) { + return gen.scopeValue("func", { + // eslint-disable-next-line @typescript-eslint/unbound-method + ref: Object.prototype.hasOwnProperty, + code: (0, codegen_1._)`Object.prototype.hasOwnProperty` + }); + } + exports.hasPropFunc = hasPropFunc; + function isOwnProperty(gen, data, property) { + return (0, codegen_1._)`${hasPropFunc(gen)}.call(${data}, ${property})`; + } + exports.isOwnProperty = isOwnProperty; + function propertyInData(gen, data, property, ownProperties) { + const cond = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(property)} !== undefined`; + return ownProperties ? (0, codegen_1._)`${cond} && ${isOwnProperty(gen, data, property)}` : cond; + } + exports.propertyInData = propertyInData; + function noPropertyInData(gen, data, property, ownProperties) { + const cond = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(property)} === undefined`; + return ownProperties ? (0, codegen_1.or)(cond, (0, codegen_1.not)(isOwnProperty(gen, data, property))) : cond; + } + exports.noPropertyInData = noPropertyInData; + function allSchemaProperties(schemaMap) { + return schemaMap ? Object.keys(schemaMap).filter((p2) => p2 !== "__proto__") : []; + } + exports.allSchemaProperties = allSchemaProperties; + function schemaProperties(it2, schemaMap) { + return allSchemaProperties(schemaMap).filter((p2) => !(0, util_1.alwaysValidSchema)(it2, schemaMap[p2])); + } + exports.schemaProperties = schemaProperties; + function callValidateCode({ schemaCode, data, it: { gen, topSchemaRef, schemaPath, errorPath }, it: it2 }, func, context, passSchema) { + const dataAndSchema = passSchema ? (0, codegen_1._)`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data; + const valCxt = [ + [names_1.default.instancePath, (0, codegen_1.strConcat)(names_1.default.instancePath, errorPath)], + [names_1.default.parentData, it2.parentData], + [names_1.default.parentDataProperty, it2.parentDataProperty], + [names_1.default.rootData, names_1.default.rootData] + ]; + if (it2.opts.dynamicRef) + valCxt.push([names_1.default.dynamicAnchors, names_1.default.dynamicAnchors]); + const args = (0, codegen_1._)`${dataAndSchema}, ${gen.object(...valCxt)}`; + return context !== codegen_1.nil ? (0, codegen_1._)`${func}.call(${context}, ${args})` : (0, codegen_1._)`${func}(${args})`; + } + exports.callValidateCode = callValidateCode; + var newRegExp = (0, codegen_1._)`new RegExp`; + function usePattern({ gen, it: { opts } }, pattern) { + const u = opts.unicodeRegExp ? "u" : ""; + const { regExp } = opts.code; + const rx = regExp(pattern, u); + return gen.scopeValue("pattern", { + key: rx.toString(), + ref: rx, + code: (0, codegen_1._)`${regExp.code === "new RegExp" ? newRegExp : (0, util_2.useFunc)(gen, regExp)}(${pattern}, ${u})` + }); + } + exports.usePattern = usePattern; + function validateArray(cxt) { + const { gen, data, keyword, it: it2 } = cxt; + const valid = gen.name("valid"); + if (it2.allErrors) { + const validArr = gen.let("valid", true); + validateItems(() => gen.assign(validArr, false)); + return validArr; + } + gen.var(valid, true); + validateItems(() => gen.break()); + return valid; + function validateItems(notValid) { + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + gen.forRange("i", 0, len, (i) => { + cxt.subschema({ + keyword, + dataProp: i, + dataPropType: util_1.Type.Num + }, valid); + gen.if((0, codegen_1.not)(valid), notValid); + }); + } + } + exports.validateArray = validateArray; + function validateUnion(cxt) { + const { gen, schema, keyword, it: it2 } = cxt; + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const alwaysValid = schema.some((sch) => (0, util_1.alwaysValidSchema)(it2, sch)); + if (alwaysValid && !it2.opts.unevaluated) + return; + const valid = gen.let("valid", false); + const schValid = gen.name("_valid"); + gen.block(() => schema.forEach((_sch, i) => { + const schCxt = cxt.subschema({ + keyword, + schemaProp: i, + compositeRule: true + }, schValid); + gen.assign(valid, (0, codegen_1._)`${valid} || ${schValid}`); + const merged = cxt.mergeValidEvaluated(schCxt, schValid); + if (!merged) + gen.if((0, codegen_1.not)(valid)); + })); + cxt.result(valid, () => cxt.reset(), () => cxt.error(true)); + } + exports.validateUnion = validateUnion; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/keyword.js +var require_keyword = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/keyword.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateKeywordUsage = exports.validSchemaType = exports.funcKeywordCode = exports.macroKeywordCode = void 0; + var codegen_1 = require_codegen(); + var names_1 = require_names(); + var code_1 = require_code2(); + var errors_1 = require_errors(); + function macroKeywordCode(cxt, def) { + const { gen, keyword, schema, parentSchema, it: it2 } = cxt; + const macroSchema = def.macro.call(it2.self, schema, parentSchema, it2); + const schemaRef = useKeyword(gen, keyword, macroSchema); + if (it2.opts.validateSchema !== false) + it2.self.validateSchema(macroSchema, true); + const valid = gen.name("valid"); + cxt.subschema({ + schema: macroSchema, + schemaPath: codegen_1.nil, + errSchemaPath: `${it2.errSchemaPath}/${keyword}`, + topSchemaRef: schemaRef, + compositeRule: true + }, valid); + cxt.pass(valid, () => cxt.error(true)); + } + exports.macroKeywordCode = macroKeywordCode; + function funcKeywordCode(cxt, def) { + var _a; + const { gen, keyword, schema, parentSchema, $data, it: it2 } = cxt; + checkAsyncKeyword(it2, def); + const validate = !$data && def.compile ? def.compile.call(it2.self, schema, parentSchema, it2) : def.validate; + const validateRef = useKeyword(gen, keyword, validate); + const valid = gen.let("valid"); + cxt.block$data(valid, validateKeyword); + cxt.ok((_a = def.valid) !== null && _a !== void 0 ? _a : valid); + function validateKeyword() { + if (def.errors === false) { + assignValid(); + if (def.modifying) + modifyData(cxt); + reportErrs(() => cxt.error()); + } else { + const ruleErrs = def.async ? validateAsync() : validateSync(); + if (def.modifying) + modifyData(cxt); + reportErrs(() => addErrs(cxt, ruleErrs)); + } + } + function validateAsync() { + const ruleErrs = gen.let("ruleErrs", null); + gen.try(() => assignValid((0, codegen_1._)`await `), (e) => gen.assign(valid, false).if((0, codegen_1._)`${e} instanceof ${it2.ValidationError}`, () => gen.assign(ruleErrs, (0, codegen_1._)`${e}.errors`), () => gen.throw(e))); + return ruleErrs; + } + function validateSync() { + const validateErrs = (0, codegen_1._)`${validateRef}.errors`; + gen.assign(validateErrs, null); + assignValid(codegen_1.nil); + return validateErrs; + } + function assignValid(_await = def.async ? (0, codegen_1._)`await ` : codegen_1.nil) { + const passCxt = it2.opts.passContext ? names_1.default.this : names_1.default.self; + const passSchema = !("compile" in def && !$data || def.schema === false); + gen.assign(valid, (0, codegen_1._)`${_await}${(0, code_1.callValidateCode)(cxt, validateRef, passCxt, passSchema)}`, def.modifying); + } + function reportErrs(errors) { + var _a2; + gen.if((0, codegen_1.not)((_a2 = def.valid) !== null && _a2 !== void 0 ? _a2 : valid), errors); + } + } + exports.funcKeywordCode = funcKeywordCode; + function modifyData(cxt) { + const { gen, data, it: it2 } = cxt; + gen.if(it2.parentData, () => gen.assign(data, (0, codegen_1._)`${it2.parentData}[${it2.parentDataProperty}]`)); + } + function addErrs(cxt, errs) { + const { gen } = cxt; + gen.if((0, codegen_1._)`Array.isArray(${errs})`, () => { + gen.assign(names_1.default.vErrors, (0, codegen_1._)`${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`).assign(names_1.default.errors, (0, codegen_1._)`${names_1.default.vErrors}.length`); + (0, errors_1.extendErrors)(cxt); + }, () => cxt.error()); + } + function checkAsyncKeyword({ schemaEnv }, def) { + if (def.async && !schemaEnv.$async) + throw new Error("async keyword in sync schema"); + } + function useKeyword(gen, keyword, result) { + if (result === void 0) + throw new Error(`keyword "${keyword}" failed to compile`); + return gen.scopeValue("keyword", typeof result == "function" ? { ref: result } : { ref: result, code: (0, codegen_1.stringify)(result) }); + } + function validSchemaType(schema, schemaType, allowUndefined = false) { + return !schemaType.length || schemaType.some((st2) => st2 === "array" ? Array.isArray(schema) : st2 === "object" ? schema && typeof schema == "object" && !Array.isArray(schema) : typeof schema == st2 || allowUndefined && typeof schema == "undefined"); + } + exports.validSchemaType = validSchemaType; + function validateKeywordUsage({ schema, opts, self, errSchemaPath }, def, keyword) { + if (Array.isArray(def.keyword) ? !def.keyword.includes(keyword) : def.keyword !== keyword) { + throw new Error("ajv implementation error"); + } + const deps = def.dependencies; + if (deps === null || deps === void 0 ? void 0 : deps.some((kwd) => !Object.prototype.hasOwnProperty.call(schema, kwd))) { + throw new Error(`parent schema must have dependencies of ${keyword}: ${deps.join(",")}`); + } + if (def.validateSchema) { + const valid = def.validateSchema(schema[keyword]); + if (!valid) { + const msg = `keyword "${keyword}" value is invalid at path "${errSchemaPath}": ` + self.errorsText(def.validateSchema.errors); + if (opts.validateSchema === "log") + self.logger.error(msg); + else + throw new Error(msg); + } + } + } + exports.validateKeywordUsage = validateKeywordUsage; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/subschema.js +var require_subschema = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/subschema.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.extendSubschemaMode = exports.extendSubschemaData = exports.getSubschema = void 0; + var codegen_1 = require_codegen(); + var util_1 = require_util(); + function getSubschema(it2, { keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef }) { + if (keyword !== void 0 && schema !== void 0) { + throw new Error('both "keyword" and "schema" passed, only one allowed'); + } + if (keyword !== void 0) { + const sch = it2.schema[keyword]; + return schemaProp === void 0 ? { + schema: sch, + schemaPath: (0, codegen_1._)`${it2.schemaPath}${(0, codegen_1.getProperty)(keyword)}`, + errSchemaPath: `${it2.errSchemaPath}/${keyword}` + } : { + schema: sch[schemaProp], + schemaPath: (0, codegen_1._)`${it2.schemaPath}${(0, codegen_1.getProperty)(keyword)}${(0, codegen_1.getProperty)(schemaProp)}`, + errSchemaPath: `${it2.errSchemaPath}/${keyword}/${(0, util_1.escapeFragment)(schemaProp)}` + }; + } + if (schema !== void 0) { + if (schemaPath === void 0 || errSchemaPath === void 0 || topSchemaRef === void 0) { + throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"'); + } + return { + schema, + schemaPath, + topSchemaRef, + errSchemaPath + }; + } + throw new Error('either "keyword" or "schema" must be passed'); + } + exports.getSubschema = getSubschema; + function extendSubschemaData(subschema, it2, { dataProp, dataPropType: dpType, data, dataTypes, propertyName }) { + if (data !== void 0 && dataProp !== void 0) { + throw new Error('both "data" and "dataProp" passed, only one allowed'); + } + const { gen } = it2; + if (dataProp !== void 0) { + const { errorPath, dataPathArr, opts } = it2; + const nextData = gen.let("data", (0, codegen_1._)`${it2.data}${(0, codegen_1.getProperty)(dataProp)}`, true); + dataContextProps(nextData); + subschema.errorPath = (0, codegen_1.str)`${errorPath}${(0, util_1.getErrorPath)(dataProp, dpType, opts.jsPropertySyntax)}`; + subschema.parentDataProperty = (0, codegen_1._)`${dataProp}`; + subschema.dataPathArr = [...dataPathArr, subschema.parentDataProperty]; + } + if (data !== void 0) { + const nextData = data instanceof codegen_1.Name ? data : gen.let("data", data, true); + dataContextProps(nextData); + if (propertyName !== void 0) + subschema.propertyName = propertyName; + } + if (dataTypes) + subschema.dataTypes = dataTypes; + function dataContextProps(_nextData) { + subschema.data = _nextData; + subschema.dataLevel = it2.dataLevel + 1; + subschema.dataTypes = []; + it2.definedProperties = /* @__PURE__ */ new Set(); + subschema.parentData = it2.data; + subschema.dataNames = [...it2.dataNames, _nextData]; + } + } + exports.extendSubschemaData = extendSubschemaData; + function extendSubschemaMode(subschema, { jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors }) { + if (compositeRule !== void 0) + subschema.compositeRule = compositeRule; + if (createErrors !== void 0) + subschema.createErrors = createErrors; + if (allErrors !== void 0) + subschema.allErrors = allErrors; + subschema.jtdDiscriminator = jtdDiscriminator; + subschema.jtdMetadata = jtdMetadata; + } + exports.extendSubschemaMode = extendSubschemaMode; + } +}); + +// node_modules/fast-deep-equal/index.js +var require_fast_deep_equal = __commonJS({ + "node_modules/fast-deep-equal/index.js"(exports, module) { + "use strict"; + module.exports = function equal(a2, b2) { + if (a2 === b2) return true; + if (a2 && b2 && typeof a2 == "object" && typeof b2 == "object") { + if (a2.constructor !== b2.constructor) return false; + var length, i, keys; + if (Array.isArray(a2)) { + length = a2.length; + if (length != b2.length) return false; + for (i = length; i-- !== 0; ) + if (!equal(a2[i], b2[i])) return false; + return true; + } + if (a2.constructor === RegExp) return a2.source === b2.source && a2.flags === b2.flags; + if (a2.valueOf !== Object.prototype.valueOf) return a2.valueOf() === b2.valueOf(); + if (a2.toString !== Object.prototype.toString) return a2.toString() === b2.toString(); + keys = Object.keys(a2); + length = keys.length; + if (length !== Object.keys(b2).length) return false; + for (i = length; i-- !== 0; ) + if (!Object.prototype.hasOwnProperty.call(b2, keys[i])) return false; + for (i = length; i-- !== 0; ) { + var key = keys[i]; + if (!equal(a2[key], b2[key])) return false; + } + return true; + } + return a2 !== a2 && b2 !== b2; + }; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse/index.js +var require_json_schema_traverse = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse/index.js"(exports, module) { + "use strict"; + var traverse = module.exports = function(schema, opts, cb) { + if (typeof opts == "function") { + cb = opts; + opts = {}; + } + cb = opts.cb || cb; + var pre = typeof cb == "function" ? cb : cb.pre || function() { + }; + var post = cb.post || function() { + }; + _traverse(opts, pre, post, schema, "", schema); + }; + traverse.keywords = { + additionalItems: true, + items: true, + contains: true, + additionalProperties: true, + propertyNames: true, + not: true, + if: true, + then: true, + else: true + }; + traverse.arrayKeywords = { + items: true, + allOf: true, + anyOf: true, + oneOf: true + }; + traverse.propsKeywords = { + $defs: true, + definitions: true, + properties: true, + patternProperties: true, + dependencies: true + }; + traverse.skipKeywords = { + default: true, + enum: true, + const: true, + required: true, + maximum: true, + minimum: true, + exclusiveMaximum: true, + exclusiveMinimum: true, + multipleOf: true, + maxLength: true, + minLength: true, + pattern: true, + format: true, + maxItems: true, + minItems: true, + uniqueItems: true, + maxProperties: true, + minProperties: true + }; + function _traverse(opts, pre, post, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) { + if (schema && typeof schema == "object" && !Array.isArray(schema)) { + pre(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex); + for (var key in schema) { + var sch = schema[key]; + if (Array.isArray(sch)) { + if (key in traverse.arrayKeywords) { + for (var i = 0; i < sch.length; i++) + _traverse(opts, pre, post, sch[i], jsonPtr + "/" + key + "/" + i, rootSchema, jsonPtr, key, schema, i); + } + } else if (key in traverse.propsKeywords) { + if (sch && typeof sch == "object") { + for (var prop in sch) + _traverse(opts, pre, post, sch[prop], jsonPtr + "/" + key + "/" + escapeJsonPtr(prop), rootSchema, jsonPtr, key, schema, prop); + } + } else if (key in traverse.keywords || opts.allKeys && !(key in traverse.skipKeywords)) { + _traverse(opts, pre, post, sch, jsonPtr + "/" + key, rootSchema, jsonPtr, key, schema); + } + } + post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex); + } + } + function escapeJsonPtr(str) { + return str.replace(/~/g, "~0").replace(/\//g, "~1"); + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/resolve.js +var require_resolve = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/resolve.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getSchemaRefs = exports.resolveUrl = exports.normalizeId = exports._getFullPath = exports.getFullPath = exports.inlineRef = void 0; + var util_1 = require_util(); + var equal = require_fast_deep_equal(); + var traverse = require_json_schema_traverse(); + var SIMPLE_INLINED = /* @__PURE__ */ new Set([ + "type", + "format", + "pattern", + "maxLength", + "minLength", + "maxProperties", + "minProperties", + "maxItems", + "minItems", + "maximum", + "minimum", + "uniqueItems", + "multipleOf", + "required", + "enum", + "const" + ]); + function inlineRef(schema, limit = true) { + if (typeof schema == "boolean") + return true; + if (limit === true) + return !hasRef(schema); + if (!limit) + return false; + return countKeys(schema) <= limit; + } + exports.inlineRef = inlineRef; + var REF_KEYWORDS = /* @__PURE__ */ new Set([ + "$ref", + "$recursiveRef", + "$recursiveAnchor", + "$dynamicRef", + "$dynamicAnchor" + ]); + function hasRef(schema) { + for (const key in schema) { + if (REF_KEYWORDS.has(key)) + return true; + const sch = schema[key]; + if (Array.isArray(sch) && sch.some(hasRef)) + return true; + if (typeof sch == "object" && hasRef(sch)) + return true; + } + return false; + } + function countKeys(schema) { + let count = 0; + for (const key in schema) { + if (key === "$ref") + return Infinity; + count++; + if (SIMPLE_INLINED.has(key)) + continue; + if (typeof schema[key] == "object") { + (0, util_1.eachItem)(schema[key], (sch) => count += countKeys(sch)); + } + if (count === Infinity) + return Infinity; + } + return count; + } + function getFullPath(resolver, id = "", normalize) { + if (normalize !== false) + id = normalizeId(id); + const p2 = resolver.parse(id); + return _getFullPath(resolver, p2); + } + exports.getFullPath = getFullPath; + function _getFullPath(resolver, p2) { + const serialized = resolver.serialize(p2); + return serialized.split("#")[0] + "#"; + } + exports._getFullPath = _getFullPath; + var TRAILING_SLASH_HASH = /#\/?$/; + function normalizeId(id) { + return id ? id.replace(TRAILING_SLASH_HASH, "") : ""; + } + exports.normalizeId = normalizeId; + function resolveUrl(resolver, baseId, id) { + id = normalizeId(id); + return resolver.resolve(baseId, id); + } + exports.resolveUrl = resolveUrl; + var ANCHOR = /^[a-z_][-a-z0-9._]*$/i; + function getSchemaRefs(schema, baseId) { + if (typeof schema == "boolean") + return {}; + const { schemaId, uriResolver } = this.opts; + const schId = normalizeId(schema[schemaId] || baseId); + const baseIds = { "": schId }; + const pathPrefix = getFullPath(uriResolver, schId, false); + const localRefs = {}; + const schemaRefs = /* @__PURE__ */ new Set(); + traverse(schema, { allKeys: true }, (sch, jsonPtr, _, parentJsonPtr) => { + if (parentJsonPtr === void 0) + return; + const fullPath = pathPrefix + jsonPtr; + let innerBaseId = baseIds[parentJsonPtr]; + if (typeof sch[schemaId] == "string") + innerBaseId = addRef.call(this, sch[schemaId]); + addAnchor.call(this, sch.$anchor); + addAnchor.call(this, sch.$dynamicAnchor); + baseIds[jsonPtr] = innerBaseId; + function addRef(ref) { + const _resolve = this.opts.uriResolver.resolve; + ref = normalizeId(innerBaseId ? _resolve(innerBaseId, ref) : ref); + if (schemaRefs.has(ref)) + throw ambiguos(ref); + schemaRefs.add(ref); + let schOrRef = this.refs[ref]; + if (typeof schOrRef == "string") + schOrRef = this.refs[schOrRef]; + if (typeof schOrRef == "object") { + checkAmbiguosRef(sch, schOrRef.schema, ref); + } else if (ref !== normalizeId(fullPath)) { + if (ref[0] === "#") { + checkAmbiguosRef(sch, localRefs[ref], ref); + localRefs[ref] = sch; + } else { + this.refs[ref] = fullPath; + } + } + return ref; + } + function addAnchor(anchor) { + if (typeof anchor == "string") { + if (!ANCHOR.test(anchor)) + throw new Error(`invalid anchor "${anchor}"`); + addRef.call(this, `#${anchor}`); + } + } + }); + return localRefs; + function checkAmbiguosRef(sch1, sch2, ref) { + if (sch2 !== void 0 && !equal(sch1, sch2)) + throw ambiguos(ref); + } + function ambiguos(ref) { + return new Error(`reference "${ref}" resolves to more than one schema`); + } + } + exports.getSchemaRefs = getSchemaRefs; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/index.js +var require_validate = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/validate/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getData = exports.KeywordCxt = exports.validateFunctionCode = void 0; + var boolSchema_1 = require_boolSchema(); + var dataType_1 = require_dataType(); + var applicability_1 = require_applicability(); + var dataType_2 = require_dataType(); + var defaults_1 = require_defaults(); + var keyword_1 = require_keyword(); + var subschema_1 = require_subschema(); + var codegen_1 = require_codegen(); + var names_1 = require_names(); + var resolve_1 = require_resolve(); + var util_1 = require_util(); + var errors_1 = require_errors(); + function validateFunctionCode(it2) { + if (isSchemaObj(it2)) { + checkKeywords(it2); + if (schemaCxtHasRules(it2)) { + topSchemaObjCode(it2); + return; + } + } + validateFunction(it2, () => (0, boolSchema_1.topBoolOrEmptySchema)(it2)); + } + exports.validateFunctionCode = validateFunctionCode; + function validateFunction({ gen, validateName, schema, schemaEnv, opts }, body) { + if (opts.code.es5) { + gen.func(validateName, (0, codegen_1._)`${names_1.default.data}, ${names_1.default.valCxt}`, schemaEnv.$async, () => { + gen.code((0, codegen_1._)`"use strict"; ${funcSourceUrl(schema, opts)}`); + destructureValCxtES5(gen, opts); + gen.code(body); + }); + } else { + gen.func(validateName, (0, codegen_1._)`${names_1.default.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body)); + } + } + function destructureValCxt(opts) { + return (0, codegen_1._)`{${names_1.default.instancePath}="", ${names_1.default.parentData}, ${names_1.default.parentDataProperty}, ${names_1.default.rootData}=${names_1.default.data}${opts.dynamicRef ? (0, codegen_1._)`, ${names_1.default.dynamicAnchors}={}` : codegen_1.nil}}={}`; + } + function destructureValCxtES5(gen, opts) { + gen.if(names_1.default.valCxt, () => { + gen.var(names_1.default.instancePath, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.instancePath}`); + gen.var(names_1.default.parentData, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.parentData}`); + gen.var(names_1.default.parentDataProperty, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.parentDataProperty}`); + gen.var(names_1.default.rootData, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.rootData}`); + if (opts.dynamicRef) + gen.var(names_1.default.dynamicAnchors, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.dynamicAnchors}`); + }, () => { + gen.var(names_1.default.instancePath, (0, codegen_1._)`""`); + gen.var(names_1.default.parentData, (0, codegen_1._)`undefined`); + gen.var(names_1.default.parentDataProperty, (0, codegen_1._)`undefined`); + gen.var(names_1.default.rootData, names_1.default.data); + if (opts.dynamicRef) + gen.var(names_1.default.dynamicAnchors, (0, codegen_1._)`{}`); + }); + } + function topSchemaObjCode(it2) { + const { schema, opts, gen } = it2; + validateFunction(it2, () => { + if (opts.$comment && schema.$comment) + commentKeyword(it2); + checkNoDefault(it2); + gen.let(names_1.default.vErrors, null); + gen.let(names_1.default.errors, 0); + if (opts.unevaluated) + resetEvaluated(it2); + typeAndKeywords(it2); + returnResults(it2); + }); + return; + } + function resetEvaluated(it2) { + const { gen, validateName } = it2; + it2.evaluated = gen.const("evaluated", (0, codegen_1._)`${validateName}.evaluated`); + gen.if((0, codegen_1._)`${it2.evaluated}.dynamicProps`, () => gen.assign((0, codegen_1._)`${it2.evaluated}.props`, (0, codegen_1._)`undefined`)); + gen.if((0, codegen_1._)`${it2.evaluated}.dynamicItems`, () => gen.assign((0, codegen_1._)`${it2.evaluated}.items`, (0, codegen_1._)`undefined`)); + } + function funcSourceUrl(schema, opts) { + const schId = typeof schema == "object" && schema[opts.schemaId]; + return schId && (opts.code.source || opts.code.process) ? (0, codegen_1._)`/*# sourceURL=${schId} */` : codegen_1.nil; + } + function subschemaCode(it2, valid) { + if (isSchemaObj(it2)) { + checkKeywords(it2); + if (schemaCxtHasRules(it2)) { + subSchemaObjCode(it2, valid); + return; + } + } + (0, boolSchema_1.boolOrEmptySchema)(it2, valid); + } + function schemaCxtHasRules({ schema, self }) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (self.RULES.all[key]) + return true; + return false; + } + function isSchemaObj(it2) { + return typeof it2.schema != "boolean"; + } + function subSchemaObjCode(it2, valid) { + const { schema, gen, opts } = it2; + if (opts.$comment && schema.$comment) + commentKeyword(it2); + updateContext(it2); + checkAsyncSchema(it2); + const errsCount = gen.const("_errs", names_1.default.errors); + typeAndKeywords(it2, errsCount); + gen.var(valid, (0, codegen_1._)`${errsCount} === ${names_1.default.errors}`); + } + function checkKeywords(it2) { + (0, util_1.checkUnknownRules)(it2); + checkRefsAndKeywords(it2); + } + function typeAndKeywords(it2, errsCount) { + if (it2.opts.jtd) + return schemaKeywords(it2, [], false, errsCount); + const types = (0, dataType_1.getSchemaTypes)(it2.schema); + const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it2, types); + schemaKeywords(it2, types, !checkedTypes, errsCount); + } + function checkRefsAndKeywords(it2) { + const { schema, errSchemaPath, opts, self } = it2; + if (schema.$ref && opts.ignoreKeywordsWithRef && (0, util_1.schemaHasRulesButRef)(schema, self.RULES)) { + self.logger.warn(`$ref: keywords ignored in schema at path "${errSchemaPath}"`); + } + } + function checkNoDefault(it2) { + const { schema, opts } = it2; + if (schema.default !== void 0 && opts.useDefaults && opts.strictSchema) { + (0, util_1.checkStrictMode)(it2, "default is ignored in the schema root"); + } + } + function updateContext(it2) { + const schId = it2.schema[it2.opts.schemaId]; + if (schId) + it2.baseId = (0, resolve_1.resolveUrl)(it2.opts.uriResolver, it2.baseId, schId); + } + function checkAsyncSchema(it2) { + if (it2.schema.$async && !it2.schemaEnv.$async) + throw new Error("async schema in sync schema"); + } + function commentKeyword({ gen, schemaEnv, schema, errSchemaPath, opts }) { + const msg = schema.$comment; + if (opts.$comment === true) { + gen.code((0, codegen_1._)`${names_1.default.self}.logger.log(${msg})`); + } else if (typeof opts.$comment == "function") { + const schemaPath = (0, codegen_1.str)`${errSchemaPath}/$comment`; + const rootName = gen.scopeValue("root", { ref: schemaEnv.root }); + gen.code((0, codegen_1._)`${names_1.default.self}.opts.$comment(${msg}, ${schemaPath}, ${rootName}.schema)`); + } + } + function returnResults(it2) { + const { gen, schemaEnv, validateName, ValidationError, opts } = it2; + if (schemaEnv.$async) { + gen.if((0, codegen_1._)`${names_1.default.errors} === 0`, () => gen.return(names_1.default.data), () => gen.throw((0, codegen_1._)`new ${ValidationError}(${names_1.default.vErrors})`)); + } else { + gen.assign((0, codegen_1._)`${validateName}.errors`, names_1.default.vErrors); + if (opts.unevaluated) + assignEvaluated(it2); + gen.return((0, codegen_1._)`${names_1.default.errors} === 0`); + } + } + function assignEvaluated({ gen, evaluated, props, items }) { + if (props instanceof codegen_1.Name) + gen.assign((0, codegen_1._)`${evaluated}.props`, props); + if (items instanceof codegen_1.Name) + gen.assign((0, codegen_1._)`${evaluated}.items`, items); + } + function schemaKeywords(it2, types, typeErrors, errsCount) { + const { gen, schema, data, allErrors, opts, self } = it2; + const { RULES } = self; + if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema, RULES))) { + gen.block(() => keywordCode(it2, "$ref", RULES.all.$ref.definition)); + return; + } + if (!opts.jtd) + checkStrictTypes(it2, types); + gen.block(() => { + for (const group of RULES.rules) + groupKeywords(group); + groupKeywords(RULES.post); + }); + function groupKeywords(group) { + if (!(0, applicability_1.shouldUseGroup)(schema, group)) + return; + if (group.type) { + gen.if((0, dataType_2.checkDataType)(group.type, data, opts.strictNumbers)); + iterateKeywords(it2, group); + if (types.length === 1 && types[0] === group.type && typeErrors) { + gen.else(); + (0, dataType_2.reportTypeError)(it2); + } + gen.endIf(); + } else { + iterateKeywords(it2, group); + } + if (!allErrors) + gen.if((0, codegen_1._)`${names_1.default.errors} === ${errsCount || 0}`); + } + } + function iterateKeywords(it2, group) { + const { gen, schema, opts: { useDefaults } } = it2; + if (useDefaults) + (0, defaults_1.assignDefaults)(it2, group.type); + gen.block(() => { + for (const rule of group.rules) { + if ((0, applicability_1.shouldUseRule)(schema, rule)) { + keywordCode(it2, rule.keyword, rule.definition, group.type); + } + } + }); + } + function checkStrictTypes(it2, types) { + if (it2.schemaEnv.meta || !it2.opts.strictTypes) + return; + checkContextTypes(it2, types); + if (!it2.opts.allowUnionTypes) + checkMultipleTypes(it2, types); + checkKeywordTypes(it2, it2.dataTypes); + } + function checkContextTypes(it2, types) { + if (!types.length) + return; + if (!it2.dataTypes.length) { + it2.dataTypes = types; + return; + } + types.forEach((t) => { + if (!includesType(it2.dataTypes, t)) { + strictTypesError(it2, `type "${t}" not allowed by context "${it2.dataTypes.join(",")}"`); + } + }); + narrowSchemaTypes(it2, types); + } + function checkMultipleTypes(it2, ts) { + if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) { + strictTypesError(it2, "use allowUnionTypes to allow union type keyword"); + } + } + function checkKeywordTypes(it2, ts) { + const rules = it2.self.RULES.all; + for (const keyword in rules) { + const rule = rules[keyword]; + if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it2.schema, rule)) { + const { type } = rule.definition; + if (type.length && !type.some((t) => hasApplicableType(ts, t))) { + strictTypesError(it2, `missing type "${type.join(",")}" for keyword "${keyword}"`); + } + } + } + } + function hasApplicableType(schTs, kwdT) { + return schTs.includes(kwdT) || kwdT === "number" && schTs.includes("integer"); + } + function includesType(ts, t) { + return ts.includes(t) || t === "integer" && ts.includes("number"); + } + function narrowSchemaTypes(it2, withTypes) { + const ts = []; + for (const t of it2.dataTypes) { + if (includesType(withTypes, t)) + ts.push(t); + else if (withTypes.includes("integer") && t === "number") + ts.push("integer"); + } + it2.dataTypes = ts; + } + function strictTypesError(it2, msg) { + const schemaPath = it2.schemaEnv.baseId + it2.errSchemaPath; + msg += ` at "${schemaPath}" (strictTypes)`; + (0, util_1.checkStrictMode)(it2, msg, it2.opts.strictTypes); + } + var KeywordCxt = class { + constructor(it2, def, keyword) { + (0, keyword_1.validateKeywordUsage)(it2, def, keyword); + this.gen = it2.gen; + this.allErrors = it2.allErrors; + this.keyword = keyword; + this.data = it2.data; + this.schema = it2.schema[keyword]; + this.$data = def.$data && it2.opts.$data && this.schema && this.schema.$data; + this.schemaValue = (0, util_1.schemaRefOrVal)(it2, this.schema, keyword, this.$data); + this.schemaType = def.schemaType; + this.parentSchema = it2.schema; + this.params = {}; + this.it = it2; + this.def = def; + if (this.$data) { + this.schemaCode = it2.gen.const("vSchema", getData(this.$data, it2)); + } else { + this.schemaCode = this.schemaValue; + if (!(0, keyword_1.validSchemaType)(this.schema, def.schemaType, def.allowUndefined)) { + throw new Error(`${keyword} value must be ${JSON.stringify(def.schemaType)}`); + } + } + if ("code" in def ? def.trackErrors : def.errors !== false) { + this.errsCount = it2.gen.const("_errs", names_1.default.errors); + } + } + result(condition, successAction, failAction) { + this.failResult((0, codegen_1.not)(condition), successAction, failAction); + } + failResult(condition, successAction, failAction) { + this.gen.if(condition); + if (failAction) + failAction(); + else + this.error(); + if (successAction) { + this.gen.else(); + successAction(); + if (this.allErrors) + this.gen.endIf(); + } else { + if (this.allErrors) + this.gen.endIf(); + else + this.gen.else(); + } + } + pass(condition, failAction) { + this.failResult((0, codegen_1.not)(condition), void 0, failAction); + } + fail(condition) { + if (condition === void 0) { + this.error(); + if (!this.allErrors) + this.gen.if(false); + return; + } + this.gen.if(condition); + this.error(); + if (this.allErrors) + this.gen.endIf(); + else + this.gen.else(); + } + fail$data(condition) { + if (!this.$data) + return this.fail(condition); + const { schemaCode } = this; + this.fail((0, codegen_1._)`${schemaCode} !== undefined && (${(0, codegen_1.or)(this.invalid$data(), condition)})`); + } + error(append, errorParams, errorPaths) { + if (errorParams) { + this.setParams(errorParams); + this._error(append, errorPaths); + this.setParams({}); + return; + } + this._error(append, errorPaths); + } + _error(append, errorPaths) { + ; + (append ? errors_1.reportExtraError : errors_1.reportError)(this, this.def.error, errorPaths); + } + $dataError() { + (0, errors_1.reportError)(this, this.def.$dataError || errors_1.keyword$DataError); + } + reset() { + if (this.errsCount === void 0) + throw new Error('add "trackErrors" to keyword definition'); + (0, errors_1.resetErrorsCount)(this.gen, this.errsCount); + } + ok(cond) { + if (!this.allErrors) + this.gen.if(cond); + } + setParams(obj, assign) { + if (assign) + Object.assign(this.params, obj); + else + this.params = obj; + } + block$data(valid, codeBlock, $dataValid = codegen_1.nil) { + this.gen.block(() => { + this.check$data(valid, $dataValid); + codeBlock(); + }); + } + check$data(valid = codegen_1.nil, $dataValid = codegen_1.nil) { + if (!this.$data) + return; + const { gen, schemaCode, schemaType, def } = this; + gen.if((0, codegen_1.or)((0, codegen_1._)`${schemaCode} === undefined`, $dataValid)); + if (valid !== codegen_1.nil) + gen.assign(valid, true); + if (schemaType.length || def.validateSchema) { + gen.elseIf(this.invalid$data()); + this.$dataError(); + if (valid !== codegen_1.nil) + gen.assign(valid, false); + } + gen.else(); + } + invalid$data() { + const { gen, schemaCode, schemaType, def, it: it2 } = this; + return (0, codegen_1.or)(wrong$DataType(), invalid$DataSchema()); + function wrong$DataType() { + if (schemaType.length) { + if (!(schemaCode instanceof codegen_1.Name)) + throw new Error("ajv implementation error"); + const st2 = Array.isArray(schemaType) ? schemaType : [schemaType]; + return (0, codegen_1._)`${(0, dataType_2.checkDataTypes)(st2, schemaCode, it2.opts.strictNumbers, dataType_2.DataType.Wrong)}`; + } + return codegen_1.nil; + } + function invalid$DataSchema() { + if (def.validateSchema) { + const validateSchemaRef = gen.scopeValue("validate$data", { ref: def.validateSchema }); + return (0, codegen_1._)`!${validateSchemaRef}(${schemaCode})`; + } + return codegen_1.nil; + } + } + subschema(appl, valid) { + const subschema = (0, subschema_1.getSubschema)(this.it, appl); + (0, subschema_1.extendSubschemaData)(subschema, this.it, appl); + (0, subschema_1.extendSubschemaMode)(subschema, appl); + const nextContext = { ...this.it, ...subschema, items: void 0, props: void 0 }; + subschemaCode(nextContext, valid); + return nextContext; + } + mergeEvaluated(schemaCxt, toName) { + const { it: it2, gen } = this; + if (!it2.opts.unevaluated) + return; + if (it2.props !== true && schemaCxt.props !== void 0) { + it2.props = util_1.mergeEvaluated.props(gen, schemaCxt.props, it2.props, toName); + } + if (it2.items !== true && schemaCxt.items !== void 0) { + it2.items = util_1.mergeEvaluated.items(gen, schemaCxt.items, it2.items, toName); + } + } + mergeValidEvaluated(schemaCxt, valid) { + const { it: it2, gen } = this; + if (it2.opts.unevaluated && (it2.props !== true || it2.items !== true)) { + gen.if(valid, () => this.mergeEvaluated(schemaCxt, codegen_1.Name)); + return true; + } + } + }; + exports.KeywordCxt = KeywordCxt; + function keywordCode(it2, keyword, def, ruleType) { + const cxt = new KeywordCxt(it2, def, keyword); + if ("code" in def) { + def.code(cxt, ruleType); + } else if (cxt.$data && def.validate) { + (0, keyword_1.funcKeywordCode)(cxt, def); + } else if ("macro" in def) { + (0, keyword_1.macroKeywordCode)(cxt, def); + } else if (def.compile || def.validate) { + (0, keyword_1.funcKeywordCode)(cxt, def); + } + } + var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/; + var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/; + function getData($data, { dataLevel, dataNames, dataPathArr }) { + let jsonPointer; + let data; + if ($data === "") + return names_1.default.rootData; + if ($data[0] === "/") { + if (!JSON_POINTER.test($data)) + throw new Error(`Invalid JSON-pointer: ${$data}`); + jsonPointer = $data; + data = names_1.default.rootData; + } else { + const matches = RELATIVE_JSON_POINTER.exec($data); + if (!matches) + throw new Error(`Invalid JSON-pointer: ${$data}`); + const up = +matches[1]; + jsonPointer = matches[2]; + if (jsonPointer === "#") { + if (up >= dataLevel) + throw new Error(errorMsg("property/index", up)); + return dataPathArr[dataLevel - up]; + } + if (up > dataLevel) + throw new Error(errorMsg("data", up)); + data = dataNames[dataLevel - up]; + if (!jsonPointer) + return data; + } + let expr = data; + const segments = jsonPointer.split("/"); + for (const segment of segments) { + if (segment) { + data = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)((0, util_1.unescapeJsonPointer)(segment))}`; + expr = (0, codegen_1._)`${expr} && ${data}`; + } + } + return expr; + function errorMsg(pointerType, up) { + return `Cannot access ${pointerType} ${up} levels up, current level is ${dataLevel}`; + } + } + exports.getData = getData; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/validation_error.js +var require_validation_error = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/validation_error.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var ValidationError = class extends Error { + constructor(errors) { + super("validation failed"); + this.errors = errors; + this.ajv = this.validation = true; + } + }; + exports.default = ValidationError; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/ref_error.js +var require_ref_error = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/ref_error.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var resolve_1 = require_resolve(); + var MissingRefError = class extends Error { + constructor(resolver, baseId, ref, msg) { + super(msg || `can't resolve reference ${ref} from id ${baseId}`); + this.missingRef = (0, resolve_1.resolveUrl)(resolver, baseId, ref); + this.missingSchema = (0, resolve_1.normalizeId)((0, resolve_1.getFullPath)(resolver, this.missingRef)); + } + }; + exports.default = MissingRefError; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/index.js +var require_compile = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/compile/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0; + var codegen_1 = require_codegen(); + var validation_error_1 = require_validation_error(); + var names_1 = require_names(); + var resolve_1 = require_resolve(); + var util_1 = require_util(); + var validate_1 = require_validate(); + var SchemaEnv = class { + constructor(env) { + var _a; + this.refs = {}; + this.dynamicAnchors = {}; + let schema; + if (typeof env.schema == "object") + schema = env.schema; + this.schema = env.schema; + this.schemaId = env.schemaId; + this.root = env.root || this; + this.baseId = (_a = env.baseId) !== null && _a !== void 0 ? _a : (0, resolve_1.normalizeId)(schema === null || schema === void 0 ? void 0 : schema[env.schemaId || "$id"]); + this.schemaPath = env.schemaPath; + this.localRefs = env.localRefs; + this.meta = env.meta; + this.$async = schema === null || schema === void 0 ? void 0 : schema.$async; + this.refs = {}; + } + }; + exports.SchemaEnv = SchemaEnv; + function compileSchema(sch) { + const _sch = getCompilingSchema.call(this, sch); + if (_sch) + return _sch; + const rootId = (0, resolve_1.getFullPath)(this.opts.uriResolver, sch.root.baseId); + const { es5, lines } = this.opts.code; + const { ownProperties } = this.opts; + const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties }); + let _ValidationError; + if (sch.$async) { + _ValidationError = gen.scopeValue("Error", { + ref: validation_error_1.default, + code: (0, codegen_1._)`require("ajv/dist/runtime/validation_error").default` + }); + } + const validateName = gen.scopeName("validate"); + sch.validateName = validateName; + const schemaCxt = { + gen, + allErrors: this.opts.allErrors, + data: names_1.default.data, + parentData: names_1.default.parentData, + parentDataProperty: names_1.default.parentDataProperty, + dataNames: [names_1.default.data], + dataPathArr: [codegen_1.nil], + // TODO can its length be used as dataLevel if nil is removed? + dataLevel: 0, + dataTypes: [], + definedProperties: /* @__PURE__ */ new Set(), + topSchemaRef: gen.scopeValue("schema", this.opts.code.source === true ? { ref: sch.schema, code: (0, codegen_1.stringify)(sch.schema) } : { ref: sch.schema }), + validateName, + ValidationError: _ValidationError, + schema: sch.schema, + schemaEnv: sch, + rootId, + baseId: sch.baseId || rootId, + schemaPath: codegen_1.nil, + errSchemaPath: sch.schemaPath || (this.opts.jtd ? "" : "#"), + errorPath: (0, codegen_1._)`""`, + opts: this.opts, + self: this + }; + let sourceCode; + try { + this._compilations.add(sch); + (0, validate_1.validateFunctionCode)(schemaCxt); + gen.optimize(this.opts.code.optimize); + const validateCode = gen.toString(); + sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${validateCode}`; + if (this.opts.code.process) + sourceCode = this.opts.code.process(sourceCode, sch); + const makeValidate = new Function(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode); + const validate = makeValidate(this, this.scope.get()); + this.scope.value(validateName, { ref: validate }); + validate.errors = null; + validate.schema = sch.schema; + validate.schemaEnv = sch; + if (sch.$async) + validate.$async = true; + if (this.opts.code.source === true) { + validate.source = { validateName, validateCode, scopeValues: gen._values }; + } + if (this.opts.unevaluated) { + const { props, items } = schemaCxt; + validate.evaluated = { + props: props instanceof codegen_1.Name ? void 0 : props, + items: items instanceof codegen_1.Name ? void 0 : items, + dynamicProps: props instanceof codegen_1.Name, + dynamicItems: items instanceof codegen_1.Name + }; + if (validate.source) + validate.source.evaluated = (0, codegen_1.stringify)(validate.evaluated); + } + sch.validate = validate; + return sch; + } catch (e) { + delete sch.validate; + delete sch.validateName; + if (sourceCode) + this.logger.error("Error compiling schema, function code:", sourceCode); + throw e; + } finally { + this._compilations.delete(sch); + } + } + exports.compileSchema = compileSchema; + function resolveRef(root, baseId, ref) { + var _a; + ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref); + const schOrFunc = root.refs[ref]; + if (schOrFunc) + return schOrFunc; + let _sch = resolve.call(this, root, ref); + if (_sch === void 0) { + const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref]; + const { schemaId } = this.opts; + if (schema) + _sch = new SchemaEnv({ schema, schemaId, root, baseId }); + } + if (_sch === void 0) + return; + return root.refs[ref] = inlineOrCompile.call(this, _sch); + } + exports.resolveRef = resolveRef; + function inlineOrCompile(sch) { + if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs)) + return sch.schema; + return sch.validate ? sch : compileSchema.call(this, sch); + } + function getCompilingSchema(schEnv) { + for (const sch of this._compilations) { + if (sameSchemaEnv(sch, schEnv)) + return sch; + } + } + exports.getCompilingSchema = getCompilingSchema; + function sameSchemaEnv(s1, s2) { + return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId; + } + function resolve(root, ref) { + let sch; + while (typeof (sch = this.refs[ref]) == "string") + ref = sch; + return sch || this.schemas[ref] || resolveSchema.call(this, root, ref); + } + function resolveSchema(root, ref) { + const p2 = this.opts.uriResolver.parse(ref); + const refPath = (0, resolve_1._getFullPath)(this.opts.uriResolver, p2); + let baseId = (0, resolve_1.getFullPath)(this.opts.uriResolver, root.baseId, void 0); + if (Object.keys(root.schema).length > 0 && refPath === baseId) { + return getJsonPointer.call(this, p2, root); + } + const id = (0, resolve_1.normalizeId)(refPath); + const schOrRef = this.refs[id] || this.schemas[id]; + if (typeof schOrRef == "string") { + const sch = resolveSchema.call(this, root, schOrRef); + if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object") + return; + return getJsonPointer.call(this, p2, sch); + } + if (typeof (schOrRef === null || schOrRef === void 0 ? void 0 : schOrRef.schema) !== "object") + return; + if (!schOrRef.validate) + compileSchema.call(this, schOrRef); + if (id === (0, resolve_1.normalizeId)(ref)) { + const { schema } = schOrRef; + const { schemaId } = this.opts; + const schId = schema[schemaId]; + if (schId) + baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId); + return new SchemaEnv({ schema, schemaId, root, baseId }); + } + return getJsonPointer.call(this, p2, schOrRef); + } + exports.resolveSchema = resolveSchema; + var PREVENT_SCOPE_CHANGE = /* @__PURE__ */ new Set([ + "properties", + "patternProperties", + "enum", + "dependencies", + "definitions" + ]); + function getJsonPointer(parsedRef, { baseId, schema, root }) { + var _a; + if (((_a = parsedRef.fragment) === null || _a === void 0 ? void 0 : _a[0]) !== "/") + return; + for (const part of parsedRef.fragment.slice(1).split("/")) { + if (typeof schema === "boolean") + return; + const partSchema = schema[(0, util_1.unescapeFragment)(part)]; + if (partSchema === void 0) + return; + schema = partSchema; + const schId = typeof schema === "object" && schema[this.opts.schemaId]; + if (!PREVENT_SCOPE_CHANGE.has(part) && schId) { + baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId); + } + } + let env; + if (typeof schema != "boolean" && schema.$ref && !(0, util_1.schemaHasRulesButRef)(schema, this.RULES)) { + const $ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schema.$ref); + env = resolveSchema.call(this, root, $ref); + } + const { schemaId } = this.opts; + env = env || new SchemaEnv({ schema, schemaId, root, baseId }); + if (env.schema !== env.root.schema) + return env; + return void 0; + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/refs/data.json +var require_data = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/refs/data.json"(exports, module) { + module.exports = { + $id: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", + description: "Meta-schema for $data reference (JSON AnySchema extension proposal)", + type: "object", + required: ["$data"], + properties: { + $data: { + type: "string", + anyOf: [{ format: "relative-json-pointer" }, { format: "json-pointer" }] + } + }, + additionalProperties: false + }; + } +}); + +// node_modules/fast-uri/lib/utils.js +var require_utils = __commonJS({ + "node_modules/fast-uri/lib/utils.js"(exports, module) { + "use strict"; + var isUUID = RegExp.prototype.test.bind(/^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/iu); + var isIPv4 = RegExp.prototype.test.bind(/^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/u); + var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu); + var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu); + var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu); + function stringArrayToHexStripped(input) { + let acc = ""; + let code = 0; + let i = 0; + for (i = 0; i < input.length; i++) { + code = input[i].charCodeAt(0); + if (code === 48) { + continue; + } + if (!(code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102)) { + return ""; + } + acc += input[i]; + break; + } + for (i += 1; i < input.length; i++) { + code = input[i].charCodeAt(0); + if (!(code >= 48 && code <= 57 || code >= 65 && code <= 70 || code >= 97 && code <= 102)) { + return ""; + } + acc += input[i]; + } + return acc; + } + var nonSimpleDomain = RegExp.prototype.test.bind(/[^!"$&'()*+,\-.;=_`a-z{}~]/u); + function consumeIsZone(buffer) { + buffer.length = 0; + return true; + } + function consumeHextets(buffer, address, output) { + if (buffer.length) { + const hex = stringArrayToHexStripped(buffer); + if (hex !== "") { + address.push(hex); + } else { + output.error = true; + return false; + } + buffer.length = 0; + } + return true; + } + function getIPV6(input) { + let tokenCount = 0; + const output = { error: false, address: "", zone: "" }; + const address = []; + const buffer = []; + let endipv6Encountered = false; + let endIpv6 = false; + let consume = consumeHextets; + for (let i = 0; i < input.length; i++) { + const cursor = input[i]; + if (cursor === "[" || cursor === "]") { + continue; + } + if (cursor === ":") { + if (endipv6Encountered === true) { + endIpv6 = true; + } + if (!consume(buffer, address, output)) { + break; + } + if (++tokenCount > 7) { + output.error = true; + break; + } + if (i > 0 && input[i - 1] === ":") { + endipv6Encountered = true; + } + address.push(":"); + continue; + } else if (cursor === "%") { + if (!consume(buffer, address, output)) { + break; + } + consume = consumeIsZone; + } else { + buffer.push(cursor); + continue; + } + } + if (buffer.length) { + if (consume === consumeIsZone) { + output.zone = buffer.join(""); + } else if (endIpv6) { + address.push(buffer.join("")); + } else { + address.push(stringArrayToHexStripped(buffer)); + } + } + output.address = address.join(""); + return output; + } + function normalizeIPv6(host) { + if (findToken(host, ":") < 2) { + return { host, isIPV6: false }; + } + const ipv62 = getIPV6(host); + if (!ipv62.error) { + let newHost = ipv62.address; + let escapedHost = ipv62.address; + if (ipv62.zone) { + newHost += "%" + ipv62.zone; + escapedHost += "%25" + ipv62.zone; + } + return { host: newHost, isIPV6: true, escapedHost }; + } else { + return { host, isIPV6: false }; + } + } + function findToken(str, token) { + let ind = 0; + for (let i = 0; i < str.length; i++) { + if (str[i] === token) ind++; + } + return ind; + } + function removeDotSegments(path3) { + let input = path3; + const output = []; + let nextSlash = -1; + let len = 0; + while (len = input.length) { + if (len === 1) { + if (input === ".") { + break; + } else if (input === "/") { + output.push("/"); + break; + } else { + output.push(input); + break; + } + } else if (len === 2) { + if (input[0] === ".") { + if (input[1] === ".") { + break; + } else if (input[1] === "/") { + input = input.slice(2); + continue; + } + } else if (input[0] === "/") { + if (input[1] === "." || input[1] === "/") { + output.push("/"); + break; + } + } + } else if (len === 3) { + if (input === "/..") { + if (output.length !== 0) { + output.pop(); + } + output.push("/"); + break; + } + } + if (input[0] === ".") { + if (input[1] === ".") { + if (input[2] === "/") { + input = input.slice(3); + continue; + } + } else if (input[1] === "/") { + input = input.slice(2); + continue; + } + } else if (input[0] === "/") { + if (input[1] === ".") { + if (input[2] === "/") { + input = input.slice(2); + continue; + } else if (input[2] === ".") { + if (input[3] === "/") { + input = input.slice(3); + if (output.length !== 0) { + output.pop(); + } + continue; + } + } + } + } + if ((nextSlash = input.indexOf("/", 1)) === -1) { + output.push(input); + break; + } else { + output.push(input.slice(0, nextSlash)); + input = input.slice(nextSlash); + } + } + return output.join(""); + } + var HOST_DELIMS = { "@": "%40", "/": "%2F", "?": "%3F", "#": "%23", ":": "%3A" }; + var HOST_DELIM_RE = /[@/?#:]/g; + var HOST_DELIM_NO_COLON_RE = /[@/?#]/g; + function reescapeHostDelimiters(host, isIP) { + const re2 = isIP ? HOST_DELIM_NO_COLON_RE : HOST_DELIM_RE; + re2.lastIndex = 0; + return host.replace(re2, (ch) => HOST_DELIMS[ch]); + } + function normalizePercentEncoding(input, decodeUnreserved = false) { + if (input.indexOf("%") === -1) { + return input; + } + let output = ""; + for (let i = 0; i < input.length; i++) { + if (input[i] === "%" && i + 2 < input.length) { + const hex = input.slice(i + 1, i + 3); + if (isHexPair(hex)) { + const normalizedHex = hex.toUpperCase(); + const decoded = String.fromCharCode(parseInt(normalizedHex, 16)); + if (decodeUnreserved && isUnreserved(decoded)) { + output += decoded; + } else { + output += "%" + normalizedHex; + } + i += 2; + continue; + } + } + output += input[i]; + } + return output; + } + function normalizePathEncoding(input) { + let output = ""; + for (let i = 0; i < input.length; i++) { + if (input[i] === "%" && i + 2 < input.length) { + const hex = input.slice(i + 1, i + 3); + if (isHexPair(hex)) { + const normalizedHex = hex.toUpperCase(); + const decoded = String.fromCharCode(parseInt(normalizedHex, 16)); + if (decoded !== "." && isUnreserved(decoded)) { + output += decoded; + } else { + output += "%" + normalizedHex; + } + i += 2; + continue; + } + } + if (isPathCharacter(input[i])) { + output += input[i]; + } else { + output += escape(input[i]); + } + } + return output; + } + function escapePreservingEscapes(input) { + let output = ""; + for (let i = 0; i < input.length; i++) { + if (input[i] === "%" && i + 2 < input.length) { + const hex = input.slice(i + 1, i + 3); + if (isHexPair(hex)) { + output += "%" + hex.toUpperCase(); + i += 2; + continue; + } + } + output += escape(input[i]); + } + return output; + } + function recomposeAuthority(component) { + const uriTokens = []; + if (component.userinfo !== void 0) { + uriTokens.push(component.userinfo); + uriTokens.push("@"); + } + if (component.host !== void 0) { + let host = unescape(component.host); + if (!isIPv4(host)) { + const ipV6res = normalizeIPv6(host); + if (ipV6res.isIPV6 === true) { + host = `[${ipV6res.escapedHost}]`; + } else { + host = reescapeHostDelimiters(host, false); + } + } + uriTokens.push(host); + } + if (typeof component.port === "number" || typeof component.port === "string") { + uriTokens.push(":"); + uriTokens.push(String(component.port)); + } + return uriTokens.length ? uriTokens.join("") : void 0; + } + module.exports = { + nonSimpleDomain, + recomposeAuthority, + reescapeHostDelimiters, + normalizePercentEncoding, + normalizePathEncoding, + escapePreservingEscapes, + removeDotSegments, + isIPv4, + isUUID, + normalizeIPv6, + stringArrayToHexStripped + }; + } +}); + +// node_modules/fast-uri/lib/schemes.js +var require_schemes = __commonJS({ + "node_modules/fast-uri/lib/schemes.js"(exports, module) { + "use strict"; + var { isUUID } = require_utils(); + var URN_REG = /([\da-z][\d\-a-z]{0,31}):((?:[\w!$'()*+,\-.:;=@]|%[\da-f]{2})+)/iu; + var supportedSchemeNames = ( + /** @type {const} */ + [ + "http", + "https", + "ws", + "wss", + "urn", + "urn:uuid" + ] + ); + function isValidSchemeName(name) { + return supportedSchemeNames.indexOf( + /** @type {*} */ + name + ) !== -1; + } + function wsIsSecure(wsComponent) { + if (wsComponent.secure === true) { + return true; + } else if (wsComponent.secure === false) { + return false; + } else if (wsComponent.scheme) { + return wsComponent.scheme.length === 3 && (wsComponent.scheme[0] === "w" || wsComponent.scheme[0] === "W") && (wsComponent.scheme[1] === "s" || wsComponent.scheme[1] === "S") && (wsComponent.scheme[2] === "s" || wsComponent.scheme[2] === "S"); + } else { + return false; + } + } + function httpParse(component) { + if (!component.host) { + component.error = component.error || "HTTP URIs must have a host."; + } + return component; + } + function httpSerialize(component) { + const secure = String(component.scheme).toLowerCase() === "https"; + if (component.port === (secure ? 443 : 80) || component.port === "") { + component.port = void 0; + } + if (!component.path) { + component.path = "/"; + } + return component; + } + function wsParse(wsComponent) { + wsComponent.secure = wsIsSecure(wsComponent); + wsComponent.resourceName = (wsComponent.path || "/") + (wsComponent.query ? "?" + wsComponent.query : ""); + wsComponent.path = void 0; + wsComponent.query = void 0; + return wsComponent; + } + function wsSerialize(wsComponent) { + if (wsComponent.port === (wsIsSecure(wsComponent) ? 443 : 80) || wsComponent.port === "") { + wsComponent.port = void 0; + } + if (typeof wsComponent.secure === "boolean") { + wsComponent.scheme = wsComponent.secure ? "wss" : "ws"; + wsComponent.secure = void 0; + } + if (wsComponent.resourceName) { + const [path3, query] = wsComponent.resourceName.split("?"); + wsComponent.path = path3 && path3 !== "/" ? path3 : void 0; + wsComponent.query = query; + wsComponent.resourceName = void 0; + } + wsComponent.fragment = void 0; + return wsComponent; + } + function urnParse(urnComponent, options) { + if (!urnComponent.path) { + urnComponent.error = "URN can not be parsed"; + return urnComponent; + } + const matches = urnComponent.path.match(URN_REG); + if (matches) { + const scheme = options.scheme || urnComponent.scheme || "urn"; + urnComponent.nid = matches[1].toLowerCase(); + urnComponent.nss = matches[2]; + const urnScheme = `${scheme}:${options.nid || urnComponent.nid}`; + const schemeHandler = getSchemeHandler(urnScheme); + urnComponent.path = void 0; + if (schemeHandler) { + urnComponent = schemeHandler.parse(urnComponent, options); + } + } else { + urnComponent.error = urnComponent.error || "URN can not be parsed."; + } + return urnComponent; + } + function urnSerialize(urnComponent, options) { + if (urnComponent.nid === void 0) { + throw new Error("URN without nid cannot be serialized"); + } + const scheme = options.scheme || urnComponent.scheme || "urn"; + const nid = urnComponent.nid.toLowerCase(); + const urnScheme = `${scheme}:${options.nid || nid}`; + const schemeHandler = getSchemeHandler(urnScheme); + if (schemeHandler) { + urnComponent = schemeHandler.serialize(urnComponent, options); + } + const uriComponent = urnComponent; + const nss = urnComponent.nss; + uriComponent.path = `${nid || options.nid}:${nss}`; + options.skipEscape = true; + return uriComponent; + } + function urnuuidParse(urnComponent, options) { + const uuidComponent = urnComponent; + uuidComponent.uuid = uuidComponent.nss; + uuidComponent.nss = void 0; + if (!options.tolerant && (!uuidComponent.uuid || !isUUID(uuidComponent.uuid))) { + uuidComponent.error = uuidComponent.error || "UUID is not valid."; + } + return uuidComponent; + } + function urnuuidSerialize(uuidComponent) { + const urnComponent = uuidComponent; + urnComponent.nss = (uuidComponent.uuid || "").toLowerCase(); + return urnComponent; + } + var http = ( + /** @type {SchemeHandler} */ + { + scheme: "http", + domainHost: true, + parse: httpParse, + serialize: httpSerialize + } + ); + var https = ( + /** @type {SchemeHandler} */ + { + scheme: "https", + domainHost: http.domainHost, + parse: httpParse, + serialize: httpSerialize + } + ); + var ws = ( + /** @type {SchemeHandler} */ + { + scheme: "ws", + domainHost: true, + parse: wsParse, + serialize: wsSerialize + } + ); + var wss = ( + /** @type {SchemeHandler} */ + { + scheme: "wss", + domainHost: ws.domainHost, + parse: ws.parse, + serialize: ws.serialize + } + ); + var urn = ( + /** @type {SchemeHandler} */ + { + scheme: "urn", + parse: urnParse, + serialize: urnSerialize, + skipNormalize: true + } + ); + var urnuuid = ( + /** @type {SchemeHandler} */ + { + scheme: "urn:uuid", + parse: urnuuidParse, + serialize: urnuuidSerialize, + skipNormalize: true + } + ); + var SCHEMES = ( + /** @type {Record} */ + { + http, + https, + ws, + wss, + urn, + "urn:uuid": urnuuid + } + ); + Object.setPrototypeOf(SCHEMES, null); + function getSchemeHandler(scheme) { + return scheme && (SCHEMES[ + /** @type {SchemeName} */ + scheme + ] || SCHEMES[ + /** @type {SchemeName} */ + scheme.toLowerCase() + ]) || void 0; + } + module.exports = { + wsIsSecure, + SCHEMES, + isValidSchemeName, + getSchemeHandler + }; + } +}); + +// node_modules/fast-uri/index.js +var require_fast_uri = __commonJS({ + "node_modules/fast-uri/index.js"(exports, module) { + "use strict"; + var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils(); + var { SCHEMES, getSchemeHandler } = require_schemes(); + function normalize(uri, options) { + if (typeof uri === "string") { + uri = /** @type {T} */ + normalizeString(uri, options); + } else if (typeof uri === "object") { + uri = /** @type {T} */ + parse3(serialize(uri, options), options); + } + return uri; + } + function resolve(baseURI, relativeURI, options) { + const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" }; + const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true); + schemelessOptions.skipEscape = true; + return serialize(resolved, schemelessOptions); + } + function resolveComponent(base, relative, options, skipNormalization) { + const target = {}; + if (!skipNormalization) { + base = parse3(serialize(base, options), options); + relative = parse3(serialize(relative, options), options); + } + options = options || {}; + if (!options.tolerant && relative.scheme) { + target.scheme = relative.scheme; + target.userinfo = relative.userinfo; + target.host = relative.host; + target.port = relative.port; + target.path = removeDotSegments(relative.path || ""); + target.query = relative.query; + } else { + if (relative.userinfo !== void 0 || relative.host !== void 0 || relative.port !== void 0) { + target.userinfo = relative.userinfo; + target.host = relative.host; + target.port = relative.port; + target.path = removeDotSegments(relative.path || ""); + target.query = relative.query; + } else { + if (!relative.path) { + target.path = base.path; + if (relative.query !== void 0) { + target.query = relative.query; + } else { + target.query = base.query; + } + } else { + if (relative.path[0] === "/") { + target.path = removeDotSegments(relative.path); + } else { + if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) { + target.path = "/" + relative.path; + } else if (!base.path) { + target.path = relative.path; + } else { + target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative.path; + } + target.path = removeDotSegments(target.path); + } + target.query = relative.query; + } + target.userinfo = base.userinfo; + target.host = base.host; + target.port = base.port; + } + target.scheme = base.scheme; + } + target.fragment = relative.fragment; + return target; + } + function equal(uriA, uriB, options) { + const normalizedA = normalizeComparableURI(uriA, options); + const normalizedB = normalizeComparableURI(uriB, options); + return normalizedA !== void 0 && normalizedB !== void 0 && normalizedA.toLowerCase() === normalizedB.toLowerCase(); + } + function serialize(cmpts, opts) { + const component = { + host: cmpts.host, + scheme: cmpts.scheme, + userinfo: cmpts.userinfo, + port: cmpts.port, + path: cmpts.path, + query: cmpts.query, + nid: cmpts.nid, + nss: cmpts.nss, + uuid: cmpts.uuid, + fragment: cmpts.fragment, + reference: cmpts.reference, + resourceName: cmpts.resourceName, + secure: cmpts.secure, + error: "" + }; + const options = Object.assign({}, opts); + const uriTokens = []; + const schemeHandler = getSchemeHandler(options.scheme || component.scheme); + if (schemeHandler && schemeHandler.serialize) schemeHandler.serialize(component, options); + if (component.path !== void 0) { + if (!options.skipEscape) { + component.path = escapePreservingEscapes(component.path); + if (component.scheme !== void 0) { + component.path = component.path.split("%3A").join(":"); + } + } else { + component.path = normalizePercentEncoding(component.path); + } + } + if (options.reference !== "suffix" && component.scheme) { + uriTokens.push(component.scheme, ":"); + } + const authority = recomposeAuthority(component); + if (authority !== void 0) { + if (options.reference !== "suffix") { + uriTokens.push("//"); + } + uriTokens.push(authority); + if (component.path && component.path[0] !== "/") { + uriTokens.push("/"); + } + } + if (component.path !== void 0) { + let s2 = component.path; + if (!options.absolutePath && (!schemeHandler || !schemeHandler.absolutePath)) { + s2 = removeDotSegments(s2); + } + if (authority === void 0 && s2[0] === "/" && s2[1] === "/") { + s2 = "/%2F" + s2.slice(2); + } + uriTokens.push(s2); + } + if (component.query !== void 0) { + uriTokens.push("?", component.query); + } + if (component.fragment !== void 0) { + uriTokens.push("#", component.fragment); + } + return uriTokens.join(""); + } + var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u; + var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/; + function getParseError(parsed, matches) { + if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") { + return 'URI path must start with "/" when authority is present.'; + } + if (typeof parsed.port === "number" && (parsed.port < 0 || parsed.port > 65535)) { + return "URI port is malformed."; + } + return void 0; + } + function parseWithStatus(uri, opts) { + const options = Object.assign({}, opts); + const parsed = { + scheme: void 0, + userinfo: void 0, + host: "", + port: void 0, + path: "", + query: void 0, + fragment: void 0 + }; + let malformedAuthorityOrPort = false; + let isIP = false; + if (options.reference === "suffix") { + if (options.scheme) { + uri = options.scheme + ":" + uri; + } else { + uri = "//" + uri; + } + } + const authorityMatch = uri.match(AUTHORITY_PREFIX); + if (authorityMatch !== null && authorityMatch[1].indexOf("\\") !== -1) { + parsed.error = "URI authority must not contain a literal backslash."; + malformedAuthorityOrPort = true; + } + const matches = uri.match(URI_PARSE); + if (matches) { + parsed.scheme = matches[1]; + parsed.userinfo = matches[3]; + parsed.host = matches[4]; + parsed.port = parseInt(matches[5], 10); + parsed.path = matches[6] || ""; + parsed.query = matches[7]; + parsed.fragment = matches[8]; + if (isNaN(parsed.port)) { + parsed.port = matches[5]; + } + const parseError = getParseError(parsed, matches); + if (parseError !== void 0) { + parsed.error = parsed.error || parseError; + malformedAuthorityOrPort = true; + } + if (parsed.host) { + const ipv4result = isIPv4(parsed.host); + if (ipv4result === false) { + const ipv6result = normalizeIPv6(parsed.host); + parsed.host = ipv6result.host.toLowerCase(); + isIP = ipv6result.isIPV6; + } else { + isIP = true; + } + } + if (parsed.scheme === void 0 && parsed.userinfo === void 0 && parsed.host === void 0 && parsed.port === void 0 && parsed.query === void 0 && !parsed.path) { + parsed.reference = "same-document"; + } else if (parsed.scheme === void 0) { + parsed.reference = "relative"; + } else if (parsed.fragment === void 0) { + parsed.reference = "absolute"; + } else { + parsed.reference = "uri"; + } + if (options.reference && options.reference !== "suffix" && options.reference !== parsed.reference) { + parsed.error = parsed.error || "URI is not a " + options.reference + " reference."; + } + const schemeHandler = getSchemeHandler(options.scheme || parsed.scheme); + if (!options.unicodeSupport && (!schemeHandler || !schemeHandler.unicodeSupport)) { + if (parsed.host && (options.domainHost || schemeHandler && schemeHandler.domainHost) && isIP === false && nonSimpleDomain(parsed.host)) { + try { + parsed.host = new URL("http://" + parsed.host).hostname; + } catch (e) { + parsed.error = parsed.error || "Host's domain name can not be converted to ASCII: " + e; + } + } + } + if (!schemeHandler || schemeHandler && !schemeHandler.skipNormalize) { + if (uri.indexOf("%") !== -1) { + if (parsed.scheme !== void 0) { + parsed.scheme = unescape(parsed.scheme); + } + if (parsed.host !== void 0) { + parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP); + } + } + if (parsed.path) { + parsed.path = normalizePathEncoding(parsed.path); + } + if (parsed.fragment) { + try { + parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment)); + } catch { + parsed.error = parsed.error || "URI malformed"; + } + } + } + if (schemeHandler && schemeHandler.parse) { + schemeHandler.parse(parsed, options); + } + } else { + parsed.error = parsed.error || "URI can not be parsed."; + } + return { parsed, malformedAuthorityOrPort }; + } + function parse3(uri, opts) { + return parseWithStatus(uri, opts).parsed; + } + function normalizeString(uri, opts) { + return normalizeStringWithStatus(uri, opts).normalized; + } + function normalizeStringWithStatus(uri, opts) { + const { parsed, malformedAuthorityOrPort } = parseWithStatus(uri, opts); + return { + normalized: malformedAuthorityOrPort ? uri : serialize(parsed, opts), + malformedAuthorityOrPort + }; + } + function normalizeComparableURI(uri, opts) { + if (typeof uri === "string") { + const { normalized, malformedAuthorityOrPort } = normalizeStringWithStatus(uri, opts); + return malformedAuthorityOrPort ? void 0 : normalized; + } + if (typeof uri === "object") { + return serialize(uri, opts); + } + } + var fastUri = { + SCHEMES, + normalize, + resolve, + resolveComponent, + equal, + serialize, + parse: parse3 + }; + module.exports = fastUri; + module.exports.default = fastUri; + module.exports.fastUri = fastUri; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/uri.js +var require_uri = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/uri.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var uri = require_fast_uri(); + uri.code = 'require("ajv/dist/runtime/uri").default'; + exports.default = uri; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/core.js +var require_core = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/core.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0; + var validate_1 = require_validate(); + Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function() { + return validate_1.KeywordCxt; + } }); + var codegen_1 = require_codegen(); + Object.defineProperty(exports, "_", { enumerable: true, get: function() { + return codegen_1._; + } }); + Object.defineProperty(exports, "str", { enumerable: true, get: function() { + return codegen_1.str; + } }); + Object.defineProperty(exports, "stringify", { enumerable: true, get: function() { + return codegen_1.stringify; + } }); + Object.defineProperty(exports, "nil", { enumerable: true, get: function() { + return codegen_1.nil; + } }); + Object.defineProperty(exports, "Name", { enumerable: true, get: function() { + return codegen_1.Name; + } }); + Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function() { + return codegen_1.CodeGen; + } }); + var validation_error_1 = require_validation_error(); + var ref_error_1 = require_ref_error(); + var rules_1 = require_rules(); + var compile_1 = require_compile(); + var codegen_2 = require_codegen(); + var resolve_1 = require_resolve(); + var dataType_1 = require_dataType(); + var util_1 = require_util(); + var $dataRefSchema = require_data(); + var uri_1 = require_uri(); + var defaultRegExp = (str, flags) => new RegExp(str, flags); + defaultRegExp.code = "new RegExp"; + var META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"]; + var EXT_SCOPE_NAMES = /* @__PURE__ */ new Set([ + "validate", + "serialize", + "parse", + "wrapper", + "root", + "schema", + "keyword", + "pattern", + "formats", + "validate$data", + "func", + "obj", + "Error" + ]); + var removedOptions = { + errorDataPath: "", + format: "`validateFormats: false` can be used instead.", + nullable: '"nullable" keyword is supported by default.', + jsonPointers: "Deprecated jsPropertySyntax can be used instead.", + extendRefs: "Deprecated ignoreKeywordsWithRef can be used instead.", + missingRefs: "Pass empty schema with $id that should be ignored to ajv.addSchema.", + processCode: "Use option `code: {process: (code, schemaEnv: object) => string}`", + sourceCode: "Use option `code: {source: true}`", + strictDefaults: "It is default now, see option `strict`.", + strictKeywords: "It is default now, see option `strict`.", + uniqueItems: '"uniqueItems" keyword is always validated.', + unknownFormats: "Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).", + cache: "Map is used as cache, schema object as key.", + serialize: "Map is used as cache, schema object as key.", + ajvErrors: "It is default now." + }; + var deprecatedOptions = { + ignoreKeywordsWithRef: "", + jsPropertySyntax: "", + unicode: '"minLength"/"maxLength" account for unicode characters by default.' + }; + var MAX_EXPRESSION = 200; + function requiredOptions(o) { + var _a, _b, _c2, _d, _e2, _f, _g2, _h, _j, _k, _l2, _m, _o2, _p, _q, _r2, _s, _t2, _u2, _v2, _w, _x, _y, _z, _0; + const s2 = o.strict; + const _optz = (_a = o.code) === null || _a === void 0 ? void 0 : _a.optimize; + const optimize = _optz === true || _optz === void 0 ? 1 : _optz || 0; + const regExp = (_c2 = (_b = o.code) === null || _b === void 0 ? void 0 : _b.regExp) !== null && _c2 !== void 0 ? _c2 : defaultRegExp; + const uriResolver = (_d = o.uriResolver) !== null && _d !== void 0 ? _d : uri_1.default; + return { + strictSchema: (_f = (_e2 = o.strictSchema) !== null && _e2 !== void 0 ? _e2 : s2) !== null && _f !== void 0 ? _f : true, + strictNumbers: (_h = (_g2 = o.strictNumbers) !== null && _g2 !== void 0 ? _g2 : s2) !== null && _h !== void 0 ? _h : true, + strictTypes: (_k = (_j = o.strictTypes) !== null && _j !== void 0 ? _j : s2) !== null && _k !== void 0 ? _k : "log", + strictTuples: (_m = (_l2 = o.strictTuples) !== null && _l2 !== void 0 ? _l2 : s2) !== null && _m !== void 0 ? _m : "log", + strictRequired: (_p = (_o2 = o.strictRequired) !== null && _o2 !== void 0 ? _o2 : s2) !== null && _p !== void 0 ? _p : false, + code: o.code ? { ...o.code, optimize, regExp } : { optimize, regExp }, + loopRequired: (_q = o.loopRequired) !== null && _q !== void 0 ? _q : MAX_EXPRESSION, + loopEnum: (_r2 = o.loopEnum) !== null && _r2 !== void 0 ? _r2 : MAX_EXPRESSION, + meta: (_s = o.meta) !== null && _s !== void 0 ? _s : true, + messages: (_t2 = o.messages) !== null && _t2 !== void 0 ? _t2 : true, + inlineRefs: (_u2 = o.inlineRefs) !== null && _u2 !== void 0 ? _u2 : true, + schemaId: (_v2 = o.schemaId) !== null && _v2 !== void 0 ? _v2 : "$id", + addUsedSchema: (_w = o.addUsedSchema) !== null && _w !== void 0 ? _w : true, + validateSchema: (_x = o.validateSchema) !== null && _x !== void 0 ? _x : true, + validateFormats: (_y = o.validateFormats) !== null && _y !== void 0 ? _y : true, + unicodeRegExp: (_z = o.unicodeRegExp) !== null && _z !== void 0 ? _z : true, + int32range: (_0 = o.int32range) !== null && _0 !== void 0 ? _0 : true, + uriResolver + }; + } + var Ajv2 = class { + constructor(opts = {}) { + this.schemas = {}; + this.refs = {}; + this.formats = /* @__PURE__ */ Object.create(null); + this._compilations = /* @__PURE__ */ new Set(); + this._loading = {}; + this._cache = /* @__PURE__ */ new Map(); + opts = this.opts = { ...opts, ...requiredOptions(opts) }; + const { es5, lines } = this.opts.code; + this.scope = new codegen_2.ValueScope({ scope: {}, prefixes: EXT_SCOPE_NAMES, es5, lines }); + this.logger = getLogger(opts.logger); + const formatOpt = opts.validateFormats; + opts.validateFormats = false; + this.RULES = (0, rules_1.getRules)(); + checkOptions.call(this, removedOptions, opts, "NOT SUPPORTED"); + checkOptions.call(this, deprecatedOptions, opts, "DEPRECATED", "warn"); + this._metaOpts = getMetaSchemaOptions.call(this); + if (opts.formats) + addInitialFormats.call(this); + this._addVocabularies(); + this._addDefaultMetaSchema(); + if (opts.keywords) + addInitialKeywords.call(this, opts.keywords); + if (typeof opts.meta == "object") + this.addMetaSchema(opts.meta); + addInitialSchemas.call(this); + opts.validateFormats = formatOpt; + } + _addVocabularies() { + this.addKeyword("$async"); + } + _addDefaultMetaSchema() { + const { $data, meta, schemaId } = this.opts; + let _dataRefSchema = $dataRefSchema; + if (schemaId === "id") { + _dataRefSchema = { ...$dataRefSchema }; + _dataRefSchema.id = _dataRefSchema.$id; + delete _dataRefSchema.$id; + } + if (meta && $data) + this.addMetaSchema(_dataRefSchema, _dataRefSchema[schemaId], false); + } + defaultMeta() { + const { meta, schemaId } = this.opts; + return this.opts.defaultMeta = typeof meta == "object" ? meta[schemaId] || meta : void 0; + } + validate(schemaKeyRef, data) { + let v; + if (typeof schemaKeyRef == "string") { + v = this.getSchema(schemaKeyRef); + if (!v) + throw new Error(`no schema with key or ref "${schemaKeyRef}"`); + } else { + v = this.compile(schemaKeyRef); + } + const valid = v(data); + if (!("$async" in v)) + this.errors = v.errors; + return valid; + } + compile(schema, _meta) { + const sch = this._addSchema(schema, _meta); + return sch.validate || this._compileSchemaEnv(sch); + } + compileAsync(schema, meta) { + if (typeof this.opts.loadSchema != "function") { + throw new Error("options.loadSchema should be a function"); + } + const { loadSchema } = this.opts; + return runCompileAsync.call(this, schema, meta); + async function runCompileAsync(_schema, _meta) { + await loadMetaSchema.call(this, _schema.$schema); + const sch = this._addSchema(_schema, _meta); + return sch.validate || _compileAsync.call(this, sch); + } + async function loadMetaSchema($ref) { + if ($ref && !this.getSchema($ref)) { + await runCompileAsync.call(this, { $ref }, true); + } + } + async function _compileAsync(sch) { + try { + return this._compileSchemaEnv(sch); + } catch (e) { + if (!(e instanceof ref_error_1.default)) + throw e; + checkLoaded.call(this, e); + await loadMissingSchema.call(this, e.missingSchema); + return _compileAsync.call(this, sch); + } + } + function checkLoaded({ missingSchema: ref, missingRef }) { + if (this.refs[ref]) { + throw new Error(`AnySchema ${ref} is loaded but ${missingRef} cannot be resolved`); + } + } + async function loadMissingSchema(ref) { + const _schema = await _loadSchema.call(this, ref); + if (!this.refs[ref]) + await loadMetaSchema.call(this, _schema.$schema); + if (!this.refs[ref]) + this.addSchema(_schema, ref, meta); + } + async function _loadSchema(ref) { + const p2 = this._loading[ref]; + if (p2) + return p2; + try { + return await (this._loading[ref] = loadSchema(ref)); + } finally { + delete this._loading[ref]; + } + } + } + // Adds schema to the instance + addSchema(schema, key, _meta, _validateSchema = this.opts.validateSchema) { + if (Array.isArray(schema)) { + for (const sch of schema) + this.addSchema(sch, void 0, _meta, _validateSchema); + return this; + } + let id; + if (typeof schema === "object") { + const { schemaId } = this.opts; + id = schema[schemaId]; + if (id !== void 0 && typeof id != "string") { + throw new Error(`schema ${schemaId} must be string`); + } + } + key = (0, resolve_1.normalizeId)(key || id); + this._checkUnique(key); + this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true); + return this; + } + // Add schema that will be used to validate other schemas + // options in META_IGNORE_OPTIONS are alway set to false + addMetaSchema(schema, key, _validateSchema = this.opts.validateSchema) { + this.addSchema(schema, key, true, _validateSchema); + return this; + } + // Validate schema against its meta-schema + validateSchema(schema, throwOrLogError) { + if (typeof schema == "boolean") + return true; + let $schema; + $schema = schema.$schema; + if ($schema !== void 0 && typeof $schema != "string") { + throw new Error("$schema must be a string"); + } + $schema = $schema || this.opts.defaultMeta || this.defaultMeta(); + if (!$schema) { + this.logger.warn("meta-schema not available"); + this.errors = null; + return true; + } + const valid = this.validate($schema, schema); + if (!valid && throwOrLogError) { + const message = "schema is invalid: " + this.errorsText(); + if (this.opts.validateSchema === "log") + this.logger.error(message); + else + throw new Error(message); + } + return valid; + } + // Get compiled schema by `key` or `ref`. + // (`key` that was passed to `addSchema` or full schema reference - `schema.$id` or resolved id) + getSchema(keyRef) { + let sch; + while (typeof (sch = getSchEnv.call(this, keyRef)) == "string") + keyRef = sch; + if (sch === void 0) { + const { schemaId } = this.opts; + const root = new compile_1.SchemaEnv({ schema: {}, schemaId }); + sch = compile_1.resolveSchema.call(this, root, keyRef); + if (!sch) + return; + this.refs[keyRef] = sch; + } + return sch.validate || this._compileSchemaEnv(sch); + } + // Remove cached schema(s). + // If no parameter is passed all schemas but meta-schemas are removed. + // If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed. + // Even if schema is referenced by other schemas it still can be removed as other schemas have local references. + removeSchema(schemaKeyRef) { + if (schemaKeyRef instanceof RegExp) { + this._removeAllSchemas(this.schemas, schemaKeyRef); + this._removeAllSchemas(this.refs, schemaKeyRef); + return this; + } + switch (typeof schemaKeyRef) { + case "undefined": + this._removeAllSchemas(this.schemas); + this._removeAllSchemas(this.refs); + this._cache.clear(); + return this; + case "string": { + const sch = getSchEnv.call(this, schemaKeyRef); + if (typeof sch == "object") + this._cache.delete(sch.schema); + delete this.schemas[schemaKeyRef]; + delete this.refs[schemaKeyRef]; + return this; + } + case "object": { + const cacheKey = schemaKeyRef; + this._cache.delete(cacheKey); + let id = schemaKeyRef[this.opts.schemaId]; + if (id) { + id = (0, resolve_1.normalizeId)(id); + delete this.schemas[id]; + delete this.refs[id]; + } + return this; + } + default: + throw new Error("ajv.removeSchema: invalid parameter"); + } + } + // add "vocabulary" - a collection of keywords + addVocabulary(definitions) { + for (const def of definitions) + this.addKeyword(def); + return this; + } + addKeyword(kwdOrDef, def) { + let keyword; + if (typeof kwdOrDef == "string") { + keyword = kwdOrDef; + if (typeof def == "object") { + this.logger.warn("these parameters are deprecated, see docs for addKeyword"); + def.keyword = keyword; + } + } else if (typeof kwdOrDef == "object" && def === void 0) { + def = kwdOrDef; + keyword = def.keyword; + if (Array.isArray(keyword) && !keyword.length) { + throw new Error("addKeywords: keyword must be string or non-empty array"); + } + } else { + throw new Error("invalid addKeywords parameters"); + } + checkKeyword.call(this, keyword, def); + if (!def) { + (0, util_1.eachItem)(keyword, (kwd) => addRule.call(this, kwd)); + return this; + } + keywordMetaschema.call(this, def); + const definition = { + ...def, + type: (0, dataType_1.getJSONTypes)(def.type), + schemaType: (0, dataType_1.getJSONTypes)(def.schemaType) + }; + (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k2) => addRule.call(this, k2, definition) : (k2) => definition.type.forEach((t) => addRule.call(this, k2, definition, t))); + return this; + } + getKeyword(keyword) { + const rule = this.RULES.all[keyword]; + return typeof rule == "object" ? rule.definition : !!rule; + } + // Remove keyword + removeKeyword(keyword) { + const { RULES } = this; + delete RULES.keywords[keyword]; + delete RULES.all[keyword]; + for (const group of RULES.rules) { + const i = group.rules.findIndex((rule) => rule.keyword === keyword); + if (i >= 0) + group.rules.splice(i, 1); + } + return this; + } + // Add format + addFormat(name, format) { + if (typeof format == "string") + format = new RegExp(format); + this.formats[name] = format; + return this; + } + errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) { + if (!errors || errors.length === 0) + return "No errors"; + return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg); + } + $dataMetaSchema(metaSchema, keywordsJsonPointers) { + const rules = this.RULES.all; + metaSchema = JSON.parse(JSON.stringify(metaSchema)); + for (const jsonPointer of keywordsJsonPointers) { + const segments = jsonPointer.split("/").slice(1); + let keywords = metaSchema; + for (const seg of segments) + keywords = keywords[seg]; + for (const key in rules) { + const rule = rules[key]; + if (typeof rule != "object") + continue; + const { $data } = rule.definition; + const schema = keywords[key]; + if ($data && schema) + keywords[key] = schemaOrData(schema); + } + } + return metaSchema; + } + _removeAllSchemas(schemas, regex) { + for (const keyRef in schemas) { + const sch = schemas[keyRef]; + if (!regex || regex.test(keyRef)) { + if (typeof sch == "string") { + delete schemas[keyRef]; + } else if (sch && !sch.meta) { + this._cache.delete(sch.schema); + delete schemas[keyRef]; + } + } + } + } + _addSchema(schema, meta, baseId, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) { + let id; + const { schemaId } = this.opts; + if (typeof schema == "object") { + id = schema[schemaId]; + } else { + if (this.opts.jtd) + throw new Error("schema must be object"); + else if (typeof schema != "boolean") + throw new Error("schema must be object or boolean"); + } + let sch = this._cache.get(schema); + if (sch !== void 0) + return sch; + baseId = (0, resolve_1.normalizeId)(id || baseId); + const localRefs = resolve_1.getSchemaRefs.call(this, schema, baseId); + sch = new compile_1.SchemaEnv({ schema, schemaId, meta, baseId, localRefs }); + this._cache.set(sch.schema, sch); + if (addSchema && !baseId.startsWith("#")) { + if (baseId) + this._checkUnique(baseId); + this.refs[baseId] = sch; + } + if (validateSchema) + this.validateSchema(schema, true); + return sch; + } + _checkUnique(id) { + if (this.schemas[id] || this.refs[id]) { + throw new Error(`schema with key or id "${id}" already exists`); + } + } + _compileSchemaEnv(sch) { + if (sch.meta) + this._compileMetaSchema(sch); + else + compile_1.compileSchema.call(this, sch); + if (!sch.validate) + throw new Error("ajv implementation error"); + return sch.validate; + } + _compileMetaSchema(sch) { + const currentOpts = this.opts; + this.opts = this._metaOpts; + try { + compile_1.compileSchema.call(this, sch); + } finally { + this.opts = currentOpts; + } + } + }; + Ajv2.ValidationError = validation_error_1.default; + Ajv2.MissingRefError = ref_error_1.default; + exports.default = Ajv2; + function checkOptions(checkOpts, options, msg, log = "error") { + for (const key in checkOpts) { + const opt = key; + if (opt in options) + this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`); + } + } + function getSchEnv(keyRef) { + keyRef = (0, resolve_1.normalizeId)(keyRef); + return this.schemas[keyRef] || this.refs[keyRef]; + } + function addInitialSchemas() { + const optsSchemas = this.opts.schemas; + if (!optsSchemas) + return; + if (Array.isArray(optsSchemas)) + this.addSchema(optsSchemas); + else + for (const key in optsSchemas) + this.addSchema(optsSchemas[key], key); + } + function addInitialFormats() { + for (const name in this.opts.formats) { + const format = this.opts.formats[name]; + if (format) + this.addFormat(name, format); + } + } + function addInitialKeywords(defs) { + if (Array.isArray(defs)) { + this.addVocabulary(defs); + return; + } + this.logger.warn("keywords option as map is deprecated, pass array"); + for (const keyword in defs) { + const def = defs[keyword]; + if (!def.keyword) + def.keyword = keyword; + this.addKeyword(def); + } + } + function getMetaSchemaOptions() { + const metaOpts = { ...this.opts }; + for (const opt of META_IGNORE_OPTIONS) + delete metaOpts[opt]; + return metaOpts; + } + var noLogs = { log() { + }, warn() { + }, error() { + } }; + function getLogger(logger) { + if (logger === false) + return noLogs; + if (logger === void 0) + return console; + if (logger.log && logger.warn && logger.error) + return logger; + throw new Error("logger must implement log, warn and error methods"); + } + var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i; + function checkKeyword(keyword, def) { + const { RULES } = this; + (0, util_1.eachItem)(keyword, (kwd) => { + if (RULES.keywords[kwd]) + throw new Error(`Keyword ${kwd} is already defined`); + if (!KEYWORD_NAME.test(kwd)) + throw new Error(`Keyword ${kwd} has invalid name`); + }); + if (!def) + return; + if (def.$data && !("code" in def || "validate" in def)) { + throw new Error('$data keyword must have "code" or "validate" function'); + } + } + function addRule(keyword, definition, dataType) { + var _a; + const post = definition === null || definition === void 0 ? void 0 : definition.post; + if (dataType && post) + throw new Error('keyword with "post" flag cannot have "type"'); + const { RULES } = this; + let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t }) => t === dataType); + if (!ruleGroup) { + ruleGroup = { type: dataType, rules: [] }; + RULES.rules.push(ruleGroup); + } + RULES.keywords[keyword] = true; + if (!definition) + return; + const rule = { + keyword, + definition: { + ...definition, + type: (0, dataType_1.getJSONTypes)(definition.type), + schemaType: (0, dataType_1.getJSONTypes)(definition.schemaType) + } + }; + if (definition.before) + addBeforeRule.call(this, ruleGroup, rule, definition.before); + else + ruleGroup.rules.push(rule); + RULES.all[keyword] = rule; + (_a = definition.implements) === null || _a === void 0 ? void 0 : _a.forEach((kwd) => this.addKeyword(kwd)); + } + function addBeforeRule(ruleGroup, rule, before) { + const i = ruleGroup.rules.findIndex((_rule) => _rule.keyword === before); + if (i >= 0) { + ruleGroup.rules.splice(i, 0, rule); + } else { + ruleGroup.rules.push(rule); + this.logger.warn(`rule ${before} is not defined`); + } + } + function keywordMetaschema(def) { + let { metaSchema } = def; + if (metaSchema === void 0) + return; + if (def.$data && this.opts.$data) + metaSchema = schemaOrData(metaSchema); + def.validateSchema = this.compile(metaSchema, true); + } + var $dataRef = { + $ref: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#" + }; + function schemaOrData(schema) { + return { anyOf: [schema, $dataRef] }; + } + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/core/id.js +var require_id = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/core/id.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var def = { + keyword: "id", + code() { + throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID'); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/core/ref.js +var require_ref = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/core/ref.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.callRef = exports.getValidate = void 0; + var ref_error_1 = require_ref_error(); + var code_1 = require_code2(); + var codegen_1 = require_codegen(); + var names_1 = require_names(); + var compile_1 = require_compile(); + var util_1 = require_util(); + var def = { + keyword: "$ref", + schemaType: "string", + code(cxt) { + const { gen, schema: $ref, it: it2 } = cxt; + const { baseId, schemaEnv: env, validateName, opts, self } = it2; + const { root } = env; + if (($ref === "#" || $ref === "#/") && baseId === root.baseId) + return callRootRef(); + const schOrEnv = compile_1.resolveRef.call(self, root, baseId, $ref); + if (schOrEnv === void 0) + throw new ref_error_1.default(it2.opts.uriResolver, baseId, $ref); + if (schOrEnv instanceof compile_1.SchemaEnv) + return callValidate(schOrEnv); + return inlineRefSchema(schOrEnv); + function callRootRef() { + if (env === root) + return callRef(cxt, validateName, env, env.$async); + const rootName = gen.scopeValue("root", { ref: root }); + return callRef(cxt, (0, codegen_1._)`${rootName}.validate`, root, root.$async); + } + function callValidate(sch) { + const v = getValidate(cxt, sch); + callRef(cxt, v, sch, sch.$async); + } + function inlineRefSchema(sch) { + const schName = gen.scopeValue("schema", opts.code.source === true ? { ref: sch, code: (0, codegen_1.stringify)(sch) } : { ref: sch }); + const valid = gen.name("valid"); + const schCxt = cxt.subschema({ + schema: sch, + dataTypes: [], + schemaPath: codegen_1.nil, + topSchemaRef: schName, + errSchemaPath: $ref + }, valid); + cxt.mergeEvaluated(schCxt); + cxt.ok(valid); + } + } + }; + function getValidate(cxt, sch) { + const { gen } = cxt; + return sch.validate ? gen.scopeValue("validate", { ref: sch.validate }) : (0, codegen_1._)`${gen.scopeValue("wrapper", { ref: sch })}.validate`; + } + exports.getValidate = getValidate; + function callRef(cxt, v, sch, $async) { + const { gen, it: it2 } = cxt; + const { allErrors, schemaEnv: env, opts } = it2; + const passCxt = opts.passContext ? names_1.default.this : codegen_1.nil; + if ($async) + callAsyncRef(); + else + callSyncRef(); + function callAsyncRef() { + if (!env.$async) + throw new Error("async schema referenced by sync schema"); + const valid = gen.let("valid"); + gen.try(() => { + gen.code((0, codegen_1._)`await ${(0, code_1.callValidateCode)(cxt, v, passCxt)}`); + addEvaluatedFrom(v); + if (!allErrors) + gen.assign(valid, true); + }, (e) => { + gen.if((0, codegen_1._)`!(${e} instanceof ${it2.ValidationError})`, () => gen.throw(e)); + addErrorsFrom(e); + if (!allErrors) + gen.assign(valid, false); + }); + cxt.ok(valid); + } + function callSyncRef() { + cxt.result((0, code_1.callValidateCode)(cxt, v, passCxt), () => addEvaluatedFrom(v), () => addErrorsFrom(v)); + } + function addErrorsFrom(source) { + const errs = (0, codegen_1._)`${source}.errors`; + gen.assign(names_1.default.vErrors, (0, codegen_1._)`${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`); + gen.assign(names_1.default.errors, (0, codegen_1._)`${names_1.default.vErrors}.length`); + } + function addEvaluatedFrom(source) { + var _a; + if (!it2.opts.unevaluated) + return; + const schEvaluated = (_a = sch === null || sch === void 0 ? void 0 : sch.validate) === null || _a === void 0 ? void 0 : _a.evaluated; + if (it2.props !== true) { + if (schEvaluated && !schEvaluated.dynamicProps) { + if (schEvaluated.props !== void 0) { + it2.props = util_1.mergeEvaluated.props(gen, schEvaluated.props, it2.props); + } + } else { + const props = gen.var("props", (0, codegen_1._)`${source}.evaluated.props`); + it2.props = util_1.mergeEvaluated.props(gen, props, it2.props, codegen_1.Name); + } + } + if (it2.items !== true) { + if (schEvaluated && !schEvaluated.dynamicItems) { + if (schEvaluated.items !== void 0) { + it2.items = util_1.mergeEvaluated.items(gen, schEvaluated.items, it2.items); + } + } else { + const items = gen.var("items", (0, codegen_1._)`${source}.evaluated.items`); + it2.items = util_1.mergeEvaluated.items(gen, items, it2.items, codegen_1.Name); + } + } + } + } + exports.callRef = callRef; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/core/index.js +var require_core2 = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/core/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var id_1 = require_id(); + var ref_1 = require_ref(); + var core = [ + "$schema", + "$id", + "$defs", + "$vocabulary", + { keyword: "$comment" }, + "definitions", + id_1.default, + ref_1.default + ]; + exports.default = core; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitNumber.js +var require_limitNumber = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitNumber.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var ops = codegen_1.operators; + var KWDs = { + maximum: { okStr: "<=", ok: ops.LTE, fail: ops.GT }, + minimum: { okStr: ">=", ok: ops.GTE, fail: ops.LT }, + exclusiveMaximum: { okStr: "<", ok: ops.LT, fail: ops.GTE }, + exclusiveMinimum: { okStr: ">", ok: ops.GT, fail: ops.LTE } + }; + var error2 = { + message: ({ keyword, schemaCode }) => (0, codegen_1.str)`must be ${KWDs[keyword].okStr} ${schemaCode}`, + params: ({ keyword, schemaCode }) => (0, codegen_1._)`{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}` + }; + var def = { + keyword: Object.keys(KWDs), + type: "number", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + cxt.fail$data((0, codegen_1._)`${data} ${KWDs[keyword].fail} ${schemaCode} || isNaN(${data})`); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/multipleOf.js +var require_multipleOf = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/multipleOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var error2 = { + message: ({ schemaCode }) => (0, codegen_1.str)`must be multiple of ${schemaCode}`, + params: ({ schemaCode }) => (0, codegen_1._)`{multipleOf: ${schemaCode}}` + }; + var def = { + keyword: "multipleOf", + type: "number", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { gen, data, schemaCode, it: it2 } = cxt; + const prec = it2.opts.multipleOfPrecision; + const res = gen.let("res"); + const invalid = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`; + cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/ucs2length.js +var require_ucs2length = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/ucs2length.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + function ucs2length(str) { + const len = str.length; + let length = 0; + let pos = 0; + let value; + while (pos < len) { + length++; + value = str.charCodeAt(pos++); + if (value >= 55296 && value <= 56319 && pos < len) { + value = str.charCodeAt(pos); + if ((value & 64512) === 56320) + pos++; + } + } + return length; + } + exports.default = ucs2length; + ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default'; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitLength.js +var require_limitLength = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitLength.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var ucs2length_1 = require_ucs2length(); + var error2 = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxLength" ? "more" : "fewer"; + return (0, codegen_1.str)`must NOT have ${comp} than ${schemaCode} characters`; + }, + params: ({ schemaCode }) => (0, codegen_1._)`{limit: ${schemaCode}}` + }; + var def = { + keyword: ["maxLength", "minLength"], + type: "string", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode, it: it2 } = cxt; + const op = keyword === "maxLength" ? codegen_1.operators.GT : codegen_1.operators.LT; + const len = it2.opts.unicode === false ? (0, codegen_1._)`${data}.length` : (0, codegen_1._)`${(0, util_1.useFunc)(cxt.gen, ucs2length_1.default)}(${data})`; + cxt.fail$data((0, codegen_1._)`${len} ${op} ${schemaCode}`); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/pattern.js +var require_pattern = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/pattern.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code2(); + var util_1 = require_util(); + var codegen_1 = require_codegen(); + var error2 = { + message: ({ schemaCode }) => (0, codegen_1.str)`must match pattern "${schemaCode}"`, + params: ({ schemaCode }) => (0, codegen_1._)`{pattern: ${schemaCode}}` + }; + var def = { + keyword: "pattern", + type: "string", + schemaType: "string", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schema, schemaCode, it: it2 } = cxt; + const u = it2.opts.unicodeRegExp ? "u" : ""; + if ($data) { + const { regExp } = it2.opts.code; + const regExpCode = regExp.code === "new RegExp" ? (0, codegen_1._)`new RegExp` : (0, util_1.useFunc)(gen, regExp); + const valid = gen.let("valid"); + gen.try(() => gen.assign(valid, (0, codegen_1._)`${regExpCode}(${schemaCode}, ${u}).test(${data})`), () => gen.assign(valid, false)); + cxt.fail$data((0, codegen_1._)`!${valid}`); + } else { + const regExp = (0, code_1.usePattern)(cxt, schema); + cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitProperties.js +var require_limitProperties = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitProperties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var error2 = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxProperties" ? "more" : "fewer"; + return (0, codegen_1.str)`must NOT have ${comp} than ${schemaCode} properties`; + }, + params: ({ schemaCode }) => (0, codegen_1._)`{limit: ${schemaCode}}` + }; + var def = { + keyword: ["maxProperties", "minProperties"], + type: "object", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + const op = keyword === "maxProperties" ? codegen_1.operators.GT : codegen_1.operators.LT; + cxt.fail$data((0, codegen_1._)`Object.keys(${data}).length ${op} ${schemaCode}`); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/required.js +var require_required = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/required.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code2(); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var error2 = { + message: ({ params: { missingProperty } }) => (0, codegen_1.str)`must have required property '${missingProperty}'`, + params: ({ params: { missingProperty } }) => (0, codegen_1._)`{missingProperty: ${missingProperty}}` + }; + var def = { + keyword: "required", + type: "object", + schemaType: "array", + $data: true, + error: error2, + code(cxt) { + const { gen, schema, schemaCode, data, $data, it: it2 } = cxt; + const { opts } = it2; + if (!$data && schema.length === 0) + return; + const useLoop = schema.length >= opts.loopRequired; + if (it2.allErrors) + allErrorsMode(); + else + exitOnErrorMode(); + if (opts.strictRequired) { + const props = cxt.parentSchema.properties; + const { definedProperties } = cxt.it; + for (const requiredKey of schema) { + if ((props === null || props === void 0 ? void 0 : props[requiredKey]) === void 0 && !definedProperties.has(requiredKey)) { + const schemaPath = it2.schemaEnv.baseId + it2.errSchemaPath; + const msg = `required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)`; + (0, util_1.checkStrictMode)(it2, msg, it2.opts.strictRequired); + } + } + } + function allErrorsMode() { + if (useLoop || $data) { + cxt.block$data(codegen_1.nil, loopAllRequired); + } else { + for (const prop of schema) { + (0, code_1.checkReportMissingProp)(cxt, prop); + } + } + } + function exitOnErrorMode() { + const missing = gen.let("missing"); + if (useLoop || $data) { + const valid = gen.let("valid", true); + cxt.block$data(valid, () => loopUntilMissing(missing, valid)); + cxt.ok(valid); + } else { + gen.if((0, code_1.checkMissingProp)(cxt, schema, missing)); + (0, code_1.reportMissingProp)(cxt, missing); + gen.else(); + } + } + function loopAllRequired() { + gen.forOf("prop", schemaCode, (prop) => { + cxt.setParams({ missingProperty: prop }); + gen.if((0, code_1.noPropertyInData)(gen, data, prop, opts.ownProperties), () => cxt.error()); + }); + } + function loopUntilMissing(missing, valid) { + cxt.setParams({ missingProperty: missing }); + gen.forOf(missing, schemaCode, () => { + gen.assign(valid, (0, code_1.propertyInData)(gen, data, missing, opts.ownProperties)); + gen.if((0, codegen_1.not)(valid), () => { + cxt.error(); + gen.break(); + }); + }, codegen_1.nil); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitItems.js +var require_limitItems = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/limitItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var error2 = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxItems" ? "more" : "fewer"; + return (0, codegen_1.str)`must NOT have ${comp} than ${schemaCode} items`; + }, + params: ({ schemaCode }) => (0, codegen_1._)`{limit: ${schemaCode}}` + }; + var def = { + keyword: ["maxItems", "minItems"], + type: "array", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + const op = keyword === "maxItems" ? codegen_1.operators.GT : codegen_1.operators.LT; + cxt.fail$data((0, codegen_1._)`${data}.length ${op} ${schemaCode}`); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/equal.js +var require_equal = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/runtime/equal.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var equal = require_fast_deep_equal(); + equal.code = 'require("ajv/dist/runtime/equal").default'; + exports.default = equal; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js +var require_uniqueItems = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var dataType_1 = require_dataType(); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var equal_1 = require_equal(); + var error2 = { + message: ({ params: { i, j: j2 } }) => (0, codegen_1.str)`must NOT have duplicate items (items ## ${j2} and ${i} are identical)`, + params: ({ params: { i, j: j2 } }) => (0, codegen_1._)`{i: ${i}, j: ${j2}}` + }; + var def = { + keyword: "uniqueItems", + type: "array", + schemaType: "boolean", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schema, parentSchema, schemaCode, it: it2 } = cxt; + if (!$data && !schema) + return; + const valid = gen.let("valid"); + const itemTypes = parentSchema.items ? (0, dataType_1.getSchemaTypes)(parentSchema.items) : []; + cxt.block$data(valid, validateUniqueItems, (0, codegen_1._)`${schemaCode} === false`); + cxt.ok(valid); + function validateUniqueItems() { + const i = gen.let("i", (0, codegen_1._)`${data}.length`); + const j2 = gen.let("j"); + cxt.setParams({ i, j: j2 }); + gen.assign(valid, true); + gen.if((0, codegen_1._)`${i} > 1`, () => (canOptimize() ? loopN : loopN2)(i, j2)); + } + function canOptimize() { + return itemTypes.length > 0 && !itemTypes.some((t) => t === "object" || t === "array"); + } + function loopN(i, j2) { + const item = gen.name("item"); + const wrongType = (0, dataType_1.checkDataTypes)(itemTypes, item, it2.opts.strictNumbers, dataType_1.DataType.Wrong); + const indices = gen.const("indices", (0, codegen_1._)`{}`); + gen.for((0, codegen_1._)`;${i}--;`, () => { + gen.let(item, (0, codegen_1._)`${data}[${i}]`); + gen.if(wrongType, (0, codegen_1._)`continue`); + if (itemTypes.length > 1) + gen.if((0, codegen_1._)`typeof ${item} == "string"`, (0, codegen_1._)`${item} += "_"`); + gen.if((0, codegen_1._)`typeof ${indices}[${item}] == "number"`, () => { + gen.assign(j2, (0, codegen_1._)`${indices}[${item}]`); + cxt.error(); + gen.assign(valid, false).break(); + }).code((0, codegen_1._)`${indices}[${item}] = ${i}`); + }); + } + function loopN2(i, j2) { + const eql = (0, util_1.useFunc)(gen, equal_1.default); + const outer = gen.name("outer"); + gen.label(outer).for((0, codegen_1._)`;${i}--;`, () => gen.for((0, codegen_1._)`${j2} = ${i}; ${j2}--;`, () => gen.if((0, codegen_1._)`${eql}(${data}[${i}], ${data}[${j2}])`, () => { + cxt.error(); + gen.assign(valid, false).break(outer); + }))); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/const.js +var require_const = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/const.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var equal_1 = require_equal(); + var error2 = { + message: "must be equal to constant", + params: ({ schemaCode }) => (0, codegen_1._)`{allowedValue: ${schemaCode}}` + }; + var def = { + keyword: "const", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schemaCode, schema } = cxt; + if ($data || schema && typeof schema == "object") { + cxt.fail$data((0, codegen_1._)`!${(0, util_1.useFunc)(gen, equal_1.default)}(${data}, ${schemaCode})`); + } else { + cxt.fail((0, codegen_1._)`${schema} !== ${data}`); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/enum.js +var require_enum = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/enum.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var equal_1 = require_equal(); + var error2 = { + message: "must be equal to one of the allowed values", + params: ({ schemaCode }) => (0, codegen_1._)`{allowedValues: ${schemaCode}}` + }; + var def = { + keyword: "enum", + schemaType: "array", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schema, schemaCode, it: it2 } = cxt; + if (!$data && schema.length === 0) + throw new Error("enum must have non-empty array"); + const useLoop = schema.length >= it2.opts.loopEnum; + let eql; + const getEql = () => eql !== null && eql !== void 0 ? eql : eql = (0, util_1.useFunc)(gen, equal_1.default); + let valid; + if (useLoop || $data) { + valid = gen.let("valid"); + cxt.block$data(valid, loopEnum); + } else { + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const vSchema = gen.const("vSchema", schemaCode); + valid = (0, codegen_1.or)(...schema.map((_x, i) => equalCode(vSchema, i))); + } + cxt.pass(valid); + function loopEnum() { + gen.assign(valid, false); + gen.forOf("v", schemaCode, (v) => gen.if((0, codegen_1._)`${getEql()}(${data}, ${v})`, () => gen.assign(valid, true).break())); + } + function equalCode(vSchema, i) { + const sch = schema[i]; + return typeof sch === "object" && sch !== null ? (0, codegen_1._)`${getEql()}(${data}, ${vSchema}[${i}])` : (0, codegen_1._)`${data} === ${sch}`; + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/index.js +var require_validation = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/validation/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var limitNumber_1 = require_limitNumber(); + var multipleOf_1 = require_multipleOf(); + var limitLength_1 = require_limitLength(); + var pattern_1 = require_pattern(); + var limitProperties_1 = require_limitProperties(); + var required_1 = require_required(); + var limitItems_1 = require_limitItems(); + var uniqueItems_1 = require_uniqueItems(); + var const_1 = require_const(); + var enum_1 = require_enum(); + var validation = [ + // number + limitNumber_1.default, + multipleOf_1.default, + // string + limitLength_1.default, + pattern_1.default, + // object + limitProperties_1.default, + required_1.default, + // array + limitItems_1.default, + uniqueItems_1.default, + // any + { keyword: "type", schemaType: ["string", "array"] }, + { keyword: "nullable", schemaType: "boolean" }, + const_1.default, + enum_1.default + ]; + exports.default = validation; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js +var require_additionalItems = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateAdditionalItems = void 0; + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var error2 = { + message: ({ params: { len } }) => (0, codegen_1.str)`must NOT have more than ${len} items`, + params: ({ params: { len } }) => (0, codegen_1._)`{limit: ${len}}` + }; + var def = { + keyword: "additionalItems", + type: "array", + schemaType: ["boolean", "object"], + before: "uniqueItems", + error: error2, + code(cxt) { + const { parentSchema, it: it2 } = cxt; + const { items } = parentSchema; + if (!Array.isArray(items)) { + (0, util_1.checkStrictMode)(it2, '"additionalItems" is ignored when "items" is not an array of schemas'); + return; + } + validateAdditionalItems(cxt, items); + } + }; + function validateAdditionalItems(cxt, items) { + const { gen, schema, data, keyword, it: it2 } = cxt; + it2.items = true; + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + if (schema === false) { + cxt.setParams({ len: items.length }); + cxt.pass((0, codegen_1._)`${len} <= ${items.length}`); + } else if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it2, schema)) { + const valid = gen.var("valid", (0, codegen_1._)`${len} <= ${items.length}`); + gen.if((0, codegen_1.not)(valid), () => validateItems(valid)); + cxt.ok(valid); + } + function validateItems(valid) { + gen.forRange("i", items.length, len, (i) => { + cxt.subschema({ keyword, dataProp: i, dataPropType: util_1.Type.Num }, valid); + if (!it2.allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + }); + } + } + exports.validateAdditionalItems = validateAdditionalItems; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/items.js +var require_items = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/items.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateTuple = void 0; + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var code_1 = require_code2(); + var def = { + keyword: "items", + type: "array", + schemaType: ["object", "array", "boolean"], + before: "uniqueItems", + code(cxt) { + const { schema, it: it2 } = cxt; + if (Array.isArray(schema)) + return validateTuple(cxt, "additionalItems", schema); + it2.items = true; + if ((0, util_1.alwaysValidSchema)(it2, schema)) + return; + cxt.ok((0, code_1.validateArray)(cxt)); + } + }; + function validateTuple(cxt, extraItems, schArr = cxt.schema) { + const { gen, parentSchema, data, keyword, it: it2 } = cxt; + checkStrictTuple(parentSchema); + if (it2.opts.unevaluated && schArr.length && it2.items !== true) { + it2.items = util_1.mergeEvaluated.items(gen, schArr.length, it2.items); + } + const valid = gen.name("valid"); + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + schArr.forEach((sch, i) => { + if ((0, util_1.alwaysValidSchema)(it2, sch)) + return; + gen.if((0, codegen_1._)`${len} > ${i}`, () => cxt.subschema({ + keyword, + schemaProp: i, + dataProp: i + }, valid)); + cxt.ok(valid); + }); + function checkStrictTuple(sch) { + const { opts, errSchemaPath } = it2; + const l = schArr.length; + const fullTuple = l === sch.minItems && (l === sch.maxItems || sch[extraItems] === false); + if (opts.strictTuples && !fullTuple) { + const msg = `"${keyword}" is ${l}-tuple, but minItems or maxItems/${extraItems} are not specified or different at path "${errSchemaPath}"`; + (0, util_1.checkStrictMode)(it2, msg, opts.strictTuples); + } + } + } + exports.validateTuple = validateTuple; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js +var require_prefixItems = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var items_1 = require_items(); + var def = { + keyword: "prefixItems", + type: "array", + schemaType: ["array"], + before: "uniqueItems", + code: (cxt) => (0, items_1.validateTuple)(cxt, "items") + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/items2020.js +var require_items2020 = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/items2020.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var code_1 = require_code2(); + var additionalItems_1 = require_additionalItems(); + var error2 = { + message: ({ params: { len } }) => (0, codegen_1.str)`must NOT have more than ${len} items`, + params: ({ params: { len } }) => (0, codegen_1._)`{limit: ${len}}` + }; + var def = { + keyword: "items", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + error: error2, + code(cxt) { + const { schema, parentSchema, it: it2 } = cxt; + const { prefixItems } = parentSchema; + it2.items = true; + if ((0, util_1.alwaysValidSchema)(it2, schema)) + return; + if (prefixItems) + (0, additionalItems_1.validateAdditionalItems)(cxt, prefixItems); + else + cxt.ok((0, code_1.validateArray)(cxt)); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/contains.js +var require_contains = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/contains.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var error2 = { + message: ({ params: { min, max } }) => max === void 0 ? (0, codegen_1.str)`must contain at least ${min} valid item(s)` : (0, codegen_1.str)`must contain at least ${min} and no more than ${max} valid item(s)`, + params: ({ params: { min, max } }) => max === void 0 ? (0, codegen_1._)`{minContains: ${min}}` : (0, codegen_1._)`{minContains: ${min}, maxContains: ${max}}` + }; + var def = { + keyword: "contains", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + trackErrors: true, + error: error2, + code(cxt) { + const { gen, schema, parentSchema, data, it: it2 } = cxt; + let min; + let max; + const { minContains, maxContains } = parentSchema; + if (it2.opts.next) { + min = minContains === void 0 ? 1 : minContains; + max = maxContains; + } else { + min = 1; + } + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + cxt.setParams({ min, max }); + if (max === void 0 && min === 0) { + (0, util_1.checkStrictMode)(it2, `"minContains" == 0 without "maxContains": "contains" keyword ignored`); + return; + } + if (max !== void 0 && min > max) { + (0, util_1.checkStrictMode)(it2, `"minContains" > "maxContains" is always invalid`); + cxt.fail(); + return; + } + if ((0, util_1.alwaysValidSchema)(it2, schema)) { + let cond = (0, codegen_1._)`${len} >= ${min}`; + if (max !== void 0) + cond = (0, codegen_1._)`${cond} && ${len} <= ${max}`; + cxt.pass(cond); + return; + } + it2.items = true; + const valid = gen.name("valid"); + if (max === void 0 && min === 1) { + validateItems(valid, () => gen.if(valid, () => gen.break())); + } else if (min === 0) { + gen.let(valid, true); + if (max !== void 0) + gen.if((0, codegen_1._)`${data}.length > 0`, validateItemsWithCount); + } else { + gen.let(valid, false); + validateItemsWithCount(); + } + cxt.result(valid, () => cxt.reset()); + function validateItemsWithCount() { + const schValid = gen.name("_valid"); + const count = gen.let("count", 0); + validateItems(schValid, () => gen.if(schValid, () => checkLimits(count))); + } + function validateItems(_valid, block) { + gen.forRange("i", 0, len, (i) => { + cxt.subschema({ + keyword: "contains", + dataProp: i, + dataPropType: util_1.Type.Num, + compositeRule: true + }, _valid); + block(); + }); + } + function checkLimits(count) { + gen.code((0, codegen_1._)`${count}++`); + if (max === void 0) { + gen.if((0, codegen_1._)`${count} >= ${min}`, () => gen.assign(valid, true).break()); + } else { + gen.if((0, codegen_1._)`${count} > ${max}`, () => gen.assign(valid, false).break()); + if (min === 1) + gen.assign(valid, true); + else + gen.if((0, codegen_1._)`${count} >= ${min}`, () => gen.assign(valid, true)); + } + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/dependencies.js +var require_dependencies = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/dependencies.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateSchemaDeps = exports.validatePropertyDeps = exports.error = void 0; + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var code_1 = require_code2(); + exports.error = { + message: ({ params: { property, depsCount, deps } }) => { + const property_ies = depsCount === 1 ? "property" : "properties"; + return (0, codegen_1.str)`must have ${property_ies} ${deps} when property ${property} is present`; + }, + params: ({ params: { property, depsCount, deps, missingProperty } }) => (0, codegen_1._)`{property: ${property}, + missingProperty: ${missingProperty}, + depsCount: ${depsCount}, + deps: ${deps}}` + // TODO change to reference + }; + var def = { + keyword: "dependencies", + type: "object", + schemaType: "object", + error: exports.error, + code(cxt) { + const [propDeps, schDeps] = splitDependencies(cxt); + validatePropertyDeps(cxt, propDeps); + validateSchemaDeps(cxt, schDeps); + } + }; + function splitDependencies({ schema }) { + const propertyDeps = {}; + const schemaDeps = {}; + for (const key in schema) { + if (key === "__proto__") + continue; + const deps = Array.isArray(schema[key]) ? propertyDeps : schemaDeps; + deps[key] = schema[key]; + } + return [propertyDeps, schemaDeps]; + } + function validatePropertyDeps(cxt, propertyDeps = cxt.schema) { + const { gen, data, it: it2 } = cxt; + if (Object.keys(propertyDeps).length === 0) + return; + const missing = gen.let("missing"); + for (const prop in propertyDeps) { + const deps = propertyDeps[prop]; + if (deps.length === 0) + continue; + const hasProperty = (0, code_1.propertyInData)(gen, data, prop, it2.opts.ownProperties); + cxt.setParams({ + property: prop, + depsCount: deps.length, + deps: deps.join(", ") + }); + if (it2.allErrors) { + gen.if(hasProperty, () => { + for (const depProp of deps) { + (0, code_1.checkReportMissingProp)(cxt, depProp); + } + }); + } else { + gen.if((0, codegen_1._)`${hasProperty} && (${(0, code_1.checkMissingProp)(cxt, deps, missing)})`); + (0, code_1.reportMissingProp)(cxt, missing); + gen.else(); + } + } + } + exports.validatePropertyDeps = validatePropertyDeps; + function validateSchemaDeps(cxt, schemaDeps = cxt.schema) { + const { gen, data, keyword, it: it2 } = cxt; + const valid = gen.name("valid"); + for (const prop in schemaDeps) { + if ((0, util_1.alwaysValidSchema)(it2, schemaDeps[prop])) + continue; + gen.if( + (0, code_1.propertyInData)(gen, data, prop, it2.opts.ownProperties), + () => { + const schCxt = cxt.subschema({ keyword, schemaProp: prop }, valid); + cxt.mergeValidEvaluated(schCxt, valid); + }, + () => gen.var(valid, true) + // TODO var + ); + cxt.ok(valid); + } + } + exports.validateSchemaDeps = validateSchemaDeps; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js +var require_propertyNames = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var error2 = { + message: "property name must be valid", + params: ({ params }) => (0, codegen_1._)`{propertyName: ${params.propertyName}}` + }; + var def = { + keyword: "propertyNames", + type: "object", + schemaType: ["object", "boolean"], + error: error2, + code(cxt) { + const { gen, schema, data, it: it2 } = cxt; + if ((0, util_1.alwaysValidSchema)(it2, schema)) + return; + const valid = gen.name("valid"); + gen.forIn("key", data, (key) => { + cxt.setParams({ propertyName: key }); + cxt.subschema({ + keyword: "propertyNames", + data: key, + dataTypes: ["string"], + propertyName: key, + compositeRule: true + }, valid); + gen.if((0, codegen_1.not)(valid), () => { + cxt.error(true); + if (!it2.allErrors) + gen.break(); + }); + }); + cxt.ok(valid); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js +var require_additionalProperties = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code2(); + var codegen_1 = require_codegen(); + var names_1 = require_names(); + var util_1 = require_util(); + var error2 = { + message: "must NOT have additional properties", + params: ({ params }) => (0, codegen_1._)`{additionalProperty: ${params.additionalProperty}}` + }; + var def = { + keyword: "additionalProperties", + type: ["object"], + schemaType: ["boolean", "object"], + allowUndefined: true, + trackErrors: true, + error: error2, + code(cxt) { + const { gen, schema, parentSchema, data, errsCount, it: it2 } = cxt; + if (!errsCount) + throw new Error("ajv implementation error"); + const { allErrors, opts } = it2; + it2.props = true; + if (opts.removeAdditional !== "all" && (0, util_1.alwaysValidSchema)(it2, schema)) + return; + const props = (0, code_1.allSchemaProperties)(parentSchema.properties); + const patProps = (0, code_1.allSchemaProperties)(parentSchema.patternProperties); + checkAdditionalProperties(); + cxt.ok((0, codegen_1._)`${errsCount} === ${names_1.default.errors}`); + function checkAdditionalProperties() { + gen.forIn("key", data, (key) => { + if (!props.length && !patProps.length) + additionalPropertyCode(key); + else + gen.if(isAdditional(key), () => additionalPropertyCode(key)); + }); + } + function isAdditional(key) { + let definedProp; + if (props.length > 8) { + const propsSchema = (0, util_1.schemaRefOrVal)(it2, parentSchema.properties, "properties"); + definedProp = (0, code_1.isOwnProperty)(gen, propsSchema, key); + } else if (props.length) { + definedProp = (0, codegen_1.or)(...props.map((p2) => (0, codegen_1._)`${key} === ${p2}`)); + } else { + definedProp = codegen_1.nil; + } + if (patProps.length) { + definedProp = (0, codegen_1.or)(definedProp, ...patProps.map((p2) => (0, codegen_1._)`${(0, code_1.usePattern)(cxt, p2)}.test(${key})`)); + } + return (0, codegen_1.not)(definedProp); + } + function deleteAdditional(key) { + gen.code((0, codegen_1._)`delete ${data}[${key}]`); + } + function additionalPropertyCode(key) { + if (opts.removeAdditional === "all" || opts.removeAdditional && schema === false) { + deleteAdditional(key); + return; + } + if (schema === false) { + cxt.setParams({ additionalProperty: key }); + cxt.error(); + if (!allErrors) + gen.break(); + return; + } + if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it2, schema)) { + const valid = gen.name("valid"); + if (opts.removeAdditional === "failing") { + applyAdditionalSchema(key, valid, false); + gen.if((0, codegen_1.not)(valid), () => { + cxt.reset(); + deleteAdditional(key); + }); + } else { + applyAdditionalSchema(key, valid); + if (!allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + } + } + } + function applyAdditionalSchema(key, valid, errors) { + const subschema = { + keyword: "additionalProperties", + dataProp: key, + dataPropType: util_1.Type.Str + }; + if (errors === false) { + Object.assign(subschema, { + compositeRule: true, + createErrors: false, + allErrors: false + }); + } + cxt.subschema(subschema, valid); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/properties.js +var require_properties = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/properties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var validate_1 = require_validate(); + var code_1 = require_code2(); + var util_1 = require_util(); + var additionalProperties_1 = require_additionalProperties(); + var def = { + keyword: "properties", + type: "object", + schemaType: "object", + code(cxt) { + const { gen, schema, parentSchema, data, it: it2 } = cxt; + if (it2.opts.removeAdditional === "all" && parentSchema.additionalProperties === void 0) { + additionalProperties_1.default.code(new validate_1.KeywordCxt(it2, additionalProperties_1.default, "additionalProperties")); + } + const allProps = (0, code_1.allSchemaProperties)(schema); + for (const prop of allProps) { + it2.definedProperties.add(prop); + } + if (it2.opts.unevaluated && allProps.length && it2.props !== true) { + it2.props = util_1.mergeEvaluated.props(gen, (0, util_1.toHash)(allProps), it2.props); + } + const properties = allProps.filter((p2) => !(0, util_1.alwaysValidSchema)(it2, schema[p2])); + if (properties.length === 0) + return; + const valid = gen.name("valid"); + for (const prop of properties) { + if (hasDefault(prop)) { + applyPropertySchema(prop); + } else { + gen.if((0, code_1.propertyInData)(gen, data, prop, it2.opts.ownProperties)); + applyPropertySchema(prop); + if (!it2.allErrors) + gen.else().var(valid, true); + gen.endIf(); + } + cxt.it.definedProperties.add(prop); + cxt.ok(valid); + } + function hasDefault(prop) { + return it2.opts.useDefaults && !it2.compositeRule && schema[prop].default !== void 0; + } + function applyPropertySchema(prop) { + cxt.subschema({ + keyword: "properties", + schemaProp: prop, + dataProp: prop + }, valid); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js +var require_patternProperties = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code2(); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var util_2 = require_util(); + var def = { + keyword: "patternProperties", + type: "object", + schemaType: "object", + code(cxt) { + const { gen, schema, data, parentSchema, it: it2 } = cxt; + const { opts } = it2; + const patterns = (0, code_1.allSchemaProperties)(schema); + const alwaysValidPatterns = patterns.filter((p2) => (0, util_1.alwaysValidSchema)(it2, schema[p2])); + if (patterns.length === 0 || alwaysValidPatterns.length === patterns.length && (!it2.opts.unevaluated || it2.props === true)) { + return; + } + const checkProperties = opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties; + const valid = gen.name("valid"); + if (it2.props !== true && !(it2.props instanceof codegen_1.Name)) { + it2.props = (0, util_2.evaluatedPropsToName)(gen, it2.props); + } + const { props } = it2; + validatePatternProperties(); + function validatePatternProperties() { + for (const pat of patterns) { + if (checkProperties) + checkMatchingProperties(pat); + if (it2.allErrors) { + validateProperties(pat); + } else { + gen.var(valid, true); + validateProperties(pat); + gen.if(valid); + } + } + } + function checkMatchingProperties(pat) { + for (const prop in checkProperties) { + if (new RegExp(pat).test(prop)) { + (0, util_1.checkStrictMode)(it2, `property ${prop} matches pattern ${pat} (use allowMatchingProperties)`); + } + } + } + function validateProperties(pat) { + gen.forIn("key", data, (key) => { + gen.if((0, codegen_1._)`${(0, code_1.usePattern)(cxt, pat)}.test(${key})`, () => { + const alwaysValid = alwaysValidPatterns.includes(pat); + if (!alwaysValid) { + cxt.subschema({ + keyword: "patternProperties", + schemaProp: pat, + dataProp: key, + dataPropType: util_2.Type.Str + }, valid); + } + if (it2.opts.unevaluated && props !== true) { + gen.assign((0, codegen_1._)`${props}[${key}]`, true); + } else if (!alwaysValid && !it2.allErrors) { + gen.if((0, codegen_1.not)(valid), () => gen.break()); + } + }); + }); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/not.js +var require_not = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/not.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var util_1 = require_util(); + var def = { + keyword: "not", + schemaType: ["object", "boolean"], + trackErrors: true, + code(cxt) { + const { gen, schema, it: it2 } = cxt; + if ((0, util_1.alwaysValidSchema)(it2, schema)) { + cxt.fail(); + return; + } + const valid = gen.name("valid"); + cxt.subschema({ + keyword: "not", + compositeRule: true, + createErrors: false, + allErrors: false + }, valid); + cxt.failResult(valid, () => cxt.reset(), () => cxt.error()); + }, + error: { message: "must NOT be valid" } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/anyOf.js +var require_anyOf = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/anyOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code2(); + var def = { + keyword: "anyOf", + schemaType: "array", + trackErrors: true, + code: code_1.validateUnion, + error: { message: "must match a schema in anyOf" } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/oneOf.js +var require_oneOf = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/oneOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var error2 = { + message: "must match exactly one schema in oneOf", + params: ({ params }) => (0, codegen_1._)`{passingSchemas: ${params.passing}}` + }; + var def = { + keyword: "oneOf", + schemaType: "array", + trackErrors: true, + error: error2, + code(cxt) { + const { gen, schema, parentSchema, it: it2 } = cxt; + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + if (it2.opts.discriminator && parentSchema.discriminator) + return; + const schArr = schema; + const valid = gen.let("valid", false); + const passing = gen.let("passing", null); + const schValid = gen.name("_valid"); + cxt.setParams({ passing }); + gen.block(validateOneOf); + cxt.result(valid, () => cxt.reset(), () => cxt.error(true)); + function validateOneOf() { + schArr.forEach((sch, i) => { + let schCxt; + if ((0, util_1.alwaysValidSchema)(it2, sch)) { + gen.var(schValid, true); + } else { + schCxt = cxt.subschema({ + keyword: "oneOf", + schemaProp: i, + compositeRule: true + }, schValid); + } + if (i > 0) { + gen.if((0, codegen_1._)`${schValid} && ${valid}`).assign(valid, false).assign(passing, (0, codegen_1._)`[${passing}, ${i}]`).else(); + } + gen.if(schValid, () => { + gen.assign(valid, true); + gen.assign(passing, i); + if (schCxt) + cxt.mergeEvaluated(schCxt, codegen_1.Name); + }); + }); + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/allOf.js +var require_allOf = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/allOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var util_1 = require_util(); + var def = { + keyword: "allOf", + schemaType: "array", + code(cxt) { + const { gen, schema, it: it2 } = cxt; + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const valid = gen.name("valid"); + schema.forEach((sch, i) => { + if ((0, util_1.alwaysValidSchema)(it2, sch)) + return; + const schCxt = cxt.subschema({ keyword: "allOf", schemaProp: i }, valid); + cxt.ok(valid); + cxt.mergeEvaluated(schCxt); + }); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/if.js +var require_if = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/if.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var util_1 = require_util(); + var error2 = { + message: ({ params }) => (0, codegen_1.str)`must match "${params.ifClause}" schema`, + params: ({ params }) => (0, codegen_1._)`{failingKeyword: ${params.ifClause}}` + }; + var def = { + keyword: "if", + schemaType: ["object", "boolean"], + trackErrors: true, + error: error2, + code(cxt) { + const { gen, parentSchema, it: it2 } = cxt; + if (parentSchema.then === void 0 && parentSchema.else === void 0) { + (0, util_1.checkStrictMode)(it2, '"if" without "then" and "else" is ignored'); + } + const hasThen = hasSchema(it2, "then"); + const hasElse = hasSchema(it2, "else"); + if (!hasThen && !hasElse) + return; + const valid = gen.let("valid", true); + const schValid = gen.name("_valid"); + validateIf(); + cxt.reset(); + if (hasThen && hasElse) { + const ifClause = gen.let("ifClause"); + cxt.setParams({ ifClause }); + gen.if(schValid, validateClause("then", ifClause), validateClause("else", ifClause)); + } else if (hasThen) { + gen.if(schValid, validateClause("then")); + } else { + gen.if((0, codegen_1.not)(schValid), validateClause("else")); + } + cxt.pass(valid, () => cxt.error(true)); + function validateIf() { + const schCxt = cxt.subschema({ + keyword: "if", + compositeRule: true, + createErrors: false, + allErrors: false + }, schValid); + cxt.mergeEvaluated(schCxt); + } + function validateClause(keyword, ifClause) { + return () => { + const schCxt = cxt.subschema({ keyword }, schValid); + gen.assign(valid, schValid); + cxt.mergeValidEvaluated(schCxt, valid); + if (ifClause) + gen.assign(ifClause, (0, codegen_1._)`${keyword}`); + else + cxt.setParams({ ifClause: keyword }); + }; + } + } + }; + function hasSchema(it2, keyword) { + const schema = it2.schema[keyword]; + return schema !== void 0 && !(0, util_1.alwaysValidSchema)(it2, schema); + } + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/thenElse.js +var require_thenElse = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/thenElse.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var util_1 = require_util(); + var def = { + keyword: ["then", "else"], + schemaType: ["object", "boolean"], + code({ keyword, parentSchema, it: it2 }) { + if (parentSchema.if === void 0) + (0, util_1.checkStrictMode)(it2, `"${keyword}" without "if" is ignored`); + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/index.js +var require_applicator = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/applicator/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var additionalItems_1 = require_additionalItems(); + var prefixItems_1 = require_prefixItems(); + var items_1 = require_items(); + var items2020_1 = require_items2020(); + var contains_1 = require_contains(); + var dependencies_1 = require_dependencies(); + var propertyNames_1 = require_propertyNames(); + var additionalProperties_1 = require_additionalProperties(); + var properties_1 = require_properties(); + var patternProperties_1 = require_patternProperties(); + var not_1 = require_not(); + var anyOf_1 = require_anyOf(); + var oneOf_1 = require_oneOf(); + var allOf_1 = require_allOf(); + var if_1 = require_if(); + var thenElse_1 = require_thenElse(); + function getApplicator(draft2020 = false) { + const applicator = [ + // any + not_1.default, + anyOf_1.default, + oneOf_1.default, + allOf_1.default, + if_1.default, + thenElse_1.default, + // object + propertyNames_1.default, + additionalProperties_1.default, + dependencies_1.default, + properties_1.default, + patternProperties_1.default + ]; + if (draft2020) + applicator.push(prefixItems_1.default, items2020_1.default); + else + applicator.push(additionalItems_1.default, items_1.default); + applicator.push(contains_1.default); + return applicator; + } + exports.default = getApplicator; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/format/format.js +var require_format = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/format/format.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var error2 = { + message: ({ schemaCode }) => (0, codegen_1.str)`must match format "${schemaCode}"`, + params: ({ schemaCode }) => (0, codegen_1._)`{format: ${schemaCode}}` + }; + var def = { + keyword: "format", + type: ["number", "string"], + schemaType: "string", + $data: true, + error: error2, + code(cxt, ruleType) { + const { gen, data, $data, schema, schemaCode, it: it2 } = cxt; + const { opts, errSchemaPath, schemaEnv, self } = it2; + if (!opts.validateFormats) + return; + if ($data) + validate$DataFormat(); + else + validateFormat(); + function validate$DataFormat() { + const fmts = gen.scopeValue("formats", { + ref: self.formats, + code: opts.code.formats + }); + const fDef = gen.const("fDef", (0, codegen_1._)`${fmts}[${schemaCode}]`); + const fType = gen.let("fType"); + const format = gen.let("format"); + gen.if((0, codegen_1._)`typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`, () => gen.assign(fType, (0, codegen_1._)`${fDef}.type || "string"`).assign(format, (0, codegen_1._)`${fDef}.validate`), () => gen.assign(fType, (0, codegen_1._)`"string"`).assign(format, fDef)); + cxt.fail$data((0, codegen_1.or)(unknownFmt(), invalidFmt())); + function unknownFmt() { + if (opts.strictSchema === false) + return codegen_1.nil; + return (0, codegen_1._)`${schemaCode} && !${format}`; + } + function invalidFmt() { + const callFormat = schemaEnv.$async ? (0, codegen_1._)`(${fDef}.async ? await ${format}(${data}) : ${format}(${data}))` : (0, codegen_1._)`${format}(${data})`; + const validData = (0, codegen_1._)`(typeof ${format} == "function" ? ${callFormat} : ${format}.test(${data}))`; + return (0, codegen_1._)`${format} && ${format} !== true && ${fType} === ${ruleType} && !${validData}`; + } + } + function validateFormat() { + const formatDef = self.formats[schema]; + if (!formatDef) { + unknownFormat(); + return; + } + if (formatDef === true) + return; + const [fmtType, format, fmtRef] = getFormat(formatDef); + if (fmtType === ruleType) + cxt.pass(validCondition()); + function unknownFormat() { + if (opts.strictSchema === false) { + self.logger.warn(unknownMsg()); + return; + } + throw new Error(unknownMsg()); + function unknownMsg() { + return `unknown format "${schema}" ignored in schema at path "${errSchemaPath}"`; + } + } + function getFormat(fmtDef) { + const code = fmtDef instanceof RegExp ? (0, codegen_1.regexpCode)(fmtDef) : opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(schema)}` : void 0; + const fmt = gen.scopeValue("formats", { key: schema, ref: fmtDef, code }); + if (typeof fmtDef == "object" && !(fmtDef instanceof RegExp)) { + return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._)`${fmt}.validate`]; + } + return ["string", fmtDef, fmt]; + } + function validCondition() { + if (typeof formatDef == "object" && !(formatDef instanceof RegExp) && formatDef.async) { + if (!schemaEnv.$async) + throw new Error("async format in sync schema"); + return (0, codegen_1._)`await ${fmtRef}(${data})`; + } + return typeof format == "function" ? (0, codegen_1._)`${fmtRef}(${data})` : (0, codegen_1._)`${fmtRef}.test(${data})`; + } + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/format/index.js +var require_format2 = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/format/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var format_1 = require_format(); + var format = [format_1.default]; + exports.default = format; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/metadata.js +var require_metadata = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/metadata.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.contentVocabulary = exports.metadataVocabulary = void 0; + exports.metadataVocabulary = [ + "title", + "description", + "default", + "deprecated", + "readOnly", + "writeOnly", + "examples" + ]; + exports.contentVocabulary = [ + "contentMediaType", + "contentEncoding", + "contentSchema" + ]; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/draft7.js +var require_draft7 = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/draft7.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var core_1 = require_core2(); + var validation_1 = require_validation(); + var applicator_1 = require_applicator(); + var format_1 = require_format2(); + var metadata_1 = require_metadata(); + var draft7Vocabularies = [ + core_1.default, + validation_1.default, + (0, applicator_1.default)(), + format_1.default, + metadata_1.metadataVocabulary, + metadata_1.contentVocabulary + ]; + exports.default = draft7Vocabularies; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/discriminator/types.js +var require_types = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/discriminator/types.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.DiscrError = void 0; + var DiscrError; + (function(DiscrError2) { + DiscrError2["Tag"] = "tag"; + DiscrError2["Mapping"] = "mapping"; + })(DiscrError || (exports.DiscrError = DiscrError = {})); + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/discriminator/index.js +var require_discriminator = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/vocabularies/discriminator/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen(); + var types_1 = require_types(); + var compile_1 = require_compile(); + var ref_error_1 = require_ref_error(); + var util_1 = require_util(); + var error2 = { + message: ({ params: { discrError, tagName } }) => discrError === types_1.DiscrError.Tag ? `tag "${tagName}" must be string` : `value of tag "${tagName}" must be in oneOf`, + params: ({ params: { discrError, tag, tagName } }) => (0, codegen_1._)`{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}` + }; + var def = { + keyword: "discriminator", + type: "object", + schemaType: "object", + error: error2, + code(cxt) { + const { gen, data, schema, parentSchema, it: it2 } = cxt; + const { oneOf } = parentSchema; + if (!it2.opts.discriminator) { + throw new Error("discriminator: requires discriminator option"); + } + const tagName = schema.propertyName; + if (typeof tagName != "string") + throw new Error("discriminator: requires propertyName"); + if (schema.mapping) + throw new Error("discriminator: mapping is not supported"); + if (!oneOf) + throw new Error("discriminator: requires oneOf keyword"); + const valid = gen.let("valid", false); + const tag = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`); + gen.if((0, codegen_1._)`typeof ${tag} == "string"`, () => validateMapping(), () => cxt.error(false, { discrError: types_1.DiscrError.Tag, tag, tagName })); + cxt.ok(valid); + function validateMapping() { + const mapping = getMapping(); + gen.if(false); + for (const tagValue in mapping) { + gen.elseIf((0, codegen_1._)`${tag} === ${tagValue}`); + gen.assign(valid, applyTagSchema(mapping[tagValue])); + } + gen.else(); + cxt.error(false, { discrError: types_1.DiscrError.Mapping, tag, tagName }); + gen.endIf(); + } + function applyTagSchema(schemaProp) { + const _valid = gen.name("valid"); + const schCxt = cxt.subschema({ keyword: "oneOf", schemaProp }, _valid); + cxt.mergeEvaluated(schCxt, codegen_1.Name); + return _valid; + } + function getMapping() { + var _a; + const oneOfMapping = {}; + const topRequired = hasRequired(parentSchema); + let tagRequired = true; + for (let i = 0; i < oneOf.length; i++) { + let sch = oneOf[i]; + if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it2.self.RULES)) { + const ref = sch.$ref; + sch = compile_1.resolveRef.call(it2.self, it2.schemaEnv.root, it2.baseId, ref); + if (sch instanceof compile_1.SchemaEnv) + sch = sch.schema; + if (sch === void 0) + throw new ref_error_1.default(it2.opts.uriResolver, it2.baseId, ref); + } + const propSch = (_a = sch === null || sch === void 0 ? void 0 : sch.properties) === null || _a === void 0 ? void 0 : _a[tagName]; + if (typeof propSch != "object") { + throw new Error(`discriminator: oneOf subschemas (or referenced schemas) must have "properties/${tagName}"`); + } + tagRequired = tagRequired && (topRequired || hasRequired(sch)); + addMappings(propSch, i); + } + if (!tagRequired) + throw new Error(`discriminator: "${tagName}" must be required`); + return oneOfMapping; + function hasRequired({ required: required2 }) { + return Array.isArray(required2) && required2.includes(tagName); + } + function addMappings(sch, i) { + if (sch.const) { + addMapping(sch.const, i); + } else if (sch.enum) { + for (const tagValue of sch.enum) { + addMapping(tagValue, i); + } + } else { + throw new Error(`discriminator: "properties/${tagName}" must have "const" or "enum"`); + } + } + function addMapping(tagValue, i) { + if (typeof tagValue != "string" || tagValue in oneOfMapping) { + throw new Error(`discriminator: "${tagName}" values must be unique strings`); + } + oneOfMapping[tagValue] = i; + } + } + } + }; + exports.default = def; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/refs/json-schema-draft-07.json +var require_json_schema_draft_07 = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/refs/json-schema-draft-07.json"(exports, module) { + module.exports = { + $schema: "http://json-schema.org/draft-07/schema#", + $id: "http://json-schema.org/draft-07/schema#", + title: "Core schema meta-schema", + definitions: { + schemaArray: { + type: "array", + minItems: 1, + items: { $ref: "#" } + }, + nonNegativeInteger: { + type: "integer", + minimum: 0 + }, + nonNegativeIntegerDefault0: { + allOf: [{ $ref: "#/definitions/nonNegativeInteger" }, { default: 0 }] + }, + simpleTypes: { + enum: ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + stringArray: { + type: "array", + items: { type: "string" }, + uniqueItems: true, + default: [] + } + }, + type: ["object", "boolean"], + properties: { + $id: { + type: "string", + format: "uri-reference" + }, + $schema: { + type: "string", + format: "uri" + }, + $ref: { + type: "string", + format: "uri-reference" + }, + $comment: { + type: "string" + }, + title: { + type: "string" + }, + description: { + type: "string" + }, + default: true, + readOnly: { + type: "boolean", + default: false + }, + examples: { + type: "array", + items: true + }, + multipleOf: { + type: "number", + exclusiveMinimum: 0 + }, + maximum: { + type: "number" + }, + exclusiveMaximum: { + type: "number" + }, + minimum: { + type: "number" + }, + exclusiveMinimum: { + type: "number" + }, + maxLength: { $ref: "#/definitions/nonNegativeInteger" }, + minLength: { $ref: "#/definitions/nonNegativeIntegerDefault0" }, + pattern: { + type: "string", + format: "regex" + }, + additionalItems: { $ref: "#" }, + items: { + anyOf: [{ $ref: "#" }, { $ref: "#/definitions/schemaArray" }], + default: true + }, + maxItems: { $ref: "#/definitions/nonNegativeInteger" }, + minItems: { $ref: "#/definitions/nonNegativeIntegerDefault0" }, + uniqueItems: { + type: "boolean", + default: false + }, + contains: { $ref: "#" }, + maxProperties: { $ref: "#/definitions/nonNegativeInteger" }, + minProperties: { $ref: "#/definitions/nonNegativeIntegerDefault0" }, + required: { $ref: "#/definitions/stringArray" }, + additionalProperties: { $ref: "#" }, + definitions: { + type: "object", + additionalProperties: { $ref: "#" }, + default: {} + }, + properties: { + type: "object", + additionalProperties: { $ref: "#" }, + default: {} + }, + patternProperties: { + type: "object", + additionalProperties: { $ref: "#" }, + propertyNames: { format: "regex" }, + default: {} + }, + dependencies: { + type: "object", + additionalProperties: { + anyOf: [{ $ref: "#" }, { $ref: "#/definitions/stringArray" }] + } + }, + propertyNames: { $ref: "#" }, + const: true, + enum: { + type: "array", + items: true, + minItems: 1, + uniqueItems: true + }, + type: { + anyOf: [ + { $ref: "#/definitions/simpleTypes" }, + { + type: "array", + items: { $ref: "#/definitions/simpleTypes" }, + minItems: 1, + uniqueItems: true + } + ] + }, + format: { type: "string" }, + contentMediaType: { type: "string" }, + contentEncoding: { type: "string" }, + if: { $ref: "#" }, + then: { $ref: "#" }, + else: { $ref: "#" }, + allOf: { $ref: "#/definitions/schemaArray" }, + anyOf: { $ref: "#/definitions/schemaArray" }, + oneOf: { $ref: "#/definitions/schemaArray" }, + not: { $ref: "#" } + }, + default: true + }; + } +}); + +// node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/ajv.js +var require_ajv = __commonJS({ + "node_modules/@modelcontextprotocol/sdk/node_modules/ajv/dist/ajv.js"(exports, module) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = exports.Ajv = void 0; + var core_1 = require_core(); + var draft7_1 = require_draft7(); + var discriminator_1 = require_discriminator(); + var draft7MetaSchema = require_json_schema_draft_07(); + var META_SUPPORT_DATA = ["/properties"]; + var META_SCHEMA_ID = "http://json-schema.org/draft-07/schema"; + var Ajv2 = class extends core_1.default { + _addVocabularies() { + super._addVocabularies(); + draft7_1.default.forEach((v) => this.addVocabulary(v)); + if (this.opts.discriminator) + this.addKeyword(discriminator_1.default); + } + _addDefaultMetaSchema() { + super._addDefaultMetaSchema(); + if (!this.opts.meta) + return; + const metaSchema = this.opts.$data ? this.$dataMetaSchema(draft7MetaSchema, META_SUPPORT_DATA) : draft7MetaSchema; + this.addMetaSchema(metaSchema, META_SCHEMA_ID, false); + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID; + } + defaultMeta() { + return this.opts.defaultMeta = super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : void 0); + } + }; + exports.Ajv = Ajv2; + module.exports = exports = Ajv2; + module.exports.Ajv = Ajv2; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.default = Ajv2; + var validate_1 = require_validate(); + Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function() { + return validate_1.KeywordCxt; + } }); + var codegen_1 = require_codegen(); + Object.defineProperty(exports, "_", { enumerable: true, get: function() { + return codegen_1._; + } }); + Object.defineProperty(exports, "str", { enumerable: true, get: function() { + return codegen_1.str; + } }); + Object.defineProperty(exports, "stringify", { enumerable: true, get: function() { + return codegen_1.stringify; + } }); + Object.defineProperty(exports, "nil", { enumerable: true, get: function() { + return codegen_1.nil; + } }); + Object.defineProperty(exports, "Name", { enumerable: true, get: function() { + return codegen_1.Name; + } }); + Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function() { + return codegen_1.CodeGen; + } }); + var validation_error_1 = require_validation_error(); + Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function() { + return validation_error_1.default; + } }); + var ref_error_1 = require_ref_error(); + Object.defineProperty(exports, "MissingRefError", { enumerable: true, get: function() { + return ref_error_1.default; + } }); + } +}); + +// node_modules/ajv-formats/dist/formats.js +var require_formats = __commonJS({ + "node_modules/ajv-formats/dist/formats.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.formatNames = exports.fastFormats = exports.fullFormats = void 0; + function fmtDef(validate, compare) { + return { validate, compare }; + } + exports.fullFormats = { + // date: http://tools.ietf.org/html/rfc3339#section-5.6 + date: fmtDef(date3, compareDate), + // date-time: http://tools.ietf.org/html/rfc3339#section-5.6 + time: fmtDef(getTime(true), compareTime), + "date-time": fmtDef(getDateTime(true), compareDateTime), + "iso-time": fmtDef(getTime(), compareIsoTime), + "iso-date-time": fmtDef(getDateTime(), compareIsoDateTime), + // duration: https://tools.ietf.org/html/rfc3339#appendix-A + duration: /^P(?!$)((\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?|(\d+W)?)$/, + uri, + "uri-reference": /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i, + // uri-template: https://tools.ietf.org/html/rfc6570 + "uri-template": /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i, + // For the source: https://gist.github.com/dperini/729294 + // For test cases: https://mathiasbynens.be/demo/url-regex + url: /^(?:https?|ftp):\/\/(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)(?:\.(?:[a-z0-9\u{00a1}-\u{ffff}]+-)*[a-z0-9\u{00a1}-\u{ffff}]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu, + email: /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i, + hostname: /^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i, + // optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html + ipv4: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/, + ipv6: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i, + regex, + // uuid: http://tools.ietf.org/html/rfc4122 + uuid: /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i, + // JSON-pointer: https://tools.ietf.org/html/rfc6901 + // uri fragment: https://tools.ietf.org/html/rfc3986#appendix-A + "json-pointer": /^(?:\/(?:[^~/]|~0|~1)*)*$/, + "json-pointer-uri-fragment": /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i, + // relative JSON-pointer: http://tools.ietf.org/html/draft-luff-relative-json-pointer-00 + "relative-json-pointer": /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/, + // the following formats are used by the openapi specification: https://spec.openapis.org/oas/v3.0.0#data-types + // byte: https://github.com/miguelmota/is-base64 + byte, + // signed 32 bit integer + int32: { type: "number", validate: validateInt32 }, + // signed 64 bit integer + int64: { type: "number", validate: validateInt64 }, + // C-type float + float: { type: "number", validate: validateNumber }, + // C-type double + double: { type: "number", validate: validateNumber }, + // hint to the UI to hide input strings + password: true, + // unchecked string payload + binary: true + }; + exports.fastFormats = { + ...exports.fullFormats, + date: fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d$/, compareDate), + time: fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i, compareTime), + "date-time": fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\dt(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i, compareDateTime), + "iso-time": fmtDef(/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i, compareIsoTime), + "iso-date-time": fmtDef(/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i, compareIsoDateTime), + // uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js + uri: /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/)?[^\s]*$/i, + "uri-reference": /^(?:(?:[a-z][a-z0-9+\-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i, + // email (sources from jsen validator): + // http://stackoverflow.com/questions/201323/using-a-regular-expression-to-validate-an-email-address#answer-8829363 + // http://www.w3.org/TR/html5/forms.html#valid-e-mail-address (search for 'wilful violation') + email: /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i + }; + exports.formatNames = Object.keys(exports.fullFormats); + function isLeapYear(year) { + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); + } + var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; + var DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; + function date3(str) { + const matches = DATE.exec(str); + if (!matches) + return false; + const year = +matches[1]; + const month = +matches[2]; + const day = +matches[3]; + return month >= 1 && month <= 12 && day >= 1 && day <= (month === 2 && isLeapYear(year) ? 29 : DAYS[month]); + } + function compareDate(d1, d2) { + if (!(d1 && d2)) + return void 0; + if (d1 > d2) + return 1; + if (d1 < d2) + return -1; + return 0; + } + var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i; + function getTime(strictTimeZone) { + return function time3(str) { + const matches = TIME.exec(str); + if (!matches) + return false; + const hr2 = +matches[1]; + const min = +matches[2]; + const sec = +matches[3]; + const tz = matches[4]; + const tzSign = matches[5] === "-" ? -1 : 1; + const tzH = +(matches[6] || 0); + const tzM = +(matches[7] || 0); + if (tzH > 23 || tzM > 59 || strictTimeZone && !tz) + return false; + if (hr2 <= 23 && min <= 59 && sec < 60) + return true; + const utcMin = min - tzM * tzSign; + const utcHr = hr2 - tzH * tzSign - (utcMin < 0 ? 1 : 0); + return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61; + }; + } + function compareTime(s1, s2) { + if (!(s1 && s2)) + return void 0; + const t1 = (/* @__PURE__ */ new Date("2020-01-01T" + s1)).valueOf(); + const t2 = (/* @__PURE__ */ new Date("2020-01-01T" + s2)).valueOf(); + if (!(t1 && t2)) + return void 0; + return t1 - t2; + } + function compareIsoTime(t1, t2) { + if (!(t1 && t2)) + return void 0; + const a1 = TIME.exec(t1); + const a2 = TIME.exec(t2); + if (!(a1 && a2)) + return void 0; + t1 = a1[1] + a1[2] + a1[3]; + t2 = a2[1] + a2[2] + a2[3]; + if (t1 > t2) + return 1; + if (t1 < t2) + return -1; + return 0; + } + var DATE_TIME_SEPARATOR = /t|\s/i; + function getDateTime(strictTimeZone) { + const time3 = getTime(strictTimeZone); + return function date_time(str) { + const dateTime = str.split(DATE_TIME_SEPARATOR); + return dateTime.length === 2 && date3(dateTime[0]) && time3(dateTime[1]); + }; + } + function compareDateTime(dt1, dt2) { + if (!(dt1 && dt2)) + return void 0; + const d1 = new Date(dt1).valueOf(); + const d2 = new Date(dt2).valueOf(); + if (!(d1 && d2)) + return void 0; + return d1 - d2; + } + function compareIsoDateTime(dt1, dt2) { + if (!(dt1 && dt2)) + return void 0; + const [d1, t1] = dt1.split(DATE_TIME_SEPARATOR); + const [d2, t2] = dt2.split(DATE_TIME_SEPARATOR); + const res = compareDate(d1, d2); + if (res === void 0) + return void 0; + return res || compareTime(t1, t2); + } + var NOT_URI_FRAGMENT = /\/|:/; + var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; + function uri(str) { + return NOT_URI_FRAGMENT.test(str) && URI.test(str); + } + var BYTE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/gm; + function byte(str) { + BYTE.lastIndex = 0; + return BYTE.test(str); + } + var MIN_INT32 = -(2 ** 31); + var MAX_INT32 = 2 ** 31 - 1; + function validateInt32(value) { + return Number.isInteger(value) && value <= MAX_INT32 && value >= MIN_INT32; + } + function validateInt64(value) { + return Number.isInteger(value); + } + function validateNumber() { + return true; + } + var Z_ANCHOR = /[^\\]\\Z/; + function regex(str) { + if (Z_ANCHOR.test(str)) + return false; + try { + new RegExp(str); + return true; + } catch (e) { + return false; + } + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/codegen/code.js +var require_code3 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/codegen/code.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = void 0; + var _CodeOrName = class { + }; + exports._CodeOrName = _CodeOrName; + exports.IDENTIFIER = /^[a-z$_][a-z$_0-9]*$/i; + var Name = class extends _CodeOrName { + constructor(s2) { + super(); + if (!exports.IDENTIFIER.test(s2)) + throw new Error("CodeGen: name must be a valid identifier"); + this.str = s2; + } + toString() { + return this.str; + } + emptyStr() { + return false; + } + get names() { + return { [this.str]: 1 }; + } + }; + exports.Name = Name; + var _Code = class extends _CodeOrName { + constructor(code) { + super(); + this._items = typeof code === "string" ? [code] : code; + } + toString() { + return this.str; + } + emptyStr() { + if (this._items.length > 1) + return false; + const item = this._items[0]; + return item === "" || item === '""'; + } + get str() { + var _a; + return (_a = this._str) !== null && _a !== void 0 ? _a : this._str = this._items.reduce((s2, c) => `${s2}${c}`, ""); + } + get names() { + var _a; + return (_a = this._names) !== null && _a !== void 0 ? _a : this._names = this._items.reduce((names, c) => { + if (c instanceof Name) + names[c.str] = (names[c.str] || 0) + 1; + return names; + }, {}); + } + }; + exports._Code = _Code; + exports.nil = new _Code(""); + function _(strs, ...args) { + const code = [strs[0]]; + let i = 0; + while (i < args.length) { + addCodeArg(code, args[i]); + code.push(strs[++i]); + } + return new _Code(code); + } + exports._ = _; + var plus = new _Code("+"); + function str(strs, ...args) { + const expr = [safeStringify(strs[0])]; + let i = 0; + while (i < args.length) { + expr.push(plus); + addCodeArg(expr, args[i]); + expr.push(plus, safeStringify(strs[++i])); + } + optimize(expr); + return new _Code(expr); + } + exports.str = str; + function addCodeArg(code, arg) { + if (arg instanceof _Code) + code.push(...arg._items); + else if (arg instanceof Name) + code.push(arg); + else + code.push(interpolate(arg)); + } + exports.addCodeArg = addCodeArg; + function optimize(expr) { + let i = 1; + while (i < expr.length - 1) { + if (expr[i] === plus) { + const res = mergeExprItems(expr[i - 1], expr[i + 1]); + if (res !== void 0) { + expr.splice(i - 1, 3, res); + continue; + } + expr[i++] = "+"; + } + i++; + } + } + function mergeExprItems(a2, b2) { + if (b2 === '""') + return a2; + if (a2 === '""') + return b2; + if (typeof a2 == "string") { + if (b2 instanceof Name || a2[a2.length - 1] !== '"') + return; + if (typeof b2 != "string") + return `${a2.slice(0, -1)}${b2}"`; + if (b2[0] === '"') + return a2.slice(0, -1) + b2.slice(1); + return; + } + if (typeof b2 == "string" && b2[0] === '"' && !(a2 instanceof Name)) + return `"${a2}${b2.slice(1)}`; + return; + } + function strConcat(c1, c2) { + return c2.emptyStr() ? c1 : c1.emptyStr() ? c2 : str`${c1}${c2}`; + } + exports.strConcat = strConcat; + function interpolate(x2) { + return typeof x2 == "number" || typeof x2 == "boolean" || x2 === null ? x2 : safeStringify(Array.isArray(x2) ? x2.join(",") : x2); + } + function stringify(x2) { + return new _Code(safeStringify(x2)); + } + exports.stringify = stringify; + function safeStringify(x2) { + return JSON.stringify(x2).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029"); + } + exports.safeStringify = safeStringify; + function getProperty(key) { + return typeof key == "string" && exports.IDENTIFIER.test(key) ? new _Code(`.${key}`) : _`[${key}]`; + } + exports.getProperty = getProperty; + function getEsmExportName(key) { + if (typeof key == "string" && exports.IDENTIFIER.test(key)) { + return new _Code(`${key}`); + } + throw new Error(`CodeGen: invalid export name: ${key}, use explicit $id name mapping`); + } + exports.getEsmExportName = getEsmExportName; + function regexpCode(rx) { + return new _Code(rx.toString()); + } + exports.regexpCode = regexpCode; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/codegen/scope.js +var require_scope2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/codegen/scope.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.ValueScope = exports.ValueScopeName = exports.Scope = exports.varKinds = exports.UsedValueState = void 0; + var code_1 = require_code3(); + var ValueError = class extends Error { + constructor(name) { + super(`CodeGen: "code" for ${name} not defined`); + this.value = name.value; + } + }; + var UsedValueState; + (function(UsedValueState2) { + UsedValueState2[UsedValueState2["Started"] = 0] = "Started"; + UsedValueState2[UsedValueState2["Completed"] = 1] = "Completed"; + })(UsedValueState || (exports.UsedValueState = UsedValueState = {})); + exports.varKinds = { + const: new code_1.Name("const"), + let: new code_1.Name("let"), + var: new code_1.Name("var") + }; + var Scope = class { + constructor({ prefixes, parent } = {}) { + this._names = {}; + this._prefixes = prefixes; + this._parent = parent; + } + toName(nameOrPrefix) { + return nameOrPrefix instanceof code_1.Name ? nameOrPrefix : this.name(nameOrPrefix); + } + name(prefix) { + return new code_1.Name(this._newName(prefix)); + } + _newName(prefix) { + const ng2 = this._names[prefix] || this._nameGroup(prefix); + return `${prefix}${ng2.index++}`; + } + _nameGroup(prefix) { + var _a, _b; + if (((_b = (_a = this._parent) === null || _a === void 0 ? void 0 : _a._prefixes) === null || _b === void 0 ? void 0 : _b.has(prefix)) || this._prefixes && !this._prefixes.has(prefix)) { + throw new Error(`CodeGen: prefix "${prefix}" is not allowed in this scope`); + } + return this._names[prefix] = { prefix, index: 0 }; + } + }; + exports.Scope = Scope; + var ValueScopeName = class extends code_1.Name { + constructor(prefix, nameStr) { + super(nameStr); + this.prefix = prefix; + } + setValue(value, { property, itemIndex }) { + this.value = value; + this.scopePath = (0, code_1._)`.${new code_1.Name(property)}[${itemIndex}]`; + } + }; + exports.ValueScopeName = ValueScopeName; + var line = (0, code_1._)`\n`; + var ValueScope = class extends Scope { + constructor(opts) { + super(opts); + this._values = {}; + this._scope = opts.scope; + this.opts = { ...opts, _n: opts.lines ? line : code_1.nil }; + } + get() { + return this._scope; + } + name(prefix) { + return new ValueScopeName(prefix, this._newName(prefix)); + } + value(nameOrPrefix, value) { + var _a; + if (value.ref === void 0) + throw new Error("CodeGen: ref must be passed in value"); + const name = this.toName(nameOrPrefix); + const { prefix } = name; + const valueKey = (_a = value.key) !== null && _a !== void 0 ? _a : value.ref; + let vs = this._values[prefix]; + if (vs) { + const _name = vs.get(valueKey); + if (_name) + return _name; + } else { + vs = this._values[prefix] = /* @__PURE__ */ new Map(); + } + vs.set(valueKey, name); + const s2 = this._scope[prefix] || (this._scope[prefix] = []); + const itemIndex = s2.length; + s2[itemIndex] = value.ref; + name.setValue(value, { property: prefix, itemIndex }); + return name; + } + getValue(prefix, keyOrRef) { + const vs = this._values[prefix]; + if (!vs) + return; + return vs.get(keyOrRef); + } + scopeRefs(scopeName, values = this._values) { + return this._reduceValues(values, (name) => { + if (name.scopePath === void 0) + throw new Error(`CodeGen: name "${name}" has no value`); + return (0, code_1._)`${scopeName}${name.scopePath}`; + }); + } + scopeCode(values = this._values, usedValues, getCode) { + return this._reduceValues(values, (name) => { + if (name.value === void 0) + throw new Error(`CodeGen: name "${name}" has no value`); + return name.value.code; + }, usedValues, getCode); + } + _reduceValues(values, valueCode, usedValues = {}, getCode) { + let code = code_1.nil; + for (const prefix in values) { + const vs = values[prefix]; + if (!vs) + continue; + const nameSet = usedValues[prefix] = usedValues[prefix] || /* @__PURE__ */ new Map(); + vs.forEach((name) => { + if (nameSet.has(name)) + return; + nameSet.set(name, UsedValueState.Started); + let c = valueCode(name); + if (c) { + const def = this.opts.es5 ? exports.varKinds.var : exports.varKinds.const; + code = (0, code_1._)`${code}${def} ${name} = ${c};${this.opts._n}`; + } else if (c = getCode === null || getCode === void 0 ? void 0 : getCode(name)) { + code = (0, code_1._)`${code}${c}${this.opts._n}`; + } else { + throw new ValueError(name); + } + nameSet.set(name, UsedValueState.Completed); + }); + } + return code; + } + }; + exports.ValueScope = ValueScope; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/codegen/index.js +var require_codegen2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/codegen/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.or = exports.and = exports.not = exports.CodeGen = exports.operators = exports.varKinds = exports.ValueScopeName = exports.ValueScope = exports.Scope = exports.Name = exports.regexpCode = exports.stringify = exports.getProperty = exports.nil = exports.strConcat = exports.str = exports._ = void 0; + var code_1 = require_code3(); + var scope_1 = require_scope2(); + var code_2 = require_code3(); + Object.defineProperty(exports, "_", { enumerable: true, get: function() { + return code_2._; + } }); + Object.defineProperty(exports, "str", { enumerable: true, get: function() { + return code_2.str; + } }); + Object.defineProperty(exports, "strConcat", { enumerable: true, get: function() { + return code_2.strConcat; + } }); + Object.defineProperty(exports, "nil", { enumerable: true, get: function() { + return code_2.nil; + } }); + Object.defineProperty(exports, "getProperty", { enumerable: true, get: function() { + return code_2.getProperty; + } }); + Object.defineProperty(exports, "stringify", { enumerable: true, get: function() { + return code_2.stringify; + } }); + Object.defineProperty(exports, "regexpCode", { enumerable: true, get: function() { + return code_2.regexpCode; + } }); + Object.defineProperty(exports, "Name", { enumerable: true, get: function() { + return code_2.Name; + } }); + var scope_2 = require_scope2(); + Object.defineProperty(exports, "Scope", { enumerable: true, get: function() { + return scope_2.Scope; + } }); + Object.defineProperty(exports, "ValueScope", { enumerable: true, get: function() { + return scope_2.ValueScope; + } }); + Object.defineProperty(exports, "ValueScopeName", { enumerable: true, get: function() { + return scope_2.ValueScopeName; + } }); + Object.defineProperty(exports, "varKinds", { enumerable: true, get: function() { + return scope_2.varKinds; + } }); + exports.operators = { + GT: new code_1._Code(">"), + GTE: new code_1._Code(">="), + LT: new code_1._Code("<"), + LTE: new code_1._Code("<="), + EQ: new code_1._Code("==="), + NEQ: new code_1._Code("!=="), + NOT: new code_1._Code("!"), + OR: new code_1._Code("||"), + AND: new code_1._Code("&&"), + ADD: new code_1._Code("+") + }; + var Node = class { + optimizeNodes() { + return this; + } + optimizeNames(_names, _constants) { + return this; + } + }; + var Def = class extends Node { + constructor(varKind, name, rhs) { + super(); + this.varKind = varKind; + this.name = name; + this.rhs = rhs; + } + render({ es5, _n: _n2 }) { + const varKind = es5 ? scope_1.varKinds.var : this.varKind; + const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`; + return `${varKind} ${this.name}${rhs};` + _n2; + } + optimizeNames(names, constants) { + if (!names[this.name.str]) + return; + if (this.rhs) + this.rhs = optimizeExpr(this.rhs, names, constants); + return this; + } + get names() { + return this.rhs instanceof code_1._CodeOrName ? this.rhs.names : {}; + } + }; + var Assign = class extends Node { + constructor(lhs, rhs, sideEffects) { + super(); + this.lhs = lhs; + this.rhs = rhs; + this.sideEffects = sideEffects; + } + render({ _n: _n2 }) { + return `${this.lhs} = ${this.rhs};` + _n2; + } + optimizeNames(names, constants) { + if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects) + return; + this.rhs = optimizeExpr(this.rhs, names, constants); + return this; + } + get names() { + const names = this.lhs instanceof code_1.Name ? {} : { ...this.lhs.names }; + return addExprNames(names, this.rhs); + } + }; + var AssignOp = class extends Assign { + constructor(lhs, op, rhs, sideEffects) { + super(lhs, rhs, sideEffects); + this.op = op; + } + render({ _n: _n2 }) { + return `${this.lhs} ${this.op}= ${this.rhs};` + _n2; + } + }; + var Label = class extends Node { + constructor(label) { + super(); + this.label = label; + this.names = {}; + } + render({ _n: _n2 }) { + return `${this.label}:` + _n2; + } + }; + var Break = class extends Node { + constructor(label) { + super(); + this.label = label; + this.names = {}; + } + render({ _n: _n2 }) { + const label = this.label ? ` ${this.label}` : ""; + return `break${label};` + _n2; + } + }; + var Throw = class extends Node { + constructor(error2) { + super(); + this.error = error2; + } + render({ _n: _n2 }) { + return `throw ${this.error};` + _n2; + } + get names() { + return this.error.names; + } + }; + var AnyCode = class extends Node { + constructor(code) { + super(); + this.code = code; + } + render({ _n: _n2 }) { + return `${this.code};` + _n2; + } + optimizeNodes() { + return `${this.code}` ? this : void 0; + } + optimizeNames(names, constants) { + this.code = optimizeExpr(this.code, names, constants); + return this; + } + get names() { + return this.code instanceof code_1._CodeOrName ? this.code.names : {}; + } + }; + var ParentNode = class extends Node { + constructor(nodes = []) { + super(); + this.nodes = nodes; + } + render(opts) { + return this.nodes.reduce((code, n) => code + n.render(opts), ""); + } + optimizeNodes() { + const { nodes } = this; + let i = nodes.length; + while (i--) { + const n = nodes[i].optimizeNodes(); + if (Array.isArray(n)) + nodes.splice(i, 1, ...n); + else if (n) + nodes[i] = n; + else + nodes.splice(i, 1); + } + return nodes.length > 0 ? this : void 0; + } + optimizeNames(names, constants) { + const { nodes } = this; + let i = nodes.length; + while (i--) { + const n = nodes[i]; + if (n.optimizeNames(names, constants)) + continue; + subtractNames(names, n.names); + nodes.splice(i, 1); + } + return nodes.length > 0 ? this : void 0; + } + get names() { + return this.nodes.reduce((names, n) => addNames(names, n.names), {}); + } + }; + var BlockNode = class extends ParentNode { + render(opts) { + return "{" + opts._n + super.render(opts) + "}" + opts._n; + } + }; + var Root = class extends ParentNode { + }; + var Else = class extends BlockNode { + }; + Else.kind = "else"; + var If = class _If extends BlockNode { + constructor(condition, nodes) { + super(nodes); + this.condition = condition; + } + render(opts) { + let code = `if(${this.condition})` + super.render(opts); + if (this.else) + code += "else " + this.else.render(opts); + return code; + } + optimizeNodes() { + super.optimizeNodes(); + const cond = this.condition; + if (cond === true) + return this.nodes; + let e = this.else; + if (e) { + const ns = e.optimizeNodes(); + e = this.else = Array.isArray(ns) ? new Else(ns) : ns; + } + if (e) { + if (cond === false) + return e instanceof _If ? e : e.nodes; + if (this.nodes.length) + return this; + return new _If(not(cond), e instanceof _If ? [e] : e.nodes); + } + if (cond === false || !this.nodes.length) + return void 0; + return this; + } + optimizeNames(names, constants) { + var _a; + this.else = (_a = this.else) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants); + if (!(super.optimizeNames(names, constants) || this.else)) + return; + this.condition = optimizeExpr(this.condition, names, constants); + return this; + } + get names() { + const names = super.names; + addExprNames(names, this.condition); + if (this.else) + addNames(names, this.else.names); + return names; + } + }; + If.kind = "if"; + var For = class extends BlockNode { + }; + For.kind = "for"; + var ForLoop = class extends For { + constructor(iteration) { + super(); + this.iteration = iteration; + } + render(opts) { + return `for(${this.iteration})` + super.render(opts); + } + optimizeNames(names, constants) { + if (!super.optimizeNames(names, constants)) + return; + this.iteration = optimizeExpr(this.iteration, names, constants); + return this; + } + get names() { + return addNames(super.names, this.iteration.names); + } + }; + var ForRange = class extends For { + constructor(varKind, name, from, to2) { + super(); + this.varKind = varKind; + this.name = name; + this.from = from; + this.to = to2; + } + render(opts) { + const varKind = opts.es5 ? scope_1.varKinds.var : this.varKind; + const { name, from, to: to2 } = this; + return `for(${varKind} ${name}=${from}; ${name}<${to2}; ${name}++)` + super.render(opts); + } + get names() { + const names = addExprNames(super.names, this.from); + return addExprNames(names, this.to); + } + }; + var ForIter = class extends For { + constructor(loop, varKind, name, iterable) { + super(); + this.loop = loop; + this.varKind = varKind; + this.name = name; + this.iterable = iterable; + } + render(opts) { + return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts); + } + optimizeNames(names, constants) { + if (!super.optimizeNames(names, constants)) + return; + this.iterable = optimizeExpr(this.iterable, names, constants); + return this; + } + get names() { + return addNames(super.names, this.iterable.names); + } + }; + var Func = class extends BlockNode { + constructor(name, args, async) { + super(); + this.name = name; + this.args = args; + this.async = async; + } + render(opts) { + const _async = this.async ? "async " : ""; + return `${_async}function ${this.name}(${this.args})` + super.render(opts); + } + }; + Func.kind = "func"; + var Return = class extends ParentNode { + render(opts) { + return "return " + super.render(opts); + } + }; + Return.kind = "return"; + var Try = class extends BlockNode { + render(opts) { + let code = "try" + super.render(opts); + if (this.catch) + code += this.catch.render(opts); + if (this.finally) + code += this.finally.render(opts); + return code; + } + optimizeNodes() { + var _a, _b; + super.optimizeNodes(); + (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNodes(); + (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes(); + return this; + } + optimizeNames(names, constants) { + var _a, _b; + super.optimizeNames(names, constants); + (_a = this.catch) === null || _a === void 0 ? void 0 : _a.optimizeNames(names, constants); + (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants); + return this; + } + get names() { + const names = super.names; + if (this.catch) + addNames(names, this.catch.names); + if (this.finally) + addNames(names, this.finally.names); + return names; + } + }; + var Catch = class extends BlockNode { + constructor(error2) { + super(); + this.error = error2; + } + render(opts) { + return `catch(${this.error})` + super.render(opts); + } + }; + Catch.kind = "catch"; + var Finally = class extends BlockNode { + render(opts) { + return "finally" + super.render(opts); + } + }; + Finally.kind = "finally"; + var CodeGen = class { + constructor(extScope, opts = {}) { + this._values = {}; + this._blockStarts = []; + this._constants = {}; + this.opts = { ...opts, _n: opts.lines ? "\n" : "" }; + this._extScope = extScope; + this._scope = new scope_1.Scope({ parent: extScope }); + this._nodes = [new Root()]; + } + toString() { + return this._root.render(this.opts); + } + // returns unique name in the internal scope + name(prefix) { + return this._scope.name(prefix); + } + // reserves unique name in the external scope + scopeName(prefix) { + return this._extScope.name(prefix); + } + // reserves unique name in the external scope and assigns value to it + scopeValue(prefixOrName, value) { + const name = this._extScope.value(prefixOrName, value); + const vs = this._values[name.prefix] || (this._values[name.prefix] = /* @__PURE__ */ new Set()); + vs.add(name); + return name; + } + getScopeValue(prefix, keyOrRef) { + return this._extScope.getValue(prefix, keyOrRef); + } + // return code that assigns values in the external scope to the names that are used internally + // (same names that were returned by gen.scopeName or gen.scopeValue) + scopeRefs(scopeName) { + return this._extScope.scopeRefs(scopeName, this._values); + } + scopeCode() { + return this._extScope.scopeCode(this._values); + } + _def(varKind, nameOrPrefix, rhs, constant) { + const name = this._scope.toName(nameOrPrefix); + if (rhs !== void 0 && constant) + this._constants[name.str] = rhs; + this._leafNode(new Def(varKind, name, rhs)); + return name; + } + // `const` declaration (`var` in es5 mode) + const(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.const, nameOrPrefix, rhs, _constant); + } + // `let` declaration with optional assignment (`var` in es5 mode) + let(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.let, nameOrPrefix, rhs, _constant); + } + // `var` declaration with optional assignment + var(nameOrPrefix, rhs, _constant) { + return this._def(scope_1.varKinds.var, nameOrPrefix, rhs, _constant); + } + // assignment code + assign(lhs, rhs, sideEffects) { + return this._leafNode(new Assign(lhs, rhs, sideEffects)); + } + // `+=` code + add(lhs, rhs) { + return this._leafNode(new AssignOp(lhs, exports.operators.ADD, rhs)); + } + // appends passed SafeExpr to code or executes Block + code(c) { + if (typeof c == "function") + c(); + else if (c !== code_1.nil) + this._leafNode(new AnyCode(c)); + return this; + } + // returns code for object literal for the passed argument list of key-value pairs + object(...keyValues) { + const code = ["{"]; + for (const [key, value] of keyValues) { + if (code.length > 1) + code.push(","); + code.push(key); + if (key !== value || this.opts.es5) { + code.push(":"); + (0, code_1.addCodeArg)(code, value); + } + } + code.push("}"); + return new code_1._Code(code); + } + // `if` clause (or statement if `thenBody` and, optionally, `elseBody` are passed) + if(condition, thenBody, elseBody) { + this._blockNode(new If(condition)); + if (thenBody && elseBody) { + this.code(thenBody).else().code(elseBody).endIf(); + } else if (thenBody) { + this.code(thenBody).endIf(); + } else if (elseBody) { + throw new Error('CodeGen: "else" body without "then" body'); + } + return this; + } + // `else if` clause - invalid without `if` or after `else` clauses + elseIf(condition) { + return this._elseNode(new If(condition)); + } + // `else` clause - only valid after `if` or `else if` clauses + else() { + return this._elseNode(new Else()); + } + // end `if` statement (needed if gen.if was used only with condition) + endIf() { + return this._endBlockNode(If, Else); + } + _for(node, forBody) { + this._blockNode(node); + if (forBody) + this.code(forBody).endFor(); + return this; + } + // a generic `for` clause (or statement if `forBody` is passed) + for(iteration, forBody) { + return this._for(new ForLoop(iteration), forBody); + } + // `for` statement for a range of values + forRange(nameOrPrefix, from, to2, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.let) { + const name = this._scope.toName(nameOrPrefix); + return this._for(new ForRange(varKind, name, from, to2), () => forBody(name)); + } + // `for-of` statement (in es5 mode replace with a normal for loop) + forOf(nameOrPrefix, iterable, forBody, varKind = scope_1.varKinds.const) { + const name = this._scope.toName(nameOrPrefix); + if (this.opts.es5) { + const arr = iterable instanceof code_1.Name ? iterable : this.var("_arr", iterable); + return this.forRange("_i", 0, (0, code_1._)`${arr}.length`, (i) => { + this.var(name, (0, code_1._)`${arr}[${i}]`); + forBody(name); + }); + } + return this._for(new ForIter("of", varKind, name, iterable), () => forBody(name)); + } + // `for-in` statement. + // With option `ownProperties` replaced with a `for-of` loop for object keys + forIn(nameOrPrefix, obj, forBody, varKind = this.opts.es5 ? scope_1.varKinds.var : scope_1.varKinds.const) { + if (this.opts.ownProperties) { + return this.forOf(nameOrPrefix, (0, code_1._)`Object.keys(${obj})`, forBody); + } + const name = this._scope.toName(nameOrPrefix); + return this._for(new ForIter("in", varKind, name, obj), () => forBody(name)); + } + // end `for` loop + endFor() { + return this._endBlockNode(For); + } + // `label` statement + label(label) { + return this._leafNode(new Label(label)); + } + // `break` statement + break(label) { + return this._leafNode(new Break(label)); + } + // `return` statement + return(value) { + const node = new Return(); + this._blockNode(node); + this.code(value); + if (node.nodes.length !== 1) + throw new Error('CodeGen: "return" should have one node'); + return this._endBlockNode(Return); + } + // `try` statement + try(tryBody, catchCode, finallyCode) { + if (!catchCode && !finallyCode) + throw new Error('CodeGen: "try" without "catch" and "finally"'); + const node = new Try(); + this._blockNode(node); + this.code(tryBody); + if (catchCode) { + const error2 = this.name("e"); + this._currNode = node.catch = new Catch(error2); + catchCode(error2); + } + if (finallyCode) { + this._currNode = node.finally = new Finally(); + this.code(finallyCode); + } + return this._endBlockNode(Catch, Finally); + } + // `throw` statement + throw(error2) { + return this._leafNode(new Throw(error2)); + } + // start self-balancing block + block(body, nodeCount) { + this._blockStarts.push(this._nodes.length); + if (body) + this.code(body).endBlock(nodeCount); + return this; + } + // end the current self-balancing block + endBlock(nodeCount) { + const len = this._blockStarts.pop(); + if (len === void 0) + throw new Error("CodeGen: not in self-balancing block"); + const toClose = this._nodes.length - len; + if (toClose < 0 || nodeCount !== void 0 && toClose !== nodeCount) { + throw new Error(`CodeGen: wrong number of nodes: ${toClose} vs ${nodeCount} expected`); + } + this._nodes.length = len; + return this; + } + // `function` heading (or definition if funcBody is passed) + func(name, args = code_1.nil, async, funcBody) { + this._blockNode(new Func(name, args, async)); + if (funcBody) + this.code(funcBody).endFunc(); + return this; + } + // end function definition + endFunc() { + return this._endBlockNode(Func); + } + optimize(n = 1) { + while (n-- > 0) { + this._root.optimizeNodes(); + this._root.optimizeNames(this._root.names, this._constants); + } + } + _leafNode(node) { + this._currNode.nodes.push(node); + return this; + } + _blockNode(node) { + this._currNode.nodes.push(node); + this._nodes.push(node); + } + _endBlockNode(N1, N2) { + const n = this._currNode; + if (n instanceof N1 || N2 && n instanceof N2) { + this._nodes.pop(); + return this; + } + throw new Error(`CodeGen: not in block "${N2 ? `${N1.kind}/${N2.kind}` : N1.kind}"`); + } + _elseNode(node) { + const n = this._currNode; + if (!(n instanceof If)) { + throw new Error('CodeGen: "else" without "if"'); + } + this._currNode = n.else = node; + return this; + } + get _root() { + return this._nodes[0]; + } + get _currNode() { + const ns = this._nodes; + return ns[ns.length - 1]; + } + set _currNode(node) { + const ns = this._nodes; + ns[ns.length - 1] = node; + } + }; + exports.CodeGen = CodeGen; + function addNames(names, from) { + for (const n in from) + names[n] = (names[n] || 0) + (from[n] || 0); + return names; + } + function addExprNames(names, from) { + return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names; + } + function optimizeExpr(expr, names, constants) { + if (expr instanceof code_1.Name) + return replaceName(expr); + if (!canOptimize(expr)) + return expr; + return new code_1._Code(expr._items.reduce((items, c) => { + if (c instanceof code_1.Name) + c = replaceName(c); + if (c instanceof code_1._Code) + items.push(...c._items); + else + items.push(c); + return items; + }, [])); + function replaceName(n) { + const c = constants[n.str]; + if (c === void 0 || names[n.str] !== 1) + return n; + delete names[n.str]; + return c; + } + function canOptimize(e) { + return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants[c.str] !== void 0); + } + } + function subtractNames(names, from) { + for (const n in from) + names[n] = (names[n] || 0) - (from[n] || 0); + } + function not(x2) { + return typeof x2 == "boolean" || typeof x2 == "number" || x2 === null ? !x2 : (0, code_1._)`!${par(x2)}`; + } + exports.not = not; + var andCode = mappend(exports.operators.AND); + function and(...args) { + return args.reduce(andCode); + } + exports.and = and; + var orCode = mappend(exports.operators.OR); + function or2(...args) { + return args.reduce(orCode); + } + exports.or = or2; + function mappend(op) { + return (x2, y2) => x2 === code_1.nil ? y2 : y2 === code_1.nil ? x2 : (0, code_1._)`${par(x2)} ${op} ${par(y2)}`; + } + function par(x2) { + return x2 instanceof code_1.Name ? x2 : (0, code_1._)`(${x2})`; + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/util.js +var require_util2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/util.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.checkStrictMode = exports.getErrorPath = exports.Type = exports.useFunc = exports.setEvaluated = exports.evaluatedPropsToName = exports.mergeEvaluated = exports.eachItem = exports.unescapeJsonPointer = exports.escapeJsonPointer = exports.escapeFragment = exports.unescapeFragment = exports.schemaRefOrVal = exports.schemaHasRulesButRef = exports.schemaHasRules = exports.checkUnknownRules = exports.alwaysValidSchema = exports.toHash = void 0; + var codegen_1 = require_codegen2(); + var code_1 = require_code3(); + function toHash(arr) { + const hash = {}; + for (const item of arr) + hash[item] = true; + return hash; + } + exports.toHash = toHash; + function alwaysValidSchema(it2, schema) { + if (typeof schema == "boolean") + return schema; + if (Object.keys(schema).length === 0) + return true; + checkUnknownRules(it2, schema); + return !schemaHasRules(schema, it2.self.RULES.all); + } + exports.alwaysValidSchema = alwaysValidSchema; + function checkUnknownRules(it2, schema = it2.schema) { + const { opts, self } = it2; + if (!opts.strictSchema) + return; + if (typeof schema === "boolean") + return; + const rules = self.RULES.keywords; + for (const key in schema) { + if (!rules[key]) + checkStrictMode(it2, `unknown keyword: "${key}"`); + } + } + exports.checkUnknownRules = checkUnknownRules; + function schemaHasRules(schema, rules) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (rules[key]) + return true; + return false; + } + exports.schemaHasRules = schemaHasRules; + function schemaHasRulesButRef(schema, RULES) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (key !== "$ref" && RULES.all[key]) + return true; + return false; + } + exports.schemaHasRulesButRef = schemaHasRulesButRef; + function schemaRefOrVal({ topSchemaRef, schemaPath }, schema, keyword, $data) { + if (!$data) { + if (typeof schema == "number" || typeof schema == "boolean") + return schema; + if (typeof schema == "string") + return (0, codegen_1._)`${schema}`; + } + return (0, codegen_1._)`${topSchemaRef}${schemaPath}${(0, codegen_1.getProperty)(keyword)}`; + } + exports.schemaRefOrVal = schemaRefOrVal; + function unescapeFragment(str) { + return unescapeJsonPointer(decodeURIComponent(str)); + } + exports.unescapeFragment = unescapeFragment; + function escapeFragment(str) { + return encodeURIComponent(escapeJsonPointer(str)); + } + exports.escapeFragment = escapeFragment; + function escapeJsonPointer(str) { + if (typeof str == "number") + return `${str}`; + return str.replace(/~/g, "~0").replace(/\//g, "~1"); + } + exports.escapeJsonPointer = escapeJsonPointer; + function unescapeJsonPointer(str) { + return str.replace(/~1/g, "/").replace(/~0/g, "~"); + } + exports.unescapeJsonPointer = unescapeJsonPointer; + function eachItem(xs, f2) { + if (Array.isArray(xs)) { + for (const x2 of xs) + f2(x2); + } else { + f2(xs); + } + } + exports.eachItem = eachItem; + function makeMergeEvaluated({ mergeNames, mergeToName, mergeValues: mergeValues3, resultToName }) { + return (gen, from, to2, toName) => { + const res = to2 === void 0 ? from : to2 instanceof codegen_1.Name ? (from instanceof codegen_1.Name ? mergeNames(gen, from, to2) : mergeToName(gen, from, to2), to2) : from instanceof codegen_1.Name ? (mergeToName(gen, to2, from), from) : mergeValues3(from, to2); + return toName === codegen_1.Name && !(res instanceof codegen_1.Name) ? resultToName(gen, res) : res; + }; + } + exports.mergeEvaluated = { + props: makeMergeEvaluated({ + mergeNames: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true && ${from} !== undefined`, () => { + gen.if((0, codegen_1._)`${from} === true`, () => gen.assign(to2, true), () => gen.assign(to2, (0, codegen_1._)`${to2} || {}`).code((0, codegen_1._)`Object.assign(${to2}, ${from})`)); + }), + mergeToName: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true`, () => { + if (from === true) { + gen.assign(to2, true); + } else { + gen.assign(to2, (0, codegen_1._)`${to2} || {}`); + setEvaluated(gen, to2, from); + } + }), + mergeValues: (from, to2) => from === true ? true : { ...from, ...to2 }, + resultToName: evaluatedPropsToName + }), + items: makeMergeEvaluated({ + mergeNames: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true && ${from} !== undefined`, () => gen.assign(to2, (0, codegen_1._)`${from} === true ? true : ${to2} > ${from} ? ${to2} : ${from}`)), + mergeToName: (gen, from, to2) => gen.if((0, codegen_1._)`${to2} !== true`, () => gen.assign(to2, from === true ? true : (0, codegen_1._)`${to2} > ${from} ? ${to2} : ${from}`)), + mergeValues: (from, to2) => from === true ? true : Math.max(from, to2), + resultToName: (gen, items) => gen.var("items", items) + }) + }; + function evaluatedPropsToName(gen, ps) { + if (ps === true) + return gen.var("props", true); + const props = gen.var("props", (0, codegen_1._)`{}`); + if (ps !== void 0) + setEvaluated(gen, props, ps); + return props; + } + exports.evaluatedPropsToName = evaluatedPropsToName; + function setEvaluated(gen, props, ps) { + Object.keys(ps).forEach((p2) => gen.assign((0, codegen_1._)`${props}${(0, codegen_1.getProperty)(p2)}`, true)); + } + exports.setEvaluated = setEvaluated; + var snippets = {}; + function useFunc(gen, f2) { + return gen.scopeValue("func", { + ref: f2, + code: snippets[f2.code] || (snippets[f2.code] = new code_1._Code(f2.code)) + }); + } + exports.useFunc = useFunc; + var Type; + (function(Type2) { + Type2[Type2["Num"] = 0] = "Num"; + Type2[Type2["Str"] = 1] = "Str"; + })(Type || (exports.Type = Type = {})); + function getErrorPath(dataProp, dataPropType, jsPropertySyntax) { + if (dataProp instanceof codegen_1.Name) { + const isNumber = dataPropType === Type.Num; + return jsPropertySyntax ? isNumber ? (0, codegen_1._)`"[" + ${dataProp} + "]"` : (0, codegen_1._)`"['" + ${dataProp} + "']"` : isNumber ? (0, codegen_1._)`"/" + ${dataProp}` : (0, codegen_1._)`"/" + ${dataProp}.replace(/~/g, "~0").replace(/\\//g, "~1")`; + } + return jsPropertySyntax ? (0, codegen_1.getProperty)(dataProp).toString() : "/" + escapeJsonPointer(dataProp); + } + exports.getErrorPath = getErrorPath; + function checkStrictMode(it2, msg, mode = it2.opts.strictSchema) { + if (!mode) + return; + msg = `strict mode: ${msg}`; + if (mode === true) + throw new Error(msg); + it2.self.logger.warn(msg); + } + exports.checkStrictMode = checkStrictMode; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/names.js +var require_names2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/names.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var names = { + // validation function arguments + data: new codegen_1.Name("data"), + // data passed to validation function + // args passed from referencing schema + valCxt: new codegen_1.Name("valCxt"), + // validation/data context - should not be used directly, it is destructured to the names below + instancePath: new codegen_1.Name("instancePath"), + parentData: new codegen_1.Name("parentData"), + parentDataProperty: new codegen_1.Name("parentDataProperty"), + rootData: new codegen_1.Name("rootData"), + // root data - same as the data passed to the first/top validation function + dynamicAnchors: new codegen_1.Name("dynamicAnchors"), + // used to support recursiveRef and dynamicRef + // function scoped variables + vErrors: new codegen_1.Name("vErrors"), + // null or array of validation errors + errors: new codegen_1.Name("errors"), + // counter of validation errors + this: new codegen_1.Name("this"), + // "globals" + self: new codegen_1.Name("self"), + scope: new codegen_1.Name("scope"), + // JTD serialize/parse name for JSON string and position + json: new codegen_1.Name("json"), + jsonPos: new codegen_1.Name("jsonPos"), + jsonLen: new codegen_1.Name("jsonLen"), + jsonPart: new codegen_1.Name("jsonPart") + }; + exports.default = names; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/errors.js +var require_errors2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/errors.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.extendErrors = exports.resetErrorsCount = exports.reportExtraError = exports.reportError = exports.keyword$DataError = exports.keywordError = void 0; + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var names_1 = require_names2(); + exports.keywordError = { + message: ({ keyword }) => (0, codegen_1.str)`must pass "${keyword}" keyword validation` + }; + exports.keyword$DataError = { + message: ({ keyword, schemaType }) => schemaType ? (0, codegen_1.str)`"${keyword}" keyword must be ${schemaType} ($data)` : (0, codegen_1.str)`"${keyword}" keyword is invalid ($data)` + }; + function reportError(cxt, error2 = exports.keywordError, errorPaths, overrideAllErrors) { + const { it: it2 } = cxt; + const { gen, compositeRule, allErrors } = it2; + const errObj = errorObjectCode(cxt, error2, errorPaths); + if (overrideAllErrors !== null && overrideAllErrors !== void 0 ? overrideAllErrors : compositeRule || allErrors) { + addError(gen, errObj); + } else { + returnErrors(it2, (0, codegen_1._)`[${errObj}]`); + } + } + exports.reportError = reportError; + function reportExtraError(cxt, error2 = exports.keywordError, errorPaths) { + const { it: it2 } = cxt; + const { gen, compositeRule, allErrors } = it2; + const errObj = errorObjectCode(cxt, error2, errorPaths); + addError(gen, errObj); + if (!(compositeRule || allErrors)) { + returnErrors(it2, names_1.default.vErrors); + } + } + exports.reportExtraError = reportExtraError; + function resetErrorsCount(gen, errsCount) { + gen.assign(names_1.default.errors, errsCount); + gen.if((0, codegen_1._)`${names_1.default.vErrors} !== null`, () => gen.if(errsCount, () => gen.assign((0, codegen_1._)`${names_1.default.vErrors}.length`, errsCount), () => gen.assign(names_1.default.vErrors, null))); + } + exports.resetErrorsCount = resetErrorsCount; + function extendErrors({ gen, keyword, schemaValue, data, errsCount, it: it2 }) { + if (errsCount === void 0) + throw new Error("ajv implementation error"); + const err = gen.name("err"); + gen.forRange("i", errsCount, names_1.default.errors, (i) => { + gen.const(err, (0, codegen_1._)`${names_1.default.vErrors}[${i}]`); + gen.if((0, codegen_1._)`${err}.instancePath === undefined`, () => gen.assign((0, codegen_1._)`${err}.instancePath`, (0, codegen_1.strConcat)(names_1.default.instancePath, it2.errorPath))); + gen.assign((0, codegen_1._)`${err}.schemaPath`, (0, codegen_1.str)`${it2.errSchemaPath}/${keyword}`); + if (it2.opts.verbose) { + gen.assign((0, codegen_1._)`${err}.schema`, schemaValue); + gen.assign((0, codegen_1._)`${err}.data`, data); + } + }); + } + exports.extendErrors = extendErrors; + function addError(gen, errObj) { + const err = gen.const("err", errObj); + gen.if((0, codegen_1._)`${names_1.default.vErrors} === null`, () => gen.assign(names_1.default.vErrors, (0, codegen_1._)`[${err}]`), (0, codegen_1._)`${names_1.default.vErrors}.push(${err})`); + gen.code((0, codegen_1._)`${names_1.default.errors}++`); + } + function returnErrors(it2, errs) { + const { gen, validateName, schemaEnv } = it2; + if (schemaEnv.$async) { + gen.throw((0, codegen_1._)`new ${it2.ValidationError}(${errs})`); + } else { + gen.assign((0, codegen_1._)`${validateName}.errors`, errs); + gen.return(false); + } + } + var E2 = { + keyword: new codegen_1.Name("keyword"), + schemaPath: new codegen_1.Name("schemaPath"), + // also used in JTD errors + params: new codegen_1.Name("params"), + propertyName: new codegen_1.Name("propertyName"), + message: new codegen_1.Name("message"), + schema: new codegen_1.Name("schema"), + parentSchema: new codegen_1.Name("parentSchema") + }; + function errorObjectCode(cxt, error2, errorPaths) { + const { createErrors } = cxt.it; + if (createErrors === false) + return (0, codegen_1._)`{}`; + return errorObject(cxt, error2, errorPaths); + } + function errorObject(cxt, error2, errorPaths = {}) { + const { gen, it: it2 } = cxt; + const keyValues = [ + errorInstancePath(it2, errorPaths), + errorSchemaPath(cxt, errorPaths) + ]; + extraErrorProps(cxt, error2, keyValues); + return gen.object(...keyValues); + } + function errorInstancePath({ errorPath }, { instancePath }) { + const instPath = instancePath ? (0, codegen_1.str)`${errorPath}${(0, util_1.getErrorPath)(instancePath, util_1.Type.Str)}` : errorPath; + return [names_1.default.instancePath, (0, codegen_1.strConcat)(names_1.default.instancePath, instPath)]; + } + function errorSchemaPath({ keyword, it: { errSchemaPath } }, { schemaPath, parentSchema }) { + let schPath = parentSchema ? errSchemaPath : (0, codegen_1.str)`${errSchemaPath}/${keyword}`; + if (schemaPath) { + schPath = (0, codegen_1.str)`${schPath}${(0, util_1.getErrorPath)(schemaPath, util_1.Type.Str)}`; + } + return [E2.schemaPath, schPath]; + } + function extraErrorProps(cxt, { params, message }, keyValues) { + const { keyword, data, schemaValue, it: it2 } = cxt; + const { opts, propertyName, topSchemaRef, schemaPath } = it2; + keyValues.push([E2.keyword, keyword], [E2.params, typeof params == "function" ? params(cxt) : params || (0, codegen_1._)`{}`]); + if (opts.messages) { + keyValues.push([E2.message, typeof message == "function" ? message(cxt) : message]); + } + if (opts.verbose) { + keyValues.push([E2.schema, schemaValue], [E2.parentSchema, (0, codegen_1._)`${topSchemaRef}${schemaPath}`], [names_1.default.data, data]); + } + if (propertyName) + keyValues.push([E2.propertyName, propertyName]); + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/boolSchema.js +var require_boolSchema2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/boolSchema.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.boolOrEmptySchema = exports.topBoolOrEmptySchema = void 0; + var errors_1 = require_errors2(); + var codegen_1 = require_codegen2(); + var names_1 = require_names2(); + var boolError = { + message: "boolean schema is false" + }; + function topBoolOrEmptySchema(it2) { + const { gen, schema, validateName } = it2; + if (schema === false) { + falseSchemaError(it2, false); + } else if (typeof schema == "object" && schema.$async === true) { + gen.return(names_1.default.data); + } else { + gen.assign((0, codegen_1._)`${validateName}.errors`, null); + gen.return(true); + } + } + exports.topBoolOrEmptySchema = topBoolOrEmptySchema; + function boolOrEmptySchema(it2, valid) { + const { gen, schema } = it2; + if (schema === false) { + gen.var(valid, false); + falseSchemaError(it2); + } else { + gen.var(valid, true); + } + } + exports.boolOrEmptySchema = boolOrEmptySchema; + function falseSchemaError(it2, overrideAllErrors) { + const { gen, data } = it2; + const cxt = { + gen, + keyword: "false schema", + data, + schema: false, + schemaCode: false, + schemaValue: false, + params: {}, + it: it2 + }; + (0, errors_1.reportError)(cxt, boolError, void 0, overrideAllErrors); + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/rules.js +var require_rules2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/rules.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getRules = exports.isJSONType = void 0; + var _jsonTypes = ["string", "number", "integer", "boolean", "null", "object", "array"]; + var jsonTypes = new Set(_jsonTypes); + function isJSONType(x2) { + return typeof x2 == "string" && jsonTypes.has(x2); + } + exports.isJSONType = isJSONType; + function getRules() { + const groups = { + number: { type: "number", rules: [] }, + string: { type: "string", rules: [] }, + array: { type: "array", rules: [] }, + object: { type: "object", rules: [] } + }; + return { + types: { ...groups, integer: true, boolean: true, null: true }, + rules: [{ rules: [] }, groups.number, groups.string, groups.array, groups.object], + post: { rules: [] }, + all: {}, + keywords: {} + }; + } + exports.getRules = getRules; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/applicability.js +var require_applicability2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/applicability.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.shouldUseRule = exports.shouldUseGroup = exports.schemaHasRulesForType = void 0; + function schemaHasRulesForType({ schema, self }, type) { + const group = self.RULES.types[type]; + return group && group !== true && shouldUseGroup(schema, group); + } + exports.schemaHasRulesForType = schemaHasRulesForType; + function shouldUseGroup(schema, group) { + return group.rules.some((rule) => shouldUseRule(schema, rule)); + } + exports.shouldUseGroup = shouldUseGroup; + function shouldUseRule(schema, rule) { + var _a; + return schema[rule.keyword] !== void 0 || ((_a = rule.definition.implements) === null || _a === void 0 ? void 0 : _a.some((kwd) => schema[kwd] !== void 0)); + } + exports.shouldUseRule = shouldUseRule; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/dataType.js +var require_dataType2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/dataType.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.reportTypeError = exports.checkDataTypes = exports.checkDataType = exports.coerceAndCheckDataType = exports.getJSONTypes = exports.getSchemaTypes = exports.DataType = void 0; + var rules_1 = require_rules2(); + var applicability_1 = require_applicability2(); + var errors_1 = require_errors2(); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var DataType; + (function(DataType2) { + DataType2[DataType2["Correct"] = 0] = "Correct"; + DataType2[DataType2["Wrong"] = 1] = "Wrong"; + })(DataType || (exports.DataType = DataType = {})); + function getSchemaTypes(schema) { + const types = getJSONTypes(schema.type); + const hasNull = types.includes("null"); + if (hasNull) { + if (schema.nullable === false) + throw new Error("type: null contradicts nullable: false"); + } else { + if (!types.length && schema.nullable !== void 0) { + throw new Error('"nullable" cannot be used without "type"'); + } + if (schema.nullable === true) + types.push("null"); + } + return types; + } + exports.getSchemaTypes = getSchemaTypes; + function getJSONTypes(ts) { + const types = Array.isArray(ts) ? ts : ts ? [ts] : []; + if (types.every(rules_1.isJSONType)) + return types; + throw new Error("type must be JSONType or JSONType[]: " + types.join(",")); + } + exports.getJSONTypes = getJSONTypes; + function coerceAndCheckDataType(it2, types) { + const { gen, data, opts } = it2; + const coerceTo = coerceToTypes(types, opts.coerceTypes); + const checkTypes = types.length > 0 && !(coerceTo.length === 0 && types.length === 1 && (0, applicability_1.schemaHasRulesForType)(it2, types[0])); + if (checkTypes) { + const wrongType = checkDataTypes(types, data, opts.strictNumbers, DataType.Wrong); + gen.if(wrongType, () => { + if (coerceTo.length) + coerceData(it2, types, coerceTo); + else + reportTypeError(it2); + }); + } + return checkTypes; + } + exports.coerceAndCheckDataType = coerceAndCheckDataType; + var COERCIBLE = /* @__PURE__ */ new Set(["string", "number", "integer", "boolean", "null"]); + function coerceToTypes(types, coerceTypes) { + return coerceTypes ? types.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : []; + } + function coerceData(it2, types, coerceTo) { + const { gen, data, opts } = it2; + const dataType = gen.let("dataType", (0, codegen_1._)`typeof ${data}`); + const coerced = gen.let("coerced", (0, codegen_1._)`undefined`); + if (opts.coerceTypes === "array") { + gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(types, data, opts.strictNumbers), () => gen.assign(coerced, data))); + } + gen.if((0, codegen_1._)`${coerced} !== undefined`); + for (const t of coerceTo) { + if (COERCIBLE.has(t) || t === "array" && opts.coerceTypes === "array") { + coerceSpecificType(t); + } + } + gen.else(); + reportTypeError(it2); + gen.endIf(); + gen.if((0, codegen_1._)`${coerced} !== undefined`, () => { + gen.assign(data, coerced); + assignParentData(it2, coerced); + }); + function coerceSpecificType(t) { + switch (t) { + case "string": + gen.elseIf((0, codegen_1._)`${dataType} == "number" || ${dataType} == "boolean"`).assign(coerced, (0, codegen_1._)`"" + ${data}`).elseIf((0, codegen_1._)`${data} === null`).assign(coerced, (0, codegen_1._)`""`); + return; + case "number": + gen.elseIf((0, codegen_1._)`${dataType} == "boolean" || ${data} === null + || (${dataType} == "string" && ${data} && ${data} == +${data})`).assign(coerced, (0, codegen_1._)`+${data}`); + return; + case "integer": + gen.elseIf((0, codegen_1._)`${dataType} === "boolean" || ${data} === null + || (${dataType} === "string" && ${data} && ${data} == +${data} && !(${data} % 1))`).assign(coerced, (0, codegen_1._)`+${data}`); + return; + case "boolean": + gen.elseIf((0, codegen_1._)`${data} === "false" || ${data} === 0 || ${data} === null`).assign(coerced, false).elseIf((0, codegen_1._)`${data} === "true" || ${data} === 1`).assign(coerced, true); + return; + case "null": + gen.elseIf((0, codegen_1._)`${data} === "" || ${data} === 0 || ${data} === false`); + gen.assign(coerced, null); + return; + case "array": + gen.elseIf((0, codegen_1._)`${dataType} === "string" || ${dataType} === "number" + || ${dataType} === "boolean" || ${data} === null`).assign(coerced, (0, codegen_1._)`[${data}]`); + } + } + } + function assignParentData({ gen, parentData, parentDataProperty }, expr) { + gen.if((0, codegen_1._)`${parentData} !== undefined`, () => gen.assign((0, codegen_1._)`${parentData}[${parentDataProperty}]`, expr)); + } + function checkDataType(dataType, data, strictNums, correct = DataType.Correct) { + const EQ = correct === DataType.Correct ? codegen_1.operators.EQ : codegen_1.operators.NEQ; + let cond; + switch (dataType) { + case "null": + return (0, codegen_1._)`${data} ${EQ} null`; + case "array": + cond = (0, codegen_1._)`Array.isArray(${data})`; + break; + case "object": + cond = (0, codegen_1._)`${data} && typeof ${data} == "object" && !Array.isArray(${data})`; + break; + case "integer": + cond = numCond((0, codegen_1._)`!(${data} % 1) && !isNaN(${data})`); + break; + case "number": + cond = numCond(); + break; + default: + return (0, codegen_1._)`typeof ${data} ${EQ} ${dataType}`; + } + return correct === DataType.Correct ? cond : (0, codegen_1.not)(cond); + function numCond(_cond = codegen_1.nil) { + return (0, codegen_1.and)((0, codegen_1._)`typeof ${data} == "number"`, _cond, strictNums ? (0, codegen_1._)`isFinite(${data})` : codegen_1.nil); + } + } + exports.checkDataType = checkDataType; + function checkDataTypes(dataTypes, data, strictNums, correct) { + if (dataTypes.length === 1) { + return checkDataType(dataTypes[0], data, strictNums, correct); + } + let cond; + const types = (0, util_1.toHash)(dataTypes); + if (types.array && types.object) { + const notObj = (0, codegen_1._)`typeof ${data} != "object"`; + cond = types.null ? notObj : (0, codegen_1._)`!${data} || ${notObj}`; + delete types.null; + delete types.array; + delete types.object; + } else { + cond = codegen_1.nil; + } + if (types.number) + delete types.integer; + for (const t in types) + cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct)); + return cond; + } + exports.checkDataTypes = checkDataTypes; + var typeError = { + message: ({ schema }) => `must be ${schema}`, + params: ({ schema, schemaValue }) => typeof schema == "string" ? (0, codegen_1._)`{type: ${schema}}` : (0, codegen_1._)`{type: ${schemaValue}}` + }; + function reportTypeError(it2) { + const cxt = getTypeErrorContext(it2); + (0, errors_1.reportError)(cxt, typeError); + } + exports.reportTypeError = reportTypeError; + function getTypeErrorContext(it2) { + const { gen, data, schema } = it2; + const schemaCode = (0, util_1.schemaRefOrVal)(it2, schema, "type"); + return { + gen, + keyword: "type", + data, + schema: schema.type, + schemaCode, + schemaValue: schemaCode, + parentSchema: schema, + params: {}, + it: it2 + }; + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/defaults.js +var require_defaults2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/defaults.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.assignDefaults = void 0; + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + function assignDefaults(it2, ty) { + const { properties, items } = it2.schema; + if (ty === "object" && properties) { + for (const key in properties) { + assignDefault(it2, key, properties[key].default); + } + } else if (ty === "array" && Array.isArray(items)) { + items.forEach((sch, i) => assignDefault(it2, i, sch.default)); + } + } + exports.assignDefaults = assignDefaults; + function assignDefault(it2, prop, defaultValue) { + const { gen, compositeRule, data, opts } = it2; + if (defaultValue === void 0) + return; + const childData = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(prop)}`; + if (compositeRule) { + (0, util_1.checkStrictMode)(it2, `default is ignored for: ${childData}`); + return; + } + let condition = (0, codegen_1._)`${childData} === undefined`; + if (opts.useDefaults === "empty") { + condition = (0, codegen_1._)`${condition} || ${childData} === null || ${childData} === ""`; + } + gen.if(condition, (0, codegen_1._)`${childData} = ${(0, codegen_1.stringify)(defaultValue)}`); + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/code.js +var require_code4 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/code.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateUnion = exports.validateArray = exports.usePattern = exports.callValidateCode = exports.schemaProperties = exports.allSchemaProperties = exports.noPropertyInData = exports.propertyInData = exports.isOwnProperty = exports.hasPropFunc = exports.reportMissingProp = exports.checkMissingProp = exports.checkReportMissingProp = void 0; + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var names_1 = require_names2(); + var util_2 = require_util2(); + function checkReportMissingProp(cxt, prop) { + const { gen, data, it: it2 } = cxt; + gen.if(noPropertyInData(gen, data, prop, it2.opts.ownProperties), () => { + cxt.setParams({ missingProperty: (0, codegen_1._)`${prop}` }, true); + cxt.error(); + }); + } + exports.checkReportMissingProp = checkReportMissingProp; + function checkMissingProp({ gen, data, it: { opts } }, properties, missing) { + return (0, codegen_1.or)(...properties.map((prop) => (0, codegen_1.and)(noPropertyInData(gen, data, prop, opts.ownProperties), (0, codegen_1._)`${missing} = ${prop}`))); + } + exports.checkMissingProp = checkMissingProp; + function reportMissingProp(cxt, missing) { + cxt.setParams({ missingProperty: missing }, true); + cxt.error(); + } + exports.reportMissingProp = reportMissingProp; + function hasPropFunc(gen) { + return gen.scopeValue("func", { + // eslint-disable-next-line @typescript-eslint/unbound-method + ref: Object.prototype.hasOwnProperty, + code: (0, codegen_1._)`Object.prototype.hasOwnProperty` + }); + } + exports.hasPropFunc = hasPropFunc; + function isOwnProperty(gen, data, property) { + return (0, codegen_1._)`${hasPropFunc(gen)}.call(${data}, ${property})`; + } + exports.isOwnProperty = isOwnProperty; + function propertyInData(gen, data, property, ownProperties) { + const cond = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(property)} !== undefined`; + return ownProperties ? (0, codegen_1._)`${cond} && ${isOwnProperty(gen, data, property)}` : cond; + } + exports.propertyInData = propertyInData; + function noPropertyInData(gen, data, property, ownProperties) { + const cond = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(property)} === undefined`; + return ownProperties ? (0, codegen_1.or)(cond, (0, codegen_1.not)(isOwnProperty(gen, data, property))) : cond; + } + exports.noPropertyInData = noPropertyInData; + function allSchemaProperties(schemaMap) { + return schemaMap ? Object.keys(schemaMap).filter((p2) => p2 !== "__proto__") : []; + } + exports.allSchemaProperties = allSchemaProperties; + function schemaProperties(it2, schemaMap) { + return allSchemaProperties(schemaMap).filter((p2) => !(0, util_1.alwaysValidSchema)(it2, schemaMap[p2])); + } + exports.schemaProperties = schemaProperties; + function callValidateCode({ schemaCode, data, it: { gen, topSchemaRef, schemaPath, errorPath }, it: it2 }, func, context, passSchema) { + const dataAndSchema = passSchema ? (0, codegen_1._)`${schemaCode}, ${data}, ${topSchemaRef}${schemaPath}` : data; + const valCxt = [ + [names_1.default.instancePath, (0, codegen_1.strConcat)(names_1.default.instancePath, errorPath)], + [names_1.default.parentData, it2.parentData], + [names_1.default.parentDataProperty, it2.parentDataProperty], + [names_1.default.rootData, names_1.default.rootData] + ]; + if (it2.opts.dynamicRef) + valCxt.push([names_1.default.dynamicAnchors, names_1.default.dynamicAnchors]); + const args = (0, codegen_1._)`${dataAndSchema}, ${gen.object(...valCxt)}`; + return context !== codegen_1.nil ? (0, codegen_1._)`${func}.call(${context}, ${args})` : (0, codegen_1._)`${func}(${args})`; + } + exports.callValidateCode = callValidateCode; + var newRegExp = (0, codegen_1._)`new RegExp`; + function usePattern({ gen, it: { opts } }, pattern) { + const u = opts.unicodeRegExp ? "u" : ""; + const { regExp } = opts.code; + const rx = regExp(pattern, u); + return gen.scopeValue("pattern", { + key: rx.toString(), + ref: rx, + code: (0, codegen_1._)`${regExp.code === "new RegExp" ? newRegExp : (0, util_2.useFunc)(gen, regExp)}(${pattern}, ${u})` + }); + } + exports.usePattern = usePattern; + function validateArray(cxt) { + const { gen, data, keyword, it: it2 } = cxt; + const valid = gen.name("valid"); + if (it2.allErrors) { + const validArr = gen.let("valid", true); + validateItems(() => gen.assign(validArr, false)); + return validArr; + } + gen.var(valid, true); + validateItems(() => gen.break()); + return valid; + function validateItems(notValid) { + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + gen.forRange("i", 0, len, (i) => { + cxt.subschema({ + keyword, + dataProp: i, + dataPropType: util_1.Type.Num + }, valid); + gen.if((0, codegen_1.not)(valid), notValid); + }); + } + } + exports.validateArray = validateArray; + function validateUnion(cxt) { + const { gen, schema, keyword, it: it2 } = cxt; + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const alwaysValid = schema.some((sch) => (0, util_1.alwaysValidSchema)(it2, sch)); + if (alwaysValid && !it2.opts.unevaluated) + return; + const valid = gen.let("valid", false); + const schValid = gen.name("_valid"); + gen.block(() => schema.forEach((_sch, i) => { + const schCxt = cxt.subschema({ + keyword, + schemaProp: i, + compositeRule: true + }, schValid); + gen.assign(valid, (0, codegen_1._)`${valid} || ${schValid}`); + const merged = cxt.mergeValidEvaluated(schCxt, schValid); + if (!merged) + gen.if((0, codegen_1.not)(valid)); + })); + cxt.result(valid, () => cxt.reset(), () => cxt.error(true)); + } + exports.validateUnion = validateUnion; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/keyword.js +var require_keyword2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/keyword.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateKeywordUsage = exports.validSchemaType = exports.funcKeywordCode = exports.macroKeywordCode = void 0; + var codegen_1 = require_codegen2(); + var names_1 = require_names2(); + var code_1 = require_code4(); + var errors_1 = require_errors2(); + function macroKeywordCode(cxt, def) { + const { gen, keyword, schema, parentSchema, it: it2 } = cxt; + const macroSchema = def.macro.call(it2.self, schema, parentSchema, it2); + const schemaRef = useKeyword(gen, keyword, macroSchema); + if (it2.opts.validateSchema !== false) + it2.self.validateSchema(macroSchema, true); + const valid = gen.name("valid"); + cxt.subschema({ + schema: macroSchema, + schemaPath: codegen_1.nil, + errSchemaPath: `${it2.errSchemaPath}/${keyword}`, + topSchemaRef: schemaRef, + compositeRule: true + }, valid); + cxt.pass(valid, () => cxt.error(true)); + } + exports.macroKeywordCode = macroKeywordCode; + function funcKeywordCode(cxt, def) { + var _a; + const { gen, keyword, schema, parentSchema, $data, it: it2 } = cxt; + checkAsyncKeyword(it2, def); + const validate = !$data && def.compile ? def.compile.call(it2.self, schema, parentSchema, it2) : def.validate; + const validateRef = useKeyword(gen, keyword, validate); + const valid = gen.let("valid"); + cxt.block$data(valid, validateKeyword); + cxt.ok((_a = def.valid) !== null && _a !== void 0 ? _a : valid); + function validateKeyword() { + if (def.errors === false) { + assignValid(); + if (def.modifying) + modifyData(cxt); + reportErrs(() => cxt.error()); + } else { + const ruleErrs = def.async ? validateAsync() : validateSync(); + if (def.modifying) + modifyData(cxt); + reportErrs(() => addErrs(cxt, ruleErrs)); + } + } + function validateAsync() { + const ruleErrs = gen.let("ruleErrs", null); + gen.try(() => assignValid((0, codegen_1._)`await `), (e) => gen.assign(valid, false).if((0, codegen_1._)`${e} instanceof ${it2.ValidationError}`, () => gen.assign(ruleErrs, (0, codegen_1._)`${e}.errors`), () => gen.throw(e))); + return ruleErrs; + } + function validateSync() { + const validateErrs = (0, codegen_1._)`${validateRef}.errors`; + gen.assign(validateErrs, null); + assignValid(codegen_1.nil); + return validateErrs; + } + function assignValid(_await = def.async ? (0, codegen_1._)`await ` : codegen_1.nil) { + const passCxt = it2.opts.passContext ? names_1.default.this : names_1.default.self; + const passSchema = !("compile" in def && !$data || def.schema === false); + gen.assign(valid, (0, codegen_1._)`${_await}${(0, code_1.callValidateCode)(cxt, validateRef, passCxt, passSchema)}`, def.modifying); + } + function reportErrs(errors) { + var _a2; + gen.if((0, codegen_1.not)((_a2 = def.valid) !== null && _a2 !== void 0 ? _a2 : valid), errors); + } + } + exports.funcKeywordCode = funcKeywordCode; + function modifyData(cxt) { + const { gen, data, it: it2 } = cxt; + gen.if(it2.parentData, () => gen.assign(data, (0, codegen_1._)`${it2.parentData}[${it2.parentDataProperty}]`)); + } + function addErrs(cxt, errs) { + const { gen } = cxt; + gen.if((0, codegen_1._)`Array.isArray(${errs})`, () => { + gen.assign(names_1.default.vErrors, (0, codegen_1._)`${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`).assign(names_1.default.errors, (0, codegen_1._)`${names_1.default.vErrors}.length`); + (0, errors_1.extendErrors)(cxt); + }, () => cxt.error()); + } + function checkAsyncKeyword({ schemaEnv }, def) { + if (def.async && !schemaEnv.$async) + throw new Error("async keyword in sync schema"); + } + function useKeyword(gen, keyword, result) { + if (result === void 0) + throw new Error(`keyword "${keyword}" failed to compile`); + return gen.scopeValue("keyword", typeof result == "function" ? { ref: result } : { ref: result, code: (0, codegen_1.stringify)(result) }); + } + function validSchemaType(schema, schemaType, allowUndefined = false) { + return !schemaType.length || schemaType.some((st2) => st2 === "array" ? Array.isArray(schema) : st2 === "object" ? schema && typeof schema == "object" && !Array.isArray(schema) : typeof schema == st2 || allowUndefined && typeof schema == "undefined"); + } + exports.validSchemaType = validSchemaType; + function validateKeywordUsage({ schema, opts, self, errSchemaPath }, def, keyword) { + if (Array.isArray(def.keyword) ? !def.keyword.includes(keyword) : def.keyword !== keyword) { + throw new Error("ajv implementation error"); + } + const deps = def.dependencies; + if (deps === null || deps === void 0 ? void 0 : deps.some((kwd) => !Object.prototype.hasOwnProperty.call(schema, kwd))) { + throw new Error(`parent schema must have dependencies of ${keyword}: ${deps.join(",")}`); + } + if (def.validateSchema) { + const valid = def.validateSchema(schema[keyword]); + if (!valid) { + const msg = `keyword "${keyword}" value is invalid at path "${errSchemaPath}": ` + self.errorsText(def.validateSchema.errors); + if (opts.validateSchema === "log") + self.logger.error(msg); + else + throw new Error(msg); + } + } + } + exports.validateKeywordUsage = validateKeywordUsage; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/subschema.js +var require_subschema2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/subschema.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.extendSubschemaMode = exports.extendSubschemaData = exports.getSubschema = void 0; + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + function getSubschema(it2, { keyword, schemaProp, schema, schemaPath, errSchemaPath, topSchemaRef }) { + if (keyword !== void 0 && schema !== void 0) { + throw new Error('both "keyword" and "schema" passed, only one allowed'); + } + if (keyword !== void 0) { + const sch = it2.schema[keyword]; + return schemaProp === void 0 ? { + schema: sch, + schemaPath: (0, codegen_1._)`${it2.schemaPath}${(0, codegen_1.getProperty)(keyword)}`, + errSchemaPath: `${it2.errSchemaPath}/${keyword}` + } : { + schema: sch[schemaProp], + schemaPath: (0, codegen_1._)`${it2.schemaPath}${(0, codegen_1.getProperty)(keyword)}${(0, codegen_1.getProperty)(schemaProp)}`, + errSchemaPath: `${it2.errSchemaPath}/${keyword}/${(0, util_1.escapeFragment)(schemaProp)}` + }; + } + if (schema !== void 0) { + if (schemaPath === void 0 || errSchemaPath === void 0 || topSchemaRef === void 0) { + throw new Error('"schemaPath", "errSchemaPath" and "topSchemaRef" are required with "schema"'); + } + return { + schema, + schemaPath, + topSchemaRef, + errSchemaPath + }; + } + throw new Error('either "keyword" or "schema" must be passed'); + } + exports.getSubschema = getSubschema; + function extendSubschemaData(subschema, it2, { dataProp, dataPropType: dpType, data, dataTypes, propertyName }) { + if (data !== void 0 && dataProp !== void 0) { + throw new Error('both "data" and "dataProp" passed, only one allowed'); + } + const { gen } = it2; + if (dataProp !== void 0) { + const { errorPath, dataPathArr, opts } = it2; + const nextData = gen.let("data", (0, codegen_1._)`${it2.data}${(0, codegen_1.getProperty)(dataProp)}`, true); + dataContextProps(nextData); + subschema.errorPath = (0, codegen_1.str)`${errorPath}${(0, util_1.getErrorPath)(dataProp, dpType, opts.jsPropertySyntax)}`; + subschema.parentDataProperty = (0, codegen_1._)`${dataProp}`; + subschema.dataPathArr = [...dataPathArr, subschema.parentDataProperty]; + } + if (data !== void 0) { + const nextData = data instanceof codegen_1.Name ? data : gen.let("data", data, true); + dataContextProps(nextData); + if (propertyName !== void 0) + subschema.propertyName = propertyName; + } + if (dataTypes) + subschema.dataTypes = dataTypes; + function dataContextProps(_nextData) { + subschema.data = _nextData; + subschema.dataLevel = it2.dataLevel + 1; + subschema.dataTypes = []; + it2.definedProperties = /* @__PURE__ */ new Set(); + subschema.parentData = it2.data; + subschema.dataNames = [...it2.dataNames, _nextData]; + } + } + exports.extendSubschemaData = extendSubschemaData; + function extendSubschemaMode(subschema, { jtdDiscriminator, jtdMetadata, compositeRule, createErrors, allErrors }) { + if (compositeRule !== void 0) + subschema.compositeRule = compositeRule; + if (createErrors !== void 0) + subschema.createErrors = createErrors; + if (allErrors !== void 0) + subschema.allErrors = allErrors; + subschema.jtdDiscriminator = jtdDiscriminator; + subschema.jtdMetadata = jtdMetadata; + } + exports.extendSubschemaMode = extendSubschemaMode; + } +}); + +// node_modules/ajv-formats/node_modules/json-schema-traverse/index.js +var require_json_schema_traverse2 = __commonJS({ + "node_modules/ajv-formats/node_modules/json-schema-traverse/index.js"(exports, module) { + "use strict"; + var traverse = module.exports = function(schema, opts, cb) { + if (typeof opts == "function") { + cb = opts; + opts = {}; + } + cb = opts.cb || cb; + var pre = typeof cb == "function" ? cb : cb.pre || function() { + }; + var post = cb.post || function() { + }; + _traverse(opts, pre, post, schema, "", schema); + }; + traverse.keywords = { + additionalItems: true, + items: true, + contains: true, + additionalProperties: true, + propertyNames: true, + not: true, + if: true, + then: true, + else: true + }; + traverse.arrayKeywords = { + items: true, + allOf: true, + anyOf: true, + oneOf: true + }; + traverse.propsKeywords = { + $defs: true, + definitions: true, + properties: true, + patternProperties: true, + dependencies: true + }; + traverse.skipKeywords = { + default: true, + enum: true, + const: true, + required: true, + maximum: true, + minimum: true, + exclusiveMaximum: true, + exclusiveMinimum: true, + multipleOf: true, + maxLength: true, + minLength: true, + pattern: true, + format: true, + maxItems: true, + minItems: true, + uniqueItems: true, + maxProperties: true, + minProperties: true + }; + function _traverse(opts, pre, post, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) { + if (schema && typeof schema == "object" && !Array.isArray(schema)) { + pre(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex); + for (var key in schema) { + var sch = schema[key]; + if (Array.isArray(sch)) { + if (key in traverse.arrayKeywords) { + for (var i = 0; i < sch.length; i++) + _traverse(opts, pre, post, sch[i], jsonPtr + "/" + key + "/" + i, rootSchema, jsonPtr, key, schema, i); + } + } else if (key in traverse.propsKeywords) { + if (sch && typeof sch == "object") { + for (var prop in sch) + _traverse(opts, pre, post, sch[prop], jsonPtr + "/" + key + "/" + escapeJsonPtr(prop), rootSchema, jsonPtr, key, schema, prop); + } + } else if (key in traverse.keywords || opts.allKeys && !(key in traverse.skipKeywords)) { + _traverse(opts, pre, post, sch, jsonPtr + "/" + key, rootSchema, jsonPtr, key, schema); + } + } + post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex); + } + } + function escapeJsonPtr(str) { + return str.replace(/~/g, "~0").replace(/\//g, "~1"); + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/resolve.js +var require_resolve2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/resolve.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getSchemaRefs = exports.resolveUrl = exports.normalizeId = exports._getFullPath = exports.getFullPath = exports.inlineRef = void 0; + var util_1 = require_util2(); + var equal = require_fast_deep_equal(); + var traverse = require_json_schema_traverse2(); + var SIMPLE_INLINED = /* @__PURE__ */ new Set([ + "type", + "format", + "pattern", + "maxLength", + "minLength", + "maxProperties", + "minProperties", + "maxItems", + "minItems", + "maximum", + "minimum", + "uniqueItems", + "multipleOf", + "required", + "enum", + "const" + ]); + function inlineRef(schema, limit = true) { + if (typeof schema == "boolean") + return true; + if (limit === true) + return !hasRef(schema); + if (!limit) + return false; + return countKeys(schema) <= limit; + } + exports.inlineRef = inlineRef; + var REF_KEYWORDS = /* @__PURE__ */ new Set([ + "$ref", + "$recursiveRef", + "$recursiveAnchor", + "$dynamicRef", + "$dynamicAnchor" + ]); + function hasRef(schema) { + for (const key in schema) { + if (REF_KEYWORDS.has(key)) + return true; + const sch = schema[key]; + if (Array.isArray(sch) && sch.some(hasRef)) + return true; + if (typeof sch == "object" && hasRef(sch)) + return true; + } + return false; + } + function countKeys(schema) { + let count = 0; + for (const key in schema) { + if (key === "$ref") + return Infinity; + count++; + if (SIMPLE_INLINED.has(key)) + continue; + if (typeof schema[key] == "object") { + (0, util_1.eachItem)(schema[key], (sch) => count += countKeys(sch)); + } + if (count === Infinity) + return Infinity; + } + return count; + } + function getFullPath(resolver, id = "", normalize) { + if (normalize !== false) + id = normalizeId(id); + const p2 = resolver.parse(id); + return _getFullPath(resolver, p2); + } + exports.getFullPath = getFullPath; + function _getFullPath(resolver, p2) { + const serialized = resolver.serialize(p2); + return serialized.split("#")[0] + "#"; + } + exports._getFullPath = _getFullPath; + var TRAILING_SLASH_HASH = /#\/?$/; + function normalizeId(id) { + return id ? id.replace(TRAILING_SLASH_HASH, "") : ""; + } + exports.normalizeId = normalizeId; + function resolveUrl(resolver, baseId, id) { + id = normalizeId(id); + return resolver.resolve(baseId, id); + } + exports.resolveUrl = resolveUrl; + var ANCHOR = /^[a-z_][-a-z0-9._]*$/i; + function getSchemaRefs(schema, baseId) { + if (typeof schema == "boolean") + return {}; + const { schemaId, uriResolver } = this.opts; + const schId = normalizeId(schema[schemaId] || baseId); + const baseIds = { "": schId }; + const pathPrefix = getFullPath(uriResolver, schId, false); + const localRefs = {}; + const schemaRefs = /* @__PURE__ */ new Set(); + traverse(schema, { allKeys: true }, (sch, jsonPtr, _, parentJsonPtr) => { + if (parentJsonPtr === void 0) + return; + const fullPath = pathPrefix + jsonPtr; + let innerBaseId = baseIds[parentJsonPtr]; + if (typeof sch[schemaId] == "string") + innerBaseId = addRef.call(this, sch[schemaId]); + addAnchor.call(this, sch.$anchor); + addAnchor.call(this, sch.$dynamicAnchor); + baseIds[jsonPtr] = innerBaseId; + function addRef(ref) { + const _resolve = this.opts.uriResolver.resolve; + ref = normalizeId(innerBaseId ? _resolve(innerBaseId, ref) : ref); + if (schemaRefs.has(ref)) + throw ambiguos(ref); + schemaRefs.add(ref); + let schOrRef = this.refs[ref]; + if (typeof schOrRef == "string") + schOrRef = this.refs[schOrRef]; + if (typeof schOrRef == "object") { + checkAmbiguosRef(sch, schOrRef.schema, ref); + } else if (ref !== normalizeId(fullPath)) { + if (ref[0] === "#") { + checkAmbiguosRef(sch, localRefs[ref], ref); + localRefs[ref] = sch; + } else { + this.refs[ref] = fullPath; + } + } + return ref; + } + function addAnchor(anchor) { + if (typeof anchor == "string") { + if (!ANCHOR.test(anchor)) + throw new Error(`invalid anchor "${anchor}"`); + addRef.call(this, `#${anchor}`); + } + } + }); + return localRefs; + function checkAmbiguosRef(sch1, sch2, ref) { + if (sch2 !== void 0 && !equal(sch1, sch2)) + throw ambiguos(ref); + } + function ambiguos(ref) { + return new Error(`reference "${ref}" resolves to more than one schema`); + } + } + exports.getSchemaRefs = getSchemaRefs; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/index.js +var require_validate2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/validate/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getData = exports.KeywordCxt = exports.validateFunctionCode = void 0; + var boolSchema_1 = require_boolSchema2(); + var dataType_1 = require_dataType2(); + var applicability_1 = require_applicability2(); + var dataType_2 = require_dataType2(); + var defaults_1 = require_defaults2(); + var keyword_1 = require_keyword2(); + var subschema_1 = require_subschema2(); + var codegen_1 = require_codegen2(); + var names_1 = require_names2(); + var resolve_1 = require_resolve2(); + var util_1 = require_util2(); + var errors_1 = require_errors2(); + function validateFunctionCode(it2) { + if (isSchemaObj(it2)) { + checkKeywords(it2); + if (schemaCxtHasRules(it2)) { + topSchemaObjCode(it2); + return; + } + } + validateFunction(it2, () => (0, boolSchema_1.topBoolOrEmptySchema)(it2)); + } + exports.validateFunctionCode = validateFunctionCode; + function validateFunction({ gen, validateName, schema, schemaEnv, opts }, body) { + if (opts.code.es5) { + gen.func(validateName, (0, codegen_1._)`${names_1.default.data}, ${names_1.default.valCxt}`, schemaEnv.$async, () => { + gen.code((0, codegen_1._)`"use strict"; ${funcSourceUrl(schema, opts)}`); + destructureValCxtES5(gen, opts); + gen.code(body); + }); + } else { + gen.func(validateName, (0, codegen_1._)`${names_1.default.data}, ${destructureValCxt(opts)}`, schemaEnv.$async, () => gen.code(funcSourceUrl(schema, opts)).code(body)); + } + } + function destructureValCxt(opts) { + return (0, codegen_1._)`{${names_1.default.instancePath}="", ${names_1.default.parentData}, ${names_1.default.parentDataProperty}, ${names_1.default.rootData}=${names_1.default.data}${opts.dynamicRef ? (0, codegen_1._)`, ${names_1.default.dynamicAnchors}={}` : codegen_1.nil}}={}`; + } + function destructureValCxtES5(gen, opts) { + gen.if(names_1.default.valCxt, () => { + gen.var(names_1.default.instancePath, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.instancePath}`); + gen.var(names_1.default.parentData, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.parentData}`); + gen.var(names_1.default.parentDataProperty, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.parentDataProperty}`); + gen.var(names_1.default.rootData, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.rootData}`); + if (opts.dynamicRef) + gen.var(names_1.default.dynamicAnchors, (0, codegen_1._)`${names_1.default.valCxt}.${names_1.default.dynamicAnchors}`); + }, () => { + gen.var(names_1.default.instancePath, (0, codegen_1._)`""`); + gen.var(names_1.default.parentData, (0, codegen_1._)`undefined`); + gen.var(names_1.default.parentDataProperty, (0, codegen_1._)`undefined`); + gen.var(names_1.default.rootData, names_1.default.data); + if (opts.dynamicRef) + gen.var(names_1.default.dynamicAnchors, (0, codegen_1._)`{}`); + }); + } + function topSchemaObjCode(it2) { + const { schema, opts, gen } = it2; + validateFunction(it2, () => { + if (opts.$comment && schema.$comment) + commentKeyword(it2); + checkNoDefault(it2); + gen.let(names_1.default.vErrors, null); + gen.let(names_1.default.errors, 0); + if (opts.unevaluated) + resetEvaluated(it2); + typeAndKeywords(it2); + returnResults(it2); + }); + return; + } + function resetEvaluated(it2) { + const { gen, validateName } = it2; + it2.evaluated = gen.const("evaluated", (0, codegen_1._)`${validateName}.evaluated`); + gen.if((0, codegen_1._)`${it2.evaluated}.dynamicProps`, () => gen.assign((0, codegen_1._)`${it2.evaluated}.props`, (0, codegen_1._)`undefined`)); + gen.if((0, codegen_1._)`${it2.evaluated}.dynamicItems`, () => gen.assign((0, codegen_1._)`${it2.evaluated}.items`, (0, codegen_1._)`undefined`)); + } + function funcSourceUrl(schema, opts) { + const schId = typeof schema == "object" && schema[opts.schemaId]; + return schId && (opts.code.source || opts.code.process) ? (0, codegen_1._)`/*# sourceURL=${schId} */` : codegen_1.nil; + } + function subschemaCode(it2, valid) { + if (isSchemaObj(it2)) { + checkKeywords(it2); + if (schemaCxtHasRules(it2)) { + subSchemaObjCode(it2, valid); + return; + } + } + (0, boolSchema_1.boolOrEmptySchema)(it2, valid); + } + function schemaCxtHasRules({ schema, self }) { + if (typeof schema == "boolean") + return !schema; + for (const key in schema) + if (self.RULES.all[key]) + return true; + return false; + } + function isSchemaObj(it2) { + return typeof it2.schema != "boolean"; + } + function subSchemaObjCode(it2, valid) { + const { schema, gen, opts } = it2; + if (opts.$comment && schema.$comment) + commentKeyword(it2); + updateContext(it2); + checkAsyncSchema(it2); + const errsCount = gen.const("_errs", names_1.default.errors); + typeAndKeywords(it2, errsCount); + gen.var(valid, (0, codegen_1._)`${errsCount} === ${names_1.default.errors}`); + } + function checkKeywords(it2) { + (0, util_1.checkUnknownRules)(it2); + checkRefsAndKeywords(it2); + } + function typeAndKeywords(it2, errsCount) { + if (it2.opts.jtd) + return schemaKeywords(it2, [], false, errsCount); + const types = (0, dataType_1.getSchemaTypes)(it2.schema); + const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it2, types); + schemaKeywords(it2, types, !checkedTypes, errsCount); + } + function checkRefsAndKeywords(it2) { + const { schema, errSchemaPath, opts, self } = it2; + if (schema.$ref && opts.ignoreKeywordsWithRef && (0, util_1.schemaHasRulesButRef)(schema, self.RULES)) { + self.logger.warn(`$ref: keywords ignored in schema at path "${errSchemaPath}"`); + } + } + function checkNoDefault(it2) { + const { schema, opts } = it2; + if (schema.default !== void 0 && opts.useDefaults && opts.strictSchema) { + (0, util_1.checkStrictMode)(it2, "default is ignored in the schema root"); + } + } + function updateContext(it2) { + const schId = it2.schema[it2.opts.schemaId]; + if (schId) + it2.baseId = (0, resolve_1.resolveUrl)(it2.opts.uriResolver, it2.baseId, schId); + } + function checkAsyncSchema(it2) { + if (it2.schema.$async && !it2.schemaEnv.$async) + throw new Error("async schema in sync schema"); + } + function commentKeyword({ gen, schemaEnv, schema, errSchemaPath, opts }) { + const msg = schema.$comment; + if (opts.$comment === true) { + gen.code((0, codegen_1._)`${names_1.default.self}.logger.log(${msg})`); + } else if (typeof opts.$comment == "function") { + const schemaPath = (0, codegen_1.str)`${errSchemaPath}/$comment`; + const rootName = gen.scopeValue("root", { ref: schemaEnv.root }); + gen.code((0, codegen_1._)`${names_1.default.self}.opts.$comment(${msg}, ${schemaPath}, ${rootName}.schema)`); + } + } + function returnResults(it2) { + const { gen, schemaEnv, validateName, ValidationError, opts } = it2; + if (schemaEnv.$async) { + gen.if((0, codegen_1._)`${names_1.default.errors} === 0`, () => gen.return(names_1.default.data), () => gen.throw((0, codegen_1._)`new ${ValidationError}(${names_1.default.vErrors})`)); + } else { + gen.assign((0, codegen_1._)`${validateName}.errors`, names_1.default.vErrors); + if (opts.unevaluated) + assignEvaluated(it2); + gen.return((0, codegen_1._)`${names_1.default.errors} === 0`); + } + } + function assignEvaluated({ gen, evaluated, props, items }) { + if (props instanceof codegen_1.Name) + gen.assign((0, codegen_1._)`${evaluated}.props`, props); + if (items instanceof codegen_1.Name) + gen.assign((0, codegen_1._)`${evaluated}.items`, items); + } + function schemaKeywords(it2, types, typeErrors, errsCount) { + const { gen, schema, data, allErrors, opts, self } = it2; + const { RULES } = self; + if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema, RULES))) { + gen.block(() => keywordCode(it2, "$ref", RULES.all.$ref.definition)); + return; + } + if (!opts.jtd) + checkStrictTypes(it2, types); + gen.block(() => { + for (const group of RULES.rules) + groupKeywords(group); + groupKeywords(RULES.post); + }); + function groupKeywords(group) { + if (!(0, applicability_1.shouldUseGroup)(schema, group)) + return; + if (group.type) { + gen.if((0, dataType_2.checkDataType)(group.type, data, opts.strictNumbers)); + iterateKeywords(it2, group); + if (types.length === 1 && types[0] === group.type && typeErrors) { + gen.else(); + (0, dataType_2.reportTypeError)(it2); + } + gen.endIf(); + } else { + iterateKeywords(it2, group); + } + if (!allErrors) + gen.if((0, codegen_1._)`${names_1.default.errors} === ${errsCount || 0}`); + } + } + function iterateKeywords(it2, group) { + const { gen, schema, opts: { useDefaults } } = it2; + if (useDefaults) + (0, defaults_1.assignDefaults)(it2, group.type); + gen.block(() => { + for (const rule of group.rules) { + if ((0, applicability_1.shouldUseRule)(schema, rule)) { + keywordCode(it2, rule.keyword, rule.definition, group.type); + } + } + }); + } + function checkStrictTypes(it2, types) { + if (it2.schemaEnv.meta || !it2.opts.strictTypes) + return; + checkContextTypes(it2, types); + if (!it2.opts.allowUnionTypes) + checkMultipleTypes(it2, types); + checkKeywordTypes(it2, it2.dataTypes); + } + function checkContextTypes(it2, types) { + if (!types.length) + return; + if (!it2.dataTypes.length) { + it2.dataTypes = types; + return; + } + types.forEach((t) => { + if (!includesType(it2.dataTypes, t)) { + strictTypesError(it2, `type "${t}" not allowed by context "${it2.dataTypes.join(",")}"`); + } + }); + narrowSchemaTypes(it2, types); + } + function checkMultipleTypes(it2, ts) { + if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) { + strictTypesError(it2, "use allowUnionTypes to allow union type keyword"); + } + } + function checkKeywordTypes(it2, ts) { + const rules = it2.self.RULES.all; + for (const keyword in rules) { + const rule = rules[keyword]; + if (typeof rule == "object" && (0, applicability_1.shouldUseRule)(it2.schema, rule)) { + const { type } = rule.definition; + if (type.length && !type.some((t) => hasApplicableType(ts, t))) { + strictTypesError(it2, `missing type "${type.join(",")}" for keyword "${keyword}"`); + } + } + } + } + function hasApplicableType(schTs, kwdT) { + return schTs.includes(kwdT) || kwdT === "number" && schTs.includes("integer"); + } + function includesType(ts, t) { + return ts.includes(t) || t === "integer" && ts.includes("number"); + } + function narrowSchemaTypes(it2, withTypes) { + const ts = []; + for (const t of it2.dataTypes) { + if (includesType(withTypes, t)) + ts.push(t); + else if (withTypes.includes("integer") && t === "number") + ts.push("integer"); + } + it2.dataTypes = ts; + } + function strictTypesError(it2, msg) { + const schemaPath = it2.schemaEnv.baseId + it2.errSchemaPath; + msg += ` at "${schemaPath}" (strictTypes)`; + (0, util_1.checkStrictMode)(it2, msg, it2.opts.strictTypes); + } + var KeywordCxt = class { + constructor(it2, def, keyword) { + (0, keyword_1.validateKeywordUsage)(it2, def, keyword); + this.gen = it2.gen; + this.allErrors = it2.allErrors; + this.keyword = keyword; + this.data = it2.data; + this.schema = it2.schema[keyword]; + this.$data = def.$data && it2.opts.$data && this.schema && this.schema.$data; + this.schemaValue = (0, util_1.schemaRefOrVal)(it2, this.schema, keyword, this.$data); + this.schemaType = def.schemaType; + this.parentSchema = it2.schema; + this.params = {}; + this.it = it2; + this.def = def; + if (this.$data) { + this.schemaCode = it2.gen.const("vSchema", getData(this.$data, it2)); + } else { + this.schemaCode = this.schemaValue; + if (!(0, keyword_1.validSchemaType)(this.schema, def.schemaType, def.allowUndefined)) { + throw new Error(`${keyword} value must be ${JSON.stringify(def.schemaType)}`); + } + } + if ("code" in def ? def.trackErrors : def.errors !== false) { + this.errsCount = it2.gen.const("_errs", names_1.default.errors); + } + } + result(condition, successAction, failAction) { + this.failResult((0, codegen_1.not)(condition), successAction, failAction); + } + failResult(condition, successAction, failAction) { + this.gen.if(condition); + if (failAction) + failAction(); + else + this.error(); + if (successAction) { + this.gen.else(); + successAction(); + if (this.allErrors) + this.gen.endIf(); + } else { + if (this.allErrors) + this.gen.endIf(); + else + this.gen.else(); + } + } + pass(condition, failAction) { + this.failResult((0, codegen_1.not)(condition), void 0, failAction); + } + fail(condition) { + if (condition === void 0) { + this.error(); + if (!this.allErrors) + this.gen.if(false); + return; + } + this.gen.if(condition); + this.error(); + if (this.allErrors) + this.gen.endIf(); + else + this.gen.else(); + } + fail$data(condition) { + if (!this.$data) + return this.fail(condition); + const { schemaCode } = this; + this.fail((0, codegen_1._)`${schemaCode} !== undefined && (${(0, codegen_1.or)(this.invalid$data(), condition)})`); + } + error(append, errorParams, errorPaths) { + if (errorParams) { + this.setParams(errorParams); + this._error(append, errorPaths); + this.setParams({}); + return; + } + this._error(append, errorPaths); + } + _error(append, errorPaths) { + ; + (append ? errors_1.reportExtraError : errors_1.reportError)(this, this.def.error, errorPaths); + } + $dataError() { + (0, errors_1.reportError)(this, this.def.$dataError || errors_1.keyword$DataError); + } + reset() { + if (this.errsCount === void 0) + throw new Error('add "trackErrors" to keyword definition'); + (0, errors_1.resetErrorsCount)(this.gen, this.errsCount); + } + ok(cond) { + if (!this.allErrors) + this.gen.if(cond); + } + setParams(obj, assign) { + if (assign) + Object.assign(this.params, obj); + else + this.params = obj; + } + block$data(valid, codeBlock, $dataValid = codegen_1.nil) { + this.gen.block(() => { + this.check$data(valid, $dataValid); + codeBlock(); + }); + } + check$data(valid = codegen_1.nil, $dataValid = codegen_1.nil) { + if (!this.$data) + return; + const { gen, schemaCode, schemaType, def } = this; + gen.if((0, codegen_1.or)((0, codegen_1._)`${schemaCode} === undefined`, $dataValid)); + if (valid !== codegen_1.nil) + gen.assign(valid, true); + if (schemaType.length || def.validateSchema) { + gen.elseIf(this.invalid$data()); + this.$dataError(); + if (valid !== codegen_1.nil) + gen.assign(valid, false); + } + gen.else(); + } + invalid$data() { + const { gen, schemaCode, schemaType, def, it: it2 } = this; + return (0, codegen_1.or)(wrong$DataType(), invalid$DataSchema()); + function wrong$DataType() { + if (schemaType.length) { + if (!(schemaCode instanceof codegen_1.Name)) + throw new Error("ajv implementation error"); + const st2 = Array.isArray(schemaType) ? schemaType : [schemaType]; + return (0, codegen_1._)`${(0, dataType_2.checkDataTypes)(st2, schemaCode, it2.opts.strictNumbers, dataType_2.DataType.Wrong)}`; + } + return codegen_1.nil; + } + function invalid$DataSchema() { + if (def.validateSchema) { + const validateSchemaRef = gen.scopeValue("validate$data", { ref: def.validateSchema }); + return (0, codegen_1._)`!${validateSchemaRef}(${schemaCode})`; + } + return codegen_1.nil; + } + } + subschema(appl, valid) { + const subschema = (0, subschema_1.getSubschema)(this.it, appl); + (0, subschema_1.extendSubschemaData)(subschema, this.it, appl); + (0, subschema_1.extendSubschemaMode)(subschema, appl); + const nextContext = { ...this.it, ...subschema, items: void 0, props: void 0 }; + subschemaCode(nextContext, valid); + return nextContext; + } + mergeEvaluated(schemaCxt, toName) { + const { it: it2, gen } = this; + if (!it2.opts.unevaluated) + return; + if (it2.props !== true && schemaCxt.props !== void 0) { + it2.props = util_1.mergeEvaluated.props(gen, schemaCxt.props, it2.props, toName); + } + if (it2.items !== true && schemaCxt.items !== void 0) { + it2.items = util_1.mergeEvaluated.items(gen, schemaCxt.items, it2.items, toName); + } + } + mergeValidEvaluated(schemaCxt, valid) { + const { it: it2, gen } = this; + if (it2.opts.unevaluated && (it2.props !== true || it2.items !== true)) { + gen.if(valid, () => this.mergeEvaluated(schemaCxt, codegen_1.Name)); + return true; + } + } + }; + exports.KeywordCxt = KeywordCxt; + function keywordCode(it2, keyword, def, ruleType) { + const cxt = new KeywordCxt(it2, def, keyword); + if ("code" in def) { + def.code(cxt, ruleType); + } else if (cxt.$data && def.validate) { + (0, keyword_1.funcKeywordCode)(cxt, def); + } else if ("macro" in def) { + (0, keyword_1.macroKeywordCode)(cxt, def); + } else if (def.compile || def.validate) { + (0, keyword_1.funcKeywordCode)(cxt, def); + } + } + var JSON_POINTER = /^\/(?:[^~]|~0|~1)*$/; + var RELATIVE_JSON_POINTER = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/; + function getData($data, { dataLevel, dataNames, dataPathArr }) { + let jsonPointer; + let data; + if ($data === "") + return names_1.default.rootData; + if ($data[0] === "/") { + if (!JSON_POINTER.test($data)) + throw new Error(`Invalid JSON-pointer: ${$data}`); + jsonPointer = $data; + data = names_1.default.rootData; + } else { + const matches = RELATIVE_JSON_POINTER.exec($data); + if (!matches) + throw new Error(`Invalid JSON-pointer: ${$data}`); + const up = +matches[1]; + jsonPointer = matches[2]; + if (jsonPointer === "#") { + if (up >= dataLevel) + throw new Error(errorMsg("property/index", up)); + return dataPathArr[dataLevel - up]; + } + if (up > dataLevel) + throw new Error(errorMsg("data", up)); + data = dataNames[dataLevel - up]; + if (!jsonPointer) + return data; + } + let expr = data; + const segments = jsonPointer.split("/"); + for (const segment of segments) { + if (segment) { + data = (0, codegen_1._)`${data}${(0, codegen_1.getProperty)((0, util_1.unescapeJsonPointer)(segment))}`; + expr = (0, codegen_1._)`${expr} && ${data}`; + } + } + return expr; + function errorMsg(pointerType, up) { + return `Cannot access ${pointerType} ${up} levels up, current level is ${dataLevel}`; + } + } + exports.getData = getData; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/runtime/validation_error.js +var require_validation_error2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/runtime/validation_error.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var ValidationError = class extends Error { + constructor(errors) { + super("validation failed"); + this.errors = errors; + this.ajv = this.validation = true; + } + }; + exports.default = ValidationError; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/ref_error.js +var require_ref_error2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/ref_error.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var resolve_1 = require_resolve2(); + var MissingRefError = class extends Error { + constructor(resolver, baseId, ref, msg) { + super(msg || `can't resolve reference ${ref} from id ${baseId}`); + this.missingRef = (0, resolve_1.resolveUrl)(resolver, baseId, ref); + this.missingSchema = (0, resolve_1.normalizeId)((0, resolve_1.getFullPath)(resolver, this.missingRef)); + } + }; + exports.default = MissingRefError; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/compile/index.js +var require_compile2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/compile/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.resolveSchema = exports.getCompilingSchema = exports.resolveRef = exports.compileSchema = exports.SchemaEnv = void 0; + var codegen_1 = require_codegen2(); + var validation_error_1 = require_validation_error2(); + var names_1 = require_names2(); + var resolve_1 = require_resolve2(); + var util_1 = require_util2(); + var validate_1 = require_validate2(); + var SchemaEnv = class { + constructor(env) { + var _a; + this.refs = {}; + this.dynamicAnchors = {}; + let schema; + if (typeof env.schema == "object") + schema = env.schema; + this.schema = env.schema; + this.schemaId = env.schemaId; + this.root = env.root || this; + this.baseId = (_a = env.baseId) !== null && _a !== void 0 ? _a : (0, resolve_1.normalizeId)(schema === null || schema === void 0 ? void 0 : schema[env.schemaId || "$id"]); + this.schemaPath = env.schemaPath; + this.localRefs = env.localRefs; + this.meta = env.meta; + this.$async = schema === null || schema === void 0 ? void 0 : schema.$async; + this.refs = {}; + } + }; + exports.SchemaEnv = SchemaEnv; + function compileSchema(sch) { + const _sch = getCompilingSchema.call(this, sch); + if (_sch) + return _sch; + const rootId = (0, resolve_1.getFullPath)(this.opts.uriResolver, sch.root.baseId); + const { es5, lines } = this.opts.code; + const { ownProperties } = this.opts; + const gen = new codegen_1.CodeGen(this.scope, { es5, lines, ownProperties }); + let _ValidationError; + if (sch.$async) { + _ValidationError = gen.scopeValue("Error", { + ref: validation_error_1.default, + code: (0, codegen_1._)`require("ajv/dist/runtime/validation_error").default` + }); + } + const validateName = gen.scopeName("validate"); + sch.validateName = validateName; + const schemaCxt = { + gen, + allErrors: this.opts.allErrors, + data: names_1.default.data, + parentData: names_1.default.parentData, + parentDataProperty: names_1.default.parentDataProperty, + dataNames: [names_1.default.data], + dataPathArr: [codegen_1.nil], + // TODO can its length be used as dataLevel if nil is removed? + dataLevel: 0, + dataTypes: [], + definedProperties: /* @__PURE__ */ new Set(), + topSchemaRef: gen.scopeValue("schema", this.opts.code.source === true ? { ref: sch.schema, code: (0, codegen_1.stringify)(sch.schema) } : { ref: sch.schema }), + validateName, + ValidationError: _ValidationError, + schema: sch.schema, + schemaEnv: sch, + rootId, + baseId: sch.baseId || rootId, + schemaPath: codegen_1.nil, + errSchemaPath: sch.schemaPath || (this.opts.jtd ? "" : "#"), + errorPath: (0, codegen_1._)`""`, + opts: this.opts, + self: this + }; + let sourceCode; + try { + this._compilations.add(sch); + (0, validate_1.validateFunctionCode)(schemaCxt); + gen.optimize(this.opts.code.optimize); + const validateCode = gen.toString(); + sourceCode = `${gen.scopeRefs(names_1.default.scope)}return ${validateCode}`; + if (this.opts.code.process) + sourceCode = this.opts.code.process(sourceCode, sch); + const makeValidate = new Function(`${names_1.default.self}`, `${names_1.default.scope}`, sourceCode); + const validate = makeValidate(this, this.scope.get()); + this.scope.value(validateName, { ref: validate }); + validate.errors = null; + validate.schema = sch.schema; + validate.schemaEnv = sch; + if (sch.$async) + validate.$async = true; + if (this.opts.code.source === true) { + validate.source = { validateName, validateCode, scopeValues: gen._values }; + } + if (this.opts.unevaluated) { + const { props, items } = schemaCxt; + validate.evaluated = { + props: props instanceof codegen_1.Name ? void 0 : props, + items: items instanceof codegen_1.Name ? void 0 : items, + dynamicProps: props instanceof codegen_1.Name, + dynamicItems: items instanceof codegen_1.Name + }; + if (validate.source) + validate.source.evaluated = (0, codegen_1.stringify)(validate.evaluated); + } + sch.validate = validate; + return sch; + } catch (e) { + delete sch.validate; + delete sch.validateName; + if (sourceCode) + this.logger.error("Error compiling schema, function code:", sourceCode); + throw e; + } finally { + this._compilations.delete(sch); + } + } + exports.compileSchema = compileSchema; + function resolveRef(root, baseId, ref) { + var _a; + ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, ref); + const schOrFunc = root.refs[ref]; + if (schOrFunc) + return schOrFunc; + let _sch = resolve.call(this, root, ref); + if (_sch === void 0) { + const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref]; + const { schemaId } = this.opts; + if (schema) + _sch = new SchemaEnv({ schema, schemaId, root, baseId }); + } + if (_sch === void 0) + return; + return root.refs[ref] = inlineOrCompile.call(this, _sch); + } + exports.resolveRef = resolveRef; + function inlineOrCompile(sch) { + if ((0, resolve_1.inlineRef)(sch.schema, this.opts.inlineRefs)) + return sch.schema; + return sch.validate ? sch : compileSchema.call(this, sch); + } + function getCompilingSchema(schEnv) { + for (const sch of this._compilations) { + if (sameSchemaEnv(sch, schEnv)) + return sch; + } + } + exports.getCompilingSchema = getCompilingSchema; + function sameSchemaEnv(s1, s2) { + return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId; + } + function resolve(root, ref) { + let sch; + while (typeof (sch = this.refs[ref]) == "string") + ref = sch; + return sch || this.schemas[ref] || resolveSchema.call(this, root, ref); + } + function resolveSchema(root, ref) { + const p2 = this.opts.uriResolver.parse(ref); + const refPath = (0, resolve_1._getFullPath)(this.opts.uriResolver, p2); + let baseId = (0, resolve_1.getFullPath)(this.opts.uriResolver, root.baseId, void 0); + if (Object.keys(root.schema).length > 0 && refPath === baseId) { + return getJsonPointer.call(this, p2, root); + } + const id = (0, resolve_1.normalizeId)(refPath); + const schOrRef = this.refs[id] || this.schemas[id]; + if (typeof schOrRef == "string") { + const sch = resolveSchema.call(this, root, schOrRef); + if (typeof (sch === null || sch === void 0 ? void 0 : sch.schema) !== "object") + return; + return getJsonPointer.call(this, p2, sch); + } + if (typeof (schOrRef === null || schOrRef === void 0 ? void 0 : schOrRef.schema) !== "object") + return; + if (!schOrRef.validate) + compileSchema.call(this, schOrRef); + if (id === (0, resolve_1.normalizeId)(ref)) { + const { schema } = schOrRef; + const { schemaId } = this.opts; + const schId = schema[schemaId]; + if (schId) + baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId); + return new SchemaEnv({ schema, schemaId, root, baseId }); + } + return getJsonPointer.call(this, p2, schOrRef); + } + exports.resolveSchema = resolveSchema; + var PREVENT_SCOPE_CHANGE = /* @__PURE__ */ new Set([ + "properties", + "patternProperties", + "enum", + "dependencies", + "definitions" + ]); + function getJsonPointer(parsedRef, { baseId, schema, root }) { + var _a; + if (((_a = parsedRef.fragment) === null || _a === void 0 ? void 0 : _a[0]) !== "/") + return; + for (const part of parsedRef.fragment.slice(1).split("/")) { + if (typeof schema === "boolean") + return; + const partSchema = schema[(0, util_1.unescapeFragment)(part)]; + if (partSchema === void 0) + return; + schema = partSchema; + const schId = typeof schema === "object" && schema[this.opts.schemaId]; + if (!PREVENT_SCOPE_CHANGE.has(part) && schId) { + baseId = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schId); + } + } + let env; + if (typeof schema != "boolean" && schema.$ref && !(0, util_1.schemaHasRulesButRef)(schema, this.RULES)) { + const $ref = (0, resolve_1.resolveUrl)(this.opts.uriResolver, baseId, schema.$ref); + env = resolveSchema.call(this, root, $ref); + } + const { schemaId } = this.opts; + env = env || new SchemaEnv({ schema, schemaId, root, baseId }); + if (env.schema !== env.root.schema) + return env; + return void 0; + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/refs/data.json +var require_data2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/refs/data.json"(exports, module) { + module.exports = { + $id: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#", + description: "Meta-schema for $data reference (JSON AnySchema extension proposal)", + type: "object", + required: ["$data"], + properties: { + $data: { + type: "string", + anyOf: [{ format: "relative-json-pointer" }, { format: "json-pointer" }] + } + }, + additionalProperties: false + }; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/runtime/uri.js +var require_uri2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/runtime/uri.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var uri = require_fast_uri(); + uri.code = 'require("ajv/dist/runtime/uri").default'; + exports.default = uri; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/core.js +var require_core3 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/core.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = void 0; + var validate_1 = require_validate2(); + Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function() { + return validate_1.KeywordCxt; + } }); + var codegen_1 = require_codegen2(); + Object.defineProperty(exports, "_", { enumerable: true, get: function() { + return codegen_1._; + } }); + Object.defineProperty(exports, "str", { enumerable: true, get: function() { + return codegen_1.str; + } }); + Object.defineProperty(exports, "stringify", { enumerable: true, get: function() { + return codegen_1.stringify; + } }); + Object.defineProperty(exports, "nil", { enumerable: true, get: function() { + return codegen_1.nil; + } }); + Object.defineProperty(exports, "Name", { enumerable: true, get: function() { + return codegen_1.Name; + } }); + Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function() { + return codegen_1.CodeGen; + } }); + var validation_error_1 = require_validation_error2(); + var ref_error_1 = require_ref_error2(); + var rules_1 = require_rules2(); + var compile_1 = require_compile2(); + var codegen_2 = require_codegen2(); + var resolve_1 = require_resolve2(); + var dataType_1 = require_dataType2(); + var util_1 = require_util2(); + var $dataRefSchema = require_data2(); + var uri_1 = require_uri2(); + var defaultRegExp = (str, flags) => new RegExp(str, flags); + defaultRegExp.code = "new RegExp"; + var META_IGNORE_OPTIONS = ["removeAdditional", "useDefaults", "coerceTypes"]; + var EXT_SCOPE_NAMES = /* @__PURE__ */ new Set([ + "validate", + "serialize", + "parse", + "wrapper", + "root", + "schema", + "keyword", + "pattern", + "formats", + "validate$data", + "func", + "obj", + "Error" + ]); + var removedOptions = { + errorDataPath: "", + format: "`validateFormats: false` can be used instead.", + nullable: '"nullable" keyword is supported by default.', + jsonPointers: "Deprecated jsPropertySyntax can be used instead.", + extendRefs: "Deprecated ignoreKeywordsWithRef can be used instead.", + missingRefs: "Pass empty schema with $id that should be ignored to ajv.addSchema.", + processCode: "Use option `code: {process: (code, schemaEnv: object) => string}`", + sourceCode: "Use option `code: {source: true}`", + strictDefaults: "It is default now, see option `strict`.", + strictKeywords: "It is default now, see option `strict`.", + uniqueItems: '"uniqueItems" keyword is always validated.', + unknownFormats: "Disable strict mode or pass `true` to `ajv.addFormat` (or `formats` option).", + cache: "Map is used as cache, schema object as key.", + serialize: "Map is used as cache, schema object as key.", + ajvErrors: "It is default now." + }; + var deprecatedOptions = { + ignoreKeywordsWithRef: "", + jsPropertySyntax: "", + unicode: '"minLength"/"maxLength" account for unicode characters by default.' + }; + var MAX_EXPRESSION = 200; + function requiredOptions(o) { + var _a, _b, _c2, _d, _e2, _f, _g2, _h, _j, _k, _l2, _m, _o2, _p, _q, _r2, _s, _t2, _u2, _v2, _w, _x, _y, _z, _0; + const s2 = o.strict; + const _optz = (_a = o.code) === null || _a === void 0 ? void 0 : _a.optimize; + const optimize = _optz === true || _optz === void 0 ? 1 : _optz || 0; + const regExp = (_c2 = (_b = o.code) === null || _b === void 0 ? void 0 : _b.regExp) !== null && _c2 !== void 0 ? _c2 : defaultRegExp; + const uriResolver = (_d = o.uriResolver) !== null && _d !== void 0 ? _d : uri_1.default; + return { + strictSchema: (_f = (_e2 = o.strictSchema) !== null && _e2 !== void 0 ? _e2 : s2) !== null && _f !== void 0 ? _f : true, + strictNumbers: (_h = (_g2 = o.strictNumbers) !== null && _g2 !== void 0 ? _g2 : s2) !== null && _h !== void 0 ? _h : true, + strictTypes: (_k = (_j = o.strictTypes) !== null && _j !== void 0 ? _j : s2) !== null && _k !== void 0 ? _k : "log", + strictTuples: (_m = (_l2 = o.strictTuples) !== null && _l2 !== void 0 ? _l2 : s2) !== null && _m !== void 0 ? _m : "log", + strictRequired: (_p = (_o2 = o.strictRequired) !== null && _o2 !== void 0 ? _o2 : s2) !== null && _p !== void 0 ? _p : false, + code: o.code ? { ...o.code, optimize, regExp } : { optimize, regExp }, + loopRequired: (_q = o.loopRequired) !== null && _q !== void 0 ? _q : MAX_EXPRESSION, + loopEnum: (_r2 = o.loopEnum) !== null && _r2 !== void 0 ? _r2 : MAX_EXPRESSION, + meta: (_s = o.meta) !== null && _s !== void 0 ? _s : true, + messages: (_t2 = o.messages) !== null && _t2 !== void 0 ? _t2 : true, + inlineRefs: (_u2 = o.inlineRefs) !== null && _u2 !== void 0 ? _u2 : true, + schemaId: (_v2 = o.schemaId) !== null && _v2 !== void 0 ? _v2 : "$id", + addUsedSchema: (_w = o.addUsedSchema) !== null && _w !== void 0 ? _w : true, + validateSchema: (_x = o.validateSchema) !== null && _x !== void 0 ? _x : true, + validateFormats: (_y = o.validateFormats) !== null && _y !== void 0 ? _y : true, + unicodeRegExp: (_z = o.unicodeRegExp) !== null && _z !== void 0 ? _z : true, + int32range: (_0 = o.int32range) !== null && _0 !== void 0 ? _0 : true, + uriResolver + }; + } + var Ajv2 = class { + constructor(opts = {}) { + this.schemas = {}; + this.refs = {}; + this.formats = /* @__PURE__ */ Object.create(null); + this._compilations = /* @__PURE__ */ new Set(); + this._loading = {}; + this._cache = /* @__PURE__ */ new Map(); + opts = this.opts = { ...opts, ...requiredOptions(opts) }; + const { es5, lines } = this.opts.code; + this.scope = new codegen_2.ValueScope({ scope: {}, prefixes: EXT_SCOPE_NAMES, es5, lines }); + this.logger = getLogger(opts.logger); + const formatOpt = opts.validateFormats; + opts.validateFormats = false; + this.RULES = (0, rules_1.getRules)(); + checkOptions.call(this, removedOptions, opts, "NOT SUPPORTED"); + checkOptions.call(this, deprecatedOptions, opts, "DEPRECATED", "warn"); + this._metaOpts = getMetaSchemaOptions.call(this); + if (opts.formats) + addInitialFormats.call(this); + this._addVocabularies(); + this._addDefaultMetaSchema(); + if (opts.keywords) + addInitialKeywords.call(this, opts.keywords); + if (typeof opts.meta == "object") + this.addMetaSchema(opts.meta); + addInitialSchemas.call(this); + opts.validateFormats = formatOpt; + } + _addVocabularies() { + this.addKeyword("$async"); + } + _addDefaultMetaSchema() { + const { $data, meta, schemaId } = this.opts; + let _dataRefSchema = $dataRefSchema; + if (schemaId === "id") { + _dataRefSchema = { ...$dataRefSchema }; + _dataRefSchema.id = _dataRefSchema.$id; + delete _dataRefSchema.$id; + } + if (meta && $data) + this.addMetaSchema(_dataRefSchema, _dataRefSchema[schemaId], false); + } + defaultMeta() { + const { meta, schemaId } = this.opts; + return this.opts.defaultMeta = typeof meta == "object" ? meta[schemaId] || meta : void 0; + } + validate(schemaKeyRef, data) { + let v; + if (typeof schemaKeyRef == "string") { + v = this.getSchema(schemaKeyRef); + if (!v) + throw new Error(`no schema with key or ref "${schemaKeyRef}"`); + } else { + v = this.compile(schemaKeyRef); + } + const valid = v(data); + if (!("$async" in v)) + this.errors = v.errors; + return valid; + } + compile(schema, _meta) { + const sch = this._addSchema(schema, _meta); + return sch.validate || this._compileSchemaEnv(sch); + } + compileAsync(schema, meta) { + if (typeof this.opts.loadSchema != "function") { + throw new Error("options.loadSchema should be a function"); + } + const { loadSchema } = this.opts; + return runCompileAsync.call(this, schema, meta); + async function runCompileAsync(_schema, _meta) { + await loadMetaSchema.call(this, _schema.$schema); + const sch = this._addSchema(_schema, _meta); + return sch.validate || _compileAsync.call(this, sch); + } + async function loadMetaSchema($ref) { + if ($ref && !this.getSchema($ref)) { + await runCompileAsync.call(this, { $ref }, true); + } + } + async function _compileAsync(sch) { + try { + return this._compileSchemaEnv(sch); + } catch (e) { + if (!(e instanceof ref_error_1.default)) + throw e; + checkLoaded.call(this, e); + await loadMissingSchema.call(this, e.missingSchema); + return _compileAsync.call(this, sch); + } + } + function checkLoaded({ missingSchema: ref, missingRef }) { + if (this.refs[ref]) { + throw new Error(`AnySchema ${ref} is loaded but ${missingRef} cannot be resolved`); + } + } + async function loadMissingSchema(ref) { + const _schema = await _loadSchema.call(this, ref); + if (!this.refs[ref]) + await loadMetaSchema.call(this, _schema.$schema); + if (!this.refs[ref]) + this.addSchema(_schema, ref, meta); + } + async function _loadSchema(ref) { + const p2 = this._loading[ref]; + if (p2) + return p2; + try { + return await (this._loading[ref] = loadSchema(ref)); + } finally { + delete this._loading[ref]; + } + } + } + // Adds schema to the instance + addSchema(schema, key, _meta, _validateSchema = this.opts.validateSchema) { + if (Array.isArray(schema)) { + for (const sch of schema) + this.addSchema(sch, void 0, _meta, _validateSchema); + return this; + } + let id; + if (typeof schema === "object") { + const { schemaId } = this.opts; + id = schema[schemaId]; + if (id !== void 0 && typeof id != "string") { + throw new Error(`schema ${schemaId} must be string`); + } + } + key = (0, resolve_1.normalizeId)(key || id); + this._checkUnique(key); + this.schemas[key] = this._addSchema(schema, _meta, key, _validateSchema, true); + return this; + } + // Add schema that will be used to validate other schemas + // options in META_IGNORE_OPTIONS are alway set to false + addMetaSchema(schema, key, _validateSchema = this.opts.validateSchema) { + this.addSchema(schema, key, true, _validateSchema); + return this; + } + // Validate schema against its meta-schema + validateSchema(schema, throwOrLogError) { + if (typeof schema == "boolean") + return true; + let $schema; + $schema = schema.$schema; + if ($schema !== void 0 && typeof $schema != "string") { + throw new Error("$schema must be a string"); + } + $schema = $schema || this.opts.defaultMeta || this.defaultMeta(); + if (!$schema) { + this.logger.warn("meta-schema not available"); + this.errors = null; + return true; + } + const valid = this.validate($schema, schema); + if (!valid && throwOrLogError) { + const message = "schema is invalid: " + this.errorsText(); + if (this.opts.validateSchema === "log") + this.logger.error(message); + else + throw new Error(message); + } + return valid; + } + // Get compiled schema by `key` or `ref`. + // (`key` that was passed to `addSchema` or full schema reference - `schema.$id` or resolved id) + getSchema(keyRef) { + let sch; + while (typeof (sch = getSchEnv.call(this, keyRef)) == "string") + keyRef = sch; + if (sch === void 0) { + const { schemaId } = this.opts; + const root = new compile_1.SchemaEnv({ schema: {}, schemaId }); + sch = compile_1.resolveSchema.call(this, root, keyRef); + if (!sch) + return; + this.refs[keyRef] = sch; + } + return sch.validate || this._compileSchemaEnv(sch); + } + // Remove cached schema(s). + // If no parameter is passed all schemas but meta-schemas are removed. + // If RegExp is passed all schemas with key/id matching pattern but meta-schemas are removed. + // Even if schema is referenced by other schemas it still can be removed as other schemas have local references. + removeSchema(schemaKeyRef) { + if (schemaKeyRef instanceof RegExp) { + this._removeAllSchemas(this.schemas, schemaKeyRef); + this._removeAllSchemas(this.refs, schemaKeyRef); + return this; + } + switch (typeof schemaKeyRef) { + case "undefined": + this._removeAllSchemas(this.schemas); + this._removeAllSchemas(this.refs); + this._cache.clear(); + return this; + case "string": { + const sch = getSchEnv.call(this, schemaKeyRef); + if (typeof sch == "object") + this._cache.delete(sch.schema); + delete this.schemas[schemaKeyRef]; + delete this.refs[schemaKeyRef]; + return this; + } + case "object": { + const cacheKey = schemaKeyRef; + this._cache.delete(cacheKey); + let id = schemaKeyRef[this.opts.schemaId]; + if (id) { + id = (0, resolve_1.normalizeId)(id); + delete this.schemas[id]; + delete this.refs[id]; + } + return this; + } + default: + throw new Error("ajv.removeSchema: invalid parameter"); + } + } + // add "vocabulary" - a collection of keywords + addVocabulary(definitions) { + for (const def of definitions) + this.addKeyword(def); + return this; + } + addKeyword(kwdOrDef, def) { + let keyword; + if (typeof kwdOrDef == "string") { + keyword = kwdOrDef; + if (typeof def == "object") { + this.logger.warn("these parameters are deprecated, see docs for addKeyword"); + def.keyword = keyword; + } + } else if (typeof kwdOrDef == "object" && def === void 0) { + def = kwdOrDef; + keyword = def.keyword; + if (Array.isArray(keyword) && !keyword.length) { + throw new Error("addKeywords: keyword must be string or non-empty array"); + } + } else { + throw new Error("invalid addKeywords parameters"); + } + checkKeyword.call(this, keyword, def); + if (!def) { + (0, util_1.eachItem)(keyword, (kwd) => addRule.call(this, kwd)); + return this; + } + keywordMetaschema.call(this, def); + const definition = { + ...def, + type: (0, dataType_1.getJSONTypes)(def.type), + schemaType: (0, dataType_1.getJSONTypes)(def.schemaType) + }; + (0, util_1.eachItem)(keyword, definition.type.length === 0 ? (k2) => addRule.call(this, k2, definition) : (k2) => definition.type.forEach((t) => addRule.call(this, k2, definition, t))); + return this; + } + getKeyword(keyword) { + const rule = this.RULES.all[keyword]; + return typeof rule == "object" ? rule.definition : !!rule; + } + // Remove keyword + removeKeyword(keyword) { + const { RULES } = this; + delete RULES.keywords[keyword]; + delete RULES.all[keyword]; + for (const group of RULES.rules) { + const i = group.rules.findIndex((rule) => rule.keyword === keyword); + if (i >= 0) + group.rules.splice(i, 1); + } + return this; + } + // Add format + addFormat(name, format) { + if (typeof format == "string") + format = new RegExp(format); + this.formats[name] = format; + return this; + } + errorsText(errors = this.errors, { separator = ", ", dataVar = "data" } = {}) { + if (!errors || errors.length === 0) + return "No errors"; + return errors.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg); + } + $dataMetaSchema(metaSchema, keywordsJsonPointers) { + const rules = this.RULES.all; + metaSchema = JSON.parse(JSON.stringify(metaSchema)); + for (const jsonPointer of keywordsJsonPointers) { + const segments = jsonPointer.split("/").slice(1); + let keywords = metaSchema; + for (const seg of segments) + keywords = keywords[seg]; + for (const key in rules) { + const rule = rules[key]; + if (typeof rule != "object") + continue; + const { $data } = rule.definition; + const schema = keywords[key]; + if ($data && schema) + keywords[key] = schemaOrData(schema); + } + } + return metaSchema; + } + _removeAllSchemas(schemas, regex) { + for (const keyRef in schemas) { + const sch = schemas[keyRef]; + if (!regex || regex.test(keyRef)) { + if (typeof sch == "string") { + delete schemas[keyRef]; + } else if (sch && !sch.meta) { + this._cache.delete(sch.schema); + delete schemas[keyRef]; + } + } + } + } + _addSchema(schema, meta, baseId, validateSchema = this.opts.validateSchema, addSchema = this.opts.addUsedSchema) { + let id; + const { schemaId } = this.opts; + if (typeof schema == "object") { + id = schema[schemaId]; + } else { + if (this.opts.jtd) + throw new Error("schema must be object"); + else if (typeof schema != "boolean") + throw new Error("schema must be object or boolean"); + } + let sch = this._cache.get(schema); + if (sch !== void 0) + return sch; + baseId = (0, resolve_1.normalizeId)(id || baseId); + const localRefs = resolve_1.getSchemaRefs.call(this, schema, baseId); + sch = new compile_1.SchemaEnv({ schema, schemaId, meta, baseId, localRefs }); + this._cache.set(sch.schema, sch); + if (addSchema && !baseId.startsWith("#")) { + if (baseId) + this._checkUnique(baseId); + this.refs[baseId] = sch; + } + if (validateSchema) + this.validateSchema(schema, true); + return sch; + } + _checkUnique(id) { + if (this.schemas[id] || this.refs[id]) { + throw new Error(`schema with key or id "${id}" already exists`); + } + } + _compileSchemaEnv(sch) { + if (sch.meta) + this._compileMetaSchema(sch); + else + compile_1.compileSchema.call(this, sch); + if (!sch.validate) + throw new Error("ajv implementation error"); + return sch.validate; + } + _compileMetaSchema(sch) { + const currentOpts = this.opts; + this.opts = this._metaOpts; + try { + compile_1.compileSchema.call(this, sch); + } finally { + this.opts = currentOpts; + } + } + }; + Ajv2.ValidationError = validation_error_1.default; + Ajv2.MissingRefError = ref_error_1.default; + exports.default = Ajv2; + function checkOptions(checkOpts, options, msg, log = "error") { + for (const key in checkOpts) { + const opt = key; + if (opt in options) + this.logger[log](`${msg}: option ${key}. ${checkOpts[opt]}`); + } + } + function getSchEnv(keyRef) { + keyRef = (0, resolve_1.normalizeId)(keyRef); + return this.schemas[keyRef] || this.refs[keyRef]; + } + function addInitialSchemas() { + const optsSchemas = this.opts.schemas; + if (!optsSchemas) + return; + if (Array.isArray(optsSchemas)) + this.addSchema(optsSchemas); + else + for (const key in optsSchemas) + this.addSchema(optsSchemas[key], key); + } + function addInitialFormats() { + for (const name in this.opts.formats) { + const format = this.opts.formats[name]; + if (format) + this.addFormat(name, format); + } + } + function addInitialKeywords(defs) { + if (Array.isArray(defs)) { + this.addVocabulary(defs); + return; + } + this.logger.warn("keywords option as map is deprecated, pass array"); + for (const keyword in defs) { + const def = defs[keyword]; + if (!def.keyword) + def.keyword = keyword; + this.addKeyword(def); + } + } + function getMetaSchemaOptions() { + const metaOpts = { ...this.opts }; + for (const opt of META_IGNORE_OPTIONS) + delete metaOpts[opt]; + return metaOpts; + } + var noLogs = { log() { + }, warn() { + }, error() { + } }; + function getLogger(logger) { + if (logger === false) + return noLogs; + if (logger === void 0) + return console; + if (logger.log && logger.warn && logger.error) + return logger; + throw new Error("logger must implement log, warn and error methods"); + } + var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i; + function checkKeyword(keyword, def) { + const { RULES } = this; + (0, util_1.eachItem)(keyword, (kwd) => { + if (RULES.keywords[kwd]) + throw new Error(`Keyword ${kwd} is already defined`); + if (!KEYWORD_NAME.test(kwd)) + throw new Error(`Keyword ${kwd} has invalid name`); + }); + if (!def) + return; + if (def.$data && !("code" in def || "validate" in def)) { + throw new Error('$data keyword must have "code" or "validate" function'); + } + } + function addRule(keyword, definition, dataType) { + var _a; + const post = definition === null || definition === void 0 ? void 0 : definition.post; + if (dataType && post) + throw new Error('keyword with "post" flag cannot have "type"'); + const { RULES } = this; + let ruleGroup = post ? RULES.post : RULES.rules.find(({ type: t }) => t === dataType); + if (!ruleGroup) { + ruleGroup = { type: dataType, rules: [] }; + RULES.rules.push(ruleGroup); + } + RULES.keywords[keyword] = true; + if (!definition) + return; + const rule = { + keyword, + definition: { + ...definition, + type: (0, dataType_1.getJSONTypes)(definition.type), + schemaType: (0, dataType_1.getJSONTypes)(definition.schemaType) + } + }; + if (definition.before) + addBeforeRule.call(this, ruleGroup, rule, definition.before); + else + ruleGroup.rules.push(rule); + RULES.all[keyword] = rule; + (_a = definition.implements) === null || _a === void 0 ? void 0 : _a.forEach((kwd) => this.addKeyword(kwd)); + } + function addBeforeRule(ruleGroup, rule, before) { + const i = ruleGroup.rules.findIndex((_rule) => _rule.keyword === before); + if (i >= 0) { + ruleGroup.rules.splice(i, 0, rule); + } else { + ruleGroup.rules.push(rule); + this.logger.warn(`rule ${before} is not defined`); + } + } + function keywordMetaschema(def) { + let { metaSchema } = def; + if (metaSchema === void 0) + return; + if (def.$data && this.opts.$data) + metaSchema = schemaOrData(metaSchema); + def.validateSchema = this.compile(metaSchema, true); + } + var $dataRef = { + $ref: "https://raw.githubusercontent.com/ajv-validator/ajv/master/lib/refs/data.json#" + }; + function schemaOrData(schema) { + return { anyOf: [schema, $dataRef] }; + } + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/core/id.js +var require_id2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/core/id.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var def = { + keyword: "id", + code() { + throw new Error('NOT SUPPORTED: keyword "id", use "$id" for schema ID'); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/core/ref.js +var require_ref2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/core/ref.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.callRef = exports.getValidate = void 0; + var ref_error_1 = require_ref_error2(); + var code_1 = require_code4(); + var codegen_1 = require_codegen2(); + var names_1 = require_names2(); + var compile_1 = require_compile2(); + var util_1 = require_util2(); + var def = { + keyword: "$ref", + schemaType: "string", + code(cxt) { + const { gen, schema: $ref, it: it2 } = cxt; + const { baseId, schemaEnv: env, validateName, opts, self } = it2; + const { root } = env; + if (($ref === "#" || $ref === "#/") && baseId === root.baseId) + return callRootRef(); + const schOrEnv = compile_1.resolveRef.call(self, root, baseId, $ref); + if (schOrEnv === void 0) + throw new ref_error_1.default(it2.opts.uriResolver, baseId, $ref); + if (schOrEnv instanceof compile_1.SchemaEnv) + return callValidate(schOrEnv); + return inlineRefSchema(schOrEnv); + function callRootRef() { + if (env === root) + return callRef(cxt, validateName, env, env.$async); + const rootName = gen.scopeValue("root", { ref: root }); + return callRef(cxt, (0, codegen_1._)`${rootName}.validate`, root, root.$async); + } + function callValidate(sch) { + const v = getValidate(cxt, sch); + callRef(cxt, v, sch, sch.$async); + } + function inlineRefSchema(sch) { + const schName = gen.scopeValue("schema", opts.code.source === true ? { ref: sch, code: (0, codegen_1.stringify)(sch) } : { ref: sch }); + const valid = gen.name("valid"); + const schCxt = cxt.subschema({ + schema: sch, + dataTypes: [], + schemaPath: codegen_1.nil, + topSchemaRef: schName, + errSchemaPath: $ref + }, valid); + cxt.mergeEvaluated(schCxt); + cxt.ok(valid); + } + } + }; + function getValidate(cxt, sch) { + const { gen } = cxt; + return sch.validate ? gen.scopeValue("validate", { ref: sch.validate }) : (0, codegen_1._)`${gen.scopeValue("wrapper", { ref: sch })}.validate`; + } + exports.getValidate = getValidate; + function callRef(cxt, v, sch, $async) { + const { gen, it: it2 } = cxt; + const { allErrors, schemaEnv: env, opts } = it2; + const passCxt = opts.passContext ? names_1.default.this : codegen_1.nil; + if ($async) + callAsyncRef(); + else + callSyncRef(); + function callAsyncRef() { + if (!env.$async) + throw new Error("async schema referenced by sync schema"); + const valid = gen.let("valid"); + gen.try(() => { + gen.code((0, codegen_1._)`await ${(0, code_1.callValidateCode)(cxt, v, passCxt)}`); + addEvaluatedFrom(v); + if (!allErrors) + gen.assign(valid, true); + }, (e) => { + gen.if((0, codegen_1._)`!(${e} instanceof ${it2.ValidationError})`, () => gen.throw(e)); + addErrorsFrom(e); + if (!allErrors) + gen.assign(valid, false); + }); + cxt.ok(valid); + } + function callSyncRef() { + cxt.result((0, code_1.callValidateCode)(cxt, v, passCxt), () => addEvaluatedFrom(v), () => addErrorsFrom(v)); + } + function addErrorsFrom(source) { + const errs = (0, codegen_1._)`${source}.errors`; + gen.assign(names_1.default.vErrors, (0, codegen_1._)`${names_1.default.vErrors} === null ? ${errs} : ${names_1.default.vErrors}.concat(${errs})`); + gen.assign(names_1.default.errors, (0, codegen_1._)`${names_1.default.vErrors}.length`); + } + function addEvaluatedFrom(source) { + var _a; + if (!it2.opts.unevaluated) + return; + const schEvaluated = (_a = sch === null || sch === void 0 ? void 0 : sch.validate) === null || _a === void 0 ? void 0 : _a.evaluated; + if (it2.props !== true) { + if (schEvaluated && !schEvaluated.dynamicProps) { + if (schEvaluated.props !== void 0) { + it2.props = util_1.mergeEvaluated.props(gen, schEvaluated.props, it2.props); + } + } else { + const props = gen.var("props", (0, codegen_1._)`${source}.evaluated.props`); + it2.props = util_1.mergeEvaluated.props(gen, props, it2.props, codegen_1.Name); + } + } + if (it2.items !== true) { + if (schEvaluated && !schEvaluated.dynamicItems) { + if (schEvaluated.items !== void 0) { + it2.items = util_1.mergeEvaluated.items(gen, schEvaluated.items, it2.items); + } + } else { + const items = gen.var("items", (0, codegen_1._)`${source}.evaluated.items`); + it2.items = util_1.mergeEvaluated.items(gen, items, it2.items, codegen_1.Name); + } + } + } + } + exports.callRef = callRef; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/core/index.js +var require_core4 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/core/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var id_1 = require_id2(); + var ref_1 = require_ref2(); + var core = [ + "$schema", + "$id", + "$defs", + "$vocabulary", + { keyword: "$comment" }, + "definitions", + id_1.default, + ref_1.default + ]; + exports.default = core; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitNumber.js +var require_limitNumber2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitNumber.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var ops = codegen_1.operators; + var KWDs = { + maximum: { okStr: "<=", ok: ops.LTE, fail: ops.GT }, + minimum: { okStr: ">=", ok: ops.GTE, fail: ops.LT }, + exclusiveMaximum: { okStr: "<", ok: ops.LT, fail: ops.GTE }, + exclusiveMinimum: { okStr: ">", ok: ops.GT, fail: ops.LTE } + }; + var error2 = { + message: ({ keyword, schemaCode }) => (0, codegen_1.str)`must be ${KWDs[keyword].okStr} ${schemaCode}`, + params: ({ keyword, schemaCode }) => (0, codegen_1._)`{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}` + }; + var def = { + keyword: Object.keys(KWDs), + type: "number", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + cxt.fail$data((0, codegen_1._)`${data} ${KWDs[keyword].fail} ${schemaCode} || isNaN(${data})`); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/multipleOf.js +var require_multipleOf2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/multipleOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var error2 = { + message: ({ schemaCode }) => (0, codegen_1.str)`must be multiple of ${schemaCode}`, + params: ({ schemaCode }) => (0, codegen_1._)`{multipleOf: ${schemaCode}}` + }; + var def = { + keyword: "multipleOf", + type: "number", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { gen, data, schemaCode, it: it2 } = cxt; + const prec = it2.opts.multipleOfPrecision; + const res = gen.let("res"); + const invalid = prec ? (0, codegen_1._)`Math.abs(Math.round(${res}) - ${res}) > 1e-${prec}` : (0, codegen_1._)`${res} !== parseInt(${res})`; + cxt.fail$data((0, codegen_1._)`(${schemaCode} === 0 || (${res} = ${data}/${schemaCode}, ${invalid}))`); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/runtime/ucs2length.js +var require_ucs2length2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/runtime/ucs2length.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + function ucs2length(str) { + const len = str.length; + let length = 0; + let pos = 0; + let value; + while (pos < len) { + length++; + value = str.charCodeAt(pos++); + if (value >= 55296 && value <= 56319 && pos < len) { + value = str.charCodeAt(pos); + if ((value & 64512) === 56320) + pos++; + } + } + return length; + } + exports.default = ucs2length; + ucs2length.code = 'require("ajv/dist/runtime/ucs2length").default'; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitLength.js +var require_limitLength2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitLength.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var ucs2length_1 = require_ucs2length2(); + var error2 = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxLength" ? "more" : "fewer"; + return (0, codegen_1.str)`must NOT have ${comp} than ${schemaCode} characters`; + }, + params: ({ schemaCode }) => (0, codegen_1._)`{limit: ${schemaCode}}` + }; + var def = { + keyword: ["maxLength", "minLength"], + type: "string", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode, it: it2 } = cxt; + const op = keyword === "maxLength" ? codegen_1.operators.GT : codegen_1.operators.LT; + const len = it2.opts.unicode === false ? (0, codegen_1._)`${data}.length` : (0, codegen_1._)`${(0, util_1.useFunc)(cxt.gen, ucs2length_1.default)}(${data})`; + cxt.fail$data((0, codegen_1._)`${len} ${op} ${schemaCode}`); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/pattern.js +var require_pattern2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/pattern.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code4(); + var util_1 = require_util2(); + var codegen_1 = require_codegen2(); + var error2 = { + message: ({ schemaCode }) => (0, codegen_1.str)`must match pattern "${schemaCode}"`, + params: ({ schemaCode }) => (0, codegen_1._)`{pattern: ${schemaCode}}` + }; + var def = { + keyword: "pattern", + type: "string", + schemaType: "string", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schema, schemaCode, it: it2 } = cxt; + const u = it2.opts.unicodeRegExp ? "u" : ""; + if ($data) { + const { regExp } = it2.opts.code; + const regExpCode = regExp.code === "new RegExp" ? (0, codegen_1._)`new RegExp` : (0, util_1.useFunc)(gen, regExp); + const valid = gen.let("valid"); + gen.try(() => gen.assign(valid, (0, codegen_1._)`${regExpCode}(${schemaCode}, ${u}).test(${data})`), () => gen.assign(valid, false)); + cxt.fail$data((0, codegen_1._)`!${valid}`); + } else { + const regExp = (0, code_1.usePattern)(cxt, schema); + cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitProperties.js +var require_limitProperties2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitProperties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var error2 = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxProperties" ? "more" : "fewer"; + return (0, codegen_1.str)`must NOT have ${comp} than ${schemaCode} properties`; + }, + params: ({ schemaCode }) => (0, codegen_1._)`{limit: ${schemaCode}}` + }; + var def = { + keyword: ["maxProperties", "minProperties"], + type: "object", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + const op = keyword === "maxProperties" ? codegen_1.operators.GT : codegen_1.operators.LT; + cxt.fail$data((0, codegen_1._)`Object.keys(${data}).length ${op} ${schemaCode}`); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/required.js +var require_required2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/required.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code4(); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var error2 = { + message: ({ params: { missingProperty } }) => (0, codegen_1.str)`must have required property '${missingProperty}'`, + params: ({ params: { missingProperty } }) => (0, codegen_1._)`{missingProperty: ${missingProperty}}` + }; + var def = { + keyword: "required", + type: "object", + schemaType: "array", + $data: true, + error: error2, + code(cxt) { + const { gen, schema, schemaCode, data, $data, it: it2 } = cxt; + const { opts } = it2; + if (!$data && schema.length === 0) + return; + const useLoop = schema.length >= opts.loopRequired; + if (it2.allErrors) + allErrorsMode(); + else + exitOnErrorMode(); + if (opts.strictRequired) { + const props = cxt.parentSchema.properties; + const { definedProperties } = cxt.it; + for (const requiredKey of schema) { + if ((props === null || props === void 0 ? void 0 : props[requiredKey]) === void 0 && !definedProperties.has(requiredKey)) { + const schemaPath = it2.schemaEnv.baseId + it2.errSchemaPath; + const msg = `required property "${requiredKey}" is not defined at "${schemaPath}" (strictRequired)`; + (0, util_1.checkStrictMode)(it2, msg, it2.opts.strictRequired); + } + } + } + function allErrorsMode() { + if (useLoop || $data) { + cxt.block$data(codegen_1.nil, loopAllRequired); + } else { + for (const prop of schema) { + (0, code_1.checkReportMissingProp)(cxt, prop); + } + } + } + function exitOnErrorMode() { + const missing = gen.let("missing"); + if (useLoop || $data) { + const valid = gen.let("valid", true); + cxt.block$data(valid, () => loopUntilMissing(missing, valid)); + cxt.ok(valid); + } else { + gen.if((0, code_1.checkMissingProp)(cxt, schema, missing)); + (0, code_1.reportMissingProp)(cxt, missing); + gen.else(); + } + } + function loopAllRequired() { + gen.forOf("prop", schemaCode, (prop) => { + cxt.setParams({ missingProperty: prop }); + gen.if((0, code_1.noPropertyInData)(gen, data, prop, opts.ownProperties), () => cxt.error()); + }); + } + function loopUntilMissing(missing, valid) { + cxt.setParams({ missingProperty: missing }); + gen.forOf(missing, schemaCode, () => { + gen.assign(valid, (0, code_1.propertyInData)(gen, data, missing, opts.ownProperties)); + gen.if((0, codegen_1.not)(valid), () => { + cxt.error(); + gen.break(); + }); + }, codegen_1.nil); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitItems.js +var require_limitItems2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/limitItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var error2 = { + message({ keyword, schemaCode }) { + const comp = keyword === "maxItems" ? "more" : "fewer"; + return (0, codegen_1.str)`must NOT have ${comp} than ${schemaCode} items`; + }, + params: ({ schemaCode }) => (0, codegen_1._)`{limit: ${schemaCode}}` + }; + var def = { + keyword: ["maxItems", "minItems"], + type: "array", + schemaType: "number", + $data: true, + error: error2, + code(cxt) { + const { keyword, data, schemaCode } = cxt; + const op = keyword === "maxItems" ? codegen_1.operators.GT : codegen_1.operators.LT; + cxt.fail$data((0, codegen_1._)`${data}.length ${op} ${schemaCode}`); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/runtime/equal.js +var require_equal2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/runtime/equal.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var equal = require_fast_deep_equal(); + equal.code = 'require("ajv/dist/runtime/equal").default'; + exports.default = equal; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js +var require_uniqueItems2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var dataType_1 = require_dataType2(); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var equal_1 = require_equal2(); + var error2 = { + message: ({ params: { i, j: j2 } }) => (0, codegen_1.str)`must NOT have duplicate items (items ## ${j2} and ${i} are identical)`, + params: ({ params: { i, j: j2 } }) => (0, codegen_1._)`{i: ${i}, j: ${j2}}` + }; + var def = { + keyword: "uniqueItems", + type: "array", + schemaType: "boolean", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schema, parentSchema, schemaCode, it: it2 } = cxt; + if (!$data && !schema) + return; + const valid = gen.let("valid"); + const itemTypes = parentSchema.items ? (0, dataType_1.getSchemaTypes)(parentSchema.items) : []; + cxt.block$data(valid, validateUniqueItems, (0, codegen_1._)`${schemaCode} === false`); + cxt.ok(valid); + function validateUniqueItems() { + const i = gen.let("i", (0, codegen_1._)`${data}.length`); + const j2 = gen.let("j"); + cxt.setParams({ i, j: j2 }); + gen.assign(valid, true); + gen.if((0, codegen_1._)`${i} > 1`, () => (canOptimize() ? loopN : loopN2)(i, j2)); + } + function canOptimize() { + return itemTypes.length > 0 && !itemTypes.some((t) => t === "object" || t === "array"); + } + function loopN(i, j2) { + const item = gen.name("item"); + const wrongType = (0, dataType_1.checkDataTypes)(itemTypes, item, it2.opts.strictNumbers, dataType_1.DataType.Wrong); + const indices = gen.const("indices", (0, codegen_1._)`{}`); + gen.for((0, codegen_1._)`;${i}--;`, () => { + gen.let(item, (0, codegen_1._)`${data}[${i}]`); + gen.if(wrongType, (0, codegen_1._)`continue`); + if (itemTypes.length > 1) + gen.if((0, codegen_1._)`typeof ${item} == "string"`, (0, codegen_1._)`${item} += "_"`); + gen.if((0, codegen_1._)`typeof ${indices}[${item}] == "number"`, () => { + gen.assign(j2, (0, codegen_1._)`${indices}[${item}]`); + cxt.error(); + gen.assign(valid, false).break(); + }).code((0, codegen_1._)`${indices}[${item}] = ${i}`); + }); + } + function loopN2(i, j2) { + const eql = (0, util_1.useFunc)(gen, equal_1.default); + const outer = gen.name("outer"); + gen.label(outer).for((0, codegen_1._)`;${i}--;`, () => gen.for((0, codegen_1._)`${j2} = ${i}; ${j2}--;`, () => gen.if((0, codegen_1._)`${eql}(${data}[${i}], ${data}[${j2}])`, () => { + cxt.error(); + gen.assign(valid, false).break(outer); + }))); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/const.js +var require_const2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/const.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var equal_1 = require_equal2(); + var error2 = { + message: "must be equal to constant", + params: ({ schemaCode }) => (0, codegen_1._)`{allowedValue: ${schemaCode}}` + }; + var def = { + keyword: "const", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schemaCode, schema } = cxt; + if ($data || schema && typeof schema == "object") { + cxt.fail$data((0, codegen_1._)`!${(0, util_1.useFunc)(gen, equal_1.default)}(${data}, ${schemaCode})`); + } else { + cxt.fail((0, codegen_1._)`${schema} !== ${data}`); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/enum.js +var require_enum2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/enum.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var equal_1 = require_equal2(); + var error2 = { + message: "must be equal to one of the allowed values", + params: ({ schemaCode }) => (0, codegen_1._)`{allowedValues: ${schemaCode}}` + }; + var def = { + keyword: "enum", + schemaType: "array", + $data: true, + error: error2, + code(cxt) { + const { gen, data, $data, schema, schemaCode, it: it2 } = cxt; + if (!$data && schema.length === 0) + throw new Error("enum must have non-empty array"); + const useLoop = schema.length >= it2.opts.loopEnum; + let eql; + const getEql = () => eql !== null && eql !== void 0 ? eql : eql = (0, util_1.useFunc)(gen, equal_1.default); + let valid; + if (useLoop || $data) { + valid = gen.let("valid"); + cxt.block$data(valid, loopEnum); + } else { + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const vSchema = gen.const("vSchema", schemaCode); + valid = (0, codegen_1.or)(...schema.map((_x, i) => equalCode(vSchema, i))); + } + cxt.pass(valid); + function loopEnum() { + gen.assign(valid, false); + gen.forOf("v", schemaCode, (v) => gen.if((0, codegen_1._)`${getEql()}(${data}, ${v})`, () => gen.assign(valid, true).break())); + } + function equalCode(vSchema, i) { + const sch = schema[i]; + return typeof sch === "object" && sch !== null ? (0, codegen_1._)`${getEql()}(${data}, ${vSchema}[${i}])` : (0, codegen_1._)`${data} === ${sch}`; + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/index.js +var require_validation2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/validation/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var limitNumber_1 = require_limitNumber2(); + var multipleOf_1 = require_multipleOf2(); + var limitLength_1 = require_limitLength2(); + var pattern_1 = require_pattern2(); + var limitProperties_1 = require_limitProperties2(); + var required_1 = require_required2(); + var limitItems_1 = require_limitItems2(); + var uniqueItems_1 = require_uniqueItems2(); + var const_1 = require_const2(); + var enum_1 = require_enum2(); + var validation = [ + // number + limitNumber_1.default, + multipleOf_1.default, + // string + limitLength_1.default, + pattern_1.default, + // object + limitProperties_1.default, + required_1.default, + // array + limitItems_1.default, + uniqueItems_1.default, + // any + { keyword: "type", schemaType: ["string", "array"] }, + { keyword: "nullable", schemaType: "boolean" }, + const_1.default, + enum_1.default + ]; + exports.default = validation; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js +var require_additionalItems2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateAdditionalItems = void 0; + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var error2 = { + message: ({ params: { len } }) => (0, codegen_1.str)`must NOT have more than ${len} items`, + params: ({ params: { len } }) => (0, codegen_1._)`{limit: ${len}}` + }; + var def = { + keyword: "additionalItems", + type: "array", + schemaType: ["boolean", "object"], + before: "uniqueItems", + error: error2, + code(cxt) { + const { parentSchema, it: it2 } = cxt; + const { items } = parentSchema; + if (!Array.isArray(items)) { + (0, util_1.checkStrictMode)(it2, '"additionalItems" is ignored when "items" is not an array of schemas'); + return; + } + validateAdditionalItems(cxt, items); + } + }; + function validateAdditionalItems(cxt, items) { + const { gen, schema, data, keyword, it: it2 } = cxt; + it2.items = true; + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + if (schema === false) { + cxt.setParams({ len: items.length }); + cxt.pass((0, codegen_1._)`${len} <= ${items.length}`); + } else if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it2, schema)) { + const valid = gen.var("valid", (0, codegen_1._)`${len} <= ${items.length}`); + gen.if((0, codegen_1.not)(valid), () => validateItems(valid)); + cxt.ok(valid); + } + function validateItems(valid) { + gen.forRange("i", items.length, len, (i) => { + cxt.subschema({ keyword, dataProp: i, dataPropType: util_1.Type.Num }, valid); + if (!it2.allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + }); + } + } + exports.validateAdditionalItems = validateAdditionalItems; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/items.js +var require_items2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/items.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateTuple = void 0; + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var code_1 = require_code4(); + var def = { + keyword: "items", + type: "array", + schemaType: ["object", "array", "boolean"], + before: "uniqueItems", + code(cxt) { + const { schema, it: it2 } = cxt; + if (Array.isArray(schema)) + return validateTuple(cxt, "additionalItems", schema); + it2.items = true; + if ((0, util_1.alwaysValidSchema)(it2, schema)) + return; + cxt.ok((0, code_1.validateArray)(cxt)); + } + }; + function validateTuple(cxt, extraItems, schArr = cxt.schema) { + const { gen, parentSchema, data, keyword, it: it2 } = cxt; + checkStrictTuple(parentSchema); + if (it2.opts.unevaluated && schArr.length && it2.items !== true) { + it2.items = util_1.mergeEvaluated.items(gen, schArr.length, it2.items); + } + const valid = gen.name("valid"); + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + schArr.forEach((sch, i) => { + if ((0, util_1.alwaysValidSchema)(it2, sch)) + return; + gen.if((0, codegen_1._)`${len} > ${i}`, () => cxt.subschema({ + keyword, + schemaProp: i, + dataProp: i + }, valid)); + cxt.ok(valid); + }); + function checkStrictTuple(sch) { + const { opts, errSchemaPath } = it2; + const l = schArr.length; + const fullTuple = l === sch.minItems && (l === sch.maxItems || sch[extraItems] === false); + if (opts.strictTuples && !fullTuple) { + const msg = `"${keyword}" is ${l}-tuple, but minItems or maxItems/${extraItems} are not specified or different at path "${errSchemaPath}"`; + (0, util_1.checkStrictMode)(it2, msg, opts.strictTuples); + } + } + } + exports.validateTuple = validateTuple; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js +var require_prefixItems2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var items_1 = require_items2(); + var def = { + keyword: "prefixItems", + type: "array", + schemaType: ["array"], + before: "uniqueItems", + code: (cxt) => (0, items_1.validateTuple)(cxt, "items") + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/items2020.js +var require_items20202 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/items2020.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var code_1 = require_code4(); + var additionalItems_1 = require_additionalItems2(); + var error2 = { + message: ({ params: { len } }) => (0, codegen_1.str)`must NOT have more than ${len} items`, + params: ({ params: { len } }) => (0, codegen_1._)`{limit: ${len}}` + }; + var def = { + keyword: "items", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + error: error2, + code(cxt) { + const { schema, parentSchema, it: it2 } = cxt; + const { prefixItems } = parentSchema; + it2.items = true; + if ((0, util_1.alwaysValidSchema)(it2, schema)) + return; + if (prefixItems) + (0, additionalItems_1.validateAdditionalItems)(cxt, prefixItems); + else + cxt.ok((0, code_1.validateArray)(cxt)); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/contains.js +var require_contains2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/contains.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var error2 = { + message: ({ params: { min, max } }) => max === void 0 ? (0, codegen_1.str)`must contain at least ${min} valid item(s)` : (0, codegen_1.str)`must contain at least ${min} and no more than ${max} valid item(s)`, + params: ({ params: { min, max } }) => max === void 0 ? (0, codegen_1._)`{minContains: ${min}}` : (0, codegen_1._)`{minContains: ${min}, maxContains: ${max}}` + }; + var def = { + keyword: "contains", + type: "array", + schemaType: ["object", "boolean"], + before: "uniqueItems", + trackErrors: true, + error: error2, + code(cxt) { + const { gen, schema, parentSchema, data, it: it2 } = cxt; + let min; + let max; + const { minContains, maxContains } = parentSchema; + if (it2.opts.next) { + min = minContains === void 0 ? 1 : minContains; + max = maxContains; + } else { + min = 1; + } + const len = gen.const("len", (0, codegen_1._)`${data}.length`); + cxt.setParams({ min, max }); + if (max === void 0 && min === 0) { + (0, util_1.checkStrictMode)(it2, `"minContains" == 0 without "maxContains": "contains" keyword ignored`); + return; + } + if (max !== void 0 && min > max) { + (0, util_1.checkStrictMode)(it2, `"minContains" > "maxContains" is always invalid`); + cxt.fail(); + return; + } + if ((0, util_1.alwaysValidSchema)(it2, schema)) { + let cond = (0, codegen_1._)`${len} >= ${min}`; + if (max !== void 0) + cond = (0, codegen_1._)`${cond} && ${len} <= ${max}`; + cxt.pass(cond); + return; + } + it2.items = true; + const valid = gen.name("valid"); + if (max === void 0 && min === 1) { + validateItems(valid, () => gen.if(valid, () => gen.break())); + } else if (min === 0) { + gen.let(valid, true); + if (max !== void 0) + gen.if((0, codegen_1._)`${data}.length > 0`, validateItemsWithCount); + } else { + gen.let(valid, false); + validateItemsWithCount(); + } + cxt.result(valid, () => cxt.reset()); + function validateItemsWithCount() { + const schValid = gen.name("_valid"); + const count = gen.let("count", 0); + validateItems(schValid, () => gen.if(schValid, () => checkLimits(count))); + } + function validateItems(_valid, block) { + gen.forRange("i", 0, len, (i) => { + cxt.subschema({ + keyword: "contains", + dataProp: i, + dataPropType: util_1.Type.Num, + compositeRule: true + }, _valid); + block(); + }); + } + function checkLimits(count) { + gen.code((0, codegen_1._)`${count}++`); + if (max === void 0) { + gen.if((0, codegen_1._)`${count} >= ${min}`, () => gen.assign(valid, true).break()); + } else { + gen.if((0, codegen_1._)`${count} > ${max}`, () => gen.assign(valid, false).break()); + if (min === 1) + gen.assign(valid, true); + else + gen.if((0, codegen_1._)`${count} >= ${min}`, () => gen.assign(valid, true)); + } + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/dependencies.js +var require_dependencies2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/dependencies.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.validateSchemaDeps = exports.validatePropertyDeps = exports.error = void 0; + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var code_1 = require_code4(); + exports.error = { + message: ({ params: { property, depsCount, deps } }) => { + const property_ies = depsCount === 1 ? "property" : "properties"; + return (0, codegen_1.str)`must have ${property_ies} ${deps} when property ${property} is present`; + }, + params: ({ params: { property, depsCount, deps, missingProperty } }) => (0, codegen_1._)`{property: ${property}, + missingProperty: ${missingProperty}, + depsCount: ${depsCount}, + deps: ${deps}}` + // TODO change to reference + }; + var def = { + keyword: "dependencies", + type: "object", + schemaType: "object", + error: exports.error, + code(cxt) { + const [propDeps, schDeps] = splitDependencies(cxt); + validatePropertyDeps(cxt, propDeps); + validateSchemaDeps(cxt, schDeps); + } + }; + function splitDependencies({ schema }) { + const propertyDeps = {}; + const schemaDeps = {}; + for (const key in schema) { + if (key === "__proto__") + continue; + const deps = Array.isArray(schema[key]) ? propertyDeps : schemaDeps; + deps[key] = schema[key]; + } + return [propertyDeps, schemaDeps]; + } + function validatePropertyDeps(cxt, propertyDeps = cxt.schema) { + const { gen, data, it: it2 } = cxt; + if (Object.keys(propertyDeps).length === 0) + return; + const missing = gen.let("missing"); + for (const prop in propertyDeps) { + const deps = propertyDeps[prop]; + if (deps.length === 0) + continue; + const hasProperty = (0, code_1.propertyInData)(gen, data, prop, it2.opts.ownProperties); + cxt.setParams({ + property: prop, + depsCount: deps.length, + deps: deps.join(", ") + }); + if (it2.allErrors) { + gen.if(hasProperty, () => { + for (const depProp of deps) { + (0, code_1.checkReportMissingProp)(cxt, depProp); + } + }); + } else { + gen.if((0, codegen_1._)`${hasProperty} && (${(0, code_1.checkMissingProp)(cxt, deps, missing)})`); + (0, code_1.reportMissingProp)(cxt, missing); + gen.else(); + } + } + } + exports.validatePropertyDeps = validatePropertyDeps; + function validateSchemaDeps(cxt, schemaDeps = cxt.schema) { + const { gen, data, keyword, it: it2 } = cxt; + const valid = gen.name("valid"); + for (const prop in schemaDeps) { + if ((0, util_1.alwaysValidSchema)(it2, schemaDeps[prop])) + continue; + gen.if( + (0, code_1.propertyInData)(gen, data, prop, it2.opts.ownProperties), + () => { + const schCxt = cxt.subschema({ keyword, schemaProp: prop }, valid); + cxt.mergeValidEvaluated(schCxt, valid); + }, + () => gen.var(valid, true) + // TODO var + ); + cxt.ok(valid); + } + } + exports.validateSchemaDeps = validateSchemaDeps; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js +var require_propertyNames2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var error2 = { + message: "property name must be valid", + params: ({ params }) => (0, codegen_1._)`{propertyName: ${params.propertyName}}` + }; + var def = { + keyword: "propertyNames", + type: "object", + schemaType: ["object", "boolean"], + error: error2, + code(cxt) { + const { gen, schema, data, it: it2 } = cxt; + if ((0, util_1.alwaysValidSchema)(it2, schema)) + return; + const valid = gen.name("valid"); + gen.forIn("key", data, (key) => { + cxt.setParams({ propertyName: key }); + cxt.subschema({ + keyword: "propertyNames", + data: key, + dataTypes: ["string"], + propertyName: key, + compositeRule: true + }, valid); + gen.if((0, codegen_1.not)(valid), () => { + cxt.error(true); + if (!it2.allErrors) + gen.break(); + }); + }); + cxt.ok(valid); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js +var require_additionalProperties2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code4(); + var codegen_1 = require_codegen2(); + var names_1 = require_names2(); + var util_1 = require_util2(); + var error2 = { + message: "must NOT have additional properties", + params: ({ params }) => (0, codegen_1._)`{additionalProperty: ${params.additionalProperty}}` + }; + var def = { + keyword: "additionalProperties", + type: ["object"], + schemaType: ["boolean", "object"], + allowUndefined: true, + trackErrors: true, + error: error2, + code(cxt) { + const { gen, schema, parentSchema, data, errsCount, it: it2 } = cxt; + if (!errsCount) + throw new Error("ajv implementation error"); + const { allErrors, opts } = it2; + it2.props = true; + if (opts.removeAdditional !== "all" && (0, util_1.alwaysValidSchema)(it2, schema)) + return; + const props = (0, code_1.allSchemaProperties)(parentSchema.properties); + const patProps = (0, code_1.allSchemaProperties)(parentSchema.patternProperties); + checkAdditionalProperties(); + cxt.ok((0, codegen_1._)`${errsCount} === ${names_1.default.errors}`); + function checkAdditionalProperties() { + gen.forIn("key", data, (key) => { + if (!props.length && !patProps.length) + additionalPropertyCode(key); + else + gen.if(isAdditional(key), () => additionalPropertyCode(key)); + }); + } + function isAdditional(key) { + let definedProp; + if (props.length > 8) { + const propsSchema = (0, util_1.schemaRefOrVal)(it2, parentSchema.properties, "properties"); + definedProp = (0, code_1.isOwnProperty)(gen, propsSchema, key); + } else if (props.length) { + definedProp = (0, codegen_1.or)(...props.map((p2) => (0, codegen_1._)`${key} === ${p2}`)); + } else { + definedProp = codegen_1.nil; + } + if (patProps.length) { + definedProp = (0, codegen_1.or)(definedProp, ...patProps.map((p2) => (0, codegen_1._)`${(0, code_1.usePattern)(cxt, p2)}.test(${key})`)); + } + return (0, codegen_1.not)(definedProp); + } + function deleteAdditional(key) { + gen.code((0, codegen_1._)`delete ${data}[${key}]`); + } + function additionalPropertyCode(key) { + if (opts.removeAdditional === "all" || opts.removeAdditional && schema === false) { + deleteAdditional(key); + return; + } + if (schema === false) { + cxt.setParams({ additionalProperty: key }); + cxt.error(); + if (!allErrors) + gen.break(); + return; + } + if (typeof schema == "object" && !(0, util_1.alwaysValidSchema)(it2, schema)) { + const valid = gen.name("valid"); + if (opts.removeAdditional === "failing") { + applyAdditionalSchema(key, valid, false); + gen.if((0, codegen_1.not)(valid), () => { + cxt.reset(); + deleteAdditional(key); + }); + } else { + applyAdditionalSchema(key, valid); + if (!allErrors) + gen.if((0, codegen_1.not)(valid), () => gen.break()); + } + } + } + function applyAdditionalSchema(key, valid, errors) { + const subschema = { + keyword: "additionalProperties", + dataProp: key, + dataPropType: util_1.Type.Str + }; + if (errors === false) { + Object.assign(subschema, { + compositeRule: true, + createErrors: false, + allErrors: false + }); + } + cxt.subschema(subschema, valid); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/properties.js +var require_properties2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/properties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var validate_1 = require_validate2(); + var code_1 = require_code4(); + var util_1 = require_util2(); + var additionalProperties_1 = require_additionalProperties2(); + var def = { + keyword: "properties", + type: "object", + schemaType: "object", + code(cxt) { + const { gen, schema, parentSchema, data, it: it2 } = cxt; + if (it2.opts.removeAdditional === "all" && parentSchema.additionalProperties === void 0) { + additionalProperties_1.default.code(new validate_1.KeywordCxt(it2, additionalProperties_1.default, "additionalProperties")); + } + const allProps = (0, code_1.allSchemaProperties)(schema); + for (const prop of allProps) { + it2.definedProperties.add(prop); + } + if (it2.opts.unevaluated && allProps.length && it2.props !== true) { + it2.props = util_1.mergeEvaluated.props(gen, (0, util_1.toHash)(allProps), it2.props); + } + const properties = allProps.filter((p2) => !(0, util_1.alwaysValidSchema)(it2, schema[p2])); + if (properties.length === 0) + return; + const valid = gen.name("valid"); + for (const prop of properties) { + if (hasDefault(prop)) { + applyPropertySchema(prop); + } else { + gen.if((0, code_1.propertyInData)(gen, data, prop, it2.opts.ownProperties)); + applyPropertySchema(prop); + if (!it2.allErrors) + gen.else().var(valid, true); + gen.endIf(); + } + cxt.it.definedProperties.add(prop); + cxt.ok(valid); + } + function hasDefault(prop) { + return it2.opts.useDefaults && !it2.compositeRule && schema[prop].default !== void 0; + } + function applyPropertySchema(prop) { + cxt.subschema({ + keyword: "properties", + schemaProp: prop, + dataProp: prop + }, valid); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js +var require_patternProperties2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code4(); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var util_2 = require_util2(); + var def = { + keyword: "patternProperties", + type: "object", + schemaType: "object", + code(cxt) { + const { gen, schema, data, parentSchema, it: it2 } = cxt; + const { opts } = it2; + const patterns = (0, code_1.allSchemaProperties)(schema); + const alwaysValidPatterns = patterns.filter((p2) => (0, util_1.alwaysValidSchema)(it2, schema[p2])); + if (patterns.length === 0 || alwaysValidPatterns.length === patterns.length && (!it2.opts.unevaluated || it2.props === true)) { + return; + } + const checkProperties = opts.strictSchema && !opts.allowMatchingProperties && parentSchema.properties; + const valid = gen.name("valid"); + if (it2.props !== true && !(it2.props instanceof codegen_1.Name)) { + it2.props = (0, util_2.evaluatedPropsToName)(gen, it2.props); + } + const { props } = it2; + validatePatternProperties(); + function validatePatternProperties() { + for (const pat of patterns) { + if (checkProperties) + checkMatchingProperties(pat); + if (it2.allErrors) { + validateProperties(pat); + } else { + gen.var(valid, true); + validateProperties(pat); + gen.if(valid); + } + } + } + function checkMatchingProperties(pat) { + for (const prop in checkProperties) { + if (new RegExp(pat).test(prop)) { + (0, util_1.checkStrictMode)(it2, `property ${prop} matches pattern ${pat} (use allowMatchingProperties)`); + } + } + } + function validateProperties(pat) { + gen.forIn("key", data, (key) => { + gen.if((0, codegen_1._)`${(0, code_1.usePattern)(cxt, pat)}.test(${key})`, () => { + const alwaysValid = alwaysValidPatterns.includes(pat); + if (!alwaysValid) { + cxt.subschema({ + keyword: "patternProperties", + schemaProp: pat, + dataProp: key, + dataPropType: util_2.Type.Str + }, valid); + } + if (it2.opts.unevaluated && props !== true) { + gen.assign((0, codegen_1._)`${props}[${key}]`, true); + } else if (!alwaysValid && !it2.allErrors) { + gen.if((0, codegen_1.not)(valid), () => gen.break()); + } + }); + }); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/not.js +var require_not2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/not.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var util_1 = require_util2(); + var def = { + keyword: "not", + schemaType: ["object", "boolean"], + trackErrors: true, + code(cxt) { + const { gen, schema, it: it2 } = cxt; + if ((0, util_1.alwaysValidSchema)(it2, schema)) { + cxt.fail(); + return; + } + const valid = gen.name("valid"); + cxt.subschema({ + keyword: "not", + compositeRule: true, + createErrors: false, + allErrors: false + }, valid); + cxt.failResult(valid, () => cxt.reset(), () => cxt.error()); + }, + error: { message: "must NOT be valid" } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/anyOf.js +var require_anyOf2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/anyOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var code_1 = require_code4(); + var def = { + keyword: "anyOf", + schemaType: "array", + trackErrors: true, + code: code_1.validateUnion, + error: { message: "must match a schema in anyOf" } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/oneOf.js +var require_oneOf2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/oneOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var error2 = { + message: "must match exactly one schema in oneOf", + params: ({ params }) => (0, codegen_1._)`{passingSchemas: ${params.passing}}` + }; + var def = { + keyword: "oneOf", + schemaType: "array", + trackErrors: true, + error: error2, + code(cxt) { + const { gen, schema, parentSchema, it: it2 } = cxt; + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + if (it2.opts.discriminator && parentSchema.discriminator) + return; + const schArr = schema; + const valid = gen.let("valid", false); + const passing = gen.let("passing", null); + const schValid = gen.name("_valid"); + cxt.setParams({ passing }); + gen.block(validateOneOf); + cxt.result(valid, () => cxt.reset(), () => cxt.error(true)); + function validateOneOf() { + schArr.forEach((sch, i) => { + let schCxt; + if ((0, util_1.alwaysValidSchema)(it2, sch)) { + gen.var(schValid, true); + } else { + schCxt = cxt.subschema({ + keyword: "oneOf", + schemaProp: i, + compositeRule: true + }, schValid); + } + if (i > 0) { + gen.if((0, codegen_1._)`${schValid} && ${valid}`).assign(valid, false).assign(passing, (0, codegen_1._)`[${passing}, ${i}]`).else(); + } + gen.if(schValid, () => { + gen.assign(valid, true); + gen.assign(passing, i); + if (schCxt) + cxt.mergeEvaluated(schCxt, codegen_1.Name); + }); + }); + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/allOf.js +var require_allOf2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/allOf.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var util_1 = require_util2(); + var def = { + keyword: "allOf", + schemaType: "array", + code(cxt) { + const { gen, schema, it: it2 } = cxt; + if (!Array.isArray(schema)) + throw new Error("ajv implementation error"); + const valid = gen.name("valid"); + schema.forEach((sch, i) => { + if ((0, util_1.alwaysValidSchema)(it2, sch)) + return; + const schCxt = cxt.subschema({ keyword: "allOf", schemaProp: i }, valid); + cxt.ok(valid); + cxt.mergeEvaluated(schCxt); + }); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/if.js +var require_if2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/if.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var util_1 = require_util2(); + var error2 = { + message: ({ params }) => (0, codegen_1.str)`must match "${params.ifClause}" schema`, + params: ({ params }) => (0, codegen_1._)`{failingKeyword: ${params.ifClause}}` + }; + var def = { + keyword: "if", + schemaType: ["object", "boolean"], + trackErrors: true, + error: error2, + code(cxt) { + const { gen, parentSchema, it: it2 } = cxt; + if (parentSchema.then === void 0 && parentSchema.else === void 0) { + (0, util_1.checkStrictMode)(it2, '"if" without "then" and "else" is ignored'); + } + const hasThen = hasSchema(it2, "then"); + const hasElse = hasSchema(it2, "else"); + if (!hasThen && !hasElse) + return; + const valid = gen.let("valid", true); + const schValid = gen.name("_valid"); + validateIf(); + cxt.reset(); + if (hasThen && hasElse) { + const ifClause = gen.let("ifClause"); + cxt.setParams({ ifClause }); + gen.if(schValid, validateClause("then", ifClause), validateClause("else", ifClause)); + } else if (hasThen) { + gen.if(schValid, validateClause("then")); + } else { + gen.if((0, codegen_1.not)(schValid), validateClause("else")); + } + cxt.pass(valid, () => cxt.error(true)); + function validateIf() { + const schCxt = cxt.subschema({ + keyword: "if", + compositeRule: true, + createErrors: false, + allErrors: false + }, schValid); + cxt.mergeEvaluated(schCxt); + } + function validateClause(keyword, ifClause) { + return () => { + const schCxt = cxt.subschema({ keyword }, schValid); + gen.assign(valid, schValid); + cxt.mergeValidEvaluated(schCxt, valid); + if (ifClause) + gen.assign(ifClause, (0, codegen_1._)`${keyword}`); + else + cxt.setParams({ ifClause: keyword }); + }; + } + } + }; + function hasSchema(it2, keyword) { + const schema = it2.schema[keyword]; + return schema !== void 0 && !(0, util_1.alwaysValidSchema)(it2, schema); + } + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/thenElse.js +var require_thenElse2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/thenElse.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var util_1 = require_util2(); + var def = { + keyword: ["then", "else"], + schemaType: ["object", "boolean"], + code({ keyword, parentSchema, it: it2 }) { + if (parentSchema.if === void 0) + (0, util_1.checkStrictMode)(it2, `"${keyword}" without "if" is ignored`); + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/index.js +var require_applicator2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/applicator/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var additionalItems_1 = require_additionalItems2(); + var prefixItems_1 = require_prefixItems2(); + var items_1 = require_items2(); + var items2020_1 = require_items20202(); + var contains_1 = require_contains2(); + var dependencies_1 = require_dependencies2(); + var propertyNames_1 = require_propertyNames2(); + var additionalProperties_1 = require_additionalProperties2(); + var properties_1 = require_properties2(); + var patternProperties_1 = require_patternProperties2(); + var not_1 = require_not2(); + var anyOf_1 = require_anyOf2(); + var oneOf_1 = require_oneOf2(); + var allOf_1 = require_allOf2(); + var if_1 = require_if2(); + var thenElse_1 = require_thenElse2(); + function getApplicator(draft2020 = false) { + const applicator = [ + // any + not_1.default, + anyOf_1.default, + oneOf_1.default, + allOf_1.default, + if_1.default, + thenElse_1.default, + // object + propertyNames_1.default, + additionalProperties_1.default, + dependencies_1.default, + properties_1.default, + patternProperties_1.default + ]; + if (draft2020) + applicator.push(prefixItems_1.default, items2020_1.default); + else + applicator.push(additionalItems_1.default, items_1.default); + applicator.push(contains_1.default); + return applicator; + } + exports.default = getApplicator; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/format/format.js +var require_format3 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/format/format.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var error2 = { + message: ({ schemaCode }) => (0, codegen_1.str)`must match format "${schemaCode}"`, + params: ({ schemaCode }) => (0, codegen_1._)`{format: ${schemaCode}}` + }; + var def = { + keyword: "format", + type: ["number", "string"], + schemaType: "string", + $data: true, + error: error2, + code(cxt, ruleType) { + const { gen, data, $data, schema, schemaCode, it: it2 } = cxt; + const { opts, errSchemaPath, schemaEnv, self } = it2; + if (!opts.validateFormats) + return; + if ($data) + validate$DataFormat(); + else + validateFormat(); + function validate$DataFormat() { + const fmts = gen.scopeValue("formats", { + ref: self.formats, + code: opts.code.formats + }); + const fDef = gen.const("fDef", (0, codegen_1._)`${fmts}[${schemaCode}]`); + const fType = gen.let("fType"); + const format = gen.let("format"); + gen.if((0, codegen_1._)`typeof ${fDef} == "object" && !(${fDef} instanceof RegExp)`, () => gen.assign(fType, (0, codegen_1._)`${fDef}.type || "string"`).assign(format, (0, codegen_1._)`${fDef}.validate`), () => gen.assign(fType, (0, codegen_1._)`"string"`).assign(format, fDef)); + cxt.fail$data((0, codegen_1.or)(unknownFmt(), invalidFmt())); + function unknownFmt() { + if (opts.strictSchema === false) + return codegen_1.nil; + return (0, codegen_1._)`${schemaCode} && !${format}`; + } + function invalidFmt() { + const callFormat = schemaEnv.$async ? (0, codegen_1._)`(${fDef}.async ? await ${format}(${data}) : ${format}(${data}))` : (0, codegen_1._)`${format}(${data})`; + const validData = (0, codegen_1._)`(typeof ${format} == "function" ? ${callFormat} : ${format}.test(${data}))`; + return (0, codegen_1._)`${format} && ${format} !== true && ${fType} === ${ruleType} && !${validData}`; + } + } + function validateFormat() { + const formatDef = self.formats[schema]; + if (!formatDef) { + unknownFormat(); + return; + } + if (formatDef === true) + return; + const [fmtType, format, fmtRef] = getFormat(formatDef); + if (fmtType === ruleType) + cxt.pass(validCondition()); + function unknownFormat() { + if (opts.strictSchema === false) { + self.logger.warn(unknownMsg()); + return; + } + throw new Error(unknownMsg()); + function unknownMsg() { + return `unknown format "${schema}" ignored in schema at path "${errSchemaPath}"`; + } + } + function getFormat(fmtDef) { + const code = fmtDef instanceof RegExp ? (0, codegen_1.regexpCode)(fmtDef) : opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(schema)}` : void 0; + const fmt = gen.scopeValue("formats", { key: schema, ref: fmtDef, code }); + if (typeof fmtDef == "object" && !(fmtDef instanceof RegExp)) { + return [fmtDef.type || "string", fmtDef.validate, (0, codegen_1._)`${fmt}.validate`]; + } + return ["string", fmtDef, fmt]; + } + function validCondition() { + if (typeof formatDef == "object" && !(formatDef instanceof RegExp) && formatDef.async) { + if (!schemaEnv.$async) + throw new Error("async format in sync schema"); + return (0, codegen_1._)`await ${fmtRef}(${data})`; + } + return typeof format == "function" ? (0, codegen_1._)`${fmtRef}(${data})` : (0, codegen_1._)`${fmtRef}.test(${data})`; + } + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/format/index.js +var require_format4 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/format/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var format_1 = require_format3(); + var format = [format_1.default]; + exports.default = format; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/metadata.js +var require_metadata2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/metadata.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.contentVocabulary = exports.metadataVocabulary = void 0; + exports.metadataVocabulary = [ + "title", + "description", + "default", + "deprecated", + "readOnly", + "writeOnly", + "examples" + ]; + exports.contentVocabulary = [ + "contentMediaType", + "contentEncoding", + "contentSchema" + ]; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/draft7.js +var require_draft72 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/draft7.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var core_1 = require_core4(); + var validation_1 = require_validation2(); + var applicator_1 = require_applicator2(); + var format_1 = require_format4(); + var metadata_1 = require_metadata2(); + var draft7Vocabularies = [ + core_1.default, + validation_1.default, + (0, applicator_1.default)(), + format_1.default, + metadata_1.metadataVocabulary, + metadata_1.contentVocabulary + ]; + exports.default = draft7Vocabularies; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/discriminator/types.js +var require_types2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/discriminator/types.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.DiscrError = void 0; + var DiscrError; + (function(DiscrError2) { + DiscrError2["Tag"] = "tag"; + DiscrError2["Mapping"] = "mapping"; + })(DiscrError || (exports.DiscrError = DiscrError = {})); + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/discriminator/index.js +var require_discriminator2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/vocabularies/discriminator/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var codegen_1 = require_codegen2(); + var types_1 = require_types2(); + var compile_1 = require_compile2(); + var ref_error_1 = require_ref_error2(); + var util_1 = require_util2(); + var error2 = { + message: ({ params: { discrError, tagName } }) => discrError === types_1.DiscrError.Tag ? `tag "${tagName}" must be string` : `value of tag "${tagName}" must be in oneOf`, + params: ({ params: { discrError, tag, tagName } }) => (0, codegen_1._)`{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}` + }; + var def = { + keyword: "discriminator", + type: "object", + schemaType: "object", + error: error2, + code(cxt) { + const { gen, data, schema, parentSchema, it: it2 } = cxt; + const { oneOf } = parentSchema; + if (!it2.opts.discriminator) { + throw new Error("discriminator: requires discriminator option"); + } + const tagName = schema.propertyName; + if (typeof tagName != "string") + throw new Error("discriminator: requires propertyName"); + if (schema.mapping) + throw new Error("discriminator: mapping is not supported"); + if (!oneOf) + throw new Error("discriminator: requires oneOf keyword"); + const valid = gen.let("valid", false); + const tag = gen.const("tag", (0, codegen_1._)`${data}${(0, codegen_1.getProperty)(tagName)}`); + gen.if((0, codegen_1._)`typeof ${tag} == "string"`, () => validateMapping(), () => cxt.error(false, { discrError: types_1.DiscrError.Tag, tag, tagName })); + cxt.ok(valid); + function validateMapping() { + const mapping = getMapping(); + gen.if(false); + for (const tagValue in mapping) { + gen.elseIf((0, codegen_1._)`${tag} === ${tagValue}`); + gen.assign(valid, applyTagSchema(mapping[tagValue])); + } + gen.else(); + cxt.error(false, { discrError: types_1.DiscrError.Mapping, tag, tagName }); + gen.endIf(); + } + function applyTagSchema(schemaProp) { + const _valid = gen.name("valid"); + const schCxt = cxt.subschema({ keyword: "oneOf", schemaProp }, _valid); + cxt.mergeEvaluated(schCxt, codegen_1.Name); + return _valid; + } + function getMapping() { + var _a; + const oneOfMapping = {}; + const topRequired = hasRequired(parentSchema); + let tagRequired = true; + for (let i = 0; i < oneOf.length; i++) { + let sch = oneOf[i]; + if ((sch === null || sch === void 0 ? void 0 : sch.$ref) && !(0, util_1.schemaHasRulesButRef)(sch, it2.self.RULES)) { + const ref = sch.$ref; + sch = compile_1.resolveRef.call(it2.self, it2.schemaEnv.root, it2.baseId, ref); + if (sch instanceof compile_1.SchemaEnv) + sch = sch.schema; + if (sch === void 0) + throw new ref_error_1.default(it2.opts.uriResolver, it2.baseId, ref); + } + const propSch = (_a = sch === null || sch === void 0 ? void 0 : sch.properties) === null || _a === void 0 ? void 0 : _a[tagName]; + if (typeof propSch != "object") { + throw new Error(`discriminator: oneOf subschemas (or referenced schemas) must have "properties/${tagName}"`); + } + tagRequired = tagRequired && (topRequired || hasRequired(sch)); + addMappings(propSch, i); + } + if (!tagRequired) + throw new Error(`discriminator: "${tagName}" must be required`); + return oneOfMapping; + function hasRequired({ required: required2 }) { + return Array.isArray(required2) && required2.includes(tagName); + } + function addMappings(sch, i) { + if (sch.const) { + addMapping(sch.const, i); + } else if (sch.enum) { + for (const tagValue of sch.enum) { + addMapping(tagValue, i); + } + } else { + throw new Error(`discriminator: "properties/${tagName}" must have "const" or "enum"`); + } + } + function addMapping(tagValue, i) { + if (typeof tagValue != "string" || tagValue in oneOfMapping) { + throw new Error(`discriminator: "${tagName}" values must be unique strings`); + } + oneOfMapping[tagValue] = i; + } + } + } + }; + exports.default = def; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/refs/json-schema-draft-07.json +var require_json_schema_draft_072 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/refs/json-schema-draft-07.json"(exports, module) { + module.exports = { + $schema: "http://json-schema.org/draft-07/schema#", + $id: "http://json-schema.org/draft-07/schema#", + title: "Core schema meta-schema", + definitions: { + schemaArray: { + type: "array", + minItems: 1, + items: { $ref: "#" } + }, + nonNegativeInteger: { + type: "integer", + minimum: 0 + }, + nonNegativeIntegerDefault0: { + allOf: [{ $ref: "#/definitions/nonNegativeInteger" }, { default: 0 }] + }, + simpleTypes: { + enum: ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + stringArray: { + type: "array", + items: { type: "string" }, + uniqueItems: true, + default: [] + } + }, + type: ["object", "boolean"], + properties: { + $id: { + type: "string", + format: "uri-reference" + }, + $schema: { + type: "string", + format: "uri" + }, + $ref: { + type: "string", + format: "uri-reference" + }, + $comment: { + type: "string" + }, + title: { + type: "string" + }, + description: { + type: "string" + }, + default: true, + readOnly: { + type: "boolean", + default: false + }, + examples: { + type: "array", + items: true + }, + multipleOf: { + type: "number", + exclusiveMinimum: 0 + }, + maximum: { + type: "number" + }, + exclusiveMaximum: { + type: "number" + }, + minimum: { + type: "number" + }, + exclusiveMinimum: { + type: "number" + }, + maxLength: { $ref: "#/definitions/nonNegativeInteger" }, + minLength: { $ref: "#/definitions/nonNegativeIntegerDefault0" }, + pattern: { + type: "string", + format: "regex" + }, + additionalItems: { $ref: "#" }, + items: { + anyOf: [{ $ref: "#" }, { $ref: "#/definitions/schemaArray" }], + default: true + }, + maxItems: { $ref: "#/definitions/nonNegativeInteger" }, + minItems: { $ref: "#/definitions/nonNegativeIntegerDefault0" }, + uniqueItems: { + type: "boolean", + default: false + }, + contains: { $ref: "#" }, + maxProperties: { $ref: "#/definitions/nonNegativeInteger" }, + minProperties: { $ref: "#/definitions/nonNegativeIntegerDefault0" }, + required: { $ref: "#/definitions/stringArray" }, + additionalProperties: { $ref: "#" }, + definitions: { + type: "object", + additionalProperties: { $ref: "#" }, + default: {} + }, + properties: { + type: "object", + additionalProperties: { $ref: "#" }, + default: {} + }, + patternProperties: { + type: "object", + additionalProperties: { $ref: "#" }, + propertyNames: { format: "regex" }, + default: {} + }, + dependencies: { + type: "object", + additionalProperties: { + anyOf: [{ $ref: "#" }, { $ref: "#/definitions/stringArray" }] + } + }, + propertyNames: { $ref: "#" }, + const: true, + enum: { + type: "array", + items: true, + minItems: 1, + uniqueItems: true + }, + type: { + anyOf: [ + { $ref: "#/definitions/simpleTypes" }, + { + type: "array", + items: { $ref: "#/definitions/simpleTypes" }, + minItems: 1, + uniqueItems: true + } + ] + }, + format: { type: "string" }, + contentMediaType: { type: "string" }, + contentEncoding: { type: "string" }, + if: { $ref: "#" }, + then: { $ref: "#" }, + else: { $ref: "#" }, + allOf: { $ref: "#/definitions/schemaArray" }, + anyOf: { $ref: "#/definitions/schemaArray" }, + oneOf: { $ref: "#/definitions/schemaArray" }, + not: { $ref: "#" } + }, + default: true + }; + } +}); + +// node_modules/ajv-formats/node_modules/ajv/dist/ajv.js +var require_ajv2 = __commonJS({ + "node_modules/ajv-formats/node_modules/ajv/dist/ajv.js"(exports, module) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.MissingRefError = exports.ValidationError = exports.CodeGen = exports.Name = exports.nil = exports.stringify = exports.str = exports._ = exports.KeywordCxt = exports.Ajv = void 0; + var core_1 = require_core3(); + var draft7_1 = require_draft72(); + var discriminator_1 = require_discriminator2(); + var draft7MetaSchema = require_json_schema_draft_072(); + var META_SUPPORT_DATA = ["/properties"]; + var META_SCHEMA_ID = "http://json-schema.org/draft-07/schema"; + var Ajv2 = class extends core_1.default { + _addVocabularies() { + super._addVocabularies(); + draft7_1.default.forEach((v) => this.addVocabulary(v)); + if (this.opts.discriminator) + this.addKeyword(discriminator_1.default); + } + _addDefaultMetaSchema() { + super._addDefaultMetaSchema(); + if (!this.opts.meta) + return; + const metaSchema = this.opts.$data ? this.$dataMetaSchema(draft7MetaSchema, META_SUPPORT_DATA) : draft7MetaSchema; + this.addMetaSchema(metaSchema, META_SCHEMA_ID, false); + this.refs["http://json-schema.org/schema"] = META_SCHEMA_ID; + } + defaultMeta() { + return this.opts.defaultMeta = super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : void 0); + } + }; + exports.Ajv = Ajv2; + module.exports = exports = Ajv2; + module.exports.Ajv = Ajv2; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.default = Ajv2; + var validate_1 = require_validate2(); + Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function() { + return validate_1.KeywordCxt; + } }); + var codegen_1 = require_codegen2(); + Object.defineProperty(exports, "_", { enumerable: true, get: function() { + return codegen_1._; + } }); + Object.defineProperty(exports, "str", { enumerable: true, get: function() { + return codegen_1.str; + } }); + Object.defineProperty(exports, "stringify", { enumerable: true, get: function() { + return codegen_1.stringify; + } }); + Object.defineProperty(exports, "nil", { enumerable: true, get: function() { + return codegen_1.nil; + } }); + Object.defineProperty(exports, "Name", { enumerable: true, get: function() { + return codegen_1.Name; + } }); + Object.defineProperty(exports, "CodeGen", { enumerable: true, get: function() { + return codegen_1.CodeGen; + } }); + var validation_error_1 = require_validation_error2(); + Object.defineProperty(exports, "ValidationError", { enumerable: true, get: function() { + return validation_error_1.default; + } }); + var ref_error_1 = require_ref_error2(); + Object.defineProperty(exports, "MissingRefError", { enumerable: true, get: function() { + return ref_error_1.default; + } }); + } +}); + +// node_modules/ajv-formats/dist/limit.js +var require_limit = __commonJS({ + "node_modules/ajv-formats/dist/limit.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.formatLimitDefinition = void 0; + var ajv_1 = require_ajv2(); + var codegen_1 = require_codegen2(); + var ops = codegen_1.operators; + var KWDs = { + formatMaximum: { okStr: "<=", ok: ops.LTE, fail: ops.GT }, + formatMinimum: { okStr: ">=", ok: ops.GTE, fail: ops.LT }, + formatExclusiveMaximum: { okStr: "<", ok: ops.LT, fail: ops.GTE }, + formatExclusiveMinimum: { okStr: ">", ok: ops.GT, fail: ops.LTE } + }; + var error2 = { + message: ({ keyword, schemaCode }) => (0, codegen_1.str)`should be ${KWDs[keyword].okStr} ${schemaCode}`, + params: ({ keyword, schemaCode }) => (0, codegen_1._)`{comparison: ${KWDs[keyword].okStr}, limit: ${schemaCode}}` + }; + exports.formatLimitDefinition = { + keyword: Object.keys(KWDs), + type: "string", + schemaType: "string", + $data: true, + error: error2, + code(cxt) { + const { gen, data, schemaCode, keyword, it: it2 } = cxt; + const { opts, self } = it2; + if (!opts.validateFormats) + return; + const fCxt = new ajv_1.KeywordCxt(it2, self.RULES.all.format.definition, "format"); + if (fCxt.$data) + validate$DataFormat(); + else + validateFormat(); + function validate$DataFormat() { + const fmts = gen.scopeValue("formats", { + ref: self.formats, + code: opts.code.formats + }); + const fmt = gen.const("fmt", (0, codegen_1._)`${fmts}[${fCxt.schemaCode}]`); + cxt.fail$data((0, codegen_1.or)((0, codegen_1._)`typeof ${fmt} != "object"`, (0, codegen_1._)`${fmt} instanceof RegExp`, (0, codegen_1._)`typeof ${fmt}.compare != "function"`, compareCode(fmt))); + } + function validateFormat() { + const format = fCxt.schema; + const fmtDef = self.formats[format]; + if (!fmtDef || fmtDef === true) + return; + if (typeof fmtDef != "object" || fmtDef instanceof RegExp || typeof fmtDef.compare != "function") { + throw new Error(`"${keyword}": format "${format}" does not define "compare" function`); + } + const fmt = gen.scopeValue("formats", { + key: format, + ref: fmtDef, + code: opts.code.formats ? (0, codegen_1._)`${opts.code.formats}${(0, codegen_1.getProperty)(format)}` : void 0 + }); + cxt.fail$data(compareCode(fmt)); + } + function compareCode(fmt) { + return (0, codegen_1._)`${fmt}.compare(${data}, ${schemaCode}) ${KWDs[keyword].fail} 0`; + } + }, + dependencies: ["format"] + }; + var formatLimitPlugin = (ajv) => { + ajv.addKeyword(exports.formatLimitDefinition); + return ajv; + }; + exports.default = formatLimitPlugin; + } +}); + +// node_modules/ajv-formats/dist/index.js +var require_dist = __commonJS({ + "node_modules/ajv-formats/dist/index.js"(exports, module) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var formats_1 = require_formats(); + var limit_1 = require_limit(); + var codegen_1 = require_codegen2(); + var fullName = new codegen_1.Name("fullFormats"); + var fastName = new codegen_1.Name("fastFormats"); + var formatsPlugin = (ajv, opts = { keywords: true }) => { + if (Array.isArray(opts)) { + addFormats(ajv, opts, formats_1.fullFormats, fullName); + return ajv; + } + const [formats, exportName] = opts.mode === "fast" ? [formats_1.fastFormats, fastName] : [formats_1.fullFormats, fullName]; + const list = opts.formats || formats_1.formatNames; + addFormats(ajv, list, formats, exportName); + if (opts.keywords) + (0, limit_1.default)(ajv); + return ajv; + }; + formatsPlugin.get = (name, mode = "full") => { + const formats = mode === "fast" ? formats_1.fastFormats : formats_1.fullFormats; + const f2 = formats[name]; + if (!f2) + throw new Error(`Unknown format "${name}"`); + return f2; + }; + function addFormats(ajv, list, fs3, exportName) { + var _a; + var _b; + (_a = (_b = ajv.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`; + for (const f2 of list) + ajv.addFormat(f2, fs3[f2]); + } + module.exports = exports = formatsPlugin; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.default = formatsPlugin; + } +}); + +// node_modules/isexe/windows.js +var require_windows = __commonJS({ + "node_modules/isexe/windows.js"(exports, module) { + module.exports = isexe; + isexe.sync = sync; + var fs3 = __require("fs"); + function checkPathExt(path3, options) { + var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT; + if (!pathext) { + return true; + } + pathext = pathext.split(";"); + if (pathext.indexOf("") !== -1) { + return true; + } + for (var i = 0; i < pathext.length; i++) { + var p2 = pathext[i].toLowerCase(); + if (p2 && path3.substr(-p2.length).toLowerCase() === p2) { + return true; + } + } + return false; + } + function checkStat(stat, path3, options) { + if (!stat.isSymbolicLink() && !stat.isFile()) { + return false; + } + return checkPathExt(path3, options); + } + function isexe(path3, options, cb) { + fs3.stat(path3, function(er2, stat) { + cb(er2, er2 ? false : checkStat(stat, path3, options)); + }); + } + function sync(path3, options) { + return checkStat(fs3.statSync(path3), path3, options); + } + } +}); + +// node_modules/isexe/mode.js +var require_mode = __commonJS({ + "node_modules/isexe/mode.js"(exports, module) { + module.exports = isexe; + isexe.sync = sync; + var fs3 = __require("fs"); + function isexe(path3, options, cb) { + fs3.stat(path3, function(er2, stat) { + cb(er2, er2 ? false : checkStat(stat, options)); + }); + } + function sync(path3, options) { + return checkStat(fs3.statSync(path3), options); + } + function checkStat(stat, options) { + return stat.isFile() && checkMode(stat, options); + } + function checkMode(stat, options) { + var mod = stat.mode; + var uid = stat.uid; + var gid = stat.gid; + var myUid = options.uid !== void 0 ? options.uid : process.getuid && process.getuid(); + var myGid = options.gid !== void 0 ? options.gid : process.getgid && process.getgid(); + var u = parseInt("100", 8); + var g2 = parseInt("010", 8); + var o = parseInt("001", 8); + var ug2 = u | g2; + var ret = mod & o || mod & g2 && gid === myGid || mod & u && uid === myUid || mod & ug2 && myUid === 0; + return ret; + } + } +}); + +// node_modules/isexe/index.js +var require_isexe = __commonJS({ + "node_modules/isexe/index.js"(exports, module) { + var fs3 = __require("fs"); + var core; + if (process.platform === "win32" || global.TESTING_WINDOWS) { + core = require_windows(); + } else { + core = require_mode(); + } + module.exports = isexe; + isexe.sync = sync; + function isexe(path3, options, cb) { + if (typeof options === "function") { + cb = options; + options = {}; + } + if (!cb) { + if (typeof Promise !== "function") { + throw new TypeError("callback not provided"); + } + return new Promise(function(resolve, reject) { + isexe(path3, options || {}, function(er2, is) { + if (er2) { + reject(er2); + } else { + resolve(is); + } + }); + }); + } + core(path3, options || {}, function(er2, is) { + if (er2) { + if (er2.code === "EACCES" || options && options.ignoreErrors) { + er2 = null; + is = false; + } + } + cb(er2, is); + }); + } + function sync(path3, options) { + try { + return core.sync(path3, options || {}); + } catch (er2) { + if (options && options.ignoreErrors || er2.code === "EACCES") { + return false; + } else { + throw er2; + } + } + } + } +}); + +// node_modules/which/which.js +var require_which = __commonJS({ + "node_modules/which/which.js"(exports, module) { + var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys"; + var path3 = __require("path"); + var COLON = isWindows ? ";" : ":"; + var isexe = require_isexe(); + var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" }); + var getPathInfo = (cmd, opt) => { + const colon = opt.colon || COLON; + const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [""] : [ + // windows always checks the cwd first + ...isWindows ? [process.cwd()] : [], + ...(opt.path || process.env.PATH || /* istanbul ignore next: very unusual */ + "").split(colon) + ]; + const pathExtExe = isWindows ? opt.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : ""; + const pathExt = isWindows ? pathExtExe.split(colon) : [""]; + if (isWindows) { + if (cmd.indexOf(".") !== -1 && pathExt[0] !== "") + pathExt.unshift(""); + } + return { + pathEnv, + pathExt, + pathExtExe + }; + }; + var which = (cmd, opt, cb) => { + if (typeof opt === "function") { + cb = opt; + opt = {}; + } + if (!opt) + opt = {}; + const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt); + const found = []; + const step = (i) => new Promise((resolve, reject) => { + if (i === pathEnv.length) + return opt.all && found.length ? resolve(found) : reject(getNotFoundError(cmd)); + const ppRaw = pathEnv[i]; + const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw; + const pCmd = path3.join(pathPart, cmd); + const p2 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd; + resolve(subStep(p2, i, 0)); + }); + const subStep = (p2, i, ii2) => new Promise((resolve, reject) => { + if (ii2 === pathExt.length) + return resolve(step(i + 1)); + const ext = pathExt[ii2]; + isexe(p2 + ext, { pathExt: pathExtExe }, (er2, is) => { + if (!er2 && is) { + if (opt.all) + found.push(p2 + ext); + else + return resolve(p2 + ext); + } + return resolve(subStep(p2, i, ii2 + 1)); + }); + }); + return cb ? step(0).then((res) => cb(null, res), cb) : step(0); + }; + var whichSync = (cmd, opt) => { + opt = opt || {}; + const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt); + const found = []; + for (let i = 0; i < pathEnv.length; i++) { + const ppRaw = pathEnv[i]; + const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw; + const pCmd = path3.join(pathPart, cmd); + const p2 = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd; + for (let j2 = 0; j2 < pathExt.length; j2++) { + const cur = p2 + pathExt[j2]; + try { + const is = isexe.sync(cur, { pathExt: pathExtExe }); + if (is) { + if (opt.all) + found.push(cur); + else + return cur; + } + } catch (ex) { + } + } + } + if (opt.all && found.length) + return found; + if (opt.nothrow) + return null; + throw getNotFoundError(cmd); + }; + module.exports = which; + which.sync = whichSync; + } +}); + +// node_modules/path-key/index.js +var require_path_key = __commonJS({ + "node_modules/path-key/index.js"(exports, module) { + "use strict"; + var pathKey = (options = {}) => { + const environment = options.env || process.env; + const platform = options.platform || process.platform; + if (platform !== "win32") { + return "PATH"; + } + return Object.keys(environment).reverse().find((key) => key.toUpperCase() === "PATH") || "Path"; + }; + module.exports = pathKey; + module.exports.default = pathKey; + } +}); + +// node_modules/cross-spawn/lib/util/resolveCommand.js +var require_resolveCommand = __commonJS({ + "node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module) { + "use strict"; + var path3 = __require("path"); + var which = require_which(); + var getPathKey = require_path_key(); + function resolveCommandAttempt(parsed, withoutPathExt) { + const env = parsed.options.env || process.env; + const cwd = process.cwd(); + const hasCustomCwd = parsed.options.cwd != null; + const shouldSwitchCwd = hasCustomCwd && process.chdir !== void 0 && !process.chdir.disabled; + if (shouldSwitchCwd) { + try { + process.chdir(parsed.options.cwd); + } catch (err) { + } + } + let resolved; + try { + resolved = which.sync(parsed.command, { + path: env[getPathKey({ env })], + pathExt: withoutPathExt ? path3.delimiter : void 0 + }); + } catch (e) { + } finally { + if (shouldSwitchCwd) { + process.chdir(cwd); + } + } + if (resolved) { + resolved = path3.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved); + } + return resolved; + } + function resolveCommand(parsed) { + return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true); + } + module.exports = resolveCommand; + } +}); + +// node_modules/cross-spawn/lib/util/escape.js +var require_escape = __commonJS({ + "node_modules/cross-spawn/lib/util/escape.js"(exports, module) { + "use strict"; + var metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g; + function escapeCommand(arg) { + arg = arg.replace(metaCharsRegExp, "^$1"); + return arg; + } + function escapeArgument(arg, doubleEscapeMetaChars) { + arg = `${arg}`; + arg = arg.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"'); + arg = arg.replace(/(?=(\\+?)?)\1$/, "$1$1"); + arg = `"${arg}"`; + arg = arg.replace(metaCharsRegExp, "^$1"); + if (doubleEscapeMetaChars) { + arg = arg.replace(metaCharsRegExp, "^$1"); + } + return arg; + } + module.exports.command = escapeCommand; + module.exports.argument = escapeArgument; + } +}); + +// node_modules/shebang-regex/index.js +var require_shebang_regex = __commonJS({ + "node_modules/shebang-regex/index.js"(exports, module) { + "use strict"; + module.exports = /^#!(.*)/; + } +}); + +// node_modules/shebang-command/index.js +var require_shebang_command = __commonJS({ + "node_modules/shebang-command/index.js"(exports, module) { + "use strict"; + var shebangRegex = require_shebang_regex(); + module.exports = (string3 = "") => { + const match = string3.match(shebangRegex); + if (!match) { + return null; + } + const [path3, argument] = match[0].replace(/#! ?/, "").split(" "); + const binary = path3.split("/").pop(); + if (binary === "env") { + return argument; + } + return argument ? `${binary} ${argument}` : binary; + }; + } +}); + +// node_modules/cross-spawn/lib/util/readShebang.js +var require_readShebang = __commonJS({ + "node_modules/cross-spawn/lib/util/readShebang.js"(exports, module) { + "use strict"; + var fs3 = __require("fs"); + var shebangCommand = require_shebang_command(); + function readShebang(command) { + const size = 150; + const buffer = Buffer.alloc(size); + let fd; + try { + fd = fs3.openSync(command, "r"); + fs3.readSync(fd, buffer, 0, size, 0); + fs3.closeSync(fd); + } catch (e) { + } + return shebangCommand(buffer.toString()); + } + module.exports = readShebang; + } +}); + +// node_modules/cross-spawn/lib/parse.js +var require_parse = __commonJS({ + "node_modules/cross-spawn/lib/parse.js"(exports, module) { + "use strict"; + var path3 = __require("path"); + var resolveCommand = require_resolveCommand(); + var escape2 = require_escape(); + var readShebang = require_readShebang(); + var isWin = process.platform === "win32"; + var isExecutableRegExp = /\.(?:com|exe)$/i; + var isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; + function detectShebang(parsed) { + parsed.file = resolveCommand(parsed); + const shebang = parsed.file && readShebang(parsed.file); + if (shebang) { + parsed.args.unshift(parsed.file); + parsed.command = shebang; + return resolveCommand(parsed); + } + return parsed.file; + } + function parseNonShell(parsed) { + if (!isWin) { + return parsed; + } + const commandFile = detectShebang(parsed); + const needsShell = !isExecutableRegExp.test(commandFile); + if (parsed.options.forceShell || needsShell) { + const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile); + parsed.command = path3.normalize(parsed.command); + parsed.command = escape2.command(parsed.command); + parsed.args = parsed.args.map((arg) => escape2.argument(arg, needsDoubleEscapeMetaChars)); + const shellCommand = [parsed.command].concat(parsed.args).join(" "); + parsed.args = ["/d", "/s", "/c", `"${shellCommand}"`]; + parsed.command = process.env.comspec || "cmd.exe"; + parsed.options.windowsVerbatimArguments = true; + } + return parsed; + } + function parse3(command, args, options) { + if (args && !Array.isArray(args)) { + options = args; + args = null; + } + args = args ? args.slice(0) : []; + options = Object.assign({}, options); + const parsed = { + command, + args, + options, + file: void 0, + original: { + command, + args + } + }; + return options.shell ? parsed : parseNonShell(parsed); + } + module.exports = parse3; + } +}); + +// node_modules/cross-spawn/lib/enoent.js +var require_enoent = __commonJS({ + "node_modules/cross-spawn/lib/enoent.js"(exports, module) { + "use strict"; + var isWin = process.platform === "win32"; + function notFoundError(original, syscall) { + return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), { + code: "ENOENT", + errno: "ENOENT", + syscall: `${syscall} ${original.command}`, + path: original.command, + spawnargs: original.args + }); + } + function hookChildProcess(cp, parsed) { + if (!isWin) { + return; + } + const originalEmit = cp.emit; + cp.emit = function(name, arg1) { + if (name === "exit") { + const err = verifyENOENT(arg1, parsed); + if (err) { + return originalEmit.call(cp, "error", err); + } + } + return originalEmit.apply(cp, arguments); + }; + } + function verifyENOENT(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, "spawn"); + } + return null; + } + function verifyENOENTSync(status, parsed) { + if (isWin && status === 1 && !parsed.file) { + return notFoundError(parsed.original, "spawnSync"); + } + return null; + } + module.exports = { + hookChildProcess, + verifyENOENT, + verifyENOENTSync, + notFoundError + }; + } +}); + +// node_modules/cross-spawn/index.js +var require_cross_spawn = __commonJS({ + "node_modules/cross-spawn/index.js"(exports, module) { + "use strict"; + var cp = __require("child_process"); + var parse3 = require_parse(); + var enoent = require_enoent(); + function spawn3(command, args, options) { + const parsed = parse3(command, args, options); + const spawned = cp.spawn(parsed.command, parsed.args, parsed.options); + enoent.hookChildProcess(spawned, parsed); + return spawned; + } + function spawnSync(command, args, options) { + const parsed = parse3(command, args, options); + const result = cp.spawnSync(parsed.command, parsed.args, parsed.options); + result.error = result.error || enoent.verifyENOENTSync(result.status, parsed); + return result; + } + module.exports = spawn3; + module.exports.spawn = spawn3; + module.exports.sync = spawnSync; + module.exports._parse = parse3; + module.exports._enoent = enoent; + } +}); + +// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js +import process2 from "node:process"; + +// node_modules/zod/v4/core/core.js +var NEVER = Object.freeze({ + status: "aborted" +}); +// @__NO_SIDE_EFFECTS__ +function $constructor(name, initializer3, params) { + function init(inst, def) { + var _a; + Object.defineProperty(inst, "_zod", { + value: inst._zod ?? {}, + enumerable: false + }); + (_a = inst._zod).traits ?? (_a.traits = /* @__PURE__ */ new Set()); + inst._zod.traits.add(name); + initializer3(inst, def); + for (const k2 in _.prototype) { + if (!(k2 in inst)) + Object.defineProperty(inst, k2, { value: _.prototype[k2].bind(inst) }); + } + inst._zod.constr = _; + inst._zod.def = def; + } + const Parent = params?.Parent ?? Object; + class Definition extends Parent { + } + Object.defineProperty(Definition, "name", { value: name }); + function _(def) { + var _a; + const inst = params?.Parent ? new Definition() : this; + init(inst, def); + (_a = inst._zod).deferred ?? (_a.deferred = []); + for (const fn2 of inst._zod.deferred) { + fn2(); + } + return inst; + } + Object.defineProperty(_, "init", { value: init }); + Object.defineProperty(_, Symbol.hasInstance, { + value: (inst) => { + if (params?.Parent && inst instanceof params.Parent) + return true; + return inst?._zod?.traits?.has(name); + } + }); + Object.defineProperty(_, "name", { value: name }); + return _; +} +var $ZodAsyncError = class extends Error { + constructor() { + super(`Encountered Promise during synchronous parse. Use .parseAsync() instead.`); + } +}; +var globalConfig = {}; +function config(newConfig) { + if (newConfig) + Object.assign(globalConfig, newConfig); + return globalConfig; +} + +// node_modules/zod/v4/core/util.js +var util_exports = {}; +__export(util_exports, { + BIGINT_FORMAT_RANGES: () => BIGINT_FORMAT_RANGES, + Class: () => Class, + NUMBER_FORMAT_RANGES: () => NUMBER_FORMAT_RANGES, + aborted: () => aborted, + allowsEval: () => allowsEval, + assert: () => assert, + assertEqual: () => assertEqual, + assertIs: () => assertIs, + assertNever: () => assertNever, + assertNotEqual: () => assertNotEqual, + assignProp: () => assignProp, + cached: () => cached, + captureStackTrace: () => captureStackTrace, + cleanEnum: () => cleanEnum, + cleanRegex: () => cleanRegex, + clone: () => clone, + createTransparentProxy: () => createTransparentProxy, + defineLazy: () => defineLazy, + esc: () => esc, + escapeRegex: () => escapeRegex, + extend: () => extend, + finalizeIssue: () => finalizeIssue, + floatSafeRemainder: () => floatSafeRemainder, + getElementAtPath: () => getElementAtPath, + getEnumValues: () => getEnumValues, + getLengthableOrigin: () => getLengthableOrigin, + getParsedType: () => getParsedType, + getSizableOrigin: () => getSizableOrigin, + isObject: () => isObject, + isPlainObject: () => isPlainObject, + issue: () => issue, + joinValues: () => joinValues, + jsonStringifyReplacer: () => jsonStringifyReplacer, + merge: () => merge, + normalizeParams: () => normalizeParams, + nullish: () => nullish, + numKeys: () => numKeys, + omit: () => omit, + optionalKeys: () => optionalKeys, + partial: () => partial, + pick: () => pick, + prefixIssues: () => prefixIssues, + primitiveTypes: () => primitiveTypes, + promiseAllObject: () => promiseAllObject, + propertyKeyTypes: () => propertyKeyTypes, + randomString: () => randomString, + required: () => required, + stringifyPrimitive: () => stringifyPrimitive, + unwrapMessage: () => unwrapMessage +}); +function assertEqual(val) { + return val; +} +function assertNotEqual(val) { + return val; +} +function assertIs(_arg) { +} +function assertNever(_x) { + throw new Error(); +} +function assert(_) { +} +function getEnumValues(entries) { + const numericValues = Object.values(entries).filter((v) => typeof v === "number"); + const values = Object.entries(entries).filter(([k2, _]) => numericValues.indexOf(+k2) === -1).map(([_, v]) => v); + return values; +} +function joinValues(array2, separator = "|") { + return array2.map((val) => stringifyPrimitive(val)).join(separator); +} +function jsonStringifyReplacer(_, value) { + if (typeof value === "bigint") + return value.toString(); + return value; +} +function cached(getter) { + const set = false; + return { + get value() { + if (!set) { + const value = getter(); + Object.defineProperty(this, "value", { value }); + return value; + } + throw new Error("cached value already set"); + } + }; +} +function nullish(input) { + return input === null || input === void 0; +} +function cleanRegex(source) { + const start = source.startsWith("^") ? 1 : 0; + const end = source.endsWith("$") ? source.length - 1 : source.length; + return source.slice(start, end); +} +function floatSafeRemainder(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = Number.parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", "")); + return valInt % stepInt / 10 ** decCount; +} +function defineLazy(object3, key, getter) { + const set = false; + Object.defineProperty(object3, key, { + get() { + if (!set) { + const value = getter(); + object3[key] = value; + return value; + } + throw new Error("cached value already set"); + }, + set(v) { + Object.defineProperty(object3, key, { + value: v + // configurable: true, + }); + }, + configurable: true + }); +} +function assignProp(target, prop, value) { + Object.defineProperty(target, prop, { + value, + writable: true, + enumerable: true, + configurable: true + }); +} +function getElementAtPath(obj, path3) { + if (!path3) + return obj; + return path3.reduce((acc, key) => acc?.[key], obj); +} +function promiseAllObject(promisesObj) { + const keys = Object.keys(promisesObj); + const promises = keys.map((key) => promisesObj[key]); + return Promise.all(promises).then((results) => { + const resolvedObj = {}; + for (let i = 0; i < keys.length; i++) { + resolvedObj[keys[i]] = results[i]; + } + return resolvedObj; + }); +} +function randomString(length = 10) { + const chars = "abcdefghijklmnopqrstuvwxyz"; + let str = ""; + for (let i = 0; i < length; i++) { + str += chars[Math.floor(Math.random() * chars.length)]; + } + return str; +} +function esc(str) { + return JSON.stringify(str); +} +var captureStackTrace = Error.captureStackTrace ? Error.captureStackTrace : (..._args) => { +}; +function isObject(data) { + return typeof data === "object" && data !== null && !Array.isArray(data); +} +var allowsEval = cached(() => { + if (typeof navigator !== "undefined" && navigator?.userAgent?.includes("Cloudflare")) { + return false; + } + try { + const F = Function; + new F(""); + return true; + } catch (_) { + return false; + } +}); +function isPlainObject(o) { + if (isObject(o) === false) + return false; + const ctor = o.constructor; + if (ctor === void 0) + return true; + const prot = ctor.prototype; + if (isObject(prot) === false) + return false; + if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) { + return false; + } + return true; +} +function numKeys(data) { + let keyCount = 0; + for (const key in data) { + if (Object.prototype.hasOwnProperty.call(data, key)) { + keyCount++; + } + } + return keyCount; +} +var getParsedType = (data) => { + const t = typeof data; + switch (t) { + case "undefined": + return "undefined"; + case "string": + return "string"; + case "number": + return Number.isNaN(data) ? "nan" : "number"; + case "boolean": + return "boolean"; + case "function": + return "function"; + case "bigint": + return "bigint"; + case "symbol": + return "symbol"; + case "object": + if (Array.isArray(data)) { + return "array"; + } + if (data === null) { + return "null"; + } + if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") { + return "promise"; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return "map"; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return "set"; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return "date"; + } + if (typeof File !== "undefined" && data instanceof File) { + return "file"; + } + return "object"; + default: + throw new Error(`Unknown data type: ${t}`); + } +}; +var propertyKeyTypes = /* @__PURE__ */ new Set(["string", "number", "symbol"]); +var primitiveTypes = /* @__PURE__ */ new Set(["string", "number", "bigint", "boolean", "symbol", "undefined"]); +function escapeRegex(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} +function clone(inst, def, params) { + const cl2 = new inst._zod.constr(def ?? inst._zod.def); + if (!def || params?.parent) + cl2._zod.parent = inst; + return cl2; +} +function normalizeParams(_params) { + const params = _params; + if (!params) + return {}; + if (typeof params === "string") + return { error: () => params }; + if (params?.message !== void 0) { + if (params?.error !== void 0) + throw new Error("Cannot specify both `message` and `error` params"); + params.error = params.message; + } + delete params.message; + if (typeof params.error === "string") + return { ...params, error: () => params.error }; + return params; +} +function createTransparentProxy(getter) { + let target; + return new Proxy({}, { + get(_, prop, receiver) { + target ?? (target = getter()); + return Reflect.get(target, prop, receiver); + }, + set(_, prop, value, receiver) { + target ?? (target = getter()); + return Reflect.set(target, prop, value, receiver); + }, + has(_, prop) { + target ?? (target = getter()); + return Reflect.has(target, prop); + }, + deleteProperty(_, prop) { + target ?? (target = getter()); + return Reflect.deleteProperty(target, prop); + }, + ownKeys(_) { + target ?? (target = getter()); + return Reflect.ownKeys(target); + }, + getOwnPropertyDescriptor(_, prop) { + target ?? (target = getter()); + return Reflect.getOwnPropertyDescriptor(target, prop); + }, + defineProperty(_, prop, descriptor) { + target ?? (target = getter()); + return Reflect.defineProperty(target, prop, descriptor); + } + }); +} +function stringifyPrimitive(value) { + if (typeof value === "bigint") + return value.toString() + "n"; + if (typeof value === "string") + return `"${value}"`; + return `${value}`; +} +function optionalKeys(shape) { + return Object.keys(shape).filter((k2) => { + return shape[k2]._zod.optin === "optional" && shape[k2]._zod.optout === "optional"; + }); +} +var NUMBER_FORMAT_RANGES = { + safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], + int32: [-2147483648, 2147483647], + uint32: [0, 4294967295], + float32: [-34028234663852886e22, 34028234663852886e22], + float64: [-Number.MAX_VALUE, Number.MAX_VALUE] +}; +var BIGINT_FORMAT_RANGES = { + int64: [/* @__PURE__ */ BigInt("-9223372036854775808"), /* @__PURE__ */ BigInt("9223372036854775807")], + uint64: [/* @__PURE__ */ BigInt(0), /* @__PURE__ */ BigInt("18446744073709551615")] +}; +function pick(schema, mask) { + const newShape = {}; + const currDef = schema._zod.def; + for (const key in mask) { + if (!(key in currDef.shape)) { + throw new Error(`Unrecognized key: "${key}"`); + } + if (!mask[key]) + continue; + newShape[key] = currDef.shape[key]; + } + return clone(schema, { + ...schema._zod.def, + shape: newShape, + checks: [] + }); +} +function omit(schema, mask) { + const newShape = { ...schema._zod.def.shape }; + const currDef = schema._zod.def; + for (const key in mask) { + if (!(key in currDef.shape)) { + throw new Error(`Unrecognized key: "${key}"`); + } + if (!mask[key]) + continue; + delete newShape[key]; + } + return clone(schema, { + ...schema._zod.def, + shape: newShape, + checks: [] + }); +} +function extend(schema, shape) { + if (!isPlainObject(shape)) { + throw new Error("Invalid input to extend: expected a plain object"); + } + const def = { + ...schema._zod.def, + get shape() { + const _shape = { ...schema._zod.def.shape, ...shape }; + assignProp(this, "shape", _shape); + return _shape; + }, + checks: [] + // delete existing checks + }; + return clone(schema, def); +} +function merge(a2, b2) { + return clone(a2, { + ...a2._zod.def, + get shape() { + const _shape = { ...a2._zod.def.shape, ...b2._zod.def.shape }; + assignProp(this, "shape", _shape); + return _shape; + }, + catchall: b2._zod.def.catchall, + checks: [] + // delete existing checks + }); +} +function partial(Class2, schema, mask) { + const oldShape = schema._zod.def.shape; + const shape = { ...oldShape }; + if (mask) { + for (const key in mask) { + if (!(key in oldShape)) { + throw new Error(`Unrecognized key: "${key}"`); + } + if (!mask[key]) + continue; + shape[key] = Class2 ? new Class2({ + type: "optional", + innerType: oldShape[key] + }) : oldShape[key]; + } + } else { + for (const key in oldShape) { + shape[key] = Class2 ? new Class2({ + type: "optional", + innerType: oldShape[key] + }) : oldShape[key]; + } + } + return clone(schema, { + ...schema._zod.def, + shape, + checks: [] + }); +} +function required(Class2, schema, mask) { + const oldShape = schema._zod.def.shape; + const shape = { ...oldShape }; + if (mask) { + for (const key in mask) { + if (!(key in shape)) { + throw new Error(`Unrecognized key: "${key}"`); + } + if (!mask[key]) + continue; + shape[key] = new Class2({ + type: "nonoptional", + innerType: oldShape[key] + }); + } + } else { + for (const key in oldShape) { + shape[key] = new Class2({ + type: "nonoptional", + innerType: oldShape[key] + }); + } + } + return clone(schema, { + ...schema._zod.def, + shape, + // optional: [], + checks: [] + }); +} +function aborted(x2, startIndex = 0) { + for (let i = startIndex; i < x2.issues.length; i++) { + if (x2.issues[i]?.continue !== true) + return true; + } + return false; +} +function prefixIssues(path3, issues) { + return issues.map((iss) => { + var _a; + (_a = iss).path ?? (_a.path = []); + iss.path.unshift(path3); + return iss; + }); +} +function unwrapMessage(message) { + return typeof message === "string" ? message : message?.message; +} +function finalizeIssue(iss, ctx, config2) { + const full = { ...iss, path: iss.path ?? [] }; + if (!iss.message) { + const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config2.customError?.(iss)) ?? unwrapMessage(config2.localeError?.(iss)) ?? "Invalid input"; + full.message = message; + } + delete full.inst; + delete full.continue; + if (!ctx?.reportInput) { + delete full.input; + } + return full; +} +function getSizableOrigin(input) { + if (input instanceof Set) + return "set"; + if (input instanceof Map) + return "map"; + if (input instanceof File) + return "file"; + return "unknown"; +} +function getLengthableOrigin(input) { + if (Array.isArray(input)) + return "array"; + if (typeof input === "string") + return "string"; + return "unknown"; +} +function issue(...args) { + const [iss, input, inst] = args; + if (typeof iss === "string") { + return { + message: iss, + code: "custom", + input, + inst + }; + } + return { ...iss }; +} +function cleanEnum(obj) { + return Object.entries(obj).filter(([k2, _]) => { + return Number.isNaN(Number.parseInt(k2, 10)); + }).map((el2) => el2[1]); +} +var Class = class { + constructor(..._args) { + } +}; + +// node_modules/zod/v4/core/errors.js +var initializer = (inst, def) => { + inst.name = "$ZodError"; + Object.defineProperty(inst, "_zod", { + value: inst._zod, + enumerable: false + }); + Object.defineProperty(inst, "issues", { + value: def, + enumerable: false + }); + Object.defineProperty(inst, "message", { + get() { + return JSON.stringify(def, jsonStringifyReplacer, 2); + }, + enumerable: true + // configurable: false, + }); + Object.defineProperty(inst, "toString", { + value: () => inst.message, + enumerable: false + }); +}; +var $ZodError = $constructor("$ZodError", initializer); +var $ZodRealError = $constructor("$ZodError", initializer, { Parent: Error }); +function flattenError(error2, mapper = (issue2) => issue2.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of error2.issues) { + if (sub.path.length > 0) { + fieldErrors[sub.path[0]] = fieldErrors[sub.path[0]] || []; + fieldErrors[sub.path[0]].push(mapper(sub)); + } else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; +} +function formatError(error2, _mapper) { + const mapper = _mapper || function(issue2) { + return issue2.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error3) => { + for (const issue2 of error3.issues) { + if (issue2.code === "invalid_union" && issue2.errors.length) { + issue2.errors.map((issues) => processError({ issues })); + } else if (issue2.code === "invalid_key") { + processError({ issues: issue2.issues }); + } else if (issue2.code === "invalid_element") { + processError({ issues: issue2.issues }); + } else if (issue2.path.length === 0) { + fieldErrors._errors.push(mapper(issue2)); + } else { + let curr = fieldErrors; + let i = 0; + while (i < issue2.path.length) { + const el2 = issue2.path[i]; + const terminal = i === issue2.path.length - 1; + if (!terminal) { + curr[el2] = curr[el2] || { _errors: [] }; + } else { + curr[el2] = curr[el2] || { _errors: [] }; + curr[el2]._errors.push(mapper(issue2)); + } + curr = curr[el2]; + i++; + } + } + } + }; + processError(error2); + return fieldErrors; +} + +// node_modules/zod/v4/core/parse.js +var _parse = (_Err) => (schema, value, _ctx, _params) => { + const ctx = _ctx ? Object.assign(_ctx, { async: false }) : { async: false }; + const result = schema._zod.run({ value, issues: [] }, ctx); + if (result instanceof Promise) { + throw new $ZodAsyncError(); + } + if (result.issues.length) { + const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config()))); + captureStackTrace(e, _params?.callee); + throw e; + } + return result.value; +}; +var parse = /* @__PURE__ */ _parse($ZodRealError); +var _parseAsync = (_Err) => async (schema, value, _ctx, params) => { + const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true }; + let result = schema._zod.run({ value, issues: [] }, ctx); + if (result instanceof Promise) + result = await result; + if (result.issues.length) { + const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config()))); + captureStackTrace(e, params?.callee); + throw e; + } + return result.value; +}; +var parseAsync = /* @__PURE__ */ _parseAsync($ZodRealError); +var _safeParse = (_Err) => (schema, value, _ctx) => { + const ctx = _ctx ? { ..._ctx, async: false } : { async: false }; + const result = schema._zod.run({ value, issues: [] }, ctx); + if (result instanceof Promise) { + throw new $ZodAsyncError(); + } + return result.issues.length ? { + success: false, + error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config()))) + } : { success: true, data: result.value }; +}; +var safeParse = /* @__PURE__ */ _safeParse($ZodRealError); +var _safeParseAsync = (_Err) => async (schema, value, _ctx) => { + const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true }; + let result = schema._zod.run({ value, issues: [] }, ctx); + if (result instanceof Promise) + result = await result; + return result.issues.length ? { + success: false, + error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config()))) + } : { success: true, data: result.value }; +}; +var safeParseAsync = /* @__PURE__ */ _safeParseAsync($ZodRealError); + +// node_modules/zod/v4/core/regexes.js +var cuid = /^[cC][^\s-]{8,}$/; +var cuid2 = /^[0-9a-z]+$/; +var ulid = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/; +var xid = /^[0-9a-vA-V]{20}$/; +var ksuid = /^[A-Za-z0-9]{27}$/; +var nanoid = /^[a-zA-Z0-9_-]{21}$/; +var duration = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/; +var guid = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/; +var uuid = (version2) => { + if (!version2) + return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$/; + return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version2}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`); +}; +var email = /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/; +var _emoji = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`; +function emoji() { + return new RegExp(_emoji, "u"); +} +var ipv4 = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +var ipv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$/; +var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/; +var cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/; +var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/; +var base64url = /^[A-Za-z0-9_-]*$/; +var hostname = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+$/; +var e164 = /^\+(?:[0-9]){6,14}[0-9]$/; +var dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`; +var date = /* @__PURE__ */ new RegExp(`^${dateSource}$`); +function timeSource(args) { + const hhmm = `(?:[01]\\d|2[0-3]):[0-5]\\d`; + const regex = typeof args.precision === "number" ? args.precision === -1 ? `${hhmm}` : args.precision === 0 ? `${hhmm}:[0-5]\\d` : `${hhmm}:[0-5]\\d\\.\\d{${args.precision}}` : `${hhmm}(?::[0-5]\\d(?:\\.\\d+)?)?`; + return regex; +} +function time(args) { + return new RegExp(`^${timeSource(args)}$`); +} +function datetime(args) { + const time3 = timeSource({ precision: args.precision }); + const opts = ["Z"]; + if (args.local) + opts.push(""); + if (args.offset) + opts.push(`([+-]\\d{2}:\\d{2})`); + const timeRegex2 = `${time3}(?:${opts.join("|")})`; + return new RegExp(`^${dateSource}T(?:${timeRegex2})$`); +} +var string = (params) => { + const regex = params ? `[\\s\\S]{${params?.minimum ?? 0},${params?.maximum ?? ""}}` : `[\\s\\S]*`; + return new RegExp(`^${regex}$`); +}; +var integer = /^\d+$/; +var number = /^-?\d+(?:\.\d+)?/i; +var boolean = /true|false/i; +var _null = /null/i; +var lowercase = /^[^A-Z]*$/; +var uppercase = /^[^a-z]*$/; + +// node_modules/zod/v4/core/checks.js +var $ZodCheck = /* @__PURE__ */ $constructor("$ZodCheck", (inst, def) => { + var _a; + inst._zod ?? (inst._zod = {}); + inst._zod.def = def; + (_a = inst._zod).onattach ?? (_a.onattach = []); +}); +var numericOriginMap = { + number: "number", + bigint: "bigint", + object: "date" +}; +var $ZodCheckLessThan = /* @__PURE__ */ $constructor("$ZodCheckLessThan", (inst, def) => { + $ZodCheck.init(inst, def); + const origin = numericOriginMap[typeof def.value]; + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY; + if (def.value < curr) { + if (def.inclusive) + bag.maximum = def.value; + else + bag.exclusiveMaximum = def.value; + } + }); + inst._zod.check = (payload) => { + if (def.inclusive ? payload.value <= def.value : payload.value < def.value) { + return; + } + payload.issues.push({ + origin, + code: "too_big", + maximum: def.value, + input: payload.value, + inclusive: def.inclusive, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckGreaterThan = /* @__PURE__ */ $constructor("$ZodCheckGreaterThan", (inst, def) => { + $ZodCheck.init(inst, def); + const origin = numericOriginMap[typeof def.value]; + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY; + if (def.value > curr) { + if (def.inclusive) + bag.minimum = def.value; + else + bag.exclusiveMinimum = def.value; + } + }); + inst._zod.check = (payload) => { + if (def.inclusive ? payload.value >= def.value : payload.value > def.value) { + return; + } + payload.issues.push({ + origin, + code: "too_small", + minimum: def.value, + input: payload.value, + inclusive: def.inclusive, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckMultipleOf = /* @__PURE__ */ $constructor("$ZodCheckMultipleOf", (inst, def) => { + $ZodCheck.init(inst, def); + inst._zod.onattach.push((inst2) => { + var _a; + (_a = inst2._zod.bag).multipleOf ?? (_a.multipleOf = def.value); + }); + inst._zod.check = (payload) => { + if (typeof payload.value !== typeof def.value) + throw new Error("Cannot mix number and bigint in multiple_of check."); + const isMultiple = typeof payload.value === "bigint" ? payload.value % def.value === BigInt(0) : floatSafeRemainder(payload.value, def.value) === 0; + if (isMultiple) + return; + payload.issues.push({ + origin: typeof payload.value, + code: "not_multiple_of", + divisor: def.value, + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckNumberFormat = /* @__PURE__ */ $constructor("$ZodCheckNumberFormat", (inst, def) => { + $ZodCheck.init(inst, def); + def.format = def.format || "float64"; + const isInt = def.format?.includes("int"); + const origin = isInt ? "int" : "number"; + const [minimum, maximum] = NUMBER_FORMAT_RANGES[def.format]; + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.format = def.format; + bag.minimum = minimum; + bag.maximum = maximum; + if (isInt) + bag.pattern = integer; + }); + inst._zod.check = (payload) => { + const input = payload.value; + if (isInt) { + if (!Number.isInteger(input)) { + payload.issues.push({ + expected: origin, + format: def.format, + code: "invalid_type", + input, + inst + }); + return; + } + if (!Number.isSafeInteger(input)) { + if (input > 0) { + payload.issues.push({ + input, + code: "too_big", + maximum: Number.MAX_SAFE_INTEGER, + note: "Integers must be within the safe integer range.", + inst, + origin, + continue: !def.abort + }); + } else { + payload.issues.push({ + input, + code: "too_small", + minimum: Number.MIN_SAFE_INTEGER, + note: "Integers must be within the safe integer range.", + inst, + origin, + continue: !def.abort + }); + } + return; + } + } + if (input < minimum) { + payload.issues.push({ + origin: "number", + input, + code: "too_small", + minimum, + inclusive: true, + inst, + continue: !def.abort + }); + } + if (input > maximum) { + payload.issues.push({ + origin: "number", + input, + code: "too_big", + maximum, + inst + }); + } + }; +}); +var $ZodCheckMaxLength = /* @__PURE__ */ $constructor("$ZodCheckMaxLength", (inst, def) => { + var _a; + $ZodCheck.init(inst, def); + (_a = inst._zod.def).when ?? (_a.when = (payload) => { + const val = payload.value; + return !nullish(val) && val.length !== void 0; + }); + inst._zod.onattach.push((inst2) => { + const curr = inst2._zod.bag.maximum ?? Number.POSITIVE_INFINITY; + if (def.maximum < curr) + inst2._zod.bag.maximum = def.maximum; + }); + inst._zod.check = (payload) => { + const input = payload.value; + const length = input.length; + if (length <= def.maximum) + return; + const origin = getLengthableOrigin(input); + payload.issues.push({ + origin, + code: "too_big", + maximum: def.maximum, + inclusive: true, + input, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckMinLength = /* @__PURE__ */ $constructor("$ZodCheckMinLength", (inst, def) => { + var _a; + $ZodCheck.init(inst, def); + (_a = inst._zod.def).when ?? (_a.when = (payload) => { + const val = payload.value; + return !nullish(val) && val.length !== void 0; + }); + inst._zod.onattach.push((inst2) => { + const curr = inst2._zod.bag.minimum ?? Number.NEGATIVE_INFINITY; + if (def.minimum > curr) + inst2._zod.bag.minimum = def.minimum; + }); + inst._zod.check = (payload) => { + const input = payload.value; + const length = input.length; + if (length >= def.minimum) + return; + const origin = getLengthableOrigin(input); + payload.issues.push({ + origin, + code: "too_small", + minimum: def.minimum, + inclusive: true, + input, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckLengthEquals = /* @__PURE__ */ $constructor("$ZodCheckLengthEquals", (inst, def) => { + var _a; + $ZodCheck.init(inst, def); + (_a = inst._zod.def).when ?? (_a.when = (payload) => { + const val = payload.value; + return !nullish(val) && val.length !== void 0; + }); + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.minimum = def.length; + bag.maximum = def.length; + bag.length = def.length; + }); + inst._zod.check = (payload) => { + const input = payload.value; + const length = input.length; + if (length === def.length) + return; + const origin = getLengthableOrigin(input); + const tooBig = length > def.length; + payload.issues.push({ + origin, + ...tooBig ? { code: "too_big", maximum: def.length } : { code: "too_small", minimum: def.length }, + inclusive: true, + exact: true, + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckStringFormat = /* @__PURE__ */ $constructor("$ZodCheckStringFormat", (inst, def) => { + var _a, _b; + $ZodCheck.init(inst, def); + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.format = def.format; + if (def.pattern) { + bag.patterns ?? (bag.patterns = /* @__PURE__ */ new Set()); + bag.patterns.add(def.pattern); + } + }); + if (def.pattern) + (_a = inst._zod).check ?? (_a.check = (payload) => { + def.pattern.lastIndex = 0; + if (def.pattern.test(payload.value)) + return; + payload.issues.push({ + origin: "string", + code: "invalid_format", + format: def.format, + input: payload.value, + ...def.pattern ? { pattern: def.pattern.toString() } : {}, + inst, + continue: !def.abort + }); + }); + else + (_b = inst._zod).check ?? (_b.check = () => { + }); +}); +var $ZodCheckRegex = /* @__PURE__ */ $constructor("$ZodCheckRegex", (inst, def) => { + $ZodCheckStringFormat.init(inst, def); + inst._zod.check = (payload) => { + def.pattern.lastIndex = 0; + if (def.pattern.test(payload.value)) + return; + payload.issues.push({ + origin: "string", + code: "invalid_format", + format: "regex", + input: payload.value, + pattern: def.pattern.toString(), + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckLowerCase = /* @__PURE__ */ $constructor("$ZodCheckLowerCase", (inst, def) => { + def.pattern ?? (def.pattern = lowercase); + $ZodCheckStringFormat.init(inst, def); +}); +var $ZodCheckUpperCase = /* @__PURE__ */ $constructor("$ZodCheckUpperCase", (inst, def) => { + def.pattern ?? (def.pattern = uppercase); + $ZodCheckStringFormat.init(inst, def); +}); +var $ZodCheckIncludes = /* @__PURE__ */ $constructor("$ZodCheckIncludes", (inst, def) => { + $ZodCheck.init(inst, def); + const escapedRegex = escapeRegex(def.includes); + const pattern = new RegExp(typeof def.position === "number" ? `^.{${def.position}}${escapedRegex}` : escapedRegex); + def.pattern = pattern; + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.patterns ?? (bag.patterns = /* @__PURE__ */ new Set()); + bag.patterns.add(pattern); + }); + inst._zod.check = (payload) => { + if (payload.value.includes(def.includes, def.position)) + return; + payload.issues.push({ + origin: "string", + code: "invalid_format", + format: "includes", + includes: def.includes, + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckStartsWith = /* @__PURE__ */ $constructor("$ZodCheckStartsWith", (inst, def) => { + $ZodCheck.init(inst, def); + const pattern = new RegExp(`^${escapeRegex(def.prefix)}.*`); + def.pattern ?? (def.pattern = pattern); + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.patterns ?? (bag.patterns = /* @__PURE__ */ new Set()); + bag.patterns.add(pattern); + }); + inst._zod.check = (payload) => { + if (payload.value.startsWith(def.prefix)) + return; + payload.issues.push({ + origin: "string", + code: "invalid_format", + format: "starts_with", + prefix: def.prefix, + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckEndsWith = /* @__PURE__ */ $constructor("$ZodCheckEndsWith", (inst, def) => { + $ZodCheck.init(inst, def); + const pattern = new RegExp(`.*${escapeRegex(def.suffix)}$`); + def.pattern ?? (def.pattern = pattern); + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.patterns ?? (bag.patterns = /* @__PURE__ */ new Set()); + bag.patterns.add(pattern); + }); + inst._zod.check = (payload) => { + if (payload.value.endsWith(def.suffix)) + return; + payload.issues.push({ + origin: "string", + code: "invalid_format", + format: "ends_with", + suffix: def.suffix, + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +var $ZodCheckOverwrite = /* @__PURE__ */ $constructor("$ZodCheckOverwrite", (inst, def) => { + $ZodCheck.init(inst, def); + inst._zod.check = (payload) => { + payload.value = def.tx(payload.value); + }; +}); + +// node_modules/zod/v4/core/doc.js +var Doc = class { + constructor(args = []) { + this.content = []; + this.indent = 0; + if (this) + this.args = args; + } + indented(fn2) { + this.indent += 1; + fn2(this); + this.indent -= 1; + } + write(arg) { + if (typeof arg === "function") { + arg(this, { execution: "sync" }); + arg(this, { execution: "async" }); + return; + } + const content = arg; + const lines = content.split("\n").filter((x2) => x2); + const minIndent = Math.min(...lines.map((x2) => x2.length - x2.trimStart().length)); + const dedented = lines.map((x2) => x2.slice(minIndent)).map((x2) => " ".repeat(this.indent * 2) + x2); + for (const line of dedented) { + this.content.push(line); + } + } + compile() { + const F = Function; + const args = this?.args; + const content = this?.content ?? [``]; + const lines = [...content.map((x2) => ` ${x2}`)]; + return new F(...args, lines.join("\n")); + } +}; + +// node_modules/zod/v4/core/versions.js +var version = { + major: 4, + minor: 0, + patch: 0 +}; + +// node_modules/zod/v4/core/schemas.js +var $ZodType = /* @__PURE__ */ $constructor("$ZodType", (inst, def) => { + var _a; + inst ?? (inst = {}); + inst._zod.def = def; + inst._zod.bag = inst._zod.bag || {}; + inst._zod.version = version; + const checks = [...inst._zod.def.checks ?? []]; + if (inst._zod.traits.has("$ZodCheck")) { + checks.unshift(inst); + } + for (const ch of checks) { + for (const fn2 of ch._zod.onattach) { + fn2(inst); + } + } + if (checks.length === 0) { + (_a = inst._zod).deferred ?? (_a.deferred = []); + inst._zod.deferred?.push(() => { + inst._zod.run = inst._zod.parse; + }); + } else { + const runChecks = (payload, checks2, ctx) => { + let isAborted2 = aborted(payload); + let asyncResult; + for (const ch of checks2) { + if (ch._zod.def.when) { + const shouldRun = ch._zod.def.when(payload); + if (!shouldRun) + continue; + } else if (isAborted2) { + continue; + } + const currLen = payload.issues.length; + const _ = ch._zod.check(payload); + if (_ instanceof Promise && ctx?.async === false) { + throw new $ZodAsyncError(); + } + if (asyncResult || _ instanceof Promise) { + asyncResult = (asyncResult ?? Promise.resolve()).then(async () => { + await _; + const nextLen = payload.issues.length; + if (nextLen === currLen) + return; + if (!isAborted2) + isAborted2 = aborted(payload, currLen); + }); + } else { + const nextLen = payload.issues.length; + if (nextLen === currLen) + continue; + if (!isAborted2) + isAborted2 = aborted(payload, currLen); + } + } + if (asyncResult) { + return asyncResult.then(() => { + return payload; + }); + } + return payload; + }; + inst._zod.run = (payload, ctx) => { + const result = inst._zod.parse(payload, ctx); + if (result instanceof Promise) { + if (ctx.async === false) + throw new $ZodAsyncError(); + return result.then((result2) => runChecks(result2, checks, ctx)); + } + return runChecks(result, checks, ctx); + }; + } + inst["~standard"] = { + validate: (value) => { + try { + const r = safeParse(inst, value); + return r.success ? { value: r.data } : { issues: r.error?.issues }; + } catch (_) { + return safeParseAsync(inst, value).then((r) => r.success ? { value: r.data } : { issues: r.error?.issues }); + } + }, + vendor: "zod", + version: 1 + }; +}); +var $ZodString = /* @__PURE__ */ $constructor("$ZodString", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.pattern = [...inst?._zod.bag?.patterns ?? []].pop() ?? string(inst._zod.bag); + inst._zod.parse = (payload, _) => { + if (def.coerce) + try { + payload.value = String(payload.value); + } catch (_2) { + } + if (typeof payload.value === "string") + return payload; + payload.issues.push({ + expected: "string", + code: "invalid_type", + input: payload.value, + inst + }); + return payload; + }; +}); +var $ZodStringFormat = /* @__PURE__ */ $constructor("$ZodStringFormat", (inst, def) => { + $ZodCheckStringFormat.init(inst, def); + $ZodString.init(inst, def); +}); +var $ZodGUID = /* @__PURE__ */ $constructor("$ZodGUID", (inst, def) => { + def.pattern ?? (def.pattern = guid); + $ZodStringFormat.init(inst, def); +}); +var $ZodUUID = /* @__PURE__ */ $constructor("$ZodUUID", (inst, def) => { + if (def.version) { + const versionMap = { + v1: 1, + v2: 2, + v3: 3, + v4: 4, + v5: 5, + v6: 6, + v7: 7, + v8: 8 + }; + const v = versionMap[def.version]; + if (v === void 0) + throw new Error(`Invalid UUID version: "${def.version}"`); + def.pattern ?? (def.pattern = uuid(v)); + } else + def.pattern ?? (def.pattern = uuid()); + $ZodStringFormat.init(inst, def); +}); +var $ZodEmail = /* @__PURE__ */ $constructor("$ZodEmail", (inst, def) => { + def.pattern ?? (def.pattern = email); + $ZodStringFormat.init(inst, def); +}); +var $ZodURL = /* @__PURE__ */ $constructor("$ZodURL", (inst, def) => { + $ZodStringFormat.init(inst, def); + inst._zod.check = (payload) => { + try { + const orig = payload.value; + const url = new URL(orig); + const href = url.href; + if (def.hostname) { + def.hostname.lastIndex = 0; + if (!def.hostname.test(url.hostname)) { + payload.issues.push({ + code: "invalid_format", + format: "url", + note: "Invalid hostname", + pattern: hostname.source, + input: payload.value, + inst, + continue: !def.abort + }); + } + } + if (def.protocol) { + def.protocol.lastIndex = 0; + if (!def.protocol.test(url.protocol.endsWith(":") ? url.protocol.slice(0, -1) : url.protocol)) { + payload.issues.push({ + code: "invalid_format", + format: "url", + note: "Invalid protocol", + pattern: def.protocol.source, + input: payload.value, + inst, + continue: !def.abort + }); + } + } + if (!orig.endsWith("/") && href.endsWith("/")) { + payload.value = href.slice(0, -1); + } else { + payload.value = href; + } + return; + } catch (_) { + payload.issues.push({ + code: "invalid_format", + format: "url", + input: payload.value, + inst, + continue: !def.abort + }); + } + }; +}); +var $ZodEmoji = /* @__PURE__ */ $constructor("$ZodEmoji", (inst, def) => { + def.pattern ?? (def.pattern = emoji()); + $ZodStringFormat.init(inst, def); +}); +var $ZodNanoID = /* @__PURE__ */ $constructor("$ZodNanoID", (inst, def) => { + def.pattern ?? (def.pattern = nanoid); + $ZodStringFormat.init(inst, def); +}); +var $ZodCUID = /* @__PURE__ */ $constructor("$ZodCUID", (inst, def) => { + def.pattern ?? (def.pattern = cuid); + $ZodStringFormat.init(inst, def); +}); +var $ZodCUID2 = /* @__PURE__ */ $constructor("$ZodCUID2", (inst, def) => { + def.pattern ?? (def.pattern = cuid2); + $ZodStringFormat.init(inst, def); +}); +var $ZodULID = /* @__PURE__ */ $constructor("$ZodULID", (inst, def) => { + def.pattern ?? (def.pattern = ulid); + $ZodStringFormat.init(inst, def); +}); +var $ZodXID = /* @__PURE__ */ $constructor("$ZodXID", (inst, def) => { + def.pattern ?? (def.pattern = xid); + $ZodStringFormat.init(inst, def); +}); +var $ZodKSUID = /* @__PURE__ */ $constructor("$ZodKSUID", (inst, def) => { + def.pattern ?? (def.pattern = ksuid); + $ZodStringFormat.init(inst, def); +}); +var $ZodISODateTime = /* @__PURE__ */ $constructor("$ZodISODateTime", (inst, def) => { + def.pattern ?? (def.pattern = datetime(def)); + $ZodStringFormat.init(inst, def); +}); +var $ZodISODate = /* @__PURE__ */ $constructor("$ZodISODate", (inst, def) => { + def.pattern ?? (def.pattern = date); + $ZodStringFormat.init(inst, def); +}); +var $ZodISOTime = /* @__PURE__ */ $constructor("$ZodISOTime", (inst, def) => { + def.pattern ?? (def.pattern = time(def)); + $ZodStringFormat.init(inst, def); +}); +var $ZodISODuration = /* @__PURE__ */ $constructor("$ZodISODuration", (inst, def) => { + def.pattern ?? (def.pattern = duration); + $ZodStringFormat.init(inst, def); +}); +var $ZodIPv4 = /* @__PURE__ */ $constructor("$ZodIPv4", (inst, def) => { + def.pattern ?? (def.pattern = ipv4); + $ZodStringFormat.init(inst, def); + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.format = `ipv4`; + }); +}); +var $ZodIPv6 = /* @__PURE__ */ $constructor("$ZodIPv6", (inst, def) => { + def.pattern ?? (def.pattern = ipv6); + $ZodStringFormat.init(inst, def); + inst._zod.onattach.push((inst2) => { + const bag = inst2._zod.bag; + bag.format = `ipv6`; + }); + inst._zod.check = (payload) => { + try { + new URL(`http://[${payload.value}]`); + } catch { + payload.issues.push({ + code: "invalid_format", + format: "ipv6", + input: payload.value, + inst, + continue: !def.abort + }); + } + }; +}); +var $ZodCIDRv4 = /* @__PURE__ */ $constructor("$ZodCIDRv4", (inst, def) => { + def.pattern ?? (def.pattern = cidrv4); + $ZodStringFormat.init(inst, def); +}); +var $ZodCIDRv6 = /* @__PURE__ */ $constructor("$ZodCIDRv6", (inst, def) => { + def.pattern ?? (def.pattern = cidrv6); + $ZodStringFormat.init(inst, def); + inst._zod.check = (payload) => { + const [address, prefix] = payload.value.split("/"); + try { + if (!prefix) + throw new Error(); + const prefixNum = Number(prefix); + if (`${prefixNum}` !== prefix) + throw new Error(); + if (prefixNum < 0 || prefixNum > 128) + throw new Error(); + new URL(`http://[${address}]`); + } catch { + payload.issues.push({ + code: "invalid_format", + format: "cidrv6", + input: payload.value, + inst, + continue: !def.abort + }); + } + }; +}); +function isValidBase64(data) { + if (data === "") + return true; + if (data.length % 4 !== 0) + return false; + try { + atob(data); + return true; + } catch { + return false; + } +} +var $ZodBase64 = /* @__PURE__ */ $constructor("$ZodBase64", (inst, def) => { + def.pattern ?? (def.pattern = base64); + $ZodStringFormat.init(inst, def); + inst._zod.onattach.push((inst2) => { + inst2._zod.bag.contentEncoding = "base64"; + }); + inst._zod.check = (payload) => { + if (isValidBase64(payload.value)) + return; + payload.issues.push({ + code: "invalid_format", + format: "base64", + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +function isValidBase64URL(data) { + if (!base64url.test(data)) + return false; + const base642 = data.replace(/[-_]/g, (c) => c === "-" ? "+" : "/"); + const padded = base642.padEnd(Math.ceil(base642.length / 4) * 4, "="); + return isValidBase64(padded); +} +var $ZodBase64URL = /* @__PURE__ */ $constructor("$ZodBase64URL", (inst, def) => { + def.pattern ?? (def.pattern = base64url); + $ZodStringFormat.init(inst, def); + inst._zod.onattach.push((inst2) => { + inst2._zod.bag.contentEncoding = "base64url"; + }); + inst._zod.check = (payload) => { + if (isValidBase64URL(payload.value)) + return; + payload.issues.push({ + code: "invalid_format", + format: "base64url", + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +var $ZodE164 = /* @__PURE__ */ $constructor("$ZodE164", (inst, def) => { + def.pattern ?? (def.pattern = e164); + $ZodStringFormat.init(inst, def); +}); +function isValidJWT(token, algorithm = null) { + try { + const tokensParts = token.split("."); + if (tokensParts.length !== 3) + return false; + const [header] = tokensParts; + if (!header) + return false; + const parsedHeader = JSON.parse(atob(header)); + if ("typ" in parsedHeader && parsedHeader?.typ !== "JWT") + return false; + if (!parsedHeader.alg) + return false; + if (algorithm && (!("alg" in parsedHeader) || parsedHeader.alg !== algorithm)) + return false; + return true; + } catch { + return false; + } +} +var $ZodJWT = /* @__PURE__ */ $constructor("$ZodJWT", (inst, def) => { + $ZodStringFormat.init(inst, def); + inst._zod.check = (payload) => { + if (isValidJWT(payload.value, def.alg)) + return; + payload.issues.push({ + code: "invalid_format", + format: "jwt", + input: payload.value, + inst, + continue: !def.abort + }); + }; +}); +var $ZodNumber = /* @__PURE__ */ $constructor("$ZodNumber", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.pattern = inst._zod.bag.pattern ?? number; + inst._zod.parse = (payload, _ctx) => { + if (def.coerce) + try { + payload.value = Number(payload.value); + } catch (_) { + } + const input = payload.value; + if (typeof input === "number" && !Number.isNaN(input) && Number.isFinite(input)) { + return payload; + } + const received = typeof input === "number" ? Number.isNaN(input) ? "NaN" : !Number.isFinite(input) ? "Infinity" : void 0 : void 0; + payload.issues.push({ + expected: "number", + code: "invalid_type", + input, + inst, + ...received ? { received } : {} + }); + return payload; + }; +}); +var $ZodNumberFormat = /* @__PURE__ */ $constructor("$ZodNumber", (inst, def) => { + $ZodCheckNumberFormat.init(inst, def); + $ZodNumber.init(inst, def); +}); +var $ZodBoolean = /* @__PURE__ */ $constructor("$ZodBoolean", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.pattern = boolean; + inst._zod.parse = (payload, _ctx) => { + if (def.coerce) + try { + payload.value = Boolean(payload.value); + } catch (_) { + } + const input = payload.value; + if (typeof input === "boolean") + return payload; + payload.issues.push({ + expected: "boolean", + code: "invalid_type", + input, + inst + }); + return payload; + }; +}); +var $ZodNull = /* @__PURE__ */ $constructor("$ZodNull", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.pattern = _null; + inst._zod.values = /* @__PURE__ */ new Set([null]); + inst._zod.parse = (payload, _ctx) => { + const input = payload.value; + if (input === null) + return payload; + payload.issues.push({ + expected: "null", + code: "invalid_type", + input, + inst + }); + return payload; + }; +}); +var $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.parse = (payload) => payload; +}); +var $ZodNever = /* @__PURE__ */ $constructor("$ZodNever", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.parse = (payload, _ctx) => { + payload.issues.push({ + expected: "never", + code: "invalid_type", + input: payload.value, + inst + }); + return payload; + }; +}); +function handleArrayResult(result, final, index) { + if (result.issues.length) { + final.issues.push(...prefixIssues(index, result.issues)); + } + final.value[index] = result.value; +} +var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.parse = (payload, ctx) => { + const input = payload.value; + if (!Array.isArray(input)) { + payload.issues.push({ + expected: "array", + code: "invalid_type", + input, + inst + }); + return payload; + } + payload.value = Array(input.length); + const proms = []; + for (let i = 0; i < input.length; i++) { + const item = input[i]; + const result = def.element._zod.run({ + value: item, + issues: [] + }, ctx); + if (result instanceof Promise) { + proms.push(result.then((result2) => handleArrayResult(result2, payload, i))); + } else { + handleArrayResult(result, payload, i); + } + } + if (proms.length) { + return Promise.all(proms).then(() => payload); + } + return payload; + }; +}); +function handleObjectResult(result, final, key) { + if (result.issues.length) { + final.issues.push(...prefixIssues(key, result.issues)); + } + final.value[key] = result.value; +} +function handleOptionalObjectResult(result, final, key, input) { + if (result.issues.length) { + if (input[key] === void 0) { + if (key in input) { + final.value[key] = void 0; + } else { + final.value[key] = result.value; + } + } else { + final.issues.push(...prefixIssues(key, result.issues)); + } + } else if (result.value === void 0) { + if (key in input) + final.value[key] = void 0; + } else { + final.value[key] = result.value; + } +} +var $ZodObject = /* @__PURE__ */ $constructor("$ZodObject", (inst, def) => { + $ZodType.init(inst, def); + const _normalized = cached(() => { + const keys = Object.keys(def.shape); + for (const k2 of keys) { + if (!(def.shape[k2] instanceof $ZodType)) { + throw new Error(`Invalid element at key "${k2}": expected a Zod schema`); + } + } + const okeys = optionalKeys(def.shape); + return { + shape: def.shape, + keys, + keySet: new Set(keys), + numKeys: keys.length, + optionalKeys: new Set(okeys) + }; + }); + defineLazy(inst._zod, "propValues", () => { + const shape = def.shape; + const propValues = {}; + for (const key in shape) { + const field = shape[key]._zod; + if (field.values) { + propValues[key] ?? (propValues[key] = /* @__PURE__ */ new Set()); + for (const v of field.values) + propValues[key].add(v); + } + } + return propValues; + }); + const generateFastpass = (shape) => { + const doc = new Doc(["shape", "payload", "ctx"]); + const normalized = _normalized.value; + const parseStr = (key) => { + const k2 = esc(key); + return `shape[${k2}]._zod.run({ value: input[${k2}], issues: [] }, ctx)`; + }; + doc.write(`const input = payload.value;`); + const ids = /* @__PURE__ */ Object.create(null); + let counter = 0; + for (const key of normalized.keys) { + ids[key] = `key_${counter++}`; + } + doc.write(`const newResult = {}`); + for (const key of normalized.keys) { + if (normalized.optionalKeys.has(key)) { + const id = ids[key]; + doc.write(`const ${id} = ${parseStr(key)};`); + const k2 = esc(key); + doc.write(` + if (${id}.issues.length) { + if (input[${k2}] === undefined) { + if (${k2} in input) { + newResult[${k2}] = undefined; + } + } else { + payload.issues = payload.issues.concat( + ${id}.issues.map((iss) => ({ + ...iss, + path: iss.path ? [${k2}, ...iss.path] : [${k2}], + })) + ); + } + } else if (${id}.value === undefined) { + if (${k2} in input) newResult[${k2}] = undefined; + } else { + newResult[${k2}] = ${id}.value; + } + `); + } else { + const id = ids[key]; + doc.write(`const ${id} = ${parseStr(key)};`); + doc.write(` + if (${id}.issues.length) payload.issues = payload.issues.concat(${id}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${esc(key)}, ...iss.path] : [${esc(key)}] + })));`); + doc.write(`newResult[${esc(key)}] = ${id}.value`); + } + } + doc.write(`payload.value = newResult;`); + doc.write(`return payload;`); + const fn2 = doc.compile(); + return (payload, ctx) => fn2(shape, payload, ctx); + }; + let fastpass; + const isObject2 = isObject; + const jit = !globalConfig.jitless; + const allowsEval2 = allowsEval; + const fastEnabled = jit && allowsEval2.value; + const catchall = def.catchall; + let value; + inst._zod.parse = (payload, ctx) => { + value ?? (value = _normalized.value); + const input = payload.value; + if (!isObject2(input)) { + payload.issues.push({ + expected: "object", + code: "invalid_type", + input, + inst + }); + return payload; + } + const proms = []; + if (jit && fastEnabled && ctx?.async === false && ctx.jitless !== true) { + if (!fastpass) + fastpass = generateFastpass(def.shape); + payload = fastpass(payload, ctx); + } else { + payload.value = {}; + const shape = value.shape; + for (const key of value.keys) { + const el2 = shape[key]; + const r = el2._zod.run({ value: input[key], issues: [] }, ctx); + const isOptional = el2._zod.optin === "optional" && el2._zod.optout === "optional"; + if (r instanceof Promise) { + proms.push(r.then((r2) => isOptional ? handleOptionalObjectResult(r2, payload, key, input) : handleObjectResult(r2, payload, key))); + } else if (isOptional) { + handleOptionalObjectResult(r, payload, key, input); + } else { + handleObjectResult(r, payload, key); + } + } + } + if (!catchall) { + return proms.length ? Promise.all(proms).then(() => payload) : payload; + } + const unrecognized = []; + const keySet = value.keySet; + const _catchall = catchall._zod; + const t = _catchall.def.type; + for (const key of Object.keys(input)) { + if (keySet.has(key)) + continue; + if (t === "never") { + unrecognized.push(key); + continue; + } + const r = _catchall.run({ value: input[key], issues: [] }, ctx); + if (r instanceof Promise) { + proms.push(r.then((r2) => handleObjectResult(r2, payload, key))); + } else { + handleObjectResult(r, payload, key); + } + } + if (unrecognized.length) { + payload.issues.push({ + code: "unrecognized_keys", + keys: unrecognized, + input, + inst + }); + } + if (!proms.length) + return payload; + return Promise.all(proms).then(() => { + return payload; + }); + }; +}); +function handleUnionResults(results, final, inst, ctx) { + for (const result of results) { + if (result.issues.length === 0) { + final.value = result.value; + return final; + } + } + final.issues.push({ + code: "invalid_union", + input: final.value, + inst, + errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config()))) + }); + return final; +} +var $ZodUnion = /* @__PURE__ */ $constructor("$ZodUnion", (inst, def) => { + $ZodType.init(inst, def); + defineLazy(inst._zod, "optin", () => def.options.some((o) => o._zod.optin === "optional") ? "optional" : void 0); + defineLazy(inst._zod, "optout", () => def.options.some((o) => o._zod.optout === "optional") ? "optional" : void 0); + defineLazy(inst._zod, "values", () => { + if (def.options.every((o) => o._zod.values)) { + return new Set(def.options.flatMap((option) => Array.from(option._zod.values))); + } + return void 0; + }); + defineLazy(inst._zod, "pattern", () => { + if (def.options.every((o) => o._zod.pattern)) { + const patterns = def.options.map((o) => o._zod.pattern); + return new RegExp(`^(${patterns.map((p2) => cleanRegex(p2.source)).join("|")})$`); + } + return void 0; + }); + inst._zod.parse = (payload, ctx) => { + let async = false; + const results = []; + for (const option of def.options) { + const result = option._zod.run({ + value: payload.value, + issues: [] + }, ctx); + if (result instanceof Promise) { + results.push(result); + async = true; + } else { + if (result.issues.length === 0) + return result; + results.push(result); + } + } + if (!async) + return handleUnionResults(results, payload, inst, ctx); + return Promise.all(results).then((results2) => { + return handleUnionResults(results2, payload, inst, ctx); + }); + }; +}); +var $ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("$ZodDiscriminatedUnion", (inst, def) => { + $ZodUnion.init(inst, def); + const _super = inst._zod.parse; + defineLazy(inst._zod, "propValues", () => { + const propValues = {}; + for (const option of def.options) { + const pv2 = option._zod.propValues; + if (!pv2 || Object.keys(pv2).length === 0) + throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(option)}"`); + for (const [k2, v] of Object.entries(pv2)) { + if (!propValues[k2]) + propValues[k2] = /* @__PURE__ */ new Set(); + for (const val of v) { + propValues[k2].add(val); + } + } + } + return propValues; + }); + const disc = cached(() => { + const opts = def.options; + const map = /* @__PURE__ */ new Map(); + for (const o of opts) { + const values = o._zod.propValues[def.discriminator]; + if (!values || values.size === 0) + throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`); + for (const v of values) { + if (map.has(v)) { + throw new Error(`Duplicate discriminator value "${String(v)}"`); + } + map.set(v, o); + } + } + return map; + }); + inst._zod.parse = (payload, ctx) => { + const input = payload.value; + if (!isObject(input)) { + payload.issues.push({ + code: "invalid_type", + expected: "object", + input, + inst + }); + return payload; + } + const opt = disc.value.get(input?.[def.discriminator]); + if (opt) { + return opt._zod.run(payload, ctx); + } + if (def.unionFallback) { + return _super(payload, ctx); + } + payload.issues.push({ + code: "invalid_union", + errors: [], + note: "No matching discriminator", + input, + path: [def.discriminator], + inst + }); + return payload; + }; +}); +var $ZodIntersection = /* @__PURE__ */ $constructor("$ZodIntersection", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.parse = (payload, ctx) => { + const input = payload.value; + const left = def.left._zod.run({ value: input, issues: [] }, ctx); + const right = def.right._zod.run({ value: input, issues: [] }, ctx); + const async = left instanceof Promise || right instanceof Promise; + if (async) { + return Promise.all([left, right]).then(([left2, right2]) => { + return handleIntersectionResults(payload, left2, right2); + }); + } + return handleIntersectionResults(payload, left, right); + }; +}); +function mergeValues(a2, b2) { + if (a2 === b2) { + return { valid: true, data: a2 }; + } + if (a2 instanceof Date && b2 instanceof Date && +a2 === +b2) { + return { valid: true, data: a2 }; + } + if (isPlainObject(a2) && isPlainObject(b2)) { + const bKeys = Object.keys(b2); + const sharedKeys = Object.keys(a2).filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a2, ...b2 }; + for (const key of sharedKeys) { + const sharedValue = mergeValues(a2[key], b2[key]); + if (!sharedValue.valid) { + return { + valid: false, + mergeErrorPath: [key, ...sharedValue.mergeErrorPath] + }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } + if (Array.isArray(a2) && Array.isArray(b2)) { + if (a2.length !== b2.length) { + return { valid: false, mergeErrorPath: [] }; + } + const newArray = []; + for (let index = 0; index < a2.length; index++) { + const itemA = a2[index]; + const itemB = b2[index]; + const sharedValue = mergeValues(itemA, itemB); + if (!sharedValue.valid) { + return { + valid: false, + mergeErrorPath: [index, ...sharedValue.mergeErrorPath] + }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } + return { valid: false, mergeErrorPath: [] }; +} +function handleIntersectionResults(result, left, right) { + if (left.issues.length) { + result.issues.push(...left.issues); + } + if (right.issues.length) { + result.issues.push(...right.issues); + } + if (aborted(result)) + return result; + const merged = mergeValues(left.value, right.value); + if (!merged.valid) { + throw new Error(`Unmergable intersection. Error path: ${JSON.stringify(merged.mergeErrorPath)}`); + } + result.value = merged.data; + return result; +} +var $ZodRecord = /* @__PURE__ */ $constructor("$ZodRecord", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.parse = (payload, ctx) => { + const input = payload.value; + if (!isPlainObject(input)) { + payload.issues.push({ + expected: "record", + code: "invalid_type", + input, + inst + }); + return payload; + } + const proms = []; + if (def.keyType._zod.values) { + const values = def.keyType._zod.values; + payload.value = {}; + for (const key of values) { + if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") { + const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx); + if (result instanceof Promise) { + proms.push(result.then((result2) => { + if (result2.issues.length) { + payload.issues.push(...prefixIssues(key, result2.issues)); + } + payload.value[key] = result2.value; + })); + } else { + if (result.issues.length) { + payload.issues.push(...prefixIssues(key, result.issues)); + } + payload.value[key] = result.value; + } + } + } + let unrecognized; + for (const key in input) { + if (!values.has(key)) { + unrecognized = unrecognized ?? []; + unrecognized.push(key); + } + } + if (unrecognized && unrecognized.length > 0) { + payload.issues.push({ + code: "unrecognized_keys", + input, + inst, + keys: unrecognized + }); + } + } else { + payload.value = {}; + for (const key of Reflect.ownKeys(input)) { + if (key === "__proto__") + continue; + const keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx); + if (keyResult instanceof Promise) { + throw new Error("Async schemas not supported in object keys currently"); + } + if (keyResult.issues.length) { + payload.issues.push({ + origin: "record", + code: "invalid_key", + issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())), + input: key, + path: [key], + inst + }); + payload.value[keyResult.value] = keyResult.value; + continue; + } + const result = def.valueType._zod.run({ value: input[key], issues: [] }, ctx); + if (result instanceof Promise) { + proms.push(result.then((result2) => { + if (result2.issues.length) { + payload.issues.push(...prefixIssues(key, result2.issues)); + } + payload.value[keyResult.value] = result2.value; + })); + } else { + if (result.issues.length) { + payload.issues.push(...prefixIssues(key, result.issues)); + } + payload.value[keyResult.value] = result.value; + } + } + } + if (proms.length) { + return Promise.all(proms).then(() => payload); + } + return payload; + }; +}); +var $ZodEnum = /* @__PURE__ */ $constructor("$ZodEnum", (inst, def) => { + $ZodType.init(inst, def); + const values = getEnumValues(def.entries); + inst._zod.values = new Set(values); + inst._zod.pattern = new RegExp(`^(${values.filter((k2) => propertyKeyTypes.has(typeof k2)).map((o) => typeof o === "string" ? escapeRegex(o) : o.toString()).join("|")})$`); + inst._zod.parse = (payload, _ctx) => { + const input = payload.value; + if (inst._zod.values.has(input)) { + return payload; + } + payload.issues.push({ + code: "invalid_value", + values, + input, + inst + }); + return payload; + }; +}); +var $ZodLiteral = /* @__PURE__ */ $constructor("$ZodLiteral", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.values = new Set(def.values); + inst._zod.pattern = new RegExp(`^(${def.values.map((o) => typeof o === "string" ? escapeRegex(o) : o ? o.toString() : String(o)).join("|")})$`); + inst._zod.parse = (payload, _ctx) => { + const input = payload.value; + if (inst._zod.values.has(input)) { + return payload; + } + payload.issues.push({ + code: "invalid_value", + values: def.values, + input, + inst + }); + return payload; + }; +}); +var $ZodTransform = /* @__PURE__ */ $constructor("$ZodTransform", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.parse = (payload, _ctx) => { + const _out = def.transform(payload.value, payload); + if (_ctx.async) { + const output = _out instanceof Promise ? _out : Promise.resolve(_out); + return output.then((output2) => { + payload.value = output2; + return payload; + }); + } + if (_out instanceof Promise) { + throw new $ZodAsyncError(); + } + payload.value = _out; + return payload; + }; +}); +var $ZodOptional = /* @__PURE__ */ $constructor("$ZodOptional", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.optin = "optional"; + inst._zod.optout = "optional"; + defineLazy(inst._zod, "values", () => { + return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, void 0]) : void 0; + }); + defineLazy(inst._zod, "pattern", () => { + const pattern = def.innerType._zod.pattern; + return pattern ? new RegExp(`^(${cleanRegex(pattern.source)})?$`) : void 0; + }); + inst._zod.parse = (payload, ctx) => { + if (def.innerType._zod.optin === "optional") { + return def.innerType._zod.run(payload, ctx); + } + if (payload.value === void 0) { + return payload; + } + return def.innerType._zod.run(payload, ctx); + }; +}); +var $ZodNullable = /* @__PURE__ */ $constructor("$ZodNullable", (inst, def) => { + $ZodType.init(inst, def); + defineLazy(inst._zod, "optin", () => def.innerType._zod.optin); + defineLazy(inst._zod, "optout", () => def.innerType._zod.optout); + defineLazy(inst._zod, "pattern", () => { + const pattern = def.innerType._zod.pattern; + return pattern ? new RegExp(`^(${cleanRegex(pattern.source)}|null)$`) : void 0; + }); + defineLazy(inst._zod, "values", () => { + return def.innerType._zod.values ? /* @__PURE__ */ new Set([...def.innerType._zod.values, null]) : void 0; + }); + inst._zod.parse = (payload, ctx) => { + if (payload.value === null) + return payload; + return def.innerType._zod.run(payload, ctx); + }; +}); +var $ZodDefault = /* @__PURE__ */ $constructor("$ZodDefault", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.optin = "optional"; + defineLazy(inst._zod, "values", () => def.innerType._zod.values); + inst._zod.parse = (payload, ctx) => { + if (payload.value === void 0) { + payload.value = def.defaultValue; + return payload; + } + const result = def.innerType._zod.run(payload, ctx); + if (result instanceof Promise) { + return result.then((result2) => handleDefaultResult(result2, def)); + } + return handleDefaultResult(result, def); + }; +}); +function handleDefaultResult(payload, def) { + if (payload.value === void 0) { + payload.value = def.defaultValue; + } + return payload; +} +var $ZodPrefault = /* @__PURE__ */ $constructor("$ZodPrefault", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.optin = "optional"; + defineLazy(inst._zod, "values", () => def.innerType._zod.values); + inst._zod.parse = (payload, ctx) => { + if (payload.value === void 0) { + payload.value = def.defaultValue; + } + return def.innerType._zod.run(payload, ctx); + }; +}); +var $ZodNonOptional = /* @__PURE__ */ $constructor("$ZodNonOptional", (inst, def) => { + $ZodType.init(inst, def); + defineLazy(inst._zod, "values", () => { + const v = def.innerType._zod.values; + return v ? new Set([...v].filter((x2) => x2 !== void 0)) : void 0; + }); + inst._zod.parse = (payload, ctx) => { + const result = def.innerType._zod.run(payload, ctx); + if (result instanceof Promise) { + return result.then((result2) => handleNonOptionalResult(result2, inst)); + } + return handleNonOptionalResult(result, inst); + }; +}); +function handleNonOptionalResult(payload, inst) { + if (!payload.issues.length && payload.value === void 0) { + payload.issues.push({ + code: "invalid_type", + expected: "nonoptional", + input: payload.value, + inst + }); + } + return payload; +} +var $ZodCatch = /* @__PURE__ */ $constructor("$ZodCatch", (inst, def) => { + $ZodType.init(inst, def); + inst._zod.optin = "optional"; + defineLazy(inst._zod, "optout", () => def.innerType._zod.optout); + defineLazy(inst._zod, "values", () => def.innerType._zod.values); + inst._zod.parse = (payload, ctx) => { + const result = def.innerType._zod.run(payload, ctx); + if (result instanceof Promise) { + return result.then((result2) => { + payload.value = result2.value; + if (result2.issues.length) { + payload.value = def.catchValue({ + ...payload, + error: { + issues: result2.issues.map((iss) => finalizeIssue(iss, ctx, config())) + }, + input: payload.value + }); + payload.issues = []; + } + return payload; + }); + } + payload.value = result.value; + if (result.issues.length) { + payload.value = def.catchValue({ + ...payload, + error: { + issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config())) + }, + input: payload.value + }); + payload.issues = []; + } + return payload; + }; +}); +var $ZodPipe = /* @__PURE__ */ $constructor("$ZodPipe", (inst, def) => { + $ZodType.init(inst, def); + defineLazy(inst._zod, "values", () => def.in._zod.values); + defineLazy(inst._zod, "optin", () => def.in._zod.optin); + defineLazy(inst._zod, "optout", () => def.out._zod.optout); + inst._zod.parse = (payload, ctx) => { + const left = def.in._zod.run(payload, ctx); + if (left instanceof Promise) { + return left.then((left2) => handlePipeResult(left2, def, ctx)); + } + return handlePipeResult(left, def, ctx); + }; +}); +function handlePipeResult(left, def, ctx) { + if (aborted(left)) { + return left; + } + return def.out._zod.run({ value: left.value, issues: left.issues }, ctx); +} +var $ZodReadonly = /* @__PURE__ */ $constructor("$ZodReadonly", (inst, def) => { + $ZodType.init(inst, def); + defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues); + defineLazy(inst._zod, "values", () => def.innerType._zod.values); + defineLazy(inst._zod, "optin", () => def.innerType._zod.optin); + defineLazy(inst._zod, "optout", () => def.innerType._zod.optout); + inst._zod.parse = (payload, ctx) => { + const result = def.innerType._zod.run(payload, ctx); + if (result instanceof Promise) { + return result.then(handleReadonlyResult); + } + return handleReadonlyResult(result); + }; +}); +function handleReadonlyResult(payload) { + payload.value = Object.freeze(payload.value); + return payload; +} +var $ZodCustom = /* @__PURE__ */ $constructor("$ZodCustom", (inst, def) => { + $ZodCheck.init(inst, def); + $ZodType.init(inst, def); + inst._zod.parse = (payload, _) => { + return payload; + }; + inst._zod.check = (payload) => { + const input = payload.value; + const r = def.fn(input); + if (r instanceof Promise) { + return r.then((r2) => handleRefineResult(r2, payload, input, inst)); + } + handleRefineResult(r, payload, input, inst); + return; + }; +}); +function handleRefineResult(result, payload, input, inst) { + if (!result) { + const _iss = { + code: "custom", + input, + inst, + // incorporates params.error into issue reporting + path: [...inst._zod.def.path ?? []], + // incorporates params.error into issue reporting + continue: !inst._zod.def.abort + // params: inst._zod.def.params, + }; + if (inst._zod.def.params) + _iss.params = inst._zod.def.params; + payload.issues.push(issue(_iss)); + } +} + +// node_modules/zod/v4/locales/en.js +var parsedType = (data) => { + const t = typeof data; + switch (t) { + case "number": { + return Number.isNaN(data) ? "NaN" : "number"; + } + case "object": { + if (Array.isArray(data)) { + return "array"; + } + if (data === null) { + return "null"; + } + if (Object.getPrototypeOf(data) !== Object.prototype && data.constructor) { + return data.constructor.name; + } + } + } + return t; +}; +var error = () => { + const Sizable = { + string: { unit: "characters", verb: "to have" }, + file: { unit: "bytes", verb: "to have" }, + array: { unit: "items", verb: "to have" }, + set: { unit: "items", verb: "to have" } + }; + function getSizing(origin) { + return Sizable[origin] ?? null; + } + const Nouns = { + regex: "input", + email: "email address", + url: "URL", + emoji: "emoji", + uuid: "UUID", + uuidv4: "UUIDv4", + uuidv6: "UUIDv6", + nanoid: "nanoid", + guid: "GUID", + cuid: "cuid", + cuid2: "cuid2", + ulid: "ULID", + xid: "XID", + ksuid: "KSUID", + datetime: "ISO datetime", + date: "ISO date", + time: "ISO time", + duration: "ISO duration", + ipv4: "IPv4 address", + ipv6: "IPv6 address", + cidrv4: "IPv4 range", + cidrv6: "IPv6 range", + base64: "base64-encoded string", + base64url: "base64url-encoded string", + json_string: "JSON string", + e164: "E.164 number", + jwt: "JWT", + template_literal: "input" + }; + return (issue2) => { + switch (issue2.code) { + case "invalid_type": + return `Invalid input: expected ${issue2.expected}, received ${parsedType(issue2.input)}`; + case "invalid_value": + if (issue2.values.length === 1) + return `Invalid input: expected ${stringifyPrimitive(issue2.values[0])}`; + return `Invalid option: expected one of ${joinValues(issue2.values, "|")}`; + case "too_big": { + const adj = issue2.inclusive ? "<=" : "<"; + const sizing = getSizing(issue2.origin); + if (sizing) + return `Too big: expected ${issue2.origin ?? "value"} to have ${adj}${issue2.maximum.toString()} ${sizing.unit ?? "elements"}`; + return `Too big: expected ${issue2.origin ?? "value"} to be ${adj}${issue2.maximum.toString()}`; + } + case "too_small": { + const adj = issue2.inclusive ? ">=" : ">"; + const sizing = getSizing(issue2.origin); + if (sizing) { + return `Too small: expected ${issue2.origin} to have ${adj}${issue2.minimum.toString()} ${sizing.unit}`; + } + return `Too small: expected ${issue2.origin} to be ${adj}${issue2.minimum.toString()}`; + } + case "invalid_format": { + const _issue = issue2; + if (_issue.format === "starts_with") { + return `Invalid string: must start with "${_issue.prefix}"`; + } + if (_issue.format === "ends_with") + return `Invalid string: must end with "${_issue.suffix}"`; + if (_issue.format === "includes") + return `Invalid string: must include "${_issue.includes}"`; + if (_issue.format === "regex") + return `Invalid string: must match pattern ${_issue.pattern}`; + return `Invalid ${Nouns[_issue.format] ?? issue2.format}`; + } + case "not_multiple_of": + return `Invalid number: must be a multiple of ${issue2.divisor}`; + case "unrecognized_keys": + return `Unrecognized key${issue2.keys.length > 1 ? "s" : ""}: ${joinValues(issue2.keys, ", ")}`; + case "invalid_key": + return `Invalid key in ${issue2.origin}`; + case "invalid_union": + return "Invalid input"; + case "invalid_element": + return `Invalid value in ${issue2.origin}`; + default: + return `Invalid input`; + } + }; +}; +function en_default() { + return { + localeError: error() + }; +} + +// node_modules/zod/v4/core/registries.js +var $ZodRegistry = class { + constructor() { + this._map = /* @__PURE__ */ new Map(); + this._idmap = /* @__PURE__ */ new Map(); + } + add(schema, ..._meta) { + const meta = _meta[0]; + this._map.set(schema, meta); + if (meta && typeof meta === "object" && "id" in meta) { + if (this._idmap.has(meta.id)) { + throw new Error(`ID ${meta.id} already exists in the registry`); + } + this._idmap.set(meta.id, schema); + } + return this; + } + clear() { + this._map = /* @__PURE__ */ new Map(); + this._idmap = /* @__PURE__ */ new Map(); + return this; + } + remove(schema) { + const meta = this._map.get(schema); + if (meta && typeof meta === "object" && "id" in meta) { + this._idmap.delete(meta.id); + } + this._map.delete(schema); + return this; + } + get(schema) { + const p2 = schema._zod.parent; + if (p2) { + const pm = { ...this.get(p2) ?? {} }; + delete pm.id; + return { ...pm, ...this._map.get(schema) }; + } + return this._map.get(schema); + } + has(schema) { + return this._map.has(schema); + } +}; +function registry() { + return new $ZodRegistry(); +} +var globalRegistry = /* @__PURE__ */ registry(); + +// node_modules/zod/v4/core/api.js +function _string(Class2, params) { + return new Class2({ + type: "string", + ...normalizeParams(params) + }); +} +function _email(Class2, params) { + return new Class2({ + type: "string", + format: "email", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _guid(Class2, params) { + return new Class2({ + type: "string", + format: "guid", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _uuid(Class2, params) { + return new Class2({ + type: "string", + format: "uuid", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _uuidv4(Class2, params) { + return new Class2({ + type: "string", + format: "uuid", + check: "string_format", + abort: false, + version: "v4", + ...normalizeParams(params) + }); +} +function _uuidv6(Class2, params) { + return new Class2({ + type: "string", + format: "uuid", + check: "string_format", + abort: false, + version: "v6", + ...normalizeParams(params) + }); +} +function _uuidv7(Class2, params) { + return new Class2({ + type: "string", + format: "uuid", + check: "string_format", + abort: false, + version: "v7", + ...normalizeParams(params) + }); +} +function _url(Class2, params) { + return new Class2({ + type: "string", + format: "url", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _emoji2(Class2, params) { + return new Class2({ + type: "string", + format: "emoji", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _nanoid(Class2, params) { + return new Class2({ + type: "string", + format: "nanoid", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _cuid(Class2, params) { + return new Class2({ + type: "string", + format: "cuid", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _cuid2(Class2, params) { + return new Class2({ + type: "string", + format: "cuid2", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _ulid(Class2, params) { + return new Class2({ + type: "string", + format: "ulid", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _xid(Class2, params) { + return new Class2({ + type: "string", + format: "xid", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _ksuid(Class2, params) { + return new Class2({ + type: "string", + format: "ksuid", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _ipv4(Class2, params) { + return new Class2({ + type: "string", + format: "ipv4", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _ipv6(Class2, params) { + return new Class2({ + type: "string", + format: "ipv6", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _cidrv4(Class2, params) { + return new Class2({ + type: "string", + format: "cidrv4", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _cidrv6(Class2, params) { + return new Class2({ + type: "string", + format: "cidrv6", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _base64(Class2, params) { + return new Class2({ + type: "string", + format: "base64", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _base64url(Class2, params) { + return new Class2({ + type: "string", + format: "base64url", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _e164(Class2, params) { + return new Class2({ + type: "string", + format: "e164", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _jwt(Class2, params) { + return new Class2({ + type: "string", + format: "jwt", + check: "string_format", + abort: false, + ...normalizeParams(params) + }); +} +function _isoDateTime(Class2, params) { + return new Class2({ + type: "string", + format: "datetime", + check: "string_format", + offset: false, + local: false, + precision: null, + ...normalizeParams(params) + }); +} +function _isoDate(Class2, params) { + return new Class2({ + type: "string", + format: "date", + check: "string_format", + ...normalizeParams(params) + }); +} +function _isoTime(Class2, params) { + return new Class2({ + type: "string", + format: "time", + check: "string_format", + precision: null, + ...normalizeParams(params) + }); +} +function _isoDuration(Class2, params) { + return new Class2({ + type: "string", + format: "duration", + check: "string_format", + ...normalizeParams(params) + }); +} +function _number(Class2, params) { + return new Class2({ + type: "number", + checks: [], + ...normalizeParams(params) + }); +} +function _int(Class2, params) { + return new Class2({ + type: "number", + check: "number_format", + abort: false, + format: "safeint", + ...normalizeParams(params) + }); +} +function _boolean(Class2, params) { + return new Class2({ + type: "boolean", + ...normalizeParams(params) + }); +} +function _null2(Class2, params) { + return new Class2({ + type: "null", + ...normalizeParams(params) + }); +} +function _unknown(Class2) { + return new Class2({ + type: "unknown" + }); +} +function _never(Class2, params) { + return new Class2({ + type: "never", + ...normalizeParams(params) + }); +} +function _lt(value, params) { + return new $ZodCheckLessThan({ + check: "less_than", + ...normalizeParams(params), + value, + inclusive: false + }); +} +function _lte(value, params) { + return new $ZodCheckLessThan({ + check: "less_than", + ...normalizeParams(params), + value, + inclusive: true + }); +} +function _gt(value, params) { + return new $ZodCheckGreaterThan({ + check: "greater_than", + ...normalizeParams(params), + value, + inclusive: false + }); +} +function _gte(value, params) { + return new $ZodCheckGreaterThan({ + check: "greater_than", + ...normalizeParams(params), + value, + inclusive: true + }); +} +function _multipleOf(value, params) { + return new $ZodCheckMultipleOf({ + check: "multiple_of", + ...normalizeParams(params), + value + }); +} +function _maxLength(maximum, params) { + const ch = new $ZodCheckMaxLength({ + check: "max_length", + ...normalizeParams(params), + maximum + }); + return ch; +} +function _minLength(minimum, params) { + return new $ZodCheckMinLength({ + check: "min_length", + ...normalizeParams(params), + minimum + }); +} +function _length(length, params) { + return new $ZodCheckLengthEquals({ + check: "length_equals", + ...normalizeParams(params), + length + }); +} +function _regex(pattern, params) { + return new $ZodCheckRegex({ + check: "string_format", + format: "regex", + ...normalizeParams(params), + pattern + }); +} +function _lowercase(params) { + return new $ZodCheckLowerCase({ + check: "string_format", + format: "lowercase", + ...normalizeParams(params) + }); +} +function _uppercase(params) { + return new $ZodCheckUpperCase({ + check: "string_format", + format: "uppercase", + ...normalizeParams(params) + }); +} +function _includes(includes, params) { + return new $ZodCheckIncludes({ + check: "string_format", + format: "includes", + ...normalizeParams(params), + includes + }); +} +function _startsWith(prefix, params) { + return new $ZodCheckStartsWith({ + check: "string_format", + format: "starts_with", + ...normalizeParams(params), + prefix + }); +} +function _endsWith(suffix, params) { + return new $ZodCheckEndsWith({ + check: "string_format", + format: "ends_with", + ...normalizeParams(params), + suffix + }); +} +function _overwrite(tx) { + return new $ZodCheckOverwrite({ + check: "overwrite", + tx + }); +} +function _normalize(form) { + return _overwrite((input) => input.normalize(form)); +} +function _trim() { + return _overwrite((input) => input.trim()); +} +function _toLowerCase() { + return _overwrite((input) => input.toLowerCase()); +} +function _toUpperCase() { + return _overwrite((input) => input.toUpperCase()); +} +function _array(Class2, element, params) { + return new Class2({ + type: "array", + element, + // get element() { + // return element; + // }, + ...normalizeParams(params) + }); +} +function _custom(Class2, fn2, _params) { + const norm = normalizeParams(_params); + norm.abort ?? (norm.abort = true); + const schema = new Class2({ + type: "custom", + check: "custom", + fn: fn2, + ...norm + }); + return schema; +} +function _refine(Class2, fn2, _params) { + const schema = new Class2({ + type: "custom", + check: "custom", + fn: fn2, + ...normalizeParams(_params) + }); + return schema; +} + +// node_modules/zod/v4/core/to-json-schema.js +var JSONSchemaGenerator = class { + constructor(params) { + this.counter = 0; + this.metadataRegistry = params?.metadata ?? globalRegistry; + this.target = params?.target ?? "draft-2020-12"; + this.unrepresentable = params?.unrepresentable ?? "throw"; + this.override = params?.override ?? (() => { + }); + this.io = params?.io ?? "output"; + this.seen = /* @__PURE__ */ new Map(); + } + process(schema, _params = { path: [], schemaPath: [] }) { + var _a; + const def = schema._zod.def; + const formatMap = { + guid: "uuid", + url: "uri", + datetime: "date-time", + json_string: "json-string", + regex: "" + // do not set + }; + const seen = this.seen.get(schema); + if (seen) { + seen.count++; + const isCycle = _params.schemaPath.includes(schema); + if (isCycle) { + seen.cycle = _params.path; + } + return seen.schema; + } + const result = { schema: {}, count: 1, cycle: void 0, path: _params.path }; + this.seen.set(schema, result); + const overrideSchema = schema._zod.toJSONSchema?.(); + if (overrideSchema) { + result.schema = overrideSchema; + } else { + const params = { + ..._params, + schemaPath: [..._params.schemaPath, schema], + path: _params.path + }; + const parent = schema._zod.parent; + if (parent) { + result.ref = parent; + this.process(parent, params); + this.seen.get(parent).isParent = true; + } else { + const _json = result.schema; + switch (def.type) { + case "string": { + const json = _json; + json.type = "string"; + const { minimum, maximum, format, patterns, contentEncoding } = schema._zod.bag; + if (typeof minimum === "number") + json.minLength = minimum; + if (typeof maximum === "number") + json.maxLength = maximum; + if (format) { + json.format = formatMap[format] ?? format; + if (json.format === "") + delete json.format; + } + if (contentEncoding) + json.contentEncoding = contentEncoding; + if (patterns && patterns.size > 0) { + const regexes = [...patterns]; + if (regexes.length === 1) + json.pattern = regexes[0].source; + else if (regexes.length > 1) { + result.schema.allOf = [ + ...regexes.map((regex) => ({ + ...this.target === "draft-7" ? { type: "string" } : {}, + pattern: regex.source + })) + ]; + } + } + break; + } + case "number": { + const json = _json; + const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag; + if (typeof format === "string" && format.includes("int")) + json.type = "integer"; + else + json.type = "number"; + if (typeof exclusiveMinimum === "number") + json.exclusiveMinimum = exclusiveMinimum; + if (typeof minimum === "number") { + json.minimum = minimum; + if (typeof exclusiveMinimum === "number") { + if (exclusiveMinimum >= minimum) + delete json.minimum; + else + delete json.exclusiveMinimum; + } + } + if (typeof exclusiveMaximum === "number") + json.exclusiveMaximum = exclusiveMaximum; + if (typeof maximum === "number") { + json.maximum = maximum; + if (typeof exclusiveMaximum === "number") { + if (exclusiveMaximum <= maximum) + delete json.maximum; + else + delete json.exclusiveMaximum; + } + } + if (typeof multipleOf === "number") + json.multipleOf = multipleOf; + break; + } + case "boolean": { + const json = _json; + json.type = "boolean"; + break; + } + case "bigint": { + if (this.unrepresentable === "throw") { + throw new Error("BigInt cannot be represented in JSON Schema"); + } + break; + } + case "symbol": { + if (this.unrepresentable === "throw") { + throw new Error("Symbols cannot be represented in JSON Schema"); + } + break; + } + case "null": { + _json.type = "null"; + break; + } + case "any": { + break; + } + case "unknown": { + break; + } + case "undefined": { + if (this.unrepresentable === "throw") { + throw new Error("Undefined cannot be represented in JSON Schema"); + } + break; + } + case "void": { + if (this.unrepresentable === "throw") { + throw new Error("Void cannot be represented in JSON Schema"); + } + break; + } + case "never": { + _json.not = {}; + break; + } + case "date": { + if (this.unrepresentable === "throw") { + throw new Error("Date cannot be represented in JSON Schema"); + } + break; + } + case "array": { + const json = _json; + const { minimum, maximum } = schema._zod.bag; + if (typeof minimum === "number") + json.minItems = minimum; + if (typeof maximum === "number") + json.maxItems = maximum; + json.type = "array"; + json.items = this.process(def.element, { ...params, path: [...params.path, "items"] }); + break; + } + case "object": { + const json = _json; + json.type = "object"; + json.properties = {}; + const shape = def.shape; + for (const key in shape) { + json.properties[key] = this.process(shape[key], { + ...params, + path: [...params.path, "properties", key] + }); + } + const allKeys = new Set(Object.keys(shape)); + const requiredKeys = new Set([...allKeys].filter((key) => { + const v = def.shape[key]._zod; + if (this.io === "input") { + return v.optin === void 0; + } else { + return v.optout === void 0; + } + })); + if (requiredKeys.size > 0) { + json.required = Array.from(requiredKeys); + } + if (def.catchall?._zod.def.type === "never") { + json.additionalProperties = false; + } else if (!def.catchall) { + if (this.io === "output") + json.additionalProperties = false; + } else if (def.catchall) { + json.additionalProperties = this.process(def.catchall, { + ...params, + path: [...params.path, "additionalProperties"] + }); + } + break; + } + case "union": { + const json = _json; + json.anyOf = def.options.map((x2, i) => this.process(x2, { + ...params, + path: [...params.path, "anyOf", i] + })); + break; + } + case "intersection": { + const json = _json; + const a2 = this.process(def.left, { + ...params, + path: [...params.path, "allOf", 0] + }); + const b2 = this.process(def.right, { + ...params, + path: [...params.path, "allOf", 1] + }); + const isSimpleIntersection = (val) => "allOf" in val && Object.keys(val).length === 1; + const allOf = [ + ...isSimpleIntersection(a2) ? a2.allOf : [a2], + ...isSimpleIntersection(b2) ? b2.allOf : [b2] + ]; + json.allOf = allOf; + break; + } + case "tuple": { + const json = _json; + json.type = "array"; + const prefixItems = def.items.map((x2, i) => this.process(x2, { ...params, path: [...params.path, "prefixItems", i] })); + if (this.target === "draft-2020-12") { + json.prefixItems = prefixItems; + } else { + json.items = prefixItems; + } + if (def.rest) { + const rest = this.process(def.rest, { + ...params, + path: [...params.path, "items"] + }); + if (this.target === "draft-2020-12") { + json.items = rest; + } else { + json.additionalItems = rest; + } + } + if (def.rest) { + json.items = this.process(def.rest, { + ...params, + path: [...params.path, "items"] + }); + } + const { minimum, maximum } = schema._zod.bag; + if (typeof minimum === "number") + json.minItems = minimum; + if (typeof maximum === "number") + json.maxItems = maximum; + break; + } + case "record": { + const json = _json; + json.type = "object"; + json.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] }); + json.additionalProperties = this.process(def.valueType, { + ...params, + path: [...params.path, "additionalProperties"] + }); + break; + } + case "map": { + if (this.unrepresentable === "throw") { + throw new Error("Map cannot be represented in JSON Schema"); + } + break; + } + case "set": { + if (this.unrepresentable === "throw") { + throw new Error("Set cannot be represented in JSON Schema"); + } + break; + } + case "enum": { + const json = _json; + const values = getEnumValues(def.entries); + if (values.every((v) => typeof v === "number")) + json.type = "number"; + if (values.every((v) => typeof v === "string")) + json.type = "string"; + json.enum = values; + break; + } + case "literal": { + const json = _json; + const vals = []; + for (const val of def.values) { + if (val === void 0) { + if (this.unrepresentable === "throw") { + throw new Error("Literal `undefined` cannot be represented in JSON Schema"); + } else { + } + } else if (typeof val === "bigint") { + if (this.unrepresentable === "throw") { + throw new Error("BigInt literals cannot be represented in JSON Schema"); + } else { + vals.push(Number(val)); + } + } else { + vals.push(val); + } + } + if (vals.length === 0) { + } else if (vals.length === 1) { + const val = vals[0]; + json.type = val === null ? "null" : typeof val; + json.const = val; + } else { + if (vals.every((v) => typeof v === "number")) + json.type = "number"; + if (vals.every((v) => typeof v === "string")) + json.type = "string"; + if (vals.every((v) => typeof v === "boolean")) + json.type = "string"; + if (vals.every((v) => v === null)) + json.type = "null"; + json.enum = vals; + } + break; + } + case "file": { + const json = _json; + const file = { + type: "string", + format: "binary", + contentEncoding: "binary" + }; + const { minimum, maximum, mime } = schema._zod.bag; + if (minimum !== void 0) + file.minLength = minimum; + if (maximum !== void 0) + file.maxLength = maximum; + if (mime) { + if (mime.length === 1) { + file.contentMediaType = mime[0]; + Object.assign(json, file); + } else { + json.anyOf = mime.map((m2) => { + const mFile = { ...file, contentMediaType: m2 }; + return mFile; + }); + } + } else { + Object.assign(json, file); + } + break; + } + case "transform": { + if (this.unrepresentable === "throw") { + throw new Error("Transforms cannot be represented in JSON Schema"); + } + break; + } + case "nullable": { + const inner = this.process(def.innerType, params); + _json.anyOf = [inner, { type: "null" }]; + break; + } + case "nonoptional": { + this.process(def.innerType, params); + result.ref = def.innerType; + break; + } + case "success": { + const json = _json; + json.type = "boolean"; + break; + } + case "default": { + this.process(def.innerType, params); + result.ref = def.innerType; + _json.default = JSON.parse(JSON.stringify(def.defaultValue)); + break; + } + case "prefault": { + this.process(def.innerType, params); + result.ref = def.innerType; + if (this.io === "input") + _json._prefault = JSON.parse(JSON.stringify(def.defaultValue)); + break; + } + case "catch": { + this.process(def.innerType, params); + result.ref = def.innerType; + let catchValue; + try { + catchValue = def.catchValue(void 0); + } catch { + throw new Error("Dynamic catch values are not supported in JSON Schema"); + } + _json.default = catchValue; + break; + } + case "nan": { + if (this.unrepresentable === "throw") { + throw new Error("NaN cannot be represented in JSON Schema"); + } + break; + } + case "template_literal": { + const json = _json; + const pattern = schema._zod.pattern; + if (!pattern) + throw new Error("Pattern not found in template literal"); + json.type = "string"; + json.pattern = pattern.source; + break; + } + case "pipe": { + const innerType = this.io === "input" ? def.in._zod.def.type === "transform" ? def.out : def.in : def.out; + this.process(innerType, params); + result.ref = innerType; + break; + } + case "readonly": { + this.process(def.innerType, params); + result.ref = def.innerType; + _json.readOnly = true; + break; + } + // passthrough types + case "promise": { + this.process(def.innerType, params); + result.ref = def.innerType; + break; + } + case "optional": { + this.process(def.innerType, params); + result.ref = def.innerType; + break; + } + case "lazy": { + const innerType = schema._zod.innerType; + this.process(innerType, params); + result.ref = innerType; + break; + } + case "custom": { + if (this.unrepresentable === "throw") { + throw new Error("Custom types cannot be represented in JSON Schema"); + } + break; + } + default: { + def; + } + } + } + } + const meta = this.metadataRegistry.get(schema); + if (meta) + Object.assign(result.schema, meta); + if (this.io === "input" && isTransforming(schema)) { + delete result.schema.examples; + delete result.schema.default; + } + if (this.io === "input" && result.schema._prefault) + (_a = result.schema).default ?? (_a.default = result.schema._prefault); + delete result.schema._prefault; + const _result = this.seen.get(schema); + return _result.schema; + } + emit(schema, _params) { + const params = { + cycles: _params?.cycles ?? "ref", + reused: _params?.reused ?? "inline", + // unrepresentable: _params?.unrepresentable ?? "throw", + // uri: _params?.uri ?? ((id) => `${id}`), + external: _params?.external ?? void 0 + }; + const root = this.seen.get(schema); + if (!root) + throw new Error("Unprocessed schema. This is a bug in Zod."); + const makeURI = (entry) => { + const defsSegment = this.target === "draft-2020-12" ? "$defs" : "definitions"; + if (params.external) { + const externalId = params.external.registry.get(entry[0])?.id; + const uriGenerator = params.external.uri ?? ((id2) => id2); + if (externalId) { + return { ref: uriGenerator(externalId) }; + } + const id = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`; + entry[1].defId = id; + return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` }; + } + if (entry[1] === root) { + return { ref: "#" }; + } + const uriPrefix = `#`; + const defUriPrefix = `${uriPrefix}/${defsSegment}/`; + const defId = entry[1].schema.id ?? `__schema${this.counter++}`; + return { defId, ref: defUriPrefix + defId }; + }; + const extractToDef = (entry) => { + if (entry[1].schema.$ref) { + return; + } + const seen = entry[1]; + const { ref, defId } = makeURI(entry); + seen.def = { ...seen.schema }; + if (defId) + seen.defId = defId; + const schema2 = seen.schema; + for (const key in schema2) { + delete schema2[key]; + } + schema2.$ref = ref; + }; + if (params.cycles === "throw") { + for (const entry of this.seen.entries()) { + const seen = entry[1]; + if (seen.cycle) { + throw new Error(`Cycle detected: #/${seen.cycle?.join("/")}/ + +Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`); + } + } + } + for (const entry of this.seen.entries()) { + const seen = entry[1]; + if (schema === entry[0]) { + extractToDef(entry); + continue; + } + if (params.external) { + const ext = params.external.registry.get(entry[0])?.id; + if (schema !== entry[0] && ext) { + extractToDef(entry); + continue; + } + } + const id = this.metadataRegistry.get(entry[0])?.id; + if (id) { + extractToDef(entry); + continue; + } + if (seen.cycle) { + extractToDef(entry); + continue; + } + if (seen.count > 1) { + if (params.reused === "ref") { + extractToDef(entry); + continue; + } + } + } + const flattenRef = (zodSchema, params2) => { + const seen = this.seen.get(zodSchema); + const schema2 = seen.def ?? seen.schema; + const _cached = { ...schema2 }; + if (seen.ref === null) { + return; + } + const ref = seen.ref; + seen.ref = null; + if (ref) { + flattenRef(ref, params2); + const refSchema = this.seen.get(ref).schema; + if (refSchema.$ref && params2.target === "draft-7") { + schema2.allOf = schema2.allOf ?? []; + schema2.allOf.push(refSchema); + } else { + Object.assign(schema2, refSchema); + Object.assign(schema2, _cached); + } + } + if (!seen.isParent) + this.override({ + zodSchema, + jsonSchema: schema2, + path: seen.path ?? [] + }); + }; + for (const entry of [...this.seen.entries()].reverse()) { + flattenRef(entry[0], { target: this.target }); + } + const result = {}; + if (this.target === "draft-2020-12") { + result.$schema = "https://json-schema.org/draft/2020-12/schema"; + } else if (this.target === "draft-7") { + result.$schema = "http://json-schema.org/draft-07/schema#"; + } else { + console.warn(`Invalid target: ${this.target}`); + } + if (params.external?.uri) { + const id = params.external.registry.get(schema)?.id; + if (!id) + throw new Error("Schema is missing an `id` property"); + result.$id = params.external.uri(id); + } + Object.assign(result, root.def); + const defs = params.external?.defs ?? {}; + for (const entry of this.seen.entries()) { + const seen = entry[1]; + if (seen.def && seen.defId) { + defs[seen.defId] = seen.def; + } + } + if (params.external) { + } else { + if (Object.keys(defs).length > 0) { + if (this.target === "draft-2020-12") { + result.$defs = defs; + } else { + result.definitions = defs; + } + } + } + try { + return JSON.parse(JSON.stringify(result)); + } catch (_err) { + throw new Error("Error converting schema to JSON."); + } + } +}; +function toJSONSchema(input, _params) { + if (input instanceof $ZodRegistry) { + const gen2 = new JSONSchemaGenerator(_params); + const defs = {}; + for (const entry of input._idmap.entries()) { + const [_, schema] = entry; + gen2.process(schema); + } + const schemas = {}; + const external = { + registry: input, + uri: _params?.uri, + defs + }; + for (const entry of input._idmap.entries()) { + const [key, schema] = entry; + schemas[key] = gen2.emit(schema, { + ..._params, + external + }); + } + if (Object.keys(defs).length > 0) { + const defsSegment = gen2.target === "draft-2020-12" ? "$defs" : "definitions"; + schemas.__shared = { + [defsSegment]: defs + }; + } + return { schemas }; + } + const gen = new JSONSchemaGenerator(_params); + gen.process(input); + return gen.emit(input, _params); +} +function isTransforming(_schema, _ctx) { + const ctx = _ctx ?? { seen: /* @__PURE__ */ new Set() }; + if (ctx.seen.has(_schema)) + return false; + ctx.seen.add(_schema); + const schema = _schema; + const def = schema._zod.def; + switch (def.type) { + case "string": + case "number": + case "bigint": + case "boolean": + case "date": + case "symbol": + case "undefined": + case "null": + case "any": + case "unknown": + case "never": + case "void": + case "literal": + case "enum": + case "nan": + case "file": + case "template_literal": + return false; + case "array": { + return isTransforming(def.element, ctx); + } + case "object": { + for (const key in def.shape) { + if (isTransforming(def.shape[key], ctx)) + return true; + } + return false; + } + case "union": { + for (const option of def.options) { + if (isTransforming(option, ctx)) + return true; + } + return false; + } + case "intersection": { + return isTransforming(def.left, ctx) || isTransforming(def.right, ctx); + } + case "tuple": { + for (const item of def.items) { + if (isTransforming(item, ctx)) + return true; + } + if (def.rest && isTransforming(def.rest, ctx)) + return true; + return false; + } + case "record": { + return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx); + } + case "map": { + return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx); + } + case "set": { + return isTransforming(def.valueType, ctx); + } + // inner types + case "promise": + case "optional": + case "nonoptional": + case "nullable": + case "readonly": + return isTransforming(def.innerType, ctx); + case "lazy": + return isTransforming(def.getter(), ctx); + case "default": { + return isTransforming(def.innerType, ctx); + } + case "prefault": { + return isTransforming(def.innerType, ctx); + } + case "custom": { + return false; + } + case "transform": { + return true; + } + case "pipe": { + return isTransforming(def.in, ctx) || isTransforming(def.out, ctx); + } + case "success": { + return false; + } + case "catch": { + return false; + } + default: + def; + } + throw new Error(`Unknown schema type: ${def.type}`); +} + +// node_modules/zod/v4/classic/iso.js +var iso_exports = {}; +__export(iso_exports, { + ZodISODate: () => ZodISODate, + ZodISODateTime: () => ZodISODateTime, + ZodISODuration: () => ZodISODuration, + ZodISOTime: () => ZodISOTime, + date: () => date2, + datetime: () => datetime2, + duration: () => duration2, + time: () => time2 +}); +var ZodISODateTime = /* @__PURE__ */ $constructor("ZodISODateTime", (inst, def) => { + $ZodISODateTime.init(inst, def); + ZodStringFormat.init(inst, def); +}); +function datetime2(params) { + return _isoDateTime(ZodISODateTime, params); +} +var ZodISODate = /* @__PURE__ */ $constructor("ZodISODate", (inst, def) => { + $ZodISODate.init(inst, def); + ZodStringFormat.init(inst, def); +}); +function date2(params) { + return _isoDate(ZodISODate, params); +} +var ZodISOTime = /* @__PURE__ */ $constructor("ZodISOTime", (inst, def) => { + $ZodISOTime.init(inst, def); + ZodStringFormat.init(inst, def); +}); +function time2(params) { + return _isoTime(ZodISOTime, params); +} +var ZodISODuration = /* @__PURE__ */ $constructor("ZodISODuration", (inst, def) => { + $ZodISODuration.init(inst, def); + ZodStringFormat.init(inst, def); +}); +function duration2(params) { + return _isoDuration(ZodISODuration, params); +} + +// node_modules/zod/v4/classic/errors.js +var initializer2 = (inst, issues) => { + $ZodError.init(inst, issues); + inst.name = "ZodError"; + Object.defineProperties(inst, { + format: { + value: (mapper) => formatError(inst, mapper) + // enumerable: false, + }, + flatten: { + value: (mapper) => flattenError(inst, mapper) + // enumerable: false, + }, + addIssue: { + value: (issue2) => inst.issues.push(issue2) + // enumerable: false, + }, + addIssues: { + value: (issues2) => inst.issues.push(...issues2) + // enumerable: false, + }, + isEmpty: { + get() { + return inst.issues.length === 0; + } + // enumerable: false, + } + }); +}; +var ZodError = $constructor("ZodError", initializer2); +var ZodRealError = $constructor("ZodError", initializer2, { + Parent: Error +}); + +// node_modules/zod/v4/classic/parse.js +var parse2 = /* @__PURE__ */ _parse(ZodRealError); +var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError); +var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError); +var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError); + +// node_modules/zod/v4/classic/schemas.js +var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => { + $ZodType.init(inst, def); + inst.def = def; + Object.defineProperty(inst, "_def", { value: def }); + inst.check = (...checks) => { + return inst.clone( + { + ...def, + checks: [ + ...def.checks ?? [], + ...checks.map((ch) => typeof ch === "function" ? { _zod: { check: ch, def: { check: "custom" }, onattach: [] } } : ch) + ] + } + // { parent: true } + ); + }; + inst.clone = (def2, params) => clone(inst, def2, params); + inst.brand = () => inst; + inst.register = ((reg, meta) => { + reg.add(inst, meta); + return inst; + }); + inst.parse = (data, params) => parse2(inst, data, params, { callee: inst.parse }); + inst.safeParse = (data, params) => safeParse2(inst, data, params); + inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync }); + inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params); + inst.spa = inst.safeParseAsync; + inst.refine = (check2, params) => inst.check(refine(check2, params)); + inst.superRefine = (refinement) => inst.check(superRefine(refinement)); + inst.overwrite = (fn2) => inst.check(_overwrite(fn2)); + inst.optional = () => optional(inst); + inst.nullable = () => nullable(inst); + inst.nullish = () => optional(nullable(inst)); + inst.nonoptional = (params) => nonoptional(inst, params); + inst.array = () => array(inst); + inst.or = (arg) => union([inst, arg]); + inst.and = (arg) => intersection(inst, arg); + inst.transform = (tx) => pipe(inst, transform(tx)); + inst.default = (def2) => _default(inst, def2); + inst.prefault = (def2) => prefault(inst, def2); + inst.catch = (params) => _catch(inst, params); + inst.pipe = (target) => pipe(inst, target); + inst.readonly = () => readonly(inst); + inst.describe = (description) => { + const cl2 = inst.clone(); + globalRegistry.add(cl2, { description }); + return cl2; + }; + Object.defineProperty(inst, "description", { + get() { + return globalRegistry.get(inst)?.description; + }, + configurable: true + }); + inst.meta = (...args) => { + if (args.length === 0) { + return globalRegistry.get(inst); + } + const cl2 = inst.clone(); + globalRegistry.add(cl2, args[0]); + return cl2; + }; + inst.isOptional = () => inst.safeParse(void 0).success; + inst.isNullable = () => inst.safeParse(null).success; + return inst; +}); +var _ZodString = /* @__PURE__ */ $constructor("_ZodString", (inst, def) => { + $ZodString.init(inst, def); + ZodType.init(inst, def); + const bag = inst._zod.bag; + inst.format = bag.format ?? null; + inst.minLength = bag.minimum ?? null; + inst.maxLength = bag.maximum ?? null; + inst.regex = (...args) => inst.check(_regex(...args)); + inst.includes = (...args) => inst.check(_includes(...args)); + inst.startsWith = (...args) => inst.check(_startsWith(...args)); + inst.endsWith = (...args) => inst.check(_endsWith(...args)); + inst.min = (...args) => inst.check(_minLength(...args)); + inst.max = (...args) => inst.check(_maxLength(...args)); + inst.length = (...args) => inst.check(_length(...args)); + inst.nonempty = (...args) => inst.check(_minLength(1, ...args)); + inst.lowercase = (params) => inst.check(_lowercase(params)); + inst.uppercase = (params) => inst.check(_uppercase(params)); + inst.trim = () => inst.check(_trim()); + inst.normalize = (...args) => inst.check(_normalize(...args)); + inst.toLowerCase = () => inst.check(_toLowerCase()); + inst.toUpperCase = () => inst.check(_toUpperCase()); +}); +var ZodString = /* @__PURE__ */ $constructor("ZodString", (inst, def) => { + $ZodString.init(inst, def); + _ZodString.init(inst, def); + inst.email = (params) => inst.check(_email(ZodEmail, params)); + inst.url = (params) => inst.check(_url(ZodURL, params)); + inst.jwt = (params) => inst.check(_jwt(ZodJWT, params)); + inst.emoji = (params) => inst.check(_emoji2(ZodEmoji, params)); + inst.guid = (params) => inst.check(_guid(ZodGUID, params)); + inst.uuid = (params) => inst.check(_uuid(ZodUUID, params)); + inst.uuidv4 = (params) => inst.check(_uuidv4(ZodUUID, params)); + inst.uuidv6 = (params) => inst.check(_uuidv6(ZodUUID, params)); + inst.uuidv7 = (params) => inst.check(_uuidv7(ZodUUID, params)); + inst.nanoid = (params) => inst.check(_nanoid(ZodNanoID, params)); + inst.guid = (params) => inst.check(_guid(ZodGUID, params)); + inst.cuid = (params) => inst.check(_cuid(ZodCUID, params)); + inst.cuid2 = (params) => inst.check(_cuid2(ZodCUID2, params)); + inst.ulid = (params) => inst.check(_ulid(ZodULID, params)); + inst.base64 = (params) => inst.check(_base64(ZodBase64, params)); + inst.base64url = (params) => inst.check(_base64url(ZodBase64URL, params)); + inst.xid = (params) => inst.check(_xid(ZodXID, params)); + inst.ksuid = (params) => inst.check(_ksuid(ZodKSUID, params)); + inst.ipv4 = (params) => inst.check(_ipv4(ZodIPv4, params)); + inst.ipv6 = (params) => inst.check(_ipv6(ZodIPv6, params)); + inst.cidrv4 = (params) => inst.check(_cidrv4(ZodCIDRv4, params)); + inst.cidrv6 = (params) => inst.check(_cidrv6(ZodCIDRv6, params)); + inst.e164 = (params) => inst.check(_e164(ZodE164, params)); + inst.datetime = (params) => inst.check(datetime2(params)); + inst.date = (params) => inst.check(date2(params)); + inst.time = (params) => inst.check(time2(params)); + inst.duration = (params) => inst.check(duration2(params)); +}); +function string2(params) { + return _string(ZodString, params); +} +var ZodStringFormat = /* @__PURE__ */ $constructor("ZodStringFormat", (inst, def) => { + $ZodStringFormat.init(inst, def); + _ZodString.init(inst, def); +}); +var ZodEmail = /* @__PURE__ */ $constructor("ZodEmail", (inst, def) => { + $ZodEmail.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodGUID = /* @__PURE__ */ $constructor("ZodGUID", (inst, def) => { + $ZodGUID.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodUUID = /* @__PURE__ */ $constructor("ZodUUID", (inst, def) => { + $ZodUUID.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodURL = /* @__PURE__ */ $constructor("ZodURL", (inst, def) => { + $ZodURL.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodEmoji = /* @__PURE__ */ $constructor("ZodEmoji", (inst, def) => { + $ZodEmoji.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodNanoID = /* @__PURE__ */ $constructor("ZodNanoID", (inst, def) => { + $ZodNanoID.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodCUID = /* @__PURE__ */ $constructor("ZodCUID", (inst, def) => { + $ZodCUID.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodCUID2 = /* @__PURE__ */ $constructor("ZodCUID2", (inst, def) => { + $ZodCUID2.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodULID = /* @__PURE__ */ $constructor("ZodULID", (inst, def) => { + $ZodULID.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodXID = /* @__PURE__ */ $constructor("ZodXID", (inst, def) => { + $ZodXID.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodKSUID = /* @__PURE__ */ $constructor("ZodKSUID", (inst, def) => { + $ZodKSUID.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodIPv4 = /* @__PURE__ */ $constructor("ZodIPv4", (inst, def) => { + $ZodIPv4.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodIPv6 = /* @__PURE__ */ $constructor("ZodIPv6", (inst, def) => { + $ZodIPv6.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodCIDRv4 = /* @__PURE__ */ $constructor("ZodCIDRv4", (inst, def) => { + $ZodCIDRv4.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodCIDRv6 = /* @__PURE__ */ $constructor("ZodCIDRv6", (inst, def) => { + $ZodCIDRv6.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodBase64 = /* @__PURE__ */ $constructor("ZodBase64", (inst, def) => { + $ZodBase64.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodBase64URL = /* @__PURE__ */ $constructor("ZodBase64URL", (inst, def) => { + $ZodBase64URL.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodE164 = /* @__PURE__ */ $constructor("ZodE164", (inst, def) => { + $ZodE164.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodJWT = /* @__PURE__ */ $constructor("ZodJWT", (inst, def) => { + $ZodJWT.init(inst, def); + ZodStringFormat.init(inst, def); +}); +var ZodNumber = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => { + $ZodNumber.init(inst, def); + ZodType.init(inst, def); + inst.gt = (value, params) => inst.check(_gt(value, params)); + inst.gte = (value, params) => inst.check(_gte(value, params)); + inst.min = (value, params) => inst.check(_gte(value, params)); + inst.lt = (value, params) => inst.check(_lt(value, params)); + inst.lte = (value, params) => inst.check(_lte(value, params)); + inst.max = (value, params) => inst.check(_lte(value, params)); + inst.int = (params) => inst.check(int(params)); + inst.safe = (params) => inst.check(int(params)); + inst.positive = (params) => inst.check(_gt(0, params)); + inst.nonnegative = (params) => inst.check(_gte(0, params)); + inst.negative = (params) => inst.check(_lt(0, params)); + inst.nonpositive = (params) => inst.check(_lte(0, params)); + inst.multipleOf = (value, params) => inst.check(_multipleOf(value, params)); + inst.step = (value, params) => inst.check(_multipleOf(value, params)); + inst.finite = () => inst; + const bag = inst._zod.bag; + inst.minValue = Math.max(bag.minimum ?? Number.NEGATIVE_INFINITY, bag.exclusiveMinimum ?? Number.NEGATIVE_INFINITY) ?? null; + inst.maxValue = Math.min(bag.maximum ?? Number.POSITIVE_INFINITY, bag.exclusiveMaximum ?? Number.POSITIVE_INFINITY) ?? null; + inst.isInt = (bag.format ?? "").includes("int") || Number.isSafeInteger(bag.multipleOf ?? 0.5); + inst.isFinite = true; + inst.format = bag.format ?? null; +}); +function number2(params) { + return _number(ZodNumber, params); +} +var ZodNumberFormat = /* @__PURE__ */ $constructor("ZodNumberFormat", (inst, def) => { + $ZodNumberFormat.init(inst, def); + ZodNumber.init(inst, def); +}); +function int(params) { + return _int(ZodNumberFormat, params); +} +var ZodBoolean = /* @__PURE__ */ $constructor("ZodBoolean", (inst, def) => { + $ZodBoolean.init(inst, def); + ZodType.init(inst, def); +}); +function boolean2(params) { + return _boolean(ZodBoolean, params); +} +var ZodNull = /* @__PURE__ */ $constructor("ZodNull", (inst, def) => { + $ZodNull.init(inst, def); + ZodType.init(inst, def); +}); +function _null3(params) { + return _null2(ZodNull, params); +} +var ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => { + $ZodUnknown.init(inst, def); + ZodType.init(inst, def); +}); +function unknown() { + return _unknown(ZodUnknown); +} +var ZodNever = /* @__PURE__ */ $constructor("ZodNever", (inst, def) => { + $ZodNever.init(inst, def); + ZodType.init(inst, def); +}); +function never(params) { + return _never(ZodNever, params); +} +var ZodArray = /* @__PURE__ */ $constructor("ZodArray", (inst, def) => { + $ZodArray.init(inst, def); + ZodType.init(inst, def); + inst.element = def.element; + inst.min = (minLength, params) => inst.check(_minLength(minLength, params)); + inst.nonempty = (params) => inst.check(_minLength(1, params)); + inst.max = (maxLength, params) => inst.check(_maxLength(maxLength, params)); + inst.length = (len, params) => inst.check(_length(len, params)); + inst.unwrap = () => inst.element; +}); +function array(element, params) { + return _array(ZodArray, element, params); +} +var ZodObject = /* @__PURE__ */ $constructor("ZodObject", (inst, def) => { + $ZodObject.init(inst, def); + ZodType.init(inst, def); + util_exports.defineLazy(inst, "shape", () => def.shape); + inst.keyof = () => _enum(Object.keys(inst._zod.def.shape)); + inst.catchall = (catchall) => inst.clone({ ...inst._zod.def, catchall }); + inst.passthrough = () => inst.clone({ ...inst._zod.def, catchall: unknown() }); + inst.loose = () => inst.clone({ ...inst._zod.def, catchall: unknown() }); + inst.strict = () => inst.clone({ ...inst._zod.def, catchall: never() }); + inst.strip = () => inst.clone({ ...inst._zod.def, catchall: void 0 }); + inst.extend = (incoming) => { + return util_exports.extend(inst, incoming); + }; + inst.merge = (other) => util_exports.merge(inst, other); + inst.pick = (mask) => util_exports.pick(inst, mask); + inst.omit = (mask) => util_exports.omit(inst, mask); + inst.partial = (...args) => util_exports.partial(ZodOptional, inst, args[0]); + inst.required = (...args) => util_exports.required(ZodNonOptional, inst, args[0]); +}); +function object(shape, params) { + const def = { + type: "object", + get shape() { + util_exports.assignProp(this, "shape", { ...shape }); + return this.shape; + }, + ...util_exports.normalizeParams(params) + }; + return new ZodObject(def); +} +function looseObject(shape, params) { + return new ZodObject({ + type: "object", + get shape() { + util_exports.assignProp(this, "shape", { ...shape }); + return this.shape; + }, + catchall: unknown(), + ...util_exports.normalizeParams(params) + }); +} +var ZodUnion = /* @__PURE__ */ $constructor("ZodUnion", (inst, def) => { + $ZodUnion.init(inst, def); + ZodType.init(inst, def); + inst.options = def.options; +}); +function union(options, params) { + return new ZodUnion({ + type: "union", + options, + ...util_exports.normalizeParams(params) + }); +} +var ZodDiscriminatedUnion = /* @__PURE__ */ $constructor("ZodDiscriminatedUnion", (inst, def) => { + ZodUnion.init(inst, def); + $ZodDiscriminatedUnion.init(inst, def); +}); +function discriminatedUnion(discriminator, options, params) { + return new ZodDiscriminatedUnion({ + type: "union", + options, + discriminator, + ...util_exports.normalizeParams(params) + }); +} +var ZodIntersection = /* @__PURE__ */ $constructor("ZodIntersection", (inst, def) => { + $ZodIntersection.init(inst, def); + ZodType.init(inst, def); +}); +function intersection(left, right) { + return new ZodIntersection({ + type: "intersection", + left, + right + }); +} +var ZodRecord = /* @__PURE__ */ $constructor("ZodRecord", (inst, def) => { + $ZodRecord.init(inst, def); + ZodType.init(inst, def); + inst.keyType = def.keyType; + inst.valueType = def.valueType; +}); +function record(keyType, valueType, params) { + return new ZodRecord({ + type: "record", + keyType, + valueType, + ...util_exports.normalizeParams(params) + }); +} +var ZodEnum = /* @__PURE__ */ $constructor("ZodEnum", (inst, def) => { + $ZodEnum.init(inst, def); + ZodType.init(inst, def); + inst.enum = def.entries; + inst.options = Object.values(def.entries); + const keys = new Set(Object.keys(def.entries)); + inst.extract = (values, params) => { + const newEntries = {}; + for (const value of values) { + if (keys.has(value)) { + newEntries[value] = def.entries[value]; + } else + throw new Error(`Key ${value} not found in enum`); + } + return new ZodEnum({ + ...def, + checks: [], + ...util_exports.normalizeParams(params), + entries: newEntries + }); + }; + inst.exclude = (values, params) => { + const newEntries = { ...def.entries }; + for (const value of values) { + if (keys.has(value)) { + delete newEntries[value]; + } else + throw new Error(`Key ${value} not found in enum`); + } + return new ZodEnum({ + ...def, + checks: [], + ...util_exports.normalizeParams(params), + entries: newEntries + }); + }; +}); +function _enum(values, params) { + const entries = Array.isArray(values) ? Object.fromEntries(values.map((v) => [v, v])) : values; + return new ZodEnum({ + type: "enum", + entries, + ...util_exports.normalizeParams(params) + }); +} +var ZodLiteral = /* @__PURE__ */ $constructor("ZodLiteral", (inst, def) => { + $ZodLiteral.init(inst, def); + ZodType.init(inst, def); + inst.values = new Set(def.values); + Object.defineProperty(inst, "value", { + get() { + if (def.values.length > 1) { + throw new Error("This schema contains multiple valid literal values. Use `.values` instead."); + } + return def.values[0]; + } + }); +}); +function literal(value, params) { + return new ZodLiteral({ + type: "literal", + values: Array.isArray(value) ? value : [value], + ...util_exports.normalizeParams(params) + }); +} +var ZodTransform = /* @__PURE__ */ $constructor("ZodTransform", (inst, def) => { + $ZodTransform.init(inst, def); + ZodType.init(inst, def); + inst._zod.parse = (payload, _ctx) => { + payload.addIssue = (issue2) => { + if (typeof issue2 === "string") { + payload.issues.push(util_exports.issue(issue2, payload.value, def)); + } else { + const _issue = issue2; + if (_issue.fatal) + _issue.continue = false; + _issue.code ?? (_issue.code = "custom"); + _issue.input ?? (_issue.input = payload.value); + _issue.inst ?? (_issue.inst = inst); + _issue.continue ?? (_issue.continue = true); + payload.issues.push(util_exports.issue(_issue)); + } + }; + const output = def.transform(payload.value, payload); + if (output instanceof Promise) { + return output.then((output2) => { + payload.value = output2; + return payload; + }); + } + payload.value = output; + return payload; + }; +}); +function transform(fn2) { + return new ZodTransform({ + type: "transform", + transform: fn2 + }); +} +var ZodOptional = /* @__PURE__ */ $constructor("ZodOptional", (inst, def) => { + $ZodOptional.init(inst, def); + ZodType.init(inst, def); + inst.unwrap = () => inst._zod.def.innerType; +}); +function optional(innerType) { + return new ZodOptional({ + type: "optional", + innerType + }); +} +var ZodNullable = /* @__PURE__ */ $constructor("ZodNullable", (inst, def) => { + $ZodNullable.init(inst, def); + ZodType.init(inst, def); + inst.unwrap = () => inst._zod.def.innerType; +}); +function nullable(innerType) { + return new ZodNullable({ + type: "nullable", + innerType + }); +} +var ZodDefault = /* @__PURE__ */ $constructor("ZodDefault", (inst, def) => { + $ZodDefault.init(inst, def); + ZodType.init(inst, def); + inst.unwrap = () => inst._zod.def.innerType; + inst.removeDefault = inst.unwrap; +}); +function _default(innerType, defaultValue) { + return new ZodDefault({ + type: "default", + innerType, + get defaultValue() { + return typeof defaultValue === "function" ? defaultValue() : defaultValue; + } + }); +} +var ZodPrefault = /* @__PURE__ */ $constructor("ZodPrefault", (inst, def) => { + $ZodPrefault.init(inst, def); + ZodType.init(inst, def); + inst.unwrap = () => inst._zod.def.innerType; +}); +function prefault(innerType, defaultValue) { + return new ZodPrefault({ + type: "prefault", + innerType, + get defaultValue() { + return typeof defaultValue === "function" ? defaultValue() : defaultValue; + } + }); +} +var ZodNonOptional = /* @__PURE__ */ $constructor("ZodNonOptional", (inst, def) => { + $ZodNonOptional.init(inst, def); + ZodType.init(inst, def); + inst.unwrap = () => inst._zod.def.innerType; +}); +function nonoptional(innerType, params) { + return new ZodNonOptional({ + type: "nonoptional", + innerType, + ...util_exports.normalizeParams(params) + }); +} +var ZodCatch = /* @__PURE__ */ $constructor("ZodCatch", (inst, def) => { + $ZodCatch.init(inst, def); + ZodType.init(inst, def); + inst.unwrap = () => inst._zod.def.innerType; + inst.removeCatch = inst.unwrap; +}); +function _catch(innerType, catchValue) { + return new ZodCatch({ + type: "catch", + innerType, + catchValue: typeof catchValue === "function" ? catchValue : () => catchValue + }); +} +var ZodPipe = /* @__PURE__ */ $constructor("ZodPipe", (inst, def) => { + $ZodPipe.init(inst, def); + ZodType.init(inst, def); + inst.in = def.in; + inst.out = def.out; +}); +function pipe(in_, out) { + return new ZodPipe({ + type: "pipe", + in: in_, + out + // ...util.normalizeParams(params), + }); +} +var ZodReadonly = /* @__PURE__ */ $constructor("ZodReadonly", (inst, def) => { + $ZodReadonly.init(inst, def); + ZodType.init(inst, def); +}); +function readonly(innerType) { + return new ZodReadonly({ + type: "readonly", + innerType + }); +} +var ZodCustom = /* @__PURE__ */ $constructor("ZodCustom", (inst, def) => { + $ZodCustom.init(inst, def); + ZodType.init(inst, def); +}); +function check(fn2) { + const ch = new $ZodCheck({ + check: "custom" + // ...util.normalizeParams(params), + }); + ch._zod.check = fn2; + return ch; +} +function custom(fn2, _params) { + return _custom(ZodCustom, fn2 ?? (() => true), _params); +} +function refine(fn2, _params = {}) { + return _refine(ZodCustom, fn2, _params); +} +function superRefine(fn2) { + const ch = check((payload) => { + payload.addIssue = (issue2) => { + if (typeof issue2 === "string") { + payload.issues.push(util_exports.issue(issue2, payload.value, ch._zod.def)); + } else { + const _issue = issue2; + if (_issue.fatal) + _issue.continue = false; + _issue.code ?? (_issue.code = "custom"); + _issue.input ?? (_issue.input = payload.value); + _issue.inst ?? (_issue.inst = ch); + _issue.continue ?? (_issue.continue = !ch._zod.def.abort); + payload.issues.push(util_exports.issue(_issue)); + } + }; + return fn2(payload.value, payload); + }); + return ch; +} +function preprocess(fn2, schema) { + return pipe(transform(fn2), schema); +} + +// node_modules/zod/v4/classic/external.js +config(en_default()); + +// node_modules/@modelcontextprotocol/sdk/dist/esm/types.js +var LATEST_PROTOCOL_VERSION = "2025-11-25"; +var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"]; +var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task"; +var JSONRPC_VERSION = "2.0"; +var AssertObjectSchema = custom((v) => v !== null && (typeof v === "object" || typeof v === "function")); +var ProgressTokenSchema = union([string2(), number2().int()]); +var CursorSchema = string2(); +var TaskCreationParamsSchema = looseObject({ + /** + * Time in milliseconds to keep task results available after completion. + * If null, the task has unlimited lifetime until manually cleaned up. + */ + ttl: union([number2(), _null3()]).optional(), + /** + * Time in milliseconds to wait between task status requests. + */ + pollInterval: number2().optional() +}); +var TaskMetadataSchema = object({ + ttl: number2().optional() +}); +var RelatedTaskMetadataSchema = object({ + taskId: string2() +}); +var RequestMetaSchema = looseObject({ + /** + * 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. + */ + progressToken: ProgressTokenSchema.optional(), + /** + * If specified, this request is related to the provided task. + */ + [RELATED_TASK_META_KEY]: RelatedTaskMetadataSchema.optional() +}); +var BaseRequestParamsSchema = object({ + /** + * See [General fields: `_meta`](/specification/draft/basic/index#meta) for notes on `_meta` usage. + */ + _meta: RequestMetaSchema.optional() +}); +var TaskAugmentedRequestParamsSchema = BaseRequestParamsSchema.extend({ + /** + * 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: TaskMetadataSchema.optional() +}); +var isTaskAugmentedRequestParams = (value) => TaskAugmentedRequestParamsSchema.safeParse(value).success; +var RequestSchema = object({ + method: string2(), + params: BaseRequestParamsSchema.loose().optional() +}); +var NotificationsParamsSchema = object({ + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: RequestMetaSchema.optional() +}); +var NotificationSchema = object({ + method: string2(), + params: NotificationsParamsSchema.loose().optional() +}); +var ResultSchema = looseObject({ + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: RequestMetaSchema.optional() +}); +var RequestIdSchema = union([string2(), number2().int()]); +var JSONRPCRequestSchema = object({ + jsonrpc: literal(JSONRPC_VERSION), + id: RequestIdSchema, + ...RequestSchema.shape +}).strict(); +var isJSONRPCRequest = (value) => JSONRPCRequestSchema.safeParse(value).success; +var JSONRPCNotificationSchema = object({ + jsonrpc: literal(JSONRPC_VERSION), + ...NotificationSchema.shape +}).strict(); +var isJSONRPCNotification = (value) => JSONRPCNotificationSchema.safeParse(value).success; +var JSONRPCResultResponseSchema = object({ + jsonrpc: literal(JSONRPC_VERSION), + id: RequestIdSchema, + result: ResultSchema +}).strict(); +var isJSONRPCResultResponse = (value) => JSONRPCResultResponseSchema.safeParse(value).success; +var ErrorCode; +(function(ErrorCode2) { + ErrorCode2[ErrorCode2["ConnectionClosed"] = -32e3] = "ConnectionClosed"; + ErrorCode2[ErrorCode2["RequestTimeout"] = -32001] = "RequestTimeout"; + ErrorCode2[ErrorCode2["ParseError"] = -32700] = "ParseError"; + ErrorCode2[ErrorCode2["InvalidRequest"] = -32600] = "InvalidRequest"; + ErrorCode2[ErrorCode2["MethodNotFound"] = -32601] = "MethodNotFound"; + ErrorCode2[ErrorCode2["InvalidParams"] = -32602] = "InvalidParams"; + ErrorCode2[ErrorCode2["InternalError"] = -32603] = "InternalError"; + ErrorCode2[ErrorCode2["UrlElicitationRequired"] = -32042] = "UrlElicitationRequired"; +})(ErrorCode || (ErrorCode = {})); +var JSONRPCErrorResponseSchema = object({ + jsonrpc: literal(JSONRPC_VERSION), + id: RequestIdSchema.optional(), + error: object({ + /** + * The error type that occurred. + */ + code: number2().int(), + /** + * A short description of the error. The message SHOULD be limited to a concise single sentence. + */ + message: string2(), + /** + * Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + */ + data: unknown().optional() + }) +}).strict(); +var isJSONRPCErrorResponse = (value) => JSONRPCErrorResponseSchema.safeParse(value).success; +var JSONRPCMessageSchema = union([ + JSONRPCRequestSchema, + JSONRPCNotificationSchema, + JSONRPCResultResponseSchema, + JSONRPCErrorResponseSchema +]); +var JSONRPCResponseSchema = union([JSONRPCResultResponseSchema, JSONRPCErrorResponseSchema]); +var EmptyResultSchema = ResultSchema.strict(); +var CancelledNotificationParamsSchema = NotificationsParamsSchema.extend({ + /** + * The ID of the request to cancel. + * + * This MUST correspond to the ID of a request previously issued in the same direction. + */ + requestId: RequestIdSchema.optional(), + /** + * An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. + */ + reason: string2().optional() +}); +var CancelledNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/cancelled"), + params: CancelledNotificationParamsSchema +}); +var IconSchema = object({ + /** + * URL or data URI for the icon. + */ + src: string2(), + /** + * Optional MIME type for the icon. + */ + mimeType: string2().optional(), + /** + * Optional array of strings that specify sizes at which the icon can be used. + * Each string should be in WxH format (e.g., `"48x48"`, `"96x96"`) or `"any"` for scalable formats like SVG. + * + * If not provided, the client should assume that the icon can be used at any size. + */ + sizes: array(string2()).optional(), + /** + * Optional specifier for the theme this icon is designed for. `light` indicates + * the icon is designed to be used with a light background, and `dark` indicates + * the icon is designed to be used with a dark background. + * + * If not provided, the client should assume the icon can be used with any theme. + */ + theme: _enum(["light", "dark"]).optional() +}); +var IconsSchema = object({ + /** + * Optional set of sized icons that the client can display in a user interface. + * + * Clients that support rendering icons MUST support at least the following MIME types: + * - `image/png` - PNG images (safe, universal compatibility) + * - `image/jpeg` (and `image/jpg`) - JPEG images (safe, universal compatibility) + * + * Clients that support rendering icons SHOULD also support: + * - `image/svg+xml` - SVG images (scalable but requires security precautions) + * - `image/webp` - WebP images (modern, efficient format) + */ + icons: array(IconSchema).optional() +}); +var BaseMetadataSchema = object({ + /** Intended for programmatic or logical use, but used as a display name in past specs or fallback */ + name: string2(), + /** + * Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + * even by those unfamiliar with domain-specific terminology. + * + * If not provided, the name should be used for display (except for Tool, + * where `annotations.title` should be given precedence over using `name`, + * if present). + */ + title: string2().optional() +}); +var ImplementationSchema = BaseMetadataSchema.extend({ + ...BaseMetadataSchema.shape, + ...IconsSchema.shape, + version: string2(), + /** + * An optional URL of the website for this implementation. + */ + websiteUrl: string2().optional(), + /** + * An optional human-readable description of what this implementation does. + * + * This can be used by clients or servers to provide context about their purpose + * and capabilities. For example, a server might describe the types of resources + * or tools it provides, while a client might describe its intended use case. + */ + description: string2().optional() +}); +var FormElicitationCapabilitySchema = intersection(object({ + applyDefaults: boolean2().optional() +}), record(string2(), unknown())); +var ElicitationCapabilitySchema = preprocess((value) => { + if (value && typeof value === "object" && !Array.isArray(value)) { + if (Object.keys(value).length === 0) { + return { form: {} }; + } + } + return value; +}, intersection(object({ + form: FormElicitationCapabilitySchema.optional(), + url: AssertObjectSchema.optional() +}), record(string2(), unknown()).optional())); +var ClientTasksCapabilitySchema = looseObject({ + /** + * Present if the client supports listing tasks. + */ + list: AssertObjectSchema.optional(), + /** + * Present if the client supports cancelling tasks. + */ + cancel: AssertObjectSchema.optional(), + /** + * Capabilities for task creation on specific request types. + */ + requests: looseObject({ + /** + * Task support for sampling requests. + */ + sampling: looseObject({ + createMessage: AssertObjectSchema.optional() + }).optional(), + /** + * Task support for elicitation requests. + */ + elicitation: looseObject({ + create: AssertObjectSchema.optional() + }).optional() + }).optional() +}); +var ServerTasksCapabilitySchema = looseObject({ + /** + * Present if the server supports listing tasks. + */ + list: AssertObjectSchema.optional(), + /** + * Present if the server supports cancelling tasks. + */ + cancel: AssertObjectSchema.optional(), + /** + * Capabilities for task creation on specific request types. + */ + requests: looseObject({ + /** + * Task support for tool requests. + */ + tools: looseObject({ + call: AssertObjectSchema.optional() + }).optional() + }).optional() +}); +var ClientCapabilitiesSchema = object({ + /** + * Experimental, non-standard capabilities that the client supports. + */ + experimental: record(string2(), AssertObjectSchema).optional(), + /** + * Present if the client supports sampling from an LLM. + */ + sampling: object({ + /** + * Present if the client supports context inclusion via includeContext parameter. + * If not declared, servers SHOULD only use `includeContext: "none"` (or omit it). + */ + context: AssertObjectSchema.optional(), + /** + * Present if the client supports tool use via tools and toolChoice parameters. + */ + tools: AssertObjectSchema.optional() + }).optional(), + /** + * Present if the client supports eliciting user input. + */ + elicitation: ElicitationCapabilitySchema.optional(), + /** + * Present if the client supports listing roots. + */ + roots: object({ + /** + * Whether the client supports issuing notifications for changes to the roots list. + */ + listChanged: boolean2().optional() + }).optional(), + /** + * Present if the client supports task creation. + */ + tasks: ClientTasksCapabilitySchema.optional() +}); +var InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({ + /** + * The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well. + */ + protocolVersion: string2(), + capabilities: ClientCapabilitiesSchema, + clientInfo: ImplementationSchema +}); +var InitializeRequestSchema = RequestSchema.extend({ + method: literal("initialize"), + params: InitializeRequestParamsSchema +}); +var ServerCapabilitiesSchema = object({ + /** + * Experimental, non-standard capabilities that the server supports. + */ + experimental: record(string2(), AssertObjectSchema).optional(), + /** + * Present if the server supports sending log messages to the client. + */ + logging: AssertObjectSchema.optional(), + /** + * Present if the server supports sending completions to the client. + */ + completions: AssertObjectSchema.optional(), + /** + * Present if the server offers any prompt templates. + */ + prompts: object({ + /** + * Whether this server supports issuing notifications for changes to the prompt list. + */ + listChanged: boolean2().optional() + }).optional(), + /** + * Present if the server offers any resources to read. + */ + resources: object({ + /** + * Whether this server supports clients subscribing to resource updates. + */ + subscribe: boolean2().optional(), + /** + * Whether this server supports issuing notifications for changes to the resource list. + */ + listChanged: boolean2().optional() + }).optional(), + /** + * Present if the server offers any tools to call. + */ + tools: object({ + /** + * Whether this server supports issuing notifications for changes to the tool list. + */ + listChanged: boolean2().optional() + }).optional(), + /** + * Present if the server supports task creation. + */ + tasks: ServerTasksCapabilitySchema.optional() +}); +var InitializeResultSchema = ResultSchema.extend({ + /** + * The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect. + */ + protocolVersion: string2(), + capabilities: ServerCapabilitiesSchema, + serverInfo: ImplementationSchema, + /** + * Instructions describing how to use the server and its features. + * + * This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt. + */ + instructions: string2().optional() +}); +var InitializedNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/initialized"), + params: NotificationsParamsSchema.optional() +}); +var PingRequestSchema = RequestSchema.extend({ + method: literal("ping"), + params: BaseRequestParamsSchema.optional() +}); +var ProgressSchema = object({ + /** + * The progress thus far. This should increase every time progress is made, even if the total is unknown. + */ + progress: number2(), + /** + * Total number of items to process (or total progress required), if known. + */ + total: optional(number2()), + /** + * An optional message describing the current progress. + */ + message: optional(string2()) +}); +var ProgressNotificationParamsSchema = object({ + ...NotificationsParamsSchema.shape, + ...ProgressSchema.shape, + /** + * The progress token which was given in the initial request, used to associate this notification with the request that is proceeding. + */ + progressToken: ProgressTokenSchema +}); +var ProgressNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/progress"), + params: ProgressNotificationParamsSchema +}); +var PaginatedRequestParamsSchema = BaseRequestParamsSchema.extend({ + /** + * An opaque token representing the current pagination position. + * If provided, the server should return results starting after this cursor. + */ + cursor: CursorSchema.optional() +}); +var PaginatedRequestSchema = RequestSchema.extend({ + params: PaginatedRequestParamsSchema.optional() +}); +var PaginatedResultSchema = ResultSchema.extend({ + /** + * An opaque token representing the pagination position after the last returned result. + * If present, there may be more results available. + */ + nextCursor: CursorSchema.optional() +}); +var TaskStatusSchema = _enum(["working", "input_required", "completed", "failed", "cancelled"]); +var TaskSchema = object({ + taskId: string2(), + status: TaskStatusSchema, + /** + * Time in milliseconds to keep task results available after completion. + * If null, the task has unlimited lifetime until manually cleaned up. + */ + ttl: union([number2(), _null3()]), + /** + * ISO 8601 timestamp when the task was created. + */ + createdAt: string2(), + /** + * ISO 8601 timestamp when the task was last updated. + */ + lastUpdatedAt: string2(), + pollInterval: optional(number2()), + /** + * Optional diagnostic message for failed tasks or other status information. + */ + statusMessage: optional(string2()) +}); +var CreateTaskResultSchema = ResultSchema.extend({ + task: TaskSchema +}); +var TaskStatusNotificationParamsSchema = NotificationsParamsSchema.merge(TaskSchema); +var TaskStatusNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/tasks/status"), + params: TaskStatusNotificationParamsSchema +}); +var GetTaskRequestSchema = RequestSchema.extend({ + method: literal("tasks/get"), + params: BaseRequestParamsSchema.extend({ + taskId: string2() + }) +}); +var GetTaskResultSchema = ResultSchema.merge(TaskSchema); +var GetTaskPayloadRequestSchema = RequestSchema.extend({ + method: literal("tasks/result"), + params: BaseRequestParamsSchema.extend({ + taskId: string2() + }) +}); +var GetTaskPayloadResultSchema = ResultSchema.loose(); +var ListTasksRequestSchema = PaginatedRequestSchema.extend({ + method: literal("tasks/list") +}); +var ListTasksResultSchema = PaginatedResultSchema.extend({ + tasks: array(TaskSchema) +}); +var CancelTaskRequestSchema = RequestSchema.extend({ + method: literal("tasks/cancel"), + params: BaseRequestParamsSchema.extend({ + taskId: string2() + }) +}); +var CancelTaskResultSchema = ResultSchema.merge(TaskSchema); +var ResourceContentsSchema = object({ + /** + * The URI of this resource. + */ + uri: string2(), + /** + * The MIME type of this resource, if known. + */ + mimeType: optional(string2()), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var TextResourceContentsSchema = ResourceContentsSchema.extend({ + /** + * The text of the item. This must only be set if the item can actually be represented as text (not binary data). + */ + text: string2() +}); +var Base64Schema = string2().refine((val) => { + try { + atob(val); + return true; + } catch { + return false; + } +}, { message: "Invalid Base64 string" }); +var BlobResourceContentsSchema = ResourceContentsSchema.extend({ + /** + * A base64-encoded string representing the binary data of the item. + */ + blob: Base64Schema +}); +var RoleSchema = _enum(["user", "assistant"]); +var AnnotationsSchema = object({ + /** + * Intended audience(s) for the resource. + */ + audience: array(RoleSchema).optional(), + /** + * Importance hint for the resource, from 0 (least) to 1 (most). + */ + priority: number2().min(0).max(1).optional(), + /** + * ISO 8601 timestamp for the most recent modification. + */ + lastModified: iso_exports.datetime({ offset: true }).optional() +}); +var ResourceSchema = object({ + ...BaseMetadataSchema.shape, + ...IconsSchema.shape, + /** + * The URI of this resource. + */ + uri: string2(), + /** + * A description of what this resource represents. + * + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + */ + description: optional(string2()), + /** + * The MIME type of this resource, if known. + */ + mimeType: optional(string2()), + /** + * Optional annotations for the client. + */ + annotations: AnnotationsSchema.optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: optional(looseObject({})) +}); +var ResourceTemplateSchema = object({ + ...BaseMetadataSchema.shape, + ...IconsSchema.shape, + /** + * A URI template (according to RFC 6570) that can be used to construct resource URIs. + */ + uriTemplate: string2(), + /** + * A description of what this template is for. + * + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + */ + description: optional(string2()), + /** + * The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type. + */ + mimeType: optional(string2()), + /** + * Optional annotations for the client. + */ + annotations: AnnotationsSchema.optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: optional(looseObject({})) +}); +var ListResourcesRequestSchema = PaginatedRequestSchema.extend({ + method: literal("resources/list") +}); +var ListResourcesResultSchema = PaginatedResultSchema.extend({ + resources: array(ResourceSchema) +}); +var ListResourceTemplatesRequestSchema = PaginatedRequestSchema.extend({ + method: literal("resources/templates/list") +}); +var ListResourceTemplatesResultSchema = PaginatedResultSchema.extend({ + resourceTemplates: array(ResourceTemplateSchema) +}); +var ResourceRequestParamsSchema = BaseRequestParamsSchema.extend({ + /** + * The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it. + * + * @format uri + */ + uri: string2() +}); +var ReadResourceRequestParamsSchema = ResourceRequestParamsSchema; +var ReadResourceRequestSchema = RequestSchema.extend({ + method: literal("resources/read"), + params: ReadResourceRequestParamsSchema +}); +var ReadResourceResultSchema = ResultSchema.extend({ + contents: array(union([TextResourceContentsSchema, BlobResourceContentsSchema])) +}); +var ResourceListChangedNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/resources/list_changed"), + params: NotificationsParamsSchema.optional() +}); +var SubscribeRequestParamsSchema = ResourceRequestParamsSchema; +var SubscribeRequestSchema = RequestSchema.extend({ + method: literal("resources/subscribe"), + params: SubscribeRequestParamsSchema +}); +var UnsubscribeRequestParamsSchema = ResourceRequestParamsSchema; +var UnsubscribeRequestSchema = RequestSchema.extend({ + method: literal("resources/unsubscribe"), + params: UnsubscribeRequestParamsSchema +}); +var ResourceUpdatedNotificationParamsSchema = NotificationsParamsSchema.extend({ + /** + * The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. + */ + uri: string2() +}); +var ResourceUpdatedNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/resources/updated"), + params: ResourceUpdatedNotificationParamsSchema +}); +var PromptArgumentSchema = object({ + /** + * The name of the argument. + */ + name: string2(), + /** + * A human-readable description of the argument. + */ + description: optional(string2()), + /** + * Whether this argument must be provided. + */ + required: optional(boolean2()) +}); +var PromptSchema = object({ + ...BaseMetadataSchema.shape, + ...IconsSchema.shape, + /** + * An optional description of what this prompt provides + */ + description: optional(string2()), + /** + * A list of arguments to use for templating the prompt. + */ + arguments: optional(array(PromptArgumentSchema)), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: optional(looseObject({})) +}); +var ListPromptsRequestSchema = PaginatedRequestSchema.extend({ + method: literal("prompts/list") +}); +var ListPromptsResultSchema = PaginatedResultSchema.extend({ + prompts: array(PromptSchema) +}); +var GetPromptRequestParamsSchema = BaseRequestParamsSchema.extend({ + /** + * The name of the prompt or prompt template. + */ + name: string2(), + /** + * Arguments to use for templating the prompt. + */ + arguments: record(string2(), string2()).optional() +}); +var GetPromptRequestSchema = RequestSchema.extend({ + method: literal("prompts/get"), + params: GetPromptRequestParamsSchema +}); +var TextContentSchema = object({ + type: literal("text"), + /** + * The text content of the message. + */ + text: string2(), + /** + * Optional annotations for the client. + */ + annotations: AnnotationsSchema.optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var ImageContentSchema = object({ + type: literal("image"), + /** + * The base64-encoded image data. + */ + data: Base64Schema, + /** + * The MIME type of the image. Different providers may support different image types. + */ + mimeType: string2(), + /** + * Optional annotations for the client. + */ + annotations: AnnotationsSchema.optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var AudioContentSchema = object({ + type: literal("audio"), + /** + * The base64-encoded audio data. + */ + data: Base64Schema, + /** + * The MIME type of the audio. Different providers may support different audio types. + */ + mimeType: string2(), + /** + * Optional annotations for the client. + */ + annotations: AnnotationsSchema.optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var ToolUseContentSchema = object({ + type: literal("tool_use"), + /** + * The name of the tool to invoke. + * Must match a tool name from the request's tools array. + */ + name: string2(), + /** + * Unique identifier for this tool call. + * Used to correlate with ToolResultContent in subsequent messages. + */ + id: string2(), + /** + * Arguments to pass to the tool. + * Must conform to the tool's inputSchema. + */ + input: record(string2(), unknown()), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var EmbeddedResourceSchema = object({ + type: literal("resource"), + resource: union([TextResourceContentsSchema, BlobResourceContentsSchema]), + /** + * Optional annotations for the client. + */ + annotations: AnnotationsSchema.optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var ResourceLinkSchema = ResourceSchema.extend({ + type: literal("resource_link") +}); +var ContentBlockSchema = union([ + TextContentSchema, + ImageContentSchema, + AudioContentSchema, + ResourceLinkSchema, + EmbeddedResourceSchema +]); +var PromptMessageSchema = object({ + role: RoleSchema, + content: ContentBlockSchema +}); +var GetPromptResultSchema = ResultSchema.extend({ + /** + * An optional description for the prompt. + */ + description: string2().optional(), + messages: array(PromptMessageSchema) +}); +var PromptListChangedNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/prompts/list_changed"), + params: NotificationsParamsSchema.optional() +}); +var ToolAnnotationsSchema = object({ + /** + * A human-readable title for the tool. + */ + title: string2().optional(), + /** + * If true, the tool does not modify its environment. + * + * Default: false + */ + readOnlyHint: boolean2().optional(), + /** + * If true, the tool may perform destructive updates to its environment. + * If false, the tool performs only additive updates. + * + * (This property is meaningful only when `readOnlyHint == false`) + * + * Default: true + */ + destructiveHint: boolean2().optional(), + /** + * If true, calling the tool repeatedly with the same arguments + * will have no additional effect on the its environment. + * + * (This property is meaningful only when `readOnlyHint == false`) + * + * Default: false + */ + idempotentHint: boolean2().optional(), + /** + * If true, this tool may interact with an "open world" of external + * entities. If false, the tool's domain of interaction is closed. + * For example, the world of a web search tool is open, whereas that + * of a memory tool is not. + * + * Default: true + */ + openWorldHint: boolean2().optional() +}); +var ToolExecutionSchema = object({ + /** + * Indicates the tool's preference for task-augmented execution. + * - "required": Clients MUST invoke the tool as a task + * - "optional": Clients MAY invoke the tool as a task or normal request + * - "forbidden": Clients MUST NOT attempt to invoke the tool as a task + * + * If not present, defaults to "forbidden". + */ + taskSupport: _enum(["required", "optional", "forbidden"]).optional() +}); +var ToolSchema = object({ + ...BaseMetadataSchema.shape, + ...IconsSchema.shape, + /** + * A human-readable description of the tool. + */ + description: string2().optional(), + /** + * A JSON Schema 2020-12 object defining the expected parameters for the tool. + * Must have type: 'object' at the root level per MCP spec. + */ + inputSchema: object({ + type: literal("object"), + properties: record(string2(), AssertObjectSchema).optional(), + required: array(string2()).optional() + }).catchall(unknown()), + /** + * An optional JSON Schema 2020-12 object defining the structure of the tool's output + * returned in the structuredContent field of a CallToolResult. + * Must have type: 'object' at the root level per MCP spec. + */ + outputSchema: object({ + type: literal("object"), + properties: record(string2(), AssertObjectSchema).optional(), + required: array(string2()).optional() + }).catchall(unknown()).optional(), + /** + * Optional additional tool information. + */ + annotations: ToolAnnotationsSchema.optional(), + /** + * Execution-related properties for this tool. + */ + execution: ToolExecutionSchema.optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var ListToolsRequestSchema = PaginatedRequestSchema.extend({ + method: literal("tools/list") +}); +var ListToolsResultSchema = PaginatedResultSchema.extend({ + tools: array(ToolSchema) +}); +var CallToolResultSchema = ResultSchema.extend({ + /** + * A list of content objects that represent the result of the tool call. + * + * If the Tool does not define an outputSchema, this field MUST be present in the result. + * For backwards compatibility, this field is always present, but it may be empty. + */ + content: array(ContentBlockSchema).default([]), + /** + * An object containing structured tool output. + * + * If the Tool defines an outputSchema, this field MUST be present in the result, and contain a JSON object that matches the schema. + */ + structuredContent: record(string2(), unknown()).optional(), + /** + * Whether the tool call ended in an error. + * + * If not set, this is assumed to be false (the call was successful). + * + * Any errors that originate from the tool SHOULD be reported inside the result + * object, with `isError` set to true, _not_ as an MCP protocol-level error + * response. Otherwise, the LLM would not be able to see that an error occurred + * and self-correct. + * + * However, any errors in _finding_ the tool, an error indicating that the + * server does not support tool calls, or any other exceptional conditions, + * should be reported as an MCP error response. + */ + isError: boolean2().optional() +}); +var CompatibilityCallToolResultSchema = CallToolResultSchema.or(ResultSchema.extend({ + toolResult: unknown() +})); +var CallToolRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({ + /** + * The name of the tool to call. + */ + name: string2(), + /** + * Arguments to pass to the tool. + */ + arguments: record(string2(), unknown()).optional() +}); +var CallToolRequestSchema = RequestSchema.extend({ + method: literal("tools/call"), + params: CallToolRequestParamsSchema +}); +var ToolListChangedNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/tools/list_changed"), + params: NotificationsParamsSchema.optional() +}); +var ListChangedOptionsBaseSchema = object({ + /** + * If true, the list will be refreshed automatically when a list changed notification is received. + * The callback will be called with the updated list. + * + * If false, the callback will be called with null items, allowing manual refresh. + * + * @default true + */ + autoRefresh: boolean2().default(true), + /** + * Debounce time in milliseconds for list changed notification processing. + * + * Multiple notifications received within this timeframe will only trigger one refresh. + * Set to 0 to disable debouncing. + * + * @default 300 + */ + debounceMs: number2().int().nonnegative().default(300) +}); +var LoggingLevelSchema = _enum(["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"]); +var SetLevelRequestParamsSchema = BaseRequestParamsSchema.extend({ + /** + * The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/logging/message. + */ + level: LoggingLevelSchema +}); +var SetLevelRequestSchema = RequestSchema.extend({ + method: literal("logging/setLevel"), + params: SetLevelRequestParamsSchema +}); +var LoggingMessageNotificationParamsSchema = NotificationsParamsSchema.extend({ + /** + * The severity of this log message. + */ + level: LoggingLevelSchema, + /** + * An optional name of the logger issuing this message. + */ + logger: string2().optional(), + /** + * The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. + */ + data: unknown() +}); +var LoggingMessageNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/message"), + params: LoggingMessageNotificationParamsSchema +}); +var ModelHintSchema = object({ + /** + * A hint for a model name. + */ + name: string2().optional() +}); +var ModelPreferencesSchema = object({ + /** + * Optional hints to use for model selection. + */ + hints: array(ModelHintSchema).optional(), + /** + * How much to prioritize cost when selecting a model. + */ + costPriority: number2().min(0).max(1).optional(), + /** + * How much to prioritize sampling speed (latency) when selecting a model. + */ + speedPriority: number2().min(0).max(1).optional(), + /** + * How much to prioritize intelligence and capabilities when selecting a model. + */ + intelligencePriority: number2().min(0).max(1).optional() +}); +var ToolChoiceSchema = object({ + /** + * Controls when tools are used: + * - "auto": Model decides whether to use tools (default) + * - "required": Model MUST use at least one tool before completing + * - "none": Model MUST NOT use any tools + */ + mode: _enum(["auto", "required", "none"]).optional() +}); +var ToolResultContentSchema = object({ + type: literal("tool_result"), + toolUseId: string2().describe("The unique identifier for the corresponding tool call."), + content: array(ContentBlockSchema).default([]), + structuredContent: object({}).loose().optional(), + isError: boolean2().optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var SamplingContentSchema = discriminatedUnion("type", [TextContentSchema, ImageContentSchema, AudioContentSchema]); +var SamplingMessageContentBlockSchema = discriminatedUnion("type", [ + TextContentSchema, + ImageContentSchema, + AudioContentSchema, + ToolUseContentSchema, + ToolResultContentSchema +]); +var SamplingMessageSchema = object({ + role: RoleSchema, + content: union([SamplingMessageContentBlockSchema, array(SamplingMessageContentBlockSchema)]), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var CreateMessageRequestParamsSchema = TaskAugmentedRequestParamsSchema.extend({ + messages: array(SamplingMessageSchema), + /** + * The server's preferences for which model to select. The client MAY modify or omit this request. + */ + modelPreferences: ModelPreferencesSchema.optional(), + /** + * An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt. + */ + systemPrompt: string2().optional(), + /** + * A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. + * The client MAY ignore this request. + * + * Default is "none". Values "thisServer" and "allServers" are soft-deprecated. Servers SHOULD only use these values if the client + * declares ClientCapabilities.sampling.context. These values may be removed in future spec releases. + */ + includeContext: _enum(["none", "thisServer", "allServers"]).optional(), + temperature: number2().optional(), + /** + * The requested maximum number of tokens to sample (to prevent runaway completions). + * + * The client MAY choose to sample fewer tokens than the requested maximum. + */ + maxTokens: number2().int(), + stopSequences: array(string2()).optional(), + /** + * Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific. + */ + metadata: AssertObjectSchema.optional(), + /** + * Tools that the model may use during generation. + * The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared. + */ + tools: array(ToolSchema).optional(), + /** + * Controls how the model uses tools. + * The client MUST return an error if this field is provided but ClientCapabilities.sampling.tools is not declared. + * Default is `{ mode: "auto" }`. + */ + toolChoice: ToolChoiceSchema.optional() +}); +var CreateMessageRequestSchema = RequestSchema.extend({ + method: literal("sampling/createMessage"), + params: CreateMessageRequestParamsSchema +}); +var CreateMessageResultSchema = ResultSchema.extend({ + /** + * The name of the model that generated the message. + */ + model: string2(), + /** + * The reason why sampling stopped, if known. + * + * Standard values: + * - "endTurn": Natural end of the assistant's turn + * - "stopSequence": A stop sequence was encountered + * - "maxTokens": Maximum token limit was reached + * + * This field is an open string to allow for provider-specific stop reasons. + */ + stopReason: optional(_enum(["endTurn", "stopSequence", "maxTokens"]).or(string2())), + role: RoleSchema, + /** + * Response content. Single content block (text, image, or audio). + */ + content: SamplingContentSchema +}); +var CreateMessageResultWithToolsSchema = ResultSchema.extend({ + /** + * The name of the model that generated the message. + */ + model: string2(), + /** + * The reason why sampling stopped, if known. + * + * Standard values: + * - "endTurn": Natural end of the assistant's turn + * - "stopSequence": A stop sequence was encountered + * - "maxTokens": Maximum token limit was reached + * - "toolUse": The model wants to use one or more tools + * + * This field is an open string to allow for provider-specific stop reasons. + */ + stopReason: optional(_enum(["endTurn", "stopSequence", "maxTokens", "toolUse"]).or(string2())), + role: RoleSchema, + /** + * Response content. May be a single block or array. May include ToolUseContent if stopReason is "toolUse". + */ + content: union([SamplingMessageContentBlockSchema, array(SamplingMessageContentBlockSchema)]) +}); +var BooleanSchemaSchema = object({ + type: literal("boolean"), + title: string2().optional(), + description: string2().optional(), + default: boolean2().optional() +}); +var StringSchemaSchema = object({ + type: literal("string"), + title: string2().optional(), + description: string2().optional(), + minLength: number2().optional(), + maxLength: number2().optional(), + format: _enum(["email", "uri", "date", "date-time"]).optional(), + default: string2().optional() +}); +var NumberSchemaSchema = object({ + type: _enum(["number", "integer"]), + title: string2().optional(), + description: string2().optional(), + minimum: number2().optional(), + maximum: number2().optional(), + default: number2().optional() +}); +var UntitledSingleSelectEnumSchemaSchema = object({ + type: literal("string"), + title: string2().optional(), + description: string2().optional(), + enum: array(string2()), + default: string2().optional() +}); +var TitledSingleSelectEnumSchemaSchema = object({ + type: literal("string"), + title: string2().optional(), + description: string2().optional(), + oneOf: array(object({ + const: string2(), + title: string2() + })), + default: string2().optional() +}); +var LegacyTitledEnumSchemaSchema = object({ + type: literal("string"), + title: string2().optional(), + description: string2().optional(), + enum: array(string2()), + enumNames: array(string2()).optional(), + default: string2().optional() +}); +var SingleSelectEnumSchemaSchema = union([UntitledSingleSelectEnumSchemaSchema, TitledSingleSelectEnumSchemaSchema]); +var UntitledMultiSelectEnumSchemaSchema = object({ + type: literal("array"), + title: string2().optional(), + description: string2().optional(), + minItems: number2().optional(), + maxItems: number2().optional(), + items: object({ + type: literal("string"), + enum: array(string2()) + }), + default: array(string2()).optional() +}); +var TitledMultiSelectEnumSchemaSchema = object({ + type: literal("array"), + title: string2().optional(), + description: string2().optional(), + minItems: number2().optional(), + maxItems: number2().optional(), + items: object({ + anyOf: array(object({ + const: string2(), + title: string2() + })) + }), + default: array(string2()).optional() +}); +var MultiSelectEnumSchemaSchema = union([UntitledMultiSelectEnumSchemaSchema, TitledMultiSelectEnumSchemaSchema]); +var EnumSchemaSchema = union([LegacyTitledEnumSchemaSchema, SingleSelectEnumSchemaSchema, MultiSelectEnumSchemaSchema]); +var PrimitiveSchemaDefinitionSchema = union([EnumSchemaSchema, BooleanSchemaSchema, StringSchemaSchema, NumberSchemaSchema]); +var ElicitRequestFormParamsSchema = TaskAugmentedRequestParamsSchema.extend({ + /** + * The elicitation mode. + * + * Optional for backward compatibility. Clients MUST treat missing mode as "form". + */ + mode: literal("form").optional(), + /** + * The message to present to the user describing what information is being requested. + */ + message: string2(), + /** + * A restricted subset of JSON Schema. + * Only top-level properties are allowed, without nesting. + */ + requestedSchema: object({ + type: literal("object"), + properties: record(string2(), PrimitiveSchemaDefinitionSchema), + required: array(string2()).optional() + }) +}); +var ElicitRequestURLParamsSchema = TaskAugmentedRequestParamsSchema.extend({ + /** + * The elicitation mode. + */ + mode: literal("url"), + /** + * The message to present to the user explaining why the interaction is needed. + */ + message: string2(), + /** + * The ID of the elicitation, which must be unique within the context of the server. + * The client MUST treat this ID as an opaque value. + */ + elicitationId: string2(), + /** + * The URL that the user should navigate to. + */ + url: string2().url() +}); +var ElicitRequestParamsSchema = union([ElicitRequestFormParamsSchema, ElicitRequestURLParamsSchema]); +var ElicitRequestSchema = RequestSchema.extend({ + method: literal("elicitation/create"), + params: ElicitRequestParamsSchema +}); +var ElicitationCompleteNotificationParamsSchema = NotificationsParamsSchema.extend({ + /** + * The ID of the elicitation that completed. + */ + elicitationId: string2() +}); +var ElicitationCompleteNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/elicitation/complete"), + params: ElicitationCompleteNotificationParamsSchema +}); +var ElicitResultSchema = ResultSchema.extend({ + /** + * The user action in response to the elicitation. + * - "accept": User submitted the form/confirmed the action + * - "decline": User explicitly decline the action + * - "cancel": User dismissed without making an explicit choice + */ + action: _enum(["accept", "decline", "cancel"]), + /** + * The submitted form data, only present when action is "accept". + * Contains values matching the requested schema. + * Per MCP spec, content is "typically omitted" for decline/cancel actions. + * We normalize null to undefined for leniency while maintaining type compatibility. + */ + content: preprocess((val) => val === null ? void 0 : val, record(string2(), union([string2(), number2(), boolean2(), array(string2())])).optional()) +}); +var ResourceTemplateReferenceSchema = object({ + type: literal("ref/resource"), + /** + * The URI or URI template of the resource. + */ + uri: string2() +}); +var PromptReferenceSchema = object({ + type: literal("ref/prompt"), + /** + * The name of the prompt or prompt template + */ + name: string2() +}); +var CompleteRequestParamsSchema = BaseRequestParamsSchema.extend({ + ref: union([PromptReferenceSchema, ResourceTemplateReferenceSchema]), + /** + * The argument's information + */ + argument: object({ + /** + * The name of the argument + */ + name: string2(), + /** + * The value of the argument to use for completion matching. + */ + value: string2() + }), + context: object({ + /** + * Previously-resolved variables in a URI template or prompt. + */ + arguments: record(string2(), string2()).optional() + }).optional() +}); +var CompleteRequestSchema = RequestSchema.extend({ + method: literal("completion/complete"), + params: CompleteRequestParamsSchema +}); +function assertCompleteRequestPrompt(request) { + if (request.params.ref.type !== "ref/prompt") { + throw new TypeError(`Expected CompleteRequestPrompt, but got ${request.params.ref.type}`); + } + void request; +} +function assertCompleteRequestResourceTemplate(request) { + if (request.params.ref.type !== "ref/resource") { + throw new TypeError(`Expected CompleteRequestResourceTemplate, but got ${request.params.ref.type}`); + } + void request; +} +var CompleteResultSchema = ResultSchema.extend({ + completion: looseObject({ + /** + * An array of completion values. Must not exceed 100 items. + */ + values: array(string2()).max(100), + /** + * The total number of completion options available. This can exceed the number of values actually sent in the response. + */ + total: optional(number2().int()), + /** + * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + */ + hasMore: optional(boolean2()) + }) +}); +var RootSchema = object({ + /** + * The URI identifying the root. This *must* start with file:// for now. + */ + uri: string2().startsWith("file://"), + /** + * An optional name for the root. + */ + name: string2().optional(), + /** + * See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + * for notes on _meta usage. + */ + _meta: record(string2(), unknown()).optional() +}); +var ListRootsRequestSchema = RequestSchema.extend({ + method: literal("roots/list"), + params: BaseRequestParamsSchema.optional() +}); +var ListRootsResultSchema = ResultSchema.extend({ + roots: array(RootSchema) +}); +var RootsListChangedNotificationSchema = NotificationSchema.extend({ + method: literal("notifications/roots/list_changed"), + params: NotificationsParamsSchema.optional() +}); +var ClientRequestSchema = union([ + PingRequestSchema, + InitializeRequestSchema, + CompleteRequestSchema, + SetLevelRequestSchema, + GetPromptRequestSchema, + ListPromptsRequestSchema, + ListResourcesRequestSchema, + ListResourceTemplatesRequestSchema, + ReadResourceRequestSchema, + SubscribeRequestSchema, + UnsubscribeRequestSchema, + CallToolRequestSchema, + ListToolsRequestSchema, + GetTaskRequestSchema, + GetTaskPayloadRequestSchema, + ListTasksRequestSchema, + CancelTaskRequestSchema +]); +var ClientNotificationSchema = union([ + CancelledNotificationSchema, + ProgressNotificationSchema, + InitializedNotificationSchema, + RootsListChangedNotificationSchema, + TaskStatusNotificationSchema +]); +var ClientResultSchema = union([ + EmptyResultSchema, + CreateMessageResultSchema, + CreateMessageResultWithToolsSchema, + ElicitResultSchema, + ListRootsResultSchema, + GetTaskResultSchema, + ListTasksResultSchema, + CreateTaskResultSchema +]); +var ServerRequestSchema = union([ + PingRequestSchema, + CreateMessageRequestSchema, + ElicitRequestSchema, + ListRootsRequestSchema, + GetTaskRequestSchema, + GetTaskPayloadRequestSchema, + ListTasksRequestSchema, + CancelTaskRequestSchema +]); +var ServerNotificationSchema = union([ + CancelledNotificationSchema, + ProgressNotificationSchema, + LoggingMessageNotificationSchema, + ResourceUpdatedNotificationSchema, + ResourceListChangedNotificationSchema, + ToolListChangedNotificationSchema, + PromptListChangedNotificationSchema, + TaskStatusNotificationSchema, + ElicitationCompleteNotificationSchema +]); +var ServerResultSchema = union([ + EmptyResultSchema, + InitializeResultSchema, + CompleteResultSchema, + GetPromptResultSchema, + ListPromptsResultSchema, + ListResourcesResultSchema, + ListResourceTemplatesResultSchema, + ReadResourceResultSchema, + CallToolResultSchema, + ListToolsResultSchema, + GetTaskResultSchema, + ListTasksResultSchema, + CreateTaskResultSchema +]); +var McpError = class _McpError extends Error { + constructor(code, message, data) { + super(`MCP error ${code}: ${message}`); + this.code = code; + this.data = data; + this.name = "McpError"; + } + /** + * Factory method to create the appropriate error type based on the error code and data + */ + static fromError(code, message, data) { + if (code === ErrorCode.UrlElicitationRequired && data) { + const errorData = data; + if (errorData.elicitations) { + return new UrlElicitationRequiredError(errorData.elicitations, message); + } + } + return new _McpError(code, message, data); + } +}; +var UrlElicitationRequiredError = class extends McpError { + constructor(elicitations, message = `URL elicitation${elicitations.length > 1 ? "s" : ""} required`) { + super(ErrorCode.UrlElicitationRequired, message, { + elicitations + }); + } + get elicitations() { + return this.data?.elicitations ?? []; + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js +var ReadBuffer = class { + append(chunk) { + this._buffer = this._buffer ? Buffer.concat([this._buffer, chunk]) : chunk; + } + readMessage() { + if (!this._buffer) { + return null; + } + const index = this._buffer.indexOf("\n"); + if (index === -1) { + return null; + } + const line = this._buffer.toString("utf8", 0, index).replace(/\r$/, ""); + this._buffer = this._buffer.subarray(index + 1); + return deserializeMessage(line); + } + clear() { + this._buffer = void 0; + } +}; +function deserializeMessage(line) { + return JSONRPCMessageSchema.parse(JSON.parse(line)); +} +function serializeMessage(message) { + return JSON.stringify(message) + "\n"; +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js +var StdioServerTransport = class { + constructor(_stdin = process2.stdin, _stdout = process2.stdout) { + this._stdin = _stdin; + this._stdout = _stdout; + this._readBuffer = new ReadBuffer(); + this._started = false; + this._ondata = (chunk) => { + this._readBuffer.append(chunk); + this.processReadBuffer(); + }; + this._onerror = (error2) => { + this.onerror?.(error2); + }; + } + /** + * Starts listening for messages on stdin. + */ + async start() { + if (this._started) { + throw new Error("StdioServerTransport already started! If using Server class, note that connect() calls start() automatically."); + } + this._started = true; + this._stdin.on("data", this._ondata); + this._stdin.on("error", this._onerror); + } + processReadBuffer() { + while (true) { + try { + const message = this._readBuffer.readMessage(); + if (message === null) { + break; + } + this.onmessage?.(message); + } catch (error2) { + this.onerror?.(error2); + } + } + } + async close() { + this._stdin.off("data", this._ondata); + this._stdin.off("error", this._onerror); + const remainingDataListeners = this._stdin.listenerCount("data"); + if (remainingDataListeners === 0) { + this._stdin.pause(); + } + this._readBuffer.clear(); + this.onclose?.(); + } + send(message) { + return new Promise((resolve) => { + const json = serializeMessage(message); + if (this._stdout.write(json)) { + resolve(); + } else { + this._stdout.once("drain", resolve); + } + }); + } +}; + +// src/cli/companionCli.ts +import { + spawn +} from "node:child_process"; +import fs from "node:fs"; +import path from "node:path"; + +// src/utils/boundedError.ts +var ERROR_DETAIL_BUDGET_BYTES = 4 * 1024; +var ERROR_TRUNCATION_MARKER = " [truncated]"; +function boundedErrorDetail(error2) { + return boundedErrorText( + error2 instanceof Error ? error2.message : String(error2) + ); +} +function boundedErrorMessage(prefix, detail) { + return boundedErrorParts( + detail === void 0 ? [prefix] : [ + prefix, + detail instanceof Error ? detail.message : String(detail) + ] + ); +} +function boundedError(error2, prefix = "") { + return new Error( + prefix ? boundedErrorMessage(prefix, error2) : boundedErrorDetail(error2) + ); +} +function boundedErrorText(value) { + return boundedErrorParts([value]); +} +function boundedErrorParts(parts) { + const markerBytes = Buffer.byteLength(ERROR_TRUNCATION_MARKER); + const contentBudget = ERROR_DETAIL_BUDGET_BYTES - markerBytes; + let output = ""; + let outputBytes = 0; + for (const part of parts) { + for (const character of part) { + const characterBytes = Buffer.byteLength(character); + if (outputBytes + characterBytes > contentBudget) { + return output + ERROR_TRUNCATION_MARKER; + } + output += character; + outputBytes += characterBytes; + } + } + return output; +} + +// src/cli/companionCli.ts +var CLI_DOCUMENTATION_URL = "https://docs.unity.com/en-us/unity-cli/use-unity-cli"; +function parseCompanionArguments(argv, isUnityProject = defaultUnityProjectValidator) { + let projectPath; + let unityCliPath; + for (let index = 0; index < argv.length; index += 2) { + const flag = argv[index]; + const value = argv[index + 1]; + if (flag !== "--project-path" && flag !== "--unity-cli-path") { + throw new Error( + boundedErrorMessage("Unknown argument: ", flag ?? "") + ); + } + if (!value || value.startsWith("--")) { + throw new Error(`${flag} requires a value.`); + } + if (flag === "--project-path") { + if (projectPath !== void 0) { + throw new Error("Duplicate --project-path argument."); + } + projectPath = value; + } else { + if (unityCliPath !== void 0) { + throw new Error("Duplicate --unity-cli-path argument."); + } + unityCliPath = value; + } + } + if (!projectPath) { + throw new Error("--project-path is required."); + } + if (!path.isAbsolute(projectPath)) { + throw new Error("--project-path must be absolute."); + } + if (!isUnityProject(projectPath)) { + throw new Error( + boundedErrorMessage( + "--project-path must identify an existing Unity project: ", + projectPath + ) + ); + } + if (unityCliPath && !path.isAbsolute(unityCliPath)) { + throw new Error("--unity-cli-path must be absolute."); + } + return { projectPath: path.resolve(projectPath), unityCliPath }; +} +function resolveUnityCliPath(explicitPath, environment = process.env) { + const environmentPath = environment.UNITY_CLI_PATH?.trim(); + return explicitPath || environmentPath || "unity"; +} +async function checkUnityCli(command, runVersion = runUnityCliVersion, options = {}) { + let output; + try { + output = await runVersion(command, ["--version"], options); + } catch (error2) { + throw actionableCliError( + boundedErrorMessage( + `Unity CLI could not be started at "${boundedErrorDetail(command)}": `, + error2 + ) + ); + } + const version2 = parseVersion(`${output.stdout} +${output.stderr}`); + if (!version2) { + throw actionableCliError( + `Unity CLI returned an unrecognized version from "${command}".` + ); + } + if (compareVersion(version2, MINIMUM_VERSION) < 0) { + throw actionableCliError( + `Unity CLI ${version2.raw} is incompatible; version ${MINIMUM_VERSION.raw} or newer is required.` + ); + } + return { + command, + version: version2.raw, + warning: version2.major > 1n ? `Unity CLI ${version2.raw} is newer than the tested major version 1.` : void 0 + }; +} +var SEMVER_PATTERN = /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; +var MINIMUM_VERSION = { + raw: "1.0.0-beta.2", + major: 1n, + minor: 0n, + patch: 0n, + prerelease: [ + { numeric: false, value: "beta", raw: "beta" }, + { numeric: true, value: 2n, raw: "2" } + ], + build: [] +}; +function parseVersion(output) { + for (const token of output.trim().split(/\s+/)) { + const parsed = parseSemVerToken( + /^v[0-9]/.test(token) ? token.slice(1) : token + ); + if (parsed) return parsed; + } + return void 0; +} +function parseSemVerToken(token) { + const match = SEMVER_PATTERN.exec(token); + if (!match) return void 0; + const prereleaseTokens = match[4]?.split(".") ?? []; + const prerelease = []; + for (const identifier of prereleaseTokens) { + if (/^[0-9]+$/.test(identifier)) { + if (identifier.length > 1 && identifier.startsWith("0")) return void 0; + prerelease.push({ + numeric: true, + value: BigInt(identifier), + raw: identifier + }); + } else { + prerelease.push({ + numeric: false, + value: identifier, + raw: identifier + }); + } + } + return { + raw: token, + major: BigInt(match[1]), + minor: BigInt(match[2]), + patch: BigInt(match[3]), + prerelease, + build: match[5]?.split(".") ?? [] + }; +} +function compareVersion(left, right) { + for (const key of ["major", "minor", "patch"]) { + if (left[key] !== right[key]) { + return left[key] > right[key] ? 1 : -1; + } + } + if (left.prerelease.length === 0 && right.prerelease.length === 0) return 0; + if (left.prerelease.length === 0) return 1; + if (right.prerelease.length === 0) return -1; + const identifierCount = Math.max( + left.prerelease.length, + right.prerelease.length + ); + for (let index = 0; index < identifierCount; index++) { + const leftIdentifier = left.prerelease[index]; + const rightIdentifier = right.prerelease[index]; + if (!leftIdentifier) return -1; + if (!rightIdentifier) return 1; + if (leftIdentifier.numeric && rightIdentifier.numeric) { + if (leftIdentifier.value === rightIdentifier.value) continue; + return leftIdentifier.value > rightIdentifier.value ? 1 : -1; + } + if (leftIdentifier.numeric !== rightIdentifier.numeric) { + return leftIdentifier.numeric ? -1 : 1; + } + const leftValue = leftIdentifier.value; + const rightValue = rightIdentifier.value; + if (leftValue === rightValue) continue; + return leftValue > rightValue ? 1 : -1; + } + return 0; +} +function defaultUnityProjectValidator(candidate) { + try { + return fs.statSync(candidate).isDirectory() && fs.statSync(path.join(candidate, "Assets")).isDirectory() && fs.statSync(path.join(candidate, "ProjectSettings")).isDirectory(); + } catch { + return false; + } +} +async function runUnityCliVersion(command, args, options = {}, spawnProcess = spawn) { + if (args.length !== 1 || args[0] !== "--version") { + throw new Error("Unity CLI validation may invoke only --version."); + } + if (options.signal?.aborted) { + throw new Error("Unity CLI version check was cancelled."); + } + const timeoutMs = options.timeoutMs ?? 1e4; + const detached = process.platform !== "win32"; + return new Promise((resolve, reject) => { + let child; + try { + child = spawnProcess(command, [...args], { + shell: false, + detached, + windowsHide: true, + stdio: ["ignore", "pipe", "pipe"] + }); + } catch (error2) { + reject(error2); + return; + } + let stdout = ""; + let stderr = ""; + let settled = false; + const maxOutputBytes = 64 * 1024; + const cleanup = () => { + clearTimeout(timeout); + options.signal?.removeEventListener("abort", cancel); + }; + const finish = (error2, result) => { + if (settled) return; + settled = true; + cleanup(); + if (error2) reject(error2); + else resolve(result ?? { stdout, stderr }); + }; + const terminate = () => { + child.stdout?.destroy(); + child.stderr?.destroy(); + if (detached && child.pid) { + try { + process.kill(-child.pid, "SIGKILL"); + return; + } catch { + } + } + try { + child.kill("SIGKILL"); + } catch { + } + }; + const append = (target, chunk) => { + if (settled) return; + const value = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk); + if (target === "stdout") stdout += value; + else stderr += value; + if (Buffer.byteLength(stdout) + Buffer.byteLength(stderr) > maxOutputBytes) { + terminate(); + finish(new Error("Unity CLI version output exceeded 64 KiB.")); + } + }; + const cancel = () => { + terminate(); + finish(new Error("Unity CLI version check was cancelled.")); + }; + const timeout = setTimeout(() => { + terminate(); + finish(new Error(`Unity CLI version check timed out after ${timeoutMs}ms.`)); + }, timeoutMs); + child.stdout?.on("data", (chunk) => append("stdout", chunk)); + child.stderr?.on("data", (chunk) => append("stderr", chunk)); + child.once("error", (error2) => finish(error2)); + child.once("close", (code, signal) => { + if (code === 0) { + finish(void 0, { stdout, stderr }); + } else { + finish( + new Error( + `Unity CLI --version exited with ${signal ? `signal ${signal}` : `code ${code ?? "unknown"}`}.` + ) + ); + } + }); + if (options.signal?.aborted) { + cancel(); + } else { + options.signal?.addEventListener("abort", cancel, { once: true }); + } + }); +} +function actionableCliError(message) { + return new Error( + boundedErrorText( + `${message} Install or update Unity CLI: ${CLI_DOCUMENTATION_URL}` + ) + ); +} + +// src/companionLifecycle.ts +function installShutdownHandlers(options) { + let shutdownPromise; + const shutdown = () => { + shutdownPromise ??= (async () => { + const results = await Promise.allSettled([ + options.closeOfficialClient(), + options.closeServer() + ]); + for (const result of results) { + if (result.status === "rejected") { + options.onError?.(result.reason); + } + } + })(); + return shutdownPromise; + }; + const trigger = () => { + void shutdown(); + }; + const bindings = [ + [options.signals, "SIGINT"], + [options.signals, "SIGTERM"], + [options.stdin, "close"], + [options.stdin, "end"] + ]; + for (const [source, event] of bindings) { + source.on(event, trigger); + } + return { + shutdown, + dispose: () => { + for (const [source, event] of bindings) { + source.off(event, trigger); + } + } + }; +} + +// node_modules/zod/v3/helpers/util.js +var util; +(function(util2) { + util2.assertEqual = (_) => { + }; + function assertIs2(_arg) { + } + util2.assertIs = assertIs2; + function assertNever2(_x) { + throw new Error(); + } + util2.assertNever = assertNever2; + util2.arrayToEnum = (items) => { + const obj = {}; + for (const item of items) { + obj[item] = item; + } + return obj; + }; + util2.getValidEnumValues = (obj) => { + const validKeys = util2.objectKeys(obj).filter((k2) => typeof obj[obj[k2]] !== "number"); + const filtered = {}; + for (const k2 of validKeys) { + filtered[k2] = obj[k2]; + } + return util2.objectValues(filtered); + }; + util2.objectValues = (obj) => { + return util2.objectKeys(obj).map(function(e) { + return obj[e]; + }); + }; + util2.objectKeys = typeof Object.keys === "function" ? (obj) => Object.keys(obj) : (object3) => { + const keys = []; + for (const key in object3) { + if (Object.prototype.hasOwnProperty.call(object3, key)) { + keys.push(key); + } + } + return keys; + }; + util2.find = (arr, checker) => { + for (const item of arr) { + if (checker(item)) + return item; + } + return void 0; + }; + util2.isInteger = typeof Number.isInteger === "function" ? (val) => Number.isInteger(val) : (val) => typeof val === "number" && Number.isFinite(val) && Math.floor(val) === val; + function joinValues2(array2, separator = " | ") { + return array2.map((val) => typeof val === "string" ? `'${val}'` : val).join(separator); + } + util2.joinValues = joinValues2; + util2.jsonStringifyReplacer = (_, value) => { + if (typeof value === "bigint") { + return value.toString(); + } + return value; + }; +})(util || (util = {})); +var objectUtil; +(function(objectUtil2) { + objectUtil2.mergeShapes = (first, second) => { + return { + ...first, + ...second + // second overwrites first + }; + }; +})(objectUtil || (objectUtil = {})); +var ZodParsedType = util.arrayToEnum([ + "string", + "nan", + "number", + "integer", + "float", + "boolean", + "date", + "bigint", + "symbol", + "function", + "undefined", + "null", + "array", + "object", + "unknown", + "promise", + "void", + "never", + "map", + "set" +]); +var getParsedType2 = (data) => { + const t = typeof data; + switch (t) { + case "undefined": + return ZodParsedType.undefined; + case "string": + return ZodParsedType.string; + case "number": + return Number.isNaN(data) ? ZodParsedType.nan : ZodParsedType.number; + case "boolean": + return ZodParsedType.boolean; + case "function": + return ZodParsedType.function; + case "bigint": + return ZodParsedType.bigint; + case "symbol": + return ZodParsedType.symbol; + case "object": + if (Array.isArray(data)) { + return ZodParsedType.array; + } + if (data === null) { + return ZodParsedType.null; + } + if (data.then && typeof data.then === "function" && data.catch && typeof data.catch === "function") { + return ZodParsedType.promise; + } + if (typeof Map !== "undefined" && data instanceof Map) { + return ZodParsedType.map; + } + if (typeof Set !== "undefined" && data instanceof Set) { + return ZodParsedType.set; + } + if (typeof Date !== "undefined" && data instanceof Date) { + return ZodParsedType.date; + } + return ZodParsedType.object; + default: + return ZodParsedType.unknown; + } +}; + +// node_modules/zod/v3/ZodError.js +var ZodIssueCode = util.arrayToEnum([ + "invalid_type", + "invalid_literal", + "custom", + "invalid_union", + "invalid_union_discriminator", + "invalid_enum_value", + "unrecognized_keys", + "invalid_arguments", + "invalid_return_type", + "invalid_date", + "invalid_string", + "too_small", + "too_big", + "invalid_intersection_types", + "not_multiple_of", + "not_finite" +]); +var ZodError2 = class _ZodError extends Error { + get errors() { + return this.issues; + } + constructor(issues) { + super(); + this.issues = []; + this.addIssue = (sub) => { + this.issues = [...this.issues, sub]; + }; + this.addIssues = (subs = []) => { + this.issues = [...this.issues, ...subs]; + }; + const actualProto = new.target.prototype; + if (Object.setPrototypeOf) { + Object.setPrototypeOf(this, actualProto); + } else { + this.__proto__ = actualProto; + } + this.name = "ZodError"; + this.issues = issues; + } + format(_mapper) { + const mapper = _mapper || function(issue2) { + return issue2.message; + }; + const fieldErrors = { _errors: [] }; + const processError = (error2) => { + for (const issue2 of error2.issues) { + if (issue2.code === "invalid_union") { + issue2.unionErrors.map(processError); + } else if (issue2.code === "invalid_return_type") { + processError(issue2.returnTypeError); + } else if (issue2.code === "invalid_arguments") { + processError(issue2.argumentsError); + } else if (issue2.path.length === 0) { + fieldErrors._errors.push(mapper(issue2)); + } else { + let curr = fieldErrors; + let i = 0; + while (i < issue2.path.length) { + const el2 = issue2.path[i]; + const terminal = i === issue2.path.length - 1; + if (!terminal) { + curr[el2] = curr[el2] || { _errors: [] }; + } else { + curr[el2] = curr[el2] || { _errors: [] }; + curr[el2]._errors.push(mapper(issue2)); + } + curr = curr[el2]; + i++; + } + } + } + }; + processError(this); + return fieldErrors; + } + static assert(value) { + if (!(value instanceof _ZodError)) { + throw new Error(`Not a ZodError: ${value}`); + } + } + toString() { + return this.message; + } + get message() { + return JSON.stringify(this.issues, util.jsonStringifyReplacer, 2); + } + get isEmpty() { + return this.issues.length === 0; + } + flatten(mapper = (issue2) => issue2.message) { + const fieldErrors = {}; + const formErrors = []; + for (const sub of this.issues) { + if (sub.path.length > 0) { + const firstEl = sub.path[0]; + fieldErrors[firstEl] = fieldErrors[firstEl] || []; + fieldErrors[firstEl].push(mapper(sub)); + } else { + formErrors.push(mapper(sub)); + } + } + return { formErrors, fieldErrors }; + } + get formErrors() { + return this.flatten(); + } +}; +ZodError2.create = (issues) => { + const error2 = new ZodError2(issues); + return error2; +}; + +// node_modules/zod/v3/locales/en.js +var errorMap = (issue2, _ctx) => { + let message; + switch (issue2.code) { + case ZodIssueCode.invalid_type: + if (issue2.received === ZodParsedType.undefined) { + message = "Required"; + } else { + message = `Expected ${issue2.expected}, received ${issue2.received}`; + } + break; + case ZodIssueCode.invalid_literal: + message = `Invalid literal value, expected ${JSON.stringify(issue2.expected, util.jsonStringifyReplacer)}`; + break; + case ZodIssueCode.unrecognized_keys: + message = `Unrecognized key(s) in object: ${util.joinValues(issue2.keys, ", ")}`; + break; + case ZodIssueCode.invalid_union: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_union_discriminator: + message = `Invalid discriminator value. Expected ${util.joinValues(issue2.options)}`; + break; + case ZodIssueCode.invalid_enum_value: + message = `Invalid enum value. Expected ${util.joinValues(issue2.options)}, received '${issue2.received}'`; + break; + case ZodIssueCode.invalid_arguments: + message = `Invalid function arguments`; + break; + case ZodIssueCode.invalid_return_type: + message = `Invalid function return type`; + break; + case ZodIssueCode.invalid_date: + message = `Invalid date`; + break; + case ZodIssueCode.invalid_string: + if (typeof issue2.validation === "object") { + if ("includes" in issue2.validation) { + message = `Invalid input: must include "${issue2.validation.includes}"`; + if (typeof issue2.validation.position === "number") { + message = `${message} at one or more positions greater than or equal to ${issue2.validation.position}`; + } + } else if ("startsWith" in issue2.validation) { + message = `Invalid input: must start with "${issue2.validation.startsWith}"`; + } else if ("endsWith" in issue2.validation) { + message = `Invalid input: must end with "${issue2.validation.endsWith}"`; + } else { + util.assertNever(issue2.validation); + } + } else if (issue2.validation !== "regex") { + message = `Invalid ${issue2.validation}`; + } else { + message = "Invalid"; + } + break; + case ZodIssueCode.too_small: + if (issue2.type === "array") + message = `Array must contain ${issue2.exact ? "exactly" : issue2.inclusive ? `at least` : `more than`} ${issue2.minimum} element(s)`; + else if (issue2.type === "string") + message = `String must contain ${issue2.exact ? "exactly" : issue2.inclusive ? `at least` : `over`} ${issue2.minimum} character(s)`; + else if (issue2.type === "number") + message = `Number must be ${issue2.exact ? `exactly equal to ` : issue2.inclusive ? `greater than or equal to ` : `greater than `}${issue2.minimum}`; + else if (issue2.type === "bigint") + message = `Number must be ${issue2.exact ? `exactly equal to ` : issue2.inclusive ? `greater than or equal to ` : `greater than `}${issue2.minimum}`; + else if (issue2.type === "date") + message = `Date must be ${issue2.exact ? `exactly equal to ` : issue2.inclusive ? `greater than or equal to ` : `greater than `}${new Date(Number(issue2.minimum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.too_big: + if (issue2.type === "array") + message = `Array must contain ${issue2.exact ? `exactly` : issue2.inclusive ? `at most` : `less than`} ${issue2.maximum} element(s)`; + else if (issue2.type === "string") + message = `String must contain ${issue2.exact ? `exactly` : issue2.inclusive ? `at most` : `under`} ${issue2.maximum} character(s)`; + else if (issue2.type === "number") + message = `Number must be ${issue2.exact ? `exactly` : issue2.inclusive ? `less than or equal to` : `less than`} ${issue2.maximum}`; + else if (issue2.type === "bigint") + message = `BigInt must be ${issue2.exact ? `exactly` : issue2.inclusive ? `less than or equal to` : `less than`} ${issue2.maximum}`; + else if (issue2.type === "date") + message = `Date must be ${issue2.exact ? `exactly` : issue2.inclusive ? `smaller than or equal to` : `smaller than`} ${new Date(Number(issue2.maximum))}`; + else + message = "Invalid input"; + break; + case ZodIssueCode.custom: + message = `Invalid input`; + break; + case ZodIssueCode.invalid_intersection_types: + message = `Intersection results could not be merged`; + break; + case ZodIssueCode.not_multiple_of: + message = `Number must be a multiple of ${issue2.multipleOf}`; + break; + case ZodIssueCode.not_finite: + message = "Number must be finite"; + break; + default: + message = _ctx.defaultError; + util.assertNever(issue2); + } + return { message }; +}; +var en_default2 = errorMap; + +// node_modules/zod/v3/errors.js +var overrideErrorMap = en_default2; +function getErrorMap() { + return overrideErrorMap; +} + +// node_modules/zod/v3/helpers/parseUtil.js +var makeIssue = (params) => { + const { data, path: path3, errorMaps, issueData } = params; + const fullPath = [...path3, ...issueData.path || []]; + const fullIssue = { + ...issueData, + path: fullPath + }; + if (issueData.message !== void 0) { + return { + ...issueData, + path: fullPath, + message: issueData.message + }; + } + let errorMessage = ""; + const maps = errorMaps.filter((m2) => !!m2).slice().reverse(); + for (const map of maps) { + errorMessage = map(fullIssue, { data, defaultError: errorMessage }).message; + } + return { + ...issueData, + path: fullPath, + message: errorMessage + }; +}; +function addIssueToContext(ctx, issueData) { + const overrideMap = getErrorMap(); + const issue2 = makeIssue({ + issueData, + data: ctx.data, + path: ctx.path, + errorMaps: [ + ctx.common.contextualErrorMap, + // contextual error map is first priority + ctx.schemaErrorMap, + // then schema-bound map if available + overrideMap, + // then global override map + overrideMap === en_default2 ? void 0 : en_default2 + // then global default map + ].filter((x2) => !!x2) + }); + ctx.common.issues.push(issue2); +} +var ParseStatus = class _ParseStatus { + constructor() { + this.value = "valid"; + } + dirty() { + if (this.value === "valid") + this.value = "dirty"; + } + abort() { + if (this.value !== "aborted") + this.value = "aborted"; + } + static mergeArray(status, results) { + const arrayValue = []; + for (const s2 of results) { + if (s2.status === "aborted") + return INVALID; + if (s2.status === "dirty") + status.dirty(); + arrayValue.push(s2.value); + } + return { status: status.value, value: arrayValue }; + } + static async mergeObjectAsync(status, pairs) { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value + }); + } + return _ParseStatus.mergeObjectSync(status, syncPairs); + } + static mergeObjectSync(status, pairs) { + const finalObject = {}; + for (const pair of pairs) { + const { key, value } = pair; + if (key.status === "aborted") + return INVALID; + if (value.status === "aborted") + return INVALID; + if (key.status === "dirty") + status.dirty(); + if (value.status === "dirty") + status.dirty(); + if (key.value !== "__proto__" && (typeof value.value !== "undefined" || pair.alwaysSet)) { + finalObject[key.value] = value.value; + } + } + return { status: status.value, value: finalObject }; + } +}; +var INVALID = Object.freeze({ + status: "aborted" +}); +var DIRTY = (value) => ({ status: "dirty", value }); +var OK = (value) => ({ status: "valid", value }); +var isAborted = (x2) => x2.status === "aborted"; +var isDirty = (x2) => x2.status === "dirty"; +var isValid = (x2) => x2.status === "valid"; +var isAsync = (x2) => typeof Promise !== "undefined" && x2 instanceof Promise; + +// node_modules/zod/v3/helpers/errorUtil.js +var errorUtil; +(function(errorUtil2) { + errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {}; + errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message; +})(errorUtil || (errorUtil = {})); + +// node_modules/zod/v3/types.js +var ParseInputLazyPath = class { + constructor(parent, value, path3, key) { + this._cachedPath = []; + this.parent = parent; + this.data = value; + this._path = path3; + this._key = key; + } + get path() { + if (!this._cachedPath.length) { + if (Array.isArray(this._key)) { + this._cachedPath.push(...this._path, ...this._key); + } else { + this._cachedPath.push(...this._path, this._key); + } + } + return this._cachedPath; + } +}; +var handleResult = (ctx, result) => { + if (isValid(result)) { + return { success: true, data: result.value }; + } else { + if (!ctx.common.issues.length) { + throw new Error("Validation failed but no issues detected."); + } + return { + success: false, + get error() { + if (this._error) + return this._error; + const error2 = new ZodError2(ctx.common.issues); + this._error = error2; + return this._error; + } + }; + } +}; +function processCreateParams(params) { + if (!params) + return {}; + const { errorMap: errorMap2, invalid_type_error, required_error, description } = params; + if (errorMap2 && (invalid_type_error || required_error)) { + throw new Error(`Can't use "invalid_type_error" or "required_error" in conjunction with custom error map.`); + } + if (errorMap2) + return { errorMap: errorMap2, description }; + const customMap = (iss, ctx) => { + const { message } = params; + if (iss.code === "invalid_enum_value") { + return { message: message ?? ctx.defaultError }; + } + if (typeof ctx.data === "undefined") { + return { message: message ?? required_error ?? ctx.defaultError }; + } + if (iss.code !== "invalid_type") + return { message: ctx.defaultError }; + return { message: message ?? invalid_type_error ?? ctx.defaultError }; + }; + return { errorMap: customMap, description }; +} +var ZodType2 = class { + get description() { + return this._def.description; + } + _getType(input) { + return getParsedType2(input.data); + } + _getOrReturnCtx(input, ctx) { + return ctx || { + common: input.parent.common, + data: input.data, + parsedType: getParsedType2(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + }; + } + _processInputParams(input) { + return { + status: new ParseStatus(), + ctx: { + common: input.parent.common, + data: input.data, + parsedType: getParsedType2(input.data), + schemaErrorMap: this._def.errorMap, + path: input.path, + parent: input.parent + } + }; + } + _parseSync(input) { + const result = this._parse(input); + if (isAsync(result)) { + throw new Error("Synchronous parse encountered promise."); + } + return result; + } + _parseAsync(input) { + const result = this._parse(input); + return Promise.resolve(result); + } + parse(data, params) { + const result = this.safeParse(data, params); + if (result.success) + return result.data; + throw result.error; + } + safeParse(data, params) { + const ctx = { + common: { + issues: [], + async: params?.async ?? false, + contextualErrorMap: params?.errorMap + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType2(data) + }; + const result = this._parseSync({ data, path: ctx.path, parent: ctx }); + return handleResult(ctx, result); + } + "~validate"(data) { + const ctx = { + common: { + issues: [], + async: !!this["~standard"].async + }, + path: [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType2(data) + }; + if (!this["~standard"].async) { + try { + const result = this._parseSync({ data, path: [], parent: ctx }); + return isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }; + } catch (err) { + if (err?.message?.toLowerCase()?.includes("encountered")) { + this["~standard"].async = true; + } + ctx.common = { + issues: [], + async: true + }; + } + } + return this._parseAsync({ data, path: [], parent: ctx }).then((result) => isValid(result) ? { + value: result.value + } : { + issues: ctx.common.issues + }); + } + async parseAsync(data, params) { + const result = await this.safeParseAsync(data, params); + if (result.success) + return result.data; + throw result.error; + } + async safeParseAsync(data, params) { + const ctx = { + common: { + issues: [], + contextualErrorMap: params?.errorMap, + async: true + }, + path: params?.path || [], + schemaErrorMap: this._def.errorMap, + parent: null, + data, + parsedType: getParsedType2(data) + }; + const maybeAsyncResult = this._parse({ data, path: ctx.path, parent: ctx }); + const result = await (isAsync(maybeAsyncResult) ? maybeAsyncResult : Promise.resolve(maybeAsyncResult)); + return handleResult(ctx, result); + } + refine(check2, message) { + const getIssueProperties = (val) => { + if (typeof message === "string" || typeof message === "undefined") { + return { message }; + } else if (typeof message === "function") { + return message(val); + } else { + return message; + } + }; + return this._refinement((val, ctx) => { + const result = check2(val); + const setError = () => ctx.addIssue({ + code: ZodIssueCode.custom, + ...getIssueProperties(val) + }); + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then((data) => { + if (!data) { + setError(); + return false; + } else { + return true; + } + }); + } + if (!result) { + setError(); + return false; + } else { + return true; + } + }); + } + refinement(check2, refinementData) { + return this._refinement((val, ctx) => { + if (!check2(val)) { + ctx.addIssue(typeof refinementData === "function" ? refinementData(val, ctx) : refinementData); + return false; + } else { + return true; + } + }); + } + _refinement(refinement) { + return new ZodEffects({ + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "refinement", refinement } + }); + } + superRefine(refinement) { + return this._refinement(refinement); + } + constructor(def) { + this.spa = this.safeParseAsync; + this._def = def; + this.parse = this.parse.bind(this); + this.safeParse = this.safeParse.bind(this); + this.parseAsync = this.parseAsync.bind(this); + this.safeParseAsync = this.safeParseAsync.bind(this); + this.spa = this.spa.bind(this); + this.refine = this.refine.bind(this); + this.refinement = this.refinement.bind(this); + this.superRefine = this.superRefine.bind(this); + this.optional = this.optional.bind(this); + this.nullable = this.nullable.bind(this); + this.nullish = this.nullish.bind(this); + this.array = this.array.bind(this); + this.promise = this.promise.bind(this); + this.or = this.or.bind(this); + this.and = this.and.bind(this); + this.transform = this.transform.bind(this); + this.brand = this.brand.bind(this); + this.default = this.default.bind(this); + this.catch = this.catch.bind(this); + this.describe = this.describe.bind(this); + this.pipe = this.pipe.bind(this); + this.readonly = this.readonly.bind(this); + this.isNullable = this.isNullable.bind(this); + this.isOptional = this.isOptional.bind(this); + this["~standard"] = { + version: 1, + vendor: "zod", + validate: (data) => this["~validate"](data) + }; + } + optional() { + return ZodOptional2.create(this, this._def); + } + nullable() { + return ZodNullable2.create(this, this._def); + } + nullish() { + return this.nullable().optional(); + } + array() { + return ZodArray2.create(this); + } + promise() { + return ZodPromise.create(this, this._def); + } + or(option) { + return ZodUnion2.create([this, option], this._def); + } + and(incoming) { + return ZodIntersection2.create(this, incoming, this._def); + } + transform(transform2) { + return new ZodEffects({ + ...processCreateParams(this._def), + schema: this, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect: { type: "transform", transform: transform2 } + }); + } + default(def) { + const defaultValueFunc = typeof def === "function" ? def : () => def; + return new ZodDefault2({ + ...processCreateParams(this._def), + innerType: this, + defaultValue: defaultValueFunc, + typeName: ZodFirstPartyTypeKind.ZodDefault + }); + } + brand() { + return new ZodBranded({ + typeName: ZodFirstPartyTypeKind.ZodBranded, + type: this, + ...processCreateParams(this._def) + }); + } + catch(def) { + const catchValueFunc = typeof def === "function" ? def : () => def; + return new ZodCatch2({ + ...processCreateParams(this._def), + innerType: this, + catchValue: catchValueFunc, + typeName: ZodFirstPartyTypeKind.ZodCatch + }); + } + describe(description) { + const This = this.constructor; + return new This({ + ...this._def, + description + }); + } + pipe(target) { + return ZodPipeline.create(this, target); + } + readonly() { + return ZodReadonly2.create(this); + } + isOptional() { + return this.safeParse(void 0).success; + } + isNullable() { + return this.safeParse(null).success; + } +}; +var cuidRegex = /^c[^\s-]{8,}$/i; +var cuid2Regex = /^[0-9a-z]+$/; +var ulidRegex = /^[0-9A-HJKMNP-TV-Z]{26}$/i; +var uuidRegex = /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/i; +var nanoidRegex = /^[a-z0-9_-]{21}$/i; +var jwtRegex = /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/; +var durationRegex = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; +var emailRegex = /^(?!\.)(?!.*\.\.)([A-Z0-9_'+\-\.]*)[A-Z0-9_+-]@([A-Z0-9][A-Z0-9\-]*\.)+[A-Z]{2,}$/i; +var _emojiRegex = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`; +var emojiRegex; +var ipv4Regex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +var ipv4CidrRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/; +var ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; +var ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/; +var base64Regex = /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/; +var base64urlRegex = /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/; +var dateRegexSource = `((\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(0[469]|11)-(0[1-9]|[12]\\d|30)|(02)-(0[1-9]|1\\d|2[0-8])))`; +var dateRegex = new RegExp(`^${dateRegexSource}$`); +function timeRegexSource(args) { + let secondsRegexSource = `[0-5]\\d`; + if (args.precision) { + secondsRegexSource = `${secondsRegexSource}\\.\\d{${args.precision}}`; + } else if (args.precision == null) { + secondsRegexSource = `${secondsRegexSource}(\\.\\d+)?`; + } + const secondsQuantifier = args.precision ? "+" : "?"; + return `([01]\\d|2[0-3]):[0-5]\\d(:${secondsRegexSource})${secondsQuantifier}`; +} +function timeRegex(args) { + return new RegExp(`^${timeRegexSource(args)}$`); +} +function datetimeRegex(args) { + let regex = `${dateRegexSource}T${timeRegexSource(args)}`; + const opts = []; + opts.push(args.local ? `Z?` : `Z`); + if (args.offset) + opts.push(`([+-]\\d{2}:?\\d{2})`); + regex = `${regex}(${opts.join("|")})`; + return new RegExp(`^${regex}$`); +} +function isValidIP(ip, version2) { + if ((version2 === "v4" || !version2) && ipv4Regex.test(ip)) { + return true; + } + if ((version2 === "v6" || !version2) && ipv6Regex.test(ip)) { + return true; + } + return false; +} +function isValidJWT2(jwt, alg) { + if (!jwtRegex.test(jwt)) + return false; + try { + const [header] = jwt.split("."); + if (!header) + return false; + const base642 = header.replace(/-/g, "+").replace(/_/g, "/").padEnd(header.length + (4 - header.length % 4) % 4, "="); + const decoded = JSON.parse(atob(base642)); + if (typeof decoded !== "object" || decoded === null) + return false; + if ("typ" in decoded && decoded?.typ !== "JWT") + return false; + if (!decoded.alg) + return false; + if (alg && decoded.alg !== alg) + return false; + return true; + } catch { + return false; + } +} +function isValidCidr(ip, version2) { + if ((version2 === "v4" || !version2) && ipv4CidrRegex.test(ip)) { + return true; + } + if ((version2 === "v6" || !version2) && ipv6CidrRegex.test(ip)) { + return true; + } + return false; +} +var ZodString2 = class _ZodString2 extends ZodType2 { + _parse(input) { + if (this._def.coerce) { + input.data = String(input.data); + } + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.string) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.string, + received: ctx2.parsedType + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check2 of this._def.checks) { + if (check2.kind === "min") { + if (input.data.length < check2.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check2.value, + type: "string", + inclusive: true, + exact: false, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "max") { + if (input.data.length > check2.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check2.value, + type: "string", + inclusive: true, + exact: false, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "length") { + const tooBig = input.data.length > check2.value; + const tooSmall = input.data.length < check2.value; + if (tooBig || tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + if (tooBig) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check2.value, + type: "string", + inclusive: true, + exact: true, + message: check2.message + }); + } else if (tooSmall) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check2.value, + type: "string", + inclusive: true, + exact: true, + message: check2.message + }); + } + status.dirty(); + } + } else if (check2.kind === "email") { + if (!emailRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "email", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "emoji") { + if (!emojiRegex) { + emojiRegex = new RegExp(_emojiRegex, "u"); + } + if (!emojiRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "emoji", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "uuid") { + if (!uuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "uuid", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "nanoid") { + if (!nanoidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "nanoid", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "cuid") { + if (!cuidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "cuid2") { + if (!cuid2Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cuid2", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "ulid") { + if (!ulidRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ulid", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "url") { + try { + new URL(input.data); + } catch { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "url", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "regex") { + check2.regex.lastIndex = 0; + const testResult = check2.regex.test(input.data); + if (!testResult) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "regex", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "trim") { + input.data = input.data.trim(); + } else if (check2.kind === "includes") { + if (!input.data.includes(check2.value, check2.position)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { includes: check2.value, position: check2.position }, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "toLowerCase") { + input.data = input.data.toLowerCase(); + } else if (check2.kind === "toUpperCase") { + input.data = input.data.toUpperCase(); + } else if (check2.kind === "startsWith") { + if (!input.data.startsWith(check2.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { startsWith: check2.value }, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "endsWith") { + if (!input.data.endsWith(check2.value)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: { endsWith: check2.value }, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "datetime") { + const regex = datetimeRegex(check2); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "datetime", + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "date") { + const regex = dateRegex; + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "date", + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "time") { + const regex = timeRegex(check2); + if (!regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_string, + validation: "time", + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "duration") { + if (!durationRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "duration", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "ip") { + if (!isValidIP(input.data, check2.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "ip", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "jwt") { + if (!isValidJWT2(input.data, check2.alg)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "jwt", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "cidr") { + if (!isValidCidr(input.data, check2.version)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "cidr", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "base64") { + if (!base64Regex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "base64url") { + if (!base64urlRegex.test(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + validation: "base64url", + code: ZodIssueCode.invalid_string, + message: check2.message + }); + status.dirty(); + } + } else { + util.assertNever(check2); + } + } + return { status: status.value, value: input.data }; + } + _regex(regex, validation, message) { + return this.refinement((data) => regex.test(data), { + validation, + code: ZodIssueCode.invalid_string, + ...errorUtil.errToObj(message) + }); + } + _addCheck(check2) { + return new _ZodString2({ + ...this._def, + checks: [...this._def.checks, check2] + }); + } + email(message) { + return this._addCheck({ kind: "email", ...errorUtil.errToObj(message) }); + } + url(message) { + return this._addCheck({ kind: "url", ...errorUtil.errToObj(message) }); + } + emoji(message) { + return this._addCheck({ kind: "emoji", ...errorUtil.errToObj(message) }); + } + uuid(message) { + return this._addCheck({ kind: "uuid", ...errorUtil.errToObj(message) }); + } + nanoid(message) { + return this._addCheck({ kind: "nanoid", ...errorUtil.errToObj(message) }); + } + cuid(message) { + return this._addCheck({ kind: "cuid", ...errorUtil.errToObj(message) }); + } + cuid2(message) { + return this._addCheck({ kind: "cuid2", ...errorUtil.errToObj(message) }); + } + ulid(message) { + return this._addCheck({ kind: "ulid", ...errorUtil.errToObj(message) }); + } + base64(message) { + return this._addCheck({ kind: "base64", ...errorUtil.errToObj(message) }); + } + base64url(message) { + return this._addCheck({ + kind: "base64url", + ...errorUtil.errToObj(message) + }); + } + jwt(options) { + return this._addCheck({ kind: "jwt", ...errorUtil.errToObj(options) }); + } + ip(options) { + return this._addCheck({ kind: "ip", ...errorUtil.errToObj(options) }); + } + cidr(options) { + return this._addCheck({ kind: "cidr", ...errorUtil.errToObj(options) }); + } + datetime(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "datetime", + precision: null, + offset: false, + local: false, + message: options + }); + } + return this._addCheck({ + kind: "datetime", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + offset: options?.offset ?? false, + local: options?.local ?? false, + ...errorUtil.errToObj(options?.message) + }); + } + date(message) { + return this._addCheck({ kind: "date", message }); + } + time(options) { + if (typeof options === "string") { + return this._addCheck({ + kind: "time", + precision: null, + message: options + }); + } + return this._addCheck({ + kind: "time", + precision: typeof options?.precision === "undefined" ? null : options?.precision, + ...errorUtil.errToObj(options?.message) + }); + } + duration(message) { + return this._addCheck({ kind: "duration", ...errorUtil.errToObj(message) }); + } + regex(regex, message) { + return this._addCheck({ + kind: "regex", + regex, + ...errorUtil.errToObj(message) + }); + } + includes(value, options) { + return this._addCheck({ + kind: "includes", + value, + position: options?.position, + ...errorUtil.errToObj(options?.message) + }); + } + startsWith(value, message) { + return this._addCheck({ + kind: "startsWith", + value, + ...errorUtil.errToObj(message) + }); + } + endsWith(value, message) { + return this._addCheck({ + kind: "endsWith", + value, + ...errorUtil.errToObj(message) + }); + } + min(minLength, message) { + return this._addCheck({ + kind: "min", + value: minLength, + ...errorUtil.errToObj(message) + }); + } + max(maxLength, message) { + return this._addCheck({ + kind: "max", + value: maxLength, + ...errorUtil.errToObj(message) + }); + } + length(len, message) { + return this._addCheck({ + kind: "length", + value: len, + ...errorUtil.errToObj(message) + }); + } + /** + * Equivalent to `.min(1)` + */ + nonempty(message) { + return this.min(1, errorUtil.errToObj(message)); + } + trim() { + return new _ZodString2({ + ...this._def, + checks: [...this._def.checks, { kind: "trim" }] + }); + } + toLowerCase() { + return new _ZodString2({ + ...this._def, + checks: [...this._def.checks, { kind: "toLowerCase" }] + }); + } + toUpperCase() { + return new _ZodString2({ + ...this._def, + checks: [...this._def.checks, { kind: "toUpperCase" }] + }); + } + get isDatetime() { + return !!this._def.checks.find((ch) => ch.kind === "datetime"); + } + get isDate() { + return !!this._def.checks.find((ch) => ch.kind === "date"); + } + get isTime() { + return !!this._def.checks.find((ch) => ch.kind === "time"); + } + get isDuration() { + return !!this._def.checks.find((ch) => ch.kind === "duration"); + } + get isEmail() { + return !!this._def.checks.find((ch) => ch.kind === "email"); + } + get isURL() { + return !!this._def.checks.find((ch) => ch.kind === "url"); + } + get isEmoji() { + return !!this._def.checks.find((ch) => ch.kind === "emoji"); + } + get isUUID() { + return !!this._def.checks.find((ch) => ch.kind === "uuid"); + } + get isNANOID() { + return !!this._def.checks.find((ch) => ch.kind === "nanoid"); + } + get isCUID() { + return !!this._def.checks.find((ch) => ch.kind === "cuid"); + } + get isCUID2() { + return !!this._def.checks.find((ch) => ch.kind === "cuid2"); + } + get isULID() { + return !!this._def.checks.find((ch) => ch.kind === "ulid"); + } + get isIP() { + return !!this._def.checks.find((ch) => ch.kind === "ip"); + } + get isCIDR() { + return !!this._def.checks.find((ch) => ch.kind === "cidr"); + } + get isBase64() { + return !!this._def.checks.find((ch) => ch.kind === "base64"); + } + get isBase64url() { + return !!this._def.checks.find((ch) => ch.kind === "base64url"); + } + get minLength() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxLength() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodString2.create = (params) => { + return new ZodString2({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodString, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +function floatSafeRemainder2(val, step) { + const valDecCount = (val.toString().split(".")[1] || "").length; + const stepDecCount = (step.toString().split(".")[1] || "").length; + const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount; + const valInt = Number.parseInt(val.toFixed(decCount).replace(".", "")); + const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", "")); + return valInt % stepInt / 10 ** decCount; +} +var ZodNumber2 = class _ZodNumber extends ZodType2 { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + this.step = this.multipleOf; + } + _parse(input) { + if (this._def.coerce) { + input.data = Number(input.data); + } + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.number) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.number, + received: ctx2.parsedType + }); + return INVALID; + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check2 of this._def.checks) { + if (check2.kind === "int") { + if (!util.isInteger(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: "integer", + received: "float", + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "min") { + const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: check2.value, + type: "number", + inclusive: check2.inclusive, + exact: false, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "max") { + const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: check2.value, + type: "number", + inclusive: check2.inclusive, + exact: false, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "multipleOf") { + if (floatSafeRemainder2(input.data, check2.value) !== 0) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check2.value, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "finite") { + if (!Number.isFinite(input.data)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_finite, + message: check2.message + }); + status.dirty(); + } + } else { + util.assertNever(check2); + } + } + return { status: status.value, value: input.data }; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodNumber({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check2) { + return new _ZodNumber({ + ...this._def, + checks: [...this._def.checks, check2] + }); + } + int(message) { + return this._addCheck({ + kind: "int", + message: errorUtil.toString(message) + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: 0, + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + finite(message) { + return this._addCheck({ + kind: "finite", + message: errorUtil.toString(message) + }); + } + safe(message) { + return this._addCheck({ + kind: "min", + inclusive: true, + value: Number.MIN_SAFE_INTEGER, + message: errorUtil.toString(message) + })._addCheck({ + kind: "max", + inclusive: true, + value: Number.MAX_SAFE_INTEGER, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } + get isInt() { + return !!this._def.checks.find((ch) => ch.kind === "int" || ch.kind === "multipleOf" && util.isInteger(ch.value)); + } + get isFinite() { + let max = null; + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "finite" || ch.kind === "int" || ch.kind === "multipleOf") { + return true; + } else if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } else if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return Number.isFinite(min) && Number.isFinite(max); + } +}; +ZodNumber2.create = (params) => { + return new ZodNumber2({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodNumber, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodBigInt = class _ZodBigInt extends ZodType2 { + constructor() { + super(...arguments); + this.min = this.gte; + this.max = this.lte; + } + _parse(input) { + if (this._def.coerce) { + try { + input.data = BigInt(input.data); + } catch { + return this._getInvalidInput(input); + } + } + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.bigint) { + return this._getInvalidInput(input); + } + let ctx = void 0; + const status = new ParseStatus(); + for (const check2 of this._def.checks) { + if (check2.kind === "min") { + const tooSmall = check2.inclusive ? input.data < check2.value : input.data <= check2.value; + if (tooSmall) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + type: "bigint", + minimum: check2.value, + inclusive: check2.inclusive, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "max") { + const tooBig = check2.inclusive ? input.data > check2.value : input.data >= check2.value; + if (tooBig) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + type: "bigint", + maximum: check2.value, + inclusive: check2.inclusive, + message: check2.message + }); + status.dirty(); + } + } else if (check2.kind === "multipleOf") { + if (input.data % check2.value !== BigInt(0)) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.not_multiple_of, + multipleOf: check2.value, + message: check2.message + }); + status.dirty(); + } + } else { + util.assertNever(check2); + } + } + return { status: status.value, value: input.data }; + } + _getInvalidInput(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.bigint, + received: ctx.parsedType + }); + return INVALID; + } + gte(value, message) { + return this.setLimit("min", value, true, errorUtil.toString(message)); + } + gt(value, message) { + return this.setLimit("min", value, false, errorUtil.toString(message)); + } + lte(value, message) { + return this.setLimit("max", value, true, errorUtil.toString(message)); + } + lt(value, message) { + return this.setLimit("max", value, false, errorUtil.toString(message)); + } + setLimit(kind, value, inclusive, message) { + return new _ZodBigInt({ + ...this._def, + checks: [ + ...this._def.checks, + { + kind, + value, + inclusive, + message: errorUtil.toString(message) + } + ] + }); + } + _addCheck(check2) { + return new _ZodBigInt({ + ...this._def, + checks: [...this._def.checks, check2] + }); + } + positive(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + negative(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: false, + message: errorUtil.toString(message) + }); + } + nonpositive(message) { + return this._addCheck({ + kind: "max", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + nonnegative(message) { + return this._addCheck({ + kind: "min", + value: BigInt(0), + inclusive: true, + message: errorUtil.toString(message) + }); + } + multipleOf(value, message) { + return this._addCheck({ + kind: "multipleOf", + value, + message: errorUtil.toString(message) + }); + } + get minValue() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min; + } + get maxValue() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max; + } +}; +ZodBigInt.create = (params) => { + return new ZodBigInt({ + checks: [], + typeName: ZodFirstPartyTypeKind.ZodBigInt, + coerce: params?.coerce ?? false, + ...processCreateParams(params) + }); +}; +var ZodBoolean2 = class extends ZodType2 { + _parse(input) { + if (this._def.coerce) { + input.data = Boolean(input.data); + } + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.boolean) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.boolean, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodBoolean2.create = (params) => { + return new ZodBoolean2({ + typeName: ZodFirstPartyTypeKind.ZodBoolean, + coerce: params?.coerce || false, + ...processCreateParams(params) + }); +}; +var ZodDate = class _ZodDate extends ZodType2 { + _parse(input) { + if (this._def.coerce) { + input.data = new Date(input.data); + } + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.date) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.date, + received: ctx2.parsedType + }); + return INVALID; + } + if (Number.isNaN(input.data.getTime())) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_date + }); + return INVALID; + } + const status = new ParseStatus(); + let ctx = void 0; + for (const check2 of this._def.checks) { + if (check2.kind === "min") { + if (input.data.getTime() < check2.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + message: check2.message, + inclusive: true, + exact: false, + minimum: check2.value, + type: "date" + }); + status.dirty(); + } + } else if (check2.kind === "max") { + if (input.data.getTime() > check2.value) { + ctx = this._getOrReturnCtx(input, ctx); + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + message: check2.message, + inclusive: true, + exact: false, + maximum: check2.value, + type: "date" + }); + status.dirty(); + } + } else { + util.assertNever(check2); + } + } + return { + status: status.value, + value: new Date(input.data.getTime()) + }; + } + _addCheck(check2) { + return new _ZodDate({ + ...this._def, + checks: [...this._def.checks, check2] + }); + } + min(minDate, message) { + return this._addCheck({ + kind: "min", + value: minDate.getTime(), + message: errorUtil.toString(message) + }); + } + max(maxDate, message) { + return this._addCheck({ + kind: "max", + value: maxDate.getTime(), + message: errorUtil.toString(message) + }); + } + get minDate() { + let min = null; + for (const ch of this._def.checks) { + if (ch.kind === "min") { + if (min === null || ch.value > min) + min = ch.value; + } + } + return min != null ? new Date(min) : null; + } + get maxDate() { + let max = null; + for (const ch of this._def.checks) { + if (ch.kind === "max") { + if (max === null || ch.value < max) + max = ch.value; + } + } + return max != null ? new Date(max) : null; + } +}; +ZodDate.create = (params) => { + return new ZodDate({ + checks: [], + coerce: params?.coerce || false, + typeName: ZodFirstPartyTypeKind.ZodDate, + ...processCreateParams(params) + }); +}; +var ZodSymbol = class extends ZodType2 { + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.symbol) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.symbol, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodSymbol.create = (params) => { + return new ZodSymbol({ + typeName: ZodFirstPartyTypeKind.ZodSymbol, + ...processCreateParams(params) + }); +}; +var ZodUndefined = class extends ZodType2 { + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.undefined, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodUndefined.create = (params) => { + return new ZodUndefined({ + typeName: ZodFirstPartyTypeKind.ZodUndefined, + ...processCreateParams(params) + }); +}; +var ZodNull2 = class extends ZodType2 { + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.null) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.null, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodNull2.create = (params) => { + return new ZodNull2({ + typeName: ZodFirstPartyTypeKind.ZodNull, + ...processCreateParams(params) + }); +}; +var ZodAny = class extends ZodType2 { + constructor() { + super(...arguments); + this._any = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodAny.create = (params) => { + return new ZodAny({ + typeName: ZodFirstPartyTypeKind.ZodAny, + ...processCreateParams(params) + }); +}; +var ZodUnknown2 = class extends ZodType2 { + constructor() { + super(...arguments); + this._unknown = true; + } + _parse(input) { + return OK(input.data); + } +}; +ZodUnknown2.create = (params) => { + return new ZodUnknown2({ + typeName: ZodFirstPartyTypeKind.ZodUnknown, + ...processCreateParams(params) + }); +}; +var ZodNever2 = class extends ZodType2 { + _parse(input) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.never, + received: ctx.parsedType + }); + return INVALID; + } +}; +ZodNever2.create = (params) => { + return new ZodNever2({ + typeName: ZodFirstPartyTypeKind.ZodNever, + ...processCreateParams(params) + }); +}; +var ZodVoid = class extends ZodType2 { + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.undefined) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.void, + received: ctx.parsedType + }); + return INVALID; + } + return OK(input.data); + } +}; +ZodVoid.create = (params) => { + return new ZodVoid({ + typeName: ZodFirstPartyTypeKind.ZodVoid, + ...processCreateParams(params) + }); +}; +var ZodArray2 = class _ZodArray extends ZodType2 { + _parse(input) { + const { ctx, status } = this._processInputParams(input); + const def = this._def; + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (def.exactLength !== null) { + const tooBig = ctx.data.length > def.exactLength.value; + const tooSmall = ctx.data.length < def.exactLength.value; + if (tooBig || tooSmall) { + addIssueToContext(ctx, { + code: tooBig ? ZodIssueCode.too_big : ZodIssueCode.too_small, + minimum: tooSmall ? def.exactLength.value : void 0, + maximum: tooBig ? def.exactLength.value : void 0, + type: "array", + inclusive: true, + exact: true, + message: def.exactLength.message + }); + status.dirty(); + } + } + if (def.minLength !== null) { + if (ctx.data.length < def.minLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.minLength.message + }); + status.dirty(); + } + } + if (def.maxLength !== null) { + if (ctx.data.length > def.maxLength.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxLength.value, + type: "array", + inclusive: true, + exact: false, + message: def.maxLength.message + }); + status.dirty(); + } + } + if (ctx.common.async) { + return Promise.all([...ctx.data].map((item, i) => { + return def.type._parseAsync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + })).then((result2) => { + return ParseStatus.mergeArray(status, result2); + }); + } + const result = [...ctx.data].map((item, i) => { + return def.type._parseSync(new ParseInputLazyPath(ctx, item, ctx.path, i)); + }); + return ParseStatus.mergeArray(status, result); + } + get element() { + return this._def.type; + } + min(minLength, message) { + return new _ZodArray({ + ...this._def, + minLength: { value: minLength, message: errorUtil.toString(message) } + }); + } + max(maxLength, message) { + return new _ZodArray({ + ...this._def, + maxLength: { value: maxLength, message: errorUtil.toString(message) } + }); + } + length(len, message) { + return new _ZodArray({ + ...this._def, + exactLength: { value: len, message: errorUtil.toString(message) } + }); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodArray2.create = (schema, params) => { + return new ZodArray2({ + type: schema, + minLength: null, + maxLength: null, + exactLength: null, + typeName: ZodFirstPartyTypeKind.ZodArray, + ...processCreateParams(params) + }); +}; +function deepPartialify(schema) { + if (schema instanceof ZodObject2) { + const newShape = {}; + for (const key in schema.shape) { + const fieldSchema = schema.shape[key]; + newShape[key] = ZodOptional2.create(deepPartialify(fieldSchema)); + } + return new ZodObject2({ + ...schema._def, + shape: () => newShape + }); + } else if (schema instanceof ZodArray2) { + return new ZodArray2({ + ...schema._def, + type: deepPartialify(schema.element) + }); + } else if (schema instanceof ZodOptional2) { + return ZodOptional2.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodNullable2) { + return ZodNullable2.create(deepPartialify(schema.unwrap())); + } else if (schema instanceof ZodTuple) { + return ZodTuple.create(schema.items.map((item) => deepPartialify(item))); + } else { + return schema; + } +} +var ZodObject2 = class _ZodObject extends ZodType2 { + constructor() { + super(...arguments); + this._cached = null; + this.nonstrict = this.passthrough; + this.augment = this.extend; + } + _getCached() { + if (this._cached !== null) + return this._cached; + const shape = this._def.shape(); + const keys = util.objectKeys(shape); + this._cached = { shape, keys }; + return this._cached; + } + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.object) { + const ctx2 = this._getOrReturnCtx(input); + addIssueToContext(ctx2, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx2.parsedType + }); + return INVALID; + } + const { status, ctx } = this._processInputParams(input); + const { shape, keys: shapeKeys } = this._getCached(); + const extraKeys = []; + if (!(this._def.catchall instanceof ZodNever2 && this._def.unknownKeys === "strip")) { + for (const key in ctx.data) { + if (!shapeKeys.includes(key)) { + extraKeys.push(key); + } + } + } + const pairs = []; + for (const key of shapeKeys) { + const keyValidator = shape[key]; + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: keyValidator._parse(new ParseInputLazyPath(ctx, value, ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (this._def.catchall instanceof ZodNever2) { + const unknownKeys = this._def.unknownKeys; + if (unknownKeys === "passthrough") { + for (const key of extraKeys) { + pairs.push({ + key: { status: "valid", value: key }, + value: { status: "valid", value: ctx.data[key] } + }); + } + } else if (unknownKeys === "strict") { + if (extraKeys.length > 0) { + addIssueToContext(ctx, { + code: ZodIssueCode.unrecognized_keys, + keys: extraKeys + }); + status.dirty(); + } + } else if (unknownKeys === "strip") { + } else { + throw new Error(`Internal ZodObject error: invalid unknownKeys value.`); + } + } else { + const catchall = this._def.catchall; + for (const key of extraKeys) { + const value = ctx.data[key]; + pairs.push({ + key: { status: "valid", value: key }, + value: catchall._parse( + new ParseInputLazyPath(ctx, value, ctx.path, key) + //, ctx.child(key), value, getParsedType(value) + ), + alwaysSet: key in ctx.data + }); + } + } + if (ctx.common.async) { + return Promise.resolve().then(async () => { + const syncPairs = []; + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + syncPairs.push({ + key, + value, + alwaysSet: pair.alwaysSet + }); + } + return syncPairs; + }).then((syncPairs) => { + return ParseStatus.mergeObjectSync(status, syncPairs); + }); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get shape() { + return this._def.shape(); + } + strict(message) { + errorUtil.errToObj; + return new _ZodObject({ + ...this._def, + unknownKeys: "strict", + ...message !== void 0 ? { + errorMap: (issue2, ctx) => { + const defaultError = this._def.errorMap?.(issue2, ctx).message ?? ctx.defaultError; + if (issue2.code === "unrecognized_keys") + return { + message: errorUtil.errToObj(message).message ?? defaultError + }; + return { + message: defaultError + }; + } + } : {} + }); + } + strip() { + return new _ZodObject({ + ...this._def, + unknownKeys: "strip" + }); + } + passthrough() { + return new _ZodObject({ + ...this._def, + unknownKeys: "passthrough" + }); + } + // const AugmentFactory = + // (def: Def) => + // ( + // augmentation: Augmentation + // ): ZodObject< + // extendShape, Augmentation>, + // Def["unknownKeys"], + // Def["catchall"] + // > => { + // return new ZodObject({ + // ...def, + // shape: () => ({ + // ...def.shape(), + // ...augmentation, + // }), + // }) as any; + // }; + extend(augmentation) { + return new _ZodObject({ + ...this._def, + shape: () => ({ + ...this._def.shape(), + ...augmentation + }) + }); + } + /** + * Prior to zod@1.0.12 there was a bug in the + * inferred type of merged objects. Please + * upgrade if you are experiencing issues. + */ + merge(merging) { + const merged = new _ZodObject({ + unknownKeys: merging._def.unknownKeys, + catchall: merging._def.catchall, + shape: () => ({ + ...this._def.shape(), + ...merging._def.shape() + }), + typeName: ZodFirstPartyTypeKind.ZodObject + }); + return merged; + } + // merge< + // Incoming extends AnyZodObject, + // Augmentation extends Incoming["shape"], + // NewOutput extends { + // [k in keyof Augmentation | keyof Output]: k extends keyof Augmentation + // ? Augmentation[k]["_output"] + // : k extends keyof Output + // ? Output[k] + // : never; + // }, + // NewInput extends { + // [k in keyof Augmentation | keyof Input]: k extends keyof Augmentation + // ? Augmentation[k]["_input"] + // : k extends keyof Input + // ? Input[k] + // : never; + // } + // >( + // merging: Incoming + // ): ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"], + // NewOutput, + // NewInput + // > { + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + setKey(key, schema) { + return this.augment({ [key]: schema }); + } + // merge( + // merging: Incoming + // ): //ZodObject = (merging) => { + // ZodObject< + // extendShape>, + // Incoming["_def"]["unknownKeys"], + // Incoming["_def"]["catchall"] + // > { + // // const mergedShape = objectUtil.mergeShapes( + // // this._def.shape(), + // // merging._def.shape() + // // ); + // const merged: any = new ZodObject({ + // unknownKeys: merging._def.unknownKeys, + // catchall: merging._def.catchall, + // shape: () => + // objectUtil.mergeShapes(this._def.shape(), merging._def.shape()), + // typeName: ZodFirstPartyTypeKind.ZodObject, + // }) as any; + // return merged; + // } + catchall(index) { + return new _ZodObject({ + ...this._def, + catchall: index + }); + } + pick(mask) { + const shape = {}; + for (const key of util.objectKeys(mask)) { + if (mask[key] && this.shape[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + omit(mask) { + const shape = {}; + for (const key of util.objectKeys(this.shape)) { + if (!mask[key]) { + shape[key] = this.shape[key]; + } + } + return new _ZodObject({ + ...this._def, + shape: () => shape + }); + } + /** + * @deprecated + */ + deepPartial() { + return deepPartialify(this); + } + partial(mask) { + const newShape = {}; + for (const key of util.objectKeys(this.shape)) { + const fieldSchema = this.shape[key]; + if (mask && !mask[key]) { + newShape[key] = fieldSchema; + } else { + newShape[key] = fieldSchema.optional(); + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + required(mask) { + const newShape = {}; + for (const key of util.objectKeys(this.shape)) { + if (mask && !mask[key]) { + newShape[key] = this.shape[key]; + } else { + const fieldSchema = this.shape[key]; + let newField = fieldSchema; + while (newField instanceof ZodOptional2) { + newField = newField._def.innerType; + } + newShape[key] = newField; + } + } + return new _ZodObject({ + ...this._def, + shape: () => newShape + }); + } + keyof() { + return createZodEnum(util.objectKeys(this.shape)); + } +}; +ZodObject2.create = (shape, params) => { + return new ZodObject2({ + shape: () => shape, + unknownKeys: "strip", + catchall: ZodNever2.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject2.strictCreate = (shape, params) => { + return new ZodObject2({ + shape: () => shape, + unknownKeys: "strict", + catchall: ZodNever2.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +ZodObject2.lazycreate = (shape, params) => { + return new ZodObject2({ + shape, + unknownKeys: "strip", + catchall: ZodNever2.create(), + typeName: ZodFirstPartyTypeKind.ZodObject, + ...processCreateParams(params) + }); +}; +var ZodUnion2 = class extends ZodType2 { + _parse(input) { + const { ctx } = this._processInputParams(input); + const options = this._def.options; + function handleResults(results) { + for (const result of results) { + if (result.result.status === "valid") { + return result.result; + } + } + for (const result of results) { + if (result.result.status === "dirty") { + ctx.common.issues.push(...result.ctx.common.issues); + return result.result; + } + } + const unionErrors = results.map((result) => new ZodError2(result.ctx.common.issues)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + if (ctx.common.async) { + return Promise.all(options.map(async (option) => { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + return { + result: await option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }), + ctx: childCtx + }; + })).then(handleResults); + } else { + let dirty = void 0; + const issues = []; + for (const option of options) { + const childCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + }, + parent: null + }; + const result = option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: childCtx + }); + if (result.status === "valid") { + return result; + } else if (result.status === "dirty" && !dirty) { + dirty = { result, ctx: childCtx }; + } + if (childCtx.common.issues.length) { + issues.push(childCtx.common.issues); + } + } + if (dirty) { + ctx.common.issues.push(...dirty.ctx.common.issues); + return dirty.result; + } + const unionErrors = issues.map((issues2) => new ZodError2(issues2)); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union, + unionErrors + }); + return INVALID; + } + } + get options() { + return this._def.options; + } +}; +ZodUnion2.create = (types, params) => { + return new ZodUnion2({ + options: types, + typeName: ZodFirstPartyTypeKind.ZodUnion, + ...processCreateParams(params) + }); +}; +var getDiscriminator = (type) => { + if (type instanceof ZodLazy) { + return getDiscriminator(type.schema); + } else if (type instanceof ZodEffects) { + return getDiscriminator(type.innerType()); + } else if (type instanceof ZodLiteral2) { + return [type.value]; + } else if (type instanceof ZodEnum2) { + return type.options; + } else if (type instanceof ZodNativeEnum) { + return util.objectValues(type.enum); + } else if (type instanceof ZodDefault2) { + return getDiscriminator(type._def.innerType); + } else if (type instanceof ZodUndefined) { + return [void 0]; + } else if (type instanceof ZodNull2) { + return [null]; + } else if (type instanceof ZodOptional2) { + return [void 0, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodNullable2) { + return [null, ...getDiscriminator(type.unwrap())]; + } else if (type instanceof ZodBranded) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodReadonly2) { + return getDiscriminator(type.unwrap()); + } else if (type instanceof ZodCatch2) { + return getDiscriminator(type._def.innerType); + } else { + return []; + } +}; +var ZodDiscriminatedUnion2 = class _ZodDiscriminatedUnion extends ZodType2 { + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const discriminator = this.discriminator; + const discriminatorValue = ctx.data[discriminator]; + const option = this.optionsMap.get(discriminatorValue); + if (!option) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_union_discriminator, + options: Array.from(this.optionsMap.keys()), + path: [discriminator] + }); + return INVALID; + } + if (ctx.common.async) { + return option._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } else { + return option._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + } + } + get discriminator() { + return this._def.discriminator; + } + get options() { + return this._def.options; + } + get optionsMap() { + return this._def.optionsMap; + } + /** + * The constructor of the discriminated union schema. Its behaviour is very similar to that of the normal z.union() constructor. + * However, it only allows a union of objects, all of which need to share a discriminator property. This property must + * have a different value for each object in the union. + * @param discriminator the name of the discriminator property + * @param types an array of object schemas + * @param params + */ + static create(discriminator, options, params) { + const optionsMap = /* @__PURE__ */ new Map(); + for (const type of options) { + const discriminatorValues = getDiscriminator(type.shape[discriminator]); + if (!discriminatorValues.length) { + throw new Error(`A discriminator value for key \`${discriminator}\` could not be extracted from all schema options`); + } + for (const value of discriminatorValues) { + if (optionsMap.has(value)) { + throw new Error(`Discriminator property ${String(discriminator)} has duplicate value ${String(value)}`); + } + optionsMap.set(value, type); + } + } + return new _ZodDiscriminatedUnion({ + typeName: ZodFirstPartyTypeKind.ZodDiscriminatedUnion, + discriminator, + options, + optionsMap, + ...processCreateParams(params) + }); + } +}; +function mergeValues2(a2, b2) { + const aType = getParsedType2(a2); + const bType = getParsedType2(b2); + if (a2 === b2) { + return { valid: true, data: a2 }; + } else if (aType === ZodParsedType.object && bType === ZodParsedType.object) { + const bKeys = util.objectKeys(b2); + const sharedKeys = util.objectKeys(a2).filter((key) => bKeys.indexOf(key) !== -1); + const newObj = { ...a2, ...b2 }; + for (const key of sharedKeys) { + const sharedValue = mergeValues2(a2[key], b2[key]); + if (!sharedValue.valid) { + return { valid: false }; + } + newObj[key] = sharedValue.data; + } + return { valid: true, data: newObj }; + } else if (aType === ZodParsedType.array && bType === ZodParsedType.array) { + if (a2.length !== b2.length) { + return { valid: false }; + } + const newArray = []; + for (let index = 0; index < a2.length; index++) { + const itemA = a2[index]; + const itemB = b2[index]; + const sharedValue = mergeValues2(itemA, itemB); + if (!sharedValue.valid) { + return { valid: false }; + } + newArray.push(sharedValue.data); + } + return { valid: true, data: newArray }; + } else if (aType === ZodParsedType.date && bType === ZodParsedType.date && +a2 === +b2) { + return { valid: true, data: a2 }; + } else { + return { valid: false }; + } +} +var ZodIntersection2 = class extends ZodType2 { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const handleParsed = (parsedLeft, parsedRight) => { + if (isAborted(parsedLeft) || isAborted(parsedRight)) { + return INVALID; + } + const merged = mergeValues2(parsedLeft.value, parsedRight.value); + if (!merged.valid) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_intersection_types + }); + return INVALID; + } + if (isDirty(parsedLeft) || isDirty(parsedRight)) { + status.dirty(); + } + return { status: status.value, value: merged.data }; + }; + if (ctx.common.async) { + return Promise.all([ + this._def.left._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), + this._def.right._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }) + ]).then(([left, right]) => handleParsed(left, right)); + } else { + return handleParsed(this._def.left._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }), this._def.right._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + })); + } + } +}; +ZodIntersection2.create = (left, right, params) => { + return new ZodIntersection2({ + left, + right, + typeName: ZodFirstPartyTypeKind.ZodIntersection, + ...processCreateParams(params) + }); +}; +var ZodTuple = class _ZodTuple extends ZodType2 { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.array) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.array, + received: ctx.parsedType + }); + return INVALID; + } + if (ctx.data.length < this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + return INVALID; + } + const rest = this._def.rest; + if (!rest && ctx.data.length > this._def.items.length) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: this._def.items.length, + inclusive: true, + exact: false, + type: "array" + }); + status.dirty(); + } + const items = [...ctx.data].map((item, itemIndex) => { + const schema = this._def.items[itemIndex] || this._def.rest; + if (!schema) + return null; + return schema._parse(new ParseInputLazyPath(ctx, item, ctx.path, itemIndex)); + }).filter((x2) => !!x2); + if (ctx.common.async) { + return Promise.all(items).then((results) => { + return ParseStatus.mergeArray(status, results); + }); + } else { + return ParseStatus.mergeArray(status, items); + } + } + get items() { + return this._def.items; + } + rest(rest) { + return new _ZodTuple({ + ...this._def, + rest + }); + } +}; +ZodTuple.create = (schemas, params) => { + if (!Array.isArray(schemas)) { + throw new Error("You must pass an array of schemas to z.tuple([ ... ])"); + } + return new ZodTuple({ + items: schemas, + typeName: ZodFirstPartyTypeKind.ZodTuple, + rest: null, + ...processCreateParams(params) + }); +}; +var ZodRecord2 = class _ZodRecord extends ZodType2 { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.object) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.object, + received: ctx.parsedType + }); + return INVALID; + } + const pairs = []; + const keyType = this._def.keyType; + const valueType = this._def.valueType; + for (const key in ctx.data) { + pairs.push({ + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, key)), + value: valueType._parse(new ParseInputLazyPath(ctx, ctx.data[key], ctx.path, key)), + alwaysSet: key in ctx.data + }); + } + if (ctx.common.async) { + return ParseStatus.mergeObjectAsync(status, pairs); + } else { + return ParseStatus.mergeObjectSync(status, pairs); + } + } + get element() { + return this._def.valueType; + } + static create(first, second, third) { + if (second instanceof ZodType2) { + return new _ZodRecord({ + keyType: first, + valueType: second, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(third) + }); + } + return new _ZodRecord({ + keyType: ZodString2.create(), + valueType: first, + typeName: ZodFirstPartyTypeKind.ZodRecord, + ...processCreateParams(second) + }); + } +}; +var ZodMap = class extends ZodType2 { + get keySchema() { + return this._def.keyType; + } + get valueSchema() { + return this._def.valueType; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.map) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.map, + received: ctx.parsedType + }); + return INVALID; + } + const keyType = this._def.keyType; + const valueType = this._def.valueType; + const pairs = [...ctx.data.entries()].map(([key, value], index) => { + return { + key: keyType._parse(new ParseInputLazyPath(ctx, key, ctx.path, [index, "key"])), + value: valueType._parse(new ParseInputLazyPath(ctx, value, ctx.path, [index, "value"])) + }; + }); + if (ctx.common.async) { + const finalMap = /* @__PURE__ */ new Map(); + return Promise.resolve().then(async () => { + for (const pair of pairs) { + const key = await pair.key; + const value = await pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + }); + } else { + const finalMap = /* @__PURE__ */ new Map(); + for (const pair of pairs) { + const key = pair.key; + const value = pair.value; + if (key.status === "aborted" || value.status === "aborted") { + return INVALID; + } + if (key.status === "dirty" || value.status === "dirty") { + status.dirty(); + } + finalMap.set(key.value, value.value); + } + return { status: status.value, value: finalMap }; + } + } +}; +ZodMap.create = (keyType, valueType, params) => { + return new ZodMap({ + valueType, + keyType, + typeName: ZodFirstPartyTypeKind.ZodMap, + ...processCreateParams(params) + }); +}; +var ZodSet = class _ZodSet extends ZodType2 { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.set) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.set, + received: ctx.parsedType + }); + return INVALID; + } + const def = this._def; + if (def.minSize !== null) { + if (ctx.data.size < def.minSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_small, + minimum: def.minSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.minSize.message + }); + status.dirty(); + } + } + if (def.maxSize !== null) { + if (ctx.data.size > def.maxSize.value) { + addIssueToContext(ctx, { + code: ZodIssueCode.too_big, + maximum: def.maxSize.value, + type: "set", + inclusive: true, + exact: false, + message: def.maxSize.message + }); + status.dirty(); + } + } + const valueType = this._def.valueType; + function finalizeSet(elements2) { + const parsedSet = /* @__PURE__ */ new Set(); + for (const element of elements2) { + if (element.status === "aborted") + return INVALID; + if (element.status === "dirty") + status.dirty(); + parsedSet.add(element.value); + } + return { status: status.value, value: parsedSet }; + } + const elements = [...ctx.data.values()].map((item, i) => valueType._parse(new ParseInputLazyPath(ctx, item, ctx.path, i))); + if (ctx.common.async) { + return Promise.all(elements).then((elements2) => finalizeSet(elements2)); + } else { + return finalizeSet(elements); + } + } + min(minSize, message) { + return new _ZodSet({ + ...this._def, + minSize: { value: minSize, message: errorUtil.toString(message) } + }); + } + max(maxSize, message) { + return new _ZodSet({ + ...this._def, + maxSize: { value: maxSize, message: errorUtil.toString(message) } + }); + } + size(size, message) { + return this.min(size, message).max(size, message); + } + nonempty(message) { + return this.min(1, message); + } +}; +ZodSet.create = (valueType, params) => { + return new ZodSet({ + valueType, + minSize: null, + maxSize: null, + typeName: ZodFirstPartyTypeKind.ZodSet, + ...processCreateParams(params) + }); +}; +var ZodFunction = class _ZodFunction extends ZodType2 { + constructor() { + super(...arguments); + this.validate = this.implement; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.function) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.function, + received: ctx.parsedType + }); + return INVALID; + } + function makeArgsIssue(args, error2) { + return makeIssue({ + data: args, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default2].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_arguments, + argumentsError: error2 + } + }); + } + function makeReturnsIssue(returns, error2) { + return makeIssue({ + data: returns, + path: ctx.path, + errorMaps: [ctx.common.contextualErrorMap, ctx.schemaErrorMap, getErrorMap(), en_default2].filter((x2) => !!x2), + issueData: { + code: ZodIssueCode.invalid_return_type, + returnTypeError: error2 + } + }); + } + const params = { errorMap: ctx.common.contextualErrorMap }; + const fn2 = ctx.data; + if (this._def.returns instanceof ZodPromise) { + const me2 = this; + return OK(async function(...args) { + const error2 = new ZodError2([]); + const parsedArgs = await me2._def.args.parseAsync(args, params).catch((e) => { + error2.addIssue(makeArgsIssue(args, e)); + throw error2; + }); + const result = await Reflect.apply(fn2, this, parsedArgs); + const parsedReturns = await me2._def.returns._def.type.parseAsync(result, params).catch((e) => { + error2.addIssue(makeReturnsIssue(result, e)); + throw error2; + }); + return parsedReturns; + }); + } else { + const me2 = this; + return OK(function(...args) { + const parsedArgs = me2._def.args.safeParse(args, params); + if (!parsedArgs.success) { + throw new ZodError2([makeArgsIssue(args, parsedArgs.error)]); + } + const result = Reflect.apply(fn2, this, parsedArgs.data); + const parsedReturns = me2._def.returns.safeParse(result, params); + if (!parsedReturns.success) { + throw new ZodError2([makeReturnsIssue(result, parsedReturns.error)]); + } + return parsedReturns.data; + }); + } + } + parameters() { + return this._def.args; + } + returnType() { + return this._def.returns; + } + args(...items) { + return new _ZodFunction({ + ...this._def, + args: ZodTuple.create(items).rest(ZodUnknown2.create()) + }); + } + returns(returnType) { + return new _ZodFunction({ + ...this._def, + returns: returnType + }); + } + implement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + strictImplement(func) { + const validatedFunc = this.parse(func); + return validatedFunc; + } + static create(args, returns, params) { + return new _ZodFunction({ + args: args ? args : ZodTuple.create([]).rest(ZodUnknown2.create()), + returns: returns || ZodUnknown2.create(), + typeName: ZodFirstPartyTypeKind.ZodFunction, + ...processCreateParams(params) + }); + } +}; +var ZodLazy = class extends ZodType2 { + get schema() { + return this._def.getter(); + } + _parse(input) { + const { ctx } = this._processInputParams(input); + const lazySchema = this._def.getter(); + return lazySchema._parse({ data: ctx.data, path: ctx.path, parent: ctx }); + } +}; +ZodLazy.create = (getter, params) => { + return new ZodLazy({ + getter, + typeName: ZodFirstPartyTypeKind.ZodLazy, + ...processCreateParams(params) + }); +}; +var ZodLiteral2 = class extends ZodType2 { + _parse(input) { + if (input.data !== this._def.value) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_literal, + expected: this._def.value + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } + get value() { + return this._def.value; + } +}; +ZodLiteral2.create = (value, params) => { + return new ZodLiteral2({ + value, + typeName: ZodFirstPartyTypeKind.ZodLiteral, + ...processCreateParams(params) + }); +}; +function createZodEnum(values, params) { + return new ZodEnum2({ + values, + typeName: ZodFirstPartyTypeKind.ZodEnum, + ...processCreateParams(params) + }); +} +var ZodEnum2 = class _ZodEnum extends ZodType2 { + _parse(input) { + if (typeof input.data !== "string") { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(this._def.values); + } + if (!this._cache.has(input.data)) { + const ctx = this._getOrReturnCtx(input); + const expectedValues = this._def.values; + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get options() { + return this._def.values; + } + get enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Values() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + get Enum() { + const enumValues = {}; + for (const val of this._def.values) { + enumValues[val] = val; + } + return enumValues; + } + extract(values, newDef = this._def) { + return _ZodEnum.create(values, { + ...this._def, + ...newDef + }); + } + exclude(values, newDef = this._def) { + return _ZodEnum.create(this.options.filter((opt) => !values.includes(opt)), { + ...this._def, + ...newDef + }); + } +}; +ZodEnum2.create = createZodEnum; +var ZodNativeEnum = class extends ZodType2 { + _parse(input) { + const nativeEnumValues = util.getValidEnumValues(this._def.values); + const ctx = this._getOrReturnCtx(input); + if (ctx.parsedType !== ZodParsedType.string && ctx.parsedType !== ZodParsedType.number) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + expected: util.joinValues(expectedValues), + received: ctx.parsedType, + code: ZodIssueCode.invalid_type + }); + return INVALID; + } + if (!this._cache) { + this._cache = new Set(util.getValidEnumValues(this._def.values)); + } + if (!this._cache.has(input.data)) { + const expectedValues = util.objectValues(nativeEnumValues); + addIssueToContext(ctx, { + received: ctx.data, + code: ZodIssueCode.invalid_enum_value, + options: expectedValues + }); + return INVALID; + } + return OK(input.data); + } + get enum() { + return this._def.values; + } +}; +ZodNativeEnum.create = (values, params) => { + return new ZodNativeEnum({ + values, + typeName: ZodFirstPartyTypeKind.ZodNativeEnum, + ...processCreateParams(params) + }); +}; +var ZodPromise = class extends ZodType2 { + unwrap() { + return this._def.type; + } + _parse(input) { + const { ctx } = this._processInputParams(input); + if (ctx.parsedType !== ZodParsedType.promise && ctx.common.async === false) { + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.promise, + received: ctx.parsedType + }); + return INVALID; + } + const promisified = ctx.parsedType === ZodParsedType.promise ? ctx.data : Promise.resolve(ctx.data); + return OK(promisified.then((data) => { + return this._def.type.parseAsync(data, { + path: ctx.path, + errorMap: ctx.common.contextualErrorMap + }); + })); + } +}; +ZodPromise.create = (schema, params) => { + return new ZodPromise({ + type: schema, + typeName: ZodFirstPartyTypeKind.ZodPromise, + ...processCreateParams(params) + }); +}; +var ZodEffects = class extends ZodType2 { + innerType() { + return this._def.schema; + } + sourceType() { + return this._def.schema._def.typeName === ZodFirstPartyTypeKind.ZodEffects ? this._def.schema.sourceType() : this._def.schema; + } + _parse(input) { + const { status, ctx } = this._processInputParams(input); + const effect = this._def.effect || null; + const checkCtx = { + addIssue: (arg) => { + addIssueToContext(ctx, arg); + if (arg.fatal) { + status.abort(); + } else { + status.dirty(); + } + }, + get path() { + return ctx.path; + } + }; + checkCtx.addIssue = checkCtx.addIssue.bind(checkCtx); + if (effect.type === "preprocess") { + const processed = effect.transform(ctx.data, checkCtx); + if (ctx.common.async) { + return Promise.resolve(processed).then(async (processed2) => { + if (status.value === "aborted") + return INVALID; + const result = await this._def.schema._parseAsync({ + data: processed2, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + }); + } else { + if (status.value === "aborted") + return INVALID; + const result = this._def.schema._parseSync({ + data: processed, + path: ctx.path, + parent: ctx + }); + if (result.status === "aborted") + return INVALID; + if (result.status === "dirty") + return DIRTY(result.value); + if (status.value === "dirty") + return DIRTY(result.value); + return result; + } + } + if (effect.type === "refinement") { + const executeRefinement = (acc) => { + const result = effect.refinement(acc, checkCtx); + if (ctx.common.async) { + return Promise.resolve(result); + } + if (result instanceof Promise) { + throw new Error("Async refinement encountered during synchronous parse operation. Use .parseAsync instead."); + } + return acc; + }; + if (ctx.common.async === false) { + const inner = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + executeRefinement(inner.value); + return { status: status.value, value: inner.value }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((inner) => { + if (inner.status === "aborted") + return INVALID; + if (inner.status === "dirty") + status.dirty(); + return executeRefinement(inner.value).then(() => { + return { status: status.value, value: inner.value }; + }); + }); + } + } + if (effect.type === "transform") { + if (ctx.common.async === false) { + const base = this._def.schema._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (!isValid(base)) + return INVALID; + const result = effect.transform(base.value, checkCtx); + if (result instanceof Promise) { + throw new Error(`Asynchronous transform encountered during synchronous parse operation. Use .parseAsync instead.`); + } + return { status: status.value, value: result }; + } else { + return this._def.schema._parseAsync({ data: ctx.data, path: ctx.path, parent: ctx }).then((base) => { + if (!isValid(base)) + return INVALID; + return Promise.resolve(effect.transform(base.value, checkCtx)).then((result) => ({ + status: status.value, + value: result + })); + }); + } + } + util.assertNever(effect); + } +}; +ZodEffects.create = (schema, effect, params) => { + return new ZodEffects({ + schema, + typeName: ZodFirstPartyTypeKind.ZodEffects, + effect, + ...processCreateParams(params) + }); +}; +ZodEffects.createWithPreprocess = (preprocess2, schema, params) => { + return new ZodEffects({ + schema, + effect: { type: "preprocess", transform: preprocess2 }, + typeName: ZodFirstPartyTypeKind.ZodEffects, + ...processCreateParams(params) + }); +}; +var ZodOptional2 = class extends ZodType2 { + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 === ZodParsedType.undefined) { + return OK(void 0); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodOptional2.create = (type, params) => { + return new ZodOptional2({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodOptional, + ...processCreateParams(params) + }); +}; +var ZodNullable2 = class extends ZodType2 { + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 === ZodParsedType.null) { + return OK(null); + } + return this._def.innerType._parse(input); + } + unwrap() { + return this._def.innerType; + } +}; +ZodNullable2.create = (type, params) => { + return new ZodNullable2({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodNullable, + ...processCreateParams(params) + }); +}; +var ZodDefault2 = class extends ZodType2 { + _parse(input) { + const { ctx } = this._processInputParams(input); + let data = ctx.data; + if (ctx.parsedType === ZodParsedType.undefined) { + data = this._def.defaultValue(); + } + return this._def.innerType._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + removeDefault() { + return this._def.innerType; + } +}; +ZodDefault2.create = (type, params) => { + return new ZodDefault2({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodDefault, + defaultValue: typeof params.default === "function" ? params.default : () => params.default, + ...processCreateParams(params) + }); +}; +var ZodCatch2 = class extends ZodType2 { + _parse(input) { + const { ctx } = this._processInputParams(input); + const newCtx = { + ...ctx, + common: { + ...ctx.common, + issues: [] + } + }; + const result = this._def.innerType._parse({ + data: newCtx.data, + path: newCtx.path, + parent: { + ...newCtx + } + }); + if (isAsync(result)) { + return result.then((result2) => { + return { + status: "valid", + value: result2.status === "valid" ? result2.value : this._def.catchValue({ + get error() { + return new ZodError2(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + }); + } else { + return { + status: "valid", + value: result.status === "valid" ? result.value : this._def.catchValue({ + get error() { + return new ZodError2(newCtx.common.issues); + }, + input: newCtx.data + }) + }; + } + } + removeCatch() { + return this._def.innerType; + } +}; +ZodCatch2.create = (type, params) => { + return new ZodCatch2({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodCatch, + catchValue: typeof params.catch === "function" ? params.catch : () => params.catch, + ...processCreateParams(params) + }); +}; +var ZodNaN = class extends ZodType2 { + _parse(input) { + const parsedType2 = this._getType(input); + if (parsedType2 !== ZodParsedType.nan) { + const ctx = this._getOrReturnCtx(input); + addIssueToContext(ctx, { + code: ZodIssueCode.invalid_type, + expected: ZodParsedType.nan, + received: ctx.parsedType + }); + return INVALID; + } + return { status: "valid", value: input.data }; + } +}; +ZodNaN.create = (params) => { + return new ZodNaN({ + typeName: ZodFirstPartyTypeKind.ZodNaN, + ...processCreateParams(params) + }); +}; +var ZodBranded = class extends ZodType2 { + _parse(input) { + const { ctx } = this._processInputParams(input); + const data = ctx.data; + return this._def.type._parse({ + data, + path: ctx.path, + parent: ctx + }); + } + unwrap() { + return this._def.type; + } +}; +var ZodPipeline = class _ZodPipeline extends ZodType2 { + _parse(input) { + const { status, ctx } = this._processInputParams(input); + if (ctx.common.async) { + const handleAsync = async () => { + const inResult = await this._def.in._parseAsync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return DIRTY(inResult.value); + } else { + return this._def.out._parseAsync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + }; + return handleAsync(); + } else { + const inResult = this._def.in._parseSync({ + data: ctx.data, + path: ctx.path, + parent: ctx + }); + if (inResult.status === "aborted") + return INVALID; + if (inResult.status === "dirty") { + status.dirty(); + return { + status: "dirty", + value: inResult.value + }; + } else { + return this._def.out._parseSync({ + data: inResult.value, + path: ctx.path, + parent: ctx + }); + } + } + } + static create(a2, b2) { + return new _ZodPipeline({ + in: a2, + out: b2, + typeName: ZodFirstPartyTypeKind.ZodPipeline + }); + } +}; +var ZodReadonly2 = class extends ZodType2 { + _parse(input) { + const result = this._def.innerType._parse(input); + const freeze = (data) => { + if (isValid(data)) { + data.value = Object.freeze(data.value); + } + return data; + }; + return isAsync(result) ? result.then((data) => freeze(data)) : freeze(result); + } + unwrap() { + return this._def.innerType; + } +}; +ZodReadonly2.create = (type, params) => { + return new ZodReadonly2({ + innerType: type, + typeName: ZodFirstPartyTypeKind.ZodReadonly, + ...processCreateParams(params) + }); +}; +var late = { + object: ZodObject2.lazycreate +}; +var ZodFirstPartyTypeKind; +(function(ZodFirstPartyTypeKind2) { + ZodFirstPartyTypeKind2["ZodString"] = "ZodString"; + ZodFirstPartyTypeKind2["ZodNumber"] = "ZodNumber"; + ZodFirstPartyTypeKind2["ZodNaN"] = "ZodNaN"; + ZodFirstPartyTypeKind2["ZodBigInt"] = "ZodBigInt"; + ZodFirstPartyTypeKind2["ZodBoolean"] = "ZodBoolean"; + ZodFirstPartyTypeKind2["ZodDate"] = "ZodDate"; + ZodFirstPartyTypeKind2["ZodSymbol"] = "ZodSymbol"; + ZodFirstPartyTypeKind2["ZodUndefined"] = "ZodUndefined"; + ZodFirstPartyTypeKind2["ZodNull"] = "ZodNull"; + ZodFirstPartyTypeKind2["ZodAny"] = "ZodAny"; + ZodFirstPartyTypeKind2["ZodUnknown"] = "ZodUnknown"; + ZodFirstPartyTypeKind2["ZodNever"] = "ZodNever"; + ZodFirstPartyTypeKind2["ZodVoid"] = "ZodVoid"; + ZodFirstPartyTypeKind2["ZodArray"] = "ZodArray"; + ZodFirstPartyTypeKind2["ZodObject"] = "ZodObject"; + ZodFirstPartyTypeKind2["ZodUnion"] = "ZodUnion"; + ZodFirstPartyTypeKind2["ZodDiscriminatedUnion"] = "ZodDiscriminatedUnion"; + ZodFirstPartyTypeKind2["ZodIntersection"] = "ZodIntersection"; + ZodFirstPartyTypeKind2["ZodTuple"] = "ZodTuple"; + ZodFirstPartyTypeKind2["ZodRecord"] = "ZodRecord"; + ZodFirstPartyTypeKind2["ZodMap"] = "ZodMap"; + ZodFirstPartyTypeKind2["ZodSet"] = "ZodSet"; + ZodFirstPartyTypeKind2["ZodFunction"] = "ZodFunction"; + ZodFirstPartyTypeKind2["ZodLazy"] = "ZodLazy"; + ZodFirstPartyTypeKind2["ZodLiteral"] = "ZodLiteral"; + ZodFirstPartyTypeKind2["ZodEnum"] = "ZodEnum"; + ZodFirstPartyTypeKind2["ZodEffects"] = "ZodEffects"; + ZodFirstPartyTypeKind2["ZodNativeEnum"] = "ZodNativeEnum"; + ZodFirstPartyTypeKind2["ZodOptional"] = "ZodOptional"; + ZodFirstPartyTypeKind2["ZodNullable"] = "ZodNullable"; + ZodFirstPartyTypeKind2["ZodDefault"] = "ZodDefault"; + ZodFirstPartyTypeKind2["ZodCatch"] = "ZodCatch"; + ZodFirstPartyTypeKind2["ZodPromise"] = "ZodPromise"; + ZodFirstPartyTypeKind2["ZodBranded"] = "ZodBranded"; + ZodFirstPartyTypeKind2["ZodPipeline"] = "ZodPipeline"; + ZodFirstPartyTypeKind2["ZodReadonly"] = "ZodReadonly"; +})(ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {})); +var stringType = ZodString2.create; +var numberType = ZodNumber2.create; +var nanType = ZodNaN.create; +var bigIntType = ZodBigInt.create; +var booleanType = ZodBoolean2.create; +var dateType = ZodDate.create; +var symbolType = ZodSymbol.create; +var undefinedType = ZodUndefined.create; +var nullType = ZodNull2.create; +var anyType = ZodAny.create; +var unknownType = ZodUnknown2.create; +var neverType = ZodNever2.create; +var voidType = ZodVoid.create; +var arrayType = ZodArray2.create; +var objectType = ZodObject2.create; +var strictObjectType = ZodObject2.strictCreate; +var unionType = ZodUnion2.create; +var discriminatedUnionType = ZodDiscriminatedUnion2.create; +var intersectionType = ZodIntersection2.create; +var tupleType = ZodTuple.create; +var recordType = ZodRecord2.create; +var mapType = ZodMap.create; +var setType = ZodSet.create; +var functionType = ZodFunction.create; +var lazyType = ZodLazy.create; +var literalType = ZodLiteral2.create; +var enumType = ZodEnum2.create; +var nativeEnumType = ZodNativeEnum.create; +var promiseType = ZodPromise.create; +var effectsType = ZodEffects.create; +var optionalType = ZodOptional2.create; +var nullableType = ZodNullable2.create; +var preprocessType = ZodEffects.createWithPreprocess; +var pipelineType = ZodPipeline.create; + +// node_modules/zod/v4/mini/schemas.js +var ZodMiniType = /* @__PURE__ */ $constructor("ZodMiniType", (inst, def) => { + if (!inst._zod) + throw new Error("Uninitialized schema in ZodMiniType."); + $ZodType.init(inst, def); + inst.def = def; + inst.parse = (data, params) => parse(inst, data, params, { callee: inst.parse }); + inst.safeParse = (data, params) => safeParse(inst, data, params); + inst.parseAsync = async (data, params) => parseAsync(inst, data, params, { callee: inst.parseAsync }); + inst.safeParseAsync = async (data, params) => safeParseAsync(inst, data, params); + inst.check = (...checks) => { + return inst.clone( + { + ...def, + checks: [ + ...def.checks ?? [], + ...checks.map((ch) => typeof ch === "function" ? { _zod: { check: ch, def: { check: "custom" }, onattach: [] } } : ch) + ] + } + // { parent: true } + ); + }; + inst.clone = (_def, params) => clone(inst, _def, params); + inst.brand = () => inst; + inst.register = ((reg, meta) => { + reg.add(inst, meta); + return inst; + }); +}); +var ZodMiniObject = /* @__PURE__ */ $constructor("ZodMiniObject", (inst, def) => { + $ZodObject.init(inst, def); + ZodMiniType.init(inst, def); + util_exports.defineLazy(inst, "shape", () => def.shape); +}); +function object2(shape, params) { + const def = { + type: "object", + get shape() { + util_exports.assignProp(this, "shape", { ...shape }); + return this.shape; + }, + ...util_exports.normalizeParams(params) + }; + return new ZodMiniObject(def); +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js +function isZ4Schema(s2) { + const schema = s2; + return !!schema._zod; +} +function objectFromShape(shape) { + const values = Object.values(shape); + if (values.length === 0) + return object2({}); + const allV4 = values.every(isZ4Schema); + const allV3 = values.every((s2) => !isZ4Schema(s2)); + if (allV4) + return object2(shape); + if (allV3) + return objectType(shape); + throw new Error("Mixed Zod versions detected in object shape."); +} +function safeParse3(schema, data) { + if (isZ4Schema(schema)) { + const result2 = safeParse(schema, data); + return result2; + } + const v3Schema = schema; + const result = v3Schema.safeParse(data); + return result; +} +async function safeParseAsync3(schema, data) { + if (isZ4Schema(schema)) { + const result2 = await safeParseAsync(schema, data); + return result2; + } + const v3Schema = schema; + const result = await v3Schema.safeParseAsync(data); + return result; +} +function getObjectShape(schema) { + if (!schema) + return void 0; + let rawShape; + if (isZ4Schema(schema)) { + const v4Schema = schema; + rawShape = v4Schema._zod?.def?.shape; + } else { + const v3Schema = schema; + rawShape = v3Schema.shape; + } + if (!rawShape) + return void 0; + if (typeof rawShape === "function") { + try { + return rawShape(); + } catch { + return void 0; + } + } + return rawShape; +} +function normalizeObjectSchema(schema) { + if (!schema) + return void 0; + if (typeof schema === "object") { + const asV3 = schema; + const asV4 = schema; + if (!asV3._def && !asV4._zod) { + const values = Object.values(schema); + if (values.length > 0 && values.every((v) => typeof v === "object" && v !== null && (v._def !== void 0 || v._zod !== void 0 || typeof v.parse === "function"))) { + return objectFromShape(schema); + } + } + } + if (isZ4Schema(schema)) { + const v4Schema = schema; + const def = v4Schema._zod?.def; + if (def && (def.type === "object" || def.shape !== void 0)) { + return schema; + } + } else { + const v3Schema = schema; + if (v3Schema.shape !== void 0) { + return schema; + } + } + return void 0; +} +function getParseErrorMessage(error2) { + if (error2 && typeof error2 === "object") { + if ("message" in error2 && typeof error2.message === "string") { + return error2.message; + } + if ("issues" in error2 && Array.isArray(error2.issues) && error2.issues.length > 0) { + const firstIssue = error2.issues[0]; + if (firstIssue && typeof firstIssue === "object" && "message" in firstIssue) { + return String(firstIssue.message); + } + } + try { + return JSON.stringify(error2); + } catch { + return String(error2); + } + } + return String(error2); +} +function getSchemaDescription(schema) { + return schema.description; +} +function isSchemaOptional(schema) { + if (isZ4Schema(schema)) { + const v4Schema = schema; + return v4Schema._zod?.def?.type === "optional"; + } + const v3Schema = schema; + if (typeof schema.isOptional === "function") { + return schema.isOptional(); + } + return v3Schema._def?.typeName === "ZodOptional"; +} +function getLiteralValue(schema) { + if (isZ4Schema(schema)) { + const v4Schema = schema; + const def2 = v4Schema._zod?.def; + if (def2) { + if (def2.value !== void 0) + return def2.value; + if (Array.isArray(def2.values) && def2.values.length > 0) { + return def2.values[0]; + } + } + } + const v3Schema = schema; + const def = v3Schema._def; + if (def) { + if (def.value !== void 0) + return def.value; + if (Array.isArray(def.values) && def.values.length > 0) { + return def.values[0]; + } + } + const directValue = schema.value; + if (directValue !== void 0) + return directValue; + return void 0; +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js +function isTerminal(status) { + return status === "completed" || status === "failed" || status === "cancelled"; +} + +// node_modules/zod-to-json-schema/dist/esm/Options.js +var ignoreOverride = /* @__PURE__ */ Symbol("Let zodToJsonSchema decide on which parser to use"); +var defaultOptions = { + name: void 0, + $refStrategy: "root", + basePath: ["#"], + effectStrategy: "input", + pipeStrategy: "all", + dateStrategy: "format:date-time", + mapStrategy: "entries", + removeAdditionalStrategy: "passthrough", + allowedAdditionalProperties: true, + rejectedAdditionalProperties: false, + definitionPath: "definitions", + target: "jsonSchema7", + strictUnions: false, + definitions: {}, + errorMessages: false, + markdownDescription: false, + patternStrategy: "escape", + applyRegexFlags: false, + emailStrategy: "format:email", + base64Strategy: "contentEncoding:base64", + nameStrategy: "ref", + openAiAnyTypeName: "OpenAiAnyType" +}; +var getDefaultOptions = (options) => typeof options === "string" ? { + ...defaultOptions, + name: options +} : { + ...defaultOptions, + ...options +}; + +// node_modules/zod-to-json-schema/dist/esm/Refs.js +var getRefs = (options) => { + const _options = getDefaultOptions(options); + const currentPath = _options.name !== void 0 ? [..._options.basePath, _options.definitionPath, _options.name] : _options.basePath; + return { + ..._options, + flags: { hasReferencedOpenAiAnyType: false }, + currentPath, + propertyPath: void 0, + seen: new Map(Object.entries(_options.definitions).map(([name, def]) => [ + def._def, + { + def: def._def, + path: [..._options.basePath, _options.definitionPath, name], + // Resolution of references will be forced even though seen, so it's ok that the schema is undefined here for now. + jsonSchema: void 0 + } + ])) + }; +}; + +// node_modules/zod-to-json-schema/dist/esm/errorMessages.js +function addErrorMessage(res, key, errorMessage, refs) { + if (!refs?.errorMessages) + return; + if (errorMessage) { + res.errorMessage = { + ...res.errorMessage, + [key]: errorMessage + }; + } +} +function setResponseValueAndErrors(res, key, value, errorMessage, refs) { + res[key] = value; + addErrorMessage(res, key, errorMessage, refs); +} + +// node_modules/zod-to-json-schema/dist/esm/getRelativePath.js +var getRelativePath = (pathA, pathB) => { + let i = 0; + for (; i < pathA.length && i < pathB.length; i++) { + if (pathA[i] !== pathB[i]) + break; + } + return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/"); +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/any.js +function parseAnyDef(refs) { + if (refs.target !== "openAi") { + return {}; + } + const anyDefinitionPath = [ + ...refs.basePath, + refs.definitionPath, + refs.openAiAnyTypeName + ]; + refs.flags.hasReferencedOpenAiAnyType = true; + return { + $ref: refs.$refStrategy === "relative" ? getRelativePath(anyDefinitionPath, refs.currentPath) : anyDefinitionPath.join("/") + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/array.js +function parseArrayDef(def, refs) { + const res = { + type: "array" + }; + if (def.type?._def && def.type?._def?.typeName !== ZodFirstPartyTypeKind.ZodAny) { + res.items = parseDef(def.type._def, { + ...refs, + currentPath: [...refs.currentPath, "items"] + }); + } + if (def.minLength) { + setResponseValueAndErrors(res, "minItems", def.minLength.value, def.minLength.message, refs); + } + if (def.maxLength) { + setResponseValueAndErrors(res, "maxItems", def.maxLength.value, def.maxLength.message, refs); + } + if (def.exactLength) { + setResponseValueAndErrors(res, "minItems", def.exactLength.value, def.exactLength.message, refs); + setResponseValueAndErrors(res, "maxItems", def.exactLength.value, def.exactLength.message, refs); + } + return res; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js +function parseBigintDef(def, refs) { + const res = { + type: "integer", + format: "int64" + }; + if (!def.checks) + return res; + for (const check2 of def.checks) { + switch (check2.kind) { + case "min": + if (refs.target === "jsonSchema7") { + if (check2.inclusive) { + setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMinimum", check2.value, check2.message, refs); + } + } else { + if (!check2.inclusive) { + res.exclusiveMinimum = true; + } + setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs); + } + break; + case "max": + if (refs.target === "jsonSchema7") { + if (check2.inclusive) { + setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMaximum", check2.value, check2.message, refs); + } + } else { + if (!check2.inclusive) { + res.exclusiveMaximum = true; + } + setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs); + } + break; + case "multipleOf": + setResponseValueAndErrors(res, "multipleOf", check2.value, check2.message, refs); + break; + } + } + return res; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js +function parseBooleanDef() { + return { + type: "boolean" + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/branded.js +function parseBrandedDef(_def, refs) { + return parseDef(_def.type._def, refs); +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/catch.js +var parseCatchDef = (def, refs) => { + return parseDef(def.innerType._def, refs); +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/date.js +function parseDateDef(def, refs, overrideDateStrategy) { + const strategy = overrideDateStrategy ?? refs.dateStrategy; + if (Array.isArray(strategy)) { + return { + anyOf: strategy.map((item, i) => parseDateDef(def, refs, item)) + }; + } + switch (strategy) { + case "string": + case "format:date-time": + return { + type: "string", + format: "date-time" + }; + case "format:date": + return { + type: "string", + format: "date" + }; + case "integer": + return integerDateParser(def, refs); + } +} +var integerDateParser = (def, refs) => { + const res = { + type: "integer", + format: "unix-time" + }; + if (refs.target === "openApi3") { + return res; + } + for (const check2 of def.checks) { + switch (check2.kind) { + case "min": + setResponseValueAndErrors( + res, + "minimum", + check2.value, + // This is in milliseconds + check2.message, + refs + ); + break; + case "max": + setResponseValueAndErrors( + res, + "maximum", + check2.value, + // This is in milliseconds + check2.message, + refs + ); + break; + } + } + return res; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/default.js +function parseDefaultDef(_def, refs) { + return { + ...parseDef(_def.innerType._def, refs), + default: _def.defaultValue() + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/effects.js +function parseEffectsDef(_def, refs) { + return refs.effectStrategy === "input" ? parseDef(_def.schema._def, refs) : parseAnyDef(refs); +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/enum.js +function parseEnumDef(def) { + return { + type: "string", + enum: Array.from(def.values) + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js +var isJsonSchema7AllOfType = (type) => { + if ("type" in type && type.type === "string") + return false; + return "allOf" in type; +}; +function parseIntersectionDef(def, refs) { + const allOf = [ + parseDef(def.left._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", "0"] + }), + parseDef(def.right._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", "1"] + }) + ].filter((x2) => !!x2); + let unevaluatedProperties = refs.target === "jsonSchema2019-09" ? { unevaluatedProperties: false } : void 0; + const mergedAllOf = []; + allOf.forEach((schema) => { + if (isJsonSchema7AllOfType(schema)) { + mergedAllOf.push(...schema.allOf); + if (schema.unevaluatedProperties === void 0) { + unevaluatedProperties = void 0; + } + } else { + let nestedSchema = schema; + if ("additionalProperties" in schema && schema.additionalProperties === false) { + const { additionalProperties, ...rest } = schema; + nestedSchema = rest; + } else { + unevaluatedProperties = void 0; + } + mergedAllOf.push(nestedSchema); + } + }); + return mergedAllOf.length ? { + allOf: mergedAllOf, + ...unevaluatedProperties + } : void 0; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/literal.js +function parseLiteralDef(def, refs) { + const parsedType2 = typeof def.value; + if (parsedType2 !== "bigint" && parsedType2 !== "number" && parsedType2 !== "boolean" && parsedType2 !== "string") { + return { + type: Array.isArray(def.value) ? "array" : "object" + }; + } + if (refs.target === "openApi3") { + return { + type: parsedType2 === "bigint" ? "integer" : parsedType2, + enum: [def.value] + }; + } + return { + type: parsedType2 === "bigint" ? "integer" : parsedType2, + const: def.value + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/string.js +var emojiRegex2 = void 0; +var zodPatterns = { + /** + * `c` was changed to `[cC]` to replicate /i flag + */ + cuid: /^[cC][^\s-]{8,}$/, + cuid2: /^[0-9a-z]+$/, + ulid: /^[0-9A-HJKMNP-TV-Z]{26}$/, + /** + * `a-z` was added to replicate /i flag + */ + email: /^(?!\.)(?!.*\.\.)([a-zA-Z0-9_'+\-\.]*)[a-zA-Z0-9_+-]@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/, + /** + * Constructed a valid Unicode RegExp + * + * Lazily instantiate since this type of regex isn't supported + * in all envs (e.g. React Native). + * + * See: + * https://github.com/colinhacks/zod/issues/2433 + * Fix in Zod: + * https://github.com/colinhacks/zod/commit/9340fd51e48576a75adc919bff65dbc4a5d4c99b + */ + emoji: () => { + if (emojiRegex2 === void 0) { + emojiRegex2 = RegExp("^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$", "u"); + } + return emojiRegex2; + }, + /** + * Unused + */ + uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/, + /** + * Unused + */ + ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/, + ipv4Cidr: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/(3[0-2]|[12]?[0-9])$/, + /** + * Unused + */ + ipv6: /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/, + ipv6Cidr: /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/, + base64: /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/, + base64url: /^([0-9a-zA-Z-_]{4})*(([0-9a-zA-Z-_]{2}(==)?)|([0-9a-zA-Z-_]{3}(=)?))?$/, + nanoid: /^[a-zA-Z0-9_-]{21}$/, + jwt: /^[A-Za-z0-9-_]+\.[A-Za-z0-9-_]+\.[A-Za-z0-9-_]*$/ +}; +function parseStringDef(def, refs) { + const res = { + type: "string" + }; + if (def.checks) { + for (const check2 of def.checks) { + switch (check2.kind) { + case "min": + setResponseValueAndErrors(res, "minLength", typeof res.minLength === "number" ? Math.max(res.minLength, check2.value) : check2.value, check2.message, refs); + break; + case "max": + setResponseValueAndErrors(res, "maxLength", typeof res.maxLength === "number" ? Math.min(res.maxLength, check2.value) : check2.value, check2.message, refs); + break; + case "email": + switch (refs.emailStrategy) { + case "format:email": + addFormat(res, "email", check2.message, refs); + break; + case "format:idn-email": + addFormat(res, "idn-email", check2.message, refs); + break; + case "pattern:zod": + addPattern(res, zodPatterns.email, check2.message, refs); + break; + } + break; + case "url": + addFormat(res, "uri", check2.message, refs); + break; + case "uuid": + addFormat(res, "uuid", check2.message, refs); + break; + case "regex": + addPattern(res, check2.regex, check2.message, refs); + break; + case "cuid": + addPattern(res, zodPatterns.cuid, check2.message, refs); + break; + case "cuid2": + addPattern(res, zodPatterns.cuid2, check2.message, refs); + break; + case "startsWith": + addPattern(res, RegExp(`^${escapeLiteralCheckValue(check2.value, refs)}`), check2.message, refs); + break; + case "endsWith": + addPattern(res, RegExp(`${escapeLiteralCheckValue(check2.value, refs)}$`), check2.message, refs); + break; + case "datetime": + addFormat(res, "date-time", check2.message, refs); + break; + case "date": + addFormat(res, "date", check2.message, refs); + break; + case "time": + addFormat(res, "time", check2.message, refs); + break; + case "duration": + addFormat(res, "duration", check2.message, refs); + break; + case "length": + setResponseValueAndErrors(res, "minLength", typeof res.minLength === "number" ? Math.max(res.minLength, check2.value) : check2.value, check2.message, refs); + setResponseValueAndErrors(res, "maxLength", typeof res.maxLength === "number" ? Math.min(res.maxLength, check2.value) : check2.value, check2.message, refs); + break; + case "includes": { + addPattern(res, RegExp(escapeLiteralCheckValue(check2.value, refs)), check2.message, refs); + break; + } + case "ip": { + if (check2.version !== "v6") { + addFormat(res, "ipv4", check2.message, refs); + } + if (check2.version !== "v4") { + addFormat(res, "ipv6", check2.message, refs); + } + break; + } + case "base64url": + addPattern(res, zodPatterns.base64url, check2.message, refs); + break; + case "jwt": + addPattern(res, zodPatterns.jwt, check2.message, refs); + break; + case "cidr": { + if (check2.version !== "v6") { + addPattern(res, zodPatterns.ipv4Cidr, check2.message, refs); + } + if (check2.version !== "v4") { + addPattern(res, zodPatterns.ipv6Cidr, check2.message, refs); + } + break; + } + case "emoji": + addPattern(res, zodPatterns.emoji(), check2.message, refs); + break; + case "ulid": { + addPattern(res, zodPatterns.ulid, check2.message, refs); + break; + } + case "base64": { + switch (refs.base64Strategy) { + case "format:binary": { + addFormat(res, "binary", check2.message, refs); + break; + } + case "contentEncoding:base64": { + setResponseValueAndErrors(res, "contentEncoding", "base64", check2.message, refs); + break; + } + case "pattern:zod": { + addPattern(res, zodPatterns.base64, check2.message, refs); + break; + } + } + break; + } + case "nanoid": { + addPattern(res, zodPatterns.nanoid, check2.message, refs); + } + case "toLowerCase": + case "toUpperCase": + case "trim": + break; + default: + /* @__PURE__ */ ((_) => { + })(check2); + } + } + } + return res; +} +function escapeLiteralCheckValue(literal2, refs) { + return refs.patternStrategy === "escape" ? escapeNonAlphaNumeric(literal2) : literal2; +} +var ALPHA_NUMERIC = new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789"); +function escapeNonAlphaNumeric(source) { + let result = ""; + for (let i = 0; i < source.length; i++) { + if (!ALPHA_NUMERIC.has(source[i])) { + result += "\\"; + } + result += source[i]; + } + return result; +} +function addFormat(schema, value, message, refs) { + if (schema.format || schema.anyOf?.some((x2) => x2.format)) { + if (!schema.anyOf) { + schema.anyOf = []; + } + if (schema.format) { + schema.anyOf.push({ + format: schema.format, + ...schema.errorMessage && refs.errorMessages && { + errorMessage: { format: schema.errorMessage.format } + } + }); + delete schema.format; + if (schema.errorMessage) { + delete schema.errorMessage.format; + if (Object.keys(schema.errorMessage).length === 0) { + delete schema.errorMessage; + } + } + } + schema.anyOf.push({ + format: value, + ...message && refs.errorMessages && { errorMessage: { format: message } } + }); + } else { + setResponseValueAndErrors(schema, "format", value, message, refs); + } +} +function addPattern(schema, regex, message, refs) { + if (schema.pattern || schema.allOf?.some((x2) => x2.pattern)) { + if (!schema.allOf) { + schema.allOf = []; + } + if (schema.pattern) { + schema.allOf.push({ + pattern: schema.pattern, + ...schema.errorMessage && refs.errorMessages && { + errorMessage: { pattern: schema.errorMessage.pattern } + } + }); + delete schema.pattern; + if (schema.errorMessage) { + delete schema.errorMessage.pattern; + if (Object.keys(schema.errorMessage).length === 0) { + delete schema.errorMessage; + } + } + } + schema.allOf.push({ + pattern: stringifyRegExpWithFlags(regex, refs), + ...message && refs.errorMessages && { errorMessage: { pattern: message } } + }); + } else { + setResponseValueAndErrors(schema, "pattern", stringifyRegExpWithFlags(regex, refs), message, refs); + } +} +function stringifyRegExpWithFlags(regex, refs) { + if (!refs.applyRegexFlags || !regex.flags) { + return regex.source; + } + const flags = { + i: regex.flags.includes("i"), + m: regex.flags.includes("m"), + s: regex.flags.includes("s") + // `.` matches newlines + }; + const source = flags.i ? regex.source.toLowerCase() : regex.source; + let pattern = ""; + let isEscaped = false; + let inCharGroup = false; + let inCharRange = false; + for (let i = 0; i < source.length; i++) { + if (isEscaped) { + pattern += source[i]; + isEscaped = false; + continue; + } + if (flags.i) { + if (inCharGroup) { + if (source[i].match(/[a-z]/)) { + if (inCharRange) { + pattern += source[i]; + pattern += `${source[i - 2]}-${source[i]}`.toUpperCase(); + inCharRange = false; + } else if (source[i + 1] === "-" && source[i + 2]?.match(/[a-z]/)) { + pattern += source[i]; + inCharRange = true; + } else { + pattern += `${source[i]}${source[i].toUpperCase()}`; + } + continue; + } + } else if (source[i].match(/[a-z]/)) { + pattern += `[${source[i]}${source[i].toUpperCase()}]`; + continue; + } + } + if (flags.m) { + if (source[i] === "^") { + pattern += `(^|(?<=[\r +]))`; + continue; + } else if (source[i] === "$") { + pattern += `($|(?=[\r +]))`; + continue; + } + } + if (flags.s && source[i] === ".") { + pattern += inCharGroup ? `${source[i]}\r +` : `[${source[i]}\r +]`; + continue; + } + pattern += source[i]; + if (source[i] === "\\") { + isEscaped = true; + } else if (inCharGroup && source[i] === "]") { + inCharGroup = false; + } else if (!inCharGroup && source[i] === "[") { + inCharGroup = true; + } + } + try { + new RegExp(pattern); + } catch { + console.warn(`Could not convert regex pattern at ${refs.currentPath.join("/")} to a flag-independent form! Falling back to the flag-ignorant source`); + return regex.source; + } + return pattern; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/record.js +function parseRecordDef(def, refs) { + if (refs.target === "openAi") { + console.warn("Warning: OpenAI may not support records in schemas! Try an array of key-value pairs instead."); + } + if (refs.target === "openApi3" && def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodEnum) { + return { + type: "object", + required: def.keyType._def.values, + properties: def.keyType._def.values.reduce((acc, key) => ({ + ...acc, + [key]: parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "properties", key] + }) ?? parseAnyDef(refs) + }), {}), + additionalProperties: refs.rejectedAdditionalProperties + }; + } + const schema = { + type: "object", + additionalProperties: parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "additionalProperties"] + }) ?? refs.allowedAdditionalProperties + }; + if (refs.target === "openApi3") { + return schema; + } + if (def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodString && def.keyType._def.checks?.length) { + const { type, ...keyType } = parseStringDef(def.keyType._def, refs); + return { + ...schema, + propertyNames: keyType + }; + } else if (def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodEnum) { + return { + ...schema, + propertyNames: { + enum: def.keyType._def.values + } + }; + } else if (def.keyType?._def.typeName === ZodFirstPartyTypeKind.ZodBranded && def.keyType._def.type._def.typeName === ZodFirstPartyTypeKind.ZodString && def.keyType._def.type._def.checks?.length) { + const { type, ...keyType } = parseBrandedDef(def.keyType._def, refs); + return { + ...schema, + propertyNames: keyType + }; + } + return schema; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/map.js +function parseMapDef(def, refs) { + if (refs.mapStrategy === "record") { + return parseRecordDef(def, refs); + } + const keys = parseDef(def.keyType._def, { + ...refs, + currentPath: [...refs.currentPath, "items", "items", "0"] + }) || parseAnyDef(refs); + const values = parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "items", "items", "1"] + }) || parseAnyDef(refs); + return { + type: "array", + maxItems: 125, + items: { + type: "array", + items: [keys, values], + minItems: 2, + maxItems: 2 + } + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js +function parseNativeEnumDef(def) { + const object3 = def.values; + const actualKeys = Object.keys(def.values).filter((key) => { + return typeof object3[object3[key]] !== "number"; + }); + const actualValues = actualKeys.map((key) => object3[key]); + const parsedTypes = Array.from(new Set(actualValues.map((values) => typeof values))); + return { + type: parsedTypes.length === 1 ? parsedTypes[0] === "string" ? "string" : "number" : ["string", "number"], + enum: actualValues + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/never.js +function parseNeverDef(refs) { + return refs.target === "openAi" ? void 0 : { + not: parseAnyDef({ + ...refs, + currentPath: [...refs.currentPath, "not"] + }) + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/null.js +function parseNullDef(refs) { + return refs.target === "openApi3" ? { + enum: ["null"], + nullable: true + } : { + type: "null" + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/union.js +var primitiveMappings = { + ZodString: "string", + ZodNumber: "number", + ZodBigInt: "integer", + ZodBoolean: "boolean", + ZodNull: "null" +}; +function parseUnionDef(def, refs) { + if (refs.target === "openApi3") + return asAnyOf(def, refs); + const options = def.options instanceof Map ? Array.from(def.options.values()) : def.options; + if (options.every((x2) => x2._def.typeName in primitiveMappings && (!x2._def.checks || !x2._def.checks.length))) { + const types = options.reduce((types2, x2) => { + const type = primitiveMappings[x2._def.typeName]; + return type && !types2.includes(type) ? [...types2, type] : types2; + }, []); + return { + type: types.length > 1 ? types : types[0] + }; + } else if (options.every((x2) => x2._def.typeName === "ZodLiteral" && !x2.description)) { + const types = options.reduce((acc, x2) => { + const type = typeof x2._def.value; + switch (type) { + case "string": + case "number": + case "boolean": + return [...acc, type]; + case "bigint": + return [...acc, "integer"]; + case "object": + if (x2._def.value === null) + return [...acc, "null"]; + case "symbol": + case "undefined": + case "function": + default: + return acc; + } + }, []); + if (types.length === options.length) { + const uniqueTypes = types.filter((x2, i, a2) => a2.indexOf(x2) === i); + return { + type: uniqueTypes.length > 1 ? uniqueTypes : uniqueTypes[0], + enum: options.reduce((acc, x2) => { + return acc.includes(x2._def.value) ? acc : [...acc, x2._def.value]; + }, []) + }; + } + } else if (options.every((x2) => x2._def.typeName === "ZodEnum")) { + return { + type: "string", + enum: options.reduce((acc, x2) => [ + ...acc, + ...x2._def.values.filter((x3) => !acc.includes(x3)) + ], []) + }; + } + return asAnyOf(def, refs); +} +var asAnyOf = (def, refs) => { + const anyOf = (def.options instanceof Map ? Array.from(def.options.values()) : def.options).map((x2, i) => parseDef(x2._def, { + ...refs, + currentPath: [...refs.currentPath, "anyOf", `${i}`] + })).filter((x2) => !!x2 && (!refs.strictUnions || typeof x2 === "object" && Object.keys(x2).length > 0)); + return anyOf.length ? { anyOf } : void 0; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js +function parseNullableDef(def, refs) { + if (["ZodString", "ZodNumber", "ZodBigInt", "ZodBoolean", "ZodNull"].includes(def.innerType._def.typeName) && (!def.innerType._def.checks || !def.innerType._def.checks.length)) { + if (refs.target === "openApi3") { + return { + type: primitiveMappings[def.innerType._def.typeName], + nullable: true + }; + } + return { + type: [ + primitiveMappings[def.innerType._def.typeName], + "null" + ] + }; + } + if (refs.target === "openApi3") { + const base2 = parseDef(def.innerType._def, { + ...refs, + currentPath: [...refs.currentPath] + }); + if (base2 && "$ref" in base2) + return { allOf: [base2], nullable: true }; + return base2 && { ...base2, nullable: true }; + } + const base = parseDef(def.innerType._def, { + ...refs, + currentPath: [...refs.currentPath, "anyOf", "0"] + }); + return base && { anyOf: [base, { type: "null" }] }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/number.js +function parseNumberDef(def, refs) { + const res = { + type: "number" + }; + if (!def.checks) + return res; + for (const check2 of def.checks) { + switch (check2.kind) { + case "int": + res.type = "integer"; + addErrorMessage(res, "type", check2.message, refs); + break; + case "min": + if (refs.target === "jsonSchema7") { + if (check2.inclusive) { + setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMinimum", check2.value, check2.message, refs); + } + } else { + if (!check2.inclusive) { + res.exclusiveMinimum = true; + } + setResponseValueAndErrors(res, "minimum", check2.value, check2.message, refs); + } + break; + case "max": + if (refs.target === "jsonSchema7") { + if (check2.inclusive) { + setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs); + } else { + setResponseValueAndErrors(res, "exclusiveMaximum", check2.value, check2.message, refs); + } + } else { + if (!check2.inclusive) { + res.exclusiveMaximum = true; + } + setResponseValueAndErrors(res, "maximum", check2.value, check2.message, refs); + } + break; + case "multipleOf": + setResponseValueAndErrors(res, "multipleOf", check2.value, check2.message, refs); + break; + } + } + return res; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/object.js +function parseObjectDef(def, refs) { + const forceOptionalIntoNullable = refs.target === "openAi"; + const result = { + type: "object", + properties: {} + }; + const required2 = []; + const shape = def.shape(); + for (const propName in shape) { + let propDef = shape[propName]; + if (propDef === void 0 || propDef._def === void 0) { + continue; + } + let propOptional = safeIsOptional(propDef); + if (propOptional && forceOptionalIntoNullable) { + if (propDef._def.typeName === "ZodOptional") { + propDef = propDef._def.innerType; + } + if (!propDef.isNullable()) { + propDef = propDef.nullable(); + } + propOptional = false; + } + const parsedDef = parseDef(propDef._def, { + ...refs, + currentPath: [...refs.currentPath, "properties", propName], + propertyPath: [...refs.currentPath, "properties", propName] + }); + if (parsedDef === void 0) { + continue; + } + result.properties[propName] = parsedDef; + if (!propOptional) { + required2.push(propName); + } + } + if (required2.length) { + result.required = required2; + } + const additionalProperties = decideAdditionalProperties(def, refs); + if (additionalProperties !== void 0) { + result.additionalProperties = additionalProperties; + } + return result; +} +function decideAdditionalProperties(def, refs) { + if (def.catchall._def.typeName !== "ZodNever") { + return parseDef(def.catchall._def, { + ...refs, + currentPath: [...refs.currentPath, "additionalProperties"] + }); + } + switch (def.unknownKeys) { + case "passthrough": + return refs.allowedAdditionalProperties; + case "strict": + return refs.rejectedAdditionalProperties; + case "strip": + return refs.removeAdditionalStrategy === "strict" ? refs.allowedAdditionalProperties : refs.rejectedAdditionalProperties; + } +} +function safeIsOptional(schema) { + try { + return schema.isOptional(); + } catch { + return true; + } +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/optional.js +var parseOptionalDef = (def, refs) => { + if (refs.currentPath.toString() === refs.propertyPath?.toString()) { + return parseDef(def.innerType._def, refs); + } + const innerSchema = parseDef(def.innerType._def, { + ...refs, + currentPath: [...refs.currentPath, "anyOf", "1"] + }); + return innerSchema ? { + anyOf: [ + { + not: parseAnyDef(refs) + }, + innerSchema + ] + } : parseAnyDef(refs); +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js +var parsePipelineDef = (def, refs) => { + if (refs.pipeStrategy === "input") { + return parseDef(def.in._def, refs); + } else if (refs.pipeStrategy === "output") { + return parseDef(def.out._def, refs); + } + const a2 = parseDef(def.in._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", "0"] + }); + const b2 = parseDef(def.out._def, { + ...refs, + currentPath: [...refs.currentPath, "allOf", a2 ? "1" : "0"] + }); + return { + allOf: [a2, b2].filter((x2) => x2 !== void 0) + }; +}; + +// node_modules/zod-to-json-schema/dist/esm/parsers/promise.js +function parsePromiseDef(def, refs) { + return parseDef(def.type._def, refs); +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/set.js +function parseSetDef(def, refs) { + const items = parseDef(def.valueType._def, { + ...refs, + currentPath: [...refs.currentPath, "items"] + }); + const schema = { + type: "array", + uniqueItems: true, + items + }; + if (def.minSize) { + setResponseValueAndErrors(schema, "minItems", def.minSize.value, def.minSize.message, refs); + } + if (def.maxSize) { + setResponseValueAndErrors(schema, "maxItems", def.maxSize.value, def.maxSize.message, refs); + } + return schema; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js +function parseTupleDef(def, refs) { + if (def.rest) { + return { + type: "array", + minItems: def.items.length, + items: def.items.map((x2, i) => parseDef(x2._def, { + ...refs, + currentPath: [...refs.currentPath, "items", `${i}`] + })).reduce((acc, x2) => x2 === void 0 ? acc : [...acc, x2], []), + additionalItems: parseDef(def.rest._def, { + ...refs, + currentPath: [...refs.currentPath, "additionalItems"] + }) + }; + } else { + return { + type: "array", + minItems: def.items.length, + maxItems: def.items.length, + items: def.items.map((x2, i) => parseDef(x2._def, { + ...refs, + currentPath: [...refs.currentPath, "items", `${i}`] + })).reduce((acc, x2) => x2 === void 0 ? acc : [...acc, x2], []) + }; + } +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js +function parseUndefinedDef(refs) { + return { + not: parseAnyDef(refs) + }; +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js +function parseUnknownDef(refs) { + return parseAnyDef(refs); +} + +// node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js +var parseReadonlyDef = (def, refs) => { + return parseDef(def.innerType._def, refs); +}; + +// node_modules/zod-to-json-schema/dist/esm/selectParser.js +var selectParser = (def, typeName, refs) => { + switch (typeName) { + case ZodFirstPartyTypeKind.ZodString: + return parseStringDef(def, refs); + case ZodFirstPartyTypeKind.ZodNumber: + return parseNumberDef(def, refs); + case ZodFirstPartyTypeKind.ZodObject: + return parseObjectDef(def, refs); + case ZodFirstPartyTypeKind.ZodBigInt: + return parseBigintDef(def, refs); + case ZodFirstPartyTypeKind.ZodBoolean: + return parseBooleanDef(); + case ZodFirstPartyTypeKind.ZodDate: + return parseDateDef(def, refs); + case ZodFirstPartyTypeKind.ZodUndefined: + return parseUndefinedDef(refs); + case ZodFirstPartyTypeKind.ZodNull: + return parseNullDef(refs); + case ZodFirstPartyTypeKind.ZodArray: + return parseArrayDef(def, refs); + case ZodFirstPartyTypeKind.ZodUnion: + case ZodFirstPartyTypeKind.ZodDiscriminatedUnion: + return parseUnionDef(def, refs); + case ZodFirstPartyTypeKind.ZodIntersection: + return parseIntersectionDef(def, refs); + case ZodFirstPartyTypeKind.ZodTuple: + return parseTupleDef(def, refs); + case ZodFirstPartyTypeKind.ZodRecord: + return parseRecordDef(def, refs); + case ZodFirstPartyTypeKind.ZodLiteral: + return parseLiteralDef(def, refs); + case ZodFirstPartyTypeKind.ZodEnum: + return parseEnumDef(def); + case ZodFirstPartyTypeKind.ZodNativeEnum: + return parseNativeEnumDef(def); + case ZodFirstPartyTypeKind.ZodNullable: + return parseNullableDef(def, refs); + case ZodFirstPartyTypeKind.ZodOptional: + return parseOptionalDef(def, refs); + case ZodFirstPartyTypeKind.ZodMap: + return parseMapDef(def, refs); + case ZodFirstPartyTypeKind.ZodSet: + return parseSetDef(def, refs); + case ZodFirstPartyTypeKind.ZodLazy: + return () => def.getter()._def; + case ZodFirstPartyTypeKind.ZodPromise: + return parsePromiseDef(def, refs); + case ZodFirstPartyTypeKind.ZodNaN: + case ZodFirstPartyTypeKind.ZodNever: + return parseNeverDef(refs); + case ZodFirstPartyTypeKind.ZodEffects: + return parseEffectsDef(def, refs); + case ZodFirstPartyTypeKind.ZodAny: + return parseAnyDef(refs); + case ZodFirstPartyTypeKind.ZodUnknown: + return parseUnknownDef(refs); + case ZodFirstPartyTypeKind.ZodDefault: + return parseDefaultDef(def, refs); + case ZodFirstPartyTypeKind.ZodBranded: + return parseBrandedDef(def, refs); + case ZodFirstPartyTypeKind.ZodReadonly: + return parseReadonlyDef(def, refs); + case ZodFirstPartyTypeKind.ZodCatch: + return parseCatchDef(def, refs); + case ZodFirstPartyTypeKind.ZodPipeline: + return parsePipelineDef(def, refs); + case ZodFirstPartyTypeKind.ZodFunction: + case ZodFirstPartyTypeKind.ZodVoid: + case ZodFirstPartyTypeKind.ZodSymbol: + return void 0; + default: + return /* @__PURE__ */ ((_) => void 0)(typeName); + } +}; + +// node_modules/zod-to-json-schema/dist/esm/parseDef.js +function parseDef(def, refs, forceResolution = false) { + const seenItem = refs.seen.get(def); + if (refs.override) { + const overrideResult = refs.override?.(def, refs, seenItem, forceResolution); + if (overrideResult !== ignoreOverride) { + return overrideResult; + } + } + if (seenItem && !forceResolution) { + const seenSchema = get$ref(seenItem, refs); + if (seenSchema !== void 0) { + return seenSchema; + } + } + const newItem = { def, path: refs.currentPath, jsonSchema: void 0 }; + refs.seen.set(def, newItem); + const jsonSchemaOrGetter = selectParser(def, def.typeName, refs); + const jsonSchema = typeof jsonSchemaOrGetter === "function" ? parseDef(jsonSchemaOrGetter(), refs) : jsonSchemaOrGetter; + if (jsonSchema) { + addMeta(def, refs, jsonSchema); + } + if (refs.postProcess) { + const postProcessResult = refs.postProcess(jsonSchema, def, refs); + newItem.jsonSchema = jsonSchema; + return postProcessResult; + } + newItem.jsonSchema = jsonSchema; + return jsonSchema; +} +var get$ref = (item, refs) => { + switch (refs.$refStrategy) { + case "root": + return { $ref: item.path.join("/") }; + case "relative": + return { $ref: getRelativePath(refs.currentPath, item.path) }; + case "none": + case "seen": { + if (item.path.length < refs.currentPath.length && item.path.every((value, index) => refs.currentPath[index] === value)) { + console.warn(`Recursive reference detected at ${refs.currentPath.join("/")}! Defaulting to any`); + return parseAnyDef(refs); + } + return refs.$refStrategy === "seen" ? parseAnyDef(refs) : void 0; + } + } +}; +var addMeta = (def, refs, jsonSchema) => { + if (def.description) { + jsonSchema.description = def.description; + if (refs.markdownDescription) { + jsonSchema.markdownDescription = def.description; + } + } + return jsonSchema; +}; + +// node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js +var zodToJsonSchema = (schema, options) => { + const refs = getRefs(options); + let definitions = typeof options === "object" && options.definitions ? Object.entries(options.definitions).reduce((acc, [name2, schema2]) => ({ + ...acc, + [name2]: parseDef(schema2._def, { + ...refs, + currentPath: [...refs.basePath, refs.definitionPath, name2] + }, true) ?? parseAnyDef(refs) + }), {}) : void 0; + const name = typeof options === "string" ? options : options?.nameStrategy === "title" ? void 0 : options?.name; + const main = parseDef(schema._def, name === void 0 ? refs : { + ...refs, + currentPath: [...refs.basePath, refs.definitionPath, name] + }, false) ?? parseAnyDef(refs); + const title = typeof options === "object" && options.name !== void 0 && options.nameStrategy === "title" ? options.name : void 0; + if (title !== void 0) { + main.title = title; + } + if (refs.flags.hasReferencedOpenAiAnyType) { + if (!definitions) { + definitions = {}; + } + if (!definitions[refs.openAiAnyTypeName]) { + definitions[refs.openAiAnyTypeName] = { + // Skipping "object" as no properties can be defined and additionalProperties must be "false" + type: ["string", "number", "integer", "boolean", "array", "null"], + items: { + $ref: refs.$refStrategy === "relative" ? "1" : [ + ...refs.basePath, + refs.definitionPath, + refs.openAiAnyTypeName + ].join("/") + } + }; + } + } + const combined = name === void 0 ? definitions ? { + ...main, + [refs.definitionPath]: definitions + } : main : { + $ref: [ + ...refs.$refStrategy === "relative" ? [] : refs.basePath, + refs.definitionPath, + name + ].join("/"), + [refs.definitionPath]: { + ...definitions, + [name]: main + } + }; + if (refs.target === "jsonSchema7") { + combined.$schema = "http://json-schema.org/draft-07/schema#"; + } else if (refs.target === "jsonSchema2019-09" || refs.target === "openAi") { + combined.$schema = "https://json-schema.org/draft/2019-09/schema#"; + } + if (refs.target === "openAi" && ("anyOf" in combined || "oneOf" in combined || "allOf" in combined || "type" in combined && Array.isArray(combined.type))) { + console.warn("Warning: OpenAI may not support schemas with unions as roots! Try wrapping it in an object property."); + } + return combined; +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js +function mapMiniTarget(t) { + if (!t) + return "draft-7"; + if (t === "jsonSchema7" || t === "draft-7") + return "draft-7"; + if (t === "jsonSchema2019-09" || t === "draft-2020-12") + return "draft-2020-12"; + return "draft-7"; +} +function toJsonSchemaCompat(schema, opts) { + if (isZ4Schema(schema)) { + return toJSONSchema(schema, { + target: mapMiniTarget(opts?.target), + io: opts?.pipeStrategy ?? "input" + }); + } + return zodToJsonSchema(schema, { + strictUnions: opts?.strictUnions ?? true, + pipeStrategy: opts?.pipeStrategy ?? "input" + }); +} +function getMethodLiteral(schema) { + const shape = getObjectShape(schema); + const methodSchema = shape?.method; + if (!methodSchema) { + throw new Error("Schema is missing a method literal"); + } + const value = getLiteralValue(methodSchema); + if (typeof value !== "string") { + throw new Error("Schema method literal must be a string"); + } + return value; +} +function parseWithCompat(schema, data) { + const result = safeParse3(schema, data); + if (!result.success) { + throw result.error; + } + return result.data; +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js +var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4; +var Protocol = class { + constructor(_options) { + this._options = _options; + this._requestMessageId = 0; + this._requestHandlers = /* @__PURE__ */ new Map(); + this._requestHandlerAbortControllers = /* @__PURE__ */ new Map(); + this._notificationHandlers = /* @__PURE__ */ new Map(); + this._responseHandlers = /* @__PURE__ */ new Map(); + this._progressHandlers = /* @__PURE__ */ new Map(); + this._timeoutInfo = /* @__PURE__ */ new Map(); + this._pendingDebouncedNotifications = /* @__PURE__ */ new Set(); + this._taskProgressTokens = /* @__PURE__ */ new Map(); + this._requestResolvers = /* @__PURE__ */ new Map(); + this.setNotificationHandler(CancelledNotificationSchema, (notification) => { + this._oncancel(notification); + }); + this.setNotificationHandler(ProgressNotificationSchema, (notification) => { + this._onprogress(notification); + }); + this.setRequestHandler( + PingRequestSchema, + // Automatic pong by default. + (_request) => ({}) + ); + this._taskStore = _options?.taskStore; + this._taskMessageQueue = _options?.taskMessageQueue; + if (this._taskStore) { + this.setRequestHandler(GetTaskRequestSchema, async (request, extra) => { + const task = await this._taskStore.getTask(request.params.taskId, extra.sessionId); + if (!task) { + throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found"); + } + return { + ...task + }; + }); + this.setRequestHandler(GetTaskPayloadRequestSchema, async (request, extra) => { + const handleTaskResult = async () => { + const taskId = request.params.taskId; + if (this._taskMessageQueue) { + let queuedMessage; + while (queuedMessage = await this._taskMessageQueue.dequeue(taskId, extra.sessionId)) { + if (queuedMessage.type === "response" || queuedMessage.type === "error") { + const message = queuedMessage.message; + const requestId = message.id; + const resolver = this._requestResolvers.get(requestId); + if (resolver) { + this._requestResolvers.delete(requestId); + if (queuedMessage.type === "response") { + resolver(message); + } else { + const errorMessage = message; + const error2 = new McpError(errorMessage.error.code, errorMessage.error.message, errorMessage.error.data); + resolver(error2); + } + } else { + const messageType = queuedMessage.type === "response" ? "Response" : "Error"; + this._onerror(new Error(`${messageType} handler missing for request ${requestId}`)); + } + continue; + } + await this._transport?.send(queuedMessage.message, { relatedRequestId: extra.requestId }); + } + } + const task = await this._taskStore.getTask(taskId, extra.sessionId); + if (!task) { + throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`); + } + if (!isTerminal(task.status)) { + await this._waitForTaskUpdate(taskId, extra.signal); + return await handleTaskResult(); + } + if (isTerminal(task.status)) { + const result = await this._taskStore.getTaskResult(taskId, extra.sessionId); + this._clearTaskQueue(taskId); + return { + ...result, + _meta: { + ...result._meta, + [RELATED_TASK_META_KEY]: { + taskId + } + } + }; + } + return await handleTaskResult(); + }; + return await handleTaskResult(); + }); + this.setRequestHandler(ListTasksRequestSchema, async (request, extra) => { + try { + const { tasks, nextCursor } = await this._taskStore.listTasks(request.params?.cursor, extra.sessionId); + return { + tasks, + nextCursor, + _meta: {} + }; + } catch (error2) { + throw new McpError(ErrorCode.InvalidParams, `Failed to list tasks: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + }); + this.setRequestHandler(CancelTaskRequestSchema, async (request, extra) => { + try { + const task = await this._taskStore.getTask(request.params.taskId, extra.sessionId); + if (!task) { + throw new McpError(ErrorCode.InvalidParams, `Task not found: ${request.params.taskId}`); + } + if (isTerminal(task.status)) { + throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`); + } + await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId); + this._clearTaskQueue(request.params.taskId); + const cancelledTask = await this._taskStore.getTask(request.params.taskId, extra.sessionId); + if (!cancelledTask) { + throw new McpError(ErrorCode.InvalidParams, `Task not found after cancellation: ${request.params.taskId}`); + } + return { + _meta: {}, + ...cancelledTask + }; + } catch (error2) { + if (error2 instanceof McpError) { + throw error2; + } + throw new McpError(ErrorCode.InvalidRequest, `Failed to cancel task: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + }); + } + } + async _oncancel(notification) { + if (!notification.params.requestId) { + return; + } + const controller = this._requestHandlerAbortControllers.get(notification.params.requestId); + controller?.abort(notification.params.reason); + } + _setupTimeout(messageId, timeout, maxTotalTimeout, onTimeout, resetTimeoutOnProgress = false) { + this._timeoutInfo.set(messageId, { + timeoutId: setTimeout(onTimeout, timeout), + startTime: Date.now(), + timeout, + maxTotalTimeout, + resetTimeoutOnProgress, + onTimeout + }); + } + _resetTimeout(messageId) { + const info = this._timeoutInfo.get(messageId); + if (!info) + return false; + const totalElapsed = Date.now() - info.startTime; + if (info.maxTotalTimeout && totalElapsed >= info.maxTotalTimeout) { + this._timeoutInfo.delete(messageId); + throw McpError.fromError(ErrorCode.RequestTimeout, "Maximum total timeout exceeded", { + maxTotalTimeout: info.maxTotalTimeout, + totalElapsed + }); + } + clearTimeout(info.timeoutId); + info.timeoutId = setTimeout(info.onTimeout, info.timeout); + return true; + } + _cleanupTimeout(messageId) { + const info = this._timeoutInfo.get(messageId); + if (info) { + clearTimeout(info.timeoutId); + this._timeoutInfo.delete(messageId); + } + } + /** + * Attaches to the given transport, starts it, and starts listening for messages. + * + * The Protocol object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward. + */ + async connect(transport) { + if (this._transport) { + throw new Error("Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection."); + } + this._transport = transport; + const _onclose = this.transport?.onclose; + this._transport.onclose = () => { + _onclose?.(); + this._onclose(); + }; + const _onerror = this.transport?.onerror; + this._transport.onerror = (error2) => { + _onerror?.(error2); + this._onerror(error2); + }; + const _onmessage = this._transport?.onmessage; + this._transport.onmessage = (message, extra) => { + _onmessage?.(message, extra); + if (isJSONRPCResultResponse(message) || isJSONRPCErrorResponse(message)) { + this._onresponse(message); + } else if (isJSONRPCRequest(message)) { + this._onrequest(message, extra); + } else if (isJSONRPCNotification(message)) { + this._onnotification(message); + } else { + this._onerror(new Error(`Unknown message type: ${JSON.stringify(message)}`)); + } + }; + await this._transport.start(); + } + _onclose() { + const responseHandlers = this._responseHandlers; + this._responseHandlers = /* @__PURE__ */ new Map(); + this._progressHandlers.clear(); + this._taskProgressTokens.clear(); + this._pendingDebouncedNotifications.clear(); + for (const controller of this._requestHandlerAbortControllers.values()) { + controller.abort(); + } + this._requestHandlerAbortControllers.clear(); + const error2 = McpError.fromError(ErrorCode.ConnectionClosed, "Connection closed"); + this._transport = void 0; + this.onclose?.(); + for (const handler of responseHandlers.values()) { + handler(error2); + } + } + _onerror(error2) { + this.onerror?.(error2); + } + _onnotification(notification) { + const handler = this._notificationHandlers.get(notification.method) ?? this.fallbackNotificationHandler; + if (handler === void 0) { + return; + } + Promise.resolve().then(() => handler(notification)).catch((error2) => this._onerror(new Error(`Uncaught error in notification handler: ${error2}`))); + } + _onrequest(request, extra) { + const handler = this._requestHandlers.get(request.method) ?? this.fallbackRequestHandler; + const capturedTransport = this._transport; + const relatedTaskId = request.params?._meta?.[RELATED_TASK_META_KEY]?.taskId; + if (handler === void 0) { + const errorResponse = { + jsonrpc: "2.0", + id: request.id, + error: { + code: ErrorCode.MethodNotFound, + message: "Method not found" + } + }; + if (relatedTaskId && this._taskMessageQueue) { + this._enqueueTaskMessage(relatedTaskId, { + type: "error", + message: errorResponse, + timestamp: Date.now() + }, capturedTransport?.sessionId).catch((error2) => this._onerror(new Error(`Failed to enqueue error response: ${error2}`))); + } else { + capturedTransport?.send(errorResponse).catch((error2) => this._onerror(new Error(`Failed to send an error response: ${error2}`))); + } + return; + } + const abortController = new AbortController(); + this._requestHandlerAbortControllers.set(request.id, abortController); + const taskCreationParams = isTaskAugmentedRequestParams(request.params) ? request.params.task : void 0; + const taskStore = this._taskStore ? this.requestTaskStore(request, capturedTransport?.sessionId) : void 0; + const fullExtra = { + signal: abortController.signal, + sessionId: capturedTransport?.sessionId, + _meta: request.params?._meta, + sendNotification: async (notification) => { + if (abortController.signal.aborted) + return; + const notificationOptions = { relatedRequestId: request.id }; + if (relatedTaskId) { + notificationOptions.relatedTask = { taskId: relatedTaskId }; + } + await this.notification(notification, notificationOptions); + }, + sendRequest: async (r, resultSchema, options) => { + if (abortController.signal.aborted) { + throw new McpError(ErrorCode.ConnectionClosed, "Request was cancelled"); + } + const requestOptions = { ...options, relatedRequestId: request.id }; + if (relatedTaskId && !requestOptions.relatedTask) { + requestOptions.relatedTask = { taskId: relatedTaskId }; + } + const effectiveTaskId = requestOptions.relatedTask?.taskId ?? relatedTaskId; + if (effectiveTaskId && taskStore) { + await taskStore.updateTaskStatus(effectiveTaskId, "input_required"); + } + return await this.request(r, resultSchema, requestOptions); + }, + authInfo: extra?.authInfo, + requestId: request.id, + requestInfo: extra?.requestInfo, + taskId: relatedTaskId, + taskStore, + taskRequestedTtl: taskCreationParams?.ttl, + closeSSEStream: extra?.closeSSEStream, + closeStandaloneSSEStream: extra?.closeStandaloneSSEStream + }; + Promise.resolve().then(() => { + if (taskCreationParams) { + this.assertTaskHandlerCapability(request.method); + } + }).then(() => handler(request, fullExtra)).then(async (result) => { + if (abortController.signal.aborted) { + return; + } + const response = { + result, + jsonrpc: "2.0", + id: request.id + }; + if (relatedTaskId && this._taskMessageQueue) { + await this._enqueueTaskMessage(relatedTaskId, { + type: "response", + message: response, + timestamp: Date.now() + }, capturedTransport?.sessionId); + } else { + await capturedTransport?.send(response); + } + }, async (error2) => { + if (abortController.signal.aborted) { + return; + } + const errorResponse = { + jsonrpc: "2.0", + id: request.id, + error: { + code: Number.isSafeInteger(error2["code"]) ? error2["code"] : ErrorCode.InternalError, + message: error2.message ?? "Internal error", + ...error2["data"] !== void 0 && { data: error2["data"] } + } + }; + if (relatedTaskId && this._taskMessageQueue) { + await this._enqueueTaskMessage(relatedTaskId, { + type: "error", + message: errorResponse, + timestamp: Date.now() + }, capturedTransport?.sessionId); + } else { + await capturedTransport?.send(errorResponse); + } + }).catch((error2) => this._onerror(new Error(`Failed to send response: ${error2}`))).finally(() => { + this._requestHandlerAbortControllers.delete(request.id); + }); + } + _onprogress(notification) { + const { progressToken, ...params } = notification.params; + const messageId = Number(progressToken); + const handler = this._progressHandlers.get(messageId); + if (!handler) { + this._onerror(new Error(`Received a progress notification for an unknown token: ${JSON.stringify(notification)}`)); + return; + } + const responseHandler = this._responseHandlers.get(messageId); + const timeoutInfo = this._timeoutInfo.get(messageId); + if (timeoutInfo && responseHandler && timeoutInfo.resetTimeoutOnProgress) { + try { + this._resetTimeout(messageId); + } catch (error2) { + this._responseHandlers.delete(messageId); + this._progressHandlers.delete(messageId); + this._cleanupTimeout(messageId); + responseHandler(error2); + return; + } + } + handler(params); + } + _onresponse(response) { + const messageId = Number(response.id); + const resolver = this._requestResolvers.get(messageId); + if (resolver) { + this._requestResolvers.delete(messageId); + if (isJSONRPCResultResponse(response)) { + resolver(response); + } else { + const error2 = new McpError(response.error.code, response.error.message, response.error.data); + resolver(error2); + } + return; + } + const handler = this._responseHandlers.get(messageId); + if (handler === void 0) { + this._onerror(new Error(`Received a response for an unknown message ID: ${JSON.stringify(response)}`)); + return; + } + this._responseHandlers.delete(messageId); + this._cleanupTimeout(messageId); + let isTaskResponse = false; + if (isJSONRPCResultResponse(response) && response.result && typeof response.result === "object") { + const result = response.result; + if (result.task && typeof result.task === "object") { + const task = result.task; + if (typeof task.taskId === "string") { + isTaskResponse = true; + this._taskProgressTokens.set(task.taskId, messageId); + } + } + } + if (!isTaskResponse) { + this._progressHandlers.delete(messageId); + } + if (isJSONRPCResultResponse(response)) { + handler(response); + } else { + const error2 = McpError.fromError(response.error.code, response.error.message, response.error.data); + handler(error2); + } + } + get transport() { + return this._transport; + } + /** + * Closes the connection. + */ + async close() { + await this._transport?.close(); + } + /** + * Sends a request and returns an AsyncGenerator that yields response messages. + * The generator is guaranteed to end with either a 'result' or 'error' message. + * + * @example + * ```typescript + * const stream = protocol.requestStream(request, resultSchema, options); + * for await (const message of stream) { + * switch (message.type) { + * case 'taskCreated': + * console.log('Task created:', message.task.taskId); + * break; + * case 'taskStatus': + * console.log('Task status:', message.task.status); + * break; + * case 'result': + * console.log('Final result:', message.result); + * break; + * case 'error': + * console.error('Error:', message.error); + * break; + * } + * } + * ``` + * + * @experimental Use `client.experimental.tasks.requestStream()` to access this method. + */ + async *requestStream(request, resultSchema, options) { + const { task } = options ?? {}; + if (!task) { + try { + const result = await this.request(request, resultSchema, options); + yield { type: "result", result }; + } catch (error2) { + yield { + type: "error", + error: error2 instanceof McpError ? error2 : new McpError(ErrorCode.InternalError, String(error2)) + }; + } + return; + } + let taskId; + try { + const createResult = await this.request(request, CreateTaskResultSchema, options); + if (createResult.task) { + taskId = createResult.task.taskId; + yield { type: "taskCreated", task: createResult.task }; + } else { + throw new McpError(ErrorCode.InternalError, "Task creation did not return a task"); + } + while (true) { + const task2 = await this.getTask({ taskId }, options); + yield { type: "taskStatus", task: task2 }; + if (isTerminal(task2.status)) { + if (task2.status === "completed") { + const result = await this.getTaskResult({ taskId }, resultSchema, options); + yield { type: "result", result }; + } else if (task2.status === "failed") { + yield { + type: "error", + error: new McpError(ErrorCode.InternalError, `Task ${taskId} failed`) + }; + } else if (task2.status === "cancelled") { + yield { + type: "error", + error: new McpError(ErrorCode.InternalError, `Task ${taskId} was cancelled`) + }; + } + return; + } + if (task2.status === "input_required") { + const result = await this.getTaskResult({ taskId }, resultSchema, options); + yield { type: "result", result }; + return; + } + const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3; + await new Promise((resolve) => setTimeout(resolve, pollInterval)); + options?.signal?.throwIfAborted(); + } + } catch (error2) { + yield { + type: "error", + error: error2 instanceof McpError ? error2 : new McpError(ErrorCode.InternalError, String(error2)) + }; + } + } + /** + * Sends a request and waits for a response. + * + * Do not use this method to emit notifications! Use notification() instead. + */ + request(request, resultSchema, options) { + const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {}; + return new Promise((resolve, reject) => { + const earlyReject = (error2) => { + reject(error2); + }; + if (!this._transport) { + earlyReject(new Error("Not connected")); + return; + } + if (this._options?.enforceStrictCapabilities === true) { + try { + this.assertCapabilityForMethod(request.method); + if (task) { + this.assertTaskCapability(request.method); + } + } catch (e) { + earlyReject(e); + return; + } + } + options?.signal?.throwIfAborted(); + const messageId = this._requestMessageId++; + const jsonrpcRequest = { + ...request, + jsonrpc: "2.0", + id: messageId + }; + if (options?.onprogress) { + this._progressHandlers.set(messageId, options.onprogress); + jsonrpcRequest.params = { + ...request.params, + _meta: { + ...request.params?._meta || {}, + progressToken: messageId + } + }; + } + if (task) { + jsonrpcRequest.params = { + ...jsonrpcRequest.params, + task + }; + } + if (relatedTask) { + jsonrpcRequest.params = { + ...jsonrpcRequest.params, + _meta: { + ...jsonrpcRequest.params?._meta || {}, + [RELATED_TASK_META_KEY]: relatedTask + } + }; + } + const cancel = (reason) => { + this._responseHandlers.delete(messageId); + this._progressHandlers.delete(messageId); + this._cleanupTimeout(messageId); + this._transport?.send({ + jsonrpc: "2.0", + method: "notifications/cancelled", + params: { + requestId: messageId, + reason: String(reason) + } + }, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error3) => this._onerror(new Error(`Failed to send cancellation: ${error3}`))); + const error2 = reason instanceof McpError ? reason : new McpError(ErrorCode.RequestTimeout, String(reason)); + reject(error2); + }; + this._responseHandlers.set(messageId, (response) => { + if (options?.signal?.aborted) { + return; + } + if (response instanceof Error) { + return reject(response); + } + try { + const parseResult = safeParse3(resultSchema, response.result); + if (!parseResult.success) { + reject(parseResult.error); + } else { + resolve(parseResult.data); + } + } catch (error2) { + reject(error2); + } + }); + options?.signal?.addEventListener("abort", () => { + cancel(options?.signal?.reason); + }); + const timeout = options?.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC; + const timeoutHandler = () => cancel(McpError.fromError(ErrorCode.RequestTimeout, "Request timed out", { timeout })); + this._setupTimeout(messageId, timeout, options?.maxTotalTimeout, timeoutHandler, options?.resetTimeoutOnProgress ?? false); + const relatedTaskId = relatedTask?.taskId; + if (relatedTaskId) { + const responseResolver = (response) => { + const handler = this._responseHandlers.get(messageId); + if (handler) { + handler(response); + } else { + this._onerror(new Error(`Response handler missing for side-channeled request ${messageId}`)); + } + }; + this._requestResolvers.set(messageId, responseResolver); + this._enqueueTaskMessage(relatedTaskId, { + type: "request", + message: jsonrpcRequest, + timestamp: Date.now() + }).catch((error2) => { + this._cleanupTimeout(messageId); + reject(error2); + }); + } else { + this._transport.send(jsonrpcRequest, { relatedRequestId, resumptionToken, onresumptiontoken }).catch((error2) => { + this._cleanupTimeout(messageId); + reject(error2); + }); + } + }); + } + /** + * Gets the current status of a task. + * + * @experimental Use `client.experimental.tasks.getTask()` to access this method. + */ + async getTask(params, options) { + return this.request({ method: "tasks/get", params }, GetTaskResultSchema, options); + } + /** + * Retrieves the result of a completed task. + * + * @experimental Use `client.experimental.tasks.getTaskResult()` to access this method. + */ + async getTaskResult(params, resultSchema, options) { + return this.request({ method: "tasks/result", params }, resultSchema, options); + } + /** + * Lists tasks, optionally starting from a pagination cursor. + * + * @experimental Use `client.experimental.tasks.listTasks()` to access this method. + */ + async listTasks(params, options) { + return this.request({ method: "tasks/list", params }, ListTasksResultSchema, options); + } + /** + * Cancels a specific task. + * + * @experimental Use `client.experimental.tasks.cancelTask()` to access this method. + */ + async cancelTask(params, options) { + return this.request({ method: "tasks/cancel", params }, CancelTaskResultSchema, options); + } + /** + * Emits a notification, which is a one-way message that does not expect a response. + */ + async notification(notification, options) { + if (!this._transport) { + throw new Error("Not connected"); + } + this.assertNotificationCapability(notification.method); + const relatedTaskId = options?.relatedTask?.taskId; + if (relatedTaskId) { + const jsonrpcNotification2 = { + ...notification, + jsonrpc: "2.0", + params: { + ...notification.params, + _meta: { + ...notification.params?._meta || {}, + [RELATED_TASK_META_KEY]: options.relatedTask + } + } + }; + await this._enqueueTaskMessage(relatedTaskId, { + type: "notification", + message: jsonrpcNotification2, + timestamp: Date.now() + }); + return; + } + const debouncedMethods = this._options?.debouncedNotificationMethods ?? []; + const canDebounce = debouncedMethods.includes(notification.method) && !notification.params && !options?.relatedRequestId && !options?.relatedTask; + if (canDebounce) { + if (this._pendingDebouncedNotifications.has(notification.method)) { + return; + } + this._pendingDebouncedNotifications.add(notification.method); + Promise.resolve().then(() => { + this._pendingDebouncedNotifications.delete(notification.method); + if (!this._transport) { + return; + } + let jsonrpcNotification2 = { + ...notification, + jsonrpc: "2.0" + }; + if (options?.relatedTask) { + jsonrpcNotification2 = { + ...jsonrpcNotification2, + params: { + ...jsonrpcNotification2.params, + _meta: { + ...jsonrpcNotification2.params?._meta || {}, + [RELATED_TASK_META_KEY]: options.relatedTask + } + } + }; + } + this._transport?.send(jsonrpcNotification2, options).catch((error2) => this._onerror(error2)); + }); + return; + } + let jsonrpcNotification = { + ...notification, + jsonrpc: "2.0" + }; + if (options?.relatedTask) { + jsonrpcNotification = { + ...jsonrpcNotification, + params: { + ...jsonrpcNotification.params, + _meta: { + ...jsonrpcNotification.params?._meta || {}, + [RELATED_TASK_META_KEY]: options.relatedTask + } + } + }; + } + await this._transport.send(jsonrpcNotification, options); + } + /** + * Registers a handler to invoke when this protocol object receives a request with the given method. + * + * Note that this will replace any previous request handler for the same method. + */ + setRequestHandler(requestSchema, handler) { + const method = getMethodLiteral(requestSchema); + this.assertRequestHandlerCapability(method); + this._requestHandlers.set(method, (request, extra) => { + const parsed = parseWithCompat(requestSchema, request); + return Promise.resolve(handler(parsed, extra)); + }); + } + /** + * Removes the request handler for the given method. + */ + removeRequestHandler(method) { + this._requestHandlers.delete(method); + } + /** + * Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed. + */ + assertCanSetRequestHandler(method) { + if (this._requestHandlers.has(method)) { + throw new Error(`A request handler for ${method} already exists, which would be overridden`); + } + } + /** + * Registers a handler to invoke when this protocol object receives a notification with the given method. + * + * Note that this will replace any previous notification handler for the same method. + */ + setNotificationHandler(notificationSchema, handler) { + const method = getMethodLiteral(notificationSchema); + this._notificationHandlers.set(method, (notification) => { + const parsed = parseWithCompat(notificationSchema, notification); + return Promise.resolve(handler(parsed)); + }); + } + /** + * Removes the notification handler for the given method. + */ + removeNotificationHandler(method) { + this._notificationHandlers.delete(method); + } + /** + * Cleans up the progress handler associated with a task. + * This should be called when a task reaches a terminal status. + */ + _cleanupTaskProgressHandler(taskId) { + const progressToken = this._taskProgressTokens.get(taskId); + if (progressToken !== void 0) { + this._progressHandlers.delete(progressToken); + this._taskProgressTokens.delete(taskId); + } + } + /** + * Enqueues a task-related message for side-channel delivery via tasks/result. + * @param taskId The task ID to associate the message with + * @param message The message to enqueue + * @param sessionId Optional session ID for binding the operation to a specific session + * @throws Error if taskStore is not configured or if enqueue fails (e.g., queue overflow) + * + * Note: If enqueue fails, it's the TaskMessageQueue implementation's responsibility to handle + * the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer + * simply propagates the error. + */ + async _enqueueTaskMessage(taskId, message, sessionId) { + if (!this._taskStore || !this._taskMessageQueue) { + throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured"); + } + const maxQueueSize = this._options?.maxTaskQueueSize; + await this._taskMessageQueue.enqueue(taskId, message, sessionId, maxQueueSize); + } + /** + * Clears the message queue for a task and rejects any pending request resolvers. + * @param taskId The task ID whose queue should be cleared + * @param sessionId Optional session ID for binding the operation to a specific session + */ + async _clearTaskQueue(taskId, sessionId) { + if (this._taskMessageQueue) { + const messages = await this._taskMessageQueue.dequeueAll(taskId, sessionId); + for (const message of messages) { + if (message.type === "request" && isJSONRPCRequest(message.message)) { + const requestId = message.message.id; + const resolver = this._requestResolvers.get(requestId); + if (resolver) { + resolver(new McpError(ErrorCode.InternalError, "Task cancelled or completed")); + this._requestResolvers.delete(requestId); + } else { + this._onerror(new Error(`Resolver missing for request ${requestId} during task ${taskId} cleanup`)); + } + } + } + } + } + /** + * Waits for a task update (new messages or status change) with abort signal support. + * Uses polling to check for updates at the task's configured poll interval. + * @param taskId The task ID to wait for + * @param signal Abort signal to cancel the wait + * @returns Promise that resolves when an update occurs or rejects if aborted + */ + async _waitForTaskUpdate(taskId, signal) { + let interval = this._options?.defaultTaskPollInterval ?? 1e3; + try { + const task = await this._taskStore?.getTask(taskId); + if (task?.pollInterval) { + interval = task.pollInterval; + } + } catch { + } + return new Promise((resolve, reject) => { + if (signal.aborted) { + reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled")); + return; + } + const timeoutId = setTimeout(resolve, interval); + signal.addEventListener("abort", () => { + clearTimeout(timeoutId); + reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled")); + }, { once: true }); + }); + } + requestTaskStore(request, sessionId) { + const taskStore = this._taskStore; + if (!taskStore) { + throw new Error("No task store configured"); + } + return { + createTask: async (taskParams) => { + if (!request) { + throw new Error("No request provided"); + } + return await taskStore.createTask(taskParams, request.id, { + method: request.method, + params: request.params + }, sessionId); + }, + getTask: async (taskId) => { + const task = await taskStore.getTask(taskId, sessionId); + if (!task) { + throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found"); + } + return task; + }, + storeTaskResult: async (taskId, status, result) => { + await taskStore.storeTaskResult(taskId, status, result, sessionId); + const task = await taskStore.getTask(taskId, sessionId); + if (task) { + const notification = TaskStatusNotificationSchema.parse({ + method: "notifications/tasks/status", + params: task + }); + await this.notification(notification); + if (isTerminal(task.status)) { + this._cleanupTaskProgressHandler(taskId); + } + } + }, + getTaskResult: (taskId) => { + return taskStore.getTaskResult(taskId, sessionId); + }, + updateTaskStatus: async (taskId, status, statusMessage) => { + const task = await taskStore.getTask(taskId, sessionId); + if (!task) { + throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`); + } + if (isTerminal(task.status)) { + throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`); + } + await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId); + const updatedTask = await taskStore.getTask(taskId, sessionId); + if (updatedTask) { + const notification = TaskStatusNotificationSchema.parse({ + method: "notifications/tasks/status", + params: updatedTask + }); + await this.notification(notification); + if (isTerminal(updatedTask.status)) { + this._cleanupTaskProgressHandler(taskId); + } + } + }, + listTasks: (cursor) => { + return taskStore.listTasks(cursor, sessionId); + } + }; + } +}; +function isPlainObject2(value) { + return value !== null && typeof value === "object" && !Array.isArray(value); +} +function mergeCapabilities(base, additional) { + const result = { ...base }; + for (const key in additional) { + const k2 = key; + const addValue = additional[k2]; + if (addValue === void 0) + continue; + const baseValue = result[k2]; + if (isPlainObject2(baseValue) && isPlainObject2(addValue)) { + result[k2] = { ...baseValue, ...addValue }; + } else { + result[k2] = addValue; + } + } + return result; +} + +// node_modules/@modelcontextprotocol/ext-apps/dist/src/server/index.js +var QI = Object.defineProperty; +var s = (r, i) => { + for (var o in i) QI(r, o, { get: i[o], enumerable: true, configurable: true, set: (t) => i[o] = () => t }); +}; +var g = {}; +s(g, { xor: () => al, xid: () => Ol, void: () => Zl, uuidv7: () => cl, uuidv6: () => Il, uuidv4: () => ll, uuid: () => el, util: () => D, url: () => bl, uppercase: () => Kr, unknown: () => Nr, union: () => ev, undefined: () => Rl, ulid: () => Nl, uint64: () => Tl, uint32: () => Bl, tuple: () => Yg, trim: () => mr, treeifyError: () => Xv, transform: () => Iv, toUpperCase: () => Tr, toLowerCase: () => Hr, toJSONSchema: () => Yi, templateLiteral: () => lI, symbol: () => Ml, superRefine: () => ee, success: () => uI, stringbool: () => wI, stringFormat: () => El, string: () => Mi, strictObject: () => yl, startsWith: () => Qr, slugify: () => Mr, size: () => kr, setErrorMap: () => b6, set: () => iI, safeParseAsync: () => lg, safeParse: () => eg, safeEncodeAsync: () => Dg, safeEncode: () => Ug, safeDecodeAsync: () => wg, safeDecode: () => kg, registry: () => ui, regexes: () => x, regex: () => Er, refine: () => ge, record: () => Fg, readonly: () => ie, property: () => Ai, promise: () => II, prettifyError: () => Ev, preprocess: () => OI, prefault: () => yg, positive: () => Wi, pipe: () => Gn, partialRecord: () => sl, parseAsync: () => gg, parse: () => ug, overwrite: () => d, optional: () => Jn, object: () => fl, number: () => Og, nullish: () => $I, nullable: () => Ln, null: () => Jg, normalize: () => Br, nonpositive: () => Xi, nonoptional: () => hg, nonnegative: () => Ei, never: () => gv, negative: () => Vi, nativeEnum: () => vI, nanoid: () => kl, nan: () => gI, multipleOf: () => ur, minSize: () => a, minLength: () => nr, mime: () => Fr, meta: () => kI, maxSize: () => gr, maxLength: () => Dr, map: () => nI, mac: () => Pl, lte: () => M, lt: () => y, lowercase: () => Ar, looseRecord: () => rI, looseObject: () => hl, locales: () => On, literal: () => oI, length: () => wr, lazy: () => te, ksuid: () => zl, keyof: () => Cl, jwt: () => Xl, json: () => NI, iso: () => Zr, ipv6: () => jl, ipv4: () => Sl, intersection: () => qg, int64: () => Hl, int32: () => Fl, int: () => Ri, instanceof: () => DI, includes: () => qr, httpUrl: () => _l, hostname: () => Al, hex: () => Kl, hash: () => ql, guid: () => gl, gte: () => Q, gt: () => h, globalRegistry: () => A, getErrorMap: () => _6, function: () => cI, fromJSONSchema: () => SI, formatError: () => en, float64: () => Yl, float32: () => Ql, flattenError: () => gn, file: () => tI, exactOptional: () => xg, enum: () => lv, endsWith: () => Yr, encodeAsync: () => bg, encode: () => Ig, emoji: () => Ul, email: () => ul, e164: () => Vl, discriminatedUnion: () => pl, describe: () => UI, decodeAsync: () => _g, decode: () => cg, date: () => dl, custom: () => _I, cuid2: () => wl, cuid: () => Dl, core: () => ir, config: () => E, coerce: () => Ie, codec: () => eI, clone: () => q, cidrv6: () => Ll, cidrv4: () => Jl, check: () => bI, catch: () => sg, boolean: () => zg, bigint: () => ml, base64url: () => Wl, base64: () => Gl, array: () => Xn, any: () => xl, _function: () => cI, _default: () => Cg, _ZodString: () => xi, ZodXor: () => Eg, ZodXID: () => ai, ZodVoid: () => Vg, ZodUnknown: () => Gg, ZodUnion: () => An, ZodUndefined: () => Pg, ZodUUID: () => p, ZodURL: () => Wn, ZodULID: () => hi, ZodType: () => P, ZodTuple: () => Qg, ZodTransform: () => Mg, ZodTemplateLiteral: () => ve, ZodSymbol: () => Sg, ZodSuccess: () => ag, ZodStringFormat: () => W, ZodString: () => Cr, ZodSet: () => mg, ZodRecord: () => Kn, ZodRealError: () => H, ZodReadonly: () => ne, ZodPromise: () => $e, ZodPrefault: () => fg, ZodPipe: () => _v, ZodOptional: () => cv, ZodObject: () => En, ZodNumberFormat: () => Or, ZodNumber: () => yr, ZodNullable: () => Zg, ZodNull: () => jg, ZodNonOptional: () => bv, ZodNever: () => Wg, ZodNanoID: () => Ci, ZodNaN: () => re, ZodMap: () => Bg, ZodMAC: () => Ng, ZodLiteral: () => Hg, ZodLazy: () => oe, ZodKSUID: () => pi, ZodJWT: () => $v, ZodIssueCode: () => c6, ZodIntersection: () => Kg, ZodISOTime: () => Hi, ZodISODuration: () => Ti, ZodISODateTime: () => Bi, ZodISODate: () => mi, ZodIPv6: () => rv, ZodIPv4: () => si, ZodGUID: () => jn, ZodFunction: () => ue, ZodFirstPartyTypeKind: () => le, ZodFile: () => Tg, ZodExactOptional: () => Rg, ZodError: () => l6, ZodEnum: () => dr, ZodEmoji: () => di, ZodEmail: () => Zi, ZodE164: () => tv, ZodDiscriminatedUnion: () => Ag, ZodDefault: () => dg, ZodDate: () => Vn, ZodCustomStringFormat: () => fr, ZodCustom: () => qn, ZodCodec: () => Uv, ZodCatch: () => pg, ZodCUID2: () => yi, ZodCUID: () => fi, ZodCIDRv6: () => iv, ZodCIDRv4: () => nv, ZodBoolean: () => hr, ZodBigIntFormat: () => uv, ZodBigInt: () => ar, ZodBase64URL: () => ov, ZodBase64: () => vv, ZodArray: () => Xg, ZodAny: () => Lg, TimePrecision: () => Y$, NEVER: () => Nv, $output: () => X$, $input: () => E$, $brand: () => Ov }); +var ir = {}; +s(ir, { version: () => Lo, util: () => D, treeifyError: () => Xv, toJSONSchema: () => Yi, toDotPath: () => Xe, safeParseAsync: () => Kv, safeParse: () => Av, safeEncodeAsync: () => Uc, safeEncode: () => bc, safeDecodeAsync: () => kc, safeDecode: () => _c, registry: () => ui, regexes: () => x, process: () => L, prettifyError: () => Ev, parseAsync: () => mn, parse: () => Bn, meta: () => ku, locales: () => On, isValidJWT: () => ye, isValidBase64URL: () => fe, isValidBase64: () => yo, initializeContext: () => er, globalRegistry: () => A, globalConfig: () => sr, formatError: () => en, flattenError: () => gn, finalize: () => Ir, extractDefs: () => lr, encodeAsync: () => Ic, encode: () => ec, describe: () => Uu, decodeAsync: () => cc, decode: () => lc, createToJSONSchemaMethod: () => wu, createStandardJSONSchemaMethod: () => xr, config: () => E, clone: () => q, _xor: () => H4, _xid: () => wi, _void: () => $u, _uuidv7: () => ci, _uuidv6: () => Ii, _uuidv4: () => li, _uuid: () => ei, _url: () => Sn, _uppercase: () => Kr, _unknown: () => ou, _union: () => m4, _undefined: () => nu, _ulid: () => Di, _uint64: () => s$, _uint32: () => C$, _tuple: () => R4, _trim: () => mr, _transform: () => h4, _toUpperCase: () => Tr, _toLowerCase: () => Hr, _templateLiteral: () => t6, _symbol: () => ru, _superRefine: () => _u, _success: () => n6, _stringbool: () => Du, _stringFormat: () => Rr, _string: () => K$, _startsWith: () => Qr, _slugify: () => Mr, _size: () => kr, _set: () => d4, _safeParseAsync: () => Wr, _safeParse: () => Gr, _safeEncodeAsync: () => dn, _safeEncode: () => xn, _safeDecodeAsync: () => Cn, _safeDecode: () => Zn, _regex: () => Er, _refine: () => bu, _record: () => x4, _readonly: () => o6, _property: () => Ai, _promise: () => u6, _positive: () => Wi, _pipe: () => v6, _parseAsync: () => Lr, _parse: () => Jr, _overwrite: () => d, _optional: () => a4, _number: () => T$, _nullable: () => p4, _null: () => iu, _normalize: () => Br, _nonpositive: () => Xi, _nonoptional: () => r6, _nonnegative: () => Ei, _never: () => tu, _negative: () => Vi, _nativeEnum: () => f4, _nanoid: () => _i, _nan: () => eu, _multipleOf: () => ur, _minSize: () => a, _minLength: () => nr, _min: () => Q, _mime: () => Fr, _maxSize: () => gr, _maxLength: () => Dr, _max: () => M, _map: () => Z4, _mac: () => Q$, _lte: () => M, _lt: () => y, _lowercase: () => Ar, _literal: () => y4, _length: () => wr, _lazy: () => $6, _ksuid: () => Ni, _jwt: () => Gi, _isoTime: () => m$, _isoDuration: () => H$, _isoDateTime: () => F$, _isoDate: () => B$, _ipv6: () => zi, _ipv4: () => Oi, _intersection: () => M4, _int64: () => p$, _int32: () => d$, _int: () => R$, _includes: () => qr, _guid: () => zn, _gte: () => Q, _gt: () => h, _float64: () => Z$, _float32: () => x$, _file: () => Iu, _enum: () => C4, _endsWith: () => Yr, _encodeAsync: () => Mn, _encode: () => Hn, _emoji: () => bi, _email: () => gi, _e164: () => Li, _discriminatedUnion: () => T4, _default: () => s4, _decodeAsync: () => Rn, _decode: () => Tn, _date: () => uu, _custom: () => cu, _cuid2: () => ki, _cuid: () => Ui, _coercedString: () => q$, _coercedNumber: () => M$, _coercedDate: () => gu, _coercedBoolean: () => y$, _coercedBigint: () => a$, _cidrv6: () => Pi, _cidrv4: () => Si, _check: () => ol, _catch: () => i6, _boolean: () => f$, _bigint: () => h$, _base64url: () => Ji, _base64: () => ji, _array: () => lu, _any: () => vu, TimePrecision: () => Y$, NEVER: () => Nv, JSONSchemaGenerator: () => ig, JSONSchema: () => tl, Doc: () => an, $output: () => X$, $input: () => E$, $constructor: () => I, $brand: () => Ov, $ZodXor: () => bt, $ZodXID: () => Fo, $ZodVoid: () => et, $ZodUnknown: () => ut, $ZodUnion: () => _n, $ZodUndefined: () => ot, $ZodUUID: () => Vo, $ZodURL: () => Eo, $ZodULID: () => Yo, $ZodType: () => S, $ZodTuple: () => ti, $ZodTransform: () => St, $ZodTemplateLiteral: () => Kt, $ZodSymbol: () => vt, $ZodSuccess: () => Wt, $ZodStringFormat: () => G, $ZodString: () => Ur, $ZodSet: () => wt, $ZodRegistry: () => A$, $ZodRecord: () => kt, $ZodRealError: () => m, $ZodReadonly: () => At, $ZodPromise: () => Qt, $ZodPrefault: () => Lt, $ZodPipe: () => Et, $ZodOptional: () => $i, $ZodObjectJIT: () => ct, $ZodObject: () => pe, $ZodNumberFormat: () => nt, $ZodNumber: () => vi, $ZodNullable: () => jt, $ZodNull: () => tt, $ZodNonOptional: () => Gt, $ZodNever: () => gt, $ZodNanoID: () => Ko, $ZodNaN: () => Xt, $ZodMap: () => Dt, $ZodMAC: () => Zo, $ZodLiteral: () => Ot, $ZodLazy: () => Yt, $ZodKSUID: () => Bo, $ZodJWT: () => so, $ZodIntersection: () => Ut, $ZodISOTime: () => To, $ZodISODuration: () => Mo, $ZodISODateTime: () => mo, $ZodISODate: () => Ho, $ZodIPv6: () => xo, $ZodIPv4: () => Ro, $ZodGUID: () => Wo, $ZodFunction: () => qt, $ZodFile: () => zt, $ZodExactOptional: () => Pt, $ZodError: () => un, $ZodEnum: () => Nt, $ZodEncodeError: () => cr, $ZodEmoji: () => Ao, $ZodEmail: () => Xo, $ZodE164: () => po, $ZodDiscriminatedUnion: () => _t, $ZodDefault: () => Jt, $ZodDate: () => lt, $ZodCustomStringFormat: () => rt, $ZodCustom: () => Ft, $ZodCodec: () => Un, $ZodCheckUpperCase: () => No, $ZodCheckStringFormat: () => Vr, $ZodCheckStartsWith: () => zo, $ZodCheckSizeEquals: () => bo, $ZodCheckRegex: () => Do, $ZodCheckProperty: () => Po, $ZodCheckOverwrite: () => Jo, $ZodCheckNumberFormat: () => eo, $ZodCheckMultipleOf: () => go, $ZodCheckMinSize: () => co, $ZodCheckMinLength: () => Uo, $ZodCheckMimeType: () => jo, $ZodCheckMaxSize: () => Io, $ZodCheckMaxLength: () => _o, $ZodCheckLowerCase: () => wo, $ZodCheckLessThan: () => yn, $ZodCheckLengthEquals: () => ko, $ZodCheckIncludes: () => Oo, $ZodCheckGreaterThan: () => hn, $ZodCheckEndsWith: () => So, $ZodCheckBigIntFormat: () => lo, $ZodCheck: () => V, $ZodCatch: () => Vt, $ZodCUID2: () => Qo, $ZodCUID: () => qo, $ZodCIDRv6: () => fo, $ZodCIDRv4: () => Co, $ZodBoolean: () => bn, $ZodBigIntFormat: () => it, $ZodBigInt: () => oi, $ZodBase64URL: () => ao, $ZodBase64: () => ho, $ZodAsyncError: () => f, $ZodArray: () => It, $ZodAny: () => $t }); +var Nv = Object.freeze({ status: "aborted" }); +function I(r, i, o) { + function t(u, l) { + if (!u._zod) Object.defineProperty(u, "_zod", { value: { def: l, constr: $, traits: /* @__PURE__ */ new Set() }, enumerable: false }); + if (u._zod.traits.has(r)) return; + u._zod.traits.add(r), i(u, l); + let e = $.prototype, c = Object.keys(e); + for (let _ = 0; _ < c.length; _++) { + let N = c[_]; + if (!(N in u)) u[N] = e[N].bind(u); + } + } + let n = o?.Parent ?? Object; + class v extends n { + } + Object.defineProperty(v, "name", { value: r }); + function $(u) { + var l; + let e = o?.Parent ? new v() : this; + t(e, u), (l = e._zod).deferred ?? (l.deferred = []); + for (let c of e._zod.deferred) c(); + return e; + } + return Object.defineProperty($, "init", { value: t }), Object.defineProperty($, Symbol.hasInstance, { value: (u) => { + if (o?.Parent && u instanceof o.Parent) return true; + return u?._zod?.traits?.has(r); + } }), Object.defineProperty($, "name", { value: r }), $; +} +var Ov = /* @__PURE__ */ Symbol("zod_brand"); +var f = class extends Error { + constructor() { + super("Encountered Promise during synchronous parse. Use .parseAsync() instead."); + } +}; +var cr = class extends Error { + constructor(r) { + super(`Encountered unidirectional transform during encode: ${r}`); + this.name = "ZodEncodeError"; + } +}; +var sr = {}; +function E(r) { + if (r) Object.assign(sr, r); + return sr; +} +var D = {}; +s(D, { unwrapMessage: () => rn, uint8ArrayToHex: () => uc, uint8ArrayToBase64url: () => tc, uint8ArrayToBase64: () => Ge, stringifyPrimitive: () => U, slugify: () => Pv, shallowClone: () => Jv, safeExtend: () => sI, required: () => ic, randomString: () => dI, propertyKeyTypes: () => on, promiseAllObject: () => ZI, primitiveTypes: () => Lv, prefixIssues: () => T, pick: () => hI, partial: () => nc, parsedType: () => k, optionalKeys: () => Gv, omit: () => aI, objectClone: () => MI, numKeys: () => CI, nullish: () => vr, normalizeParams: () => w, mergeDefs: () => rr, merge: () => rc, jsonStringifyReplacer: () => Sr, joinValues: () => b, issue: () => jr, isPlainObject: () => tr, isObject: () => br, hexToUint8Array: () => $c, getSizableOrigin: () => tn, getParsedType: () => fI, getLengthableOrigin: () => $n, getEnumValues: () => nn, getElementAtPath: () => xI, floatSafeRemainder: () => Sv, finalizeIssue: () => B, extend: () => pI, escapeRegex: () => R, esc: () => Yn, defineLazy: () => j, createTransparentProxy: () => yI, cloneDef: () => RI, clone: () => q, cleanRegex: () => vn, cleanEnum: () => vc, captureStackTrace: () => Fn, cached: () => Pr, base64urlToUint8Array: () => oc, base64ToUint8Array: () => Le, assignProp: () => or, assertNotEqual: () => BI, assertNever: () => HI, assertIs: () => mI, assertEqual: () => FI, assert: () => TI, allowsEval: () => jv, aborted: () => $r, NUMBER_FORMAT_RANGES: () => Wv, Class: () => We, BIGINT_FORMAT_RANGES: () => Vv }); +function FI(r) { + return r; +} +function BI(r) { + return r; +} +function mI(r) { +} +function HI(r) { + throw Error("Unexpected value in exhaustive check"); +} +function TI(r) { +} +function nn(r) { + let i = Object.values(r).filter((t) => typeof t === "number"); + return Object.entries(r).filter(([t, n]) => i.indexOf(+t) === -1).map(([t, n]) => n); +} +function b(r, i = "|") { + return r.map((o) => U(o)).join(i); +} +function Sr(r, i) { + if (typeof i === "bigint") return i.toString(); + return i; +} +function Pr(r) { + return { get value() { + { + let o = r(); + return Object.defineProperty(this, "value", { value: o }), o; + } + throw Error("cached value already set"); + } }; +} +function vr(r) { + return r === null || r === void 0; +} +function vn(r) { + let i = r.startsWith("^") ? 1 : 0, o = r.endsWith("$") ? r.length - 1 : r.length; + return r.slice(i, o); +} +function Sv(r, i) { + let o = (r.toString().split(".")[1] || "").length, t = i.toString(), n = (t.split(".")[1] || "").length; + if (n === 0 && /\d?e-\d?/.test(t)) { + let l = t.match(/\d?e-(\d?)/); + if (l?.[1]) n = Number.parseInt(l[1]); + } + let v = o > n ? o : n, $ = Number.parseInt(r.toFixed(v).replace(".", "")), u = Number.parseInt(i.toFixed(v).replace(".", "")); + return $ % u / 10 ** v; +} +var Je = /* @__PURE__ */ Symbol("evaluating"); +function j(r, i, o) { + let t = void 0; + Object.defineProperty(r, i, { get() { + if (t === Je) return; + if (t === void 0) t = Je, t = o(); + return t; + }, set(n) { + Object.defineProperty(r, i, { value: n }); + }, configurable: true }); +} +function MI(r) { + return Object.create(Object.getPrototypeOf(r), Object.getOwnPropertyDescriptors(r)); +} +function or(r, i, o) { + Object.defineProperty(r, i, { value: o, writable: true, enumerable: true, configurable: true }); +} +function rr(...r) { + let i = {}; + for (let o of r) { + let t = Object.getOwnPropertyDescriptors(o); + Object.assign(i, t); + } + return Object.defineProperties({}, i); +} +function RI(r) { + return rr(r._zod.def); +} +function xI(r, i) { + if (!i) return r; + return i.reduce((o, t) => o?.[t], r); +} +function ZI(r) { + let i = Object.keys(r), o = i.map((t) => r[t]); + return Promise.all(o).then((t) => { + let n = {}; + for (let v = 0; v < i.length; v++) n[i[v]] = t[v]; + return n; + }); +} +function dI(r = 10) { + let o = ""; + for (let t = 0; t < r; t++) o += "abcdefghijklmnopqrstuvwxyz"[Math.floor(Math.random() * 26)]; + return o; +} +function Yn(r) { + return JSON.stringify(r); +} +function Pv(r) { + return r.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, ""); +} +var Fn = "captureStackTrace" in Error ? Error.captureStackTrace : (...r) => { +}; +function br(r) { + return typeof r === "object" && r !== null && !Array.isArray(r); +} +var jv = Pr(() => { + if (typeof navigator < "u" && navigator?.userAgent?.includes("Cloudflare")) return false; + try { + return new Function(""), true; + } catch (r) { + return false; + } +}); +function tr(r) { + if (br(r) === false) return false; + let i = r.constructor; + if (i === void 0) return true; + if (typeof i !== "function") return true; + let o = i.prototype; + if (br(o) === false) return false; + if (Object.prototype.hasOwnProperty.call(o, "isPrototypeOf") === false) return false; + return true; +} +function Jv(r) { + if (tr(r)) return { ...r }; + if (Array.isArray(r)) return [...r]; + return r; +} +function CI(r) { + let i = 0; + for (let o in r) if (Object.prototype.hasOwnProperty.call(r, o)) i++; + return i; +} +var fI = (r) => { + let i = typeof r; + switch (i) { + case "undefined": + return "undefined"; + case "string": + return "string"; + case "number": + return Number.isNaN(r) ? "nan" : "number"; + case "boolean": + return "boolean"; + case "function": + return "function"; + case "bigint": + return "bigint"; + case "symbol": + return "symbol"; + case "object": + if (Array.isArray(r)) return "array"; + if (r === null) return "null"; + if (r.then && typeof r.then === "function" && r.catch && typeof r.catch === "function") return "promise"; + if (typeof Map < "u" && r instanceof Map) return "map"; + if (typeof Set < "u" && r instanceof Set) return "set"; + if (typeof Date < "u" && r instanceof Date) return "date"; + if (typeof File < "u" && r instanceof File) return "file"; + return "object"; + default: + throw Error(`Unknown data type: ${i}`); + } +}; +var on = /* @__PURE__ */ new Set(["string", "number", "symbol"]); +var Lv = /* @__PURE__ */ new Set(["string", "number", "bigint", "boolean", "symbol", "undefined"]); +function R(r) { + return r.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} +function q(r, i, o) { + let t = new r._zod.constr(i ?? r._zod.def); + if (!i || o?.parent) t._zod.parent = r; + return t; +} +function w(r) { + let i = r; + if (!i) return {}; + if (typeof i === "string") return { error: () => i }; + if (i?.message !== void 0) { + if (i?.error !== void 0) throw Error("Cannot specify both `message` and `error` params"); + i.error = i.message; + } + if (delete i.message, typeof i.error === "string") return { ...i, error: () => i.error }; + return i; +} +function yI(r) { + let i; + return new Proxy({}, { get(o, t, n) { + return i ?? (i = r()), Reflect.get(i, t, n); + }, set(o, t, n, v) { + return i ?? (i = r()), Reflect.set(i, t, n, v); + }, has(o, t) { + return i ?? (i = r()), Reflect.has(i, t); + }, deleteProperty(o, t) { + return i ?? (i = r()), Reflect.deleteProperty(i, t); + }, ownKeys(o) { + return i ?? (i = r()), Reflect.ownKeys(i); + }, getOwnPropertyDescriptor(o, t) { + return i ?? (i = r()), Reflect.getOwnPropertyDescriptor(i, t); + }, defineProperty(o, t, n) { + return i ?? (i = r()), Reflect.defineProperty(i, t, n); + } }); +} +function U(r) { + if (typeof r === "bigint") return r.toString() + "n"; + if (typeof r === "string") return `"${r}"`; + return `${r}`; +} +function Gv(r) { + return Object.keys(r).filter((i) => { + return r[i]._zod.optin === "optional" && r[i]._zod.optout === "optional"; + }); +} +var Wv = { safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER], int32: [-2147483648, 2147483647], uint32: [0, 4294967295], float32: [-34028234663852886e22, 34028234663852886e22], float64: [-Number.MAX_VALUE, Number.MAX_VALUE] }; +var Vv = { int64: [BigInt("-9223372036854775808"), BigInt("9223372036854775807")], uint64: [BigInt(0), BigInt("18446744073709551615")] }; +function hI(r, i) { + let o = r._zod.def, t = o.checks; + if (t && t.length > 0) throw Error(".pick() cannot be used on object schemas containing refinements"); + let v = rr(r._zod.def, { get shape() { + let $ = {}; + for (let u in i) { + if (!(u in o.shape)) throw Error(`Unrecognized key: "${u}"`); + if (!i[u]) continue; + $[u] = o.shape[u]; + } + return or(this, "shape", $), $; + }, checks: [] }); + return q(r, v); +} +function aI(r, i) { + let o = r._zod.def, t = o.checks; + if (t && t.length > 0) throw Error(".omit() cannot be used on object schemas containing refinements"); + let v = rr(r._zod.def, { get shape() { + let $ = { ...r._zod.def.shape }; + for (let u in i) { + if (!(u in o.shape)) throw Error(`Unrecognized key: "${u}"`); + if (!i[u]) continue; + delete $[u]; + } + return or(this, "shape", $), $; + }, checks: [] }); + return q(r, v); +} +function pI(r, i) { + if (!tr(i)) throw Error("Invalid input to extend: expected a plain object"); + let o = r._zod.def.checks; + if (o && o.length > 0) { + let v = r._zod.def.shape; + for (let $ in i) if (Object.getOwnPropertyDescriptor(v, $) !== void 0) throw Error("Cannot overwrite keys on object schemas containing refinements. Use `.safeExtend()` instead."); + } + let n = rr(r._zod.def, { get shape() { + let v = { ...r._zod.def.shape, ...i }; + return or(this, "shape", v), v; + } }); + return q(r, n); +} +function sI(r, i) { + if (!tr(i)) throw Error("Invalid input to safeExtend: expected a plain object"); + let o = rr(r._zod.def, { get shape() { + let t = { ...r._zod.def.shape, ...i }; + return or(this, "shape", t), t; + } }); + return q(r, o); +} +function rc(r, i) { + let o = rr(r._zod.def, { get shape() { + let t = { ...r._zod.def.shape, ...i._zod.def.shape }; + return or(this, "shape", t), t; + }, get catchall() { + return i._zod.def.catchall; + }, checks: [] }); + return q(r, o); +} +function nc(r, i, o) { + let n = i._zod.def.checks; + if (n && n.length > 0) throw Error(".partial() cannot be used on object schemas containing refinements"); + let $ = rr(i._zod.def, { get shape() { + let u = i._zod.def.shape, l = { ...u }; + if (o) for (let e in o) { + if (!(e in u)) throw Error(`Unrecognized key: "${e}"`); + if (!o[e]) continue; + l[e] = r ? new r({ type: "optional", innerType: u[e] }) : u[e]; + } + else for (let e in u) l[e] = r ? new r({ type: "optional", innerType: u[e] }) : u[e]; + return or(this, "shape", l), l; + }, checks: [] }); + return q(i, $); +} +function ic(r, i, o) { + let t = rr(i._zod.def, { get shape() { + let n = i._zod.def.shape, v = { ...n }; + if (o) for (let $ in o) { + if (!($ in v)) throw Error(`Unrecognized key: "${$}"`); + if (!o[$]) continue; + v[$] = new r({ type: "nonoptional", innerType: n[$] }); + } + else for (let $ in n) v[$] = new r({ type: "nonoptional", innerType: n[$] }); + return or(this, "shape", v), v; + } }); + return q(i, t); +} +function $r(r, i = 0) { + if (r.aborted === true) return true; + for (let o = i; o < r.issues.length; o++) if (r.issues[o]?.continue !== true) return true; + return false; +} +function T(r, i) { + return i.map((o) => { + var t; + return (t = o).path ?? (t.path = []), o.path.unshift(r), o; + }); +} +function rn(r) { + return typeof r === "string" ? r : r?.message; +} +function B(r, i, o) { + let t = { ...r, path: r.path ?? [] }; + if (!r.message) { + let n = rn(r.inst?._zod.def?.error?.(r)) ?? rn(i?.error?.(r)) ?? rn(o.customError?.(r)) ?? rn(o.localeError?.(r)) ?? "Invalid input"; + t.message = n; + } + if (delete t.inst, delete t.continue, !i?.reportInput) delete t.input; + return t; +} +function tn(r) { + if (r instanceof Set) return "set"; + if (r instanceof Map) return "map"; + if (r instanceof File) return "file"; + return "unknown"; +} +function $n(r) { + if (Array.isArray(r)) return "array"; + if (typeof r === "string") return "string"; + return "unknown"; +} +function k(r) { + let i = typeof r; + switch (i) { + case "number": + return Number.isNaN(r) ? "nan" : "number"; + case "object": { + if (r === null) return "null"; + if (Array.isArray(r)) return "array"; + let o = r; + if (o && Object.getPrototypeOf(o) !== Object.prototype && "constructor" in o && o.constructor) return o.constructor.name; + } + } + return i; +} +function jr(...r) { + let [i, o, t] = r; + if (typeof i === "string") return { message: i, code: "custom", input: o, inst: t }; + return { ...i }; +} +function vc(r) { + return Object.entries(r).filter(([i, o]) => { + return Number.isNaN(Number.parseInt(i, 10)); + }).map((i) => i[1]); +} +function Le(r) { + let i = atob(r), o = new Uint8Array(i.length); + for (let t = 0; t < i.length; t++) o[t] = i.charCodeAt(t); + return o; +} +function Ge(r) { + let i = ""; + for (let o = 0; o < r.length; o++) i += String.fromCharCode(r[o]); + return btoa(i); +} +function oc(r) { + let i = r.replace(/-/g, "+").replace(/_/g, "/"), o = "=".repeat((4 - i.length % 4) % 4); + return Le(i + o); +} +function tc(r) { + return Ge(r).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, ""); +} +function $c(r) { + let i = r.replace(/^0x/, ""); + if (i.length % 2 !== 0) throw Error("Invalid hex string length"); + let o = new Uint8Array(i.length / 2); + for (let t = 0; t < i.length; t += 2) o[t / 2] = Number.parseInt(i.slice(t, t + 2), 16); + return o; +} +function uc(r) { + return Array.from(r).map((i) => i.toString(16).padStart(2, "0")).join(""); +} +var We = class { + constructor(...r) { + } +}; +var Ve = (r, i) => { + r.name = "$ZodError", Object.defineProperty(r, "_zod", { value: r._zod, enumerable: false }), Object.defineProperty(r, "issues", { value: i, enumerable: false }), r.message = JSON.stringify(i, Sr, 2), Object.defineProperty(r, "toString", { value: () => r.message, enumerable: false }); +}; +var un = I("$ZodError", Ve); +var m = I("$ZodError", Ve, { Parent: Error }); +function gn(r, i = (o) => o.message) { + let o = {}, t = []; + for (let n of r.issues) if (n.path.length > 0) o[n.path[0]] = o[n.path[0]] || [], o[n.path[0]].push(i(n)); + else t.push(i(n)); + return { formErrors: t, fieldErrors: o }; +} +function en(r, i = (o) => o.message) { + let o = { _errors: [] }, t = (n) => { + for (let v of n.issues) if (v.code === "invalid_union" && v.errors.length) v.errors.map(($) => t({ issues: $ })); + else if (v.code === "invalid_key") t({ issues: v.issues }); + else if (v.code === "invalid_element") t({ issues: v.issues }); + else if (v.path.length === 0) o._errors.push(i(v)); + else { + let $ = o, u = 0; + while (u < v.path.length) { + let l = v.path[u]; + if (u !== v.path.length - 1) $[l] = $[l] || { _errors: [] }; + else $[l] = $[l] || { _errors: [] }, $[l]._errors.push(i(v)); + $ = $[l], u++; + } + } + }; + return t(r), o; +} +function Xv(r, i = (o) => o.message) { + let o = { errors: [] }, t = (n, v = []) => { + var $, u; + for (let l of n.issues) if (l.code === "invalid_union" && l.errors.length) l.errors.map((e) => t({ issues: e }, l.path)); + else if (l.code === "invalid_key") t({ issues: l.issues }, l.path); + else if (l.code === "invalid_element") t({ issues: l.issues }, l.path); + else { + let e = [...v, ...l.path]; + if (e.length === 0) { + o.errors.push(i(l)); + continue; + } + let c = o, _ = 0; + while (_ < e.length) { + let N = e[_], O = _ === e.length - 1; + if (typeof N === "string") c.properties ?? (c.properties = {}), ($ = c.properties)[N] ?? ($[N] = { errors: [] }), c = c.properties[N]; + else c.items ?? (c.items = []), (u = c.items)[N] ?? (u[N] = { errors: [] }), c = c.items[N]; + if (O) c.errors.push(i(l)); + _++; + } + } + }; + return t(r), o; +} +function Xe(r) { + let i = [], o = r.map((t) => typeof t === "object" ? t.key : t); + for (let t of o) if (typeof t === "number") i.push(`[${t}]`); + else if (typeof t === "symbol") i.push(`[${JSON.stringify(String(t))}]`); + else if (/[^\w$]/.test(t)) i.push(`[${JSON.stringify(t)}]`); + else { + if (i.length) i.push("."); + i.push(t); + } + return i.join(""); +} +function Ev(r) { + let i = [], o = [...r.issues].sort((t, n) => (t.path ?? []).length - (n.path ?? []).length); + for (let t of o) if (i.push(`✖ ${t.message}`), t.path?.length) i.push(` → at ${Xe(t.path)}`); + return i.join(` +`); +} +var Jr = (r) => (i, o, t, n) => { + let v = t ? Object.assign(t, { async: false }) : { async: false }, $ = i._zod.run({ value: o, issues: [] }, v); + if ($ instanceof Promise) throw new f(); + if ($.issues.length) { + let u = new (n?.Err ?? r)($.issues.map((l) => B(l, v, E()))); + throw Fn(u, n?.callee), u; + } + return $.value; +}; +var Bn = Jr(m); +var Lr = (r) => async (i, o, t, n) => { + let v = t ? Object.assign(t, { async: true }) : { async: true }, $ = i._zod.run({ value: o, issues: [] }, v); + if ($ instanceof Promise) $ = await $; + if ($.issues.length) { + let u = new (n?.Err ?? r)($.issues.map((l) => B(l, v, E()))); + throw Fn(u, n?.callee), u; + } + return $.value; +}; +var mn = Lr(m); +var Gr = (r) => (i, o, t) => { + let n = t ? { ...t, async: false } : { async: false }, v = i._zod.run({ value: o, issues: [] }, n); + if (v instanceof Promise) throw new f(); + return v.issues.length ? { success: false, error: new (r ?? un)(v.issues.map(($) => B($, n, E()))) } : { success: true, data: v.value }; +}; +var Av = Gr(m); +var Wr = (r) => async (i, o, t) => { + let n = t ? Object.assign(t, { async: true }) : { async: true }, v = i._zod.run({ value: o, issues: [] }, n); + if (v instanceof Promise) v = await v; + return v.issues.length ? { success: false, error: new r(v.issues.map(($) => B($, n, E()))) } : { success: true, data: v.value }; +}; +var Kv = Wr(m); +var Hn = (r) => (i, o, t) => { + let n = t ? Object.assign(t, { direction: "backward" }) : { direction: "backward" }; + return Jr(r)(i, o, n); +}; +var ec = Hn(m); +var Tn = (r) => (i, o, t) => { + return Jr(r)(i, o, t); +}; +var lc = Tn(m); +var Mn = (r) => async (i, o, t) => { + let n = t ? Object.assign(t, { direction: "backward" }) : { direction: "backward" }; + return Lr(r)(i, o, n); +}; +var Ic = Mn(m); +var Rn = (r) => async (i, o, t) => { + return Lr(r)(i, o, t); +}; +var cc = Rn(m); +var xn = (r) => (i, o, t) => { + let n = t ? Object.assign(t, { direction: "backward" }) : { direction: "backward" }; + return Gr(r)(i, o, n); +}; +var bc = xn(m); +var Zn = (r) => (i, o, t) => { + return Gr(r)(i, o, t); +}; +var _c = Zn(m); +var dn = (r) => async (i, o, t) => { + let n = t ? Object.assign(t, { direction: "backward" }) : { direction: "backward" }; + return Wr(r)(i, o, n); +}; +var Uc = dn(m); +var Cn = (r) => async (i, o, t) => { + return Wr(r)(i, o, t); +}; +var kc = Cn(m); +var x = {}; +s(x, { xid: () => Fv, uuid7: () => Oc, uuid6: () => Nc, uuid4: () => wc, uuid: () => _r, uppercase: () => uo, unicodeEmail: () => Ee, undefined: () => to, ulid: () => Yv, time: () => pv, string: () => ro, sha512_hex: () => Tc, sha512_base64url: () => Rc, sha512_base64: () => Mc, sha384_hex: () => Bc, sha384_base64url: () => Hc, sha384_base64: () => mc, sha256_hex: () => Qc, sha256_base64url: () => Fc, sha256_base64: () => Yc, sha1_hex: () => Ac, sha1_base64url: () => qc, sha1_base64: () => Kc, rfc5322Email: () => Sc, number: () => ln, null: () => oo, nanoid: () => mv, md5_hex: () => Vc, md5_base64url: () => Ec, md5_base64: () => Xc, mac: () => dv, lowercase: () => $o, ksuid: () => Bv, ipv6: () => Zv, ipv4: () => xv, integer: () => io, idnEmail: () => Pc, html5Email: () => zc, hostname: () => Lc, hex: () => Wc, guid: () => Tv, extendedDuration: () => Dc, emoji: () => Rv, email: () => Mv, e164: () => hv, duration: () => Hv, domain: () => Gc, datetime: () => sv, date: () => av, cuid2: () => Qv, cuid: () => qv, cidrv6: () => fv, cidrv4: () => Cv, browserEmail: () => jc, boolean: () => vo, bigint: () => no, base64url: () => fn, base64: () => yv }); +var qv = /^[cC][^\s-]{8,}$/; +var Qv = /^[0-9a-z]+$/; +var Yv = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/; +var Fv = /^[0-9a-vA-V]{20}$/; +var Bv = /^[A-Za-z0-9]{27}$/; +var mv = /^[a-zA-Z0-9_-]{21}$/; +var Hv = /^P(?:(\d+W)|(?!.*W)(?=\d|T\d)(\d+Y)?(\d+M)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+([.,]\d+)?S)?)?)$/; +var Dc = /^[-+]?P(?!$)(?:(?:[-+]?\d+Y)|(?:[-+]?\d+[.,]\d+Y$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:(?:[-+]?\d+W)|(?:[-+]?\d+[.,]\d+W$))?(?:(?:[-+]?\d+D)|(?:[-+]?\d+[.,]\d+D$))?(?:T(?=[\d+-])(?:(?:[-+]?\d+H)|(?:[-+]?\d+[.,]\d+H$))?(?:(?:[-+]?\d+M)|(?:[-+]?\d+[.,]\d+M$))?(?:[-+]?\d+(?:[.,]\d+)?S)?)??$/; +var Tv = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/; +var _r = (r) => { + if (!r) return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/; + return new RegExp(`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${r}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`); +}; +var wc = _r(4); +var Nc = _r(6); +var Oc = _r(7); +var Mv = /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$/; +var zc = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; +var Sc = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; +var Ee = /^[^\s@"]{1,64}@[^\s@]{1,255}$/u; +var Pc = Ee; +var jc = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; +var Jc = "^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$"; +function Rv() { + return new RegExp(Jc, "u"); +} +var xv = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/; +var Zv = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/; +var dv = (r) => { + let i = R(r ?? ":"); + return new RegExp(`^(?:[0-9A-F]{2}${i}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${i}){5}[0-9a-f]{2}$`); +}; +var Cv = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/; +var fv = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/; +var yv = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/; +var fn = /^[A-Za-z0-9_-]*$/; +var Lc = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/; +var Gc = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/; +var hv = /^\+[1-9]\d{6,14}$/; +var Ae = "(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))"; +var av = new RegExp(`^${Ae}$`); +function Ke(r) { + return typeof r.precision === "number" ? r.precision === -1 ? "(?:[01]\\d|2[0-3]):[0-5]\\d" : r.precision === 0 ? "(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d" : `(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d\\.\\d{${r.precision}}` : "(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?"; +} +function pv(r) { + return new RegExp(`^${Ke(r)}$`); +} +function sv(r) { + let i = Ke({ precision: r.precision }), o = ["Z"]; + if (r.local) o.push(""); + if (r.offset) o.push("([+-](?:[01]\\d|2[0-3]):[0-5]\\d)"); + let t = `${i}(?:${o.join("|")})`; + return new RegExp(`^${Ae}T(?:${t})$`); +} +var ro = (r) => { + let i = r ? `[\\s\\S]{${r?.minimum ?? 0},${r?.maximum ?? ""}}` : "[\\s\\S]*"; + return new RegExp(`^${i}$`); +}; +var no = /^-?\d+n?$/; +var io = /^-?\d+$/; +var ln = /^-?\d+(?:\.\d+)?$/; +var vo = /^(?:true|false)$/i; +var oo = /^null$/i; +var to = /^undefined$/i; +var $o = /^[^A-Z]*$/; +var uo = /^[^a-z]*$/; +var Wc = /^[0-9a-fA-F]*$/; +function In(r, i) { + return new RegExp(`^[A-Za-z0-9+/]{${r}}${i}$`); +} +function cn(r) { + return new RegExp(`^[A-Za-z0-9_-]{${r}}$`); +} +var Vc = /^[0-9a-fA-F]{32}$/; +var Xc = In(22, "=="); +var Ec = cn(22); +var Ac = /^[0-9a-fA-F]{40}$/; +var Kc = In(27, "="); +var qc = cn(27); +var Qc = /^[0-9a-fA-F]{64}$/; +var Yc = In(43, "="); +var Fc = cn(43); +var Bc = /^[0-9a-fA-F]{96}$/; +var mc = In(64, ""); +var Hc = cn(64); +var Tc = /^[0-9a-fA-F]{128}$/; +var Mc = In(86, "=="); +var Rc = cn(86); +var V = I("$ZodCheck", (r, i) => { + var o; + r._zod ?? (r._zod = {}), r._zod.def = i, (o = r._zod).onattach ?? (o.onattach = []); +}); +var Qe = { number: "number", bigint: "bigint", object: "date" }; +var yn = I("$ZodCheckLessThan", (r, i) => { + V.init(r, i); + let o = Qe[typeof i.value]; + r._zod.onattach.push((t) => { + let n = t._zod.bag, v = (i.inclusive ? n.maximum : n.exclusiveMaximum) ?? Number.POSITIVE_INFINITY; + if (i.value < v) if (i.inclusive) n.maximum = i.value; + else n.exclusiveMaximum = i.value; + }), r._zod.check = (t) => { + if (i.inclusive ? t.value <= i.value : t.value < i.value) return; + t.issues.push({ origin: o, code: "too_big", maximum: typeof i.value === "object" ? i.value.getTime() : i.value, input: t.value, inclusive: i.inclusive, inst: r, continue: !i.abort }); + }; +}); +var hn = I("$ZodCheckGreaterThan", (r, i) => { + V.init(r, i); + let o = Qe[typeof i.value]; + r._zod.onattach.push((t) => { + let n = t._zod.bag, v = (i.inclusive ? n.minimum : n.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY; + if (i.value > v) if (i.inclusive) n.minimum = i.value; + else n.exclusiveMinimum = i.value; + }), r._zod.check = (t) => { + if (i.inclusive ? t.value >= i.value : t.value > i.value) return; + t.issues.push({ origin: o, code: "too_small", minimum: typeof i.value === "object" ? i.value.getTime() : i.value, input: t.value, inclusive: i.inclusive, inst: r, continue: !i.abort }); + }; +}); +var go = I("$ZodCheckMultipleOf", (r, i) => { + V.init(r, i), r._zod.onattach.push((o) => { + var t; + (t = o._zod.bag).multipleOf ?? (t.multipleOf = i.value); + }), r._zod.check = (o) => { + if (typeof o.value !== typeof i.value) throw Error("Cannot mix number and bigint in multiple_of check."); + if (typeof o.value === "bigint" ? o.value % i.value === BigInt(0) : Sv(o.value, i.value) === 0) return; + o.issues.push({ origin: typeof o.value, code: "not_multiple_of", divisor: i.value, input: o.value, inst: r, continue: !i.abort }); + }; +}); +var eo = I("$ZodCheckNumberFormat", (r, i) => { + V.init(r, i), i.format = i.format || "float64"; + let o = i.format?.includes("int"), t = o ? "int" : "number", [n, v] = Wv[i.format]; + r._zod.onattach.push(($) => { + let u = $._zod.bag; + if (u.format = i.format, u.minimum = n, u.maximum = v, o) u.pattern = io; + }), r._zod.check = ($) => { + let u = $.value; + if (o) { + if (!Number.isInteger(u)) { + $.issues.push({ expected: t, format: i.format, code: "invalid_type", continue: false, input: u, inst: r }); + return; + } + if (!Number.isSafeInteger(u)) { + if (u > 0) $.issues.push({ input: u, code: "too_big", maximum: Number.MAX_SAFE_INTEGER, note: "Integers must be within the safe integer range.", inst: r, origin: t, inclusive: true, continue: !i.abort }); + else $.issues.push({ input: u, code: "too_small", minimum: Number.MIN_SAFE_INTEGER, note: "Integers must be within the safe integer range.", inst: r, origin: t, inclusive: true, continue: !i.abort }); + return; + } + } + if (u < n) $.issues.push({ origin: "number", input: u, code: "too_small", minimum: n, inclusive: true, inst: r, continue: !i.abort }); + if (u > v) $.issues.push({ origin: "number", input: u, code: "too_big", maximum: v, inclusive: true, inst: r, continue: !i.abort }); + }; +}); +var lo = I("$ZodCheckBigIntFormat", (r, i) => { + V.init(r, i); + let [o, t] = Vv[i.format]; + r._zod.onattach.push((n) => { + let v = n._zod.bag; + v.format = i.format, v.minimum = o, v.maximum = t; + }), r._zod.check = (n) => { + let v = n.value; + if (v < o) n.issues.push({ origin: "bigint", input: v, code: "too_small", minimum: o, inclusive: true, inst: r, continue: !i.abort }); + if (v > t) n.issues.push({ origin: "bigint", input: v, code: "too_big", maximum: t, inclusive: true, inst: r, continue: !i.abort }); + }; +}); +var Io = I("$ZodCheckMaxSize", (r, i) => { + var o; + V.init(r, i), (o = r._zod.def).when ?? (o.when = (t) => { + let n = t.value; + return !vr(n) && n.size !== void 0; + }), r._zod.onattach.push((t) => { + let n = t._zod.bag.maximum ?? Number.POSITIVE_INFINITY; + if (i.maximum < n) t._zod.bag.maximum = i.maximum; + }), r._zod.check = (t) => { + let n = t.value; + if (n.size <= i.maximum) return; + t.issues.push({ origin: tn(n), code: "too_big", maximum: i.maximum, inclusive: true, input: n, inst: r, continue: !i.abort }); + }; +}); +var co = I("$ZodCheckMinSize", (r, i) => { + var o; + V.init(r, i), (o = r._zod.def).when ?? (o.when = (t) => { + let n = t.value; + return !vr(n) && n.size !== void 0; + }), r._zod.onattach.push((t) => { + let n = t._zod.bag.minimum ?? Number.NEGATIVE_INFINITY; + if (i.minimum > n) t._zod.bag.minimum = i.minimum; + }), r._zod.check = (t) => { + let n = t.value; + if (n.size >= i.minimum) return; + t.issues.push({ origin: tn(n), code: "too_small", minimum: i.minimum, inclusive: true, input: n, inst: r, continue: !i.abort }); + }; +}); +var bo = I("$ZodCheckSizeEquals", (r, i) => { + var o; + V.init(r, i), (o = r._zod.def).when ?? (o.when = (t) => { + let n = t.value; + return !vr(n) && n.size !== void 0; + }), r._zod.onattach.push((t) => { + let n = t._zod.bag; + n.minimum = i.size, n.maximum = i.size, n.size = i.size; + }), r._zod.check = (t) => { + let n = t.value, v = n.size; + if (v === i.size) return; + let $ = v > i.size; + t.issues.push({ origin: tn(n), ...$ ? { code: "too_big", maximum: i.size } : { code: "too_small", minimum: i.size }, inclusive: true, exact: true, input: t.value, inst: r, continue: !i.abort }); + }; +}); +var _o = I("$ZodCheckMaxLength", (r, i) => { + var o; + V.init(r, i), (o = r._zod.def).when ?? (o.when = (t) => { + let n = t.value; + return !vr(n) && n.length !== void 0; + }), r._zod.onattach.push((t) => { + let n = t._zod.bag.maximum ?? Number.POSITIVE_INFINITY; + if (i.maximum < n) t._zod.bag.maximum = i.maximum; + }), r._zod.check = (t) => { + let n = t.value; + if (n.length <= i.maximum) return; + let $ = $n(n); + t.issues.push({ origin: $, code: "too_big", maximum: i.maximum, inclusive: true, input: n, inst: r, continue: !i.abort }); + }; +}); +var Uo = I("$ZodCheckMinLength", (r, i) => { + var o; + V.init(r, i), (o = r._zod.def).when ?? (o.when = (t) => { + let n = t.value; + return !vr(n) && n.length !== void 0; + }), r._zod.onattach.push((t) => { + let n = t._zod.bag.minimum ?? Number.NEGATIVE_INFINITY; + if (i.minimum > n) t._zod.bag.minimum = i.minimum; + }), r._zod.check = (t) => { + let n = t.value; + if (n.length >= i.minimum) return; + let $ = $n(n); + t.issues.push({ origin: $, code: "too_small", minimum: i.minimum, inclusive: true, input: n, inst: r, continue: !i.abort }); + }; +}); +var ko = I("$ZodCheckLengthEquals", (r, i) => { + var o; + V.init(r, i), (o = r._zod.def).when ?? (o.when = (t) => { + let n = t.value; + return !vr(n) && n.length !== void 0; + }), r._zod.onattach.push((t) => { + let n = t._zod.bag; + n.minimum = i.length, n.maximum = i.length, n.length = i.length; + }), r._zod.check = (t) => { + let n = t.value, v = n.length; + if (v === i.length) return; + let $ = $n(n), u = v > i.length; + t.issues.push({ origin: $, ...u ? { code: "too_big", maximum: i.length } : { code: "too_small", minimum: i.length }, inclusive: true, exact: true, input: t.value, inst: r, continue: !i.abort }); + }; +}); +var Vr = I("$ZodCheckStringFormat", (r, i) => { + var o, t; + if (V.init(r, i), r._zod.onattach.push((n) => { + let v = n._zod.bag; + if (v.format = i.format, i.pattern) v.patterns ?? (v.patterns = /* @__PURE__ */ new Set()), v.patterns.add(i.pattern); + }), i.pattern) (o = r._zod).check ?? (o.check = (n) => { + if (i.pattern.lastIndex = 0, i.pattern.test(n.value)) return; + n.issues.push({ origin: "string", code: "invalid_format", format: i.format, input: n.value, ...i.pattern ? { pattern: i.pattern.toString() } : {}, inst: r, continue: !i.abort }); + }); + else (t = r._zod).check ?? (t.check = () => { + }); +}); +var Do = I("$ZodCheckRegex", (r, i) => { + Vr.init(r, i), r._zod.check = (o) => { + if (i.pattern.lastIndex = 0, i.pattern.test(o.value)) return; + o.issues.push({ origin: "string", code: "invalid_format", format: "regex", input: o.value, pattern: i.pattern.toString(), inst: r, continue: !i.abort }); + }; +}); +var wo = I("$ZodCheckLowerCase", (r, i) => { + i.pattern ?? (i.pattern = $o), Vr.init(r, i); +}); +var No = I("$ZodCheckUpperCase", (r, i) => { + i.pattern ?? (i.pattern = uo), Vr.init(r, i); +}); +var Oo = I("$ZodCheckIncludes", (r, i) => { + V.init(r, i); + let o = R(i.includes), t = new RegExp(typeof i.position === "number" ? `^.{${i.position}}${o}` : o); + i.pattern = t, r._zod.onattach.push((n) => { + let v = n._zod.bag; + v.patterns ?? (v.patterns = /* @__PURE__ */ new Set()), v.patterns.add(t); + }), r._zod.check = (n) => { + if (n.value.includes(i.includes, i.position)) return; + n.issues.push({ origin: "string", code: "invalid_format", format: "includes", includes: i.includes, input: n.value, inst: r, continue: !i.abort }); + }; +}); +var zo = I("$ZodCheckStartsWith", (r, i) => { + V.init(r, i); + let o = new RegExp(`^${R(i.prefix)}.*`); + i.pattern ?? (i.pattern = o), r._zod.onattach.push((t) => { + let n = t._zod.bag; + n.patterns ?? (n.patterns = /* @__PURE__ */ new Set()), n.patterns.add(o); + }), r._zod.check = (t) => { + if (t.value.startsWith(i.prefix)) return; + t.issues.push({ origin: "string", code: "invalid_format", format: "starts_with", prefix: i.prefix, input: t.value, inst: r, continue: !i.abort }); + }; +}); +var So = I("$ZodCheckEndsWith", (r, i) => { + V.init(r, i); + let o = new RegExp(`.*${R(i.suffix)}$`); + i.pattern ?? (i.pattern = o), r._zod.onattach.push((t) => { + let n = t._zod.bag; + n.patterns ?? (n.patterns = /* @__PURE__ */ new Set()), n.patterns.add(o); + }), r._zod.check = (t) => { + if (t.value.endsWith(i.suffix)) return; + t.issues.push({ origin: "string", code: "invalid_format", format: "ends_with", suffix: i.suffix, input: t.value, inst: r, continue: !i.abort }); + }; +}); +function qe(r, i, o) { + if (r.issues.length) i.issues.push(...T(o, r.issues)); +} +var Po = I("$ZodCheckProperty", (r, i) => { + V.init(r, i), r._zod.check = (o) => { + let t = i.schema._zod.run({ value: o.value[i.property], issues: [] }, {}); + if (t instanceof Promise) return t.then((n) => qe(n, o, i.property)); + qe(t, o, i.property); + return; + }; +}); +var jo = I("$ZodCheckMimeType", (r, i) => { + V.init(r, i); + let o = new Set(i.mime); + r._zod.onattach.push((t) => { + t._zod.bag.mime = i.mime; + }), r._zod.check = (t) => { + if (o.has(t.value.type)) return; + t.issues.push({ code: "invalid_value", values: i.mime, input: t.value.type, inst: r, continue: !i.abort }); + }; +}); +var Jo = I("$ZodCheckOverwrite", (r, i) => { + V.init(r, i), r._zod.check = (o) => { + o.value = i.tx(o.value); + }; +}); +var an = class { + constructor(r = []) { + if (this.content = [], this.indent = 0, this) this.args = r; + } + indented(r) { + this.indent += 1, r(this), this.indent -= 1; + } + write(r) { + if (typeof r === "function") { + r(this, { execution: "sync" }), r(this, { execution: "async" }); + return; + } + let o = r.split(` +`).filter((v) => v), t = Math.min(...o.map((v) => v.length - v.trimStart().length)), n = o.map((v) => v.slice(t)).map((v) => " ".repeat(this.indent * 2) + v); + for (let v of n) this.content.push(v); + } + compile() { + let r = Function, i = this?.args, t = [...(this?.content ?? [""]).map((n) => ` ${n}`)]; + return new r(...i, t.join(` +`)); + } +}; +var Lo = { major: 4, minor: 3, patch: 5 }; +var S = I("$ZodType", (r, i) => { + var o; + r ?? (r = {}), r._zod.def = i, r._zod.bag = r._zod.bag || {}, r._zod.version = Lo; + let t = [...r._zod.def.checks ?? []]; + if (r._zod.traits.has("$ZodCheck")) t.unshift(r); + for (let n of t) for (let v of n._zod.onattach) v(r); + if (t.length === 0) (o = r._zod).deferred ?? (o.deferred = []), r._zod.deferred?.push(() => { + r._zod.run = r._zod.parse; + }); + else { + let n = ($, u, l) => { + let e = $r($), c; + for (let _ of u) { + if (_._zod.def.when) { + if (!_._zod.def.when($)) continue; + } else if (e) continue; + let N = $.issues.length, O = _._zod.check($); + if (O instanceof Promise && l?.async === false) throw new f(); + if (c || O instanceof Promise) c = (c ?? Promise.resolve()).then(async () => { + if (await O, $.issues.length === N) return; + if (!e) e = $r($, N); + }); + else { + if ($.issues.length === N) continue; + if (!e) e = $r($, N); + } + } + if (c) return c.then(() => { + return $; + }); + return $; + }, v = ($, u, l) => { + if ($r($)) return $.aborted = true, $; + let e = n(u, t, l); + if (e instanceof Promise) { + if (l.async === false) throw new f(); + return e.then((c) => r._zod.parse(c, l)); + } + return r._zod.parse(e, l); + }; + r._zod.run = ($, u) => { + if (u.skipChecks) return r._zod.parse($, u); + if (u.direction === "backward") { + let e = r._zod.parse({ value: $.value, issues: [] }, { ...u, skipChecks: true }); + if (e instanceof Promise) return e.then((c) => { + return v(c, $, u); + }); + return v(e, $, u); + } + let l = r._zod.parse($, u); + if (l instanceof Promise) { + if (u.async === false) throw new f(); + return l.then((e) => n(e, t, u)); + } + return n(l, t, u); + }; + } + j(r, "~standard", () => ({ validate: (n) => { + try { + let v = Av(r, n); + return v.success ? { value: v.data } : { issues: v.error?.issues }; + } catch (v) { + return Kv(r, n).then(($) => $.success ? { value: $.data } : { issues: $.error?.issues }); + } + }, vendor: "zod", version: 1 })); +}); +var Ur = I("$ZodString", (r, i) => { + S.init(r, i), r._zod.pattern = [...r?._zod.bag?.patterns ?? []].pop() ?? ro(r._zod.bag), r._zod.parse = (o, t) => { + if (i.coerce) try { + o.value = String(o.value); + } catch (n) { + } + if (typeof o.value === "string") return o; + return o.issues.push({ expected: "string", code: "invalid_type", input: o.value, inst: r }), o; + }; +}); +var G = I("$ZodStringFormat", (r, i) => { + Vr.init(r, i), Ur.init(r, i); +}); +var Wo = I("$ZodGUID", (r, i) => { + i.pattern ?? (i.pattern = Tv), G.init(r, i); +}); +var Vo = I("$ZodUUID", (r, i) => { + if (i.version) { + let t = { v1: 1, v2: 2, v3: 3, v4: 4, v5: 5, v6: 6, v7: 7, v8: 8 }[i.version]; + if (t === void 0) throw Error(`Invalid UUID version: "${i.version}"`); + i.pattern ?? (i.pattern = _r(t)); + } else i.pattern ?? (i.pattern = _r()); + G.init(r, i); +}); +var Xo = I("$ZodEmail", (r, i) => { + i.pattern ?? (i.pattern = Mv), G.init(r, i); +}); +var Eo = I("$ZodURL", (r, i) => { + G.init(r, i), r._zod.check = (o) => { + try { + let t = o.value.trim(), n = new URL(t); + if (i.hostname) { + if (i.hostname.lastIndex = 0, !i.hostname.test(n.hostname)) o.issues.push({ code: "invalid_format", format: "url", note: "Invalid hostname", pattern: i.hostname.source, input: o.value, inst: r, continue: !i.abort }); + } + if (i.protocol) { + if (i.protocol.lastIndex = 0, !i.protocol.test(n.protocol.endsWith(":") ? n.protocol.slice(0, -1) : n.protocol)) o.issues.push({ code: "invalid_format", format: "url", note: "Invalid protocol", pattern: i.protocol.source, input: o.value, inst: r, continue: !i.abort }); + } + if (i.normalize) o.value = n.href; + else o.value = t; + return; + } catch (t) { + o.issues.push({ code: "invalid_format", format: "url", input: o.value, inst: r, continue: !i.abort }); + } + }; +}); +var Ao = I("$ZodEmoji", (r, i) => { + i.pattern ?? (i.pattern = Rv()), G.init(r, i); +}); +var Ko = I("$ZodNanoID", (r, i) => { + i.pattern ?? (i.pattern = mv), G.init(r, i); +}); +var qo = I("$ZodCUID", (r, i) => { + i.pattern ?? (i.pattern = qv), G.init(r, i); +}); +var Qo = I("$ZodCUID2", (r, i) => { + i.pattern ?? (i.pattern = Qv), G.init(r, i); +}); +var Yo = I("$ZodULID", (r, i) => { + i.pattern ?? (i.pattern = Yv), G.init(r, i); +}); +var Fo = I("$ZodXID", (r, i) => { + i.pattern ?? (i.pattern = Fv), G.init(r, i); +}); +var Bo = I("$ZodKSUID", (r, i) => { + i.pattern ?? (i.pattern = Bv), G.init(r, i); +}); +var mo = I("$ZodISODateTime", (r, i) => { + i.pattern ?? (i.pattern = sv(i)), G.init(r, i); +}); +var Ho = I("$ZodISODate", (r, i) => { + i.pattern ?? (i.pattern = av), G.init(r, i); +}); +var To = I("$ZodISOTime", (r, i) => { + i.pattern ?? (i.pattern = pv(i)), G.init(r, i); +}); +var Mo = I("$ZodISODuration", (r, i) => { + i.pattern ?? (i.pattern = Hv), G.init(r, i); +}); +var Ro = I("$ZodIPv4", (r, i) => { + i.pattern ?? (i.pattern = xv), G.init(r, i), r._zod.bag.format = "ipv4"; +}); +var xo = I("$ZodIPv6", (r, i) => { + i.pattern ?? (i.pattern = Zv), G.init(r, i), r._zod.bag.format = "ipv6", r._zod.check = (o) => { + try { + new URL(`http://[${o.value}]`); + } catch { + o.issues.push({ code: "invalid_format", format: "ipv6", input: o.value, inst: r, continue: !i.abort }); + } + }; +}); +var Zo = I("$ZodMAC", (r, i) => { + i.pattern ?? (i.pattern = dv(i.delimiter)), G.init(r, i), r._zod.bag.format = "mac"; +}); +var Co = I("$ZodCIDRv4", (r, i) => { + i.pattern ?? (i.pattern = Cv), G.init(r, i); +}); +var fo = I("$ZodCIDRv6", (r, i) => { + i.pattern ?? (i.pattern = fv), G.init(r, i), r._zod.check = (o) => { + let t = o.value.split("/"); + try { + if (t.length !== 2) throw Error(); + let [n, v] = t; + if (!v) throw Error(); + let $ = Number(v); + if (`${$}` !== v) throw Error(); + if ($ < 0 || $ > 128) throw Error(); + new URL(`http://[${n}]`); + } catch { + o.issues.push({ code: "invalid_format", format: "cidrv6", input: o.value, inst: r, continue: !i.abort }); + } + }; +}); +function yo(r) { + if (r === "") return true; + if (r.length % 4 !== 0) return false; + try { + return atob(r), true; + } catch { + return false; + } +} +var ho = I("$ZodBase64", (r, i) => { + i.pattern ?? (i.pattern = yv), G.init(r, i), r._zod.bag.contentEncoding = "base64", r._zod.check = (o) => { + if (yo(o.value)) return; + o.issues.push({ code: "invalid_format", format: "base64", input: o.value, inst: r, continue: !i.abort }); + }; +}); +function fe(r) { + if (!fn.test(r)) return false; + let i = r.replace(/[-_]/g, (t) => t === "-" ? "+" : "/"), o = i.padEnd(Math.ceil(i.length / 4) * 4, "="); + return yo(o); +} +var ao = I("$ZodBase64URL", (r, i) => { + i.pattern ?? (i.pattern = fn), G.init(r, i), r._zod.bag.contentEncoding = "base64url", r._zod.check = (o) => { + if (fe(o.value)) return; + o.issues.push({ code: "invalid_format", format: "base64url", input: o.value, inst: r, continue: !i.abort }); + }; +}); +var po = I("$ZodE164", (r, i) => { + i.pattern ?? (i.pattern = hv), G.init(r, i); +}); +function ye(r, i = null) { + try { + let o = r.split("."); + if (o.length !== 3) return false; + let [t] = o; + if (!t) return false; + let n = JSON.parse(atob(t)); + if ("typ" in n && n?.typ !== "JWT") return false; + if (!n.alg) return false; + if (i && (!("alg" in n) || n.alg !== i)) return false; + return true; + } catch { + return false; + } +} +var so = I("$ZodJWT", (r, i) => { + G.init(r, i), r._zod.check = (o) => { + if (ye(o.value, i.alg)) return; + o.issues.push({ code: "invalid_format", format: "jwt", input: o.value, inst: r, continue: !i.abort }); + }; +}); +var rt = I("$ZodCustomStringFormat", (r, i) => { + G.init(r, i), r._zod.check = (o) => { + if (i.fn(o.value)) return; + o.issues.push({ code: "invalid_format", format: i.format, input: o.value, inst: r, continue: !i.abort }); + }; +}); +var vi = I("$ZodNumber", (r, i) => { + S.init(r, i), r._zod.pattern = r._zod.bag.pattern ?? ln, r._zod.parse = (o, t) => { + if (i.coerce) try { + o.value = Number(o.value); + } catch ($) { + } + let n = o.value; + if (typeof n === "number" && !Number.isNaN(n) && Number.isFinite(n)) return o; + let v = typeof n === "number" ? Number.isNaN(n) ? "NaN" : !Number.isFinite(n) ? "Infinity" : void 0 : void 0; + return o.issues.push({ expected: "number", code: "invalid_type", input: n, inst: r, ...v ? { received: v } : {} }), o; + }; +}); +var nt = I("$ZodNumberFormat", (r, i) => { + eo.init(r, i), vi.init(r, i); +}); +var bn = I("$ZodBoolean", (r, i) => { + S.init(r, i), r._zod.pattern = vo, r._zod.parse = (o, t) => { + if (i.coerce) try { + o.value = Boolean(o.value); + } catch (v) { + } + let n = o.value; + if (typeof n === "boolean") return o; + return o.issues.push({ expected: "boolean", code: "invalid_type", input: n, inst: r }), o; + }; +}); +var oi = I("$ZodBigInt", (r, i) => { + S.init(r, i), r._zod.pattern = no, r._zod.parse = (o, t) => { + if (i.coerce) try { + o.value = BigInt(o.value); + } catch (n) { + } + if (typeof o.value === "bigint") return o; + return o.issues.push({ expected: "bigint", code: "invalid_type", input: o.value, inst: r }), o; + }; +}); +var it = I("$ZodBigIntFormat", (r, i) => { + lo.init(r, i), oi.init(r, i); +}); +var vt = I("$ZodSymbol", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value; + if (typeof n === "symbol") return o; + return o.issues.push({ expected: "symbol", code: "invalid_type", input: n, inst: r }), o; + }; +}); +var ot = I("$ZodUndefined", (r, i) => { + S.init(r, i), r._zod.pattern = to, r._zod.values = /* @__PURE__ */ new Set([void 0]), r._zod.optin = "optional", r._zod.optout = "optional", r._zod.parse = (o, t) => { + let n = o.value; + if (typeof n > "u") return o; + return o.issues.push({ expected: "undefined", code: "invalid_type", input: n, inst: r }), o; + }; +}); +var tt = I("$ZodNull", (r, i) => { + S.init(r, i), r._zod.pattern = oo, r._zod.values = /* @__PURE__ */ new Set([null]), r._zod.parse = (o, t) => { + let n = o.value; + if (n === null) return o; + return o.issues.push({ expected: "null", code: "invalid_type", input: n, inst: r }), o; + }; +}); +var $t = I("$ZodAny", (r, i) => { + S.init(r, i), r._zod.parse = (o) => o; +}); +var ut = I("$ZodUnknown", (r, i) => { + S.init(r, i), r._zod.parse = (o) => o; +}); +var gt = I("$ZodNever", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + return o.issues.push({ expected: "never", code: "invalid_type", input: o.value, inst: r }), o; + }; +}); +var et = I("$ZodVoid", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value; + if (typeof n > "u") return o; + return o.issues.push({ expected: "void", code: "invalid_type", input: n, inst: r }), o; + }; +}); +var lt = I("$ZodDate", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + if (i.coerce) try { + o.value = new Date(o.value); + } catch (u) { + } + let n = o.value, v = n instanceof Date; + if (v && !Number.isNaN(n.getTime())) return o; + return o.issues.push({ expected: "date", code: "invalid_type", input: n, ...v ? { received: "Invalid Date" } : {}, inst: r }), o; + }; +}); +function Fe(r, i, o) { + if (r.issues.length) i.issues.push(...T(o, r.issues)); + i.value[o] = r.value; +} +var It = I("$ZodArray", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value; + if (!Array.isArray(n)) return o.issues.push({ expected: "array", code: "invalid_type", input: n, inst: r }), o; + o.value = Array(n.length); + let v = []; + for (let $ = 0; $ < n.length; $++) { + let u = n[$], l = i.element._zod.run({ value: u, issues: [] }, t); + if (l instanceof Promise) v.push(l.then((e) => Fe(e, o, $))); + else Fe(l, o, $); + } + if (v.length) return Promise.all(v).then(() => o); + return o; + }; +}); +function ii(r, i, o, t, n) { + if (r.issues.length) { + if (n && !(o in t)) return; + i.issues.push(...T(o, r.issues)); + } + if (r.value === void 0) { + if (o in t) i.value[o] = void 0; + } else i.value[o] = r.value; +} +function he(r) { + let i = Object.keys(r.shape); + for (let t of i) if (!r.shape?.[t]?._zod?.traits?.has("$ZodType")) throw Error(`Invalid element at key "${t}": expected a Zod schema`); + let o = Gv(r.shape); + return { ...r, keys: i, keySet: new Set(i), numKeys: i.length, optionalKeys: new Set(o) }; +} +function ae(r, i, o, t, n, v) { + let $ = [], u = n.keySet, l = n.catchall._zod, e = l.def.type, c = l.optout === "optional"; + for (let _ in i) { + if (u.has(_)) continue; + if (e === "never") { + $.push(_); + continue; + } + let N = l.run({ value: i[_], issues: [] }, t); + if (N instanceof Promise) r.push(N.then((O) => ii(O, o, _, i, c))); + else ii(N, o, _, i, c); + } + if ($.length) o.issues.push({ code: "unrecognized_keys", keys: $, input: i, inst: v }); + if (!r.length) return o; + return Promise.all(r).then(() => { + return o; + }); +} +var pe = I("$ZodObject", (r, i) => { + if (S.init(r, i), !Object.getOwnPropertyDescriptor(i, "shape")?.get) { + let u = i.shape; + Object.defineProperty(i, "shape", { get: () => { + let l = { ...u }; + return Object.defineProperty(i, "shape", { value: l }), l; + } }); + } + let t = Pr(() => he(i)); + j(r._zod, "propValues", () => { + let u = i.shape, l = {}; + for (let e in u) { + let c = u[e]._zod; + if (c.values) { + l[e] ?? (l[e] = /* @__PURE__ */ new Set()); + for (let _ of c.values) l[e].add(_); + } + } + return l; + }); + let n = br, v = i.catchall, $; + r._zod.parse = (u, l) => { + $ ?? ($ = t.value); + let e = u.value; + if (!n(e)) return u.issues.push({ expected: "object", code: "invalid_type", input: e, inst: r }), u; + u.value = {}; + let c = [], _ = $.shape; + for (let N of $.keys) { + let O = _[N], J = O._zod.optout === "optional", X = O._zod.run({ value: e[N], issues: [] }, l); + if (X instanceof Promise) c.push(X.then((zr) => ii(zr, u, N, e, J))); + else ii(X, u, N, e, J); + } + if (!v) return c.length ? Promise.all(c).then(() => u) : u; + return ae(c, e, u, l, t.value, r); + }; +}); +var ct = I("$ZodObjectJIT", (r, i) => { + pe.init(r, i); + let o = r._zod.parse, t = Pr(() => he(i)), n = (N) => { + let O = new an(["shape", "payload", "ctx"]), J = t.value, X = (C) => { + let F = Yn(C); + return `shape[${F}]._zod.run({ value: input[${F}], issues: [] }, ctx)`; + }; + O.write("const input = payload.value;"); + let zr = /* @__PURE__ */ Object.create(null), AI = 0; + for (let C of J.keys) zr[C] = `key_${AI++}`; + O.write("const newResult = {};"); + for (let C of J.keys) { + let F = zr[C], Z = Yn(C), qI = N[C]?._zod?.optout === "optional"; + if (O.write(`const ${F} = ${X(C)};`), qI) O.write(` + if (${F}.issues.length) { + if (${Z} in input) { + payload.issues = payload.issues.concat(${F}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${Z}, ...iss.path] : [${Z}] + }))); + } + } + + if (${F}.value === undefined) { + if (${Z} in input) { + newResult[${Z}] = undefined; + } + } else { + newResult[${Z}] = ${F}.value; + } + + `); + else O.write(` + if (${F}.issues.length) { + payload.issues = payload.issues.concat(${F}.issues.map(iss => ({ + ...iss, + path: iss.path ? [${Z}, ...iss.path] : [${Z}] + }))); + } + + if (${F}.value === undefined) { + if (${Z} in input) { + newResult[${Z}] = undefined; + } + } else { + newResult[${Z}] = ${F}.value; + } + + `); + } + O.write("payload.value = newResult;"), O.write("return payload;"); + let KI = O.compile(); + return (C, F) => KI(N, C, F); + }, v, $ = br, u = !sr.jitless, e = u && jv.value, c = i.catchall, _; + r._zod.parse = (N, O) => { + _ ?? (_ = t.value); + let J = N.value; + if (!$(J)) return N.issues.push({ expected: "object", code: "invalid_type", input: J, inst: r }), N; + if (u && e && O?.async === false && O.jitless !== true) { + if (!v) v = n(i.shape); + if (N = v(N, O), !c) return N; + return ae([], J, N, O, _, r); + } + return o(N, O); + }; +}); +function Be(r, i, o, t) { + for (let v of r) if (v.issues.length === 0) return i.value = v.value, i; + let n = r.filter((v) => !$r(v)); + if (n.length === 1) return i.value = n[0].value, n[0]; + return i.issues.push({ code: "invalid_union", input: i.value, inst: o, errors: r.map((v) => v.issues.map(($) => B($, t, E()))) }), i; +} +var _n = I("$ZodUnion", (r, i) => { + S.init(r, i), j(r._zod, "optin", () => i.options.some((n) => n._zod.optin === "optional") ? "optional" : void 0), j(r._zod, "optout", () => i.options.some((n) => n._zod.optout === "optional") ? "optional" : void 0), j(r._zod, "values", () => { + if (i.options.every((n) => n._zod.values)) return new Set(i.options.flatMap((n) => Array.from(n._zod.values))); + return; + }), j(r._zod, "pattern", () => { + if (i.options.every((n) => n._zod.pattern)) { + let n = i.options.map((v) => v._zod.pattern); + return new RegExp(`^(${n.map((v) => vn(v.source)).join("|")})$`); + } + return; + }); + let o = i.options.length === 1, t = i.options[0]._zod.run; + r._zod.parse = (n, v) => { + if (o) return t(n, v); + let $ = false, u = []; + for (let l of i.options) { + let e = l._zod.run({ value: n.value, issues: [] }, v); + if (e instanceof Promise) u.push(e), $ = true; + else { + if (e.issues.length === 0) return e; + u.push(e); + } + } + if (!$) return Be(u, n, r, v); + return Promise.all(u).then((l) => { + return Be(l, n, r, v); + }); + }; +}); +function me(r, i, o, t) { + let n = r.filter((v) => v.issues.length === 0); + if (n.length === 1) return i.value = n[0].value, i; + if (n.length === 0) i.issues.push({ code: "invalid_union", input: i.value, inst: o, errors: r.map((v) => v.issues.map(($) => B($, t, E()))) }); + else i.issues.push({ code: "invalid_union", input: i.value, inst: o, errors: [], inclusive: false }); + return i; +} +var bt = I("$ZodXor", (r, i) => { + _n.init(r, i), i.inclusive = false; + let o = i.options.length === 1, t = i.options[0]._zod.run; + r._zod.parse = (n, v) => { + if (o) return t(n, v); + let $ = false, u = []; + for (let l of i.options) { + let e = l._zod.run({ value: n.value, issues: [] }, v); + if (e instanceof Promise) u.push(e), $ = true; + else u.push(e); + } + if (!$) return me(u, n, r, v); + return Promise.all(u).then((l) => { + return me(l, n, r, v); + }); + }; +}); +var _t = I("$ZodDiscriminatedUnion", (r, i) => { + i.inclusive = false, _n.init(r, i); + let o = r._zod.parse; + j(r._zod, "propValues", () => { + let n = {}; + for (let v of i.options) { + let $ = v._zod.propValues; + if (!$ || Object.keys($).length === 0) throw Error(`Invalid discriminated union option at index "${i.options.indexOf(v)}"`); + for (let [u, l] of Object.entries($)) { + if (!n[u]) n[u] = /* @__PURE__ */ new Set(); + for (let e of l) n[u].add(e); + } + } + return n; + }); + let t = Pr(() => { + let n = i.options, v = /* @__PURE__ */ new Map(); + for (let $ of n) { + let u = $._zod.propValues?.[i.discriminator]; + if (!u || u.size === 0) throw Error(`Invalid discriminated union option at index "${i.options.indexOf($)}"`); + for (let l of u) { + if (v.has(l)) throw Error(`Duplicate discriminator value "${String(l)}"`); + v.set(l, $); + } + } + return v; + }); + r._zod.parse = (n, v) => { + let $ = n.value; + if (!br($)) return n.issues.push({ code: "invalid_type", expected: "object", input: $, inst: r }), n; + let u = t.value.get($?.[i.discriminator]); + if (u) return u._zod.run(n, v); + if (i.unionFallback) return o(n, v); + return n.issues.push({ code: "invalid_union", errors: [], note: "No matching discriminator", discriminator: i.discriminator, input: $, path: [i.discriminator], inst: r }), n; + }; +}); +var Ut = I("$ZodIntersection", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value, v = i.left._zod.run({ value: n, issues: [] }, t), $ = i.right._zod.run({ value: n, issues: [] }, t); + if (v instanceof Promise || $ instanceof Promise) return Promise.all([v, $]).then(([l, e]) => { + return He(o, l, e); + }); + return He(o, v, $); + }; +}); +function Go(r, i) { + if (r === i) return { valid: true, data: r }; + if (r instanceof Date && i instanceof Date && +r === +i) return { valid: true, data: r }; + if (tr(r) && tr(i)) { + let o = Object.keys(i), t = Object.keys(r).filter((v) => o.indexOf(v) !== -1), n = { ...r, ...i }; + for (let v of t) { + let $ = Go(r[v], i[v]); + if (!$.valid) return { valid: false, mergeErrorPath: [v, ...$.mergeErrorPath] }; + n[v] = $.data; + } + return { valid: true, data: n }; + } + if (Array.isArray(r) && Array.isArray(i)) { + if (r.length !== i.length) return { valid: false, mergeErrorPath: [] }; + let o = []; + for (let t = 0; t < r.length; t++) { + let n = r[t], v = i[t], $ = Go(n, v); + if (!$.valid) return { valid: false, mergeErrorPath: [t, ...$.mergeErrorPath] }; + o.push($.data); + } + return { valid: true, data: o }; + } + return { valid: false, mergeErrorPath: [] }; +} +function He(r, i, o) { + let t = /* @__PURE__ */ new Map(), n; + for (let u of i.issues) if (u.code === "unrecognized_keys") { + n ?? (n = u); + for (let l of u.keys) { + if (!t.has(l)) t.set(l, {}); + t.get(l).l = true; + } + } else r.issues.push(u); + for (let u of o.issues) if (u.code === "unrecognized_keys") for (let l of u.keys) { + if (!t.has(l)) t.set(l, {}); + t.get(l).r = true; + } + else r.issues.push(u); + let v = [...t].filter(([, u]) => u.l && u.r).map(([u]) => u); + if (v.length && n) r.issues.push({ ...n, keys: v }); + if ($r(r)) return r; + let $ = Go(i.value, o.value); + if (!$.valid) throw Error(`Unmergable intersection. Error path: ${JSON.stringify($.mergeErrorPath)}`); + return r.value = $.data, r; +} +var ti = I("$ZodTuple", (r, i) => { + S.init(r, i); + let o = i.items; + r._zod.parse = (t, n) => { + let v = t.value; + if (!Array.isArray(v)) return t.issues.push({ input: v, inst: r, expected: "tuple", code: "invalid_type" }), t; + t.value = []; + let $ = [], u = [...o].reverse().findIndex((c) => c._zod.optin !== "optional"), l = u === -1 ? 0 : o.length - u; + if (!i.rest) { + let c = v.length > o.length, _ = v.length < l - 1; + if (c || _) return t.issues.push({ ...c ? { code: "too_big", maximum: o.length, inclusive: true } : { code: "too_small", minimum: o.length }, input: v, inst: r, origin: "array" }), t; + } + let e = -1; + for (let c of o) { + if (e++, e >= v.length) { + if (e >= l) continue; + } + let _ = c._zod.run({ value: v[e], issues: [] }, n); + if (_ instanceof Promise) $.push(_.then((N) => pn(N, t, e))); + else pn(_, t, e); + } + if (i.rest) { + let c = v.slice(o.length); + for (let _ of c) { + e++; + let N = i.rest._zod.run({ value: _, issues: [] }, n); + if (N instanceof Promise) $.push(N.then((O) => pn(O, t, e))); + else pn(N, t, e); + } + } + if ($.length) return Promise.all($).then(() => t); + return t; + }; +}); +function pn(r, i, o) { + if (r.issues.length) i.issues.push(...T(o, r.issues)); + i.value[o] = r.value; +} +var kt = I("$ZodRecord", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value; + if (!tr(n)) return o.issues.push({ expected: "record", code: "invalid_type", input: n, inst: r }), o; + let v = [], $ = i.keyType._zod.values; + if ($) { + o.value = {}; + let u = /* @__PURE__ */ new Set(); + for (let e of $) if (typeof e === "string" || typeof e === "number" || typeof e === "symbol") { + u.add(typeof e === "number" ? e.toString() : e); + let c = i.valueType._zod.run({ value: n[e], issues: [] }, t); + if (c instanceof Promise) v.push(c.then((_) => { + if (_.issues.length) o.issues.push(...T(e, _.issues)); + o.value[e] = _.value; + })); + else { + if (c.issues.length) o.issues.push(...T(e, c.issues)); + o.value[e] = c.value; + } + } + let l; + for (let e in n) if (!u.has(e)) l = l ?? [], l.push(e); + if (l && l.length > 0) o.issues.push({ code: "unrecognized_keys", input: n, inst: r, keys: l }); + } else { + o.value = {}; + for (let u of Reflect.ownKeys(n)) { + if (u === "__proto__") continue; + let l = i.keyType._zod.run({ value: u, issues: [] }, t); + if (l instanceof Promise) throw Error("Async schemas not supported in object keys currently"); + if (typeof u === "string" && ln.test(u) && l.issues.length && l.issues.some((_) => _.code === "invalid_type" && _.expected === "number")) { + let _ = i.keyType._zod.run({ value: Number(u), issues: [] }, t); + if (_ instanceof Promise) throw Error("Async schemas not supported in object keys currently"); + if (_.issues.length === 0) l = _; + } + if (l.issues.length) { + if (i.mode === "loose") o.value[u] = n[u]; + else o.issues.push({ code: "invalid_key", origin: "record", issues: l.issues.map((_) => B(_, t, E())), input: u, path: [u], inst: r }); + continue; + } + let c = i.valueType._zod.run({ value: n[u], issues: [] }, t); + if (c instanceof Promise) v.push(c.then((_) => { + if (_.issues.length) o.issues.push(...T(u, _.issues)); + o.value[l.value] = _.value; + })); + else { + if (c.issues.length) o.issues.push(...T(u, c.issues)); + o.value[l.value] = c.value; + } + } + } + if (v.length) return Promise.all(v).then(() => o); + return o; + }; +}); +var Dt = I("$ZodMap", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value; + if (!(n instanceof Map)) return o.issues.push({ expected: "map", code: "invalid_type", input: n, inst: r }), o; + let v = []; + o.value = /* @__PURE__ */ new Map(); + for (let [$, u] of n) { + let l = i.keyType._zod.run({ value: $, issues: [] }, t), e = i.valueType._zod.run({ value: u, issues: [] }, t); + if (l instanceof Promise || e instanceof Promise) v.push(Promise.all([l, e]).then(([c, _]) => { + Te(c, _, o, $, n, r, t); + })); + else Te(l, e, o, $, n, r, t); + } + if (v.length) return Promise.all(v).then(() => o); + return o; + }; +}); +function Te(r, i, o, t, n, v, $) { + if (r.issues.length) if (on.has(typeof t)) o.issues.push(...T(t, r.issues)); + else o.issues.push({ code: "invalid_key", origin: "map", input: n, inst: v, issues: r.issues.map((u) => B(u, $, E())) }); + if (i.issues.length) if (on.has(typeof t)) o.issues.push(...T(t, i.issues)); + else o.issues.push({ origin: "map", code: "invalid_element", input: n, inst: v, key: t, issues: i.issues.map((u) => B(u, $, E())) }); + o.value.set(r.value, i.value); +} +var wt = I("$ZodSet", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value; + if (!(n instanceof Set)) return o.issues.push({ input: n, inst: r, expected: "set", code: "invalid_type" }), o; + let v = []; + o.value = /* @__PURE__ */ new Set(); + for (let $ of n) { + let u = i.valueType._zod.run({ value: $, issues: [] }, t); + if (u instanceof Promise) v.push(u.then((l) => Me(l, o))); + else Me(u, o); + } + if (v.length) return Promise.all(v).then(() => o); + return o; + }; +}); +function Me(r, i) { + if (r.issues.length) i.issues.push(...r.issues); + i.value.add(r.value); +} +var Nt = I("$ZodEnum", (r, i) => { + S.init(r, i); + let o = nn(i.entries), t = new Set(o); + r._zod.values = t, r._zod.pattern = new RegExp(`^(${o.filter((n) => on.has(typeof n)).map((n) => typeof n === "string" ? R(n) : n.toString()).join("|")})$`), r._zod.parse = (n, v) => { + let $ = n.value; + if (t.has($)) return n; + return n.issues.push({ code: "invalid_value", values: o, input: $, inst: r }), n; + }; +}); +var Ot = I("$ZodLiteral", (r, i) => { + if (S.init(r, i), i.values.length === 0) throw Error("Cannot create literal schema with no valid values"); + let o = new Set(i.values); + r._zod.values = o, r._zod.pattern = new RegExp(`^(${i.values.map((t) => typeof t === "string" ? R(t) : t ? R(t.toString()) : String(t)).join("|")})$`), r._zod.parse = (t, n) => { + let v = t.value; + if (o.has(v)) return t; + return t.issues.push({ code: "invalid_value", values: i.values, input: v, inst: r }), t; + }; +}); +var zt = I("$ZodFile", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + let n = o.value; + if (n instanceof File) return o; + return o.issues.push({ expected: "file", code: "invalid_type", input: n, inst: r }), o; + }; +}); +var St = I("$ZodTransform", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + if (t.direction === "backward") throw new cr(r.constructor.name); + let n = i.transform(o.value, o); + if (t.async) return (n instanceof Promise ? n : Promise.resolve(n)).then(($) => { + return o.value = $, o; + }); + if (n instanceof Promise) throw new f(); + return o.value = n, o; + }; +}); +function Re(r, i) { + if (r.issues.length && i === void 0) return { issues: [], value: void 0 }; + return r; +} +var $i = I("$ZodOptional", (r, i) => { + S.init(r, i), r._zod.optin = "optional", r._zod.optout = "optional", j(r._zod, "values", () => { + return i.innerType._zod.values ? /* @__PURE__ */ new Set([...i.innerType._zod.values, void 0]) : void 0; + }), j(r._zod, "pattern", () => { + let o = i.innerType._zod.pattern; + return o ? new RegExp(`^(${vn(o.source)})?$`) : void 0; + }), r._zod.parse = (o, t) => { + if (i.innerType._zod.optin === "optional") { + let n = i.innerType._zod.run(o, t); + if (n instanceof Promise) return n.then((v) => Re(v, o.value)); + return Re(n, o.value); + } + if (o.value === void 0) return o; + return i.innerType._zod.run(o, t); + }; +}); +var Pt = I("$ZodExactOptional", (r, i) => { + $i.init(r, i), j(r._zod, "values", () => i.innerType._zod.values), j(r._zod, "pattern", () => i.innerType._zod.pattern), r._zod.parse = (o, t) => { + return i.innerType._zod.run(o, t); + }; +}); +var jt = I("$ZodNullable", (r, i) => { + S.init(r, i), j(r._zod, "optin", () => i.innerType._zod.optin), j(r._zod, "optout", () => i.innerType._zod.optout), j(r._zod, "pattern", () => { + let o = i.innerType._zod.pattern; + return o ? new RegExp(`^(${vn(o.source)}|null)$`) : void 0; + }), j(r._zod, "values", () => { + return i.innerType._zod.values ? /* @__PURE__ */ new Set([...i.innerType._zod.values, null]) : void 0; + }), r._zod.parse = (o, t) => { + if (o.value === null) return o; + return i.innerType._zod.run(o, t); + }; +}); +var Jt = I("$ZodDefault", (r, i) => { + S.init(r, i), r._zod.optin = "optional", j(r._zod, "values", () => i.innerType._zod.values), r._zod.parse = (o, t) => { + if (t.direction === "backward") return i.innerType._zod.run(o, t); + if (o.value === void 0) return o.value = i.defaultValue, o; + let n = i.innerType._zod.run(o, t); + if (n instanceof Promise) return n.then((v) => xe(v, i)); + return xe(n, i); + }; +}); +function xe(r, i) { + if (r.value === void 0) r.value = i.defaultValue; + return r; +} +var Lt = I("$ZodPrefault", (r, i) => { + S.init(r, i), r._zod.optin = "optional", j(r._zod, "values", () => i.innerType._zod.values), r._zod.parse = (o, t) => { + if (t.direction === "backward") return i.innerType._zod.run(o, t); + if (o.value === void 0) o.value = i.defaultValue; + return i.innerType._zod.run(o, t); + }; +}); +var Gt = I("$ZodNonOptional", (r, i) => { + S.init(r, i), j(r._zod, "values", () => { + let o = i.innerType._zod.values; + return o ? new Set([...o].filter((t) => t !== void 0)) : void 0; + }), r._zod.parse = (o, t) => { + let n = i.innerType._zod.run(o, t); + if (n instanceof Promise) return n.then((v) => Ze(v, r)); + return Ze(n, r); + }; +}); +function Ze(r, i) { + if (!r.issues.length && r.value === void 0) r.issues.push({ code: "invalid_type", expected: "nonoptional", input: r.value, inst: i }); + return r; +} +var Wt = I("$ZodSuccess", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + if (t.direction === "backward") throw new cr("ZodSuccess"); + let n = i.innerType._zod.run(o, t); + if (n instanceof Promise) return n.then((v) => { + return o.value = v.issues.length === 0, o; + }); + return o.value = n.issues.length === 0, o; + }; +}); +var Vt = I("$ZodCatch", (r, i) => { + S.init(r, i), j(r._zod, "optin", () => i.innerType._zod.optin), j(r._zod, "optout", () => i.innerType._zod.optout), j(r._zod, "values", () => i.innerType._zod.values), r._zod.parse = (o, t) => { + if (t.direction === "backward") return i.innerType._zod.run(o, t); + let n = i.innerType._zod.run(o, t); + if (n instanceof Promise) return n.then((v) => { + if (o.value = v.value, v.issues.length) o.value = i.catchValue({ ...o, error: { issues: v.issues.map(($) => B($, t, E())) }, input: o.value }), o.issues = []; + return o; + }); + if (o.value = n.value, n.issues.length) o.value = i.catchValue({ ...o, error: { issues: n.issues.map((v) => B(v, t, E())) }, input: o.value }), o.issues = []; + return o; + }; +}); +var Xt = I("$ZodNaN", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + if (typeof o.value !== "number" || !Number.isNaN(o.value)) return o.issues.push({ input: o.value, inst: r, expected: "nan", code: "invalid_type" }), o; + return o; + }; +}); +var Et = I("$ZodPipe", (r, i) => { + S.init(r, i), j(r._zod, "values", () => i.in._zod.values), j(r._zod, "optin", () => i.in._zod.optin), j(r._zod, "optout", () => i.out._zod.optout), j(r._zod, "propValues", () => i.in._zod.propValues), r._zod.parse = (o, t) => { + if (t.direction === "backward") { + let v = i.out._zod.run(o, t); + if (v instanceof Promise) return v.then(($) => sn($, i.in, t)); + return sn(v, i.in, t); + } + let n = i.in._zod.run(o, t); + if (n instanceof Promise) return n.then((v) => sn(v, i.out, t)); + return sn(n, i.out, t); + }; +}); +function sn(r, i, o) { + if (r.issues.length) return r.aborted = true, r; + return i._zod.run({ value: r.value, issues: r.issues }, o); +} +var Un = I("$ZodCodec", (r, i) => { + S.init(r, i), j(r._zod, "values", () => i.in._zod.values), j(r._zod, "optin", () => i.in._zod.optin), j(r._zod, "optout", () => i.out._zod.optout), j(r._zod, "propValues", () => i.in._zod.propValues), r._zod.parse = (o, t) => { + if ((t.direction || "forward") === "forward") { + let v = i.in._zod.run(o, t); + if (v instanceof Promise) return v.then(($) => ri($, i, t)); + return ri(v, i, t); + } else { + let v = i.out._zod.run(o, t); + if (v instanceof Promise) return v.then(($) => ri($, i, t)); + return ri(v, i, t); + } + }; +}); +function ri(r, i, o) { + if (r.issues.length) return r.aborted = true, r; + if ((o.direction || "forward") === "forward") { + let n = i.transform(r.value, r); + if (n instanceof Promise) return n.then((v) => ni(r, v, i.out, o)); + return ni(r, n, i.out, o); + } else { + let n = i.reverseTransform(r.value, r); + if (n instanceof Promise) return n.then((v) => ni(r, v, i.in, o)); + return ni(r, n, i.in, o); + } +} +function ni(r, i, o, t) { + if (r.issues.length) return r.aborted = true, r; + return o._zod.run({ value: i, issues: r.issues }, t); +} +var At = I("$ZodReadonly", (r, i) => { + S.init(r, i), j(r._zod, "propValues", () => i.innerType._zod.propValues), j(r._zod, "values", () => i.innerType._zod.values), j(r._zod, "optin", () => i.innerType?._zod?.optin), j(r._zod, "optout", () => i.innerType?._zod?.optout), r._zod.parse = (o, t) => { + if (t.direction === "backward") return i.innerType._zod.run(o, t); + let n = i.innerType._zod.run(o, t); + if (n instanceof Promise) return n.then(de); + return de(n); + }; +}); +function de(r) { + return r.value = Object.freeze(r.value), r; +} +var Kt = I("$ZodTemplateLiteral", (r, i) => { + S.init(r, i); + let o = []; + for (let t of i.parts) if (typeof t === "object" && t !== null) { + if (!t._zod.pattern) throw Error(`Invalid template literal part, no pattern found: ${[...t._zod.traits].shift()}`); + let n = t._zod.pattern instanceof RegExp ? t._zod.pattern.source : t._zod.pattern; + if (!n) throw Error(`Invalid template literal part: ${t._zod.traits}`); + let v = n.startsWith("^") ? 1 : 0, $ = n.endsWith("$") ? n.length - 1 : n.length; + o.push(n.slice(v, $)); + } else if (t === null || Lv.has(typeof t)) o.push(R(`${t}`)); + else throw Error(`Invalid template literal part: ${t}`); + r._zod.pattern = new RegExp(`^${o.join("")}$`), r._zod.parse = (t, n) => { + if (typeof t.value !== "string") return t.issues.push({ input: t.value, inst: r, expected: "string", code: "invalid_type" }), t; + if (r._zod.pattern.lastIndex = 0, !r._zod.pattern.test(t.value)) return t.issues.push({ input: t.value, inst: r, code: "invalid_format", format: i.format ?? "template_literal", pattern: r._zod.pattern.source }), t; + return t; + }; +}); +var qt = I("$ZodFunction", (r, i) => { + return S.init(r, i), r._def = i, r._zod.def = i, r.implement = (o) => { + if (typeof o !== "function") throw Error("implement() must be called with a function"); + return function(...t) { + let n = r._def.input ? Bn(r._def.input, t) : t, v = Reflect.apply(o, this, n); + if (r._def.output) return Bn(r._def.output, v); + return v; + }; + }, r.implementAsync = (o) => { + if (typeof o !== "function") throw Error("implementAsync() must be called with a function"); + return async function(...t) { + let n = r._def.input ? await mn(r._def.input, t) : t, v = await Reflect.apply(o, this, n); + if (r._def.output) return await mn(r._def.output, v); + return v; + }; + }, r._zod.parse = (o, t) => { + if (typeof o.value !== "function") return o.issues.push({ code: "invalid_type", expected: "function", input: o.value, inst: r }), o; + if (r._def.output && r._def.output._zod.def.type === "promise") o.value = r.implementAsync(o.value); + else o.value = r.implement(o.value); + return o; + }, r.input = (...o) => { + let t = r.constructor; + if (Array.isArray(o[0])) return new t({ type: "function", input: new ti({ type: "tuple", items: o[0], rest: o[1] }), output: r._def.output }); + return new t({ type: "function", input: o[0], output: r._def.output }); + }, r.output = (o) => { + return new r.constructor({ type: "function", input: r._def.input, output: o }); + }, r; +}); +var Qt = I("$ZodPromise", (r, i) => { + S.init(r, i), r._zod.parse = (o, t) => { + return Promise.resolve(o.value).then((n) => i.innerType._zod.run({ value: n, issues: [] }, t)); + }; +}); +var Yt = I("$ZodLazy", (r, i) => { + S.init(r, i), j(r._zod, "innerType", () => i.getter()), j(r._zod, "pattern", () => r._zod.innerType?._zod?.pattern), j(r._zod, "propValues", () => r._zod.innerType?._zod?.propValues), j(r._zod, "optin", () => r._zod.innerType?._zod?.optin ?? void 0), j(r._zod, "optout", () => r._zod.innerType?._zod?.optout ?? void 0), r._zod.parse = (o, t) => { + return r._zod.innerType._zod.run(o, t); + }; +}); +var Ft = I("$ZodCustom", (r, i) => { + V.init(r, i), S.init(r, i), r._zod.parse = (o, t) => { + return o; + }, r._zod.check = (o) => { + let t = o.value, n = i.fn(t); + if (n instanceof Promise) return n.then((v) => Ce(v, o, t, r)); + Ce(n, o, t, r); + return; + }; +}); +function Ce(r, i, o, t) { + if (!r) { + let n = { code: "custom", input: o, inst: t, path: [...t._zod.def.path ?? []], continue: !t._zod.def.abort }; + if (t._zod.def.params) n.params = t._zod.def.params; + i.issues.push(jr(n)); + } +} +var On = {}; +s(On, { zhTW: () => W$, zhCN: () => G$, yo: () => V$, vi: () => L$, uz: () => J$, ur: () => j$, uk: () => Nn, ua: () => P$, tr: () => S$, th: () => z$, ta: () => O$, sv: () => N$, sl: () => w$, ru: () => D$, pt: () => k$, ps: () => _$, pl: () => U$, ota: () => b$, no: () => c$, nl: () => I$, ms: () => l$, mk: () => e$, lt: () => g$, ko: () => u$, km: () => Dn, kh: () => $$, ka: () => t$, ja: () => o$, it: () => v$, is: () => i$, id: () => n$, hy: () => r$, hu: () => st, he: () => pt, frCA: () => at, fr: () => ht, fi: () => yt, fa: () => ft, es: () => Ct, eo: () => dt, en: () => kn, de: () => Zt, da: () => xt, cs: () => Rt, ca: () => Mt, bg: () => Tt, be: () => Ht, az: () => mt, ar: () => Bt }); +var Zc = () => { + let r = { string: { unit: "حرف", verb: "أن يحوي" }, file: { unit: "بايت", verb: "أن يحوي" }, array: { unit: "عنصر", verb: "أن يحوي" }, set: { unit: "عنصر", verb: "أن يحوي" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "مدخل", email: "بريد إلكتروني", url: "رابط", emoji: "إيموجي", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "تاريخ ووقت بمعيار ISO", date: "تاريخ بمعيار ISO", time: "وقت بمعيار ISO", duration: "مدة بمعيار ISO", ipv4: "عنوان IPv4", ipv6: "عنوان IPv6", cidrv4: "مدى عناوين بصيغة IPv4", cidrv6: "مدى عناوين بصيغة IPv6", base64: "نَص بترميز base64-encoded", base64url: "نَص بترميز base64url-encoded", json_string: "نَص على هيئة JSON", e164: "رقم هاتف بمعيار E.164", jwt: "JWT", template_literal: "مدخل" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `مدخلات غير مقبولة: يفترض إدخال instanceof ${n.expected}، ولكن تم إدخال ${u}`; + return `مدخلات غير مقبولة: يفترض إدخال ${v}، ولكن تم إدخال ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `مدخلات غير مقبولة: يفترض إدخال ${U(n.values[0])}`; + return `اختيار غير مقبول: يتوقع انتقاء أحد هذه الخيارات: ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return ` أكبر من اللازم: يفترض أن تكون ${n.origin ?? "القيمة"} ${v} ${n.maximum.toString()} ${$.unit ?? "عنصر"}`; + return `أكبر من اللازم: يفترض أن تكون ${n.origin ?? "القيمة"} ${v} ${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `أصغر من اللازم: يفترض لـ ${n.origin} أن يكون ${v} ${n.minimum.toString()} ${$.unit}`; + return `أصغر من اللازم: يفترض لـ ${n.origin} أن يكون ${v} ${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `نَص غير مقبول: يجب أن يبدأ بـ "${n.prefix}"`; + if (v.format === "ends_with") return `نَص غير مقبول: يجب أن ينتهي بـ "${v.suffix}"`; + if (v.format === "includes") return `نَص غير مقبول: يجب أن يتضمَّن "${v.includes}"`; + if (v.format === "regex") return `نَص غير مقبول: يجب أن يطابق النمط ${v.pattern}`; + return `${o[v.format] ?? n.format} غير مقبول`; + } + case "not_multiple_of": + return `رقم غير مقبول: يجب أن يكون من مضاعفات ${n.divisor}`; + case "unrecognized_keys": + return `معرف${n.keys.length > 1 ? "ات" : ""} غريب${n.keys.length > 1 ? "ة" : ""}: ${b(n.keys, "، ")}`; + case "invalid_key": + return `معرف غير مقبول في ${n.origin}`; + case "invalid_union": + return "مدخل غير مقبول"; + case "invalid_element": + return `مدخل غير مقبول في ${n.origin}`; + default: + return "مدخل غير مقبول"; + } + }; +}; +function Bt() { + return { localeError: Zc() }; +} +var dc = () => { + let r = { string: { unit: "simvol", verb: "olmalıdır" }, file: { unit: "bayt", verb: "olmalıdır" }, array: { unit: "element", verb: "olmalıdır" }, set: { unit: "element", verb: "olmalıdır" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "input", email: "email address", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO datetime", date: "ISO date", time: "ISO time", duration: "ISO duration", ipv4: "IPv4 address", ipv6: "IPv6 address", cidrv4: "IPv4 range", cidrv6: "IPv6 range", base64: "base64-encoded string", base64url: "base64url-encoded string", json_string: "JSON string", e164: "E.164 number", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Yanlış dəyər: gözlənilən instanceof ${n.expected}, daxil olan ${u}`; + return `Yanlış dəyər: gözlənilən ${v}, daxil olan ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Yanlış dəyər: gözlənilən ${U(n.values[0])}`; + return `Yanlış seçim: aşağıdakılardan biri olmalıdır: ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Çox böyük: gözlənilən ${n.origin ?? "dəyər"} ${v}${n.maximum.toString()} ${$.unit ?? "element"}`; + return `Çox böyük: gözlənilən ${n.origin ?? "dəyər"} ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Çox kiçik: gözlənilən ${n.origin} ${v}${n.minimum.toString()} ${$.unit}`; + return `Çox kiçik: gözlənilən ${n.origin} ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Yanlış mətn: "${v.prefix}" ilə başlamalıdır`; + if (v.format === "ends_with") return `Yanlış mətn: "${v.suffix}" ilə bitməlidir`; + if (v.format === "includes") return `Yanlış mətn: "${v.includes}" daxil olmalıdır`; + if (v.format === "regex") return `Yanlış mətn: ${v.pattern} şablonuna uyğun olmalıdır`; + return `Yanlış ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Yanlış ədəd: ${n.divisor} ilə bölünə bilən olmalıdır`; + case "unrecognized_keys": + return `Tanınmayan açar${n.keys.length > 1 ? "lar" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `${n.origin} daxilində yanlış açar`; + case "invalid_union": + return "Yanlış dəyər"; + case "invalid_element": + return `${n.origin} daxilində yanlış dəyər`; + default: + return "Yanlış dəyər"; + } + }; +}; +function mt() { + return { localeError: dc() }; +} +function se(r, i, o, t) { + let n = Math.abs(r), v = n % 10, $ = n % 100; + if ($ >= 11 && $ <= 19) return t; + if (v === 1) return i; + if (v >= 2 && v <= 4) return o; + return t; +} +var Cc = () => { + let r = { string: { unit: { one: "сімвал", few: "сімвалы", many: "сімвалаў" }, verb: "мець" }, array: { unit: { one: "элемент", few: "элементы", many: "элементаў" }, verb: "мець" }, set: { unit: { one: "элемент", few: "элементы", many: "элементаў" }, verb: "мець" }, file: { unit: { one: "байт", few: "байты", many: "байтаў" }, verb: "мець" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "увод", email: "email адрас", url: "URL", emoji: "эмодзі", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO дата і час", date: "ISO дата", time: "ISO час", duration: "ISO працягласць", ipv4: "IPv4 адрас", ipv6: "IPv6 адрас", cidrv4: "IPv4 дыяпазон", cidrv6: "IPv6 дыяпазон", base64: "радок у фармаце base64", base64url: "радок у фармаце base64url", json_string: "JSON радок", e164: "нумар E.164", jwt: "JWT", template_literal: "увод" }, t = { nan: "NaN", number: "лік", array: "масіў" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Няправільны ўвод: чакаўся instanceof ${n.expected}, атрымана ${u}`; + return `Няправільны ўвод: чакаўся ${v}, атрымана ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Няправільны ўвод: чакалася ${U(n.values[0])}`; + return `Няправільны варыянт: чакаўся адзін з ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) { + let u = Number(n.maximum), l = se(u, $.unit.one, $.unit.few, $.unit.many); + return `Занадта вялікі: чакалася, што ${n.origin ?? "значэнне"} павінна ${$.verb} ${v}${n.maximum.toString()} ${l}`; + } + return `Занадта вялікі: чакалася, што ${n.origin ?? "значэнне"} павінна быць ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) { + let u = Number(n.minimum), l = se(u, $.unit.one, $.unit.few, $.unit.many); + return `Занадта малы: чакалася, што ${n.origin} павінна ${$.verb} ${v}${n.minimum.toString()} ${l}`; + } + return `Занадта малы: чакалася, што ${n.origin} павінна быць ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Няправільны радок: павінен пачынацца з "${v.prefix}"`; + if (v.format === "ends_with") return `Няправільны радок: павінен заканчвацца на "${v.suffix}"`; + if (v.format === "includes") return `Няправільны радок: павінен змяшчаць "${v.includes}"`; + if (v.format === "regex") return `Няправільны радок: павінен адпавядаць шаблону ${v.pattern}`; + return `Няправільны ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Няправільны лік: павінен быць кратным ${n.divisor}`; + case "unrecognized_keys": + return `Нераспазнаны ${n.keys.length > 1 ? "ключы" : "ключ"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Няправільны ключ у ${n.origin}`; + case "invalid_union": + return "Няправільны ўвод"; + case "invalid_element": + return `Няправільнае значэнне ў ${n.origin}`; + default: + return "Няправільны ўвод"; + } + }; +}; +function Ht() { + return { localeError: Cc() }; +} +var fc = () => { + let r = { string: { unit: "символа", verb: "да съдържа" }, file: { unit: "байта", verb: "да съдържа" }, array: { unit: "елемента", verb: "да съдържа" }, set: { unit: "елемента", verb: "да съдържа" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "вход", email: "имейл адрес", url: "URL", emoji: "емоджи", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO време", date: "ISO дата", time: "ISO време", duration: "ISO продължителност", ipv4: "IPv4 адрес", ipv6: "IPv6 адрес", cidrv4: "IPv4 диапазон", cidrv6: "IPv6 диапазон", base64: "base64-кодиран низ", base64url: "base64url-кодиран низ", json_string: "JSON низ", e164: "E.164 номер", jwt: "JWT", template_literal: "вход" }, t = { nan: "NaN", number: "число", array: "масив" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Невалиден вход: очакван instanceof ${n.expected}, получен ${u}`; + return `Невалиден вход: очакван ${v}, получен ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Невалиден вход: очакван ${U(n.values[0])}`; + return `Невалидна опция: очаквано едно от ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Твърде голямо: очаква се ${n.origin ?? "стойност"} да съдържа ${v}${n.maximum.toString()} ${$.unit ?? "елемента"}`; + return `Твърде голямо: очаква се ${n.origin ?? "стойност"} да бъде ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Твърде малко: очаква се ${n.origin} да съдържа ${v}${n.minimum.toString()} ${$.unit}`; + return `Твърде малко: очаква се ${n.origin} да бъде ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Невалиден низ: трябва да започва с "${v.prefix}"`; + if (v.format === "ends_with") return `Невалиден низ: трябва да завършва с "${v.suffix}"`; + if (v.format === "includes") return `Невалиден низ: трябва да включва "${v.includes}"`; + if (v.format === "regex") return `Невалиден низ: трябва да съвпада с ${v.pattern}`; + let $ = "Невалиден"; + if (v.format === "emoji") $ = "Невалидно"; + if (v.format === "datetime") $ = "Невалидно"; + if (v.format === "date") $ = "Невалидна"; + if (v.format === "time") $ = "Невалидно"; + if (v.format === "duration") $ = "Невалидна"; + return `${$} ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Невалидно число: трябва да бъде кратно на ${n.divisor}`; + case "unrecognized_keys": + return `Неразпознат${n.keys.length > 1 ? "и" : ""} ключ${n.keys.length > 1 ? "ове" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Невалиден ключ в ${n.origin}`; + case "invalid_union": + return "Невалиден вход"; + case "invalid_element": + return `Невалидна стойност в ${n.origin}`; + default: + return "Невалиден вход"; + } + }; +}; +function Tt() { + return { localeError: fc() }; +} +var yc = () => { + let r = { string: { unit: "caràcters", verb: "contenir" }, file: { unit: "bytes", verb: "contenir" }, array: { unit: "elements", verb: "contenir" }, set: { unit: "elements", verb: "contenir" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "entrada", email: "adreça electrònica", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "data i hora ISO", date: "data ISO", time: "hora ISO", duration: "durada ISO", ipv4: "adreça IPv4", ipv6: "adreça IPv6", cidrv4: "rang IPv4", cidrv6: "rang IPv6", base64: "cadena codificada en base64", base64url: "cadena codificada en base64url", json_string: "cadena JSON", e164: "número E.164", jwt: "JWT", template_literal: "entrada" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Tipus invàlid: s'esperava instanceof ${n.expected}, s'ha rebut ${u}`; + return `Tipus invàlid: s'esperava ${v}, s'ha rebut ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Valor invàlid: s'esperava ${U(n.values[0])}`; + return `Opció invàlida: s'esperava una de ${b(n.values, " o ")}`; + case "too_big": { + let v = n.inclusive ? "com a màxim" : "menys de", $ = i(n.origin); + if ($) return `Massa gran: s'esperava que ${n.origin ?? "el valor"} contingués ${v} ${n.maximum.toString()} ${$.unit ?? "elements"}`; + return `Massa gran: s'esperava que ${n.origin ?? "el valor"} fos ${v} ${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? "com a mínim" : "més de", $ = i(n.origin); + if ($) return `Massa petit: s'esperava que ${n.origin} contingués ${v} ${n.minimum.toString()} ${$.unit}`; + return `Massa petit: s'esperava que ${n.origin} fos ${v} ${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Format invàlid: ha de començar amb "${v.prefix}"`; + if (v.format === "ends_with") return `Format invàlid: ha d'acabar amb "${v.suffix}"`; + if (v.format === "includes") return `Format invàlid: ha d'incloure "${v.includes}"`; + if (v.format === "regex") return `Format invàlid: ha de coincidir amb el patró ${v.pattern}`; + return `Format invàlid per a ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Número invàlid: ha de ser múltiple de ${n.divisor}`; + case "unrecognized_keys": + return `Clau${n.keys.length > 1 ? "s" : ""} no reconeguda${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Clau invàlida a ${n.origin}`; + case "invalid_union": + return "Entrada invàlida"; + case "invalid_element": + return `Element invàlid a ${n.origin}`; + default: + return "Entrada invàlida"; + } + }; +}; +function Mt() { + return { localeError: yc() }; +} +var hc = () => { + let r = { string: { unit: "znaků", verb: "mít" }, file: { unit: "bajtů", verb: "mít" }, array: { unit: "prvků", verb: "mít" }, set: { unit: "prvků", verb: "mít" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "regulární výraz", email: "e-mailová adresa", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "datum a čas ve formátu ISO", date: "datum ve formátu ISO", time: "čas ve formátu ISO", duration: "doba trvání ISO", ipv4: "IPv4 adresa", ipv6: "IPv6 adresa", cidrv4: "rozsah IPv4", cidrv6: "rozsah IPv6", base64: "řetězec zakódovaný ve formátu base64", base64url: "řetězec zakódovaný ve formátu base64url", json_string: "řetězec ve formátu JSON", e164: "číslo E.164", jwt: "JWT", template_literal: "vstup" }, t = { nan: "NaN", number: "číslo", string: "řetězec", function: "funkce", array: "pole" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Neplatný vstup: očekáváno instanceof ${n.expected}, obdrženo ${u}`; + return `Neplatný vstup: očekáváno ${v}, obdrženo ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Neplatný vstup: očekáváno ${U(n.values[0])}`; + return `Neplatná možnost: očekávána jedna z hodnot ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Hodnota je příliš velká: ${n.origin ?? "hodnota"} musí mít ${v}${n.maximum.toString()} ${$.unit ?? "prvků"}`; + return `Hodnota je příliš velká: ${n.origin ?? "hodnota"} musí být ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Hodnota je příliš malá: ${n.origin ?? "hodnota"} musí mít ${v}${n.minimum.toString()} ${$.unit ?? "prvků"}`; + return `Hodnota je příliš malá: ${n.origin ?? "hodnota"} musí být ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Neplatný řetězec: musí začínat na "${v.prefix}"`; + if (v.format === "ends_with") return `Neplatný řetězec: musí končit na "${v.suffix}"`; + if (v.format === "includes") return `Neplatný řetězec: musí obsahovat "${v.includes}"`; + if (v.format === "regex") return `Neplatný řetězec: musí odpovídat vzoru ${v.pattern}`; + return `Neplatný formát ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Neplatné číslo: musí být násobkem ${n.divisor}`; + case "unrecognized_keys": + return `Neznámé klíče: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Neplatný klíč v ${n.origin}`; + case "invalid_union": + return "Neplatný vstup"; + case "invalid_element": + return `Neplatná hodnota v ${n.origin}`; + default: + return "Neplatný vstup"; + } + }; +}; +function Rt() { + return { localeError: hc() }; +} +var ac = () => { + let r = { string: { unit: "tegn", verb: "havde" }, file: { unit: "bytes", verb: "havde" }, array: { unit: "elementer", verb: "indeholdt" }, set: { unit: "elementer", verb: "indeholdt" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "input", email: "e-mailadresse", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO dato- og klokkeslæt", date: "ISO-dato", time: "ISO-klokkeslæt", duration: "ISO-varighed", ipv4: "IPv4-område", ipv6: "IPv6-område", cidrv4: "IPv4-spektrum", cidrv6: "IPv6-spektrum", base64: "base64-kodet streng", base64url: "base64url-kodet streng", json_string: "JSON-streng", e164: "E.164-nummer", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN", string: "streng", number: "tal", boolean: "boolean", array: "liste", object: "objekt", set: "sæt", file: "fil" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Ugyldigt input: forventede instanceof ${n.expected}, fik ${u}`; + return `Ugyldigt input: forventede ${v}, fik ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Ugyldig værdi: forventede ${U(n.values[0])}`; + return `Ugyldigt valg: forventede en af følgende ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin), u = t[n.origin] ?? n.origin; + if ($) return `For stor: forventede ${u ?? "value"} ${$.verb} ${v} ${n.maximum.toString()} ${$.unit ?? "elementer"}`; + return `For stor: forventede ${u ?? "value"} havde ${v} ${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin), u = t[n.origin] ?? n.origin; + if ($) return `For lille: forventede ${u} ${$.verb} ${v} ${n.minimum.toString()} ${$.unit}`; + return `For lille: forventede ${u} havde ${v} ${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Ugyldig streng: skal starte med "${v.prefix}"`; + if (v.format === "ends_with") return `Ugyldig streng: skal ende med "${v.suffix}"`; + if (v.format === "includes") return `Ugyldig streng: skal indeholde "${v.includes}"`; + if (v.format === "regex") return `Ugyldig streng: skal matche mønsteret ${v.pattern}`; + return `Ugyldig ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Ugyldigt tal: skal være deleligt med ${n.divisor}`; + case "unrecognized_keys": + return `${n.keys.length > 1 ? "Ukendte nøgler" : "Ukendt nøgle"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Ugyldig nøgle i ${n.origin}`; + case "invalid_union": + return "Ugyldigt input: matcher ingen af de tilladte typer"; + case "invalid_element": + return `Ugyldig værdi i ${n.origin}`; + default: + return "Ugyldigt input"; + } + }; +}; +function xt() { + return { localeError: ac() }; +} +var pc = () => { + let r = { string: { unit: "Zeichen", verb: "zu haben" }, file: { unit: "Bytes", verb: "zu haben" }, array: { unit: "Elemente", verb: "zu haben" }, set: { unit: "Elemente", verb: "zu haben" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "Eingabe", email: "E-Mail-Adresse", url: "URL", emoji: "Emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO-Datum und -Uhrzeit", date: "ISO-Datum", time: "ISO-Uhrzeit", duration: "ISO-Dauer", ipv4: "IPv4-Adresse", ipv6: "IPv6-Adresse", cidrv4: "IPv4-Bereich", cidrv6: "IPv6-Bereich", base64: "Base64-codierter String", base64url: "Base64-URL-codierter String", json_string: "JSON-String", e164: "E.164-Nummer", jwt: "JWT", template_literal: "Eingabe" }, t = { nan: "NaN", number: "Zahl", array: "Array" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Ungültige Eingabe: erwartet instanceof ${n.expected}, erhalten ${u}`; + return `Ungültige Eingabe: erwartet ${v}, erhalten ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Ungültige Eingabe: erwartet ${U(n.values[0])}`; + return `Ungültige Option: erwartet eine von ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Zu groß: erwartet, dass ${n.origin ?? "Wert"} ${v}${n.maximum.toString()} ${$.unit ?? "Elemente"} hat`; + return `Zu groß: erwartet, dass ${n.origin ?? "Wert"} ${v}${n.maximum.toString()} ist`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Zu klein: erwartet, dass ${n.origin} ${v}${n.minimum.toString()} ${$.unit} hat`; + return `Zu klein: erwartet, dass ${n.origin} ${v}${n.minimum.toString()} ist`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Ungültiger String: muss mit "${v.prefix}" beginnen`; + if (v.format === "ends_with") return `Ungültiger String: muss mit "${v.suffix}" enden`; + if (v.format === "includes") return `Ungültiger String: muss "${v.includes}" enthalten`; + if (v.format === "regex") return `Ungültiger String: muss dem Muster ${v.pattern} entsprechen`; + return `Ungültig: ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Ungültige Zahl: muss ein Vielfaches von ${n.divisor} sein`; + case "unrecognized_keys": + return `${n.keys.length > 1 ? "Unbekannte Schlüssel" : "Unbekannter Schlüssel"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Ungültiger Schlüssel in ${n.origin}`; + case "invalid_union": + return "Ungültige Eingabe"; + case "invalid_element": + return `Ungültiger Wert in ${n.origin}`; + default: + return "Ungültige Eingabe"; + } + }; +}; +function Zt() { + return { localeError: pc() }; +} +var sc = () => { + let r = { string: { unit: "characters", verb: "to have" }, file: { unit: "bytes", verb: "to have" }, array: { unit: "items", verb: "to have" }, set: { unit: "items", verb: "to have" }, map: { unit: "entries", verb: "to have" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "input", email: "email address", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO datetime", date: "ISO date", time: "ISO time", duration: "ISO duration", ipv4: "IPv4 address", ipv6: "IPv6 address", mac: "MAC address", cidrv4: "IPv4 range", cidrv6: "IPv6 range", base64: "base64-encoded string", base64url: "base64url-encoded string", json_string: "JSON string", e164: "E.164 number", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + return `Invalid input: expected ${v}, received ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Invalid input: expected ${U(n.values[0])}`; + return `Invalid option: expected one of ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Too big: expected ${n.origin ?? "value"} to have ${v}${n.maximum.toString()} ${$.unit ?? "elements"}`; + return `Too big: expected ${n.origin ?? "value"} to be ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Too small: expected ${n.origin} to have ${v}${n.minimum.toString()} ${$.unit}`; + return `Too small: expected ${n.origin} to be ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Invalid string: must start with "${v.prefix}"`; + if (v.format === "ends_with") return `Invalid string: must end with "${v.suffix}"`; + if (v.format === "includes") return `Invalid string: must include "${v.includes}"`; + if (v.format === "regex") return `Invalid string: must match pattern ${v.pattern}`; + return `Invalid ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Invalid number: must be a multiple of ${n.divisor}`; + case "unrecognized_keys": + return `Unrecognized key${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Invalid key in ${n.origin}`; + case "invalid_union": + return "Invalid input"; + case "invalid_element": + return `Invalid value in ${n.origin}`; + default: + return "Invalid input"; + } + }; +}; +function kn() { + return { localeError: sc() }; +} +var r4 = () => { + let r = { string: { unit: "karaktrojn", verb: "havi" }, file: { unit: "bajtojn", verb: "havi" }, array: { unit: "elementojn", verb: "havi" }, set: { unit: "elementojn", verb: "havi" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "enigo", email: "retadreso", url: "URL", emoji: "emoĝio", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO-datotempo", date: "ISO-dato", time: "ISO-tempo", duration: "ISO-daŭro", ipv4: "IPv4-adreso", ipv6: "IPv6-adreso", cidrv4: "IPv4-rango", cidrv6: "IPv6-rango", base64: "64-ume kodita karaktraro", base64url: "URL-64-ume kodita karaktraro", json_string: "JSON-karaktraro", e164: "E.164-nombro", jwt: "JWT", template_literal: "enigo" }, t = { nan: "NaN", number: "nombro", array: "tabelo", null: "senvalora" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Nevalida enigo: atendiĝis instanceof ${n.expected}, riceviĝis ${u}`; + return `Nevalida enigo: atendiĝis ${v}, riceviĝis ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Nevalida enigo: atendiĝis ${U(n.values[0])}`; + return `Nevalida opcio: atendiĝis unu el ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Tro granda: atendiĝis ke ${n.origin ?? "valoro"} havu ${v}${n.maximum.toString()} ${$.unit ?? "elementojn"}`; + return `Tro granda: atendiĝis ke ${n.origin ?? "valoro"} havu ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Tro malgranda: atendiĝis ke ${n.origin} havu ${v}${n.minimum.toString()} ${$.unit}`; + return `Tro malgranda: atendiĝis ke ${n.origin} estu ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Nevalida karaktraro: devas komenciĝi per "${v.prefix}"`; + if (v.format === "ends_with") return `Nevalida karaktraro: devas finiĝi per "${v.suffix}"`; + if (v.format === "includes") return `Nevalida karaktraro: devas inkluzivi "${v.includes}"`; + if (v.format === "regex") return `Nevalida karaktraro: devas kongrui kun la modelo ${v.pattern}`; + return `Nevalida ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Nevalida nombro: devas esti oblo de ${n.divisor}`; + case "unrecognized_keys": + return `Nekonata${n.keys.length > 1 ? "j" : ""} ŝlosilo${n.keys.length > 1 ? "j" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Nevalida ŝlosilo en ${n.origin}`; + case "invalid_union": + return "Nevalida enigo"; + case "invalid_element": + return `Nevalida valoro en ${n.origin}`; + default: + return "Nevalida enigo"; + } + }; +}; +function dt() { + return { localeError: r4() }; +} +var n4 = () => { + let r = { string: { unit: "caracteres", verb: "tener" }, file: { unit: "bytes", verb: "tener" }, array: { unit: "elementos", verb: "tener" }, set: { unit: "elementos", verb: "tener" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "entrada", email: "dirección de correo electrónico", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "fecha y hora ISO", date: "fecha ISO", time: "hora ISO", duration: "duración ISO", ipv4: "dirección IPv4", ipv6: "dirección IPv6", cidrv4: "rango IPv4", cidrv6: "rango IPv6", base64: "cadena codificada en base64", base64url: "URL codificada en base64", json_string: "cadena JSON", e164: "número E.164", jwt: "JWT", template_literal: "entrada" }, t = { nan: "NaN", string: "texto", number: "número", boolean: "booleano", array: "arreglo", object: "objeto", set: "conjunto", file: "archivo", date: "fecha", bigint: "número grande", symbol: "símbolo", undefined: "indefinido", null: "nulo", function: "función", map: "mapa", record: "registro", tuple: "tupla", enum: "enumeración", union: "unión", literal: "literal", promise: "promesa", void: "vacío", never: "nunca", unknown: "desconocido", any: "cualquiera" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Entrada inválida: se esperaba instanceof ${n.expected}, recibido ${u}`; + return `Entrada inválida: se esperaba ${v}, recibido ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Entrada inválida: se esperaba ${U(n.values[0])}`; + return `Opción inválida: se esperaba una de ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin), u = t[n.origin] ?? n.origin; + if ($) return `Demasiado grande: se esperaba que ${u ?? "valor"} tuviera ${v}${n.maximum.toString()} ${$.unit ?? "elementos"}`; + return `Demasiado grande: se esperaba que ${u ?? "valor"} fuera ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin), u = t[n.origin] ?? n.origin; + if ($) return `Demasiado pequeño: se esperaba que ${u} tuviera ${v}${n.minimum.toString()} ${$.unit}`; + return `Demasiado pequeño: se esperaba que ${u} fuera ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Cadena inválida: debe comenzar con "${v.prefix}"`; + if (v.format === "ends_with") return `Cadena inválida: debe terminar en "${v.suffix}"`; + if (v.format === "includes") return `Cadena inválida: debe incluir "${v.includes}"`; + if (v.format === "regex") return `Cadena inválida: debe coincidir con el patrón ${v.pattern}`; + return `Inválido ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Número inválido: debe ser múltiplo de ${n.divisor}`; + case "unrecognized_keys": + return `Llave${n.keys.length > 1 ? "s" : ""} desconocida${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Llave inválida en ${t[n.origin] ?? n.origin}`; + case "invalid_union": + return "Entrada inválida"; + case "invalid_element": + return `Valor inválido en ${t[n.origin] ?? n.origin}`; + default: + return "Entrada inválida"; + } + }; +}; +function Ct() { + return { localeError: n4() }; +} +var i4 = () => { + let r = { string: { unit: "کاراکتر", verb: "داشته باشد" }, file: { unit: "بایت", verb: "داشته باشد" }, array: { unit: "آیتم", verb: "داشته باشد" }, set: { unit: "آیتم", verb: "داشته باشد" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "ورودی", email: "آدرس ایمیل", url: "URL", emoji: "ایموجی", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "تاریخ و زمان ایزو", date: "تاریخ ایزو", time: "زمان ایزو", duration: "مدت زمان ایزو", ipv4: "IPv4 آدرس", ipv6: "IPv6 آدرس", cidrv4: "IPv4 دامنه", cidrv6: "IPv6 دامنه", base64: "base64-encoded رشته", base64url: "base64url-encoded رشته", json_string: "JSON رشته", e164: "E.164 عدد", jwt: "JWT", template_literal: "ورودی" }, t = { nan: "NaN", number: "عدد", array: "آرایه" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `ورودی نامعتبر: می‌بایست instanceof ${n.expected} می‌بود، ${u} دریافت شد`; + return `ورودی نامعتبر: می‌بایست ${v} می‌بود، ${u} دریافت شد`; + } + case "invalid_value": + if (n.values.length === 1) return `ورودی نامعتبر: می‌بایست ${U(n.values[0])} می‌بود`; + return `گزینه نامعتبر: می‌بایست یکی از ${b(n.values, "|")} می‌بود`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `خیلی بزرگ: ${n.origin ?? "مقدار"} باید ${v}${n.maximum.toString()} ${$.unit ?? "عنصر"} باشد`; + return `خیلی بزرگ: ${n.origin ?? "مقدار"} باید ${v}${n.maximum.toString()} باشد`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `خیلی کوچک: ${n.origin} باید ${v}${n.minimum.toString()} ${$.unit} باشد`; + return `خیلی کوچک: ${n.origin} باید ${v}${n.minimum.toString()} باشد`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `رشته نامعتبر: باید با "${v.prefix}" شروع شود`; + if (v.format === "ends_with") return `رشته نامعتبر: باید با "${v.suffix}" تمام شود`; + if (v.format === "includes") return `رشته نامعتبر: باید شامل "${v.includes}" باشد`; + if (v.format === "regex") return `رشته نامعتبر: باید با الگوی ${v.pattern} مطابقت داشته باشد`; + return `${o[v.format] ?? n.format} نامعتبر`; + } + case "not_multiple_of": + return `عدد نامعتبر: باید مضرب ${n.divisor} باشد`; + case "unrecognized_keys": + return `کلید${n.keys.length > 1 ? "های" : ""} ناشناس: ${b(n.keys, ", ")}`; + case "invalid_key": + return `کلید ناشناس در ${n.origin}`; + case "invalid_union": + return "ورودی نامعتبر"; + case "invalid_element": + return `مقدار نامعتبر در ${n.origin}`; + default: + return "ورودی نامعتبر"; + } + }; +}; +function ft() { + return { localeError: i4() }; +} +var v4 = () => { + let r = { string: { unit: "merkkiä", subject: "merkkijonon" }, file: { unit: "tavua", subject: "tiedoston" }, array: { unit: "alkiota", subject: "listan" }, set: { unit: "alkiota", subject: "joukon" }, number: { unit: "", subject: "luvun" }, bigint: { unit: "", subject: "suuren kokonaisluvun" }, int: { unit: "", subject: "kokonaisluvun" }, date: { unit: "", subject: "päivämäärän" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "säännöllinen lauseke", email: "sähköpostiosoite", url: "URL-osoite", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO-aikaleima", date: "ISO-päivämäärä", time: "ISO-aika", duration: "ISO-kesto", ipv4: "IPv4-osoite", ipv6: "IPv6-osoite", cidrv4: "IPv4-alue", cidrv6: "IPv6-alue", base64: "base64-koodattu merkkijono", base64url: "base64url-koodattu merkkijono", json_string: "JSON-merkkijono", e164: "E.164-luku", jwt: "JWT", template_literal: "templaattimerkkijono" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Virheellinen tyyppi: odotettiin instanceof ${n.expected}, oli ${u}`; + return `Virheellinen tyyppi: odotettiin ${v}, oli ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Virheellinen syöte: täytyy olla ${U(n.values[0])}`; + return `Virheellinen valinta: täytyy olla yksi seuraavista: ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Liian suuri: ${$.subject} täytyy olla ${v}${n.maximum.toString()} ${$.unit}`.trim(); + return `Liian suuri: arvon täytyy olla ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Liian pieni: ${$.subject} täytyy olla ${v}${n.minimum.toString()} ${$.unit}`.trim(); + return `Liian pieni: arvon täytyy olla ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Virheellinen syöte: täytyy alkaa "${v.prefix}"`; + if (v.format === "ends_with") return `Virheellinen syöte: täytyy loppua "${v.suffix}"`; + if (v.format === "includes") return `Virheellinen syöte: täytyy sisältää "${v.includes}"`; + if (v.format === "regex") return `Virheellinen syöte: täytyy vastata säännöllistä lauseketta ${v.pattern}`; + return `Virheellinen ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Virheellinen luku: täytyy olla luvun ${n.divisor} monikerta`; + case "unrecognized_keys": + return `${n.keys.length > 1 ? "Tuntemattomat avaimet" : "Tuntematon avain"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return "Virheellinen avain tietueessa"; + case "invalid_union": + return "Virheellinen unioni"; + case "invalid_element": + return "Virheellinen arvo joukossa"; + default: + return "Virheellinen syöte"; + } + }; +}; +function yt() { + return { localeError: v4() }; +} +var o4 = () => { + let r = { string: { unit: "caractères", verb: "avoir" }, file: { unit: "octets", verb: "avoir" }, array: { unit: "éléments", verb: "avoir" }, set: { unit: "éléments", verb: "avoir" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "entrée", email: "adresse e-mail", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "date et heure ISO", date: "date ISO", time: "heure ISO", duration: "durée ISO", ipv4: "adresse IPv4", ipv6: "adresse IPv6", cidrv4: "plage IPv4", cidrv6: "plage IPv6", base64: "chaîne encodée en base64", base64url: "chaîne encodée en base64url", json_string: "chaîne JSON", e164: "numéro E.164", jwt: "JWT", template_literal: "entrée" }, t = { nan: "NaN", number: "nombre", array: "tableau" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Entrée invalide : instanceof ${n.expected} attendu, ${u} reçu`; + return `Entrée invalide : ${v} attendu, ${u} reçu`; + } + case "invalid_value": + if (n.values.length === 1) return `Entrée invalide : ${U(n.values[0])} attendu`; + return `Option invalide : une valeur parmi ${b(n.values, "|")} attendue`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Trop grand : ${n.origin ?? "valeur"} doit ${$.verb} ${v}${n.maximum.toString()} ${$.unit ?? "élément(s)"}`; + return `Trop grand : ${n.origin ?? "valeur"} doit être ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Trop petit : ${n.origin} doit ${$.verb} ${v}${n.minimum.toString()} ${$.unit}`; + return `Trop petit : ${n.origin} doit être ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Chaîne invalide : doit commencer par "${v.prefix}"`; + if (v.format === "ends_with") return `Chaîne invalide : doit se terminer par "${v.suffix}"`; + if (v.format === "includes") return `Chaîne invalide : doit inclure "${v.includes}"`; + if (v.format === "regex") return `Chaîne invalide : doit correspondre au modèle ${v.pattern}`; + return `${o[v.format] ?? n.format} invalide`; + } + case "not_multiple_of": + return `Nombre invalide : doit être un multiple de ${n.divisor}`; + case "unrecognized_keys": + return `Clé${n.keys.length > 1 ? "s" : ""} non reconnue${n.keys.length > 1 ? "s" : ""} : ${b(n.keys, ", ")}`; + case "invalid_key": + return `Clé invalide dans ${n.origin}`; + case "invalid_union": + return "Entrée invalide"; + case "invalid_element": + return `Valeur invalide dans ${n.origin}`; + default: + return "Entrée invalide"; + } + }; +}; +function ht() { + return { localeError: o4() }; +} +var t4 = () => { + let r = { string: { unit: "caractères", verb: "avoir" }, file: { unit: "octets", verb: "avoir" }, array: { unit: "éléments", verb: "avoir" }, set: { unit: "éléments", verb: "avoir" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "entrée", email: "adresse courriel", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "date-heure ISO", date: "date ISO", time: "heure ISO", duration: "durée ISO", ipv4: "adresse IPv4", ipv6: "adresse IPv6", cidrv4: "plage IPv4", cidrv6: "plage IPv6", base64: "chaîne encodée en base64", base64url: "chaîne encodée en base64url", json_string: "chaîne JSON", e164: "numéro E.164", jwt: "JWT", template_literal: "entrée" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Entrée invalide : attendu instanceof ${n.expected}, reçu ${u}`; + return `Entrée invalide : attendu ${v}, reçu ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Entrée invalide : attendu ${U(n.values[0])}`; + return `Option invalide : attendu l'une des valeurs suivantes ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "≤" : "<", $ = i(n.origin); + if ($) return `Trop grand : attendu que ${n.origin ?? "la valeur"} ait ${v}${n.maximum.toString()} ${$.unit}`; + return `Trop grand : attendu que ${n.origin ?? "la valeur"} soit ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? "≥" : ">", $ = i(n.origin); + if ($) return `Trop petit : attendu que ${n.origin} ait ${v}${n.minimum.toString()} ${$.unit}`; + return `Trop petit : attendu que ${n.origin} soit ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Chaîne invalide : doit commencer par "${v.prefix}"`; + if (v.format === "ends_with") return `Chaîne invalide : doit se terminer par "${v.suffix}"`; + if (v.format === "includes") return `Chaîne invalide : doit inclure "${v.includes}"`; + if (v.format === "regex") return `Chaîne invalide : doit correspondre au motif ${v.pattern}`; + return `${o[v.format] ?? n.format} invalide`; + } + case "not_multiple_of": + return `Nombre invalide : doit être un multiple de ${n.divisor}`; + case "unrecognized_keys": + return `Clé${n.keys.length > 1 ? "s" : ""} non reconnue${n.keys.length > 1 ? "s" : ""} : ${b(n.keys, ", ")}`; + case "invalid_key": + return `Clé invalide dans ${n.origin}`; + case "invalid_union": + return "Entrée invalide"; + case "invalid_element": + return `Valeur invalide dans ${n.origin}`; + default: + return "Entrée invalide"; + } + }; +}; +function at() { + return { localeError: t4() }; +} +var $4 = () => { + let r = { string: { label: "מחרוזת", gender: "f" }, number: { label: "מספר", gender: "m" }, boolean: { label: "ערך בוליאני", gender: "m" }, bigint: { label: "BigInt", gender: "m" }, date: { label: "תאריך", gender: "m" }, array: { label: "מערך", gender: "m" }, object: { label: "אובייקט", gender: "m" }, null: { label: "ערך ריק (null)", gender: "m" }, undefined: { label: "ערך לא מוגדר (undefined)", gender: "m" }, symbol: { label: "סימבול (Symbol)", gender: "m" }, function: { label: "פונקציה", gender: "f" }, map: { label: "מפה (Map)", gender: "f" }, set: { label: "קבוצה (Set)", gender: "f" }, file: { label: "קובץ", gender: "m" }, promise: { label: "Promise", gender: "m" }, NaN: { label: "NaN", gender: "m" }, unknown: { label: "ערך לא ידוע", gender: "m" }, value: { label: "ערך", gender: "m" } }, i = { string: { unit: "תווים", shortLabel: "קצר", longLabel: "ארוך" }, file: { unit: "בייטים", shortLabel: "קטן", longLabel: "גדול" }, array: { unit: "פריטים", shortLabel: "קטן", longLabel: "גדול" }, set: { unit: "פריטים", shortLabel: "קטן", longLabel: "גדול" }, number: { unit: "", shortLabel: "קטן", longLabel: "גדול" } }, o = (e) => e ? r[e] : void 0, t = (e) => { + let c = o(e); + if (c) return c.label; + return e ?? r.unknown.label; + }, n = (e) => `ה${t(e)}`, v = (e) => { + return (o(e)?.gender ?? "m") === "f" ? "צריכה להיות" : "צריך להיות"; + }, $ = (e) => { + if (!e) return null; + return i[e] ?? null; + }, u = { regex: { label: "קלט", gender: "m" }, email: { label: "כתובת אימייל", gender: "f" }, url: { label: "כתובת רשת", gender: "f" }, emoji: { label: "אימוג'י", gender: "m" }, uuid: { label: "UUID", gender: "m" }, nanoid: { label: "nanoid", gender: "m" }, guid: { label: "GUID", gender: "m" }, cuid: { label: "cuid", gender: "m" }, cuid2: { label: "cuid2", gender: "m" }, ulid: { label: "ULID", gender: "m" }, xid: { label: "XID", gender: "m" }, ksuid: { label: "KSUID", gender: "m" }, datetime: { label: "תאריך וזמן ISO", gender: "m" }, date: { label: "תאריך ISO", gender: "m" }, time: { label: "זמן ISO", gender: "m" }, duration: { label: "משך זמן ISO", gender: "m" }, ipv4: { label: "כתובת IPv4", gender: "f" }, ipv6: { label: "כתובת IPv6", gender: "f" }, cidrv4: { label: "טווח IPv4", gender: "m" }, cidrv6: { label: "טווח IPv6", gender: "m" }, base64: { label: "מחרוזת בבסיס 64", gender: "f" }, base64url: { label: "מחרוזת בבסיס 64 לכתובות רשת", gender: "f" }, json_string: { label: "מחרוזת JSON", gender: "f" }, e164: { label: "מספר E.164", gender: "m" }, jwt: { label: "JWT", gender: "m" }, ends_with: { label: "קלט", gender: "m" }, includes: { label: "קלט", gender: "m" }, lowercase: { label: "קלט", gender: "m" }, starts_with: { label: "קלט", gender: "m" }, uppercase: { label: "קלט", gender: "m" } }, l = { nan: "NaN" }; + return (e) => { + switch (e.code) { + case "invalid_type": { + let c = e.expected, _ = l[c ?? ""] ?? t(c), N = k(e.input), O = l[N] ?? r[N]?.label ?? N; + if (/^[A-Z]/.test(e.expected)) return `קלט לא תקין: צריך להיות instanceof ${e.expected}, התקבל ${O}`; + return `קלט לא תקין: צריך להיות ${_}, התקבל ${O}`; + } + case "invalid_value": { + if (e.values.length === 1) return `ערך לא תקין: הערך חייב להיות ${U(e.values[0])}`; + let c = e.values.map((O) => U(O)); + if (e.values.length === 2) return `ערך לא תקין: האפשרויות המתאימות הן ${c[0]} או ${c[1]}`; + let _ = c[c.length - 1]; + return `ערך לא תקין: האפשרויות המתאימות הן ${c.slice(0, -1).join(", ")} או ${_}`; + } + case "too_big": { + let c = $(e.origin), _ = n(e.origin ?? "value"); + if (e.origin === "string") return `${c?.longLabel ?? "ארוך"} מדי: ${_} צריכה להכיל ${e.maximum.toString()} ${c?.unit ?? ""} ${e.inclusive ? "או פחות" : "לכל היותר"}`.trim(); + if (e.origin === "number") { + let J = e.inclusive ? `קטן או שווה ל-${e.maximum}` : `קטן מ-${e.maximum}`; + return `גדול מדי: ${_} צריך להיות ${J}`; + } + if (e.origin === "array" || e.origin === "set") { + let J = e.origin === "set" ? "צריכה" : "צריך", X = e.inclusive ? `${e.maximum} ${c?.unit ?? ""} או פחות` : `פחות מ-${e.maximum} ${c?.unit ?? ""}`; + return `גדול מדי: ${_} ${J} להכיל ${X}`.trim(); + } + let N = e.inclusive ? "<=" : "<", O = v(e.origin ?? "value"); + if (c?.unit) return `${c.longLabel} מדי: ${_} ${O} ${N}${e.maximum.toString()} ${c.unit}`; + return `${c?.longLabel ?? "גדול"} מדי: ${_} ${O} ${N}${e.maximum.toString()}`; + } + case "too_small": { + let c = $(e.origin), _ = n(e.origin ?? "value"); + if (e.origin === "string") return `${c?.shortLabel ?? "קצר"} מדי: ${_} צריכה להכיל ${e.minimum.toString()} ${c?.unit ?? ""} ${e.inclusive ? "או יותר" : "לפחות"}`.trim(); + if (e.origin === "number") { + let J = e.inclusive ? `גדול או שווה ל-${e.minimum}` : `גדול מ-${e.minimum}`; + return `קטן מדי: ${_} צריך להיות ${J}`; + } + if (e.origin === "array" || e.origin === "set") { + let J = e.origin === "set" ? "צריכה" : "צריך"; + if (e.minimum === 1 && e.inclusive) { + let zr = e.origin === "set" ? "לפחות פריט אחד" : "לפחות פריט אחד"; + return `קטן מדי: ${_} ${J} להכיל ${zr}`; + } + let X = e.inclusive ? `${e.minimum} ${c?.unit ?? ""} או יותר` : `יותר מ-${e.minimum} ${c?.unit ?? ""}`; + return `קטן מדי: ${_} ${J} להכיל ${X}`.trim(); + } + let N = e.inclusive ? ">=" : ">", O = v(e.origin ?? "value"); + if (c?.unit) return `${c.shortLabel} מדי: ${_} ${O} ${N}${e.minimum.toString()} ${c.unit}`; + return `${c?.shortLabel ?? "קטן"} מדי: ${_} ${O} ${N}${e.minimum.toString()}`; + } + case "invalid_format": { + let c = e; + if (c.format === "starts_with") return `המחרוזת חייבת להתחיל ב "${c.prefix}"`; + if (c.format === "ends_with") return `המחרוזת חייבת להסתיים ב "${c.suffix}"`; + if (c.format === "includes") return `המחרוזת חייבת לכלול "${c.includes}"`; + if (c.format === "regex") return `המחרוזת חייבת להתאים לתבנית ${c.pattern}`; + let _ = u[c.format], N = _?.label ?? c.format, J = (_?.gender ?? "m") === "f" ? "תקינה" : "תקין"; + return `${N} לא ${J}`; + } + case "not_multiple_of": + return `מספר לא תקין: חייב להיות מכפלה של ${e.divisor}`; + case "unrecognized_keys": + return `מפתח${e.keys.length > 1 ? "ות" : ""} לא מזוה${e.keys.length > 1 ? "ים" : "ה"}: ${b(e.keys, ", ")}`; + case "invalid_key": + return "שדה לא תקין באובייקט"; + case "invalid_union": + return "קלט לא תקין"; + case "invalid_element": + return `ערך לא תקין ב${n(e.origin ?? "array")}`; + default: + return "קלט לא תקין"; + } + }; +}; +function pt() { + return { localeError: $4() }; +} +var u4 = () => { + let r = { string: { unit: "karakter", verb: "legyen" }, file: { unit: "byte", verb: "legyen" }, array: { unit: "elem", verb: "legyen" }, set: { unit: "elem", verb: "legyen" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "bemenet", email: "email cím", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO időbélyeg", date: "ISO dátum", time: "ISO idő", duration: "ISO időintervallum", ipv4: "IPv4 cím", ipv6: "IPv6 cím", cidrv4: "IPv4 tartomány", cidrv6: "IPv6 tartomány", base64: "base64-kódolt string", base64url: "base64url-kódolt string", json_string: "JSON string", e164: "E.164 szám", jwt: "JWT", template_literal: "bemenet" }, t = { nan: "NaN", number: "szám", array: "tömb" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Érvénytelen bemenet: a várt érték instanceof ${n.expected}, a kapott érték ${u}`; + return `Érvénytelen bemenet: a várt érték ${v}, a kapott érték ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Érvénytelen bemenet: a várt érték ${U(n.values[0])}`; + return `Érvénytelen opció: valamelyik érték várt ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Túl nagy: ${n.origin ?? "érték"} mérete túl nagy ${v}${n.maximum.toString()} ${$.unit ?? "elem"}`; + return `Túl nagy: a bemeneti érték ${n.origin ?? "érték"} túl nagy: ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Túl kicsi: a bemeneti érték ${n.origin} mérete túl kicsi ${v}${n.minimum.toString()} ${$.unit}`; + return `Túl kicsi: a bemeneti érték ${n.origin} túl kicsi ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Érvénytelen string: "${v.prefix}" értékkel kell kezdődnie`; + if (v.format === "ends_with") return `Érvénytelen string: "${v.suffix}" értékkel kell végződnie`; + if (v.format === "includes") return `Érvénytelen string: "${v.includes}" értéket kell tartalmaznia`; + if (v.format === "regex") return `Érvénytelen string: ${v.pattern} mintának kell megfelelnie`; + return `Érvénytelen ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Érvénytelen szám: ${n.divisor} többszörösének kell lennie`; + case "unrecognized_keys": + return `Ismeretlen kulcs${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Érvénytelen kulcs ${n.origin}`; + case "invalid_union": + return "Érvénytelen bemenet"; + case "invalid_element": + return `Érvénytelen érték: ${n.origin}`; + default: + return "Érvénytelen bemenet"; + } + }; +}; +function st() { + return { localeError: u4() }; +} +function rl(r, i, o) { + return Math.abs(r) === 1 ? i : o; +} +function Xr(r) { + if (!r) return ""; + let i = ["ա", "ե", "ը", "ի", "ո", "ու", "օ"], o = r[r.length - 1]; + return r + (i.includes(o) ? "ն" : "ը"); +} +var g4 = () => { + let r = { string: { unit: { one: "նշան", many: "նշաններ" }, verb: "ունենալ" }, file: { unit: { one: "բայթ", many: "բայթեր" }, verb: "ունենալ" }, array: { unit: { one: "տարր", many: "տարրեր" }, verb: "ունենալ" }, set: { unit: { one: "տարր", many: "տարրեր" }, verb: "ունենալ" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "մուտք", email: "էլ. հասցե", url: "URL", emoji: "էմոջի", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO ամսաթիվ և ժամ", date: "ISO ամսաթիվ", time: "ISO ժամ", duration: "ISO տևողություն", ipv4: "IPv4 հասցե", ipv6: "IPv6 հասցե", cidrv4: "IPv4 միջակայք", cidrv6: "IPv6 միջակայք", base64: "base64 ձևաչափով տող", base64url: "base64url ձևաչափով տող", json_string: "JSON տող", e164: "E.164 համար", jwt: "JWT", template_literal: "մուտք" }, t = { nan: "NaN", number: "թիվ", array: "զանգված" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Սխալ մուտքագրում․ սպասվում էր instanceof ${n.expected}, ստացվել է ${u}`; + return `Սխալ մուտքագրում․ սպասվում էր ${v}, ստացվել է ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Սխալ մուտքագրում․ սպասվում էր ${U(n.values[1])}`; + return `Սխալ տարբերակ․ սպասվում էր հետևյալներից մեկը՝ ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) { + let u = Number(n.maximum), l = rl(u, $.unit.one, $.unit.many); + return `Չափազանց մեծ արժեք․ սպասվում է, որ ${Xr(n.origin ?? "արժեք")} կունենա ${v}${n.maximum.toString()} ${l}`; + } + return `Չափազանց մեծ արժեք․ սպասվում է, որ ${Xr(n.origin ?? "արժեք")} լինի ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) { + let u = Number(n.minimum), l = rl(u, $.unit.one, $.unit.many); + return `Չափազանց փոքր արժեք․ սպասվում է, որ ${Xr(n.origin)} կունենա ${v}${n.minimum.toString()} ${l}`; + } + return `Չափազանց փոքր արժեք․ սպասվում է, որ ${Xr(n.origin)} լինի ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Սխալ տող․ պետք է սկսվի "${v.prefix}"-ով`; + if (v.format === "ends_with") return `Սխալ տող․ պետք է ավարտվի "${v.suffix}"-ով`; + if (v.format === "includes") return `Սխալ տող․ պետք է պարունակի "${v.includes}"`; + if (v.format === "regex") return `Սխալ տող․ պետք է համապատասխանի ${v.pattern} ձևաչափին`; + return `Սխալ ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Սխալ թիվ․ պետք է բազմապատիկ լինի ${n.divisor}-ի`; + case "unrecognized_keys": + return `Չճանաչված բանալի${n.keys.length > 1 ? "ներ" : ""}. ${b(n.keys, ", ")}`; + case "invalid_key": + return `Սխալ բանալի ${Xr(n.origin)}-ում`; + case "invalid_union": + return "Սխալ մուտքագրում"; + case "invalid_element": + return `Սխալ արժեք ${Xr(n.origin)}-ում`; + default: + return "Սխալ մուտքագրում"; + } + }; +}; +function r$() { + return { localeError: g4() }; +} +var e4 = () => { + let r = { string: { unit: "karakter", verb: "memiliki" }, file: { unit: "byte", verb: "memiliki" }, array: { unit: "item", verb: "memiliki" }, set: { unit: "item", verb: "memiliki" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "input", email: "alamat email", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "tanggal dan waktu format ISO", date: "tanggal format ISO", time: "jam format ISO", duration: "durasi format ISO", ipv4: "alamat IPv4", ipv6: "alamat IPv6", cidrv4: "rentang alamat IPv4", cidrv6: "rentang alamat IPv6", base64: "string dengan enkode base64", base64url: "string dengan enkode base64url", json_string: "string JSON", e164: "angka E.164", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Input tidak valid: diharapkan instanceof ${n.expected}, diterima ${u}`; + return `Input tidak valid: diharapkan ${v}, diterima ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Input tidak valid: diharapkan ${U(n.values[0])}`; + return `Pilihan tidak valid: diharapkan salah satu dari ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Terlalu besar: diharapkan ${n.origin ?? "value"} memiliki ${v}${n.maximum.toString()} ${$.unit ?? "elemen"}`; + return `Terlalu besar: diharapkan ${n.origin ?? "value"} menjadi ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Terlalu kecil: diharapkan ${n.origin} memiliki ${v}${n.minimum.toString()} ${$.unit}`; + return `Terlalu kecil: diharapkan ${n.origin} menjadi ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `String tidak valid: harus dimulai dengan "${v.prefix}"`; + if (v.format === "ends_with") return `String tidak valid: harus berakhir dengan "${v.suffix}"`; + if (v.format === "includes") return `String tidak valid: harus menyertakan "${v.includes}"`; + if (v.format === "regex") return `String tidak valid: harus sesuai pola ${v.pattern}`; + return `${o[v.format] ?? n.format} tidak valid`; + } + case "not_multiple_of": + return `Angka tidak valid: harus kelipatan dari ${n.divisor}`; + case "unrecognized_keys": + return `Kunci tidak dikenali ${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Kunci tidak valid di ${n.origin}`; + case "invalid_union": + return "Input tidak valid"; + case "invalid_element": + return `Nilai tidak valid di ${n.origin}`; + default: + return "Input tidak valid"; + } + }; +}; +function n$() { + return { localeError: e4() }; +} +var l4 = () => { + let r = { string: { unit: "stafi", verb: "að hafa" }, file: { unit: "bæti", verb: "að hafa" }, array: { unit: "hluti", verb: "að hafa" }, set: { unit: "hluti", verb: "að hafa" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "gildi", email: "netfang", url: "vefslóð", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO dagsetning og tími", date: "ISO dagsetning", time: "ISO tími", duration: "ISO tímalengd", ipv4: "IPv4 address", ipv6: "IPv6 address", cidrv4: "IPv4 range", cidrv6: "IPv6 range", base64: "base64-encoded strengur", base64url: "base64url-encoded strengur", json_string: "JSON strengur", e164: "E.164 tölugildi", jwt: "JWT", template_literal: "gildi" }, t = { nan: "NaN", number: "númer", array: "fylki" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Rangt gildi: Þú slóst inn ${u} þar sem á að vera instanceof ${n.expected}`; + return `Rangt gildi: Þú slóst inn ${u} þar sem á að vera ${v}`; + } + case "invalid_value": + if (n.values.length === 1) return `Rangt gildi: gert ráð fyrir ${U(n.values[0])}`; + return `Ógilt val: má vera eitt af eftirfarandi ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Of stórt: gert er ráð fyrir að ${n.origin ?? "gildi"} hafi ${v}${n.maximum.toString()} ${$.unit ?? "hluti"}`; + return `Of stórt: gert er ráð fyrir að ${n.origin ?? "gildi"} sé ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Of lítið: gert er ráð fyrir að ${n.origin} hafi ${v}${n.minimum.toString()} ${$.unit}`; + return `Of lítið: gert er ráð fyrir að ${n.origin} sé ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Ógildur strengur: verður að byrja á "${v.prefix}"`; + if (v.format === "ends_with") return `Ógildur strengur: verður að enda á "${v.suffix}"`; + if (v.format === "includes") return `Ógildur strengur: verður að innihalda "${v.includes}"`; + if (v.format === "regex") return `Ógildur strengur: verður að fylgja mynstri ${v.pattern}`; + return `Rangt ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Röng tala: verður að vera margfeldi af ${n.divisor}`; + case "unrecognized_keys": + return `Óþekkt ${n.keys.length > 1 ? "ir lyklar" : "ur lykill"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Rangur lykill í ${n.origin}`; + case "invalid_union": + return "Rangt gildi"; + case "invalid_element": + return `Rangt gildi í ${n.origin}`; + default: + return "Rangt gildi"; + } + }; +}; +function i$() { + return { localeError: l4() }; +} +var I4 = () => { + let r = { string: { unit: "caratteri", verb: "avere" }, file: { unit: "byte", verb: "avere" }, array: { unit: "elementi", verb: "avere" }, set: { unit: "elementi", verb: "avere" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "input", email: "indirizzo email", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "data e ora ISO", date: "data ISO", time: "ora ISO", duration: "durata ISO", ipv4: "indirizzo IPv4", ipv6: "indirizzo IPv6", cidrv4: "intervallo IPv4", cidrv6: "intervallo IPv6", base64: "stringa codificata in base64", base64url: "URL codificata in base64", json_string: "stringa JSON", e164: "numero E.164", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN", number: "numero", array: "vettore" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Input non valido: atteso instanceof ${n.expected}, ricevuto ${u}`; + return `Input non valido: atteso ${v}, ricevuto ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Input non valido: atteso ${U(n.values[0])}`; + return `Opzione non valida: atteso uno tra ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Troppo grande: ${n.origin ?? "valore"} deve avere ${v}${n.maximum.toString()} ${$.unit ?? "elementi"}`; + return `Troppo grande: ${n.origin ?? "valore"} deve essere ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Troppo piccolo: ${n.origin} deve avere ${v}${n.minimum.toString()} ${$.unit}`; + return `Troppo piccolo: ${n.origin} deve essere ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Stringa non valida: deve iniziare con "${v.prefix}"`; + if (v.format === "ends_with") return `Stringa non valida: deve terminare con "${v.suffix}"`; + if (v.format === "includes") return `Stringa non valida: deve includere "${v.includes}"`; + if (v.format === "regex") return `Stringa non valida: deve corrispondere al pattern ${v.pattern}`; + return `Invalid ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Numero non valido: deve essere un multiplo di ${n.divisor}`; + case "unrecognized_keys": + return `Chiav${n.keys.length > 1 ? "i" : "e"} non riconosciut${n.keys.length > 1 ? "e" : "a"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Chiave non valida in ${n.origin}`; + case "invalid_union": + return "Input non valido"; + case "invalid_element": + return `Valore non valido in ${n.origin}`; + default: + return "Input non valido"; + } + }; +}; +function v$() { + return { localeError: I4() }; +} +var c4 = () => { + let r = { string: { unit: "文字", verb: "である" }, file: { unit: "バイト", verb: "である" }, array: { unit: "要素", verb: "である" }, set: { unit: "要素", verb: "である" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "入力値", email: "メールアドレス", url: "URL", emoji: "絵文字", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO日時", date: "ISO日付", time: "ISO時刻", duration: "ISO期間", ipv4: "IPv4アドレス", ipv6: "IPv6アドレス", cidrv4: "IPv4範囲", cidrv6: "IPv6範囲", base64: "base64エンコード文字列", base64url: "base64urlエンコード文字列", json_string: "JSON文字列", e164: "E.164番号", jwt: "JWT", template_literal: "入力値" }, t = { nan: "NaN", number: "数値", array: "配列" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `無効な入力: instanceof ${n.expected}が期待されましたが、${u}が入力されました`; + return `無効な入力: ${v}が期待されましたが、${u}が入力されました`; + } + case "invalid_value": + if (n.values.length === 1) return `無効な入力: ${U(n.values[0])}が期待されました`; + return `無効な選択: ${b(n.values, "、")}のいずれかである必要があります`; + case "too_big": { + let v = n.inclusive ? "以下である" : "より小さい", $ = i(n.origin); + if ($) return `大きすぎる値: ${n.origin ?? "値"}は${n.maximum.toString()}${$.unit ?? "要素"}${v}必要があります`; + return `大きすぎる値: ${n.origin ?? "値"}は${n.maximum.toString()}${v}必要があります`; + } + case "too_small": { + let v = n.inclusive ? "以上である" : "より大きい", $ = i(n.origin); + if ($) return `小さすぎる値: ${n.origin}は${n.minimum.toString()}${$.unit}${v}必要があります`; + return `小さすぎる値: ${n.origin}は${n.minimum.toString()}${v}必要があります`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `無効な文字列: "${v.prefix}"で始まる必要があります`; + if (v.format === "ends_with") return `無効な文字列: "${v.suffix}"で終わる必要があります`; + if (v.format === "includes") return `無効な文字列: "${v.includes}"を含む必要があります`; + if (v.format === "regex") return `無効な文字列: パターン${v.pattern}に一致する必要があります`; + return `無効な${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `無効な数値: ${n.divisor}の倍数である必要があります`; + case "unrecognized_keys": + return `認識されていないキー${n.keys.length > 1 ? "群" : ""}: ${b(n.keys, "、")}`; + case "invalid_key": + return `${n.origin}内の無効なキー`; + case "invalid_union": + return "無効な入力"; + case "invalid_element": + return `${n.origin}内の無効な値`; + default: + return "無効な入力"; + } + }; +}; +function o$() { + return { localeError: c4() }; +} +var b4 = () => { + let r = { string: { unit: "სიმბოლო", verb: "უნდა შეიცავდეს" }, file: { unit: "ბაიტი", verb: "უნდა შეიცავდეს" }, array: { unit: "ელემენტი", verb: "უნდა შეიცავდეს" }, set: { unit: "ელემენტი", verb: "უნდა შეიცავდეს" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "შეყვანა", email: "ელ-ფოსტის მისამართი", url: "URL", emoji: "ემოჯი", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "თარიღი-დრო", date: "თარიღი", time: "დრო", duration: "ხანგრძლივობა", ipv4: "IPv4 მისამართი", ipv6: "IPv6 მისამართი", cidrv4: "IPv4 დიაპაზონი", cidrv6: "IPv6 დიაპაზონი", base64: "base64-კოდირებული სტრინგი", base64url: "base64url-კოდირებული სტრინგი", json_string: "JSON სტრინგი", e164: "E.164 ნომერი", jwt: "JWT", template_literal: "შეყვანა" }, t = { nan: "NaN", number: "რიცხვი", string: "სტრინგი", boolean: "ბულეანი", function: "ფუნქცია", array: "მასივი" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `არასწორი შეყვანა: მოსალოდნელი instanceof ${n.expected}, მიღებული ${u}`; + return `არასწორი შეყვანა: მოსალოდნელი ${v}, მიღებული ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `არასწორი შეყვანა: მოსალოდნელი ${U(n.values[0])}`; + return `არასწორი ვარიანტი: მოსალოდნელია ერთ-ერთი ${b(n.values, "|")}-დან`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `ზედმეტად დიდი: მოსალოდნელი ${n.origin ?? "მნიშვნელობა"} ${$.verb} ${v}${n.maximum.toString()} ${$.unit}`; + return `ზედმეტად დიდი: მოსალოდნელი ${n.origin ?? "მნიშვნელობა"} იყოს ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `ზედმეტად პატარა: მოსალოდნელი ${n.origin} ${$.verb} ${v}${n.minimum.toString()} ${$.unit}`; + return `ზედმეტად პატარა: მოსალოდნელი ${n.origin} იყოს ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `არასწორი სტრინგი: უნდა იწყებოდეს "${v.prefix}"-ით`; + if (v.format === "ends_with") return `არასწორი სტრინგი: უნდა მთავრდებოდეს "${v.suffix}"-ით`; + if (v.format === "includes") return `არასწორი სტრინგი: უნდა შეიცავდეს "${v.includes}"-ს`; + if (v.format === "regex") return `არასწორი სტრინგი: უნდა შეესაბამებოდეს შაბლონს ${v.pattern}`; + return `არასწორი ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `არასწორი რიცხვი: უნდა იყოს ${n.divisor}-ის ჯერადი`; + case "unrecognized_keys": + return `უცნობი გასაღებ${n.keys.length > 1 ? "ები" : "ი"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `არასწორი გასაღები ${n.origin}-ში`; + case "invalid_union": + return "არასწორი შეყვანა"; + case "invalid_element": + return `არასწორი მნიშვნელობა ${n.origin}-ში`; + default: + return "არასწორი შეყვანა"; + } + }; +}; +function t$() { + return { localeError: b4() }; +} +var _4 = () => { + let r = { string: { unit: "តួអក្សរ", verb: "គួរមាន" }, file: { unit: "បៃ", verb: "គួរមាន" }, array: { unit: "ធាតុ", verb: "គួរមាន" }, set: { unit: "ធាតុ", verb: "គួរមាន" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "ទិន្នន័យបញ្ចូល", email: "អាសយដ្ឋានអ៊ីមែល", url: "URL", emoji: "សញ្ញាអារម្មណ៍", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "កាលបរិច្ឆេទ និងម៉ោង ISO", date: "កាលបរិច្ឆេទ ISO", time: "ម៉ោង ISO", duration: "រយៈពេល ISO", ipv4: "អាសយដ្ឋាន IPv4", ipv6: "អាសយដ្ឋាន IPv6", cidrv4: "ដែនអាសយដ្ឋាន IPv4", cidrv6: "ដែនអាសយដ្ឋាន IPv6", base64: "ខ្សែអក្សរអ៊ិកូដ base64", base64url: "ខ្សែអក្សរអ៊ិកូដ base64url", json_string: "ខ្សែអក្សរ JSON", e164: "លេខ E.164", jwt: "JWT", template_literal: "ទិន្នន័យបញ្ចូល" }, t = { nan: "NaN", number: "លេខ", array: "អារេ (Array)", null: "គ្មានតម្លៃ (null)" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `ទិន្នន័យបញ្ចូលមិនត្រឹមត្រូវ៖ ត្រូវការ instanceof ${n.expected} ប៉ុន្តែទទួលបាន ${u}`; + return `ទិន្នន័យបញ្ចូលមិនត្រឹមត្រូវ៖ ត្រូវការ ${v} ប៉ុន្តែទទួលបាន ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `ទិន្នន័យបញ្ចូលមិនត្រឹមត្រូវ៖ ត្រូវការ ${U(n.values[0])}`; + return `ជម្រើសមិនត្រឹមត្រូវ៖ ត្រូវជាមួយក្នុងចំណោម ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `ធំពេក៖ ត្រូវការ ${n.origin ?? "តម្លៃ"} ${v} ${n.maximum.toString()} ${$.unit ?? "ធាតុ"}`; + return `ធំពេក៖ ត្រូវការ ${n.origin ?? "តម្លៃ"} ${v} ${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `តូចពេក៖ ត្រូវការ ${n.origin} ${v} ${n.minimum.toString()} ${$.unit}`; + return `តូចពេក៖ ត្រូវការ ${n.origin} ${v} ${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវចាប់ផ្តើមដោយ "${v.prefix}"`; + if (v.format === "ends_with") return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវបញ្ចប់ដោយ "${v.suffix}"`; + if (v.format === "includes") return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវមាន "${v.includes}"`; + if (v.format === "regex") return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវតែផ្គូផ្គងនឹងទម្រង់ដែលបានកំណត់ ${v.pattern}`; + return `មិនត្រឹមត្រូវ៖ ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `លេខមិនត្រឹមត្រូវ៖ ត្រូវតែជាពហុគុណនៃ ${n.divisor}`; + case "unrecognized_keys": + return `រកឃើញសោមិនស្គាល់៖ ${b(n.keys, ", ")}`; + case "invalid_key": + return `សោមិនត្រឹមត្រូវនៅក្នុង ${n.origin}`; + case "invalid_union": + return "ទិន្នន័យមិនត្រឹមត្រូវ"; + case "invalid_element": + return `ទិន្នន័យមិនត្រឹមត្រូវនៅក្នុង ${n.origin}`; + default: + return "ទិន្នន័យមិនត្រឹមត្រូវ"; + } + }; +}; +function Dn() { + return { localeError: _4() }; +} +function $$() { + return Dn(); +} +var U4 = () => { + let r = { string: { unit: "문자", verb: "to have" }, file: { unit: "바이트", verb: "to have" }, array: { unit: "개", verb: "to have" }, set: { unit: "개", verb: "to have" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "입력", email: "이메일 주소", url: "URL", emoji: "이모지", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO 날짜시간", date: "ISO 날짜", time: "ISO 시간", duration: "ISO 기간", ipv4: "IPv4 주소", ipv6: "IPv6 주소", cidrv4: "IPv4 범위", cidrv6: "IPv6 범위", base64: "base64 인코딩 문자열", base64url: "base64url 인코딩 문자열", json_string: "JSON 문자열", e164: "E.164 번호", jwt: "JWT", template_literal: "입력" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `잘못된 입력: 예상 타입은 instanceof ${n.expected}, 받은 타입은 ${u}입니다`; + return `잘못된 입력: 예상 타입은 ${v}, 받은 타입은 ${u}입니다`; + } + case "invalid_value": + if (n.values.length === 1) return `잘못된 입력: 값은 ${U(n.values[0])} 이어야 합니다`; + return `잘못된 옵션: ${b(n.values, "또는 ")} 중 하나여야 합니다`; + case "too_big": { + let v = n.inclusive ? "이하" : "미만", $ = v === "미만" ? "이어야 합니다" : "여야 합니다", u = i(n.origin), l = u?.unit ?? "요소"; + if (u) return `${n.origin ?? "값"}이 너무 큽니다: ${n.maximum.toString()}${l} ${v}${$}`; + return `${n.origin ?? "값"}이 너무 큽니다: ${n.maximum.toString()} ${v}${$}`; + } + case "too_small": { + let v = n.inclusive ? "이상" : "초과", $ = v === "이상" ? "이어야 합니다" : "여야 합니다", u = i(n.origin), l = u?.unit ?? "요소"; + if (u) return `${n.origin ?? "값"}이 너무 작습니다: ${n.minimum.toString()}${l} ${v}${$}`; + return `${n.origin ?? "값"}이 너무 작습니다: ${n.minimum.toString()} ${v}${$}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `잘못된 문자열: "${v.prefix}"(으)로 시작해야 합니다`; + if (v.format === "ends_with") return `잘못된 문자열: "${v.suffix}"(으)로 끝나야 합니다`; + if (v.format === "includes") return `잘못된 문자열: "${v.includes}"을(를) 포함해야 합니다`; + if (v.format === "regex") return `잘못된 문자열: 정규식 ${v.pattern} 패턴과 일치해야 합니다`; + return `잘못된 ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `잘못된 숫자: ${n.divisor}의 배수여야 합니다`; + case "unrecognized_keys": + return `인식할 수 없는 키: ${b(n.keys, ", ")}`; + case "invalid_key": + return `잘못된 키: ${n.origin}`; + case "invalid_union": + return "잘못된 입력"; + case "invalid_element": + return `잘못된 값: ${n.origin}`; + default: + return "잘못된 입력"; + } + }; +}; +function u$() { + return { localeError: U4() }; +} +var wn = (r) => { + return r.charAt(0).toUpperCase() + r.slice(1); +}; +function nl(r) { + let i = Math.abs(r), o = i % 10, t = i % 100; + if (t >= 11 && t <= 19 || o === 0) return "many"; + if (o === 1) return "one"; + return "few"; +} +var k4 = () => { + let r = { string: { unit: { one: "simbolis", few: "simboliai", many: "simbolių" }, verb: { smaller: { inclusive: "turi būti ne ilgesnė kaip", notInclusive: "turi būti trumpesnė kaip" }, bigger: { inclusive: "turi būti ne trumpesnė kaip", notInclusive: "turi būti ilgesnė kaip" } } }, file: { unit: { one: "baitas", few: "baitai", many: "baitų" }, verb: { smaller: { inclusive: "turi būti ne didesnis kaip", notInclusive: "turi būti mažesnis kaip" }, bigger: { inclusive: "turi būti ne mažesnis kaip", notInclusive: "turi būti didesnis kaip" } } }, array: { unit: { one: "elementą", few: "elementus", many: "elementų" }, verb: { smaller: { inclusive: "turi turėti ne daugiau kaip", notInclusive: "turi turėti mažiau kaip" }, bigger: { inclusive: "turi turėti ne mažiau kaip", notInclusive: "turi turėti daugiau kaip" } } }, set: { unit: { one: "elementą", few: "elementus", many: "elementų" }, verb: { smaller: { inclusive: "turi turėti ne daugiau kaip", notInclusive: "turi turėti mažiau kaip" }, bigger: { inclusive: "turi turėti ne mažiau kaip", notInclusive: "turi turėti daugiau kaip" } } } }; + function i(n, v, $, u) { + let l = r[n] ?? null; + if (l === null) return l; + return { unit: l.unit[v], verb: l.verb[u][$ ? "inclusive" : "notInclusive"] }; + } + let o = { regex: "įvestis", email: "el. pašto adresas", url: "URL", emoji: "jaustukas", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO data ir laikas", date: "ISO data", time: "ISO laikas", duration: "ISO trukmė", ipv4: "IPv4 adresas", ipv6: "IPv6 adresas", cidrv4: "IPv4 tinklo prefiksas (CIDR)", cidrv6: "IPv6 tinklo prefiksas (CIDR)", base64: "base64 užkoduota eilutė", base64url: "base64url užkoduota eilutė", json_string: "JSON eilutė", e164: "E.164 numeris", jwt: "JWT", template_literal: "įvestis" }, t = { nan: "NaN", number: "skaičius", bigint: "sveikasis skaičius", string: "eilutė", boolean: "loginė reikšmė", undefined: "neapibrėžta reikšmė", function: "funkcija", symbol: "simbolis", array: "masyvas", object: "objektas", null: "nulinė reikšmė" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Gautas tipas ${u}, o tikėtasi - instanceof ${n.expected}`; + return `Gautas tipas ${u}, o tikėtasi - ${v}`; + } + case "invalid_value": + if (n.values.length === 1) return `Privalo būti ${U(n.values[0])}`; + return `Privalo būti vienas iš ${b(n.values, "|")} pasirinkimų`; + case "too_big": { + let v = t[n.origin] ?? n.origin, $ = i(n.origin, nl(Number(n.maximum)), n.inclusive ?? false, "smaller"); + if ($?.verb) return `${wn(v ?? n.origin ?? "reikšmė")} ${$.verb} ${n.maximum.toString()} ${$.unit ?? "elementų"}`; + let u = n.inclusive ? "ne didesnis kaip" : "mažesnis kaip"; + return `${wn(v ?? n.origin ?? "reikšmė")} turi būti ${u} ${n.maximum.toString()} ${$?.unit}`; + } + case "too_small": { + let v = t[n.origin] ?? n.origin, $ = i(n.origin, nl(Number(n.minimum)), n.inclusive ?? false, "bigger"); + if ($?.verb) return `${wn(v ?? n.origin ?? "reikšmė")} ${$.verb} ${n.minimum.toString()} ${$.unit ?? "elementų"}`; + let u = n.inclusive ? "ne mažesnis kaip" : "didesnis kaip"; + return `${wn(v ?? n.origin ?? "reikšmė")} turi būti ${u} ${n.minimum.toString()} ${$?.unit}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Eilutė privalo prasidėti "${v.prefix}"`; + if (v.format === "ends_with") return `Eilutė privalo pasibaigti "${v.suffix}"`; + if (v.format === "includes") return `Eilutė privalo įtraukti "${v.includes}"`; + if (v.format === "regex") return `Eilutė privalo atitikti ${v.pattern}`; + return `Neteisingas ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Skaičius privalo būti ${n.divisor} kartotinis.`; + case "unrecognized_keys": + return `Neatpažint${n.keys.length > 1 ? "i" : "as"} rakt${n.keys.length > 1 ? "ai" : "as"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return "Rastas klaidingas raktas"; + case "invalid_union": + return "Klaidinga įvestis"; + case "invalid_element": { + let v = t[n.origin] ?? n.origin; + return `${wn(v ?? n.origin ?? "reikšmė")} turi klaidingą įvestį`; + } + default: + return "Klaidinga įvestis"; + } + }; +}; +function g$() { + return { localeError: k4() }; +} +var D4 = () => { + let r = { string: { unit: "знаци", verb: "да имаат" }, file: { unit: "бајти", verb: "да имаат" }, array: { unit: "ставки", verb: "да имаат" }, set: { unit: "ставки", verb: "да имаат" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "внес", email: "адреса на е-пошта", url: "URL", emoji: "емоџи", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO датум и време", date: "ISO датум", time: "ISO време", duration: "ISO времетраење", ipv4: "IPv4 адреса", ipv6: "IPv6 адреса", cidrv4: "IPv4 опсег", cidrv6: "IPv6 опсег", base64: "base64-енкодирана низа", base64url: "base64url-енкодирана низа", json_string: "JSON низа", e164: "E.164 број", jwt: "JWT", template_literal: "внес" }, t = { nan: "NaN", number: "број", array: "низа" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Грешен внес: се очекува instanceof ${n.expected}, примено ${u}`; + return `Грешен внес: се очекува ${v}, примено ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Invalid input: expected ${U(n.values[0])}`; + return `Грешана опција: се очекува една ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Премногу голем: се очекува ${n.origin ?? "вредноста"} да има ${v}${n.maximum.toString()} ${$.unit ?? "елементи"}`; + return `Премногу голем: се очекува ${n.origin ?? "вредноста"} да биде ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Премногу мал: се очекува ${n.origin} да има ${v}${n.minimum.toString()} ${$.unit}`; + return `Премногу мал: се очекува ${n.origin} да биде ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Неважечка низа: мора да започнува со "${v.prefix}"`; + if (v.format === "ends_with") return `Неважечка низа: мора да завршува со "${v.suffix}"`; + if (v.format === "includes") return `Неважечка низа: мора да вклучува "${v.includes}"`; + if (v.format === "regex") return `Неважечка низа: мора да одгоара на патернот ${v.pattern}`; + return `Invalid ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Грешен број: мора да биде делив со ${n.divisor}`; + case "unrecognized_keys": + return `${n.keys.length > 1 ? "Непрепознаени клучеви" : "Непрепознаен клуч"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Грешен клуч во ${n.origin}`; + case "invalid_union": + return "Грешен внес"; + case "invalid_element": + return `Грешна вредност во ${n.origin}`; + default: + return "Грешен внес"; + } + }; +}; +function e$() { + return { localeError: D4() }; +} +var w4 = () => { + let r = { string: { unit: "aksara", verb: "mempunyai" }, file: { unit: "bait", verb: "mempunyai" }, array: { unit: "elemen", verb: "mempunyai" }, set: { unit: "elemen", verb: "mempunyai" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "input", email: "alamat e-mel", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "tarikh masa ISO", date: "tarikh ISO", time: "masa ISO", duration: "tempoh ISO", ipv4: "alamat IPv4", ipv6: "alamat IPv6", cidrv4: "julat IPv4", cidrv6: "julat IPv6", base64: "string dikodkan base64", base64url: "string dikodkan base64url", json_string: "string JSON", e164: "nombor E.164", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN", number: "nombor" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Input tidak sah: dijangka instanceof ${n.expected}, diterima ${u}`; + return `Input tidak sah: dijangka ${v}, diterima ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Input tidak sah: dijangka ${U(n.values[0])}`; + return `Pilihan tidak sah: dijangka salah satu daripada ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Terlalu besar: dijangka ${n.origin ?? "nilai"} ${$.verb} ${v}${n.maximum.toString()} ${$.unit ?? "elemen"}`; + return `Terlalu besar: dijangka ${n.origin ?? "nilai"} adalah ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Terlalu kecil: dijangka ${n.origin} ${$.verb} ${v}${n.minimum.toString()} ${$.unit}`; + return `Terlalu kecil: dijangka ${n.origin} adalah ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `String tidak sah: mesti bermula dengan "${v.prefix}"`; + if (v.format === "ends_with") return `String tidak sah: mesti berakhir dengan "${v.suffix}"`; + if (v.format === "includes") return `String tidak sah: mesti mengandungi "${v.includes}"`; + if (v.format === "regex") return `String tidak sah: mesti sepadan dengan corak ${v.pattern}`; + return `${o[v.format] ?? n.format} tidak sah`; + } + case "not_multiple_of": + return `Nombor tidak sah: perlu gandaan ${n.divisor}`; + case "unrecognized_keys": + return `Kunci tidak dikenali: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Kunci tidak sah dalam ${n.origin}`; + case "invalid_union": + return "Input tidak sah"; + case "invalid_element": + return `Nilai tidak sah dalam ${n.origin}`; + default: + return "Input tidak sah"; + } + }; +}; +function l$() { + return { localeError: w4() }; +} +var N4 = () => { + let r = { string: { unit: "tekens", verb: "heeft" }, file: { unit: "bytes", verb: "heeft" }, array: { unit: "elementen", verb: "heeft" }, set: { unit: "elementen", verb: "heeft" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "invoer", email: "emailadres", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO datum en tijd", date: "ISO datum", time: "ISO tijd", duration: "ISO duur", ipv4: "IPv4-adres", ipv6: "IPv6-adres", cidrv4: "IPv4-bereik", cidrv6: "IPv6-bereik", base64: "base64-gecodeerde tekst", base64url: "base64 URL-gecodeerde tekst", json_string: "JSON string", e164: "E.164-nummer", jwt: "JWT", template_literal: "invoer" }, t = { nan: "NaN", number: "getal" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Ongeldige invoer: verwacht instanceof ${n.expected}, ontving ${u}`; + return `Ongeldige invoer: verwacht ${v}, ontving ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Ongeldige invoer: verwacht ${U(n.values[0])}`; + return `Ongeldige optie: verwacht één van ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin), u = n.origin === "date" ? "laat" : n.origin === "string" ? "lang" : "groot"; + if ($) return `Te ${u}: verwacht dat ${n.origin ?? "waarde"} ${v}${n.maximum.toString()} ${$.unit ?? "elementen"} ${$.verb}`; + return `Te ${u}: verwacht dat ${n.origin ?? "waarde"} ${v}${n.maximum.toString()} is`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin), u = n.origin === "date" ? "vroeg" : n.origin === "string" ? "kort" : "klein"; + if ($) return `Te ${u}: verwacht dat ${n.origin} ${v}${n.minimum.toString()} ${$.unit} ${$.verb}`; + return `Te ${u}: verwacht dat ${n.origin} ${v}${n.minimum.toString()} is`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Ongeldige tekst: moet met "${v.prefix}" beginnen`; + if (v.format === "ends_with") return `Ongeldige tekst: moet op "${v.suffix}" eindigen`; + if (v.format === "includes") return `Ongeldige tekst: moet "${v.includes}" bevatten`; + if (v.format === "regex") return `Ongeldige tekst: moet overeenkomen met patroon ${v.pattern}`; + return `Ongeldig: ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Ongeldig getal: moet een veelvoud van ${n.divisor} zijn`; + case "unrecognized_keys": + return `Onbekende key${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Ongeldige key in ${n.origin}`; + case "invalid_union": + return "Ongeldige invoer"; + case "invalid_element": + return `Ongeldige waarde in ${n.origin}`; + default: + return "Ongeldige invoer"; + } + }; +}; +function I$() { + return { localeError: N4() }; +} +var O4 = () => { + let r = { string: { unit: "tegn", verb: "å ha" }, file: { unit: "bytes", verb: "å ha" }, array: { unit: "elementer", verb: "å inneholde" }, set: { unit: "elementer", verb: "å inneholde" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "input", email: "e-postadresse", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO dato- og klokkeslett", date: "ISO-dato", time: "ISO-klokkeslett", duration: "ISO-varighet", ipv4: "IPv4-område", ipv6: "IPv6-område", cidrv4: "IPv4-spekter", cidrv6: "IPv6-spekter", base64: "base64-enkodet streng", base64url: "base64url-enkodet streng", json_string: "JSON-streng", e164: "E.164-nummer", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN", number: "tall", array: "liste" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Ugyldig input: forventet instanceof ${n.expected}, fikk ${u}`; + return `Ugyldig input: forventet ${v}, fikk ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Ugyldig verdi: forventet ${U(n.values[0])}`; + return `Ugyldig valg: forventet en av ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `For stor(t): forventet ${n.origin ?? "value"} til å ha ${v}${n.maximum.toString()} ${$.unit ?? "elementer"}`; + return `For stor(t): forventet ${n.origin ?? "value"} til å ha ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `For lite(n): forventet ${n.origin} til å ha ${v}${n.minimum.toString()} ${$.unit}`; + return `For lite(n): forventet ${n.origin} til å ha ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Ugyldig streng: må starte med "${v.prefix}"`; + if (v.format === "ends_with") return `Ugyldig streng: må ende med "${v.suffix}"`; + if (v.format === "includes") return `Ugyldig streng: må inneholde "${v.includes}"`; + if (v.format === "regex") return `Ugyldig streng: må matche mønsteret ${v.pattern}`; + return `Ugyldig ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Ugyldig tall: må være et multiplum av ${n.divisor}`; + case "unrecognized_keys": + return `${n.keys.length > 1 ? "Ukjente nøkler" : "Ukjent nøkkel"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Ugyldig nøkkel i ${n.origin}`; + case "invalid_union": + return "Ugyldig input"; + case "invalid_element": + return `Ugyldig verdi i ${n.origin}`; + default: + return "Ugyldig input"; + } + }; +}; +function c$() { + return { localeError: O4() }; +} +var z4 = () => { + let r = { string: { unit: "harf", verb: "olmalıdır" }, file: { unit: "bayt", verb: "olmalıdır" }, array: { unit: "unsur", verb: "olmalıdır" }, set: { unit: "unsur", verb: "olmalıdır" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "giren", email: "epostagâh", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO hengâmı", date: "ISO tarihi", time: "ISO zamanı", duration: "ISO müddeti", ipv4: "IPv4 nişânı", ipv6: "IPv6 nişânı", cidrv4: "IPv4 menzili", cidrv6: "IPv6 menzili", base64: "base64-şifreli metin", base64url: "base64url-şifreli metin", json_string: "JSON metin", e164: "E.164 sayısı", jwt: "JWT", template_literal: "giren" }, t = { nan: "NaN", number: "numara", array: "saf", null: "gayb" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Fâsit giren: umulan instanceof ${n.expected}, alınan ${u}`; + return `Fâsit giren: umulan ${v}, alınan ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Fâsit giren: umulan ${U(n.values[0])}`; + return `Fâsit tercih: mûteberler ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Fazla büyük: ${n.origin ?? "value"}, ${v}${n.maximum.toString()} ${$.unit ?? "elements"} sahip olmalıydı.`; + return `Fazla büyük: ${n.origin ?? "value"}, ${v}${n.maximum.toString()} olmalıydı.`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Fazla küçük: ${n.origin}, ${v}${n.minimum.toString()} ${$.unit} sahip olmalıydı.`; + return `Fazla küçük: ${n.origin}, ${v}${n.minimum.toString()} olmalıydı.`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Fâsit metin: "${v.prefix}" ile başlamalı.`; + if (v.format === "ends_with") return `Fâsit metin: "${v.suffix}" ile bitmeli.`; + if (v.format === "includes") return `Fâsit metin: "${v.includes}" ihtivâ etmeli.`; + if (v.format === "regex") return `Fâsit metin: ${v.pattern} nakşına uymalı.`; + return `Fâsit ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Fâsit sayı: ${n.divisor} katı olmalıydı.`; + case "unrecognized_keys": + return `Tanınmayan anahtar ${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `${n.origin} için tanınmayan anahtar var.`; + case "invalid_union": + return "Giren tanınamadı."; + case "invalid_element": + return `${n.origin} için tanınmayan kıymet var.`; + default: + return "Kıymet tanınamadı."; + } + }; +}; +function b$() { + return { localeError: z4() }; +} +var S4 = () => { + let r = { string: { unit: "توکي", verb: "ولري" }, file: { unit: "بایټس", verb: "ولري" }, array: { unit: "توکي", verb: "ولري" }, set: { unit: "توکي", verb: "ولري" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "ورودي", email: "بریښنالیک", url: "یو آر ال", emoji: "ایموجي", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "نیټه او وخت", date: "نېټه", time: "وخت", duration: "موده", ipv4: "د IPv4 پته", ipv6: "د IPv6 پته", cidrv4: "د IPv4 ساحه", cidrv6: "د IPv6 ساحه", base64: "base64-encoded متن", base64url: "base64url-encoded متن", json_string: "JSON متن", e164: "د E.164 شمېره", jwt: "JWT", template_literal: "ورودي" }, t = { nan: "NaN", number: "عدد", array: "ارې" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `ناسم ورودي: باید instanceof ${n.expected} وای, مګر ${u} ترلاسه شو`; + return `ناسم ورودي: باید ${v} وای, مګر ${u} ترلاسه شو`; + } + case "invalid_value": + if (n.values.length === 1) return `ناسم ورودي: باید ${U(n.values[0])} وای`; + return `ناسم انتخاب: باید یو له ${b(n.values, "|")} څخه وای`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `ډیر لوی: ${n.origin ?? "ارزښت"} باید ${v}${n.maximum.toString()} ${$.unit ?? "عنصرونه"} ولري`; + return `ډیر لوی: ${n.origin ?? "ارزښت"} باید ${v}${n.maximum.toString()} وي`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `ډیر کوچنی: ${n.origin} باید ${v}${n.minimum.toString()} ${$.unit} ولري`; + return `ډیر کوچنی: ${n.origin} باید ${v}${n.minimum.toString()} وي`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `ناسم متن: باید د "${v.prefix}" سره پیل شي`; + if (v.format === "ends_with") return `ناسم متن: باید د "${v.suffix}" سره پای ته ورسيږي`; + if (v.format === "includes") return `ناسم متن: باید "${v.includes}" ولري`; + if (v.format === "regex") return `ناسم متن: باید د ${v.pattern} سره مطابقت ولري`; + return `${o[v.format] ?? n.format} ناسم دی`; + } + case "not_multiple_of": + return `ناسم عدد: باید د ${n.divisor} مضرب وي`; + case "unrecognized_keys": + return `ناسم ${n.keys.length > 1 ? "کلیډونه" : "کلیډ"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `ناسم کلیډ په ${n.origin} کې`; + case "invalid_union": + return "ناسمه ورودي"; + case "invalid_element": + return `ناسم عنصر په ${n.origin} کې`; + default: + return "ناسمه ورودي"; + } + }; +}; +function _$() { + return { localeError: S4() }; +} +var P4 = () => { + let r = { string: { unit: "znaków", verb: "mieć" }, file: { unit: "bajtów", verb: "mieć" }, array: { unit: "elementów", verb: "mieć" }, set: { unit: "elementów", verb: "mieć" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "wyrażenie", email: "adres email", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "data i godzina w formacie ISO", date: "data w formacie ISO", time: "godzina w formacie ISO", duration: "czas trwania ISO", ipv4: "adres IPv4", ipv6: "adres IPv6", cidrv4: "zakres IPv4", cidrv6: "zakres IPv6", base64: "ciąg znaków zakodowany w formacie base64", base64url: "ciąg znaków zakodowany w formacie base64url", json_string: "ciąg znaków w formacie JSON", e164: "liczba E.164", jwt: "JWT", template_literal: "wejście" }, t = { nan: "NaN", number: "liczba", array: "tablica" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Nieprawidłowe dane wejściowe: oczekiwano instanceof ${n.expected}, otrzymano ${u}`; + return `Nieprawidłowe dane wejściowe: oczekiwano ${v}, otrzymano ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Nieprawidłowe dane wejściowe: oczekiwano ${U(n.values[0])}`; + return `Nieprawidłowa opcja: oczekiwano jednej z wartości ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Za duża wartość: oczekiwano, że ${n.origin ?? "wartość"} będzie mieć ${v}${n.maximum.toString()} ${$.unit ?? "elementów"}`; + return `Zbyt duż(y/a/e): oczekiwano, że ${n.origin ?? "wartość"} będzie wynosić ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Za mała wartość: oczekiwano, że ${n.origin ?? "wartość"} będzie mieć ${v}${n.minimum.toString()} ${$.unit ?? "elementów"}`; + return `Zbyt mał(y/a/e): oczekiwano, że ${n.origin ?? "wartość"} będzie wynosić ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Nieprawidłowy ciąg znaków: musi zaczynać się od "${v.prefix}"`; + if (v.format === "ends_with") return `Nieprawidłowy ciąg znaków: musi kończyć się na "${v.suffix}"`; + if (v.format === "includes") return `Nieprawidłowy ciąg znaków: musi zawierać "${v.includes}"`; + if (v.format === "regex") return `Nieprawidłowy ciąg znaków: musi odpowiadać wzorcowi ${v.pattern}`; + return `Nieprawidłow(y/a/e) ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Nieprawidłowa liczba: musi być wielokrotnością ${n.divisor}`; + case "unrecognized_keys": + return `Nierozpoznane klucze${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Nieprawidłowy klucz w ${n.origin}`; + case "invalid_union": + return "Nieprawidłowe dane wejściowe"; + case "invalid_element": + return `Nieprawidłowa wartość w ${n.origin}`; + default: + return "Nieprawidłowe dane wejściowe"; + } + }; +}; +function U$() { + return { localeError: P4() }; +} +var j4 = () => { + let r = { string: { unit: "caracteres", verb: "ter" }, file: { unit: "bytes", verb: "ter" }, array: { unit: "itens", verb: "ter" }, set: { unit: "itens", verb: "ter" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "padrão", email: "endereço de e-mail", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "data e hora ISO", date: "data ISO", time: "hora ISO", duration: "duração ISO", ipv4: "endereço IPv4", ipv6: "endereço IPv6", cidrv4: "faixa de IPv4", cidrv6: "faixa de IPv6", base64: "texto codificado em base64", base64url: "URL codificada em base64", json_string: "texto JSON", e164: "número E.164", jwt: "JWT", template_literal: "entrada" }, t = { nan: "NaN", number: "número", null: "nulo" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Tipo inválido: esperado instanceof ${n.expected}, recebido ${u}`; + return `Tipo inválido: esperado ${v}, recebido ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Entrada inválida: esperado ${U(n.values[0])}`; + return `Opção inválida: esperada uma das ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Muito grande: esperado que ${n.origin ?? "valor"} tivesse ${v}${n.maximum.toString()} ${$.unit ?? "elementos"}`; + return `Muito grande: esperado que ${n.origin ?? "valor"} fosse ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Muito pequeno: esperado que ${n.origin} tivesse ${v}${n.minimum.toString()} ${$.unit}`; + return `Muito pequeno: esperado que ${n.origin} fosse ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Texto inválido: deve começar com "${v.prefix}"`; + if (v.format === "ends_with") return `Texto inválido: deve terminar com "${v.suffix}"`; + if (v.format === "includes") return `Texto inválido: deve incluir "${v.includes}"`; + if (v.format === "regex") return `Texto inválido: deve corresponder ao padrão ${v.pattern}`; + return `${o[v.format] ?? n.format} inválido`; + } + case "not_multiple_of": + return `Número inválido: deve ser múltiplo de ${n.divisor}`; + case "unrecognized_keys": + return `Chave${n.keys.length > 1 ? "s" : ""} desconhecida${n.keys.length > 1 ? "s" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Chave inválida em ${n.origin}`; + case "invalid_union": + return "Entrada inválida"; + case "invalid_element": + return `Valor inválido em ${n.origin}`; + default: + return "Campo inválido"; + } + }; +}; +function k$() { + return { localeError: j4() }; +} +function il(r, i, o, t) { + let n = Math.abs(r), v = n % 10, $ = n % 100; + if ($ >= 11 && $ <= 19) return t; + if (v === 1) return i; + if (v >= 2 && v <= 4) return o; + return t; +} +var J4 = () => { + let r = { string: { unit: { one: "символ", few: "символа", many: "символов" }, verb: "иметь" }, file: { unit: { one: "байт", few: "байта", many: "байт" }, verb: "иметь" }, array: { unit: { one: "элемент", few: "элемента", many: "элементов" }, verb: "иметь" }, set: { unit: { one: "элемент", few: "элемента", many: "элементов" }, verb: "иметь" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "ввод", email: "email адрес", url: "URL", emoji: "эмодзи", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO дата и время", date: "ISO дата", time: "ISO время", duration: "ISO длительность", ipv4: "IPv4 адрес", ipv6: "IPv6 адрес", cidrv4: "IPv4 диапазон", cidrv6: "IPv6 диапазон", base64: "строка в формате base64", base64url: "строка в формате base64url", json_string: "JSON строка", e164: "номер E.164", jwt: "JWT", template_literal: "ввод" }, t = { nan: "NaN", number: "число", array: "массив" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Неверный ввод: ожидалось instanceof ${n.expected}, получено ${u}`; + return `Неверный ввод: ожидалось ${v}, получено ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Неверный ввод: ожидалось ${U(n.values[0])}`; + return `Неверный вариант: ожидалось одно из ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) { + let u = Number(n.maximum), l = il(u, $.unit.one, $.unit.few, $.unit.many); + return `Слишком большое значение: ожидалось, что ${n.origin ?? "значение"} будет иметь ${v}${n.maximum.toString()} ${l}`; + } + return `Слишком большое значение: ожидалось, что ${n.origin ?? "значение"} будет ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) { + let u = Number(n.minimum), l = il(u, $.unit.one, $.unit.few, $.unit.many); + return `Слишком маленькое значение: ожидалось, что ${n.origin} будет иметь ${v}${n.minimum.toString()} ${l}`; + } + return `Слишком маленькое значение: ожидалось, что ${n.origin} будет ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Неверная строка: должна начинаться с "${v.prefix}"`; + if (v.format === "ends_with") return `Неверная строка: должна заканчиваться на "${v.suffix}"`; + if (v.format === "includes") return `Неверная строка: должна содержать "${v.includes}"`; + if (v.format === "regex") return `Неверная строка: должна соответствовать шаблону ${v.pattern}`; + return `Неверный ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Неверное число: должно быть кратным ${n.divisor}`; + case "unrecognized_keys": + return `Нераспознанн${n.keys.length > 1 ? "ые" : "ый"} ключ${n.keys.length > 1 ? "и" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Неверный ключ в ${n.origin}`; + case "invalid_union": + return "Неверные входные данные"; + case "invalid_element": + return `Неверное значение в ${n.origin}`; + default: + return "Неверные входные данные"; + } + }; +}; +function D$() { + return { localeError: J4() }; +} +var L4 = () => { + let r = { string: { unit: "znakov", verb: "imeti" }, file: { unit: "bajtov", verb: "imeti" }, array: { unit: "elementov", verb: "imeti" }, set: { unit: "elementov", verb: "imeti" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "vnos", email: "e-poštni naslov", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO datum in čas", date: "ISO datum", time: "ISO čas", duration: "ISO trajanje", ipv4: "IPv4 naslov", ipv6: "IPv6 naslov", cidrv4: "obseg IPv4", cidrv6: "obseg IPv6", base64: "base64 kodiran niz", base64url: "base64url kodiran niz", json_string: "JSON niz", e164: "E.164 številka", jwt: "JWT", template_literal: "vnos" }, t = { nan: "NaN", number: "število", array: "tabela" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Neveljaven vnos: pričakovano instanceof ${n.expected}, prejeto ${u}`; + return `Neveljaven vnos: pričakovano ${v}, prejeto ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Neveljaven vnos: pričakovano ${U(n.values[0])}`; + return `Neveljavna možnost: pričakovano eno izmed ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Preveliko: pričakovano, da bo ${n.origin ?? "vrednost"} imelo ${v}${n.maximum.toString()} ${$.unit ?? "elementov"}`; + return `Preveliko: pričakovano, da bo ${n.origin ?? "vrednost"} ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Premajhno: pričakovano, da bo ${n.origin} imelo ${v}${n.minimum.toString()} ${$.unit}`; + return `Premajhno: pričakovano, da bo ${n.origin} ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Neveljaven niz: mora se začeti z "${v.prefix}"`; + if (v.format === "ends_with") return `Neveljaven niz: mora se končati z "${v.suffix}"`; + if (v.format === "includes") return `Neveljaven niz: mora vsebovati "${v.includes}"`; + if (v.format === "regex") return `Neveljaven niz: mora ustrezati vzorcu ${v.pattern}`; + return `Neveljaven ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Neveljavno število: mora biti večkratnik ${n.divisor}`; + case "unrecognized_keys": + return `Neprepoznan${n.keys.length > 1 ? "i ključi" : " ključ"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Neveljaven ključ v ${n.origin}`; + case "invalid_union": + return "Neveljaven vnos"; + case "invalid_element": + return `Neveljavna vrednost v ${n.origin}`; + default: + return "Neveljaven vnos"; + } + }; +}; +function w$() { + return { localeError: L4() }; +} +var G4 = () => { + let r = { string: { unit: "tecken", verb: "att ha" }, file: { unit: "bytes", verb: "att ha" }, array: { unit: "objekt", verb: "att innehålla" }, set: { unit: "objekt", verb: "att innehålla" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "reguljärt uttryck", email: "e-postadress", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO-datum och tid", date: "ISO-datum", time: "ISO-tid", duration: "ISO-varaktighet", ipv4: "IPv4-intervall", ipv6: "IPv6-intervall", cidrv4: "IPv4-spektrum", cidrv6: "IPv6-spektrum", base64: "base64-kodad sträng", base64url: "base64url-kodad sträng", json_string: "JSON-sträng", e164: "E.164-nummer", jwt: "JWT", template_literal: "mall-literal" }, t = { nan: "NaN", number: "antal", array: "lista" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Ogiltig inmatning: förväntat instanceof ${n.expected}, fick ${u}`; + return `Ogiltig inmatning: förväntat ${v}, fick ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Ogiltig inmatning: förväntat ${U(n.values[0])}`; + return `Ogiltigt val: förväntade en av ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `För stor(t): förväntade ${n.origin ?? "värdet"} att ha ${v}${n.maximum.toString()} ${$.unit ?? "element"}`; + return `För stor(t): förväntat ${n.origin ?? "värdet"} att ha ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `För lite(t): förväntade ${n.origin ?? "värdet"} att ha ${v}${n.minimum.toString()} ${$.unit}`; + return `För lite(t): förväntade ${n.origin ?? "värdet"} att ha ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Ogiltig sträng: måste börja med "${v.prefix}"`; + if (v.format === "ends_with") return `Ogiltig sträng: måste sluta med "${v.suffix}"`; + if (v.format === "includes") return `Ogiltig sträng: måste innehålla "${v.includes}"`; + if (v.format === "regex") return `Ogiltig sträng: måste matcha mönstret "${v.pattern}"`; + return `Ogiltig(t) ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Ogiltigt tal: måste vara en multipel av ${n.divisor}`; + case "unrecognized_keys": + return `${n.keys.length > 1 ? "Okända nycklar" : "Okänd nyckel"}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Ogiltig nyckel i ${n.origin ?? "värdet"}`; + case "invalid_union": + return "Ogiltig input"; + case "invalid_element": + return `Ogiltigt värde i ${n.origin ?? "värdet"}`; + default: + return "Ogiltig input"; + } + }; +}; +function N$() { + return { localeError: G4() }; +} +var W4 = () => { + let r = { string: { unit: "எழுத்துக்கள்", verb: "கொண்டிருக்க வேண்டும்" }, file: { unit: "பைட்டுகள்", verb: "கொண்டிருக்க வேண்டும்" }, array: { unit: "உறுப்புகள்", verb: "கொண்டிருக்க வேண்டும்" }, set: { unit: "உறுப்புகள்", verb: "கொண்டிருக்க வேண்டும்" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "உள்ளீடு", email: "மின்னஞ்சல் முகவரி", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO தேதி நேரம்", date: "ISO தேதி", time: "ISO நேரம்", duration: "ISO கால அளவு", ipv4: "IPv4 முகவரி", ipv6: "IPv6 முகவரி", cidrv4: "IPv4 வரம்பு", cidrv6: "IPv6 வரம்பு", base64: "base64-encoded சரம்", base64url: "base64url-encoded சரம்", json_string: "JSON சரம்", e164: "E.164 எண்", jwt: "JWT", template_literal: "input" }, t = { nan: "NaN", number: "எண்", array: "அணி", null: "வெறுமை" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது instanceof ${n.expected}, பெறப்பட்டது ${u}`; + return `தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது ${v}, பெறப்பட்டது ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `தவறான உள்ளீடு: எதிர்பார்க்கப்பட்டது ${U(n.values[0])}`; + return `தவறான விருப்பம்: எதிர்பார்க்கப்பட்டது ${b(n.values, "|")} இல் ஒன்று`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `மிக பெரியது: எதிர்பார்க்கப்பட்டது ${n.origin ?? "மதிப்பு"} ${v}${n.maximum.toString()} ${$.unit ?? "உறுப்புகள்"} ஆக இருக்க வேண்டும்`; + return `மிக பெரியது: எதிர்பார்க்கப்பட்டது ${n.origin ?? "மதிப்பு"} ${v}${n.maximum.toString()} ஆக இருக்க வேண்டும்`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `மிகச் சிறியது: எதிர்பார்க்கப்பட்டது ${n.origin} ${v}${n.minimum.toString()} ${$.unit} ஆக இருக்க வேண்டும்`; + return `மிகச் சிறியது: எதிர்பார்க்கப்பட்டது ${n.origin} ${v}${n.minimum.toString()} ஆக இருக்க வேண்டும்`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `தவறான சரம்: "${v.prefix}" இல் தொடங்க வேண்டும்`; + if (v.format === "ends_with") return `தவறான சரம்: "${v.suffix}" இல் முடிவடைய வேண்டும்`; + if (v.format === "includes") return `தவறான சரம்: "${v.includes}" ஐ உள்ளடக்க வேண்டும்`; + if (v.format === "regex") return `தவறான சரம்: ${v.pattern} முறைபாட்டுடன் பொருந்த வேண்டும்`; + return `தவறான ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `தவறான எண்: ${n.divisor} இன் பலமாக இருக்க வேண்டும்`; + case "unrecognized_keys": + return `அடையாளம் தெரியாத விசை${n.keys.length > 1 ? "கள்" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `${n.origin} இல் தவறான விசை`; + case "invalid_union": + return "தவறான உள்ளீடு"; + case "invalid_element": + return `${n.origin} இல் தவறான மதிப்பு`; + default: + return "தவறான உள்ளீடு"; + } + }; +}; +function O$() { + return { localeError: W4() }; +} +var V4 = () => { + let r = { string: { unit: "ตัวอักษร", verb: "ควรมี" }, file: { unit: "ไบต์", verb: "ควรมี" }, array: { unit: "รายการ", verb: "ควรมี" }, set: { unit: "รายการ", verb: "ควรมี" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "ข้อมูลที่ป้อน", email: "ที่อยู่อีเมล", url: "URL", emoji: "อิโมจิ", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "วันที่เวลาแบบ ISO", date: "วันที่แบบ ISO", time: "เวลาแบบ ISO", duration: "ช่วงเวลาแบบ ISO", ipv4: "ที่อยู่ IPv4", ipv6: "ที่อยู่ IPv6", cidrv4: "ช่วง IP แบบ IPv4", cidrv6: "ช่วง IP แบบ IPv6", base64: "ข้อความแบบ Base64", base64url: "ข้อความแบบ Base64 สำหรับ URL", json_string: "ข้อความแบบ JSON", e164: "เบอร์โทรศัพท์ระหว่างประเทศ (E.164)", jwt: "โทเคน JWT", template_literal: "ข้อมูลที่ป้อน" }, t = { nan: "NaN", number: "ตัวเลข", array: "อาร์เรย์ (Array)", null: "ไม่มีค่า (null)" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `ประเภทข้อมูลไม่ถูกต้อง: ควรเป็น instanceof ${n.expected} แต่ได้รับ ${u}`; + return `ประเภทข้อมูลไม่ถูกต้อง: ควรเป็น ${v} แต่ได้รับ ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `ค่าไม่ถูกต้อง: ควรเป็น ${U(n.values[0])}`; + return `ตัวเลือกไม่ถูกต้อง: ควรเป็นหนึ่งใน ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "ไม่เกิน" : "น้อยกว่า", $ = i(n.origin); + if ($) return `เกินกำหนด: ${n.origin ?? "ค่า"} ควรมี${v} ${n.maximum.toString()} ${$.unit ?? "รายการ"}`; + return `เกินกำหนด: ${n.origin ?? "ค่า"} ควรมี${v} ${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? "อย่างน้อย" : "มากกว่า", $ = i(n.origin); + if ($) return `น้อยกว่ากำหนด: ${n.origin} ควรมี${v} ${n.minimum.toString()} ${$.unit}`; + return `น้อยกว่ากำหนด: ${n.origin} ควรมี${v} ${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `รูปแบบไม่ถูกต้อง: ข้อความต้องขึ้นต้นด้วย "${v.prefix}"`; + if (v.format === "ends_with") return `รูปแบบไม่ถูกต้อง: ข้อความต้องลงท้ายด้วย "${v.suffix}"`; + if (v.format === "includes") return `รูปแบบไม่ถูกต้อง: ข้อความต้องมี "${v.includes}" อยู่ในข้อความ`; + if (v.format === "regex") return `รูปแบบไม่ถูกต้อง: ต้องตรงกับรูปแบบที่กำหนด ${v.pattern}`; + return `รูปแบบไม่ถูกต้อง: ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `ตัวเลขไม่ถูกต้อง: ต้องเป็นจำนวนที่หารด้วย ${n.divisor} ได้ลงตัว`; + case "unrecognized_keys": + return `พบคีย์ที่ไม่รู้จัก: ${b(n.keys, ", ")}`; + case "invalid_key": + return `คีย์ไม่ถูกต้องใน ${n.origin}`; + case "invalid_union": + return "ข้อมูลไม่ถูกต้อง: ไม่ตรงกับรูปแบบยูเนียนที่กำหนดไว้"; + case "invalid_element": + return `ข้อมูลไม่ถูกต้องใน ${n.origin}`; + default: + return "ข้อมูลไม่ถูกต้อง"; + } + }; +}; +function z$() { + return { localeError: V4() }; +} +var X4 = () => { + let r = { string: { unit: "karakter", verb: "olmalı" }, file: { unit: "bayt", verb: "olmalı" }, array: { unit: "öğe", verb: "olmalı" }, set: { unit: "öğe", verb: "olmalı" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "girdi", email: "e-posta adresi", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO tarih ve saat", date: "ISO tarih", time: "ISO saat", duration: "ISO süre", ipv4: "IPv4 adresi", ipv6: "IPv6 adresi", cidrv4: "IPv4 aralığı", cidrv6: "IPv6 aralığı", base64: "base64 ile şifrelenmiş metin", base64url: "base64url ile şifrelenmiş metin", json_string: "JSON dizesi", e164: "E.164 sayısı", jwt: "JWT", template_literal: "Şablon dizesi" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Geçersiz değer: beklenen instanceof ${n.expected}, alınan ${u}`; + return `Geçersiz değer: beklenen ${v}, alınan ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Geçersiz değer: beklenen ${U(n.values[0])}`; + return `Geçersiz seçenek: aşağıdakilerden biri olmalı: ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Çok büyük: beklenen ${n.origin ?? "değer"} ${v}${n.maximum.toString()} ${$.unit ?? "öğe"}`; + return `Çok büyük: beklenen ${n.origin ?? "değer"} ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Çok küçük: beklenen ${n.origin} ${v}${n.minimum.toString()} ${$.unit}`; + return `Çok küçük: beklenen ${n.origin} ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Geçersiz metin: "${v.prefix}" ile başlamalı`; + if (v.format === "ends_with") return `Geçersiz metin: "${v.suffix}" ile bitmeli`; + if (v.format === "includes") return `Geçersiz metin: "${v.includes}" içermeli`; + if (v.format === "regex") return `Geçersiz metin: ${v.pattern} desenine uymalı`; + return `Geçersiz ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Geçersiz sayı: ${n.divisor} ile tam bölünebilmeli`; + case "unrecognized_keys": + return `Tanınmayan anahtar${n.keys.length > 1 ? "lar" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `${n.origin} içinde geçersiz anahtar`; + case "invalid_union": + return "Geçersiz değer"; + case "invalid_element": + return `${n.origin} içinde geçersiz değer`; + default: + return "Geçersiz değer"; + } + }; +}; +function S$() { + return { localeError: X4() }; +} +var E4 = () => { + let r = { string: { unit: "символів", verb: "матиме" }, file: { unit: "байтів", verb: "матиме" }, array: { unit: "елементів", verb: "матиме" }, set: { unit: "елементів", verb: "матиме" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "вхідні дані", email: "адреса електронної пошти", url: "URL", emoji: "емодзі", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "дата та час ISO", date: "дата ISO", time: "час ISO", duration: "тривалість ISO", ipv4: "адреса IPv4", ipv6: "адреса IPv6", cidrv4: "діапазон IPv4", cidrv6: "діапазон IPv6", base64: "рядок у кодуванні base64", base64url: "рядок у кодуванні base64url", json_string: "рядок JSON", e164: "номер E.164", jwt: "JWT", template_literal: "вхідні дані" }, t = { nan: "NaN", number: "число", array: "масив" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Неправильні вхідні дані: очікується instanceof ${n.expected}, отримано ${u}`; + return `Неправильні вхідні дані: очікується ${v}, отримано ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Неправильні вхідні дані: очікується ${U(n.values[0])}`; + return `Неправильна опція: очікується одне з ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Занадто велике: очікується, що ${n.origin ?? "значення"} ${$.verb} ${v}${n.maximum.toString()} ${$.unit ?? "елементів"}`; + return `Занадто велике: очікується, що ${n.origin ?? "значення"} буде ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Занадто мале: очікується, що ${n.origin} ${$.verb} ${v}${n.minimum.toString()} ${$.unit}`; + return `Занадто мале: очікується, що ${n.origin} буде ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Неправильний рядок: повинен починатися з "${v.prefix}"`; + if (v.format === "ends_with") return `Неправильний рядок: повинен закінчуватися на "${v.suffix}"`; + if (v.format === "includes") return `Неправильний рядок: повинен містити "${v.includes}"`; + if (v.format === "regex") return `Неправильний рядок: повинен відповідати шаблону ${v.pattern}`; + return `Неправильний ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Неправильне число: повинно бути кратним ${n.divisor}`; + case "unrecognized_keys": + return `Нерозпізнаний ключ${n.keys.length > 1 ? "і" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Неправильний ключ у ${n.origin}`; + case "invalid_union": + return "Неправильні вхідні дані"; + case "invalid_element": + return `Неправильне значення у ${n.origin}`; + default: + return "Неправильні вхідні дані"; + } + }; +}; +function Nn() { + return { localeError: E4() }; +} +function P$() { + return Nn(); +} +var A4 = () => { + let r = { string: { unit: "حروف", verb: "ہونا" }, file: { unit: "بائٹس", verb: "ہونا" }, array: { unit: "آئٹمز", verb: "ہونا" }, set: { unit: "آئٹمز", verb: "ہونا" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "ان پٹ", email: "ای میل ایڈریس", url: "یو آر ایل", emoji: "ایموجی", uuid: "یو یو آئی ڈی", uuidv4: "یو یو آئی ڈی وی 4", uuidv6: "یو یو آئی ڈی وی 6", nanoid: "نینو آئی ڈی", guid: "جی یو آئی ڈی", cuid: "سی یو آئی ڈی", cuid2: "سی یو آئی ڈی 2", ulid: "یو ایل آئی ڈی", xid: "ایکس آئی ڈی", ksuid: "کے ایس یو آئی ڈی", datetime: "آئی ایس او ڈیٹ ٹائم", date: "آئی ایس او تاریخ", time: "آئی ایس او وقت", duration: "آئی ایس او مدت", ipv4: "آئی پی وی 4 ایڈریس", ipv6: "آئی پی وی 6 ایڈریس", cidrv4: "آئی پی وی 4 رینج", cidrv6: "آئی پی وی 6 رینج", base64: "بیس 64 ان کوڈڈ سٹرنگ", base64url: "بیس 64 یو آر ایل ان کوڈڈ سٹرنگ", json_string: "جے ایس او این سٹرنگ", e164: "ای 164 نمبر", jwt: "جے ڈبلیو ٹی", template_literal: "ان پٹ" }, t = { nan: "NaN", number: "نمبر", array: "آرے", null: "نل" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `غلط ان پٹ: instanceof ${n.expected} متوقع تھا، ${u} موصول ہوا`; + return `غلط ان پٹ: ${v} متوقع تھا، ${u} موصول ہوا`; + } + case "invalid_value": + if (n.values.length === 1) return `غلط ان پٹ: ${U(n.values[0])} متوقع تھا`; + return `غلط آپشن: ${b(n.values, "|")} میں سے ایک متوقع تھا`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `بہت بڑا: ${n.origin ?? "ویلیو"} کے ${v}${n.maximum.toString()} ${$.unit ?? "عناصر"} ہونے متوقع تھے`; + return `بہت بڑا: ${n.origin ?? "ویلیو"} کا ${v}${n.maximum.toString()} ہونا متوقع تھا`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `بہت چھوٹا: ${n.origin} کے ${v}${n.minimum.toString()} ${$.unit} ہونے متوقع تھے`; + return `بہت چھوٹا: ${n.origin} کا ${v}${n.minimum.toString()} ہونا متوقع تھا`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `غلط سٹرنگ: "${v.prefix}" سے شروع ہونا چاہیے`; + if (v.format === "ends_with") return `غلط سٹرنگ: "${v.suffix}" پر ختم ہونا چاہیے`; + if (v.format === "includes") return `غلط سٹرنگ: "${v.includes}" شامل ہونا چاہیے`; + if (v.format === "regex") return `غلط سٹرنگ: پیٹرن ${v.pattern} سے میچ ہونا چاہیے`; + return `غلط ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `غلط نمبر: ${n.divisor} کا مضاعف ہونا چاہیے`; + case "unrecognized_keys": + return `غیر تسلیم شدہ کی${n.keys.length > 1 ? "ز" : ""}: ${b(n.keys, "، ")}`; + case "invalid_key": + return `${n.origin} میں غلط کی`; + case "invalid_union": + return "غلط ان پٹ"; + case "invalid_element": + return `${n.origin} میں غلط ویلیو`; + default: + return "غلط ان پٹ"; + } + }; +}; +function j$() { + return { localeError: A4() }; +} +var K4 = () => { + let r = { string: { unit: "belgi", verb: "bo‘lishi kerak" }, file: { unit: "bayt", verb: "bo‘lishi kerak" }, array: { unit: "element", verb: "bo‘lishi kerak" }, set: { unit: "element", verb: "bo‘lishi kerak" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "kirish", email: "elektron pochta manzili", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO sana va vaqti", date: "ISO sana", time: "ISO vaqt", duration: "ISO davomiylik", ipv4: "IPv4 manzil", ipv6: "IPv6 manzil", mac: "MAC manzil", cidrv4: "IPv4 diapazon", cidrv6: "IPv6 diapazon", base64: "base64 kodlangan satr", base64url: "base64url kodlangan satr", json_string: "JSON satr", e164: "E.164 raqam", jwt: "JWT", template_literal: "kirish" }, t = { nan: "NaN", number: "raqam", array: "massiv" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Noto‘g‘ri kirish: kutilgan instanceof ${n.expected}, qabul qilingan ${u}`; + return `Noto‘g‘ri kirish: kutilgan ${v}, qabul qilingan ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Noto‘g‘ri kirish: kutilgan ${U(n.values[0])}`; + return `Noto‘g‘ri variant: quyidagilardan biri kutilgan ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Juda katta: kutilgan ${n.origin ?? "qiymat"} ${v}${n.maximum.toString()} ${$.unit} ${$.verb}`; + return `Juda katta: kutilgan ${n.origin ?? "qiymat"} ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Juda kichik: kutilgan ${n.origin} ${v}${n.minimum.toString()} ${$.unit} ${$.verb}`; + return `Juda kichik: kutilgan ${n.origin} ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Noto‘g‘ri satr: "${v.prefix}" bilan boshlanishi kerak`; + if (v.format === "ends_with") return `Noto‘g‘ri satr: "${v.suffix}" bilan tugashi kerak`; + if (v.format === "includes") return `Noto‘g‘ri satr: "${v.includes}" ni o‘z ichiga olishi kerak`; + if (v.format === "regex") return `Noto‘g‘ri satr: ${v.pattern} shabloniga mos kelishi kerak`; + return `Noto‘g‘ri ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Noto‘g‘ri raqam: ${n.divisor} ning karralisi bo‘lishi kerak`; + case "unrecognized_keys": + return `Noma’lum kalit${n.keys.length > 1 ? "lar" : ""}: ${b(n.keys, ", ")}`; + case "invalid_key": + return `${n.origin} dagi kalit noto‘g‘ri`; + case "invalid_union": + return "Noto‘g‘ri kirish"; + case "invalid_element": + return `${n.origin} da noto‘g‘ri qiymat`; + default: + return "Noto‘g‘ri kirish"; + } + }; +}; +function J$() { + return { localeError: K4() }; +} +var q4 = () => { + let r = { string: { unit: "ký tự", verb: "có" }, file: { unit: "byte", verb: "có" }, array: { unit: "phần tử", verb: "có" }, set: { unit: "phần tử", verb: "có" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "đầu vào", email: "địa chỉ email", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ngày giờ ISO", date: "ngày ISO", time: "giờ ISO", duration: "khoảng thời gian ISO", ipv4: "địa chỉ IPv4", ipv6: "địa chỉ IPv6", cidrv4: "dải IPv4", cidrv6: "dải IPv6", base64: "chuỗi mã hóa base64", base64url: "chuỗi mã hóa base64url", json_string: "chuỗi JSON", e164: "số E.164", jwt: "JWT", template_literal: "đầu vào" }, t = { nan: "NaN", number: "số", array: "mảng" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Đầu vào không hợp lệ: mong đợi instanceof ${n.expected}, nhận được ${u}`; + return `Đầu vào không hợp lệ: mong đợi ${v}, nhận được ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Đầu vào không hợp lệ: mong đợi ${U(n.values[0])}`; + return `Tùy chọn không hợp lệ: mong đợi một trong các giá trị ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Quá lớn: mong đợi ${n.origin ?? "giá trị"} ${$.verb} ${v}${n.maximum.toString()} ${$.unit ?? "phần tử"}`; + return `Quá lớn: mong đợi ${n.origin ?? "giá trị"} ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Quá nhỏ: mong đợi ${n.origin} ${$.verb} ${v}${n.minimum.toString()} ${$.unit}`; + return `Quá nhỏ: mong đợi ${n.origin} ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Chuỗi không hợp lệ: phải bắt đầu bằng "${v.prefix}"`; + if (v.format === "ends_with") return `Chuỗi không hợp lệ: phải kết thúc bằng "${v.suffix}"`; + if (v.format === "includes") return `Chuỗi không hợp lệ: phải bao gồm "${v.includes}"`; + if (v.format === "regex") return `Chuỗi không hợp lệ: phải khớp với mẫu ${v.pattern}`; + return `${o[v.format] ?? n.format} không hợp lệ`; + } + case "not_multiple_of": + return `Số không hợp lệ: phải là bội số của ${n.divisor}`; + case "unrecognized_keys": + return `Khóa không được nhận dạng: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Khóa không hợp lệ trong ${n.origin}`; + case "invalid_union": + return "Đầu vào không hợp lệ"; + case "invalid_element": + return `Giá trị không hợp lệ trong ${n.origin}`; + default: + return "Đầu vào không hợp lệ"; + } + }; +}; +function L$() { + return { localeError: q4() }; +} +var Q4 = () => { + let r = { string: { unit: "字符", verb: "包含" }, file: { unit: "字节", verb: "包含" }, array: { unit: "项", verb: "包含" }, set: { unit: "项", verb: "包含" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "输入", email: "电子邮件", url: "URL", emoji: "表情符号", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO日期时间", date: "ISO日期", time: "ISO时间", duration: "ISO时长", ipv4: "IPv4地址", ipv6: "IPv6地址", cidrv4: "IPv4网段", cidrv6: "IPv6网段", base64: "base64编码字符串", base64url: "base64url编码字符串", json_string: "JSON字符串", e164: "E.164号码", jwt: "JWT", template_literal: "输入" }, t = { nan: "NaN", number: "数字", array: "数组", null: "空值(null)" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `无效输入:期望 instanceof ${n.expected},实际接收 ${u}`; + return `无效输入:期望 ${v},实际接收 ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `无效输入:期望 ${U(n.values[0])}`; + return `无效选项:期望以下之一 ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `数值过大:期望 ${n.origin ?? "值"} ${v}${n.maximum.toString()} ${$.unit ?? "个元素"}`; + return `数值过大:期望 ${n.origin ?? "值"} ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `数值过小:期望 ${n.origin} ${v}${n.minimum.toString()} ${$.unit}`; + return `数值过小:期望 ${n.origin} ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `无效字符串:必须以 "${v.prefix}" 开头`; + if (v.format === "ends_with") return `无效字符串:必须以 "${v.suffix}" 结尾`; + if (v.format === "includes") return `无效字符串:必须包含 "${v.includes}"`; + if (v.format === "regex") return `无效字符串:必须满足正则表达式 ${v.pattern}`; + return `无效${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `无效数字:必须是 ${n.divisor} 的倍数`; + case "unrecognized_keys": + return `出现未知的键(key): ${b(n.keys, ", ")}`; + case "invalid_key": + return `${n.origin} 中的键(key)无效`; + case "invalid_union": + return "无效输入"; + case "invalid_element": + return `${n.origin} 中包含无效值(value)`; + default: + return "无效输入"; + } + }; +}; +function G$() { + return { localeError: Q4() }; +} +var Y4 = () => { + let r = { string: { unit: "字元", verb: "擁有" }, file: { unit: "位元組", verb: "擁有" }, array: { unit: "項目", verb: "擁有" }, set: { unit: "項目", verb: "擁有" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "輸入", email: "郵件地址", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "ISO 日期時間", date: "ISO 日期", time: "ISO 時間", duration: "ISO 期間", ipv4: "IPv4 位址", ipv6: "IPv6 位址", cidrv4: "IPv4 範圍", cidrv6: "IPv6 範圍", base64: "base64 編碼字串", base64url: "base64url 編碼字串", json_string: "JSON 字串", e164: "E.164 數值", jwt: "JWT", template_literal: "輸入" }, t = { nan: "NaN" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `無效的輸入值:預期為 instanceof ${n.expected},但收到 ${u}`; + return `無效的輸入值:預期為 ${v},但收到 ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `無效的輸入值:預期為 ${U(n.values[0])}`; + return `無效的選項:預期為以下其中之一 ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `數值過大:預期 ${n.origin ?? "值"} 應為 ${v}${n.maximum.toString()} ${$.unit ?? "個元素"}`; + return `數值過大:預期 ${n.origin ?? "值"} 應為 ${v}${n.maximum.toString()}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `數值過小:預期 ${n.origin} 應為 ${v}${n.minimum.toString()} ${$.unit}`; + return `數值過小:預期 ${n.origin} 應為 ${v}${n.minimum.toString()}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `無效的字串:必須以 "${v.prefix}" 開頭`; + if (v.format === "ends_with") return `無效的字串:必須以 "${v.suffix}" 結尾`; + if (v.format === "includes") return `無效的字串:必須包含 "${v.includes}"`; + if (v.format === "regex") return `無效的字串:必須符合格式 ${v.pattern}`; + return `無效的 ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `無效的數字:必須為 ${n.divisor} 的倍數`; + case "unrecognized_keys": + return `無法識別的鍵值${n.keys.length > 1 ? "們" : ""}:${b(n.keys, "、")}`; + case "invalid_key": + return `${n.origin} 中有無效的鍵值`; + case "invalid_union": + return "無效的輸入值"; + case "invalid_element": + return `${n.origin} 中有無效的值`; + default: + return "無效的輸入值"; + } + }; +}; +function W$() { + return { localeError: Y4() }; +} +var F4 = () => { + let r = { string: { unit: "àmi", verb: "ní" }, file: { unit: "bytes", verb: "ní" }, array: { unit: "nkan", verb: "ní" }, set: { unit: "nkan", verb: "ní" } }; + function i(n) { + return r[n] ?? null; + } + let o = { regex: "ẹ̀rọ ìbáwọlé", email: "àdírẹ́sì ìmẹ́lì", url: "URL", emoji: "emoji", uuid: "UUID", uuidv4: "UUIDv4", uuidv6: "UUIDv6", nanoid: "nanoid", guid: "GUID", cuid: "cuid", cuid2: "cuid2", ulid: "ULID", xid: "XID", ksuid: "KSUID", datetime: "àkókò ISO", date: "ọjọ́ ISO", time: "àkókò ISO", duration: "àkókò tó pé ISO", ipv4: "àdírẹ́sì IPv4", ipv6: "àdírẹ́sì IPv6", cidrv4: "àgbègbè IPv4", cidrv6: "àgbègbè IPv6", base64: "ọ̀rọ̀ tí a kọ́ ní base64", base64url: "ọ̀rọ̀ base64url", json_string: "ọ̀rọ̀ JSON", e164: "nọ́mbà E.164", jwt: "JWT", template_literal: "ẹ̀rọ ìbáwọlé" }, t = { nan: "NaN", number: "nọ́mbà", array: "akopọ" }; + return (n) => { + switch (n.code) { + case "invalid_type": { + let v = t[n.expected] ?? n.expected, $ = k(n.input), u = t[$] ?? $; + if (/^[A-Z]/.test(n.expected)) return `Ìbáwọlé aṣìṣe: a ní láti fi instanceof ${n.expected}, àmọ̀ a rí ${u}`; + return `Ìbáwọlé aṣìṣe: a ní láti fi ${v}, àmọ̀ a rí ${u}`; + } + case "invalid_value": + if (n.values.length === 1) return `Ìbáwọlé aṣìṣe: a ní láti fi ${U(n.values[0])}`; + return `Àṣàyàn aṣìṣe: yan ọ̀kan lára ${b(n.values, "|")}`; + case "too_big": { + let v = n.inclusive ? "<=" : "<", $ = i(n.origin); + if ($) return `Tó pọ̀ jù: a ní láti jẹ́ pé ${n.origin ?? "iye"} ${$.verb} ${v}${n.maximum} ${$.unit}`; + return `Tó pọ̀ jù: a ní láti jẹ́ ${v}${n.maximum}`; + } + case "too_small": { + let v = n.inclusive ? ">=" : ">", $ = i(n.origin); + if ($) return `Kéré ju: a ní láti jẹ́ pé ${n.origin} ${$.verb} ${v}${n.minimum} ${$.unit}`; + return `Kéré ju: a ní láti jẹ́ ${v}${n.minimum}`; + } + case "invalid_format": { + let v = n; + if (v.format === "starts_with") return `Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ bẹ̀rẹ̀ pẹ̀lú "${v.prefix}"`; + if (v.format === "ends_with") return `Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ parí pẹ̀lú "${v.suffix}"`; + if (v.format === "includes") return `Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ ní "${v.includes}"`; + if (v.format === "regex") return `Ọ̀rọ̀ aṣìṣe: gbọ́dọ̀ bá àpẹẹrẹ mu ${v.pattern}`; + return `Aṣìṣe: ${o[v.format] ?? n.format}`; + } + case "not_multiple_of": + return `Nọ́mbà aṣìṣe: gbọ́dọ̀ jẹ́ èyà pípín ti ${n.divisor}`; + case "unrecognized_keys": + return `Bọtìnì àìmọ̀: ${b(n.keys, ", ")}`; + case "invalid_key": + return `Bọtìnì aṣìṣe nínú ${n.origin}`; + case "invalid_union": + return "Ìbáwọlé aṣìṣe"; + case "invalid_element": + return `Iye aṣìṣe nínú ${n.origin}`; + default: + return "Ìbáwọlé aṣìṣe"; + } + }; +}; +function V$() { + return { localeError: F4() }; +} +var vl; +var X$ = /* @__PURE__ */ Symbol("ZodOutput"); +var E$ = /* @__PURE__ */ Symbol("ZodInput"); +var A$ = class { + constructor() { + this._map = /* @__PURE__ */ new WeakMap(), this._idmap = /* @__PURE__ */ new Map(); + } + add(r, ...i) { + let o = i[0]; + if (this._map.set(r, o), o && typeof o === "object" && "id" in o) this._idmap.set(o.id, r); + return this; + } + clear() { + return this._map = /* @__PURE__ */ new WeakMap(), this._idmap = /* @__PURE__ */ new Map(), this; + } + remove(r) { + let i = this._map.get(r); + if (i && typeof i === "object" && "id" in i) this._idmap.delete(i.id); + return this._map.delete(r), this; + } + get(r) { + let i = r._zod.parent; + if (i) { + let o = { ...this.get(i) ?? {} }; + delete o.id; + let t = { ...o, ...this._map.get(r) }; + return Object.keys(t).length ? t : void 0; + } + return this._map.get(r); + } + has(r) { + return this._map.has(r); + } +}; +function ui() { + return new A$(); +} +(vl = globalThis).__zod_globalRegistry ?? (vl.__zod_globalRegistry = ui()); +var A = globalThis.__zod_globalRegistry; +function K$(r, i) { + return new r({ type: "string", ...w(i) }); +} +function q$(r, i) { + return new r({ type: "string", coerce: true, ...w(i) }); +} +function gi(r, i) { + return new r({ type: "string", format: "email", check: "string_format", abort: false, ...w(i) }); +} +function zn(r, i) { + return new r({ type: "string", format: "guid", check: "string_format", abort: false, ...w(i) }); +} +function ei(r, i) { + return new r({ type: "string", format: "uuid", check: "string_format", abort: false, ...w(i) }); +} +function li(r, i) { + return new r({ type: "string", format: "uuid", check: "string_format", abort: false, version: "v4", ...w(i) }); +} +function Ii(r, i) { + return new r({ type: "string", format: "uuid", check: "string_format", abort: false, version: "v6", ...w(i) }); +} +function ci(r, i) { + return new r({ type: "string", format: "uuid", check: "string_format", abort: false, version: "v7", ...w(i) }); +} +function Sn(r, i) { + return new r({ type: "string", format: "url", check: "string_format", abort: false, ...w(i) }); +} +function bi(r, i) { + return new r({ type: "string", format: "emoji", check: "string_format", abort: false, ...w(i) }); +} +function _i(r, i) { + return new r({ type: "string", format: "nanoid", check: "string_format", abort: false, ...w(i) }); +} +function Ui(r, i) { + return new r({ type: "string", format: "cuid", check: "string_format", abort: false, ...w(i) }); +} +function ki(r, i) { + return new r({ type: "string", format: "cuid2", check: "string_format", abort: false, ...w(i) }); +} +function Di(r, i) { + return new r({ type: "string", format: "ulid", check: "string_format", abort: false, ...w(i) }); +} +function wi(r, i) { + return new r({ type: "string", format: "xid", check: "string_format", abort: false, ...w(i) }); +} +function Ni(r, i) { + return new r({ type: "string", format: "ksuid", check: "string_format", abort: false, ...w(i) }); +} +function Oi(r, i) { + return new r({ type: "string", format: "ipv4", check: "string_format", abort: false, ...w(i) }); +} +function zi(r, i) { + return new r({ type: "string", format: "ipv6", check: "string_format", abort: false, ...w(i) }); +} +function Q$(r, i) { + return new r({ type: "string", format: "mac", check: "string_format", abort: false, ...w(i) }); +} +function Si(r, i) { + return new r({ type: "string", format: "cidrv4", check: "string_format", abort: false, ...w(i) }); +} +function Pi(r, i) { + return new r({ type: "string", format: "cidrv6", check: "string_format", abort: false, ...w(i) }); +} +function ji(r, i) { + return new r({ type: "string", format: "base64", check: "string_format", abort: false, ...w(i) }); +} +function Ji(r, i) { + return new r({ type: "string", format: "base64url", check: "string_format", abort: false, ...w(i) }); +} +function Li(r, i) { + return new r({ type: "string", format: "e164", check: "string_format", abort: false, ...w(i) }); +} +function Gi(r, i) { + return new r({ type: "string", format: "jwt", check: "string_format", abort: false, ...w(i) }); +} +var Y$ = { Any: null, Minute: -1, Second: 0, Millisecond: 3, Microsecond: 6 }; +function F$(r, i) { + return new r({ type: "string", format: "datetime", check: "string_format", offset: false, local: false, precision: null, ...w(i) }); +} +function B$(r, i) { + return new r({ type: "string", format: "date", check: "string_format", ...w(i) }); +} +function m$(r, i) { + return new r({ type: "string", format: "time", check: "string_format", precision: null, ...w(i) }); +} +function H$(r, i) { + return new r({ type: "string", format: "duration", check: "string_format", ...w(i) }); +} +function T$(r, i) { + return new r({ type: "number", checks: [], ...w(i) }); +} +function M$(r, i) { + return new r({ type: "number", coerce: true, checks: [], ...w(i) }); +} +function R$(r, i) { + return new r({ type: "number", check: "number_format", abort: false, format: "safeint", ...w(i) }); +} +function x$(r, i) { + return new r({ type: "number", check: "number_format", abort: false, format: "float32", ...w(i) }); +} +function Z$(r, i) { + return new r({ type: "number", check: "number_format", abort: false, format: "float64", ...w(i) }); +} +function d$(r, i) { + return new r({ type: "number", check: "number_format", abort: false, format: "int32", ...w(i) }); +} +function C$(r, i) { + return new r({ type: "number", check: "number_format", abort: false, format: "uint32", ...w(i) }); +} +function f$(r, i) { + return new r({ type: "boolean", ...w(i) }); +} +function y$(r, i) { + return new r({ type: "boolean", coerce: true, ...w(i) }); +} +function h$(r, i) { + return new r({ type: "bigint", ...w(i) }); +} +function a$(r, i) { + return new r({ type: "bigint", coerce: true, ...w(i) }); +} +function p$(r, i) { + return new r({ type: "bigint", check: "bigint_format", abort: false, format: "int64", ...w(i) }); +} +function s$(r, i) { + return new r({ type: "bigint", check: "bigint_format", abort: false, format: "uint64", ...w(i) }); +} +function ru(r, i) { + return new r({ type: "symbol", ...w(i) }); +} +function nu(r, i) { + return new r({ type: "undefined", ...w(i) }); +} +function iu(r, i) { + return new r({ type: "null", ...w(i) }); +} +function vu(r) { + return new r({ type: "any" }); +} +function ou(r) { + return new r({ type: "unknown" }); +} +function tu(r, i) { + return new r({ type: "never", ...w(i) }); +} +function $u(r, i) { + return new r({ type: "void", ...w(i) }); +} +function uu(r, i) { + return new r({ type: "date", ...w(i) }); +} +function gu(r, i) { + return new r({ type: "date", coerce: true, ...w(i) }); +} +function eu(r, i) { + return new r({ type: "nan", ...w(i) }); +} +function y(r, i) { + return new yn({ check: "less_than", ...w(i), value: r, inclusive: false }); +} +function M(r, i) { + return new yn({ check: "less_than", ...w(i), value: r, inclusive: true }); +} +function h(r, i) { + return new hn({ check: "greater_than", ...w(i), value: r, inclusive: false }); +} +function Q(r, i) { + return new hn({ check: "greater_than", ...w(i), value: r, inclusive: true }); +} +function Wi(r) { + return h(0, r); +} +function Vi(r) { + return y(0, r); +} +function Xi(r) { + return M(0, r); +} +function Ei(r) { + return Q(0, r); +} +function ur(r, i) { + return new go({ check: "multiple_of", ...w(i), value: r }); +} +function gr(r, i) { + return new Io({ check: "max_size", ...w(i), maximum: r }); +} +function a(r, i) { + return new co({ check: "min_size", ...w(i), minimum: r }); +} +function kr(r, i) { + return new bo({ check: "size_equals", ...w(i), size: r }); +} +function Dr(r, i) { + return new _o({ check: "max_length", ...w(i), maximum: r }); +} +function nr(r, i) { + return new Uo({ check: "min_length", ...w(i), minimum: r }); +} +function wr(r, i) { + return new ko({ check: "length_equals", ...w(i), length: r }); +} +function Er(r, i) { + return new Do({ check: "string_format", format: "regex", ...w(i), pattern: r }); +} +function Ar(r) { + return new wo({ check: "string_format", format: "lowercase", ...w(r) }); +} +function Kr(r) { + return new No({ check: "string_format", format: "uppercase", ...w(r) }); +} +function qr(r, i) { + return new Oo({ check: "string_format", format: "includes", ...w(i), includes: r }); +} +function Qr(r, i) { + return new zo({ check: "string_format", format: "starts_with", ...w(i), prefix: r }); +} +function Yr(r, i) { + return new So({ check: "string_format", format: "ends_with", ...w(i), suffix: r }); +} +function Ai(r, i, o) { + return new Po({ check: "property", property: r, schema: i, ...w(o) }); +} +function Fr(r, i) { + return new jo({ check: "mime_type", mime: r, ...w(i) }); +} +function d(r) { + return new Jo({ check: "overwrite", tx: r }); +} +function Br(r) { + return d((i) => i.normalize(r)); +} +function mr() { + return d((r) => r.trim()); +} +function Hr() { + return d((r) => r.toLowerCase()); +} +function Tr() { + return d((r) => r.toUpperCase()); +} +function Mr() { + return d((r) => Pv(r)); +} +function lu(r, i, o) { + return new r({ type: "array", element: i, ...w(o) }); +} +function m4(r, i, o) { + return new r({ type: "union", options: i, ...w(o) }); +} +function H4(r, i, o) { + return new r({ type: "union", options: i, inclusive: false, ...w(o) }); +} +function T4(r, i, o, t) { + return new r({ type: "union", options: o, discriminator: i, ...w(t) }); +} +function M4(r, i, o) { + return new r({ type: "intersection", left: i, right: o }); +} +function R4(r, i, o, t) { + let n = o instanceof S; + return new r({ type: "tuple", items: i, rest: n ? o : null, ...w(n ? t : o) }); +} +function x4(r, i, o, t) { + return new r({ type: "record", keyType: i, valueType: o, ...w(t) }); +} +function Z4(r, i, o, t) { + return new r({ type: "map", keyType: i, valueType: o, ...w(t) }); +} +function d4(r, i, o) { + return new r({ type: "set", valueType: i, ...w(o) }); +} +function C4(r, i, o) { + let t = Array.isArray(i) ? Object.fromEntries(i.map((n) => [n, n])) : i; + return new r({ type: "enum", entries: t, ...w(o) }); +} +function f4(r, i, o) { + return new r({ type: "enum", entries: i, ...w(o) }); +} +function y4(r, i, o) { + return new r({ type: "literal", values: Array.isArray(i) ? i : [i], ...w(o) }); +} +function Iu(r, i) { + return new r({ type: "file", ...w(i) }); +} +function h4(r, i) { + return new r({ type: "transform", transform: i }); +} +function a4(r, i) { + return new r({ type: "optional", innerType: i }); +} +function p4(r, i) { + return new r({ type: "nullable", innerType: i }); +} +function s4(r, i, o) { + return new r({ type: "default", innerType: i, get defaultValue() { + return typeof o === "function" ? o() : Jv(o); + } }); +} +function r6(r, i, o) { + return new r({ type: "nonoptional", innerType: i, ...w(o) }); +} +function n6(r, i) { + return new r({ type: "success", innerType: i }); +} +function i6(r, i, o) { + return new r({ type: "catch", innerType: i, catchValue: typeof o === "function" ? o : () => o }); +} +function v6(r, i, o) { + return new r({ type: "pipe", in: i, out: o }); +} +function o6(r, i) { + return new r({ type: "readonly", innerType: i }); +} +function t6(r, i, o) { + return new r({ type: "template_literal", parts: i, ...w(o) }); +} +function $6(r, i) { + return new r({ type: "lazy", getter: i }); +} +function u6(r, i) { + return new r({ type: "promise", innerType: i }); +} +function cu(r, i, o) { + let t = w(o); + return t.abort ?? (t.abort = true), new r({ type: "custom", check: "custom", fn: i, ...t }); +} +function bu(r, i, o) { + return new r({ type: "custom", check: "custom", fn: i, ...w(o) }); +} +function _u(r) { + let i = ol((o) => { + return o.addIssue = (t) => { + if (typeof t === "string") o.issues.push(jr(t, o.value, i._zod.def)); + else { + let n = t; + if (n.fatal) n.continue = false; + n.code ?? (n.code = "custom"), n.input ?? (n.input = o.value), n.inst ?? (n.inst = i), n.continue ?? (n.continue = !i._zod.def.abort), o.issues.push(jr(n)); + } + }, r(o.value, o); + }); + return i; +} +function ol(r, i) { + let o = new V({ check: "custom", ...w(i) }); + return o._zod.check = r, o; +} +function Uu(r) { + let i = new V({ check: "describe" }); + return i._zod.onattach = [(o) => { + let t = A.get(o) ?? {}; + A.add(o, { ...t, description: r }); + }], i._zod.check = () => { + }, i; +} +function ku(r) { + let i = new V({ check: "meta" }); + return i._zod.onattach = [(o) => { + let t = A.get(o) ?? {}; + A.add(o, { ...t, ...r }); + }], i._zod.check = () => { + }, i; +} +function Du(r, i) { + let o = w(i), t = o.truthy ?? ["true", "1", "yes", "on", "y", "enabled"], n = o.falsy ?? ["false", "0", "no", "off", "n", "disabled"]; + if (o.case !== "sensitive") t = t.map((O) => typeof O === "string" ? O.toLowerCase() : O), n = n.map((O) => typeof O === "string" ? O.toLowerCase() : O); + let v = new Set(t), $ = new Set(n), u = r.Codec ?? Un, l = r.Boolean ?? bn, c = new (r.String ?? Ur)({ type: "string", error: o.error }), _ = new l({ type: "boolean", error: o.error }), N = new u({ type: "pipe", in: c, out: _, transform: (O, J) => { + let X = O; + if (o.case !== "sensitive") X = X.toLowerCase(); + if (v.has(X)) return true; + else if ($.has(X)) return false; + else return J.issues.push({ code: "invalid_value", expected: "stringbool", values: [...v, ...$], input: J.value, inst: N, continue: false }), {}; + }, reverseTransform: (O, J) => { + if (O === true) return t[0] || "true"; + else return n[0] || "false"; + }, error: o.error }); + return N; +} +function Rr(r, i, o, t = {}) { + let n = w(t), v = { ...w(t), check: "string_format", type: "string", format: i, fn: typeof o === "function" ? o : (u) => o.test(u), ...n }; + if (o instanceof RegExp) v.pattern = o; + return new r(v); +} +function er(r) { + let i = r?.target ?? "draft-2020-12"; + if (i === "draft-4") i = "draft-04"; + if (i === "draft-7") i = "draft-07"; + return { processors: r.processors ?? {}, metadataRegistry: r?.metadata ?? A, target: i, unrepresentable: r?.unrepresentable ?? "throw", override: r?.override ?? (() => { + }), io: r?.io ?? "output", counter: 0, seen: /* @__PURE__ */ new Map(), cycles: r?.cycles ?? "ref", reused: r?.reused ?? "inline", external: r?.external ?? void 0 }; +} +function L(r, i, o = { path: [], schemaPath: [] }) { + var t; + let n = r._zod.def, v = i.seen.get(r); + if (v) { + if (v.count++, o.schemaPath.includes(r)) v.cycle = o.path; + return v.schema; + } + let $ = { schema: {}, count: 1, cycle: void 0, path: o.path }; + i.seen.set(r, $); + let u = r._zod.toJSONSchema?.(); + if (u) $.schema = u; + else { + let c = { ...o, schemaPath: [...o.schemaPath, r], path: o.path }; + if (r._zod.processJSONSchema) r._zod.processJSONSchema(i, $.schema, c); + else { + let N = $.schema, O = i.processors[n.type]; + if (!O) throw Error(`[toJSONSchema]: Non-representable type encountered: ${n.type}`); + O(r, i, N, c); + } + let _ = r._zod.parent; + if (_) { + if (!$.ref) $.ref = _; + L(_, i, c), i.seen.get(_).isParent = true; + } + } + let l = i.metadataRegistry.get(r); + if (l) Object.assign($.schema, l); + if (i.io === "input" && Y(r)) delete $.schema.examples, delete $.schema.default; + if (i.io === "input" && $.schema._prefault) (t = $.schema).default ?? (t.default = $.schema._prefault); + return delete $.schema._prefault, i.seen.get(r).schema; +} +function lr(r, i) { + let o = r.seen.get(i); + if (!o) throw Error("Unprocessed schema. This is a bug in Zod."); + let t = /* @__PURE__ */ new Map(); + for (let $ of r.seen.entries()) { + let u = r.metadataRegistry.get($[0])?.id; + if (u) { + let l = t.get(u); + if (l && l !== $[0]) throw Error(`Duplicate schema id "${u}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`); + t.set(u, $[0]); + } + } + let n = ($) => { + let u = r.target === "draft-2020-12" ? "$defs" : "definitions"; + if (r.external) { + let _ = r.external.registry.get($[0])?.id, N = r.external.uri ?? ((J) => J); + if (_) return { ref: N(_) }; + let O = $[1].defId ?? $[1].schema.id ?? `schema${r.counter++}`; + return $[1].defId = O, { defId: O, ref: `${N("__shared")}#/${u}/${O}` }; + } + if ($[1] === o) return { ref: "#" }; + let e = `${"#"}/${u}/`, c = $[1].schema.id ?? `__schema${r.counter++}`; + return { defId: c, ref: e + c }; + }, v = ($) => { + if ($[1].schema.$ref) return; + let u = $[1], { ref: l, defId: e } = n($); + if (u.def = { ...u.schema }, e) u.defId = e; + let c = u.schema; + for (let _ in c) delete c[_]; + c.$ref = l; + }; + if (r.cycles === "throw") for (let $ of r.seen.entries()) { + let u = $[1]; + if (u.cycle) throw Error(`Cycle detected: #/${u.cycle?.join("/")}/ + +Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`); + } + for (let $ of r.seen.entries()) { + let u = $[1]; + if (i === $[0]) { + v($); + continue; + } + if (r.external) { + let e = r.external.registry.get($[0])?.id; + if (i !== $[0] && e) { + v($); + continue; + } + } + if (r.metadataRegistry.get($[0])?.id) { + v($); + continue; + } + if (u.cycle) { + v($); + continue; + } + if (u.count > 1) { + if (r.reused === "ref") { + v($); + continue; + } + } + } +} +function Ir(r, i) { + let o = r.seen.get(i); + if (!o) throw Error("Unprocessed schema. This is a bug in Zod."); + let t = ($) => { + let u = r.seen.get($); + if (u.ref === null) return; + let l = u.def ?? u.schema, e = { ...l }, c = u.ref; + if (u.ref = null, c) { + t(c); + let N = r.seen.get(c), O = N.schema; + if (O.$ref && (r.target === "draft-07" || r.target === "draft-04" || r.target === "openapi-3.0")) l.allOf = l.allOf ?? [], l.allOf.push(O); + else Object.assign(l, O); + if (Object.assign(l, e), $._zod.parent === c) for (let X in l) { + if (X === "$ref" || X === "allOf") continue; + if (!(X in e)) delete l[X]; + } + if (O.$ref) for (let X in l) { + if (X === "$ref" || X === "allOf") continue; + if (X in N.def && JSON.stringify(l[X]) === JSON.stringify(N.def[X])) delete l[X]; + } + } + let _ = $._zod.parent; + if (_ && _ !== c) { + t(_); + let N = r.seen.get(_); + if (N?.schema.$ref) { + if (l.$ref = N.schema.$ref, N.def) for (let O in l) { + if (O === "$ref" || O === "allOf") continue; + if (O in N.def && JSON.stringify(l[O]) === JSON.stringify(N.def[O])) delete l[O]; + } + } + } + r.override({ zodSchema: $, jsonSchema: l, path: u.path ?? [] }); + }; + for (let $ of [...r.seen.entries()].reverse()) t($[0]); + let n = {}; + if (r.target === "draft-2020-12") n.$schema = "https://json-schema.org/draft/2020-12/schema"; + else if (r.target === "draft-07") n.$schema = "http://json-schema.org/draft-07/schema#"; + else if (r.target === "draft-04") n.$schema = "http://json-schema.org/draft-04/schema#"; + else if (r.target === "openapi-3.0") ; + if (r.external?.uri) { + let $ = r.external.registry.get(i)?.id; + if (!$) throw Error("Schema is missing an `id` property"); + n.$id = r.external.uri($); + } + Object.assign(n, o.def ?? o.schema); + let v = r.external?.defs ?? {}; + for (let $ of r.seen.entries()) { + let u = $[1]; + if (u.def && u.defId) v[u.defId] = u.def; + } + if (r.external) ; + else if (Object.keys(v).length > 0) if (r.target === "draft-2020-12") n.$defs = v; + else n.definitions = v; + try { + let $ = JSON.parse(JSON.stringify(n)); + return Object.defineProperty($, "~standard", { value: { ...i["~standard"], jsonSchema: { input: xr(i, "input", r.processors), output: xr(i, "output", r.processors) } }, enumerable: false, writable: false }), $; + } catch ($) { + throw Error("Error converting schema to JSON."); + } +} +function Y(r, i) { + let o = i ?? { seen: /* @__PURE__ */ new Set() }; + if (o.seen.has(r)) return false; + o.seen.add(r); + let t = r._zod.def; + if (t.type === "transform") return true; + if (t.type === "array") return Y(t.element, o); + if (t.type === "set") return Y(t.valueType, o); + if (t.type === "lazy") return Y(t.getter(), o); + if (t.type === "promise" || t.type === "optional" || t.type === "nonoptional" || t.type === "nullable" || t.type === "readonly" || t.type === "default" || t.type === "prefault") return Y(t.innerType, o); + if (t.type === "intersection") return Y(t.left, o) || Y(t.right, o); + if (t.type === "record" || t.type === "map") return Y(t.keyType, o) || Y(t.valueType, o); + if (t.type === "pipe") return Y(t.in, o) || Y(t.out, o); + if (t.type === "object") { + for (let n in t.shape) if (Y(t.shape[n], o)) return true; + return false; + } + if (t.type === "union") { + for (let n of t.options) if (Y(n, o)) return true; + return false; + } + if (t.type === "tuple") { + for (let n of t.items) if (Y(n, o)) return true; + if (t.rest && Y(t.rest, o)) return true; + return false; + } + return false; +} +var wu = (r, i = {}) => (o) => { + let t = er({ ...o, processors: i }); + return L(r, t), lr(t, r), Ir(t, r); +}; +var xr = (r, i, o = {}) => (t) => { + let { libraryOptions: n, target: v } = t ?? {}, $ = er({ ...n ?? {}, target: v, io: i, processors: o }); + return L(r, $), lr($, r), Ir($, r); +}; +var g6 = { guid: "uuid", url: "uri", datetime: "date-time", json_string: "json-string", regex: "" }; +var Nu = (r, i, o, t) => { + let n = o; + n.type = "string"; + let { minimum: v, maximum: $, format: u, patterns: l, contentEncoding: e } = r._zod.bag; + if (typeof v === "number") n.minLength = v; + if (typeof $ === "number") n.maxLength = $; + if (u) { + if (n.format = g6[u] ?? u, n.format === "") delete n.format; + if (u === "time") delete n.format; + } + if (e) n.contentEncoding = e; + if (l && l.size > 0) { + let c = [...l]; + if (c.length === 1) n.pattern = c[0].source; + else if (c.length > 1) n.allOf = [...c.map((_) => ({ ...i.target === "draft-07" || i.target === "draft-04" || i.target === "openapi-3.0" ? { type: "string" } : {}, pattern: _.source }))]; + } +}; +var Ou = (r, i, o, t) => { + let n = o, { minimum: v, maximum: $, format: u, multipleOf: l, exclusiveMaximum: e, exclusiveMinimum: c } = r._zod.bag; + if (typeof u === "string" && u.includes("int")) n.type = "integer"; + else n.type = "number"; + if (typeof c === "number") if (i.target === "draft-04" || i.target === "openapi-3.0") n.minimum = c, n.exclusiveMinimum = true; + else n.exclusiveMinimum = c; + if (typeof v === "number") { + if (n.minimum = v, typeof c === "number" && i.target !== "draft-04") if (c >= v) delete n.minimum; + else delete n.exclusiveMinimum; + } + if (typeof e === "number") if (i.target === "draft-04" || i.target === "openapi-3.0") n.maximum = e, n.exclusiveMaximum = true; + else n.exclusiveMaximum = e; + if (typeof $ === "number") { + if (n.maximum = $, typeof e === "number" && i.target !== "draft-04") if (e <= $) delete n.maximum; + else delete n.exclusiveMaximum; + } + if (typeof l === "number") n.multipleOf = l; +}; +var zu = (r, i, o, t) => { + o.type = "boolean"; +}; +var Su = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("BigInt cannot be represented in JSON Schema"); +}; +var Pu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Symbols cannot be represented in JSON Schema"); +}; +var ju = (r, i, o, t) => { + if (i.target === "openapi-3.0") o.type = "string", o.nullable = true, o.enum = [null]; + else o.type = "null"; +}; +var Ju = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Undefined cannot be represented in JSON Schema"); +}; +var Lu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Void cannot be represented in JSON Schema"); +}; +var Gu = (r, i, o, t) => { + o.not = {}; +}; +var Wu = (r, i, o, t) => { +}; +var Vu = (r, i, o, t) => { +}; +var Xu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Date cannot be represented in JSON Schema"); +}; +var Eu = (r, i, o, t) => { + let n = r._zod.def, v = nn(n.entries); + if (v.every(($) => typeof $ === "number")) o.type = "number"; + if (v.every(($) => typeof $ === "string")) o.type = "string"; + o.enum = v; +}; +var Au = (r, i, o, t) => { + let n = r._zod.def, v = []; + for (let $ of n.values) if ($ === void 0) { + if (i.unrepresentable === "throw") throw Error("Literal `undefined` cannot be represented in JSON Schema"); + } else if (typeof $ === "bigint") if (i.unrepresentable === "throw") throw Error("BigInt literals cannot be represented in JSON Schema"); + else v.push(Number($)); + else v.push($); + if (v.length === 0) ; + else if (v.length === 1) { + let $ = v[0]; + if (o.type = $ === null ? "null" : typeof $, i.target === "draft-04" || i.target === "openapi-3.0") o.enum = [$]; + else o.const = $; + } else { + if (v.every(($) => typeof $ === "number")) o.type = "number"; + if (v.every(($) => typeof $ === "string")) o.type = "string"; + if (v.every(($) => typeof $ === "boolean")) o.type = "boolean"; + if (v.every(($) => $ === null)) o.type = "null"; + o.enum = v; + } +}; +var Ku = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("NaN cannot be represented in JSON Schema"); +}; +var qu = (r, i, o, t) => { + let n = o, v = r._zod.pattern; + if (!v) throw Error("Pattern not found in template literal"); + n.type = "string", n.pattern = v.source; +}; +var Qu = (r, i, o, t) => { + let n = o, v = { type: "string", format: "binary", contentEncoding: "binary" }, { minimum: $, maximum: u, mime: l } = r._zod.bag; + if ($ !== void 0) v.minLength = $; + if (u !== void 0) v.maxLength = u; + if (l) if (l.length === 1) v.contentMediaType = l[0], Object.assign(n, v); + else Object.assign(n, v), n.anyOf = l.map((e) => ({ contentMediaType: e })); + else Object.assign(n, v); +}; +var Yu = (r, i, o, t) => { + o.type = "boolean"; +}; +var Fu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Custom types cannot be represented in JSON Schema"); +}; +var Bu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Function types cannot be represented in JSON Schema"); +}; +var mu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Transforms cannot be represented in JSON Schema"); +}; +var Hu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Map cannot be represented in JSON Schema"); +}; +var Tu = (r, i, o, t) => { + if (i.unrepresentable === "throw") throw Error("Set cannot be represented in JSON Schema"); +}; +var Mu = (r, i, o, t) => { + let n = o, v = r._zod.def, { minimum: $, maximum: u } = r._zod.bag; + if (typeof $ === "number") n.minItems = $; + if (typeof u === "number") n.maxItems = u; + n.type = "array", n.items = L(v.element, i, { ...t, path: [...t.path, "items"] }); +}; +var Ru = (r, i, o, t) => { + let n = o, v = r._zod.def; + n.type = "object", n.properties = {}; + let $ = v.shape; + for (let e in $) n.properties[e] = L($[e], i, { ...t, path: [...t.path, "properties", e] }); + let u = new Set(Object.keys($)), l = new Set([...u].filter((e) => { + let c = v.shape[e]._zod; + if (i.io === "input") return c.optin === void 0; + else return c.optout === void 0; + })); + if (l.size > 0) n.required = Array.from(l); + if (v.catchall?._zod.def.type === "never") n.additionalProperties = false; + else if (!v.catchall) { + if (i.io === "output") n.additionalProperties = false; + } else if (v.catchall) n.additionalProperties = L(v.catchall, i, { ...t, path: [...t.path, "additionalProperties"] }); +}; +var qi = (r, i, o, t) => { + let n = r._zod.def, v = n.inclusive === false, $ = n.options.map((u, l) => L(u, i, { ...t, path: [...t.path, v ? "oneOf" : "anyOf", l] })); + if (v) o.oneOf = $; + else o.anyOf = $; +}; +var xu = (r, i, o, t) => { + let n = r._zod.def, v = L(n.left, i, { ...t, path: [...t.path, "allOf", 0] }), $ = L(n.right, i, { ...t, path: [...t.path, "allOf", 1] }), u = (e) => "allOf" in e && Object.keys(e).length === 1, l = [...u(v) ? v.allOf : [v], ...u($) ? $.allOf : [$]]; + o.allOf = l; +}; +var Zu = (r, i, o, t) => { + let n = o, v = r._zod.def; + n.type = "array"; + let $ = i.target === "draft-2020-12" ? "prefixItems" : "items", u = i.target === "draft-2020-12" ? "items" : i.target === "openapi-3.0" ? "items" : "additionalItems", l = v.items.map((N, O) => L(N, i, { ...t, path: [...t.path, $, O] })), e = v.rest ? L(v.rest, i, { ...t, path: [...t.path, u, ...i.target === "openapi-3.0" ? [v.items.length] : []] }) : null; + if (i.target === "draft-2020-12") { + if (n.prefixItems = l, e) n.items = e; + } else if (i.target === "openapi-3.0") { + if (n.items = { anyOf: l }, e) n.items.anyOf.push(e); + if (n.minItems = l.length, !e) n.maxItems = l.length; + } else if (n.items = l, e) n.additionalItems = e; + let { minimum: c, maximum: _ } = r._zod.bag; + if (typeof c === "number") n.minItems = c; + if (typeof _ === "number") n.maxItems = _; +}; +var du = (r, i, o, t) => { + let n = o, v = r._zod.def; + n.type = "object"; + let $ = v.keyType, l = $._zod.bag?.patterns; + if (v.mode === "loose" && l && l.size > 0) { + let c = L(v.valueType, i, { ...t, path: [...t.path, "patternProperties", "*"] }); + n.patternProperties = {}; + for (let _ of l) n.patternProperties[_.source] = c; + } else { + if (i.target === "draft-07" || i.target === "draft-2020-12") n.propertyNames = L(v.keyType, i, { ...t, path: [...t.path, "propertyNames"] }); + n.additionalProperties = L(v.valueType, i, { ...t, path: [...t.path, "additionalProperties"] }); + } + let e = $._zod.values; + if (e) { + let c = [...e].filter((_) => typeof _ === "string" || typeof _ === "number"); + if (c.length > 0) n.required = c; + } +}; +var Cu = (r, i, o, t) => { + let n = r._zod.def, v = L(n.innerType, i, t), $ = i.seen.get(r); + if (i.target === "openapi-3.0") $.ref = n.innerType, o.nullable = true; + else o.anyOf = [v, { type: "null" }]; +}; +var fu = (r, i, o, t) => { + let n = r._zod.def; + L(n.innerType, i, t); + let v = i.seen.get(r); + v.ref = n.innerType; +}; +var yu = (r, i, o, t) => { + let n = r._zod.def; + L(n.innerType, i, t); + let v = i.seen.get(r); + v.ref = n.innerType, o.default = JSON.parse(JSON.stringify(n.defaultValue)); +}; +var hu = (r, i, o, t) => { + let n = r._zod.def; + L(n.innerType, i, t); + let v = i.seen.get(r); + if (v.ref = n.innerType, i.io === "input") o._prefault = JSON.parse(JSON.stringify(n.defaultValue)); +}; +var au = (r, i, o, t) => { + let n = r._zod.def; + L(n.innerType, i, t); + let v = i.seen.get(r); + v.ref = n.innerType; + let $; + try { + $ = n.catchValue(void 0); + } catch { + throw Error("Dynamic catch values are not supported in JSON Schema"); + } + o.default = $; +}; +var pu = (r, i, o, t) => { + let n = r._zod.def, v = i.io === "input" ? n.in._zod.def.type === "transform" ? n.out : n.in : n.out; + L(v, i, t); + let $ = i.seen.get(r); + $.ref = v; +}; +var su = (r, i, o, t) => { + let n = r._zod.def; + L(n.innerType, i, t); + let v = i.seen.get(r); + v.ref = n.innerType, o.readOnly = true; +}; +var rg = (r, i, o, t) => { + let n = r._zod.def; + L(n.innerType, i, t); + let v = i.seen.get(r); + v.ref = n.innerType; +}; +var Qi = (r, i, o, t) => { + let n = r._zod.def; + L(n.innerType, i, t); + let v = i.seen.get(r); + v.ref = n.innerType; +}; +var ng = (r, i, o, t) => { + let n = r._zod.innerType; + L(n, i, t); + let v = i.seen.get(r); + v.ref = n; +}; +var Ki = { string: Nu, number: Ou, boolean: zu, bigint: Su, symbol: Pu, null: ju, undefined: Ju, void: Lu, never: Gu, any: Wu, unknown: Vu, date: Xu, enum: Eu, literal: Au, nan: Ku, template_literal: qu, file: Qu, success: Yu, custom: Fu, function: Bu, transform: mu, map: Hu, set: Tu, array: Mu, object: Ru, union: qi, intersection: xu, tuple: Zu, record: du, nullable: Cu, nonoptional: fu, default: yu, prefault: hu, catch: au, pipe: pu, readonly: su, promise: rg, optional: Qi, lazy: ng }; +function Yi(r, i) { + if ("_idmap" in r) { + let t = r, n = er({ ...i, processors: Ki }), v = {}; + for (let l of t._idmap.entries()) { + let [e, c] = l; + L(c, n); + } + let $ = {}, u = { registry: t, uri: i?.uri, defs: v }; + n.external = u; + for (let l of t._idmap.entries()) { + let [e, c] = l; + lr(n, c), $[e] = Ir(n, c); + } + if (Object.keys(v).length > 0) { + let l = n.target === "draft-2020-12" ? "$defs" : "definitions"; + $.__shared = { [l]: v }; + } + return { schemas: $ }; + } + let o = er({ ...i, processors: Ki }); + return L(r, o), lr(o, r), Ir(o, r); +} +var ig = class { + get metadataRegistry() { + return this.ctx.metadataRegistry; + } + get target() { + return this.ctx.target; + } + get unrepresentable() { + return this.ctx.unrepresentable; + } + get override() { + return this.ctx.override; + } + get io() { + return this.ctx.io; + } + get counter() { + return this.ctx.counter; + } + set counter(r) { + this.ctx.counter = r; + } + get seen() { + return this.ctx.seen; + } + constructor(r) { + let i = r?.target ?? "draft-2020-12"; + if (i === "draft-4") i = "draft-04"; + if (i === "draft-7") i = "draft-07"; + this.ctx = er({ processors: Ki, target: i, ...r?.metadata && { metadata: r.metadata }, ...r?.unrepresentable && { unrepresentable: r.unrepresentable }, ...r?.override && { override: r.override }, ...r?.io && { io: r.io } }); + } + process(r, i = { path: [], schemaPath: [] }) { + return L(r, this.ctx, i); + } + emit(r, i) { + if (i) { + if (i.cycles) this.ctx.cycles = i.cycles; + if (i.reused) this.ctx.reused = i.reused; + if (i.external) this.ctx.external = i.external; + } + lr(this.ctx, r); + let o = Ir(this.ctx, r), { "~standard": t, ...n } = o; + return n; + } +}; +var tl = {}; +var Pn = {}; +s(Pn, { xor: () => al, xid: () => Ol, void: () => Zl, uuidv7: () => cl, uuidv6: () => Il, uuidv4: () => ll, uuid: () => el, url: () => bl, unknown: () => Nr, union: () => ev, undefined: () => Rl, ulid: () => Nl, uint64: () => Tl, uint32: () => Bl, tuple: () => Yg, transform: () => Iv, templateLiteral: () => lI, symbol: () => Ml, superRefine: () => ee, success: () => uI, stringbool: () => wI, stringFormat: () => El, string: () => Mi, strictObject: () => yl, set: () => iI, refine: () => ge, record: () => Fg, readonly: () => ie, promise: () => II, preprocess: () => OI, prefault: () => yg, pipe: () => Gn, partialRecord: () => sl, optional: () => Jn, object: () => fl, number: () => Og, nullish: () => $I, nullable: () => Ln, null: () => Jg, nonoptional: () => hg, never: () => gv, nativeEnum: () => vI, nanoid: () => kl, nan: () => gI, meta: () => kI, map: () => nI, mac: () => Pl, looseRecord: () => rI, looseObject: () => hl, literal: () => oI, lazy: () => te, ksuid: () => zl, keyof: () => Cl, jwt: () => Xl, json: () => NI, ipv6: () => jl, ipv4: () => Sl, intersection: () => qg, int64: () => Hl, int32: () => Fl, int: () => Ri, instanceof: () => DI, httpUrl: () => _l, hostname: () => Al, hex: () => Kl, hash: () => ql, guid: () => gl, function: () => cI, float64: () => Yl, float32: () => Ql, file: () => tI, exactOptional: () => xg, enum: () => lv, emoji: () => Ul, email: () => ul, e164: () => Vl, discriminatedUnion: () => pl, describe: () => UI, date: () => dl, custom: () => _I, cuid2: () => wl, cuid: () => Dl, codec: () => eI, cidrv6: () => Ll, cidrv4: () => Jl, check: () => bI, catch: () => sg, boolean: () => zg, bigint: () => ml, base64url: () => Wl, base64: () => Gl, array: () => Xn, any: () => xl, _function: () => cI, _default: () => Cg, _ZodString: () => xi, ZodXor: () => Eg, ZodXID: () => ai, ZodVoid: () => Vg, ZodUnknown: () => Gg, ZodUnion: () => An, ZodUndefined: () => Pg, ZodUUID: () => p, ZodURL: () => Wn, ZodULID: () => hi, ZodType: () => P, ZodTuple: () => Qg, ZodTransform: () => Mg, ZodTemplateLiteral: () => ve, ZodSymbol: () => Sg, ZodSuccess: () => ag, ZodStringFormat: () => W, ZodString: () => Cr, ZodSet: () => mg, ZodRecord: () => Kn, ZodReadonly: () => ne, ZodPromise: () => $e, ZodPrefault: () => fg, ZodPipe: () => _v, ZodOptional: () => cv, ZodObject: () => En, ZodNumberFormat: () => Or, ZodNumber: () => yr, ZodNullable: () => Zg, ZodNull: () => jg, ZodNonOptional: () => bv, ZodNever: () => Wg, ZodNanoID: () => Ci, ZodNaN: () => re, ZodMap: () => Bg, ZodMAC: () => Ng, ZodLiteral: () => Hg, ZodLazy: () => oe, ZodKSUID: () => pi, ZodJWT: () => $v, ZodIntersection: () => Kg, ZodIPv6: () => rv, ZodIPv4: () => si, ZodGUID: () => jn, ZodFunction: () => ue, ZodFile: () => Tg, ZodExactOptional: () => Rg, ZodEnum: () => dr, ZodEmoji: () => di, ZodEmail: () => Zi, ZodE164: () => tv, ZodDiscriminatedUnion: () => Ag, ZodDefault: () => dg, ZodDate: () => Vn, ZodCustomStringFormat: () => fr, ZodCustom: () => qn, ZodCodec: () => Uv, ZodCatch: () => pg, ZodCUID2: () => yi, ZodCUID: () => fi, ZodCIDRv6: () => iv, ZodCIDRv4: () => nv, ZodBoolean: () => hr, ZodBigIntFormat: () => uv, ZodBigInt: () => ar, ZodBase64URL: () => ov, ZodBase64: () => vv, ZodArray: () => Xg, ZodAny: () => Lg }); +var Fi = {}; +s(Fi, { uppercase: () => Kr, trim: () => mr, toUpperCase: () => Tr, toLowerCase: () => Hr, startsWith: () => Qr, slugify: () => Mr, size: () => kr, regex: () => Er, property: () => Ai, positive: () => Wi, overwrite: () => d, normalize: () => Br, nonpositive: () => Xi, nonnegative: () => Ei, negative: () => Vi, multipleOf: () => ur, minSize: () => a, minLength: () => nr, mime: () => Fr, maxSize: () => gr, maxLength: () => Dr, lte: () => M, lt: () => y, lowercase: () => Ar, length: () => wr, includes: () => qr, gte: () => Q, gt: () => h, endsWith: () => Yr }); +var Zr = {}; +s(Zr, { time: () => tg, duration: () => $g, datetime: () => vg, date: () => og, ZodISOTime: () => Hi, ZodISODuration: () => Ti, ZodISODateTime: () => Bi, ZodISODate: () => mi }); +var Bi = I("ZodISODateTime", (r, i) => { + mo.init(r, i), W.init(r, i); +}); +function vg(r) { + return F$(Bi, r); +} +var mi = I("ZodISODate", (r, i) => { + Ho.init(r, i), W.init(r, i); +}); +function og(r) { + return B$(mi, r); +} +var Hi = I("ZodISOTime", (r, i) => { + To.init(r, i), W.init(r, i); +}); +function tg(r) { + return m$(Hi, r); +} +var Ti = I("ZodISODuration", (r, i) => { + Mo.init(r, i), W.init(r, i); +}); +function $g(r) { + return H$(Ti, r); +} +var $l = (r, i) => { + un.init(r, i), r.name = "ZodError", Object.defineProperties(r, { format: { value: (o) => en(r, o) }, flatten: { value: (o) => gn(r, o) }, addIssue: { value: (o) => { + r.issues.push(o), r.message = JSON.stringify(r.issues, Sr, 2); + } }, addIssues: { value: (o) => { + r.issues.push(...o), r.message = JSON.stringify(r.issues, Sr, 2); + } }, isEmpty: { get() { + return r.issues.length === 0; + } } }); +}; +var l6 = I("ZodError", $l); +var H = I("ZodError", $l, { Parent: Error }); +var ug = Jr(H); +var gg = Lr(H); +var eg = Gr(H); +var lg = Wr(H); +var Ig = Hn(H); +var cg = Tn(H); +var bg = Mn(H); +var _g = Rn(H); +var Ug = xn(H); +var kg = Zn(H); +var Dg = dn(H); +var wg = Cn(H); +var P = I("ZodType", (r, i) => { + return S.init(r, i), Object.assign(r["~standard"], { jsonSchema: { input: xr(r, "input"), output: xr(r, "output") } }), r.toJSONSchema = wu(r, {}), r.def = i, r.type = i.type, Object.defineProperty(r, "_def", { value: i }), r.check = (...o) => { + return r.clone(D.mergeDefs(i, { checks: [...i.checks ?? [], ...o.map((t) => typeof t === "function" ? { _zod: { check: t, def: { check: "custom" }, onattach: [] } } : t)] }), { parent: true }); + }, r.with = r.check, r.clone = (o, t) => q(r, o, t), r.brand = () => r, r.register = (o, t) => { + return o.add(r, t), r; + }, r.parse = (o, t) => ug(r, o, t, { callee: r.parse }), r.safeParse = (o, t) => eg(r, o, t), r.parseAsync = async (o, t) => gg(r, o, t, { callee: r.parseAsync }), r.safeParseAsync = async (o, t) => lg(r, o, t), r.spa = r.safeParseAsync, r.encode = (o, t) => Ig(r, o, t), r.decode = (o, t) => cg(r, o, t), r.encodeAsync = async (o, t) => bg(r, o, t), r.decodeAsync = async (o, t) => _g(r, o, t), r.safeEncode = (o, t) => Ug(r, o, t), r.safeDecode = (o, t) => kg(r, o, t), r.safeEncodeAsync = async (o, t) => Dg(r, o, t), r.safeDecodeAsync = async (o, t) => wg(r, o, t), r.refine = (o, t) => r.check(ge(o, t)), r.superRefine = (o) => r.check(ee(o)), r.overwrite = (o) => r.check(d(o)), r.optional = () => Jn(r), r.exactOptional = () => xg(r), r.nullable = () => Ln(r), r.nullish = () => Jn(Ln(r)), r.nonoptional = (o) => hg(r, o), r.array = () => Xn(r), r.or = (o) => ev([r, o]), r.and = (o) => qg(r, o), r.transform = (o) => Gn(r, Iv(o)), r.default = (o) => Cg(r, o), r.prefault = (o) => yg(r, o), r.catch = (o) => sg(r, o), r.pipe = (o) => Gn(r, o), r.readonly = () => ie(r), r.describe = (o) => { + let t = r.clone(); + return A.add(t, { description: o }), t; + }, Object.defineProperty(r, "description", { get() { + return A.get(r)?.description; + }, configurable: true }), r.meta = (...o) => { + if (o.length === 0) return A.get(r); + let t = r.clone(); + return A.add(t, o[0]), t; + }, r.isOptional = () => r.safeParse(void 0).success, r.isNullable = () => r.safeParse(null).success, r.apply = (o) => o(r), r; +}); +var xi = I("_ZodString", (r, i) => { + Ur.init(r, i), P.init(r, i), r._zod.processJSONSchema = (t, n, v) => Nu(r, t, n, v); + let o = r._zod.bag; + r.format = o.format ?? null, r.minLength = o.minimum ?? null, r.maxLength = o.maximum ?? null, r.regex = (...t) => r.check(Er(...t)), r.includes = (...t) => r.check(qr(...t)), r.startsWith = (...t) => r.check(Qr(...t)), r.endsWith = (...t) => r.check(Yr(...t)), r.min = (...t) => r.check(nr(...t)), r.max = (...t) => r.check(Dr(...t)), r.length = (...t) => r.check(wr(...t)), r.nonempty = (...t) => r.check(nr(1, ...t)), r.lowercase = (t) => r.check(Ar(t)), r.uppercase = (t) => r.check(Kr(t)), r.trim = () => r.check(mr()), r.normalize = (...t) => r.check(Br(...t)), r.toLowerCase = () => r.check(Hr()), r.toUpperCase = () => r.check(Tr()), r.slugify = () => r.check(Mr()); +}); +var Cr = I("ZodString", (r, i) => { + Ur.init(r, i), xi.init(r, i), r.email = (o) => r.check(gi(Zi, o)), r.url = (o) => r.check(Sn(Wn, o)), r.jwt = (o) => r.check(Gi($v, o)), r.emoji = (o) => r.check(bi(di, o)), r.guid = (o) => r.check(zn(jn, o)), r.uuid = (o) => r.check(ei(p, o)), r.uuidv4 = (o) => r.check(li(p, o)), r.uuidv6 = (o) => r.check(Ii(p, o)), r.uuidv7 = (o) => r.check(ci(p, o)), r.nanoid = (o) => r.check(_i(Ci, o)), r.guid = (o) => r.check(zn(jn, o)), r.cuid = (o) => r.check(Ui(fi, o)), r.cuid2 = (o) => r.check(ki(yi, o)), r.ulid = (o) => r.check(Di(hi, o)), r.base64 = (o) => r.check(ji(vv, o)), r.base64url = (o) => r.check(Ji(ov, o)), r.xid = (o) => r.check(wi(ai, o)), r.ksuid = (o) => r.check(Ni(pi, o)), r.ipv4 = (o) => r.check(Oi(si, o)), r.ipv6 = (o) => r.check(zi(rv, o)), r.cidrv4 = (o) => r.check(Si(nv, o)), r.cidrv6 = (o) => r.check(Pi(iv, o)), r.e164 = (o) => r.check(Li(tv, o)), r.datetime = (o) => r.check(vg(o)), r.date = (o) => r.check(og(o)), r.time = (o) => r.check(tg(o)), r.duration = (o) => r.check($g(o)); +}); +function Mi(r) { + return K$(Cr, r); +} +var W = I("ZodStringFormat", (r, i) => { + G.init(r, i), xi.init(r, i); +}); +var Zi = I("ZodEmail", (r, i) => { + Xo.init(r, i), W.init(r, i); +}); +function ul(r) { + return gi(Zi, r); +} +var jn = I("ZodGUID", (r, i) => { + Wo.init(r, i), W.init(r, i); +}); +function gl(r) { + return zn(jn, r); +} +var p = I("ZodUUID", (r, i) => { + Vo.init(r, i), W.init(r, i); +}); +function el(r) { + return ei(p, r); +} +function ll(r) { + return li(p, r); +} +function Il(r) { + return Ii(p, r); +} +function cl(r) { + return ci(p, r); +} +var Wn = I("ZodURL", (r, i) => { + Eo.init(r, i), W.init(r, i); +}); +function bl(r) { + return Sn(Wn, r); +} +function _l(r) { + return Sn(Wn, { protocol: /^https?$/, hostname: x.domain, ...D.normalizeParams(r) }); +} +var di = I("ZodEmoji", (r, i) => { + Ao.init(r, i), W.init(r, i); +}); +function Ul(r) { + return bi(di, r); +} +var Ci = I("ZodNanoID", (r, i) => { + Ko.init(r, i), W.init(r, i); +}); +function kl(r) { + return _i(Ci, r); +} +var fi = I("ZodCUID", (r, i) => { + qo.init(r, i), W.init(r, i); +}); +function Dl(r) { + return Ui(fi, r); +} +var yi = I("ZodCUID2", (r, i) => { + Qo.init(r, i), W.init(r, i); +}); +function wl(r) { + return ki(yi, r); +} +var hi = I("ZodULID", (r, i) => { + Yo.init(r, i), W.init(r, i); +}); +function Nl(r) { + return Di(hi, r); +} +var ai = I("ZodXID", (r, i) => { + Fo.init(r, i), W.init(r, i); +}); +function Ol(r) { + return wi(ai, r); +} +var pi = I("ZodKSUID", (r, i) => { + Bo.init(r, i), W.init(r, i); +}); +function zl(r) { + return Ni(pi, r); +} +var si = I("ZodIPv4", (r, i) => { + Ro.init(r, i), W.init(r, i); +}); +function Sl(r) { + return Oi(si, r); +} +var Ng = I("ZodMAC", (r, i) => { + Zo.init(r, i), W.init(r, i); +}); +function Pl(r) { + return Q$(Ng, r); +} +var rv = I("ZodIPv6", (r, i) => { + xo.init(r, i), W.init(r, i); +}); +function jl(r) { + return zi(rv, r); +} +var nv = I("ZodCIDRv4", (r, i) => { + Co.init(r, i), W.init(r, i); +}); +function Jl(r) { + return Si(nv, r); +} +var iv = I("ZodCIDRv6", (r, i) => { + fo.init(r, i), W.init(r, i); +}); +function Ll(r) { + return Pi(iv, r); +} +var vv = I("ZodBase64", (r, i) => { + ho.init(r, i), W.init(r, i); +}); +function Gl(r) { + return ji(vv, r); +} +var ov = I("ZodBase64URL", (r, i) => { + ao.init(r, i), W.init(r, i); +}); +function Wl(r) { + return Ji(ov, r); +} +var tv = I("ZodE164", (r, i) => { + po.init(r, i), W.init(r, i); +}); +function Vl(r) { + return Li(tv, r); +} +var $v = I("ZodJWT", (r, i) => { + so.init(r, i), W.init(r, i); +}); +function Xl(r) { + return Gi($v, r); +} +var fr = I("ZodCustomStringFormat", (r, i) => { + rt.init(r, i), W.init(r, i); +}); +function El(r, i, o = {}) { + return Rr(fr, r, i, o); +} +function Al(r) { + return Rr(fr, "hostname", x.hostname, r); +} +function Kl(r) { + return Rr(fr, "hex", x.hex, r); +} +function ql(r, i) { + let o = i?.enc ?? "hex", t = `${r}_${o}`, n = x[t]; + if (!n) throw Error(`Unrecognized hash format: ${t}`); + return Rr(fr, t, n, i); +} +var yr = I("ZodNumber", (r, i) => { + vi.init(r, i), P.init(r, i), r._zod.processJSONSchema = (t, n, v) => Ou(r, t, n, v), r.gt = (t, n) => r.check(h(t, n)), r.gte = (t, n) => r.check(Q(t, n)), r.min = (t, n) => r.check(Q(t, n)), r.lt = (t, n) => r.check(y(t, n)), r.lte = (t, n) => r.check(M(t, n)), r.max = (t, n) => r.check(M(t, n)), r.int = (t) => r.check(Ri(t)), r.safe = (t) => r.check(Ri(t)), r.positive = (t) => r.check(h(0, t)), r.nonnegative = (t) => r.check(Q(0, t)), r.negative = (t) => r.check(y(0, t)), r.nonpositive = (t) => r.check(M(0, t)), r.multipleOf = (t, n) => r.check(ur(t, n)), r.step = (t, n) => r.check(ur(t, n)), r.finite = () => r; + let o = r._zod.bag; + r.minValue = Math.max(o.minimum ?? Number.NEGATIVE_INFINITY, o.exclusiveMinimum ?? Number.NEGATIVE_INFINITY) ?? null, r.maxValue = Math.min(o.maximum ?? Number.POSITIVE_INFINITY, o.exclusiveMaximum ?? Number.POSITIVE_INFINITY) ?? null, r.isInt = (o.format ?? "").includes("int") || Number.isSafeInteger(o.multipleOf ?? 0.5), r.isFinite = true, r.format = o.format ?? null; +}); +function Og(r) { + return T$(yr, r); +} +var Or = I("ZodNumberFormat", (r, i) => { + nt.init(r, i), yr.init(r, i); +}); +function Ri(r) { + return R$(Or, r); +} +function Ql(r) { + return x$(Or, r); +} +function Yl(r) { + return Z$(Or, r); +} +function Fl(r) { + return d$(Or, r); +} +function Bl(r) { + return C$(Or, r); +} +var hr = I("ZodBoolean", (r, i) => { + bn.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => zu(r, o, t, n); +}); +function zg(r) { + return f$(hr, r); +} +var ar = I("ZodBigInt", (r, i) => { + oi.init(r, i), P.init(r, i), r._zod.processJSONSchema = (t, n, v) => Su(r, t, n, v), r.gte = (t, n) => r.check(Q(t, n)), r.min = (t, n) => r.check(Q(t, n)), r.gt = (t, n) => r.check(h(t, n)), r.gte = (t, n) => r.check(Q(t, n)), r.min = (t, n) => r.check(Q(t, n)), r.lt = (t, n) => r.check(y(t, n)), r.lte = (t, n) => r.check(M(t, n)), r.max = (t, n) => r.check(M(t, n)), r.positive = (t) => r.check(h(BigInt(0), t)), r.negative = (t) => r.check(y(BigInt(0), t)), r.nonpositive = (t) => r.check(M(BigInt(0), t)), r.nonnegative = (t) => r.check(Q(BigInt(0), t)), r.multipleOf = (t, n) => r.check(ur(t, n)); + let o = r._zod.bag; + r.minValue = o.minimum ?? null, r.maxValue = o.maximum ?? null, r.format = o.format ?? null; +}); +function ml(r) { + return h$(ar, r); +} +var uv = I("ZodBigIntFormat", (r, i) => { + it.init(r, i), ar.init(r, i); +}); +function Hl(r) { + return p$(uv, r); +} +function Tl(r) { + return s$(uv, r); +} +var Sg = I("ZodSymbol", (r, i) => { + vt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Pu(r, o, t, n); +}); +function Ml(r) { + return ru(Sg, r); +} +var Pg = I("ZodUndefined", (r, i) => { + ot.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Ju(r, o, t, n); +}); +function Rl(r) { + return nu(Pg, r); +} +var jg = I("ZodNull", (r, i) => { + tt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => ju(r, o, t, n); +}); +function Jg(r) { + return iu(jg, r); +} +var Lg = I("ZodAny", (r, i) => { + $t.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Wu(r, o, t, n); +}); +function xl() { + return vu(Lg); +} +var Gg = I("ZodUnknown", (r, i) => { + ut.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Vu(r, o, t, n); +}); +function Nr() { + return ou(Gg); +} +var Wg = I("ZodNever", (r, i) => { + gt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Gu(r, o, t, n); +}); +function gv(r) { + return tu(Wg, r); +} +var Vg = I("ZodVoid", (r, i) => { + et.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Lu(r, o, t, n); +}); +function Zl(r) { + return $u(Vg, r); +} +var Vn = I("ZodDate", (r, i) => { + lt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (t, n, v) => Xu(r, t, n, v), r.min = (t, n) => r.check(Q(t, n)), r.max = (t, n) => r.check(M(t, n)); + let o = r._zod.bag; + r.minDate = o.minimum ? new Date(o.minimum) : null, r.maxDate = o.maximum ? new Date(o.maximum) : null; +}); +function dl(r) { + return uu(Vn, r); +} +var Xg = I("ZodArray", (r, i) => { + It.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Mu(r, o, t, n), r.element = i.element, r.min = (o, t) => r.check(nr(o, t)), r.nonempty = (o) => r.check(nr(1, o)), r.max = (o, t) => r.check(Dr(o, t)), r.length = (o, t) => r.check(wr(o, t)), r.unwrap = () => r.element; +}); +function Xn(r, i) { + return lu(Xg, r, i); +} +function Cl(r) { + let i = r._zod.def.shape; + return lv(Object.keys(i)); +} +var En = I("ZodObject", (r, i) => { + ct.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Ru(r, o, t, n), D.defineLazy(r, "shape", () => { + return i.shape; + }), r.keyof = () => lv(Object.keys(r._zod.def.shape)), r.catchall = (o) => r.clone({ ...r._zod.def, catchall: o }), r.passthrough = () => r.clone({ ...r._zod.def, catchall: Nr() }), r.loose = () => r.clone({ ...r._zod.def, catchall: Nr() }), r.strict = () => r.clone({ ...r._zod.def, catchall: gv() }), r.strip = () => r.clone({ ...r._zod.def, catchall: void 0 }), r.extend = (o) => { + return D.extend(r, o); + }, r.safeExtend = (o) => { + return D.safeExtend(r, o); + }, r.merge = (o) => D.merge(r, o), r.pick = (o) => D.pick(r, o), r.omit = (o) => D.omit(r, o), r.partial = (...o) => D.partial(cv, r, o[0]), r.required = (...o) => D.required(bv, r, o[0]); +}); +function fl(r, i) { + let o = { type: "object", shape: r ?? {}, ...D.normalizeParams(i) }; + return new En(o); +} +function yl(r, i) { + return new En({ type: "object", shape: r, catchall: gv(), ...D.normalizeParams(i) }); +} +function hl(r, i) { + return new En({ type: "object", shape: r, catchall: Nr(), ...D.normalizeParams(i) }); +} +var An = I("ZodUnion", (r, i) => { + _n.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => qi(r, o, t, n), r.options = i.options; +}); +function ev(r, i) { + return new An({ type: "union", options: r, ...D.normalizeParams(i) }); +} +var Eg = I("ZodXor", (r, i) => { + An.init(r, i), bt.init(r, i), r._zod.processJSONSchema = (o, t, n) => qi(r, o, t, n), r.options = i.options; +}); +function al(r, i) { + return new Eg({ type: "union", options: r, inclusive: false, ...D.normalizeParams(i) }); +} +var Ag = I("ZodDiscriminatedUnion", (r, i) => { + An.init(r, i), _t.init(r, i); +}); +function pl(r, i, o) { + return new Ag({ type: "union", options: i, discriminator: r, ...D.normalizeParams(o) }); +} +var Kg = I("ZodIntersection", (r, i) => { + Ut.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => xu(r, o, t, n); +}); +function qg(r, i) { + return new Kg({ type: "intersection", left: r, right: i }); +} +var Qg = I("ZodTuple", (r, i) => { + ti.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Zu(r, o, t, n), r.rest = (o) => r.clone({ ...r._zod.def, rest: o }); +}); +function Yg(r, i, o) { + let t = i instanceof S, n = t ? o : i; + return new Qg({ type: "tuple", items: r, rest: t ? i : null, ...D.normalizeParams(n) }); +} +var Kn = I("ZodRecord", (r, i) => { + kt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => du(r, o, t, n), r.keyType = i.keyType, r.valueType = i.valueType; +}); +function Fg(r, i, o) { + return new Kn({ type: "record", keyType: r, valueType: i, ...D.normalizeParams(o) }); +} +function sl(r, i, o) { + let t = q(r); + return t._zod.values = void 0, new Kn({ type: "record", keyType: t, valueType: i, ...D.normalizeParams(o) }); +} +function rI(r, i, o) { + return new Kn({ type: "record", keyType: r, valueType: i, mode: "loose", ...D.normalizeParams(o) }); +} +var Bg = I("ZodMap", (r, i) => { + Dt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Hu(r, o, t, n), r.keyType = i.keyType, r.valueType = i.valueType, r.min = (...o) => r.check(a(...o)), r.nonempty = (o) => r.check(a(1, o)), r.max = (...o) => r.check(gr(...o)), r.size = (...o) => r.check(kr(...o)); +}); +function nI(r, i, o) { + return new Bg({ type: "map", keyType: r, valueType: i, ...D.normalizeParams(o) }); +} +var mg = I("ZodSet", (r, i) => { + wt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Tu(r, o, t, n), r.min = (...o) => r.check(a(...o)), r.nonempty = (o) => r.check(a(1, o)), r.max = (...o) => r.check(gr(...o)), r.size = (...o) => r.check(kr(...o)); +}); +function iI(r, i) { + return new mg({ type: "set", valueType: r, ...D.normalizeParams(i) }); +} +var dr = I("ZodEnum", (r, i) => { + Nt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (t, n, v) => Eu(r, t, n, v), r.enum = i.entries, r.options = Object.values(i.entries); + let o = new Set(Object.keys(i.entries)); + r.extract = (t, n) => { + let v = {}; + for (let $ of t) if (o.has($)) v[$] = i.entries[$]; + else throw Error(`Key ${$} not found in enum`); + return new dr({ ...i, checks: [], ...D.normalizeParams(n), entries: v }); + }, r.exclude = (t, n) => { + let v = { ...i.entries }; + for (let $ of t) if (o.has($)) delete v[$]; + else throw Error(`Key ${$} not found in enum`); + return new dr({ ...i, checks: [], ...D.normalizeParams(n), entries: v }); + }; +}); +function lv(r, i) { + let o = Array.isArray(r) ? Object.fromEntries(r.map((t) => [t, t])) : r; + return new dr({ type: "enum", entries: o, ...D.normalizeParams(i) }); +} +function vI(r, i) { + return new dr({ type: "enum", entries: r, ...D.normalizeParams(i) }); +} +var Hg = I("ZodLiteral", (r, i) => { + Ot.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Au(r, o, t, n), r.values = new Set(i.values), Object.defineProperty(r, "value", { get() { + if (i.values.length > 1) throw Error("This schema contains multiple valid literal values. Use `.values` instead."); + return i.values[0]; + } }); +}); +function oI(r, i) { + return new Hg({ type: "literal", values: Array.isArray(r) ? r : [r], ...D.normalizeParams(i) }); +} +var Tg = I("ZodFile", (r, i) => { + zt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Qu(r, o, t, n), r.min = (o, t) => r.check(a(o, t)), r.max = (o, t) => r.check(gr(o, t)), r.mime = (o, t) => r.check(Fr(Array.isArray(o) ? o : [o], t)); +}); +function tI(r) { + return Iu(Tg, r); +} +var Mg = I("ZodTransform", (r, i) => { + St.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => mu(r, o, t, n), r._zod.parse = (o, t) => { + if (t.direction === "backward") throw new cr(r.constructor.name); + o.addIssue = (v) => { + if (typeof v === "string") o.issues.push(D.issue(v, o.value, i)); + else { + let $ = v; + if ($.fatal) $.continue = false; + $.code ?? ($.code = "custom"), $.input ?? ($.input = o.value), $.inst ?? ($.inst = r), o.issues.push(D.issue($)); + } + }; + let n = i.transform(o.value, o); + if (n instanceof Promise) return n.then((v) => { + return o.value = v, o; + }); + return o.value = n, o; + }; +}); +function Iv(r) { + return new Mg({ type: "transform", transform: r }); +} +var cv = I("ZodOptional", (r, i) => { + $i.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Qi(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function Jn(r) { + return new cv({ type: "optional", innerType: r }); +} +var Rg = I("ZodExactOptional", (r, i) => { + Pt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Qi(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function xg(r) { + return new Rg({ type: "optional", innerType: r }); +} +var Zg = I("ZodNullable", (r, i) => { + jt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Cu(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function Ln(r) { + return new Zg({ type: "nullable", innerType: r }); +} +function $I(r) { + return Jn(Ln(r)); +} +var dg = I("ZodDefault", (r, i) => { + Jt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => yu(r, o, t, n), r.unwrap = () => r._zod.def.innerType, r.removeDefault = r.unwrap; +}); +function Cg(r, i) { + return new dg({ type: "default", innerType: r, get defaultValue() { + return typeof i === "function" ? i() : D.shallowClone(i); + } }); +} +var fg = I("ZodPrefault", (r, i) => { + Lt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => hu(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function yg(r, i) { + return new fg({ type: "prefault", innerType: r, get defaultValue() { + return typeof i === "function" ? i() : D.shallowClone(i); + } }); +} +var bv = I("ZodNonOptional", (r, i) => { + Gt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => fu(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function hg(r, i) { + return new bv({ type: "nonoptional", innerType: r, ...D.normalizeParams(i) }); +} +var ag = I("ZodSuccess", (r, i) => { + Wt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Yu(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function uI(r) { + return new ag({ type: "success", innerType: r }); +} +var pg = I("ZodCatch", (r, i) => { + Vt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => au(r, o, t, n), r.unwrap = () => r._zod.def.innerType, r.removeCatch = r.unwrap; +}); +function sg(r, i) { + return new pg({ type: "catch", innerType: r, catchValue: typeof i === "function" ? i : () => i }); +} +var re = I("ZodNaN", (r, i) => { + Xt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Ku(r, o, t, n); +}); +function gI(r) { + return eu(re, r); +} +var _v = I("ZodPipe", (r, i) => { + Et.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => pu(r, o, t, n), r.in = i.in, r.out = i.out; +}); +function Gn(r, i) { + return new _v({ type: "pipe", in: r, out: i }); +} +var Uv = I("ZodCodec", (r, i) => { + _v.init(r, i), Un.init(r, i); +}); +function eI(r, i, o) { + return new Uv({ type: "pipe", in: r, out: i, transform: o.decode, reverseTransform: o.encode }); +} +var ne = I("ZodReadonly", (r, i) => { + At.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => su(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function ie(r) { + return new ne({ type: "readonly", innerType: r }); +} +var ve = I("ZodTemplateLiteral", (r, i) => { + Kt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => qu(r, o, t, n); +}); +function lI(r, i) { + return new ve({ type: "template_literal", parts: r, ...D.normalizeParams(i) }); +} +var oe = I("ZodLazy", (r, i) => { + Yt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => ng(r, o, t, n), r.unwrap = () => r._zod.def.getter(); +}); +function te(r) { + return new oe({ type: "lazy", getter: r }); +} +var $e = I("ZodPromise", (r, i) => { + Qt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => rg(r, o, t, n), r.unwrap = () => r._zod.def.innerType; +}); +function II(r) { + return new $e({ type: "promise", innerType: r }); +} +var ue = I("ZodFunction", (r, i) => { + qt.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Bu(r, o, t, n); +}); +function cI(r) { + return new ue({ type: "function", input: Array.isArray(r?.input) ? Yg(r?.input) : r?.input ?? Xn(Nr()), output: r?.output ?? Nr() }); +} +var qn = I("ZodCustom", (r, i) => { + Ft.init(r, i), P.init(r, i), r._zod.processJSONSchema = (o, t, n) => Fu(r, o, t, n); +}); +function bI(r) { + let i = new V({ check: "custom" }); + return i._zod.check = r, i; +} +function _I(r, i) { + return cu(qn, r ?? (() => true), i); +} +function ge(r, i = {}) { + return bu(qn, r, i); +} +function ee(r) { + return _u(r); +} +var UI = Uu; +var kI = ku; +function DI(r, i = {}) { + let o = new qn({ type: "custom", check: "custom", fn: (t) => t instanceof r, abort: true, ...D.normalizeParams(i) }); + return o._zod.bag.Class = r, o._zod.check = (t) => { + if (!(t.value instanceof r)) t.issues.push({ code: "invalid_type", expected: r.name, input: t.value, inst: o, path: [...o._zod.def.path ?? []] }); + }, o; +} +var wI = (...r) => Du({ Codec: Uv, Boolean: hr, String: Cr }, ...r); +function NI(r) { + let i = te(() => { + return ev([Mi(r), Og(), zg(), Jg(), Xn(i), Fg(Mi(), i)]); + }); + return i; +} +function OI(r, i) { + return Gn(Iv(r), i); +} +var c6 = { invalid_type: "invalid_type", too_big: "too_big", too_small: "too_small", invalid_format: "invalid_format", not_multiple_of: "not_multiple_of", unrecognized_keys: "unrecognized_keys", invalid_union: "invalid_union", invalid_key: "invalid_key", invalid_element: "invalid_element", invalid_value: "invalid_value", custom: "custom" }; +function b6(r) { + E({ customError: r }); +} +function _6() { + return E().customError; +} +var le; +/* @__PURE__ */ (function(r) { +})(le || (le = {})); +var z = { ...Pn, ...Fi, iso: Zr }; +var U6 = /* @__PURE__ */ new Set(["$schema", "$ref", "$defs", "definitions", "$id", "id", "$comment", "$anchor", "$vocabulary", "$dynamicRef", "$dynamicAnchor", "type", "enum", "const", "anyOf", "oneOf", "allOf", "not", "properties", "required", "additionalProperties", "patternProperties", "propertyNames", "minProperties", "maxProperties", "items", "prefixItems", "additionalItems", "minItems", "maxItems", "uniqueItems", "contains", "minContains", "maxContains", "minLength", "maxLength", "pattern", "format", "minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum", "multipleOf", "description", "default", "contentEncoding", "contentMediaType", "contentSchema", "unevaluatedItems", "unevaluatedProperties", "if", "then", "else", "dependentSchemas", "dependentRequired", "nullable", "readOnly"]); +function k6(r, i) { + let o = r.$schema; + if (o === "https://json-schema.org/draft/2020-12/schema") return "draft-2020-12"; + if (o === "http://json-schema.org/draft-07/schema#") return "draft-7"; + if (o === "http://json-schema.org/draft-04/schema#") return "draft-4"; + return i ?? "draft-2020-12"; +} +function D6(r, i) { + if (!r.startsWith("#")) throw Error("External $ref is not supported, only local refs (#/...) are allowed"); + let o = r.slice(1).split("/").filter(Boolean); + if (o.length === 0) return i.rootSchema; + let t = i.version === "draft-2020-12" ? "$defs" : "definitions"; + if (o[0] === t) { + let n = o[1]; + if (!n || !i.defs[n]) throw Error(`Reference not found: ${r}`); + return i.defs[n]; + } + throw Error(`Reference not found: ${r}`); +} +function zI(r, i) { + if (r.not !== void 0) { + if (typeof r.not === "object" && Object.keys(r.not).length === 0) return z.never(); + throw Error("not is not supported in Zod (except { not: {} } for never)"); + } + if (r.unevaluatedItems !== void 0) throw Error("unevaluatedItems is not supported"); + if (r.unevaluatedProperties !== void 0) throw Error("unevaluatedProperties is not supported"); + if (r.if !== void 0 || r.then !== void 0 || r.else !== void 0) throw Error("Conditional schemas (if/then/else) are not supported"); + if (r.dependentSchemas !== void 0 || r.dependentRequired !== void 0) throw Error("dependentSchemas and dependentRequired are not supported"); + if (r.$ref) { + let n = r.$ref; + if (i.refs.has(n)) return i.refs.get(n); + if (i.processing.has(n)) return z.lazy(() => { + if (!i.refs.has(n)) throw Error(`Circular reference not resolved: ${n}`); + return i.refs.get(n); + }); + i.processing.add(n); + let v = D6(n, i), $ = K(v, i); + return i.refs.set(n, $), i.processing.delete(n), $; + } + if (r.enum !== void 0) { + let n = r.enum; + if (i.version === "openapi-3.0" && r.nullable === true && n.length === 1 && n[0] === null) return z.null(); + if (n.length === 0) return z.never(); + if (n.length === 1) return z.literal(n[0]); + if (n.every(($) => typeof $ === "string")) return z.enum(n); + let v = n.map(($) => z.literal($)); + if (v.length < 2) return v[0]; + return z.union([v[0], v[1], ...v.slice(2)]); + } + if (r.const !== void 0) return z.literal(r.const); + let o = r.type; + if (Array.isArray(o)) { + let n = o.map((v) => { + let $ = { ...r, type: v }; + return zI($, i); + }); + if (n.length === 0) return z.never(); + if (n.length === 1) return n[0]; + return z.union(n); + } + if (!o) return z.any(); + let t; + switch (o) { + case "string": { + let n = z.string(); + if (r.format) { + let v = r.format; + if (v === "email") n = n.check(z.email()); + else if (v === "uri" || v === "uri-reference") n = n.check(z.url()); + else if (v === "uuid" || v === "guid") n = n.check(z.uuid()); + else if (v === "date-time") n = n.check(z.iso.datetime()); + else if (v === "date") n = n.check(z.iso.date()); + else if (v === "time") n = n.check(z.iso.time()); + else if (v === "duration") n = n.check(z.iso.duration()); + else if (v === "ipv4") n = n.check(z.ipv4()); + else if (v === "ipv6") n = n.check(z.ipv6()); + else if (v === "mac") n = n.check(z.mac()); + else if (v === "cidr") n = n.check(z.cidrv4()); + else if (v === "cidr-v6") n = n.check(z.cidrv6()); + else if (v === "base64") n = n.check(z.base64()); + else if (v === "base64url") n = n.check(z.base64url()); + else if (v === "e164") n = n.check(z.e164()); + else if (v === "jwt") n = n.check(z.jwt()); + else if (v === "emoji") n = n.check(z.emoji()); + else if (v === "nanoid") n = n.check(z.nanoid()); + else if (v === "cuid") n = n.check(z.cuid()); + else if (v === "cuid2") n = n.check(z.cuid2()); + else if (v === "ulid") n = n.check(z.ulid()); + else if (v === "xid") n = n.check(z.xid()); + else if (v === "ksuid") n = n.check(z.ksuid()); + } + if (typeof r.minLength === "number") n = n.min(r.minLength); + if (typeof r.maxLength === "number") n = n.max(r.maxLength); + if (r.pattern) n = n.regex(new RegExp(r.pattern)); + t = n; + break; + } + case "number": + case "integer": { + let n = o === "integer" ? z.number().int() : z.number(); + if (typeof r.minimum === "number") n = n.min(r.minimum); + if (typeof r.maximum === "number") n = n.max(r.maximum); + if (typeof r.exclusiveMinimum === "number") n = n.gt(r.exclusiveMinimum); + else if (r.exclusiveMinimum === true && typeof r.minimum === "number") n = n.gt(r.minimum); + if (typeof r.exclusiveMaximum === "number") n = n.lt(r.exclusiveMaximum); + else if (r.exclusiveMaximum === true && typeof r.maximum === "number") n = n.lt(r.maximum); + if (typeof r.multipleOf === "number") n = n.multipleOf(r.multipleOf); + t = n; + break; + } + case "boolean": { + t = z.boolean(); + break; + } + case "null": { + t = z.null(); + break; + } + case "object": { + let n = {}, v = r.properties || {}, $ = new Set(r.required || []); + for (let [l, e] of Object.entries(v)) { + let c = K(e, i); + n[l] = $.has(l) ? c : c.optional(); + } + if (r.propertyNames) { + let l = K(r.propertyNames, i), e = r.additionalProperties && typeof r.additionalProperties === "object" ? K(r.additionalProperties, i) : z.any(); + if (Object.keys(n).length === 0) { + t = z.record(l, e); + break; + } + let c = z.object(n).passthrough(), _ = z.looseRecord(l, e); + t = z.intersection(c, _); + break; + } + if (r.patternProperties) { + let l = r.patternProperties, e = Object.keys(l), c = []; + for (let N of e) { + let O = K(l[N], i), J = z.string().regex(new RegExp(N)); + c.push(z.looseRecord(J, O)); + } + let _ = []; + if (Object.keys(n).length > 0) _.push(z.object(n).passthrough()); + if (_.push(...c), _.length === 0) t = z.object({}).passthrough(); + else if (_.length === 1) t = _[0]; + else { + let N = z.intersection(_[0], _[1]); + for (let O = 2; O < _.length; O++) N = z.intersection(N, _[O]); + t = N; + } + break; + } + let u = z.object(n); + if (r.additionalProperties === false) t = u.strict(); + else if (typeof r.additionalProperties === "object") t = u.catchall(K(r.additionalProperties, i)); + else t = u.passthrough(); + break; + } + case "array": { + let { prefixItems: n, items: v } = r; + if (n && Array.isArray(n)) { + let $ = n.map((l) => K(l, i)), u = v && typeof v === "object" && !Array.isArray(v) ? K(v, i) : void 0; + if (u) t = z.tuple($).rest(u); + else t = z.tuple($); + if (typeof r.minItems === "number") t = t.check(z.minLength(r.minItems)); + if (typeof r.maxItems === "number") t = t.check(z.maxLength(r.maxItems)); + } else if (Array.isArray(v)) { + let $ = v.map((l) => K(l, i)), u = r.additionalItems && typeof r.additionalItems === "object" ? K(r.additionalItems, i) : void 0; + if (u) t = z.tuple($).rest(u); + else t = z.tuple($); + if (typeof r.minItems === "number") t = t.check(z.minLength(r.minItems)); + if (typeof r.maxItems === "number") t = t.check(z.maxLength(r.maxItems)); + } else if (v !== void 0) { + let $ = K(v, i), u = z.array($); + if (typeof r.minItems === "number") u = u.min(r.minItems); + if (typeof r.maxItems === "number") u = u.max(r.maxItems); + t = u; + } else t = z.array(z.any()); + break; + } + default: + throw Error(`Unsupported type: ${o}`); + } + if (r.description) t = t.describe(r.description); + if (r.default !== void 0) t = t.default(r.default); + return t; +} +function K(r, i) { + if (typeof r === "boolean") return r ? z.any() : z.never(); + let o = zI(r, i), t = r.type || r.enum !== void 0 || r.const !== void 0; + if (r.anyOf && Array.isArray(r.anyOf)) { + let u = r.anyOf.map((e) => K(e, i)), l = z.union(u); + o = t ? z.intersection(o, l) : l; + } + if (r.oneOf && Array.isArray(r.oneOf)) { + let u = r.oneOf.map((e) => K(e, i)), l = z.xor(u); + o = t ? z.intersection(o, l) : l; + } + if (r.allOf && Array.isArray(r.allOf)) if (r.allOf.length === 0) o = t ? o : z.any(); + else { + let u = t ? o : K(r.allOf[0], i), l = t ? 0 : 1; + for (let e = l; e < r.allOf.length; e++) u = z.intersection(u, K(r.allOf[e], i)); + o = u; + } + if (r.nullable === true && i.version === "openapi-3.0") o = z.nullable(o); + if (r.readOnly === true) o = z.readonly(o); + let n = {}, v = ["$id", "id", "$comment", "$anchor", "$vocabulary", "$dynamicRef", "$dynamicAnchor"]; + for (let u of v) if (u in r) n[u] = r[u]; + let $ = ["contentEncoding", "contentMediaType", "contentSchema"]; + for (let u of $) if (u in r) n[u] = r[u]; + for (let u of Object.keys(r)) if (!U6.has(u)) n[u] = r[u]; + if (Object.keys(n).length > 0) i.registry.add(o, n); + return o; +} +function SI(r, i) { + if (typeof r === "boolean") return r ? z.any() : z.never(); + let o = k6(r, i?.defaultTarget), t = r.$defs || r.definitions || {}, n = { version: o, defs: t, refs: /* @__PURE__ */ new Map(), processing: /* @__PURE__ */ new Set(), rootSchema: r, registry: i?.registry ?? A }; + return K(r, n); +} +var Ie = {}; +s(Ie, { string: () => w6, number: () => N6, date: () => S6, boolean: () => O6, bigint: () => z6 }); +function w6(r) { + return q$(Cr, r); +} +function N6(r) { + return M$(yr, r); +} +function O6(r) { + return y$(hr, r); +} +function z6(r) { + return a$(ar, r); +} +function S6(r) { + return gu(Vn, r); +} +E(kn()); +var JI = g.union([g.literal("light"), g.literal("dark")]).describe("Color theme preference for the host environment."); +var pr = g.union([g.literal("inline"), g.literal("fullscreen"), g.literal("pip")]).describe("Display mode for UI presentation."); +var L6 = g.union([g.literal("--color-background-primary"), g.literal("--color-background-secondary"), g.literal("--color-background-tertiary"), g.literal("--color-background-inverse"), g.literal("--color-background-ghost"), g.literal("--color-background-info"), g.literal("--color-background-danger"), g.literal("--color-background-success"), g.literal("--color-background-warning"), g.literal("--color-background-disabled"), g.literal("--color-text-primary"), g.literal("--color-text-secondary"), g.literal("--color-text-tertiary"), g.literal("--color-text-inverse"), g.literal("--color-text-ghost"), g.literal("--color-text-info"), g.literal("--color-text-danger"), g.literal("--color-text-success"), g.literal("--color-text-warning"), g.literal("--color-text-disabled"), g.literal("--color-text-ghost"), g.literal("--color-border-primary"), g.literal("--color-border-secondary"), g.literal("--color-border-tertiary"), g.literal("--color-border-inverse"), g.literal("--color-border-ghost"), g.literal("--color-border-info"), g.literal("--color-border-danger"), g.literal("--color-border-success"), g.literal("--color-border-warning"), g.literal("--color-border-disabled"), g.literal("--color-ring-primary"), g.literal("--color-ring-secondary"), g.literal("--color-ring-inverse"), g.literal("--color-ring-info"), g.literal("--color-ring-danger"), g.literal("--color-ring-success"), g.literal("--color-ring-warning"), g.literal("--font-sans"), g.literal("--font-mono"), g.literal("--font-weight-normal"), g.literal("--font-weight-medium"), g.literal("--font-weight-semibold"), g.literal("--font-weight-bold"), g.literal("--font-text-xs-size"), g.literal("--font-text-sm-size"), g.literal("--font-text-md-size"), g.literal("--font-text-lg-size"), g.literal("--font-heading-xs-size"), g.literal("--font-heading-sm-size"), g.literal("--font-heading-md-size"), g.literal("--font-heading-lg-size"), g.literal("--font-heading-xl-size"), g.literal("--font-heading-2xl-size"), g.literal("--font-heading-3xl-size"), g.literal("--font-text-xs-line-height"), g.literal("--font-text-sm-line-height"), g.literal("--font-text-md-line-height"), g.literal("--font-text-lg-line-height"), g.literal("--font-heading-xs-line-height"), g.literal("--font-heading-sm-line-height"), g.literal("--font-heading-md-line-height"), g.literal("--font-heading-lg-line-height"), g.literal("--font-heading-xl-line-height"), g.literal("--font-heading-2xl-line-height"), g.literal("--font-heading-3xl-line-height"), g.literal("--border-radius-xs"), g.literal("--border-radius-sm"), g.literal("--border-radius-md"), g.literal("--border-radius-lg"), g.literal("--border-radius-xl"), g.literal("--border-radius-full"), g.literal("--border-width-regular"), g.literal("--shadow-hairline"), g.literal("--shadow-sm"), g.literal("--shadow-md"), g.literal("--shadow-lg")]).describe("CSS variable keys available to MCP apps for theming."); +var G6 = g.record(L6.describe(`Style variables for theming MCP apps. + +Individual style keys are optional - hosts may provide any subset of these values. +Values are strings containing CSS values (colors, sizes, font stacks, etc.). + +Note: This type uses \`Record\` rather than \`Partial>\` +for compatibility with Zod schema generation. Both are functionally equivalent for validation.`), g.union([g.string(), g.undefined()]).describe(`Style variables for theming MCP apps. + +Individual style keys are optional - hosts may provide any subset of these values. +Values are strings containing CSS values (colors, sizes, font stacks, etc.). + +Note: This type uses \`Record\` rather than \`Partial>\` +for compatibility with Zod schema generation. Both are functionally equivalent for validation.`)).describe(`Style variables for theming MCP apps. + +Individual style keys are optional - hosts may provide any subset of these values. +Values are strings containing CSS values (colors, sizes, font stacks, etc.). + +Note: This type uses \`Record\` rather than \`Partial>\` +for compatibility with Zod schema generation. Both are functionally equivalent for validation.`); +var W6 = g.object({ method: g.literal("ui/open-link"), params: g.object({ url: g.string().describe("URL to open in the host's browser") }) }); +var be = g.object({ isError: g.boolean().optional().describe("True if the host failed to open the URL (e.g., due to security policy).") }).passthrough(); +var _e = g.object({ isError: g.boolean().optional().describe("True if the host rejected or failed to deliver the message.") }).passthrough(); +var V6 = g.object({ method: g.literal("ui/notifications/sandbox-proxy-ready"), params: g.object({}) }); +var kv = g.object({ connectDomains: g.array(g.string()).optional().describe("Origins for network requests (fetch/XHR/WebSocket)."), resourceDomains: g.array(g.string()).optional().describe("Origins for static resources (scripts, images, styles, fonts)."), frameDomains: g.array(g.string()).optional().describe("Origins for nested iframes (frame-src directive)."), baseUriDomains: g.array(g.string()).optional().describe("Allowed base URIs for the document (base-uri directive).") }); +var Dv = g.object({ camera: g.object({}).optional().describe("Request camera access (Permission Policy `camera` feature)."), microphone: g.object({}).optional().describe("Request microphone access (Permission Policy `microphone` feature)."), geolocation: g.object({}).optional().describe("Request geolocation access (Permission Policy `geolocation` feature)."), clipboardWrite: g.object({}).optional().describe("Request clipboard write access (Permission Policy `clipboard-write` feature).") }); +var X6 = g.object({ method: g.literal("ui/notifications/size-changed"), params: g.object({ width: g.number().optional().describe("New width in pixels."), height: g.number().optional().describe("New height in pixels.") }) }); +var Ue = g.object({ method: g.literal("ui/notifications/tool-input"), params: g.object({ arguments: g.record(g.string(), g.unknown().describe("Complete tool call arguments as key-value pairs.")).optional().describe("Complete tool call arguments as key-value pairs.") }) }); +var ke = g.object({ method: g.literal("ui/notifications/tool-input-partial"), params: g.object({ arguments: g.record(g.string(), g.unknown().describe("Partial tool call arguments (incomplete, may change).")).optional().describe("Partial tool call arguments (incomplete, may change).") }) }); +var De = g.object({ method: g.literal("ui/notifications/tool-cancelled"), params: g.object({ reason: g.string().optional().describe('Optional reason for the cancellation (e.g., "user action", "timeout").') }) }); +var LI = g.object({ fonts: g.string().optional() }); +var GI = g.object({ variables: G6.optional().describe("CSS variables for theming the app."), css: LI.optional().describe("CSS blocks that apps can inject.") }); +var we = g.object({ method: g.literal("ui/resource-teardown"), params: g.object({}) }); +var E6 = g.record(g.string(), g.unknown()); +var ce = g.object({ text: g.object({}).optional().describe("Host supports text content blocks."), image: g.object({}).optional().describe("Host supports image content blocks."), audio: g.object({}).optional().describe("Host supports audio content blocks."), resource: g.object({}).optional().describe("Host supports resource content blocks."), resourceLink: g.object({}).optional().describe("Host supports resource link content blocks."), structuredContent: g.object({}).optional().describe("Host supports structured content.") }); +var WI = g.object({ experimental: g.object({}).optional().describe("Experimental features (structure TBD)."), openLinks: g.object({}).optional().describe("Host supports opening external URLs."), serverTools: g.object({ listChanged: g.boolean().optional().describe("Host supports tools/list_changed notifications.") }).optional().describe("Host can proxy tool calls to the MCP server."), serverResources: g.object({ listChanged: g.boolean().optional().describe("Host supports resources/list_changed notifications.") }).optional().describe("Host can proxy resource reads to the MCP server."), logging: g.object({}).optional().describe("Host accepts log messages."), sandbox: g.object({ permissions: Dv.optional().describe("Permissions granted by the host (camera, microphone, geolocation)."), csp: kv.optional().describe("CSP domains approved by the host.") }).optional().describe("Sandbox configuration applied by the host."), updateModelContext: ce.optional().describe("Host accepts context updates (ui/update-model-context) to be included in the model's context for future turns."), message: ce.optional().describe("Host supports receiving content messages (ui/message) from the view.") }); +var VI = g.object({ experimental: g.object({}).optional().describe("Experimental features (structure TBD)."), tools: g.object({ listChanged: g.boolean().optional().describe("App supports tools/list_changed notifications.") }).optional().describe("App exposes MCP-style tools that the host can call."), availableDisplayModes: g.array(pr).optional().describe("Display modes the app supports.") }); +var A6 = g.object({ method: g.literal("ui/notifications/initialized"), params: g.object({}).optional() }); +var K6 = g.object({ csp: kv.optional().describe("Content Security Policy configuration."), permissions: Dv.optional().describe("Sandbox permissions requested by the UI."), domain: g.string().optional().describe("Dedicated origin for view sandbox."), prefersBorder: g.boolean().optional().describe("Visual boundary preference - true if UI prefers a visible border.") }); +var q6 = g.object({ method: g.literal("ui/request-display-mode"), params: g.object({ mode: pr.describe("The display mode being requested.") }) }); +var Ne = g.object({ mode: pr.describe("The display mode that was actually set. May differ from requested if not supported.") }).passthrough(); +var XI = g.union([g.literal("model"), g.literal("app")]).describe("Tool visibility scope - who can access the tool."); +var Q6 = g.object({ resourceUri: g.string().optional(), visibility: g.array(XI).optional().describe(`Who can access this tool. Default: ["model", "app"] +- "model": Tool visible to and callable by the agent +- "app": Tool callable by the app from this server only`) }); +var ZU = g.object({ mimeTypes: g.array(g.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.') }); +var Y6 = g.object({ method: g.literal("ui/message"), params: g.object({ role: g.literal("user").describe('Message role, currently only "user" is supported.'), content: g.array(ContentBlockSchema).describe("Message content blocks (text, image, etc.).") }) }); +var F6 = g.object({ method: g.literal("ui/notifications/sandbox-resource-ready"), params: g.object({ html: g.string().describe("HTML content to load into the inner iframe."), sandbox: g.string().optional().describe("Optional override for the inner iframe's sandbox attribute."), csp: kv.optional().describe("CSP configuration from resource metadata."), permissions: Dv.optional().describe("Sandbox permissions from resource metadata.") }) }); +var Oe = g.object({ method: g.literal("ui/notifications/tool-result"), params: CallToolResultSchema.describe("Standard MCP tool execution result.") }); +var ze = g.object({ toolInfo: g.object({ id: RequestIdSchema.optional().describe("JSON-RPC id of the tools/call request."), tool: ToolSchema.describe("Tool definition including name, inputSchema, etc.") }).optional().describe("Metadata of the tool call that instantiated this App."), theme: JI.optional().describe("Current color theme preference."), styles: GI.optional().describe("Style configuration for theming the app."), displayMode: pr.optional().describe("How the UI is currently displayed."), availableDisplayModes: g.array(pr).optional().describe("Display modes the host supports."), containerDimensions: g.union([g.object({ height: g.number().describe("Fixed container height in pixels.") }), g.object({ maxHeight: g.union([g.number(), g.undefined()]).optional().describe("Maximum container height in pixels.") })]).and(g.union([g.object({ width: g.number().describe("Fixed container width in pixels.") }), g.object({ maxWidth: g.union([g.number(), g.undefined()]).optional().describe("Maximum container width in pixels.") })])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other +container holding the app. Specify either width or maxWidth, and either height or maxHeight.`), locale: g.string().optional().describe("User's language and region preference in BCP 47 format."), timeZone: g.string().optional().describe("User's timezone in IANA format."), userAgent: g.string().optional().describe("Host application identifier."), platform: g.union([g.literal("web"), g.literal("desktop"), g.literal("mobile")]).optional().describe("Platform type for responsive design decisions."), deviceCapabilities: g.object({ touch: g.boolean().optional().describe("Whether the device supports touch input."), hover: g.boolean().optional().describe("Whether the device supports hover interactions.") }).optional().describe("Device input capabilities."), safeAreaInsets: g.object({ top: g.number().describe("Top safe area inset in pixels."), right: g.number().describe("Right safe area inset in pixels."), bottom: g.number().describe("Bottom safe area inset in pixels."), left: g.number().describe("Left safe area inset in pixels.") }).optional().describe("Mobile safe area boundaries in pixels.") }).passthrough(); +var Se = g.object({ method: g.literal("ui/notifications/host-context-changed"), params: ze.describe("Partial context update containing only changed fields.") }); +var B6 = g.object({ method: g.literal("ui/update-model-context"), params: g.object({ content: g.array(ContentBlockSchema).optional().describe("Context content blocks (text, image, etc.)."), structuredContent: g.record(g.string(), g.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.") }) }); +var m6 = g.object({ method: g.literal("ui/initialize"), params: g.object({ appInfo: ImplementationSchema.describe("App identification (name and version)."), appCapabilities: VI.describe("Features and capabilities this app provides."), protocolVersion: g.string().describe("Protocol version this app supports.") }) }); +var Pe = g.object({ protocolVersion: g.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'), hostInfo: ImplementationSchema.describe("Host application identification and version."), hostCapabilities: WI.describe("Features and capabilities provided by the host."), hostContext: ze.describe("Rich context about the host environment.") }).passthrough(); +var je = "ui/resourceUri"; +var EI = "text/html;profile=mcp-app"; +function hk(r, i, o, t) { + let n = o._meta, v = n.ui, $ = n[je], u = n; + if (v?.resourceUri && !$) u = { ...n, [je]: v.resourceUri }; + else if ($ && !v?.resourceUri) u = { ...n, ui: { ...v, resourceUri: $ } }; + return r.registerTool(i, { ...o, _meta: u }, t); +} +function ak(r, i, o, t, n) { + r.registerResource(i, o, { mimeType: EI, ...t }, n); +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js +var import_ajv = __toESM(require_ajv(), 1); +var import_ajv_formats = __toESM(require_dist(), 1); +function createDefaultAjvInstance() { + const ajv = new import_ajv.default({ + strict: false, + validateFormats: true, + validateSchema: false, + allErrors: true + }); + const addFormats = import_ajv_formats.default; + addFormats(ajv); + return ajv; +} +var AjvJsonSchemaValidator = class { + /** + * Create an AJV validator + * + * @param ajv - Optional pre-configured AJV instance. If not provided, a default instance will be created. + * + * @example + * ```typescript + * // Use default configuration (recommended for most cases) + * import { AjvJsonSchemaValidator } from '@modelcontextprotocol/sdk/validation/ajv'; + * const validator = new AjvJsonSchemaValidator(); + * + * // Or provide custom AJV instance for advanced configuration + * import { Ajv } from 'ajv'; + * import addFormats from 'ajv-formats'; + * + * const ajv = new Ajv({ validateFormats: true }); + * addFormats(ajv); + * const validator = new AjvJsonSchemaValidator(ajv); + * ``` + */ + constructor(ajv) { + this._ajv = ajv ?? createDefaultAjvInstance(); + } + /** + * Create a validator for the given JSON Schema + * + * The validator is compiled once and can be reused multiple times. + * If the schema has an $id, it will be cached by AJV automatically. + * + * @param schema - Standard JSON Schema object + * @returns A validator function that validates input data + */ + getValidator(schema) { + const ajvValidator = "$id" in schema && typeof schema.$id === "string" ? this._ajv.getSchema(schema.$id) ?? this._ajv.compile(schema) : this._ajv.compile(schema); + return (input) => { + const valid = ajvValidator(input); + if (valid) { + return { + valid: true, + data: input, + errorMessage: void 0 + }; + } else { + return { + valid: false, + data: void 0, + errorMessage: this._ajv.errorsText(ajvValidator.errors) + }; + } + }; + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js +var ExperimentalServerTasks = class { + constructor(_server) { + this._server = _server; + } + /** + * Sends a request and returns an AsyncGenerator that yields response messages. + * The generator is guaranteed to end with either a 'result' or 'error' message. + * + * This method provides streaming access to request processing, allowing you to + * observe intermediate task status updates for task-augmented requests. + * + * @param request - The request to send + * @param resultSchema - Zod schema for validating the result + * @param options - Optional request options (timeout, signal, task creation params, etc.) + * @returns AsyncGenerator that yields ResponseMessage objects + * + * @experimental + */ + requestStream(request, resultSchema, options) { + return this._server.requestStream(request, resultSchema, options); + } + /** + * Gets the current status of a task. + * + * @param taskId - The task identifier + * @param options - Optional request options + * @returns The task status + * + * @experimental + */ + async getTask(taskId, options) { + return this._server.getTask({ taskId }, options); + } + /** + * 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 + * + * @experimental + */ + async getTaskResult(taskId, resultSchema, options) { + return this._server.getTaskResult({ taskId }, resultSchema, options); + } + /** + * Lists tasks with optional pagination. + * + * @param cursor - Optional pagination cursor + * @param options - Optional request options + * @returns List of tasks with optional next cursor + * + * @experimental + */ + async listTasks(cursor, options) { + return this._server.listTasks(cursor ? { cursor } : void 0, options); + } + /** + * Cancels a running task. + * + * @param taskId - The task identifier + * @param options - Optional request options + * + * @experimental + */ + async cancelTask(taskId, options) { + return this._server.cancelTask({ taskId }, options); + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js +function assertToolsCallTaskCapability(requests, method, entityName) { + if (!requests) { + throw new Error(`${entityName} does not support task creation (required for ${method})`); + } + switch (method) { + case "tools/call": + if (!requests.tools?.call) { + throw new Error(`${entityName} does not support task creation for tools/call (required for ${method})`); + } + break; + default: + break; + } +} +function assertClientRequestTaskCapability(requests, method, entityName) { + if (!requests) { + throw new Error(`${entityName} does not support task creation (required for ${method})`); + } + switch (method) { + case "sampling/createMessage": + if (!requests.sampling?.createMessage) { + throw new Error(`${entityName} does not support task creation for sampling/createMessage (required for ${method})`); + } + break; + case "elicitation/create": + if (!requests.elicitation?.create) { + throw new Error(`${entityName} does not support task creation for elicitation/create (required for ${method})`); + } + break; + default: + break; + } +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js +var Server = class extends Protocol { + /** + * Initializes this server with the given name and version information. + */ + constructor(_serverInfo, options) { + super(options); + this._serverInfo = _serverInfo; + this._loggingLevels = /* @__PURE__ */ new Map(); + this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index])); + this.isMessageIgnored = (level, sessionId) => { + const currentLevel = this._loggingLevels.get(sessionId); + return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false; + }; + 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?.()); + if (this._capabilities.logging) { + this.setRequestHandler(SetLevelRequestSchema, async (request, extra) => { + const transportSessionId = extra.sessionId || extra.requestInfo?.headers["mcp-session-id"] || void 0; + const { level } = request.params; + const parseResult = LoggingLevelSchema.safeParse(level); + if (parseResult.success) { + this._loggingLevels.set(transportSessionId, parseResult.data); + } + return {}; + }); + } + } + /** + * Access experimental features. + * + * WARNING: These APIs are experimental and may change without notice. + * + * @experimental + */ + get experimental() { + if (!this._experimental) { + this._experimental = { + tasks: new ExperimentalServerTasks(this) + }; + } + return this._experimental; + } + /** + * Registers new capabilities. This can only be called before connecting to a transport. + * + * The new capabilities will be merged with any existing capabilities previously given (e.g., at initialization). + */ + registerCapabilities(capabilities) { + if (this.transport) { + throw new Error("Cannot register capabilities after connecting to transport"); + } + this._capabilities = mergeCapabilities(this._capabilities, capabilities); + } + /** + * Override request handler registration to enforce server-side validation for tools/call. + */ + setRequestHandler(requestSchema, handler) { + const shape = getObjectShape(requestSchema); + const methodSchema = shape?.method; + if (!methodSchema) { + throw new Error("Schema is missing a method literal"); + } + let methodValue; + if (isZ4Schema(methodSchema)) { + const v4Schema = methodSchema; + const v4Def = v4Schema._zod?.def; + methodValue = v4Def?.value ?? v4Schema.value; + } else { + const v3Schema = methodSchema; + const legacyDef = v3Schema._def; + methodValue = legacyDef?.value ?? v3Schema.value; + } + if (typeof methodValue !== "string") { + throw new Error("Schema method literal must be a string"); + } + const method = methodValue; + if (method === "tools/call") { + const wrappedHandler = async (request, extra) => { + const validatedRequest = safeParse3(CallToolRequestSchema, request); + if (!validatedRequest.success) { + const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call request: ${errorMessage}`); + } + const { params } = validatedRequest.data; + const result = await Promise.resolve(handler(request, extra)); + if (params.task) { + const taskValidationResult = safeParse3(CreateTaskResultSchema, result); + if (!taskValidationResult.success) { + const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`); + } + return taskValidationResult.data; + } + const validationResult = safeParse3(CallToolResultSchema, result); + if (!validationResult.success) { + const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid tools/call result: ${errorMessage}`); + } + return validationResult.data; + }; + return super.setRequestHandler(requestSchema, wrappedHandler); + } + return super.setRequestHandler(requestSchema, handler); + } + assertCapabilityForMethod(method) { + switch (method) { + case "sampling/createMessage": + if (!this._clientCapabilities?.sampling) { + throw new Error(`Client does not support sampling (required for ${method})`); + } + break; + case "elicitation/create": + if (!this._clientCapabilities?.elicitation) { + throw new Error(`Client does not support elicitation (required for ${method})`); + } + break; + case "roots/list": + if (!this._clientCapabilities?.roots) { + throw new Error(`Client does not support listing roots (required for ${method})`); + } + break; + case "ping": + break; + } + } + assertNotificationCapability(method) { + switch (method) { + case "notifications/message": + if (!this._capabilities.logging) { + throw new Error(`Server does not support logging (required for ${method})`); + } + break; + case "notifications/resources/updated": + case "notifications/resources/list_changed": + if (!this._capabilities.resources) { + throw new Error(`Server does not support notifying about resources (required for ${method})`); + } + break; + case "notifications/tools/list_changed": + if (!this._capabilities.tools) { + throw new Error(`Server does not support notifying of tool list changes (required for ${method})`); + } + break; + case "notifications/prompts/list_changed": + if (!this._capabilities.prompts) { + throw new Error(`Server does not support notifying of prompt list changes (required for ${method})`); + } + break; + case "notifications/elicitation/complete": + if (!this._clientCapabilities?.elicitation?.url) { + throw new Error(`Client does not support URL elicitation (required for ${method})`); + } + break; + case "notifications/cancelled": + break; + case "notifications/progress": + break; + } + } + assertRequestHandlerCapability(method) { + if (!this._capabilities) { + return; + } + switch (method) { + case "completion/complete": + if (!this._capabilities.completions) { + throw new Error(`Server does not support completions (required for ${method})`); + } + break; + case "logging/setLevel": + if (!this._capabilities.logging) { + throw new Error(`Server does not support logging (required for ${method})`); + } + break; + case "prompts/get": + case "prompts/list": + if (!this._capabilities.prompts) { + throw new Error(`Server does not support prompts (required for ${method})`); + } + break; + case "resources/list": + case "resources/templates/list": + case "resources/read": + if (!this._capabilities.resources) { + throw new Error(`Server does not support resources (required for ${method})`); + } + break; + case "tools/call": + case "tools/list": + if (!this._capabilities.tools) { + throw new Error(`Server does not support tools (required for ${method})`); + } + break; + case "tasks/get": + case "tasks/list": + case "tasks/result": + case "tasks/cancel": + if (!this._capabilities.tasks) { + throw new Error(`Server does not support tasks capability (required for ${method})`); + } + break; + case "ping": + case "initialize": + break; + } + } + assertTaskCapability(method) { + assertClientRequestTaskCapability(this._clientCapabilities?.tasks?.requests, method, "Client"); + } + assertTaskHandlerCapability(method) { + if (!this._capabilities) { + return; + } + assertToolsCallTaskCapability(this._capabilities.tasks?.requests, method, "Server"); + } + async _oninitialize(request) { + const requestedVersion = request.params.protocolVersion; + this._clientCapabilities = request.params.capabilities; + this._clientVersion = request.params.clientInfo; + const protocolVersion = SUPPORTED_PROTOCOL_VERSIONS.includes(requestedVersion) ? requestedVersion : LATEST_PROTOCOL_VERSION; + return { + protocolVersion, + capabilities: this.getCapabilities(), + serverInfo: this._serverInfo, + ...this._instructions && { instructions: this._instructions } + }; + } + /** + * After initialization has completed, this will be populated with the client's reported capabilities. + */ + getClientCapabilities() { + return this._clientCapabilities; + } + /** + * After initialization has completed, this will be populated with information about the client's name and version. + */ + getClientVersion() { + return this._clientVersion; + } + getCapabilities() { + return this._capabilities; + } + async ping() { + return this.request({ method: "ping" }, EmptyResultSchema); + } + // Implementation + async createMessage(params, options) { + if (params.tools || params.toolChoice) { + if (!this._clientCapabilities?.sampling?.tools) { + throw new Error("Client does not support sampling tools capability."); + } + } + if (params.messages.length > 0) { + const lastMessage = params.messages[params.messages.length - 1]; + const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content]; + const hasToolResults = lastContent.some((c) => c.type === "tool_result"); + const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : void 0; + const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : []; + const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use"); + if (hasToolResults) { + if (lastContent.some((c) => c.type !== "tool_result")) { + throw new Error("The last message must contain only tool_result content if any is present"); + } + if (!hasPreviousToolUse) { + throw new Error("tool_result blocks are not matching any tool_use from the previous message"); + } + } + if (hasPreviousToolUse) { + const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id)); + const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId)); + if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) { + throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match"); + } + } + } + if (params.tools) { + return this.request({ method: "sampling/createMessage", params }, CreateMessageResultWithToolsSchema, options); + } + return this.request({ method: "sampling/createMessage", params }, CreateMessageResultSchema, options); + } + /** + * Creates an elicitation request for the given parameters. + * For backwards compatibility, `mode` may be omitted for form requests and will default to `'form'`. + * @param params The parameters for the elicitation request. + * @param options Optional request options. + * @returns The result of the elicitation request. + */ + async elicitInput(params, options) { + const mode = params.mode ?? "form"; + switch (mode) { + case "url": { + if (!this._clientCapabilities?.elicitation?.url) { + throw new Error("Client does not support url elicitation."); + } + const urlParams = params; + return this.request({ method: "elicitation/create", params: urlParams }, ElicitResultSchema, options); + } + case "form": { + if (!this._clientCapabilities?.elicitation?.form) { + throw new Error("Client does not support form elicitation."); + } + const formParams = params.mode === "form" ? params : { ...params, mode: "form" }; + const result = await this.request({ method: "elicitation/create", params: formParams }, ElicitResultSchema, options); + if (result.action === "accept" && result.content && formParams.requestedSchema) { + try { + const validator = this._jsonSchemaValidator.getValidator(formParams.requestedSchema); + const validationResult = validator(result.content); + if (!validationResult.valid) { + throw new McpError(ErrorCode.InvalidParams, `Elicitation response content does not match requested schema: ${validationResult.errorMessage}`); + } + } catch (error2) { + if (error2 instanceof McpError) { + throw error2; + } + throw new McpError(ErrorCode.InternalError, `Error validating elicitation response: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + } + return result; + } + } + } + /** + * Creates a reusable callback that, when invoked, will send a `notifications/elicitation/complete` + * notification for the specified elicitation ID. + * + * @param elicitationId The ID of the elicitation to mark as complete. + * @param options Optional notification options. Useful when the completion notification should be related to a prior request. + * @returns A function that emits the completion notification when awaited. + */ + createElicitationCompletionNotifier(elicitationId, options) { + if (!this._clientCapabilities?.elicitation?.url) { + throw new Error("Client does not support URL elicitation (required for notifications/elicitation/complete)"); + } + return () => this.notification({ + method: "notifications/elicitation/complete", + params: { + elicitationId + } + }, options); + } + async listRoots(params, options) { + return this.request({ method: "roots/list", params }, ListRootsResultSchema, options); + } + /** + * Sends a logging message to the client, if connected. + * Note: You only need to send the parameters object, not the entire JSON RPC message + * @see LoggingMessageNotification + * @param params + * @param sessionId optional for stateless and backward compatibility + */ + async sendLoggingMessage(params, sessionId) { + if (this._capabilities.logging) { + if (!this.isMessageIgnored(params.level, sessionId)) { + return this.notification({ method: "notifications/message", params }); + } + } + } + async sendResourceUpdated(params) { + return this.notification({ + method: "notifications/resources/updated", + params + }); + } + async sendResourceListChanged() { + return this.notification({ + method: "notifications/resources/list_changed" + }); + } + async sendToolListChanged() { + return this.notification({ method: "notifications/tools/list_changed" }); + } + async sendPromptListChanged() { + return this.notification({ method: "notifications/prompts/list_changed" }); + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js +var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable"); +function isCompletable(schema) { + return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema; +} +function getCompleter(schema) { + const meta = schema[COMPLETABLE_SYMBOL]; + return meta?.complete; +} +var McpZodTypeKind; +(function(McpZodTypeKind2) { + McpZodTypeKind2["Completable"] = "McpCompletable"; +})(McpZodTypeKind || (McpZodTypeKind = {})); + +// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/uriTemplate.js +var MAX_TEMPLATE_LENGTH = 1e6; +var MAX_VARIABLE_LENGTH = 1e6; +var MAX_TEMPLATE_EXPRESSIONS = 1e4; +var MAX_REGEX_LENGTH = 1e6; +var UriTemplate = class _UriTemplate { + /** + * Returns true if the given string contains any URI template expressions. + * A template expression is a sequence of characters enclosed in curly braces, + * like {foo} or {?bar}. + */ + static isTemplate(str) { + return /\{[^}\s]+\}/.test(str); + } + static validateLength(str, max, context) { + if (str.length > max) { + throw new Error(`${context} exceeds maximum length of ${max} characters (got ${str.length})`); + } + } + get variableNames() { + return this.parts.flatMap((part) => typeof part === "string" ? [] : part.names); + } + constructor(template) { + _UriTemplate.validateLength(template, MAX_TEMPLATE_LENGTH, "Template"); + this.template = template; + this.parts = this.parse(template); + } + toString() { + return this.template; + } + parse(template) { + const parts = []; + let currentText = ""; + let i = 0; + let expressionCount = 0; + while (i < template.length) { + if (template[i] === "{") { + if (currentText) { + parts.push(currentText); + currentText = ""; + } + const end = template.indexOf("}", i); + if (end === -1) + throw new Error("Unclosed template expression"); + expressionCount++; + if (expressionCount > MAX_TEMPLATE_EXPRESSIONS) { + throw new Error(`Template contains too many expressions (max ${MAX_TEMPLATE_EXPRESSIONS})`); + } + const expr = template.slice(i + 1, end); + const operator = this.getOperator(expr); + const exploded = expr.includes("*"); + const names = this.getNames(expr); + const name = names[0]; + for (const name2 of names) { + _UriTemplate.validateLength(name2, MAX_VARIABLE_LENGTH, "Variable name"); + } + parts.push({ name, operator, names, exploded }); + i = end + 1; + } else { + currentText += template[i]; + i++; + } + } + if (currentText) { + parts.push(currentText); + } + return parts; + } + getOperator(expr) { + const operators = ["+", "#", ".", "/", "?", "&"]; + return operators.find((op) => expr.startsWith(op)) || ""; + } + getNames(expr) { + const operator = this.getOperator(expr); + return expr.slice(operator.length).split(",").map((name) => name.replace("*", "").trim()).filter((name) => name.length > 0); + } + encodeValue(value, operator) { + _UriTemplate.validateLength(value, MAX_VARIABLE_LENGTH, "Variable value"); + if (operator === "+" || operator === "#") { + return encodeURI(value); + } + return encodeURIComponent(value); + } + expandPart(part, variables) { + if (part.operator === "?" || part.operator === "&") { + const pairs = part.names.map((name) => { + const value2 = variables[name]; + if (value2 === void 0) + return ""; + const encoded2 = Array.isArray(value2) ? value2.map((v) => this.encodeValue(v, part.operator)).join(",") : this.encodeValue(value2.toString(), part.operator); + return `${name}=${encoded2}`; + }).filter((pair) => pair.length > 0); + if (pairs.length === 0) + return ""; + const separator = part.operator === "?" ? "?" : "&"; + return separator + pairs.join("&"); + } + if (part.names.length > 1) { + const values2 = part.names.map((name) => variables[name]).filter((v) => v !== void 0); + if (values2.length === 0) + return ""; + return values2.map((v) => Array.isArray(v) ? v[0] : v).join(","); + } + const value = variables[part.name]; + if (value === void 0) + return ""; + const values = Array.isArray(value) ? value : [value]; + const encoded = values.map((v) => this.encodeValue(v, part.operator)); + switch (part.operator) { + case "": + return encoded.join(","); + case "+": + return encoded.join(","); + case "#": + return "#" + encoded.join(","); + case ".": + return "." + encoded.join("."); + case "/": + return "/" + encoded.join("/"); + default: + return encoded.join(","); + } + } + expand(variables) { + let result = ""; + let hasQueryParam = false; + for (const part of this.parts) { + if (typeof part === "string") { + result += part; + continue; + } + const expanded = this.expandPart(part, variables); + if (!expanded) + continue; + if ((part.operator === "?" || part.operator === "&") && hasQueryParam) { + result += expanded.replace("?", "&"); + } else { + result += expanded; + } + if (part.operator === "?" || part.operator === "&") { + hasQueryParam = true; + } + } + return result; + } + escapeRegExp(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + partToRegExp(part) { + const patterns = []; + for (const name2 of part.names) { + _UriTemplate.validateLength(name2, MAX_VARIABLE_LENGTH, "Variable name"); + } + if (part.operator === "?" || part.operator === "&") { + for (let i = 0; i < part.names.length; i++) { + const name2 = part.names[i]; + const prefix = i === 0 ? "\\" + part.operator : "&"; + patterns.push({ + pattern: prefix + this.escapeRegExp(name2) + "=([^&]+)", + name: name2 + }); + } + return patterns; + } + let pattern; + const name = part.name; + switch (part.operator) { + case "": + pattern = part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)"; + break; + case "+": + case "#": + pattern = "(.+)"; + break; + case ".": + pattern = "\\.([^/,]+)"; + break; + case "/": + pattern = "/" + (part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)"); + break; + default: + pattern = "([^/]+)"; + } + patterns.push({ pattern, name }); + return patterns; + } + match(uri) { + _UriTemplate.validateLength(uri, MAX_TEMPLATE_LENGTH, "URI"); + let pattern = "^"; + const names = []; + for (const part of this.parts) { + if (typeof part === "string") { + pattern += this.escapeRegExp(part); + } else { + const patterns = this.partToRegExp(part); + for (const { pattern: partPattern, name } of patterns) { + pattern += partPattern; + names.push({ name, exploded: part.exploded }); + } + } + } + pattern += "$"; + _UriTemplate.validateLength(pattern, MAX_REGEX_LENGTH, "Generated regex pattern"); + const regex = new RegExp(pattern); + const match = uri.match(regex); + if (!match) + return null; + const result = {}; + for (let i = 0; i < names.length; i++) { + const { name, exploded } = names[i]; + const value = match[i + 1]; + const cleanName = name.replace("*", ""); + if (exploded && value.includes(",")) { + result[cleanName] = value.split(","); + } else { + result[cleanName] = value; + } + } + return result; + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js +var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/; +function validateToolName(name) { + const warnings = []; + if (name.length === 0) { + return { + isValid: false, + warnings: ["Tool name cannot be empty"] + }; + } + if (name.length > 128) { + return { + isValid: false, + warnings: [`Tool name exceeds maximum length of 128 characters (current: ${name.length})`] + }; + } + if (name.includes(" ")) { + warnings.push("Tool name contains spaces, which may cause parsing issues"); + } + if (name.includes(",")) { + warnings.push("Tool name contains commas, which may cause parsing issues"); + } + if (name.startsWith("-") || name.endsWith("-")) { + warnings.push("Tool name starts or ends with a dash, which may cause parsing issues in some contexts"); + } + if (name.startsWith(".") || name.endsWith(".")) { + warnings.push("Tool name starts or ends with a dot, which may cause parsing issues in some contexts"); + } + if (!TOOL_NAME_REGEX.test(name)) { + const invalidChars = name.split("").filter((char) => !/[A-Za-z0-9._-]/.test(char)).filter((char, index, arr) => arr.indexOf(char) === index); + warnings.push(`Tool name contains invalid characters: ${invalidChars.map((c) => `"${c}"`).join(", ")}`, "Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)"); + return { + isValid: false, + warnings + }; + } + return { + isValid: true, + warnings + }; +} +function issueToolNameWarning(name, warnings) { + if (warnings.length > 0) { + console.warn(`Tool name validation warning for "${name}":`); + for (const warning of warnings) { + console.warn(` - ${warning}`); + } + console.warn("Tool registration will proceed, but this may cause compatibility issues."); + console.warn("Consider updating the tool name to conform to the MCP tool naming standard."); + console.warn("See SEP: Specify Format for Tool Names (https://github.com/modelcontextprotocol/modelcontextprotocol/issues/986) for more details."); + } +} +function validateAndWarnToolName(name) { + const result = validateToolName(name); + issueToolNameWarning(name, result.warnings); + return result.isValid; +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js +var ExperimentalMcpServerTasks = class { + constructor(_mcpServer) { + this._mcpServer = _mcpServer; + } + registerToolTask(name, config2, handler) { + const execution = { taskSupport: "required", ...config2.execution }; + if (execution.taskSupport === "forbidden") { + throw new Error(`Cannot register task-based tool '${name}' with taskSupport 'forbidden'. Use registerTool() instead.`); + } + const mcpServerInternal = this._mcpServer; + return mcpServerInternal._createRegisteredTool(name, config2.title, config2.description, config2.inputSchema, config2.outputSchema, config2.annotations, execution, config2._meta, handler); + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js +var McpServer = class { + constructor(serverInfo, options) { + this._registeredResources = {}; + this._registeredResourceTemplates = {}; + this._registeredTools = {}; + this._registeredPrompts = {}; + this._toolHandlersInitialized = false; + this._completionHandlerInitialized = false; + this._resourceHandlersInitialized = false; + this._promptHandlersInitialized = false; + this.server = new Server(serverInfo, options); + } + /** + * Access experimental features. + * + * WARNING: These APIs are experimental and may change without notice. + * + * @experimental + */ + get experimental() { + if (!this._experimental) { + this._experimental = { + tasks: new ExperimentalMcpServerTasks(this) + }; + } + return this._experimental; + } + /** + * Attaches to the given transport, starts it, and starts listening for messages. + * + * The `server` object assumes ownership of the Transport, replacing any callbacks that have already been set, and expects that it is the only user of the Transport instance going forward. + */ + async connect(transport) { + return await this.server.connect(transport); + } + /** + * Closes the connection. + */ + async close() { + await this.server.close(); + } + setToolRequestHandlers() { + if (this._toolHandlersInitialized) { + return; + } + this.server.assertCanSetRequestHandler(getMethodValue(ListToolsRequestSchema)); + this.server.assertCanSetRequestHandler(getMethodValue(CallToolRequestSchema)); + this.server.registerCapabilities({ + tools: { + listChanged: true + } + }); + this.server.setRequestHandler(ListToolsRequestSchema, () => ({ + tools: Object.entries(this._registeredTools).filter(([, tool]) => tool.enabled).map(([name, tool]) => { + const toolDefinition = { + name, + title: tool.title, + description: tool.description, + inputSchema: (() => { + const obj = normalizeObjectSchema(tool.inputSchema); + return obj ? toJsonSchemaCompat(obj, { + strictUnions: true, + pipeStrategy: "input" + }) : EMPTY_OBJECT_JSON_SCHEMA; + })(), + annotations: tool.annotations, + execution: tool.execution, + _meta: tool._meta + }; + if (tool.outputSchema) { + const obj = normalizeObjectSchema(tool.outputSchema); + if (obj) { + toolDefinition.outputSchema = toJsonSchemaCompat(obj, { + strictUnions: true, + pipeStrategy: "output" + }); + } + } + return toolDefinition; + }) + })); + this.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => { + try { + const tool = this._registeredTools[request.params.name]; + if (!tool) { + throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} not found`); + } + if (!tool.enabled) { + throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} disabled`); + } + const isTaskRequest = !!request.params.task; + const taskSupport = tool.execution?.taskSupport; + const isTaskHandler = "createTask" in tool.handler; + if ((taskSupport === "required" || taskSupport === "optional") && !isTaskHandler) { + throw new McpError(ErrorCode.InternalError, `Tool ${request.params.name} has taskSupport '${taskSupport}' but was not registered with registerToolTask`); + } + if (taskSupport === "required" && !isTaskRequest) { + throw new McpError(ErrorCode.MethodNotFound, `Tool ${request.params.name} requires task augmentation (taskSupport: 'required')`); + } + if (taskSupport === "optional" && !isTaskRequest && isTaskHandler) { + return await this.handleAutomaticTaskPolling(tool, request, extra); + } + const args = await this.validateToolInput(tool, request.params.arguments, request.params.name); + const result = await this.executeToolHandler(tool, args, extra); + if (isTaskRequest) { + return result; + } + await this.validateToolOutput(tool, result, request.params.name); + return result; + } catch (error2) { + if (error2 instanceof McpError) { + if (error2.code === ErrorCode.UrlElicitationRequired) { + throw error2; + } + } + return this.createToolError(error2 instanceof Error ? error2.message : String(error2)); + } + }); + this._toolHandlersInitialized = true; + } + /** + * Creates a tool error result. + * + * @param errorMessage - The error message. + * @returns The tool error result. + */ + createToolError(errorMessage) { + return { + content: [ + { + type: "text", + text: errorMessage + } + ], + isError: true + }; + } + /** + * Validates tool input arguments against the tool's input schema. + */ + async validateToolInput(tool, args, toolName) { + if (!tool.inputSchema) { + return void 0; + } + const inputObj = normalizeObjectSchema(tool.inputSchema); + const schemaToParse = inputObj ?? tool.inputSchema; + const parseResult = await safeParseAsync3(schemaToParse, args); + if (!parseResult.success) { + const error2 = "error" in parseResult ? parseResult.error : "Unknown error"; + const errorMessage = getParseErrorMessage(error2); + throw new McpError(ErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${errorMessage}`); + } + return parseResult.data; + } + /** + * Validates tool output against the tool's output schema. + */ + async validateToolOutput(tool, result, toolName) { + if (!tool.outputSchema) { + return; + } + if (!("content" in result)) { + return; + } + if (result.isError) { + return; + } + if (!result.structuredContent) { + throw new McpError(ErrorCode.InvalidParams, `Output validation error: Tool ${toolName} has an output schema but no structured content was provided`); + } + const outputObj = normalizeObjectSchema(tool.outputSchema); + const parseResult = await safeParseAsync3(outputObj, result.structuredContent); + if (!parseResult.success) { + const error2 = "error" in parseResult ? parseResult.error : "Unknown error"; + const errorMessage = getParseErrorMessage(error2); + throw new McpError(ErrorCode.InvalidParams, `Output validation error: Invalid structured content for tool ${toolName}: ${errorMessage}`); + } + } + /** + * Executes a tool handler (either regular or task-based). + */ + async executeToolHandler(tool, args, extra) { + const handler = tool.handler; + const isTaskHandler = "createTask" in handler; + if (isTaskHandler) { + if (!extra.taskStore) { + throw new Error("No task store provided."); + } + const taskExtra = { ...extra, taskStore: extra.taskStore }; + if (tool.inputSchema) { + const typedHandler = handler; + return await Promise.resolve(typedHandler.createTask(args, taskExtra)); + } else { + const typedHandler = handler; + return await Promise.resolve(typedHandler.createTask(taskExtra)); + } + } + if (tool.inputSchema) { + const typedHandler = handler; + return await Promise.resolve(typedHandler(args, extra)); + } else { + const typedHandler = handler; + return await Promise.resolve(typedHandler(extra)); + } + } + /** + * Handles automatic task polling for tools with taskSupport 'optional'. + */ + async handleAutomaticTaskPolling(tool, request, extra) { + if (!extra.taskStore) { + throw new Error("No task store provided for task-capable tool."); + } + const args = await this.validateToolInput(tool, request.params.arguments, request.params.name); + const handler = tool.handler; + const taskExtra = { ...extra, taskStore: extra.taskStore }; + const createTaskResult = args ? await Promise.resolve(handler.createTask(args, taskExtra)) : ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await Promise.resolve(handler.createTask(taskExtra)) + ); + const taskId = createTaskResult.task.taskId; + let task = createTaskResult.task; + const pollInterval = task.pollInterval ?? 5e3; + while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") { + await new Promise((resolve) => setTimeout(resolve, pollInterval)); + const updatedTask = await extra.taskStore.getTask(taskId); + if (!updatedTask) { + throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`); + } + task = updatedTask; + } + return await extra.taskStore.getTaskResult(taskId); + } + setCompletionRequestHandler() { + if (this._completionHandlerInitialized) { + return; + } + this.server.assertCanSetRequestHandler(getMethodValue(CompleteRequestSchema)); + this.server.registerCapabilities({ + completions: {} + }); + this.server.setRequestHandler(CompleteRequestSchema, async (request) => { + switch (request.params.ref.type) { + case "ref/prompt": + assertCompleteRequestPrompt(request); + return this.handlePromptCompletion(request, request.params.ref); + case "ref/resource": + assertCompleteRequestResourceTemplate(request); + return this.handleResourceCompletion(request, request.params.ref); + default: + throw new McpError(ErrorCode.InvalidParams, `Invalid completion reference: ${request.params.ref}`); + } + }); + this._completionHandlerInitialized = true; + } + async handlePromptCompletion(request, ref) { + const prompt = this._registeredPrompts[ref.name]; + if (!prompt) { + throw new McpError(ErrorCode.InvalidParams, `Prompt ${ref.name} not found`); + } + if (!prompt.enabled) { + throw new McpError(ErrorCode.InvalidParams, `Prompt ${ref.name} disabled`); + } + if (!prompt.argsSchema) { + return EMPTY_COMPLETION_RESULT; + } + const promptShape = getObjectShape(prompt.argsSchema); + const field = promptShape?.[request.params.argument.name]; + if (!isCompletable(field)) { + return EMPTY_COMPLETION_RESULT; + } + const completer = getCompleter(field); + if (!completer) { + return EMPTY_COMPLETION_RESULT; + } + const suggestions = await completer(request.params.argument.value, request.params.context); + return createCompletionResult(suggestions); + } + async handleResourceCompletion(request, ref) { + const template = Object.values(this._registeredResourceTemplates).find((t) => t.resourceTemplate.uriTemplate.toString() === ref.uri); + if (!template) { + if (this._registeredResources[ref.uri]) { + return EMPTY_COMPLETION_RESULT; + } + throw new McpError(ErrorCode.InvalidParams, `Resource template ${request.params.ref.uri} not found`); + } + const completer = template.resourceTemplate.completeCallback(request.params.argument.name); + if (!completer) { + return EMPTY_COMPLETION_RESULT; + } + const suggestions = await completer(request.params.argument.value, request.params.context); + return createCompletionResult(suggestions); + } + setResourceRequestHandlers() { + if (this._resourceHandlersInitialized) { + return; + } + this.server.assertCanSetRequestHandler(getMethodValue(ListResourcesRequestSchema)); + this.server.assertCanSetRequestHandler(getMethodValue(ListResourceTemplatesRequestSchema)); + this.server.assertCanSetRequestHandler(getMethodValue(ReadResourceRequestSchema)); + this.server.registerCapabilities({ + resources: { + listChanged: true + } + }); + this.server.setRequestHandler(ListResourcesRequestSchema, async (request, extra) => { + const resources = Object.entries(this._registeredResources).filter(([_, resource]) => resource.enabled).map(([uri, resource]) => ({ + uri, + name: resource.name, + ...resource.metadata + })); + const templateResources = []; + for (const template of Object.values(this._registeredResourceTemplates)) { + if (!template.resourceTemplate.listCallback) { + continue; + } + const result = await template.resourceTemplate.listCallback(extra); + for (const resource of result.resources) { + templateResources.push({ + ...template.metadata, + // the defined resource metadata should override the template metadata if present + ...resource + }); + } + } + return { resources: [...resources, ...templateResources] }; + }); + this.server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => { + const resourceTemplates = Object.entries(this._registeredResourceTemplates).map(([name, template]) => ({ + name, + uriTemplate: template.resourceTemplate.uriTemplate.toString(), + ...template.metadata + })); + return { resourceTemplates }; + }); + this.server.setRequestHandler(ReadResourceRequestSchema, async (request, extra) => { + const uri = new URL(request.params.uri); + const resource = this._registeredResources[uri.toString()]; + if (resource) { + if (!resource.enabled) { + throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} disabled`); + } + return resource.readCallback(uri, extra); + } + for (const template of Object.values(this._registeredResourceTemplates)) { + const variables = template.resourceTemplate.uriTemplate.match(uri.toString()); + if (variables) { + return template.readCallback(uri, variables, extra); + } + } + throw new McpError(ErrorCode.InvalidParams, `Resource ${uri} not found`); + }); + this._resourceHandlersInitialized = true; + } + setPromptRequestHandlers() { + if (this._promptHandlersInitialized) { + return; + } + this.server.assertCanSetRequestHandler(getMethodValue(ListPromptsRequestSchema)); + this.server.assertCanSetRequestHandler(getMethodValue(GetPromptRequestSchema)); + this.server.registerCapabilities({ + prompts: { + listChanged: true + } + }); + this.server.setRequestHandler(ListPromptsRequestSchema, () => ({ + prompts: Object.entries(this._registeredPrompts).filter(([, prompt]) => prompt.enabled).map(([name, prompt]) => { + return { + name, + title: prompt.title, + description: prompt.description, + arguments: prompt.argsSchema ? promptArgumentsFromSchema(prompt.argsSchema) : void 0 + }; + }) + })); + this.server.setRequestHandler(GetPromptRequestSchema, async (request, extra) => { + const prompt = this._registeredPrompts[request.params.name]; + if (!prompt) { + throw new McpError(ErrorCode.InvalidParams, `Prompt ${request.params.name} not found`); + } + if (!prompt.enabled) { + throw new McpError(ErrorCode.InvalidParams, `Prompt ${request.params.name} disabled`); + } + if (prompt.argsSchema) { + const argsObj = normalizeObjectSchema(prompt.argsSchema); + const parseResult = await safeParseAsync3(argsObj, request.params.arguments); + if (!parseResult.success) { + const error2 = "error" in parseResult ? parseResult.error : "Unknown error"; + const errorMessage = getParseErrorMessage(error2); + throw new McpError(ErrorCode.InvalidParams, `Invalid arguments for prompt ${request.params.name}: ${errorMessage}`); + } + const args = parseResult.data; + const cb = prompt.callback; + return await Promise.resolve(cb(args, extra)); + } else { + const cb = prompt.callback; + return await Promise.resolve(cb(extra)); + } + }); + this._promptHandlersInitialized = true; + } + resource(name, uriOrTemplate, ...rest) { + let metadata; + if (typeof rest[0] === "object") { + metadata = rest.shift(); + } + const readCallback = rest[0]; + if (typeof uriOrTemplate === "string") { + if (this._registeredResources[uriOrTemplate]) { + throw new Error(`Resource ${uriOrTemplate} is already registered`); + } + const registeredResource = this._createRegisteredResource(name, void 0, uriOrTemplate, metadata, readCallback); + this.setResourceRequestHandlers(); + this.sendResourceListChanged(); + return registeredResource; + } else { + if (this._registeredResourceTemplates[name]) { + throw new Error(`Resource template ${name} is already registered`); + } + const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, void 0, uriOrTemplate, metadata, readCallback); + this.setResourceRequestHandlers(); + this.sendResourceListChanged(); + return registeredResourceTemplate; + } + } + registerResource(name, uriOrTemplate, config2, readCallback) { + if (typeof uriOrTemplate === "string") { + if (this._registeredResources[uriOrTemplate]) { + throw new Error(`Resource ${uriOrTemplate} is already registered`); + } + const registeredResource = this._createRegisteredResource(name, config2.title, uriOrTemplate, config2, readCallback); + this.setResourceRequestHandlers(); + this.sendResourceListChanged(); + return registeredResource; + } else { + if (this._registeredResourceTemplates[name]) { + throw new Error(`Resource template ${name} is already registered`); + } + const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config2.title, uriOrTemplate, config2, readCallback); + this.setResourceRequestHandlers(); + this.sendResourceListChanged(); + return registeredResourceTemplate; + } + } + _createRegisteredResource(name, title, uri, metadata, readCallback) { + const registeredResource = { + name, + title, + metadata, + readCallback, + enabled: true, + disable: () => registeredResource.update({ enabled: false }), + enable: () => registeredResource.update({ enabled: true }), + remove: () => registeredResource.update({ uri: null }), + update: (updates) => { + if (typeof updates.uri !== "undefined" && updates.uri !== uri) { + delete this._registeredResources[uri]; + if (updates.uri) + this._registeredResources[updates.uri] = registeredResource; + } + if (typeof updates.name !== "undefined") + registeredResource.name = updates.name; + if (typeof updates.title !== "undefined") + registeredResource.title = updates.title; + if (typeof updates.metadata !== "undefined") + registeredResource.metadata = updates.metadata; + if (typeof updates.callback !== "undefined") + registeredResource.readCallback = updates.callback; + if (typeof updates.enabled !== "undefined") + registeredResource.enabled = updates.enabled; + this.sendResourceListChanged(); + } + }; + this._registeredResources[uri] = registeredResource; + return registeredResource; + } + _createRegisteredResourceTemplate(name, title, template, metadata, readCallback) { + const registeredResourceTemplate = { + resourceTemplate: template, + title, + metadata, + readCallback, + enabled: true, + disable: () => registeredResourceTemplate.update({ enabled: false }), + enable: () => registeredResourceTemplate.update({ enabled: true }), + remove: () => registeredResourceTemplate.update({ name: null }), + update: (updates) => { + if (typeof updates.name !== "undefined" && updates.name !== name) { + delete this._registeredResourceTemplates[name]; + if (updates.name) + this._registeredResourceTemplates[updates.name] = registeredResourceTemplate; + } + if (typeof updates.title !== "undefined") + registeredResourceTemplate.title = updates.title; + if (typeof updates.template !== "undefined") + registeredResourceTemplate.resourceTemplate = updates.template; + if (typeof updates.metadata !== "undefined") + registeredResourceTemplate.metadata = updates.metadata; + if (typeof updates.callback !== "undefined") + registeredResourceTemplate.readCallback = updates.callback; + if (typeof updates.enabled !== "undefined") + registeredResourceTemplate.enabled = updates.enabled; + this.sendResourceListChanged(); + } + }; + this._registeredResourceTemplates[name] = registeredResourceTemplate; + const variableNames = template.uriTemplate.variableNames; + const hasCompleter = Array.isArray(variableNames) && variableNames.some((v) => !!template.completeCallback(v)); + if (hasCompleter) { + this.setCompletionRequestHandler(); + } + return registeredResourceTemplate; + } + _createRegisteredPrompt(name, title, description, argsSchema, callback) { + const registeredPrompt = { + title, + description, + argsSchema: argsSchema === void 0 ? void 0 : objectFromShape(argsSchema), + callback, + enabled: true, + disable: () => registeredPrompt.update({ enabled: false }), + enable: () => registeredPrompt.update({ enabled: true }), + remove: () => registeredPrompt.update({ name: null }), + update: (updates) => { + if (typeof updates.name !== "undefined" && updates.name !== name) { + delete this._registeredPrompts[name]; + if (updates.name) + this._registeredPrompts[updates.name] = registeredPrompt; + } + if (typeof updates.title !== "undefined") + registeredPrompt.title = updates.title; + if (typeof updates.description !== "undefined") + registeredPrompt.description = updates.description; + if (typeof updates.argsSchema !== "undefined") + registeredPrompt.argsSchema = objectFromShape(updates.argsSchema); + if (typeof updates.callback !== "undefined") + registeredPrompt.callback = updates.callback; + if (typeof updates.enabled !== "undefined") + registeredPrompt.enabled = updates.enabled; + this.sendPromptListChanged(); + } + }; + this._registeredPrompts[name] = registeredPrompt; + if (argsSchema) { + const hasCompletable = Object.values(argsSchema).some((field) => { + const inner = field instanceof ZodOptional2 ? field._def?.innerType : field; + return isCompletable(inner); + }); + if (hasCompletable) { + this.setCompletionRequestHandler(); + } + } + return registeredPrompt; + } + _createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, execution, _meta, handler) { + validateAndWarnToolName(name); + const registeredTool = { + title, + description, + inputSchema: getZodSchemaObject(inputSchema), + outputSchema: getZodSchemaObject(outputSchema), + annotations, + execution, + _meta, + handler, + enabled: true, + disable: () => registeredTool.update({ enabled: false }), + enable: () => registeredTool.update({ enabled: true }), + remove: () => registeredTool.update({ name: null }), + update: (updates) => { + if (typeof updates.name !== "undefined" && updates.name !== name) { + if (typeof updates.name === "string") { + validateAndWarnToolName(updates.name); + } + delete this._registeredTools[name]; + if (updates.name) + this._registeredTools[updates.name] = registeredTool; + } + if (typeof updates.title !== "undefined") + registeredTool.title = updates.title; + if (typeof updates.description !== "undefined") + registeredTool.description = updates.description; + if (typeof updates.paramsSchema !== "undefined") + registeredTool.inputSchema = objectFromShape(updates.paramsSchema); + if (typeof updates.outputSchema !== "undefined") + registeredTool.outputSchema = objectFromShape(updates.outputSchema); + if (typeof updates.callback !== "undefined") + registeredTool.handler = updates.callback; + if (typeof updates.annotations !== "undefined") + registeredTool.annotations = updates.annotations; + if (typeof updates._meta !== "undefined") + registeredTool._meta = updates._meta; + if (typeof updates.enabled !== "undefined") + registeredTool.enabled = updates.enabled; + this.sendToolListChanged(); + } + }; + this._registeredTools[name] = registeredTool; + this.setToolRequestHandlers(); + this.sendToolListChanged(); + return registeredTool; + } + /** + * tool() implementation. Parses arguments passed to overrides defined above. + */ + tool(name, ...rest) { + if (this._registeredTools[name]) { + throw new Error(`Tool ${name} is already registered`); + } + let description; + let inputSchema; + let outputSchema; + let annotations; + if (typeof rest[0] === "string") { + description = rest.shift(); + } + if (rest.length > 1) { + const firstArg = rest[0]; + if (isZodRawShapeCompat(firstArg)) { + inputSchema = rest.shift(); + if (rest.length > 1 && typeof rest[0] === "object" && rest[0] !== null && !isZodRawShapeCompat(rest[0])) { + annotations = rest.shift(); + } + } else if (typeof firstArg === "object" && firstArg !== null) { + annotations = rest.shift(); + } + } + const callback = rest[0]; + return this._createRegisteredTool(name, void 0, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, void 0, callback); + } + /** + * Registers a tool with a config object and callback. + */ + registerTool(name, config2, cb) { + if (this._registeredTools[name]) { + throw new Error(`Tool ${name} is already registered`); + } + const { title, description, inputSchema, outputSchema, annotations, _meta } = config2; + return this._createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, _meta, cb); + } + prompt(name, ...rest) { + if (this._registeredPrompts[name]) { + throw new Error(`Prompt ${name} is already registered`); + } + let description; + if (typeof rest[0] === "string") { + description = rest.shift(); + } + let argsSchema; + if (rest.length > 1) { + argsSchema = rest.shift(); + } + const cb = rest[0]; + const registeredPrompt = this._createRegisteredPrompt(name, void 0, description, argsSchema, cb); + this.setPromptRequestHandlers(); + this.sendPromptListChanged(); + return registeredPrompt; + } + /** + * Registers a prompt with a config object and callback. + */ + registerPrompt(name, config2, cb) { + if (this._registeredPrompts[name]) { + throw new Error(`Prompt ${name} is already registered`); + } + const { title, description, argsSchema } = config2; + const registeredPrompt = this._createRegisteredPrompt(name, title, description, argsSchema, cb); + this.setPromptRequestHandlers(); + this.sendPromptListChanged(); + return registeredPrompt; + } + /** + * Checks if the server is connected to a transport. + * @returns True if the server is connected + */ + isConnected() { + return this.server.transport !== void 0; + } + /** + * Sends a logging message to the client, if connected. + * Note: You only need to send the parameters object, not the entire JSON RPC message + * @see LoggingMessageNotification + * @param params + * @param sessionId optional for stateless and backward compatibility + */ + async sendLoggingMessage(params, sessionId) { + return this.server.sendLoggingMessage(params, sessionId); + } + /** + * Sends a resource list changed event to the client, if connected. + */ + sendResourceListChanged() { + if (this.isConnected()) { + this.server.sendResourceListChanged(); + } + } + /** + * Sends a tool list changed event to the client, if connected. + */ + sendToolListChanged() { + if (this.isConnected()) { + this.server.sendToolListChanged(); + } + } + /** + * Sends a prompt list changed event to the client, if connected. + */ + sendPromptListChanged() { + if (this.isConnected()) { + this.server.sendPromptListChanged(); + } + } +}; +var ResourceTemplate = class { + constructor(uriTemplate, _callbacks) { + this._callbacks = _callbacks; + this._uriTemplate = typeof uriTemplate === "string" ? new UriTemplate(uriTemplate) : uriTemplate; + } + /** + * Gets the URI template pattern. + */ + get uriTemplate() { + return this._uriTemplate; + } + /** + * Gets the list callback, if one was provided. + */ + get listCallback() { + return this._callbacks.list; + } + /** + * Gets the callback for completing a specific URI template variable, if one was provided. + */ + completeCallback(variable) { + return this._callbacks.complete?.[variable]; + } +}; +var EMPTY_OBJECT_JSON_SCHEMA = { + type: "object", + properties: {} +}; +function isZodTypeLike(value) { + return value !== null && typeof value === "object" && "parse" in value && typeof value.parse === "function" && "safeParse" in value && typeof value.safeParse === "function"; +} +function isZodSchemaInstance(obj) { + return "_def" in obj || "_zod" in obj || isZodTypeLike(obj); +} +function isZodRawShapeCompat(obj) { + if (typeof obj !== "object" || obj === null) { + return false; + } + if (isZodSchemaInstance(obj)) { + return false; + } + if (Object.keys(obj).length === 0) { + return true; + } + return Object.values(obj).some(isZodTypeLike); +} +function getZodSchemaObject(schema) { + if (!schema) { + return void 0; + } + if (isZodRawShapeCompat(schema)) { + return objectFromShape(schema); + } + return schema; +} +function promptArgumentsFromSchema(schema) { + const shape = getObjectShape(schema); + if (!shape) + return []; + return Object.entries(shape).map(([name, field]) => { + const description = getSchemaDescription(field); + const isOptional = isSchemaOptional(field); + return { + name, + description, + required: !isOptional + }; + }); +} +function getMethodValue(schema) { + const shape = getObjectShape(schema); + const methodSchema = shape?.method; + if (!methodSchema) { + throw new Error("Schema is missing a method literal"); + } + const value = getLiteralValue(methodSchema); + if (typeof value === "string") { + return value; + } + throw new Error("Schema method literal must be a string"); +} +function createCompletionResult(suggestions) { + return { + completion: { + values: suggestions.slice(0, 100), + total: suggestions.length, + hasMore: suggestions.length > 100 + } + }; +} +var EMPTY_COMPLETION_RESULT = { + completion: { + values: [], + hasMore: false + } +}; + +// src/prompts/companionPrompts.ts +var GAMEOBJECT_STRATEGY = `Use Pipeline read commands before mutations: +1. Read get_scene_hierarchy or find_gameobjects to obtain a stable target. +2. Use inspect_gameobject for bounded component and serialized-property inspection. +3. Use official Pipeline authoring commands for standard changes. +4. Use only the MCP Unity extensions when needed: duplicate_gameobject, unload_scene, editor_step, and assign_material. +5. Re-read get_scene_hierarchy or inspect_gameobject to verify the result.`; +var DASHBOARD_GUIDE = `Open show_unity_dashboard for a read-only project overview. +The companion resources map to official Pipeline commands: get_console_logs, get_scene_hierarchy, package_list, and list_tests. +GameObject details use inspect_gameobject. The other MCP Unity extensions are duplicate_gameobject, unload_scene, editor_step, and assign_material. +The dashboard never invokes mutations; execute any authoring command explicitly through Unity CLI/Pipeline.`; +function registerCompanionPrompts(server) { + server.registerPrompt( + "gameobject_handling_strategy", + { + description: "A safe discovery, targeting, mutation, and verification workflow." + }, + async () => ({ + messages: [ + { + role: "user", + content: { type: "text", text: GAMEOBJECT_STRATEGY } + } + ] + }) + ); + server.registerPrompt( + "unity_dashboard", + { + description: "Guidance for the read-only Unity dashboard and Pipeline commands." + }, + async () => ({ + messages: [ + { + role: "user", + content: { type: "text", text: DASHBOARD_GUIDE } + } + ] + }) + ); +} + +// src/resources/dashboardResource.ts +import fs2 from "node:fs"; +import path2 from "node:path"; +import { fileURLToPath } from "node:url"; +var DASHBOARD_URI = "ui://unity-dashboard"; +function readDashboardHtml() { + const moduleDirectory = path2.dirname(fileURLToPath(import.meta.url)); + const candidates = [ + path2.join(moduleDirectory, "ui", "unity-dashboard.html"), + path2.join(moduleDirectory, "..", "ui", "unity-dashboard.html"), + path2.join(moduleDirectory, "..", "..", "src", "ui", "unity-dashboard.html") + ]; + const dashboardPath = candidates.find((candidate) => fs2.existsSync(candidate)); + if (!dashboardPath) { + throw new Error(`Unity dashboard HTML is missing. Checked: ${candidates.join(", ")}`); + } + return { + text: fs2.readFileSync(dashboardPath, "utf8"), + mimeType: EI + }; +} + +// src/companionServer.ts +var RESOURCE_TEMPLATES = [ + { + name: "unity_logs", + template: "unity://logs{?severity,limit}", + description: "Recent Unity Editor logs." + }, + { + name: "unity_scenes_hierarchy", + template: "unity://scenes-hierarchy{?path,max_nodes}", + description: "Bounded hierarchy of an open Unity scene." + }, + { + name: "unity_gameobject", + template: "unity://gameobject/{target}", + description: "Bounded GameObject inspection." + }, + { + name: "unity_packages", + template: "unity://packages{?include_indirect}", + description: "Installed Unity packages." + }, + { + name: "unity_tests", + template: "unity://tests/{mode}", + description: "Available Unity tests." + } +]; +function createCompanionServer(resources, options = {}) { + const server = new McpServer( + { name: "MCP Unity Companion", version: "2.0.0" }, + { capabilities: { tools: {}, resources: {}, prompts: {} } } + ); + hk( + server, + "show_unity_dashboard", + { + description: "Open the read-only Unity CLI and Pipeline dashboard.", + annotations: { readOnlyHint: true }, + _meta: { + ui: { + resourceUri: DASHBOARD_URI + } + } + }, + async () => ({ + content: [ + { + type: "text", + text: "Unity dashboard opened. Its views are read-only." + } + ] + }) + ); + ak( + server, + "unity_dashboard", + DASHBOARD_URI, + { + description: "Read-only Unity CLI and Pipeline dashboard.", + _meta: { ui: { prefersBorder: true } } + }, + async () => { + try { + const dashboard = (options.readDashboardHtml ?? readDashboardHtml)(); + return { + contents: [ + { + uri: DASHBOARD_URI, + mimeType: dashboard.mimeType, + text: dashboard.text, + _meta: { + ui: { + csp: { + connectDomains: [], + resourceDomains: [], + frameDomains: [], + baseUriDomains: [] + } + } + } + } + ] + }; + } catch (error2) { + throw boundedError(error2); + } + } + ); + for (const definition of RESOURCE_TEMPLATES) { + server.registerResource( + definition.name, + new ResourceTemplate(definition.template, { list: void 0 }), + { + description: definition.description, + mimeType: "application/json" + }, + async (uri) => { + try { + const result = await resources.read(uri.toString()); + return { + contents: [ + { + uri: result.uri, + mimeType: "application/json", + text: JSON.stringify(result.payload) + } + ] + }; + } catch (error2) { + throw boundedError(error2); + } + } + ); + } + registerCompanionPrompts(server); + return server; +} + +// src/resources/companionResources.ts +var LOG_SEVERITIES = /* @__PURE__ */ new Set(["all", "log", "warning", "error"]); +var TEST_MODES = /* @__PURE__ */ new Set(["all", "editor", "playmode"]); +var NODE_NAME_MAX_LENGTH = 256; +var HIERARCHY_PATH_MAX_LENGTH = 1024; +var SCENE_NAME_MAX_LENGTH = 256; +var SCENE_PATH_MAX_LENGTH = 1024; +var INSTANCE_ID_MAX_LENGTH = 128; +var COMPONENT_MAX_COUNT = 32; +var COMPONENT_SCAN_LIMIT = 128; +var COMPONENT_NAME_MAX_LENGTH = 128; +var HIERARCHY_PAYLOAD_BUDGET_BYTES = 512 * 1024; +var HIERARCHY_ENVELOPE_RESERVE_BYTES = 16 * 1024; +var HIERARCHY_DYNAMIC_MARKER_RESERVE_PER_NODE = 128; +var RESOURCE_PAYLOAD_BUDGET_BYTES = 512 * 1024; +var RESOURCE_PROJECTION_RESERVE_BYTES = 16 * 1024; +var RESOURCE_MAX_STRING_LENGTH = 16 * 1024; +var RESOURCE_MAX_KEY_LENGTH = 256; +var RESOURCE_MAX_ARRAY_ITEMS = 1e3; +var RESOURCE_MAX_OBJECT_KEYS = 256; +var RESOURCE_MAX_DEPTH = 32; +var RESOURCE_MAX_VALUES = 2e4; +var CompanionResourceService = class { + constructor(client) { + this.client = client; + } + client; + async read(uri) { + try { + return await this.readInternal(uri); + } catch (error2) { + throw boundedError(error2); + } + } + async readInternal(uri) { + const parsed = parseResourceUri(uri); + switch (parsed.hostname) { + case "logs": + return this.call(uri, "get_console_logs", { + severity: parseEnum( + parsed.searchParams.get("severity") ?? "all", + LOG_SEVERITIES, + "severity" + ), + limit: parseBoundedInteger( + parsed.searchParams.get("limit"), + 100, + 1, + 1e3, + "limit" + ) + }); + case "scenes-hierarchy": { + const maxNodes = parseBoundedInteger( + parsed.searchParams.get("max_nodes"), + 500, + 1, + 2e3, + "max_nodes" + ); + const path3 = parsed.searchParams.get("path"); + const args = path3 ? { path: path3 } : {}; + const result = await this.call(uri, "get_scene_hierarchy", args, false); + return { + uri, + payload: truncateHierarchy(result.payload, maxNodes) + }; + } + case "gameobject": { + const target = decodeURIComponent(parsed.pathname.replace(/^\/+/, "")); + if (!target) { + throw new Error("unity://gameobject/{target} requires a target."); + } + return this.call(uri, "inspect_gameobject", { + target, + max_depth: 2, + max_nodes: 200, + include_components: true, + include_properties: true, + max_properties_per_component: 100 + }); + } + case "packages": + return this.call(uri, "package_list", { + scope: "installed", + include_indirect: parseBoolean( + parsed.searchParams.get("include_indirect"), + true, + "include_indirect" + ) + }); + case "tests": { + const mode = decodeURIComponent(parsed.pathname.replace(/^\/+/, "")); + parseEnum(mode, TEST_MODES, "mode"); + return this.call(uri, "list_tests", { mode }); + } + default: + throw new Error(`Unknown companion resource: ${uri}`); + } + } + async call(uri, command, args, project = true) { + let result; + try { + result = await this.client.readTool(command, args); + } catch (error2) { + throw new Error(boundedErrorMessage(`${command} failed: `, error2)); + } + return { + uri, + payload: project ? projectResourcePayload(decodeToolPayload(command, result)) : decodeToolPayload(command, result) + }; + } +}; +function decodeToolPayload(command, result) { + if (result.isError) { + const detail = firstText(result) ?? "Unity command returned an error."; + throw new Error(boundedErrorMessage(`${command} failed: `, detail)); + } + if (isRecord(result.structuredContent)) { + return result.structuredContent; + } + const text = firstText(result); + if (text === void 0) { + throw new Error(`${command} returned no JSON payload.`); + } + try { + const parsed = JSON.parse(text); + if (!isRecord(parsed)) { + throw new Error("payload is not a JSON object"); + } + return parsed; + } catch (error2) { + throw new Error( + boundedErrorMessage(`${command} returned malformed JSON: `, error2) + ); + } +} +function parseResourceUri(uri) { + let parsed; + try { + parsed = new URL(uri); + } catch { + throw new Error(`Invalid companion resource URI: ${uri}`); + } + if (parsed.protocol !== "unity:") { + throw new Error(`Unknown companion resource: ${uri}`); + } + return parsed; +} +function parseBoundedInteger(value, fallback, minimum, maximum, name) { + if (value === null || value === "") return fallback; + if (!/^-?[0-9]+$/.test(value)) { + throw new Error(`${name} must be an integer.`); + } + const parsed = Number(value); + if (!Number.isSafeInteger(parsed)) { + return value.startsWith("-") ? minimum : maximum; + } + return Math.min(maximum, Math.max(minimum, parsed)); +} +function parseBoolean(value, fallback, name) { + if (value === null || value === "") return fallback; + if (value === "true") return true; + if (value === "false") return false; + throw new Error(`${name} must be true or false.`); +} +function parseEnum(value, allowed, name) { + if (!allowed.has(value)) { + throw new Error(`${name} must be one of: ${[...allowed].join(", ")}.`); + } + return value; +} +function firstText(result) { + const item = result.content.find( + (content) => content.type === "text" + ); + return item?.text; +} +function projectResourcePayload(source) { + const output = {}; + const notice = { + truncated: false, + payloadBudgetBytes: RESOURCE_PAYLOAD_BUDGET_BYTES, + projectedBytes: 0 + }; + const seen = /* @__PURE__ */ new WeakSet(); + seen.add(source); + const frames = [ + { + source, + output, + entries: boundedEntries(source, notice), + nextEntry: 0, + depth: 0 + } + ]; + let projectedBytes = 2; + let valuesVisited = 0; + while (frames.length > 0) { + const frame = frames[frames.length - 1]; + if (frame.nextEntry >= frame.entries.length) { + frames.pop(); + continue; + } + if (valuesVisited >= RESOURCE_MAX_VALUES) { + markResourceProjection(notice, "valueLimitReached"); + notice.omittedValues = (notice.omittedValues ?? 0) + remainingFrameEntries(frames); + break; + } + const [rawKey, sourceValue] = frame.entries[frame.nextEntry++]; + valuesVisited++; + let outputKey = rawKey; + if (typeof rawKey === "string" && rawKey.length > RESOURCE_MAX_KEY_LENGTH) { + outputKey = rawKey.slice(0, RESOURCE_MAX_KEY_LENGTH); + markResourceProjection(notice); + notice.truncatedKeys = (notice.truncatedKeys ?? 0) + 1; + } + const keyCost = Array.isArray(frame.output) ? frame.output.length > 0 ? 1 : 0 : (Object.keys(frame.output).length > 0 ? 1 : 0) + jsonBytes(String(outputKey)) + 1; + if (!Array.isArray(frame.output) && Object.prototype.hasOwnProperty.call(frame.output, String(outputKey))) { + markResourceProjection(notice, "keyCollisionDetected"); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + let projectedValue; + let childFrame; + if (typeof sourceValue === "string") { + const bounded = sourceValue.slice(0, RESOURCE_MAX_STRING_LENGTH); + if (bounded.length !== sourceValue.length) { + markResourceProjection(notice); + notice.truncatedStrings = (notice.truncatedStrings ?? 0) + 1; + } + projectedValue = bounded; + } else if (sourceValue === null || typeof sourceValue === "boolean" || typeof sourceValue === "number") { + projectedValue = typeof sourceValue === "number" && !Number.isFinite(sourceValue) ? null : sourceValue; + } else if (Array.isArray(sourceValue) || isRecord(sourceValue)) { + if (frame.depth >= RESOURCE_MAX_DEPTH) { + markResourceProjection(notice, "depthLimitReached"); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + if (seen.has(sourceValue)) { + markResourceProjection(notice, "cycleDetected"); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + seen.add(sourceValue); + const childOutput = Array.isArray(sourceValue) ? [] : {}; + projectedValue = childOutput; + childFrame = { + source: sourceValue, + output: childOutput, + entries: boundedEntries(sourceValue, notice), + nextEntry: 0, + depth: frame.depth + 1 + }; + } else { + markResourceProjection(notice); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + const valueCost = childFrame === void 0 ? jsonBytes(projectedValue) : 2; + if (projectedBytes + keyCost + valueCost > RESOURCE_PAYLOAD_BUDGET_BYTES - RESOURCE_PROJECTION_RESERVE_BYTES) { + markResourceProjection(notice, "payloadBudgetReached"); + notice.omittedValues = (notice.omittedValues ?? 0) + 1 + remainingFrameEntries(frames); + break; + } + if (Array.isArray(frame.output)) { + frame.output.push(projectedValue); + } else { + frame.output[String(outputKey)] = projectedValue; + } + projectedBytes += keyCost + valueCost; + if (childFrame) frames.push(childFrame); + } + output.projection = notice; + stabilizeResourceProjection(output, notice); + return output; +} +function boundedEntries(source, notice) { + if (Array.isArray(source)) { + const count2 = Math.min(source.length, RESOURCE_MAX_ARRAY_ITEMS); + if (count2 < source.length) { + markResourceProjection(notice, "collectionLimitReached"); + notice.omittedValues = (notice.omittedValues ?? 0) + source.length - count2; + } + return Array.from({ length: count2 }, (_, index) => [index, source[index]]); + } + const keys = Object.keys(source); + const count = Math.min(keys.length, RESOURCE_MAX_OBJECT_KEYS); + if (count < keys.length) { + markResourceProjection(notice, "keyLimitReached"); + notice.omittedValues = (notice.omittedValues ?? 0) + keys.length - count; + } + return keys.slice(0, count).map((key) => [key, source[key]]); +} +function remainingFrameEntries(frames) { + return frames.reduce( + (total, frame) => total + frame.entries.length - frame.nextEntry, + 0 + ); +} +function markResourceProjection(notice, flag) { + notice.truncated = true; + if (flag) notice[flag] = true; +} +function stabilizeResourceProjection(output, notice) { + for (let attempt = 0; attempt < 6; attempt++) { + const bytes = jsonBytes(output); + if (notice.projectedBytes === bytes) return; + notice.projectedBytes = bytes; + } +} +function truncateHierarchy(hierarchy, maxNodes) { + const sourceRoots = Array.isArray(hierarchy.roots) ? hierarchy.roots : []; + const traversalBudget = Math.min( + 1e4, + Math.max(maxNodes * 4, maxNodes + 1024) + ); + const roots = []; + const frames = []; + const omissionOwners = /* @__PURE__ */ new Set(); + const projectedContentBudget = Math.max( + 0, + HIERARCHY_PAYLOAD_BUDGET_BYTES - HIERARCHY_ENVELOPE_RESERVE_BYTES - maxNodes * HIERARCHY_DYNAMIC_MARKER_RESERVE_PER_NODE + ); + let rootIndex = 0; + let visitedNodes = 0; + let returnedNodes = 0; + let rootsTruncated = false; + let projectedContentBytes = 0; + let payloadBudgetReached = false; + let omittedAtBudgetNodes = 0; + let omittedAtBudgetComponents = 0; + while (visitedNodes < traversalBudget) { + let source; + let parentOutput; + let inheritedOmissionOwner; + let isRoot = false; + while (frames.length > 0) { + const frame = frames[frames.length - 1]; + if (frame.nextChild < frame.children.length) { + source = frame.children[frame.nextChild++]; + parentOutput = frame.output; + inheritedOmissionOwner = frame.omissionOwner; + break; + } + frames.pop(); + } + if (source === void 0) { + if (rootIndex >= sourceRoots.length) break; + source = sourceRoots[rootIndex++]; + isRoot = true; + } + visitedNodes++; + if (!isRecord(source)) continue; + let output; + let omissionOwner; + let countedBudgetOmission = false; + const outputEligible = returnedNodes < maxNodes && (isRoot || parentOutput !== void 0); + if (outputEligible && !payloadBudgetReached) { + const destination = parentOutput?.children ?? roots; + const separatorBytes = destination.length > 0 ? 1 : 0; + const remainingBytes = projectedContentBudget - projectedContentBytes - separatorBytes; + const candidate = projectHierarchyNode(source); + const fitted = fitProjectedNodeToBudget(candidate, remainingBytes); + if (fitted.output) { + output = fitted.output; + returnedNodes++; + projectedContentBytes += separatorBytes + fitted.serializedBytes; + omittedAtBudgetComponents += fitted.omittedAtBudgetComponents; + destination.push(output); + if (fitted.payloadBudgetReached) { + payloadBudgetReached = true; + } + } else { + payloadBudgetReached = true; + omittedAtBudgetNodes++; + omittedAtBudgetComponents += sourceComponentCount(source); + countedBudgetOmission = true; + } + } + if (!output) { + if (payloadBudgetReached && !countedBudgetOmission && isRecord(source)) { + omittedAtBudgetNodes++; + omittedAtBudgetComponents += sourceComponentCount(source); + } + omissionOwner = parentOutput ?? inheritedOmissionOwner; + if (omissionOwner) { + omissionOwner.childrenTruncated = true; + omissionOwner.omittedDescendants = (omissionOwner.omittedDescendants ?? 0) + 1; + omissionOwners.add(omissionOwner); + } else { + rootsTruncated = true; + } + } + const children = Array.isArray(source.children) ? source.children : []; + if (children.length > 0) { + frames.push({ + children, + nextChild: 0, + output, + omissionOwner: output ? void 0 : omissionOwner + }); + } + } + const hasRemaining = rootIndex < sourceRoots.length || frames.some((frame) => frame.nextChild < frame.children.length); + const totalNodesKnown = !hasRemaining; + if (totalNodesKnown) { + for (const owner of omissionOwners) { + owner.omittedDescendantsKnown = true; + } + } else { + if (rootIndex < sourceRoots.length) rootsTruncated = true; + for (const owner of omissionOwners) { + owner.omittedDescendantsKnown = false; + } + for (const frame of frames) { + if (frame.nextChild >= frame.children.length) continue; + const owner = frame.output ?? frame.omissionOwner; + if (owner) { + owner.childrenTruncated = true; + owner.omittedDescendantsKnown = false; + } else { + rootsTruncated = true; + } + } + } + const truncation = totalNodesKnown ? { + truncated: returnedNodes < visitedNodes || payloadBudgetReached, + maxNodes, + traversalBudget, + visitedNodes, + returnedNodes, + totalNodesKnown: true, + totalNodes: visitedNodes, + omittedNodes: visitedNodes - returnedNodes, + rootsTruncated + } : { + truncated: true, + maxNodes, + traversalBudget, + visitedNodes, + returnedNodes, + totalNodesKnown: false, + totalNodesAtLeast: visitedNodes + 1, + omittedNodesAtLeast: visitedNodes + 1 - returnedNodes, + rootsTruncated + }; + Object.assign(truncation, { + payloadBudgetReached, + payloadBudgetBytes: HIERARCHY_PAYLOAD_BUDGET_BYTES, + projectedBytes: 0, + omittedAtBudgetNodes, + omittedAtBudgetComponents + }); + const result = { + ...projectHierarchyMetadata(hierarchy), + roots, + truncation + }; + stabilizeProjectedByteCount(result, truncation); + return result; +} +function fitProjectedNodeToBudget(candidate, maxBytes) { + let serializedBytes = jsonBytes(candidate); + if (serializedBytes <= maxBytes) { + return { + output: candidate, + serializedBytes, + payloadBudgetReached: false, + omittedAtBudgetComponents: 0 + }; + } + const components = Array.isArray(candidate.components) ? candidate.components : void 0; + if (!components || components.length === 0) { + return { + serializedBytes, + payloadBudgetReached: true, + omittedAtBudgetComponents: 0 + }; + } + const initialReturnedCount = components.length; + while (components.length > 0) { + components.pop(); + markComponentsOmittedAtBudget( + candidate, + initialReturnedCount - components.length, + initialReturnedCount + ); + serializedBytes = jsonBytes(candidate); + if (serializedBytes <= maxBytes) { + return { + output: candidate, + serializedBytes, + payloadBudgetReached: true, + omittedAtBudgetComponents: initialReturnedCount - components.length + }; + } + } + return { + serializedBytes, + payloadBudgetReached: true, + omittedAtBudgetComponents: initialReturnedCount + }; +} +function markComponentsOmittedAtBudget(node, omittedAtBudgetCount, initialReturnedCount) { + const projection = isRecord(node.projection) ? node.projection : {}; + node.projection = projection; + const existing = projection.components; + const returnedCount = initialReturnedCount - omittedAtBudgetCount; + projection.components = { + sourceCount: existing?.sourceCount ?? initialReturnedCount, + scannedCount: existing?.scannedCount ?? initialReturnedCount, + returnedCount, + omittedCount: (existing?.sourceCount ?? initialReturnedCount) - returnedCount, + invalidScanned: existing?.invalidScanned ?? 0, + namesTruncated: existing?.namesTruncated ?? 0, + scanTruncated: existing?.scanTruncated ?? false, + payloadBudgetReached: true, + omittedAtBudgetCount + }; +} +function sourceComponentCount(source) { + return Array.isArray(source.components) ? source.components.length : 0; +} +function stabilizeProjectedByteCount(result, truncation) { + for (let attempt = 0; attempt < 4; attempt++) { + const projectedBytes = jsonBytes(result); + if (truncation.projectedBytes === projectedBytes) return; + truncation.projectedBytes = projectedBytes; + } +} +function jsonBytes(value) { + return Buffer.byteLength(JSON.stringify(value)); +} +function projectHierarchyNode(source) { + const output = { + children: [], + childrenTruncated: source.childrenTruncated === true + }; + const projection = {}; + copyBoundedString( + source, + output, + projection, + "name", + NODE_NAME_MAX_LENGTH + ); + copyBoundedString( + source, + output, + projection, + "hierarchyPath", + HIERARCHY_PATH_MAX_LENGTH + ); + copyBoundedInstanceId(source, output, projection); + copyBoolean(source, output, projection, "activeSelf"); + copyBoundedComponents(source, output, projection); + if (hasProjectionNotice(projection)) { + output.projection = projection; + } + return output; +} +function projectHierarchyMetadata(hierarchy) { + const metadata = {}; + const projection = {}; + copyBoundedString( + hierarchy, + metadata, + projection, + "sceneName", + SCENE_NAME_MAX_LENGTH + ); + copyBoundedString( + hierarchy, + metadata, + projection, + "scenePath", + SCENE_PATH_MAX_LENGTH + ); + copyBoolean(hierarchy, metadata, projection, "isDirty"); + copyBoolean(hierarchy, metadata, projection, "isActive"); + if (hasProjectionNotice(projection)) { + metadata.metadataProjection = projection; + } + return metadata; +} +function copyBoundedString(source, output, projection, field, maxLength) { + if (!(field in source)) return; + const value = source[field]; + if (typeof value !== "string") { + markOmittedField(projection, field); + return; + } + output[field] = boundedString(value, maxLength, projection, field); +} +function copyBoundedInstanceId(source, output, projection) { + if (!("instanceId" in source)) return; + const value = source.instanceId; + if (typeof value === "number" && Number.isSafeInteger(value)) { + output.instanceId = value; + return; + } + if (typeof value === "string") { + output.instanceId = boundedString( + value, + INSTANCE_ID_MAX_LENGTH, + projection, + "instanceId" + ); + return; + } + markOmittedField(projection, "instanceId"); +} +function copyBoolean(source, output, projection, field) { + if (!(field in source)) return; + const value = source[field]; + if (typeof value === "boolean") { + output[field] = value; + } else { + markOmittedField(projection, field); + } +} +function copyBoundedComponents(source, output, projection) { + if (!("components" in source)) return; + if (!Array.isArray(source.components)) { + markOmittedField(projection, "components"); + return; + } + const sourceComponents = source.components; + const components = []; + let scannedCount = 0; + let invalidScanned = 0; + let namesTruncated = 0; + const scanCount = Math.min(sourceComponents.length, COMPONENT_SCAN_LIMIT); + while (scannedCount < scanCount && components.length < COMPONENT_MAX_COUNT) { + const candidate = sourceComponents[scannedCount++]; + const name = typeof candidate === "string" ? candidate : isRecord(candidate) && typeof candidate.name === "string" ? candidate.name : void 0; + if (name === void 0) { + invalidScanned++; + continue; + } + if (name.length > COMPONENT_NAME_MAX_LENGTH) namesTruncated++; + components.push(name.slice(0, COMPONENT_NAME_MAX_LENGTH)); + } + output.components = components; + const omittedCount = sourceComponents.length - components.length; + if (omittedCount > 0 || namesTruncated > 0 || invalidScanned > 0) { + projection.components = { + sourceCount: sourceComponents.length, + scannedCount, + returnedCount: components.length, + omittedCount, + invalidScanned, + namesTruncated, + scanTruncated: scannedCount < sourceComponents.length + }; + } +} +function boundedString(value, maxLength, projection, field) { + if (value.length <= maxLength) return value; + projection.truncatedStringCount = (projection.truncatedStringCount ?? 0) + 1; + projection.truncatedStrings ??= {}; + projection.truncatedStrings[field] = { + originalLength: value.length, + returnedLength: maxLength + }; + return value.slice(0, maxLength); +} +function markOmittedField(projection, field) { + projection.omittedKnownFieldCount = (projection.omittedKnownFieldCount ?? 0) + 1; + projection.omittedKnownFields ??= []; + projection.omittedKnownFields.push(field); +} +function hasProjectionNotice(projection) { + return projection.truncatedStrings !== void 0 || projection.omittedKnownFields !== void 0 || projection.components !== void 0; +} +function isRecord(value) { + return typeof value === "object" && value !== null && !Array.isArray(value); +} + +// node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/client.js +var ExperimentalClientTasks = class { + constructor(_client) { + this._client = _client; + } + /** + * Calls a tool and returns an AsyncGenerator that yields response messages. + * The generator is guaranteed to end with either a 'result' or 'error' message. + * + * This method provides streaming access to tool execution, allowing you to + * observe intermediate task status updates for long-running tool calls. + * Automatically validates structured output if the tool has an outputSchema. + * + * @example + * ```typescript + * const stream = client.experimental.tasks.callToolStream({ name: 'myTool', arguments: {} }); + * for await (const message of stream) { + * switch (message.type) { + * case 'taskCreated': + * console.log('Tool execution started:', message.task.taskId); + * break; + * case 'taskStatus': + * console.log('Tool status:', message.task.status); + * break; + * case 'result': + * console.log('Tool result:', message.result); + * break; + * case 'error': + * console.error('Tool error:', message.error); + * break; + * } + * } + * ``` + * + * @param params - Tool call parameters (name and arguments) + * @param resultSchema - Zod schema for validating the result (defaults to CallToolResultSchema) + * @param options - Optional request options (timeout, signal, task creation params, etc.) + * @returns AsyncGenerator that yields ResponseMessage objects + * + * @experimental + */ + async *callToolStream(params, resultSchema = CallToolResultSchema, options) { + const clientInternal = this._client; + const optionsWithTask = { + ...options, + // We check if the tool is known to be a task during auto-configuration, but assume + // the caller knows what they're doing if they pass this explicitly + task: options?.task ?? (clientInternal.isToolTask(params.name) ? {} : void 0) + }; + const stream = clientInternal.requestStream({ method: "tools/call", params }, resultSchema, optionsWithTask); + const validator = clientInternal.getToolOutputValidator(params.name); + for await (const message of stream) { + if (message.type === "result" && validator) { + const result = message.result; + if (!result.structuredContent && !result.isError) { + yield { + type: "error", + error: new McpError(ErrorCode.InvalidRequest, `Tool ${params.name} has an output schema but did not return structured content`) + }; + return; + } + if (result.structuredContent) { + try { + const validationResult = validator(result.structuredContent); + if (!validationResult.valid) { + yield { + type: "error", + error: new McpError(ErrorCode.InvalidParams, `Structured content does not match the tool's output schema: ${validationResult.errorMessage}`) + }; + return; + } + } catch (error2) { + if (error2 instanceof McpError) { + yield { type: "error", error: error2 }; + return; + } + yield { + type: "error", + error: new McpError(ErrorCode.InvalidParams, `Failed to validate structured content: ${error2 instanceof Error ? error2.message : String(error2)}`) + }; + return; + } + } + } + yield message; + } + } + /** + * Gets the current status of a task. + * + * @param taskId - The task identifier + * @param options - Optional request options + * @returns The task status + * + * @experimental + */ + async getTask(taskId, options) { + return this._client.getTask({ taskId }, options); + } + /** + * 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 + * + * @experimental + */ + async getTaskResult(taskId, resultSchema, options) { + return this._client.getTaskResult({ taskId }, resultSchema, options); + } + /** + * Lists tasks with optional pagination. + * + * @param cursor - Optional pagination cursor + * @param options - Optional request options + * @returns List of tasks with optional next cursor + * + * @experimental + */ + async listTasks(cursor, options) { + return this._client.listTasks(cursor ? { cursor } : void 0, options); + } + /** + * Cancels a running task. + * + * @param taskId - The task identifier + * @param options - Optional request options + * + * @experimental + */ + async cancelTask(taskId, options) { + return this._client.cancelTask({ taskId }, options); + } + /** + * Sends a request and returns an AsyncGenerator that yields response messages. + * The generator is guaranteed to end with either a 'result' or 'error' message. + * + * This method provides streaming access to request processing, allowing you to + * observe intermediate task status updates for task-augmented requests. + * + * @param request - The request to send + * @param resultSchema - Zod schema for validating the result + * @param options - Optional request options (timeout, signal, task creation params, etc.) + * @returns AsyncGenerator that yields ResponseMessage objects + * + * @experimental + */ + requestStream(request, resultSchema, options) { + return this._client.requestStream(request, resultSchema, options); + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js +function applyElicitationDefaults(schema, data) { + if (!schema || data === null || typeof data !== "object") + return; + if (schema.type === "object" && schema.properties && typeof schema.properties === "object") { + const obj = data; + const props = schema.properties; + for (const key of Object.keys(props)) { + const propSchema = props[key]; + if (obj[key] === void 0 && Object.prototype.hasOwnProperty.call(propSchema, "default")) { + obj[key] = propSchema.default; + } + if (obj[key] !== void 0) { + applyElicitationDefaults(propSchema, obj[key]); + } + } + } + if (Array.isArray(schema.anyOf)) { + for (const sub of schema.anyOf) { + if (typeof sub !== "boolean") { + applyElicitationDefaults(sub, data); + } + } + } + if (Array.isArray(schema.oneOf)) { + for (const sub of schema.oneOf) { + if (typeof sub !== "boolean") { + applyElicitationDefaults(sub, data); + } + } + } +} +function getSupportedElicitationModes(capabilities) { + if (!capabilities) { + return { supportsFormMode: false, supportsUrlMode: false }; + } + const hasFormCapability = capabilities.form !== void 0; + const hasUrlCapability = capabilities.url !== void 0; + const supportsFormMode = hasFormCapability || !hasFormCapability && !hasUrlCapability; + const supportsUrlMode = hasUrlCapability; + return { supportsFormMode, supportsUrlMode }; +} +var Client = class extends Protocol { + /** + * Initializes this client with the given name and version information. + */ + constructor(_clientInfo, options) { + super(options); + this._clientInfo = _clientInfo; + this._cachedToolOutputValidators = /* @__PURE__ */ new Map(); + this._cachedKnownTaskTools = /* @__PURE__ */ new Set(); + this._cachedRequiredTaskTools = /* @__PURE__ */ new Set(); + this._listChangedDebounceTimers = /* @__PURE__ */ new Map(); + this._capabilities = options?.capabilities ?? {}; + this._jsonSchemaValidator = options?.jsonSchemaValidator ?? new AjvJsonSchemaValidator(); + if (options?.listChanged) { + this._pendingListChangedConfig = options.listChanged; + } + } + /** + * Set up handlers for list changed notifications based on config and server capabilities. + * This should only be called after initialization when server capabilities are known. + * Handlers are silently skipped if the server doesn't advertise the corresponding listChanged capability. + * @internal + */ + _setupListChangedHandlers(config2) { + if (config2.tools && this._serverCapabilities?.tools?.listChanged) { + this._setupListChangedHandler("tools", ToolListChangedNotificationSchema, config2.tools, async () => { + const result = await this.listTools(); + return result.tools; + }); + } + if (config2.prompts && this._serverCapabilities?.prompts?.listChanged) { + this._setupListChangedHandler("prompts", PromptListChangedNotificationSchema, config2.prompts, async () => { + const result = await this.listPrompts(); + return result.prompts; + }); + } + if (config2.resources && this._serverCapabilities?.resources?.listChanged) { + this._setupListChangedHandler("resources", ResourceListChangedNotificationSchema, config2.resources, async () => { + const result = await this.listResources(); + return result.resources; + }); + } + } + /** + * Access experimental features. + * + * WARNING: These APIs are experimental and may change without notice. + * + * @experimental + */ + get experimental() { + if (!this._experimental) { + this._experimental = { + tasks: new ExperimentalClientTasks(this) + }; + } + return this._experimental; + } + /** + * Registers new capabilities. This can only be called before connecting to a transport. + * + * The new capabilities will be merged with any existing capabilities previously given (e.g., at initialization). + */ + registerCapabilities(capabilities) { + if (this.transport) { + throw new Error("Cannot register capabilities after connecting to transport"); + } + this._capabilities = mergeCapabilities(this._capabilities, capabilities); + } + /** + * Override request handler registration to enforce client-side validation for elicitation. + */ + setRequestHandler(requestSchema, handler) { + const shape = getObjectShape(requestSchema); + const methodSchema = shape?.method; + if (!methodSchema) { + throw new Error("Schema is missing a method literal"); + } + let methodValue; + if (isZ4Schema(methodSchema)) { + const v4Schema = methodSchema; + const v4Def = v4Schema._zod?.def; + methodValue = v4Def?.value ?? v4Schema.value; + } else { + const v3Schema = methodSchema; + const legacyDef = v3Schema._def; + methodValue = legacyDef?.value ?? v3Schema.value; + } + if (typeof methodValue !== "string") { + throw new Error("Schema method literal must be a string"); + } + const method = methodValue; + if (method === "elicitation/create") { + const wrappedHandler = async (request, extra) => { + const validatedRequest = safeParse3(ElicitRequestSchema, request); + if (!validatedRequest.success) { + const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid elicitation request: ${errorMessage}`); + } + const { params } = validatedRequest.data; + params.mode = params.mode ?? "form"; + const { supportsFormMode, supportsUrlMode } = getSupportedElicitationModes(this._capabilities.elicitation); + if (params.mode === "form" && !supportsFormMode) { + throw new McpError(ErrorCode.InvalidParams, "Client does not support form-mode elicitation requests"); + } + if (params.mode === "url" && !supportsUrlMode) { + throw new McpError(ErrorCode.InvalidParams, "Client does not support URL-mode elicitation requests"); + } + const result = await Promise.resolve(handler(request, extra)); + if (params.task) { + const taskValidationResult = safeParse3(CreateTaskResultSchema, result); + if (!taskValidationResult.success) { + const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`); + } + return taskValidationResult.data; + } + const validationResult = safeParse3(ElicitResultSchema, result); + if (!validationResult.success) { + const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid elicitation result: ${errorMessage}`); + } + const validatedResult = validationResult.data; + const requestedSchema = params.mode === "form" ? params.requestedSchema : void 0; + if (params.mode === "form" && validatedResult.action === "accept" && validatedResult.content && requestedSchema) { + if (this._capabilities.elicitation?.form?.applyDefaults) { + try { + applyElicitationDefaults(requestedSchema, validatedResult.content); + } catch { + } + } + } + return validatedResult; + }; + return super.setRequestHandler(requestSchema, wrappedHandler); + } + if (method === "sampling/createMessage") { + const wrappedHandler = async (request, extra) => { + const validatedRequest = safeParse3(CreateMessageRequestSchema, request); + if (!validatedRequest.success) { + const errorMessage = validatedRequest.error instanceof Error ? validatedRequest.error.message : String(validatedRequest.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid sampling request: ${errorMessage}`); + } + const { params } = validatedRequest.data; + const result = await Promise.resolve(handler(request, extra)); + if (params.task) { + const taskValidationResult = safeParse3(CreateTaskResultSchema, result); + if (!taskValidationResult.success) { + const errorMessage = taskValidationResult.error instanceof Error ? taskValidationResult.error.message : String(taskValidationResult.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid task creation result: ${errorMessage}`); + } + return taskValidationResult.data; + } + const hasTools = params.tools || params.toolChoice; + const resultSchema = hasTools ? CreateMessageResultWithToolsSchema : CreateMessageResultSchema; + const validationResult = safeParse3(resultSchema, result); + if (!validationResult.success) { + const errorMessage = validationResult.error instanceof Error ? validationResult.error.message : String(validationResult.error); + throw new McpError(ErrorCode.InvalidParams, `Invalid sampling result: ${errorMessage}`); + } + return validationResult.data; + }; + return super.setRequestHandler(requestSchema, wrappedHandler); + } + return super.setRequestHandler(requestSchema, handler); + } + assertCapability(capability, method) { + if (!this._serverCapabilities?.[capability]) { + throw new Error(`Server does not support ${capability} (required for ${method})`); + } + } + async connect(transport, options) { + await super.connect(transport); + if (transport.sessionId !== void 0) { + return; + } + try { + const result = await this.request({ + method: "initialize", + params: { + protocolVersion: LATEST_PROTOCOL_VERSION, + capabilities: this._capabilities, + clientInfo: this._clientInfo + } + }, InitializeResultSchema, options); + if (result === void 0) { + throw new Error(`Server sent invalid initialize result: ${result}`); + } + if (!SUPPORTED_PROTOCOL_VERSIONS.includes(result.protocolVersion)) { + throw new Error(`Server's protocol version is not supported: ${result.protocolVersion}`); + } + this._serverCapabilities = result.capabilities; + this._serverVersion = result.serverInfo; + if (transport.setProtocolVersion) { + transport.setProtocolVersion(result.protocolVersion); + } + this._instructions = result.instructions; + await this.notification({ + method: "notifications/initialized" + }); + if (this._pendingListChangedConfig) { + this._setupListChangedHandlers(this._pendingListChangedConfig); + this._pendingListChangedConfig = void 0; + } + } catch (error2) { + void this.close(); + throw error2; + } + } + /** + * After initialization has completed, this will be populated with the server's reported capabilities. + */ + getServerCapabilities() { + return this._serverCapabilities; + } + /** + * After initialization has completed, this will be populated with information about the server's name and version. + */ + getServerVersion() { + return this._serverVersion; + } + /** + * After initialization has completed, this may be populated with information about the server's instructions. + */ + getInstructions() { + return this._instructions; + } + assertCapabilityForMethod(method) { + switch (method) { + case "logging/setLevel": + if (!this._serverCapabilities?.logging) { + throw new Error(`Server does not support logging (required for ${method})`); + } + break; + case "prompts/get": + case "prompts/list": + if (!this._serverCapabilities?.prompts) { + throw new Error(`Server does not support prompts (required for ${method})`); + } + break; + case "resources/list": + case "resources/templates/list": + case "resources/read": + case "resources/subscribe": + case "resources/unsubscribe": + if (!this._serverCapabilities?.resources) { + throw new Error(`Server does not support resources (required for ${method})`); + } + if (method === "resources/subscribe" && !this._serverCapabilities.resources.subscribe) { + throw new Error(`Server does not support resource subscriptions (required for ${method})`); + } + break; + case "tools/call": + case "tools/list": + if (!this._serverCapabilities?.tools) { + throw new Error(`Server does not support tools (required for ${method})`); + } + break; + case "completion/complete": + if (!this._serverCapabilities?.completions) { + throw new Error(`Server does not support completions (required for ${method})`); + } + break; + case "initialize": + break; + case "ping": + break; + } + } + assertNotificationCapability(method) { + switch (method) { + case "notifications/roots/list_changed": + if (!this._capabilities.roots?.listChanged) { + throw new Error(`Client does not support roots list changed notifications (required for ${method})`); + } + break; + case "notifications/initialized": + break; + case "notifications/cancelled": + break; + case "notifications/progress": + break; + } + } + assertRequestHandlerCapability(method) { + if (!this._capabilities) { + return; + } + switch (method) { + case "sampling/createMessage": + if (!this._capabilities.sampling) { + throw new Error(`Client does not support sampling capability (required for ${method})`); + } + break; + case "elicitation/create": + if (!this._capabilities.elicitation) { + throw new Error(`Client does not support elicitation capability (required for ${method})`); + } + break; + case "roots/list": + if (!this._capabilities.roots) { + throw new Error(`Client does not support roots capability (required for ${method})`); + } + break; + case "tasks/get": + case "tasks/list": + case "tasks/result": + case "tasks/cancel": + if (!this._capabilities.tasks) { + throw new Error(`Client does not support tasks capability (required for ${method})`); + } + break; + case "ping": + break; + } + } + assertTaskCapability(method) { + assertToolsCallTaskCapability(this._serverCapabilities?.tasks?.requests, method, "Server"); + } + assertTaskHandlerCapability(method) { + if (!this._capabilities) { + return; + } + assertClientRequestTaskCapability(this._capabilities.tasks?.requests, method, "Client"); + } + async ping(options) { + return this.request({ method: "ping" }, EmptyResultSchema, options); + } + async complete(params, options) { + return this.request({ method: "completion/complete", params }, CompleteResultSchema, options); + } + async setLoggingLevel(level, options) { + return this.request({ method: "logging/setLevel", params: { level } }, EmptyResultSchema, options); + } + async getPrompt(params, options) { + return this.request({ method: "prompts/get", params }, GetPromptResultSchema, options); + } + async listPrompts(params, options) { + return this.request({ method: "prompts/list", params }, ListPromptsResultSchema, options); + } + async listResources(params, options) { + return this.request({ method: "resources/list", params }, ListResourcesResultSchema, options); + } + async listResourceTemplates(params, options) { + return this.request({ method: "resources/templates/list", params }, ListResourceTemplatesResultSchema, options); + } + async readResource(params, options) { + return this.request({ method: "resources/read", params }, ReadResourceResultSchema, options); + } + async subscribeResource(params, options) { + return this.request({ method: "resources/subscribe", params }, EmptyResultSchema, options); + } + async unsubscribeResource(params, options) { + return this.request({ method: "resources/unsubscribe", params }, EmptyResultSchema, options); + } + /** + * Calls a tool and waits for the result. Automatically validates structured output if the tool has an outputSchema. + * + * For task-based execution with streaming behavior, use client.experimental.tasks.callToolStream() instead. + */ + async callTool(params, resultSchema = CallToolResultSchema, options) { + if (this.isToolTaskRequired(params.name)) { + throw new McpError(ErrorCode.InvalidRequest, `Tool "${params.name}" requires task-based execution. Use client.experimental.tasks.callToolStream() instead.`); + } + const result = await this.request({ method: "tools/call", params }, resultSchema, options); + const validator = this.getToolOutputValidator(params.name); + if (validator) { + if (!result.structuredContent && !result.isError) { + throw new McpError(ErrorCode.InvalidRequest, `Tool ${params.name} has an output schema but did not return structured content`); + } + if (result.structuredContent) { + try { + const validationResult = validator(result.structuredContent); + if (!validationResult.valid) { + throw new McpError(ErrorCode.InvalidParams, `Structured content does not match the tool's output schema: ${validationResult.errorMessage}`); + } + } catch (error2) { + if (error2 instanceof McpError) { + throw error2; + } + throw new McpError(ErrorCode.InvalidParams, `Failed to validate structured content: ${error2 instanceof Error ? error2.message : String(error2)}`); + } + } + } + return result; + } + isToolTask(toolName) { + if (!this._serverCapabilities?.tasks?.requests?.tools?.call) { + return false; + } + return this._cachedKnownTaskTools.has(toolName); + } + /** + * Check if a tool requires task-based execution. + * Unlike isToolTask which includes 'optional' tools, this only checks for 'required'. + */ + isToolTaskRequired(toolName) { + return this._cachedRequiredTaskTools.has(toolName); + } + /** + * Cache validators for tool output schemas. + * Called after listTools() to pre-compile validators for better performance. + */ + cacheToolMetadata(tools) { + this._cachedToolOutputValidators.clear(); + this._cachedKnownTaskTools.clear(); + this._cachedRequiredTaskTools.clear(); + for (const tool of tools) { + if (tool.outputSchema) { + const toolValidator = this._jsonSchemaValidator.getValidator(tool.outputSchema); + this._cachedToolOutputValidators.set(tool.name, toolValidator); + } + const taskSupport = tool.execution?.taskSupport; + if (taskSupport === "required" || taskSupport === "optional") { + this._cachedKnownTaskTools.add(tool.name); + } + if (taskSupport === "required") { + this._cachedRequiredTaskTools.add(tool.name); + } + } + } + /** + * Get cached validator for a tool + */ + getToolOutputValidator(toolName) { + return this._cachedToolOutputValidators.get(toolName); + } + async listTools(params, options) { + const result = await this.request({ method: "tools/list", params }, ListToolsResultSchema, options); + this.cacheToolMetadata(result.tools); + return result; + } + /** + * Set up a single list changed handler. + * @internal + */ + _setupListChangedHandler(listType, notificationSchema, options, fetcher) { + const parseResult = ListChangedOptionsBaseSchema.safeParse(options); + if (!parseResult.success) { + throw new Error(`Invalid ${listType} listChanged options: ${parseResult.error.message}`); + } + if (typeof options.onChanged !== "function") { + throw new Error(`Invalid ${listType} listChanged options: onChanged must be a function`); + } + const { autoRefresh, debounceMs } = parseResult.data; + const { onChanged } = options; + const refresh = async () => { + if (!autoRefresh) { + onChanged(null, null); + return; + } + try { + const items = await fetcher(); + onChanged(null, items); + } catch (e) { + const error2 = e instanceof Error ? e : new Error(String(e)); + onChanged(error2, null); + } + }; + const handler = () => { + if (debounceMs) { + const existingTimer = this._listChangedDebounceTimers.get(listType); + if (existingTimer) { + clearTimeout(existingTimer); + } + const timer = setTimeout(refresh, debounceMs); + this._listChangedDebounceTimers.set(listType, timer); + } else { + refresh(); + } + }; + this.setNotificationHandler(notificationSchema, handler); + } + async sendRootsListChanged() { + return this.notification({ method: "notifications/roots/list_changed" }); + } +}; + +// node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js +var import_cross_spawn = __toESM(require_cross_spawn(), 1); +import process3 from "node:process"; +import { PassThrough } from "node:stream"; +var DEFAULT_INHERITED_ENV_VARS = process3.platform === "win32" ? [ + "APPDATA", + "HOMEDRIVE", + "HOMEPATH", + "LOCALAPPDATA", + "PATH", + "PROCESSOR_ARCHITECTURE", + "SYSTEMDRIVE", + "SYSTEMROOT", + "TEMP", + "USERNAME", + "USERPROFILE", + "PROGRAMFILES" +] : ( + /* list inspired by the default env inheritance of sudo */ + ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"] +); +function getDefaultEnvironment() { + const env = {}; + for (const key of DEFAULT_INHERITED_ENV_VARS) { + const value = process3.env[key]; + if (value === void 0) { + continue; + } + if (value.startsWith("()")) { + continue; + } + env[key] = value; + } + return env; +} +var StdioClientTransport = class { + constructor(server) { + this._readBuffer = new ReadBuffer(); + this._stderrStream = null; + this._serverParams = server; + if (server.stderr === "pipe" || server.stderr === "overlapped") { + this._stderrStream = new PassThrough(); + } + } + /** + * Starts the server process and prepares to communicate with it. + */ + async start() { + if (this._process) { + throw new Error("StdioClientTransport already started! If using Client class, note that connect() calls start() automatically."); + } + return new Promise((resolve, reject) => { + this._process = (0, import_cross_spawn.default)(this._serverParams.command, this._serverParams.args ?? [], { + // merge default env with server env because mcp server needs some env vars + env: { + ...getDefaultEnvironment(), + ...this._serverParams.env + }, + stdio: ["pipe", "pipe", this._serverParams.stderr ?? "inherit"], + shell: false, + windowsHide: process3.platform === "win32" && isElectron(), + cwd: this._serverParams.cwd + }); + this._process.on("error", (error2) => { + reject(error2); + this.onerror?.(error2); + }); + this._process.on("spawn", () => { + resolve(); + }); + this._process.on("close", (_code) => { + this._process = void 0; + this.onclose?.(); + }); + this._process.stdin?.on("error", (error2) => { + this.onerror?.(error2); + }); + this._process.stdout?.on("data", (chunk) => { + this._readBuffer.append(chunk); + this.processReadBuffer(); + }); + this._process.stdout?.on("error", (error2) => { + this.onerror?.(error2); + }); + if (this._stderrStream && this._process.stderr) { + this._process.stderr.pipe(this._stderrStream); + } + }); + } + /** + * The stderr stream of the child process, if `StdioServerParameters.stderr` was set to "pipe" or "overlapped". + * + * If stderr piping was requested, a PassThrough stream is returned _immediately_, allowing callers to + * attach listeners before the start method is invoked. This prevents loss of any early + * error output emitted by the child process. + */ + get stderr() { + if (this._stderrStream) { + return this._stderrStream; + } + return this._process?.stderr ?? null; + } + /** + * The child process pid spawned by this transport. + * + * This is only available after the transport has been started. + */ + get pid() { + return this._process?.pid ?? null; + } + processReadBuffer() { + while (true) { + try { + const message = this._readBuffer.readMessage(); + if (message === null) { + break; + } + this.onmessage?.(message); + } catch (error2) { + this.onerror?.(error2); + } + } + } + async close() { + if (this._process) { + const processToClose = this._process; + this._process = void 0; + const closePromise = new Promise((resolve) => { + processToClose.once("close", () => { + resolve(); + }); + }); + try { + processToClose.stdin?.end(); + } catch { + } + await Promise.race([closePromise, new Promise((resolve) => setTimeout(resolve, 2e3).unref())]); + if (processToClose.exitCode === null) { + try { + processToClose.kill("SIGTERM"); + } catch { + } + await Promise.race([closePromise, new Promise((resolve) => setTimeout(resolve, 2e3).unref())]); + } + if (processToClose.exitCode === null) { + try { + processToClose.kill("SIGKILL"); + } catch { + } + } + } + this._readBuffer.clear(); + } + send(message) { + return new Promise((resolve) => { + if (!this._process?.stdin) { + throw new Error("Not connected"); + } + const json = serializeMessage(message); + if (this._process.stdin.write(json)) { + resolve(); + } else { + this._process.stdin.once("drain", resolve); + } + }); + } +}; +function isElectron() { + return "type" in process3; +} + +// src/unity/officialUnityMcpClient.ts +var CLOSED_MESSAGE = "Official Unity MCP client is closed."; +var OfficialUnityMcpClient = class { + options; + sessionFactory; + startTeardowns = /* @__PURE__ */ new WeakMap(); + closeSignal; + signalClose; + active; + activeStart; + startupPromise; + teardownPromise; + closePromise; + connectionState = "disconnected"; + closed = false; + constructor(options) { + this.options = { + cliPath: options.cliPath, + projectPath: options.projectPath + }; + this.sessionFactory = options.sessionFactory ?? createOfficialUnitySessionStart; + this.closeSignal = new Promise((resolve) => { + this.signalClose = resolve; + }); + } + get state() { + return this.connectionState; + } + async readTool(name, args) { + this.assertOpen(); + const firstConnection = await this.getConnection(); + try { + return await this.raceWithClose(firstConnection.session.callTool(name, args)); + } catch (firstError) { + if (!isTransportInterruption(firstError)) { + throw firstError; + } + await this.discardConnection(firstConnection); + this.assertOpen(); + const retryConnection = await this.getConnection(); + try { + return await this.raceWithClose(retryConnection.session.callTool(name, args)); + } catch (retryError) { + if (isTransportInterruption(retryError)) { + await this.discardConnection(retryConnection); + } + throw retryError; + } + } + } + close() { + if (this.closePromise) return this.closePromise; + this.closed = true; + this.connectionState = "closed"; + this.signalClose(); + const start = this.activeStart; + const teardown = this.teardownPromise; + this.active = void 0; + this.activeStart = void 0; + const pending = /* @__PURE__ */ new Set(); + if (teardown) pending.add(teardown); + if (start) pending.add(this.closeStart(start)); + this.closePromise = Promise.all([...pending]).then(() => void 0); + return this.closePromise; + } + async getConnection() { + this.assertOpen(); + if (this.teardownPromise) { + await this.raceWithClose(this.teardownPromise); + this.assertOpen(); + } + if (this.active) return this.active; + if (this.startupPromise) { + return this.raceWithClose(this.startupPromise); + } + this.connectionState = "connecting"; + let start; + try { + start = this.sessionFactory(this.options); + } catch (error2) { + this.connectionState = "disconnected"; + throw error2; + } + this.activeStart = start; + const pending = start.ready.then((session) => { + if (this.closed || this.activeStart !== start) { + throw new Error(CLOSED_MESSAGE); + } + const connection = { start, session }; + this.active = connection; + this.connectionState = "connected"; + return connection; + }).catch(async (error2) => { + if (this.activeStart === start) { + this.activeStart = void 0; + if (!this.closed) { + this.connectionState = "disconnected"; + } + } + await this.trackTeardown(start); + throw error2; + }).finally(() => { + if (this.startupPromise === pending) { + this.startupPromise = void 0; + } + }); + this.startupPromise = pending; + return this.raceWithClose(pending); + } + async discardConnection(candidate) { + if (this.active?.start === candidate.start) { + this.active = void 0; + } + if (this.activeStart === candidate.start) { + this.activeStart = void 0; + } + if (!this.closed) { + this.connectionState = "disconnected"; + } + await this.trackTeardown(candidate.start); + } + closeStart(start) { + const existing = this.startTeardowns.get(start); + if (existing) return existing; + const teardown = Promise.resolve().then(() => start.close()); + this.startTeardowns.set(start, teardown); + return teardown; + } + async trackTeardown(start) { + const teardown = this.closeStart(start); + this.teardownPromise = teardown; + try { + await teardown; + } finally { + if (this.teardownPromise === teardown) { + this.teardownPromise = void 0; + } + } + } + async raceWithClose(operation) { + return Promise.race([ + operation, + this.closeSignal.then(() => { + throw new Error(CLOSED_MESSAGE); + }) + ]); + } + assertOpen() { + if (this.closed) { + throw new Error(CLOSED_MESSAGE); + } + } +}; +function isTransportInterruption(error2) { + if (error2 instanceof McpError && error2.code === ErrorCode.ConnectionClosed) { + return true; + } + const code = error2?.code; + if (typeof code === "string" && ["EPIPE", "ECONNRESET", "ECONNREFUSED", "ENOTCONN", "ERR_STREAM_DESTROYED"].includes( + code + )) { + return true; + } + const message = error2 instanceof Error ? error2.message : String(error2); + return /^(Connection closed|Not connected)$/i.test(message.trim()) || /transport (?:is )?closed/i.test(message) || /Unity CLI process exited/i.test(message); +} +var INITIALIZE_TIMEOUT_MS = 1e4; +var CHILD_CLOSE_OBSERVATION_TIMEOUT_MS = 500; +var SDK_CLOSE_GRACE_MS = 4250; +var TRANSPORT_CLOSE_TIMEOUT_MS = 4750; +var OwnedStdioClientTransport = class { + constructor(underlying, closeObservationTimeoutMs = CHILD_CLOSE_OBSERVATION_TIMEOUT_MS) { + this.underlying = underlying; + this.closeObservationTimeoutMs = closeObservationTimeoutMs; + this.childClosed = new Promise((resolve) => { + this.resolveChildClosed = resolve; + }); + this.underlying.onclose = () => { + if (!this.childCloseObserved) { + this.childCloseObserved = true; + this.resolveChildClosed(); + } + if (!this.closeForwarded) { + this.closeForwarded = true; + this.onclose?.(); + } + }; + this.underlying.onerror = (error2) => this.onerror?.(error2); + this.underlying.onmessage = (message, extra) => this.onmessage?.(message, extra); + } + underlying; + closeObservationTimeoutMs; + onclose; + onerror; + onmessage; + childClosed; + resolveChildClosed; + closePromise; + startSucceeded = false; + childCloseObserved = false; + closeForwarded = false; + get sessionId() { + return this.underlying.sessionId; + } + set sessionId(value) { + this.underlying.sessionId = value; + } + async start() { + await this.underlying.start(); + this.startSucceeded = true; + } + send(message, options) { + return this.underlying.send(message, options); + } + setProtocolVersion(version2) { + this.underlying.setProtocolVersion?.(version2); + } + close() { + if (this.closePromise) return this.closePromise; + this.closePromise = this.closeOwnedChild(); + return this.closePromise; + } + async closeOwnedChild() { + await this.underlying.close(); + if (!this.startSucceeded || this.childCloseObserved) return; + const observation = await settleWithin( + this.childClosed, + this.closeObservationTimeoutMs + ); + if (observation.status === "fulfilled") return; + throw new Error( + `Unity CLI child did not report process closure within ${this.closeObservationTimeoutMs}ms after stdio transport teardown.` + ); + } +}; +var DEFAULT_SESSION_DEPENDENCIES = { + createTransport: (options) => new OwnedStdioClientTransport( + new StdioClientTransport({ + command: options.cliPath, + args: ["mcp", "--project-path", options.projectPath], + stderr: "inherit" + }) + ), + createClient: () => { + const client = new Client( + { name: "mcp-unity-companion", version: "2.0.0" }, + { capabilities: {} } + ); + return { + connect: (transport, options) => client.connect(transport, options), + callTool: (request, schema) => client.callTool(request, schema), + close: () => client.close() + }; + } +}; +function createOfficialUnitySessionStart(options, dependencies = DEFAULT_SESSION_DEPENDENCIES) { + const transport = dependencies.createTransport(options); + const client = dependencies.createClient(); + const initializeAbort = new AbortController(); + let closeRequested = false; + let teardownPromise; + const ready = client.connect(transport, { + signal: initializeAbort.signal, + timeout: INITIALIZE_TIMEOUT_MS, + maxTotalTimeout: INITIALIZE_TIMEOUT_MS + }).then(() => { + if (closeRequested) { + throw new Error(CLOSED_MESSAGE); + } + return { + callTool: async (name, args) => { + const result = await client.callTool( + { name, arguments: args }, + CallToolResultSchema + ); + return CallToolResultSchema.parse(result); + } + }; + }); + return { + ready, + close() { + if (teardownPromise) return teardownPromise; + closeRequested = true; + initializeAbort.abort(); + teardownPromise = teardownSdkSession(client, transport, { + sdkCloseGraceMs: dependencies.sdkCloseGraceMs ?? SDK_CLOSE_GRACE_MS, + transportCloseTimeoutMs: dependencies.transportCloseTimeoutMs ?? TRANSPORT_CLOSE_TIMEOUT_MS + }); + return teardownPromise; + } + }; +} +async function teardownSdkSession(client, transport, deadlines) { + const clientClose = invokeClose(() => client.close()); + const clientResult = await settleWithin(clientClose, deadlines.sdkCloseGraceMs); + if (clientResult.status === "fulfilled") return; + const transportClose = invokeClose(() => transport.close()); + const transportResult = await settleWithin( + transportClose, + deadlines.transportCloseTimeoutMs + ); + if (transportResult.status === "fulfilled") return; + if (transportResult.status === "timed-out") { + throw new Error( + `Unity CLI transport teardown timed out after ${deadlines.transportCloseTimeoutMs}ms.` + ); + } + throw new Error( + boundedErrorMessage( + "Unity CLI transport teardown failed: ", + transportResult.reason + ) + ); +} +function invokeClose(operation) { + try { + return Promise.resolve(operation()); + } catch (error2) { + return Promise.reject(error2); + } +} +function settleWithin(operation, timeoutMs) { + return new Promise((resolve) => { + let settled = false; + const finish = (result) => { + if (settled) return; + settled = true; + clearTimeout(timeout); + resolve(result); + }; + const timeout = setTimeout( + () => finish({ status: "timed-out" }), + Math.max(0, timeoutMs) + ); + operation.then( + () => finish({ status: "fulfilled" }), + (reason) => finish({ status: "rejected", reason }) + ); + }); +} + +// src/companionEntrypoint.ts +async function startCompanion(options) { + const args = parseCompanionArguments(options.argv, options.isUnityProject); + const cliPath = resolveUnityCliPath(args.unityCliPath, options.environment); + const checked = await (options.checkCli ?? checkUnityCli)(cliPath); + if (checked.warning) { + options.stderr.write(`Warning: ${checked.warning} +`); + } + const officialClient = new OfficialUnityMcpClient({ + cliPath: checked.command, + projectPath: args.projectPath + }); + const server = createCompanionServer( + new CompanionResourceService(officialClient) + ); + await server.connect(options.transport); + const handlers = installShutdownHandlers({ + signals: options.signals, + stdin: options.stdin, + closeOfficialClient: () => officialClient.close(), + closeServer: () => server.close(), + onError: (error2) => { + options.stderr.write(`Shutdown error: ${boundedErrorDetail(error2)} +`); + } + }); + return { + officialClient, + shutdown: async () => { + await handlers.shutdown(); + handlers.dispose(); + } + }; +} + +// src/index.ts +try { + await startCompanion({ + argv: process.argv.slice(2), + environment: process.env, + transport: new StdioServerTransport(), + signals: process, + stdin: process.stdin, + stderr: process.stderr + }); +} catch (error2) { + process.stderr.write( + `${boundedErrorMessage("MCP Unity Companion could not start: ", error2)} +` + ); + process.exitCode = 1; +} diff --git a/Server~/build/ui/unity-dashboard.html b/Server~/build/ui/unity-dashboard.html new file mode 100644 index 00000000..de029b5c --- /dev/null +++ b/Server~/build/ui/unity-dashboard.html @@ -0,0 +1,166 @@ + + + + + + Unity Dashboard + + + +
+
+

Unity CLI + Pipeline

+
+ Not checked + Never refreshed +
+
+ +
+
+
+

Scene hierarchy

Waiting…
+

Console logs

Waiting…
+

Packages

Waiting…
+

Tests

Waiting…
+

GameObject inspector

Select a target URI such as unity://gameobject/%2FPlayer in your MCP client.
+

Companion resources

unity://logs{?severity,limit}
+unity://scenes-hierarchy{?path,max_nodes}
+unity://gameobject/{target}
+unity://packages{?include_indirect}
+unity://tests/{mode}
+ui://unity-dashboard
+
+ + + diff --git a/Server~/package-lock.json b/Server~/package-lock.json index 2fb44f95..46222668 100644 --- a/Server~/package-lock.json +++ b/Server~/package-lock.json @@ -1,50 +1,39 @@ { - "name": "mcp-unity-server", - "version": "1.4.0", + "name": "mcp-unity-companion", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "mcp-unity-server", - "version": "1.4.0", + "name": "mcp-unity-companion", + "version": "2.0.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.0.0", - "@modelcontextprotocol/sdk": "^1.7.0", - "axios": "^1.8.4", - "cors": "^2.8.5", - "express": "^5.0.1", - "uuid": "^11.1.0", - "winreg": "^1.2.5", - "ws": "^8.18.1", - "zod": "^3.24.4", - "zod-to-json-schema": "^3.24.3" - }, - "bin": { - "mcp-unity-server": "build/index.js" + "@modelcontextprotocol/ext-apps": "1.0.1", + "@modelcontextprotocol/sdk": "1.26.0", + "zod": "3.25.76" }, "devDependencies": { "@modelcontextprotocol/inspector": "^0.20.0", - "@types/cors": "^2.8.17", - "@types/express": "^5.0.0", "@types/jest": "^29.5.14", "@types/node": "^22.13.10", - "@types/uuid": "^10.0.0", - "@types/winreg": "^1.2.36", - "@types/ws": "^8.18.0", + "esbuild": "0.28.1", "jest": "^29.7.0", "ts-jest": "^29.2.5", "typescript": "^5.8.2" + }, + "engines": { + "node": ">=20" } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -53,9 +42,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -63,22 +52,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -95,14 +83,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -112,14 +100,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -129,9 +117,9 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -139,29 +127,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -181,9 +169,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -191,9 +179,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -201,9 +189,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -211,27 +199,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -480,33 +468,33 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", "debug": "^4.3.1" }, "engines": { @@ -514,48 +502,490 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": ">=6.9.0" + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, "node_modules/@floating-ui/core": { @@ -601,9 +1031,9 @@ "license": "MIT" }, "node_modules/@hono/node-server": { - "version": "1.19.9", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", - "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "version": "1.19.15", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.15.tgz", + "integrity": "sha512-Za2ai6TLdKjUvnur+eenO6nuYYipVAEhyCAdaV8IRvmU9kK8crOZUSYvIXn72E4f8fJqyAbpcJuTsYYmZp9Deg==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -1200,7 +1630,6 @@ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", "license": "MIT", - "peer": true, "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", @@ -1237,9 +1666,9 @@ } }, "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2700,62 +3129,6 @@ "@babel/types": "^7.28.2" } }, - "node_modules/@types/body-parser": { - "version": "1.19.5", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", - "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cors": { - "version": "2.8.17", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", - "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz", - "integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", - "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", @@ -2766,13 +3139,6 @@ "@types/node": "*" } }, - "node_modules/@types/http-errors": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", - "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -2811,13 +3177,6 @@ "pretty-format": "^29.0.0" } }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/node": { "version": "22.13.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.16.tgz", @@ -2835,20 +3194,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/qs": { - "version": "6.9.18", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", - "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/react": { "version": "18.3.28", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", @@ -2860,29 +3205,6 @@ "csstype": "^3.2.2" } }, - "node_modules/@types/send": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", - "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.7", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", - "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2890,30 +3212,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/winreg": { - "version": "1.2.36", - "resolved": "https://registry.npmjs.org/@types/winreg/-/winreg-1.2.36.tgz", - "integrity": "sha512-DtafHy5A8hbaosXrbr7YdjQZaqVewXmiasRS5J4tYMzt3s1gkh40ixpxgVFfKiQ0JIYetTJABat47v9cpr/sQg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/yargs": { "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", @@ -2971,9 +3269,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3005,9 +3303,9 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -3112,23 +3410,6 @@ "node": ">=10" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", - "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -3253,31 +3534,47 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz", + "integrity": "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==", "dev": true, "license": "Apache-2.0", "bin": { - "baseline-browser-mapping": "dist/cli.js" + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" } }, "node_modules/body-parser": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", - "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", + "content-type": "^2.0.0", "debug": "^4.4.3", - "http-errors": "^2.0.0", - "iconv-lite": "^0.7.0", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", "on-finished": "^2.4.1", - "qs": "^6.14.1", - "raw-body": "^3.0.1", - "type-is": "^2.0.1" + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", "engines": { "node": ">=18" }, @@ -3287,9 +3584,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -3311,9 +3608,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", "dev": true, "funding": [ { @@ -3330,13 +3627,12 @@ } ], "license": "MIT", - "peer": true, "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -3450,9 +3746,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001762", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001762.tgz", - "integrity": "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "dev": true, "funding": [ { @@ -3613,18 +3909,6 @@ "dev": true, "license": "MIT" }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", @@ -3643,15 +3927,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz", + "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", - "shell-quote": "1.8.3", + "shell-quote": "1.9.0", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" @@ -3887,15 +4171,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -3963,9 +4238,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", "dev": true, "license": "ISC" }, @@ -4027,9 +4302,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -4038,19 +4313,46 @@ "node": ">= 0.4" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { @@ -4178,7 +4480,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -4218,12 +4519,13 @@ } }, "node_modules/express-rate-limit": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", - "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.6.0.tgz", + "integrity": "sha512-XKJXDsASUOo0LLtFwW5hCcQGH0N4WQc/Rn8/Pvoia+TJFOkkFPvrtW9lZOeeNcxQJspvOIERMwiRLsVFlhHEkA==", "license": "MIT", "dependencies": { - "ip-address": "10.0.1" + "debug": "^4.4.3", + "ip-address": "^10.2.0" }, "engines": { "node": ">= 16" @@ -4249,9 +4551,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -4342,62 +4644,6 @@ "node": ">=8" } }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -4592,9 +4838,9 @@ "license": "ISC" }, "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4635,21 +4881,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4663,11 +4894,10 @@ } }, "node_modules/hono": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", - "integrity": "sha512-Eaw2YTGM6WOxA6CXbckaEvslr2Ne4NFsKrvc0v97JD5awbmeBLO5w9Ho9L9kmKonrwF9RJlW6BxT1PVv/agBHQ==", + "version": "4.12.31", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.31.tgz", + "integrity": "sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -4791,9 +5021,9 @@ } }, "node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", "license": "MIT", "engines": { "node": ">= 12" @@ -5027,7 +5257,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -5635,9 +5864,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz", + "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==", "dev": true, "license": "MIT", "dependencies": { @@ -5903,9 +6132,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -6002,11 +6231,14 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", - "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6235,12 +6467,13 @@ "license": "MIT" }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/picocolors": { @@ -6251,9 +6484,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -6361,12 +6594,6 @@ "node": ">= 0.10" } }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6395,12 +6622,13 @@ "license": "MIT" }, "node_modules/qs": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", - "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -6439,7 +6667,6 @@ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6453,7 +6680,6 @@ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -6745,16 +6971,16 @@ } }, "node_modules/serve-handler": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.6.tgz", - "integrity": "sha512-x5RL9Y2p5+Sh3D38Fh9i/iQ5ZK+e4xuXRd/pGbM4D13tgo/MGwbttUk8emytcr1YYzBYs+apnUngBDFYfpjPuQ==", + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/serve-handler/-/serve-handler-6.1.7.tgz", + "integrity": "sha512-CinAq1xWb0vR3twAv9evEU8cNWkXCb9kd5ePAHUKJBkOsUpR1wt/CvGdeca7vqumL1U5cSaeVQ6zZMxiJ3yWsg==", "dev": true, "license": "MIT", "dependencies": { "bytes": "3.0.0", "content-disposition": "0.5.2", "mime-types": "2.1.18", - "minimatch": "3.1.2", + "minimatch": "3.1.5", "path-is-inside": "1.0.2", "path-to-regexp": "3.3.0", "range-parser": "1.2.0" @@ -6863,9 +7089,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -6911,14 +7137,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -6930,13 +7156,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -7387,17 +7613,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typescript": { @@ -7530,19 +7773,6 @@ } } }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -7609,12 +7839,6 @@ "node": ">= 8" } }, - "node_modules/winreg": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/winreg/-/winreg-1.2.5.tgz", - "integrity": "sha512-uf7tHf+tw0B1y+x+mKTLHkykBgK2KMs3g+KlzmyMbLvICSHQyB/xOFjTT8qZ3oeTFyU7Bbj4FzXitGG6jvKhYw==", - "license": "BSD-2-Clause" - }, "node_modules/wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -7661,9 +7885,10 @@ } }, "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10.0.0" @@ -7771,7 +7996,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/Server~/package.json b/Server~/package.json index 8dcb1184..12556e8d 100644 --- a/Server~/package.json +++ b/Server~/package.json @@ -1,22 +1,21 @@ { - "name": "mcp-unity-server", - "version": "1.4.0", - "description": "MCP Unity Server for executing Unity operations and request Editor information", - "main": "dist/index.js", + "name": "mcp-unity-companion", + "version": "2.0.0", + "description": "Optional read-oriented MCP companion for Unity CLI and Pipeline", + "main": "build/index.js", "type": "module", - "mcpName": "io.github.codergamester/mcp-unity", - "bin": { - "mcp-unity-server": "./build/index.js" + "private": true, + "engines": { + "node": ">=20" }, - "files": [ - "build" - ], "scripts": { - "build": "tsc && node scripts/copy-ui.mjs", + "build": "node scripts/build-bundle.mjs", + "build:check": "node scripts/build-bundle.mjs --check", "start": "node build/index.js", "watch": "tsc --watch", "inspector": "npx @modelcontextprotocol/inspector build/index.js", "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js", + "test:clean-archive-mcp": "node scripts/clean-archive-mcp-smoke.mjs", "test:watch": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watch" }, "keywords": [ @@ -30,27 +29,16 @@ "license": "MIT", "devDependencies": { "@modelcontextprotocol/inspector": "^0.20.0", - "@types/cors": "^2.8.17", - "@types/express": "^5.0.0", "@types/jest": "^29.5.14", "@types/node": "^22.13.10", - "@types/uuid": "^10.0.0", - "@types/winreg": "^1.2.36", - "@types/ws": "^8.18.0", + "esbuild": "0.28.1", "jest": "^29.7.0", "ts-jest": "^29.2.5", "typescript": "^5.8.2" }, "dependencies": { - "@modelcontextprotocol/ext-apps": "^1.0.0", - "@modelcontextprotocol/sdk": "^1.7.0", - "axios": "^1.8.4", - "cors": "^2.8.5", - "express": "^5.0.1", - "uuid": "^11.1.0", - "winreg": "^1.2.5", - "ws": "^8.18.1", - "zod": "^3.24.4", - "zod-to-json-schema": "^3.24.3" + "@modelcontextprotocol/ext-apps": "1.0.1", + "@modelcontextprotocol/sdk": "1.26.0", + "zod": "3.25.76" } } diff --git a/Server~/scripts/build-bundle.mjs b/Server~/scripts/build-bundle.mjs new file mode 100644 index 00000000..c9084f52 --- /dev/null +++ b/Server~/scripts/build-bundle.mjs @@ -0,0 +1,186 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { execFileSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; +import { build } from 'esbuild'; + +const serverRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', +); +const trackedBuild = path.join(serverRoot, 'build'); +const trackedNotices = path.join(serverRoot, 'THIRD_PARTY_NOTICES.md'); +const checkOnly = process.argv.includes('--check'); +const temporaryRoot = fs.mkdtempSync( + path.join(os.tmpdir(), 'mcp-unity-companion-build-'), +); +const outputRoot = path.join(temporaryRoot, 'build'); + +try { + fs.mkdirSync(outputRoot, { recursive: true }); + execFileSync( + process.execPath, + [ + path.join(serverRoot, 'node_modules', 'typescript', 'bin', 'tsc'), + '--noEmit', + ], + { cwd: serverRoot, stdio: 'inherit' }, + ); + const buildResult = await build({ + entryPoints: [path.join(serverRoot, 'src', 'index.ts')], + outfile: path.join(outputRoot, 'index.js'), + bundle: true, + platform: 'node', + format: 'esm', + target: 'node20', + charset: 'utf8', + legalComments: 'none', + metafile: true, + sourcemap: false, + minify: false, + treeShaking: true, + logLevel: 'error', + banner: { + js: [ + "import { createRequire as __mcpCreateRequire } from 'node:module';", + 'const require = __mcpCreateRequire(import.meta.url);', + ].join('\n'), + }, + }); + const bundledEntrypoint = path.join(outputRoot, 'index.js'); + fs.writeFileSync( + bundledEntrypoint, + fs.readFileSync(bundledEntrypoint, 'utf8').replace(/^[\t ]+$/gm, ''), + ); + + fs.mkdirSync(path.join(outputRoot, 'ui'), { recursive: true }); + fs.copyFileSync( + path.join(serverRoot, 'src', 'ui', 'unity-dashboard.html'), + path.join(outputRoot, 'ui', 'unity-dashboard.html'), + ); + + const notices = createThirdPartyNotices(buildResult.metafile.inputs); + const generatedNotices = path.join(temporaryRoot, 'THIRD_PARTY_NOTICES.md'); + fs.writeFileSync(generatedNotices, notices); + + if (checkOnly) { + assertDirectoriesEqual(trackedBuild, outputRoot); + assertFilesEqual(trackedNotices, generatedNotices); + } else { + fs.rmSync(trackedBuild, { recursive: true, force: true }); + fs.cpSync(outputRoot, trackedBuild, { recursive: true }); + fs.copyFileSync(generatedNotices, trackedNotices); + } +} finally { + fs.rmSync(temporaryRoot, { recursive: true, force: true }); +} + +function createThirdPartyNotices(inputs) { + const packageRoots = new Map(); + for (const input of Object.keys(inputs)) { + const normalized = input.split(path.sep).join('/'); + const marker = 'node_modules/'; + const markerIndex = normalized.lastIndexOf(marker); + if (markerIndex < 0) continue; + const relative = normalized.slice(markerIndex + marker.length); + const segments = relative.split('/'); + const packageName = segments[0].startsWith('@') + ? `${segments[0]}/${segments[1]}` + : segments[0]; + const packageRoot = path.resolve( + serverRoot, + normalized.slice(0, markerIndex + marker.length), + packageName, + ); + packageRoots.set(packageName, packageRoot); + } + + const sections = [...packageRoots] + .sort(([left], [right]) => left.localeCompare(right)) + .map(([packageName, packageRoot]) => { + const manifest = JSON.parse( + fs.readFileSync(path.join(packageRoot, 'package.json'), 'utf8'), + ); + const licensePath = findLicenseFile(packageRoot); + if (!licensePath) { + throw new Error(`Bundled package ${packageName} has no license file.`); + } + const licenseText = fs.readFileSync(licensePath, 'utf8').trim(); + const source = + typeof manifest.repository === 'string' + ? manifest.repository + : manifest.repository?.url ?? manifest.homepage ?? 'Not declared'; + return [ + `## ${packageName} ${manifest.version}`, + '', + `License: ${manifest.license ?? 'See included text'}`, + '', + `Source: ${source}`, + '', + '```text', + licenseText.replaceAll('```', '`` `'), + '```', + ].join('\n'); + }); + + return [ + '# Third-Party Notices', + '', + 'MCP Unity bundles the following runtime dependencies into `build/index.js`.', + 'This file is generated from the exact packages included by the companion build.', + '', + ...sections.flatMap((section) => [section, '']), + ].join('\n'); +} + +function findLicenseFile(packageRoot) { + const candidates = fs + .readdirSync(packageRoot) + .filter((name) => /^(license|licence|copying|notice)(\.|$)/i.test(name)) + .sort(); + return candidates.length > 0 ? path.join(packageRoot, candidates[0]) : undefined; +} + +function assertDirectoriesEqual(expectedRoot, actualRoot) { + const expectedFiles = walkFiles(expectedRoot).map((file) => + path.relative(expectedRoot, file), + ); + const actualFiles = walkFiles(actualRoot).map((file) => + path.relative(actualRoot, file), + ); + if (JSON.stringify(expectedFiles) !== JSON.stringify(actualFiles)) { + throw new Error( + `Tracked build files differ.\nExpected: ${expectedFiles.join(', ')}\nActual: ${actualFiles.join(', ')}`, + ); + } + for (const relative of expectedFiles) { + assertFilesEqual( + path.join(expectedRoot, relative), + path.join(actualRoot, relative), + ); + } +} + +function walkFiles(directory) { + if (!fs.existsSync(directory)) return []; + return fs + .readdirSync(directory, { withFileTypes: true }) + .sort((left, right) => left.name.localeCompare(right.name)) + .flatMap((entry) => { + const resolved = path.join(directory, entry.name); + return entry.isDirectory() ? walkFiles(resolved) : [resolved]; + }); +} + +function assertFilesEqual(expected, actual) { + if ( + !fs.existsSync(expected) || + !fs.existsSync(actual) || + !fs.readFileSync(expected).equals(fs.readFileSync(actual)) + ) { + throw new Error( + `Tracked artifact ${path.relative(serverRoot, expected)} is stale. Run npm run build.`, + ); + } +} diff --git a/Server~/scripts/clean-archive-mcp-smoke.mjs b/Server~/scripts/clean-archive-mcp-smoke.mjs new file mode 100644 index 00000000..bac8f2d9 --- /dev/null +++ b/Server~/scripts/clean-archive-mcp-smoke.mjs @@ -0,0 +1,155 @@ +import { cp, mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { existsSync } from 'node:fs'; +import { spawnSync } from 'node:child_process'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; + +const serverRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); +const temporaryRoot = await mkdtemp(path.join(os.tmpdir(), 'mcp-unity-clean-mcp-')); +const cleanPackage = path.join(temporaryRoot, 'package'); +const cleanServer = path.join(cleanPackage, 'Server~'); +const projectPath = path.join(temporaryRoot, 'UnityProject'); +const fakeMcp = path.join(cleanServer, 'mcp'); +let client; +let transport; + +try { + await mkdir(cleanServer, { recursive: true }); + await cp(path.join(serverRoot, 'build'), path.join(cleanServer, 'build'), { + recursive: true, + }); + await cp( + path.join(serverRoot, 'package.json'), + path.join(cleanServer, 'package.json'), + ); + await cp( + path.join(serverRoot, 'THIRD_PARTY_NOTICES.md'), + path.join(cleanServer, 'THIRD_PARTY_NOTICES.md'), + ); + await mkdir(path.join(projectPath, 'Assets'), { recursive: true }); + await mkdir(path.join(projectPath, 'ProjectSettings'), { recursive: true }); + await writeFile( + fakeMcp, + [ + "import readline from 'node:readline';", + '', + 'const input = readline.createInterface({ input: process.stdin });', + "input.on('line', (line) => {", + ' const request = JSON.parse(line);', + ' if (request.id === undefined) return;', + ' let result;', + " if (request.method === 'initialize') {", + ' result = {', + ' protocolVersion: request.params.protocolVersion,', + ' capabilities: { tools: {} },', + " serverInfo: { name: 'portable-fake-unity-mcp', version: '1.0.0' },", + ' };', + " } else if (request.method === 'tools/call') {", + ' result = {', + " content: [{ type: 'text', text: '{\"logs\":[]}' }],", + ' structuredContent: { logs: [] },', + ' };', + ' } else {', + ' result = {};', + ' }', + ' process.stdout.write(JSON.stringify({', + " jsonrpc: '2.0',", + ' id: request.id,', + ' result,', + " }) + '\\n');", + '});', + '', + ].join('\n'), + 'utf8', + ); + + assertNoAncestorNodeModules(cleanServer); + + const cleanEntrypoint = path.join(cleanServer, 'build', 'index.js'); + const startup = spawnSync(process.execPath, [cleanEntrypoint], { + cwd: cleanServer, + encoding: 'utf8', + env: { PATH: process.env.PATH ?? '' }, + timeout: 10_000, + }); + if ( + startup.status !== 1 || + !startup.stderr.includes('--project-path') || + startup.stderr.includes('ERR_MODULE_NOT_FOUND') + ) { + throw new Error( + `Clean companion startup smoke failed: status=${startup.status}, stderr=${startup.stderr}`, + ); + } + + transport = new StdioClientTransport({ + command: process.execPath, + args: [ + cleanEntrypoint, + '--project-path', + projectPath, + '--unity-cli-path', + process.execPath, + ], + cwd: cleanServer, + stderr: 'pipe', + }); + client = new Client( + { name: 'clean-archive-dashboard-smoke', version: '1.0.0' }, + { capabilities: {} }, + ); + await client.connect(transport); + const result = await client.readResource({ uri: 'ui://unity-dashboard' }); + const content = result.contents[0]; + + if (content?.mimeType !== 'text/html;profile=mcp-app') { + throw new Error(`Unexpected dashboard MIME type: ${content?.mimeType}`); + } + if ( + typeof content.text !== 'string' || + !content.text.includes('Unity Dashboard') || + !content.text.includes('unity://logs') + ) { + throw new Error('Bundled dashboard resource did not return its HTML.'); + } + if ( + !content._meta || + !('ui' in content._meta) || + !content._meta.ui || + typeof content._meta.ui !== 'object' || + !('csp' in content._meta.ui) + ) { + throw new Error('Bundled dashboard resource did not return MCP App metadata.'); + } + + const logs = await client.readResource({ + uri: 'unity://logs?severity=all&limit=1', + }); + const logsPayload = JSON.parse(logs.contents[0]?.text ?? 'null'); + if (!Array.isArray(logsPayload?.logs)) { + throw new Error('Portable fake MCP server did not return Unity logs.'); + } +} finally { + await client?.close().catch(() => undefined); + await transport?.close().catch(() => undefined); + await rm(temporaryRoot, { recursive: true, force: true }); +} + +function assertNoAncestorNodeModules(start) { + let current = path.resolve(start); + while (true) { + const candidate = path.join(current, 'node_modules'); + if ( + candidate !== path.join(serverRoot, 'node_modules') && + existsSync(candidate) + ) { + throw new Error(`Clean package has an accessible node_modules at ${candidate}`); + } + const parent = path.dirname(current); + if (parent === current) break; + current = parent; + } +} diff --git a/Server~/scripts/copy-ui.mjs b/Server~/scripts/copy-ui.mjs deleted file mode 100644 index c85754d3..00000000 --- a/Server~/scripts/copy-ui.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const here = path.dirname(fileURLToPath(import.meta.url)); -const serverRoot = path.resolve(here, '..'); - -const srcHtml = path.join(serverRoot, 'src', 'ui', 'unity-dashboard.html'); -const outDir = path.join(serverRoot, 'build', 'ui'); -const outHtml = path.join(outDir, 'unity-dashboard.html'); - -if (!fs.existsSync(srcHtml)) { - console.error(`UI source file not found: ${srcHtml}`); - process.exit(1); -} - -fs.mkdirSync(outDir, { recursive: true }); -fs.copyFileSync(srcHtml, outHtml); - -console.log(`Copied UI: ${srcHtml} -> ${outHtml}`); diff --git a/Server~/smithery.yaml b/Server~/smithery.yaml deleted file mode 100644 index 8a7a3ece..00000000 --- a/Server~/smithery.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Smithery.ai configuration -startCommand: - type: stdio - configSchema: - # JSON Schema defining the configuration options for the MCP. - type: object - required: - - ABSOLUTE_PATH_TO_mcp-unity - properties: - ABSOLUTE_PATH_TO_mcp-unity - type: string - description: "The absolute folder path where your mcp-unity package is installed. Go to the Unity Editor MCP Server window (Tools > MCP Unity > Server Window)" - commandFunction: - # A function that produces the CLI command to start the MCP on stdio. - |- - (config) => ({ - "command": "node", - "args": [ - `${config.ABSOLUTE_PATH_TO_mcp-unity}/build/index.js` - ], - "env": { - "UNITY_PORT": "8090" - } - }) \ No newline at end of file diff --git a/Server~/src/__tests__/batchExecuteTool.test.ts b/Server~/src/__tests__/batchExecuteTool.test.ts deleted file mode 100644 index ae4051b8..00000000 --- a/Server~/src/__tests__/batchExecuteTool.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { jest, describe, it, expect, beforeEach } from '@jest/globals'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { registerBatchExecuteTool } from '../tools/batchExecuteTool.js'; - -// Mock the McpUnity class -const mockSendRequest = jest.fn(); -const mockMcpUnity = { - sendRequest: mockSendRequest -}; - -// Mock the Logger -const mockLogger = { - info: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn() -}; - -// Mock the McpServer -const mockServerTool = jest.fn(); -const mockServer = { - tool: mockServerTool -}; - -describe('Batch Execute Tool', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('registerBatchExecuteTool', () => { - it('should register the batch_execute tool with the server', () => { - registerBatchExecuteTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledTimes(1); - expect(mockServerTool).toHaveBeenCalledWith( - 'batch_execute', - expect.any(String), - expect.any(Object), - expect.any(Function) - ); - expect(mockLogger.info).toHaveBeenCalledWith('Registering tool: batch_execute'); - }); - - it('should have correct tool description mentioning batch and performance', () => { - registerBatchExecuteTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - const [, description] = mockServerTool.mock.calls[0]; - expect(description).toContain('batch'); - expect(description).toContain('operations'); - }); - - it('should have correct schema with operations array', () => { - registerBatchExecuteTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - const [, , schema] = mockServerTool.mock.calls[0]; - expect(schema).toHaveProperty('operations'); - expect(schema).toHaveProperty('stopOnError'); - expect(schema).toHaveProperty('atomic'); - }); - }); - - describe('batch_execute handler', () => { - let toolHandler: (params: any) => Promise; - - beforeEach(() => { - registerBatchExecuteTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - toolHandler = mockServerTool.mock.calls[0][3]; - }); - - it('should send batch request to Unity with correct parameters', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Successfully executed 2/2 operations.', - results: [ - { index: 0, id: '0', success: true }, - { index: 1, id: '1', success: true } - ], - summary: { total: 2, succeeded: 2, failed: 0, executed: 2 } - }); - - const params = { - operations: [ - { tool: 'create_gameobject', params: { name: 'Test1' } }, - { tool: 'create_gameobject', params: { name: 'Test2' } } - ], - stopOnError: true, - atomic: false - }; - - const result = await toolHandler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'batch_execute', - params: expect.objectContaining({ - operations: expect.arrayContaining([ - expect.objectContaining({ tool: 'create_gameobject' }), - expect.objectContaining({ tool: 'create_gameobject' }) - ]), - stopOnError: true, - atomic: false - }) - }); - expect(result.content[0].text).toContain('Successfully'); - }); - - it('should throw error when operations array is empty', async () => { - const params = { - operations: [], - stopOnError: true - }; - - await expect(toolHandler(params)).rejects.toThrow(McpUnityError); - }); - - it('should throw error when nested batch_execute is detected', async () => { - const params = { - operations: [ - { tool: 'batch_execute', params: { operations: [] } } - ] - }; - - await expect(toolHandler(params)).rejects.toThrow('Cannot nest batch_execute'); - }); - - it('should handle partial failures with stopOnError=false', async () => { - mockSendRequest.mockResolvedValue({ - success: false, - type: 'text', - message: 'Batch execution completed with errors. 1/2 operations succeeded, 1 failed.', - results: [ - { index: 0, id: '0', success: true }, - { index: 1, id: '1', success: false, error: 'Tool failed' } - ], - summary: { total: 2, succeeded: 1, failed: 1, executed: 2 } - }); - - const params = { - operations: [ - { tool: 'tool1', params: {} }, - { tool: 'tool2', params: {} } - ], - stopOnError: false - }; - - // With stopOnError=false, should return result even with failures - const result = await toolHandler(params); - expect(result.content[0].text).toContain('1/2'); - expect(result.content[0].text).toContain('failed'); - }); - - it('should throw error on failure when stopOnError=true', async () => { - mockSendRequest.mockResolvedValue({ - success: false, - type: 'text', - message: 'Batch execution stopped on error. 0/2 operations succeeded.', - results: [ - { index: 0, id: '0', success: false, error: 'First tool failed' } - ], - summary: { total: 2, succeeded: 0, failed: 1, executed: 1 } - }); - - const params = { - operations: [ - { tool: 'tool1', params: {} }, - { tool: 'tool2', params: {} } - ], - stopOnError: true - }; - - await expect(toolHandler(params)).rejects.toThrow(McpUnityError); - }); - - it('should preserve operation ids in request', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Successfully executed 2/2 operations.', - results: [ - { index: 0, id: 'op1', success: true }, - { index: 1, id: 'op2', success: true } - ], - summary: { total: 2, succeeded: 2, failed: 0, executed: 2 } - }); - - const params = { - operations: [ - { tool: 'tool1', params: {}, id: 'op1' }, - { tool: 'tool2', params: {}, id: 'op2' } - ] - }; - - await toolHandler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'batch_execute', - params: expect.objectContaining({ - operations: expect.arrayContaining([ - expect.objectContaining({ id: 'op1' }), - expect.objectContaining({ id: 'op2' }) - ]) - }) - }); - }); - - it('should use default values for stopOnError and atomic', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Success', - results: [], - summary: { total: 1, succeeded: 1, failed: 0, executed: 1 } - }); - - const params = { - operations: [{ tool: 'tool1', params: {} }] - }; - - await toolHandler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'batch_execute', - params: expect.objectContaining({ - stopOnError: true, - atomic: false - }) - }); - }); - }); -}); diff --git a/Server~/src/__tests__/boundedError.test.ts b/Server~/src/__tests__/boundedError.test.ts new file mode 100644 index 00000000..5dc54032 --- /dev/null +++ b/Server~/src/__tests__/boundedError.test.ts @@ -0,0 +1,20 @@ +import { + ERROR_DETAIL_BUDGET_BYTES, + boundedErrorMessage, +} from '../utils/boundedError.js'; + +describe('bounded companion error details', () => { + test('uses a UTF-8 byte ceiling without splitting surrogate pairs', () => { + const message = boundedErrorMessage( + 'transport failed: ', + `${'🙂'.repeat(16 * 1024)}-secret-tail`, + ); + + expect(Buffer.byteLength(message)).toBeLessThanOrEqual( + ERROR_DETAIL_BUDGET_BYTES, + ); + expect(message).toContain('[truncated]'); + expect(message).not.toContain('\uFFFD'); + expect(message).not.toContain('secret-tail'); + }); +}); diff --git a/Server~/src/__tests__/commandQueue.test.ts b/Server~/src/__tests__/commandQueue.test.ts deleted file mode 100644 index c62925c9..00000000 --- a/Server~/src/__tests__/commandQueue.test.ts +++ /dev/null @@ -1,412 +0,0 @@ -import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals'; -import { CommandQueue, CommandQueueConfig, QueuedCommand } from '../unity/commandQueue'; -import { Logger, LogLevel } from '../utils/logger'; -import { McpUnityError, ErrorType } from '../utils/errors'; - -// Create a silent logger for tests -const createTestLogger = (): Logger => { - return new Logger('Test', LogLevel.ERROR); -}; - -describe('CommandQueue', () => { - let queue: CommandQueue; - let logger: Logger; - - beforeEach(() => { - logger = createTestLogger(); - queue = new CommandQueue(logger); - }); - - afterEach(() => { - queue.dispose(); - }); - - describe('constructor', () => { - it('should create with default configuration', () => { - const stats = queue.getStats(); - expect(stats.maxSize).toBe(100); - expect(stats.size).toBe(0); - }); - - it('should accept custom configuration', () => { - const customQueue = new CommandQueue(logger, { - maxSize: 50, - defaultTimeout: 30000, - cleanupInterval: 1000, - }); - - const stats = customQueue.getStats(); - expect(stats.maxSize).toBe(50); - - customQueue.dispose(); - }); - }); - - describe('enqueue', () => { - it('should successfully enqueue a command', () => { - const result = queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - - expect(result.success).toBe(true); - expect(result.position).toBe(1); - expect(queue.size).toBe(1); - }); - - it('should enqueue multiple commands in order', () => { - for (let i = 1; i <= 3; i++) { - const result = queue.enqueue({ - id: `test-${i}`, - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - expect(result.position).toBe(i); - } - - expect(queue.size).toBe(3); - }); - - it('should reject commands when queue is full', () => { - const smallQueue = new CommandQueue(logger, { maxSize: 2 }); - const rejectFn = jest.fn(); - - // Fill the queue - smallQueue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - smallQueue.enqueue({ - id: 'test-2', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - - // Try to add one more - const result = smallQueue.enqueue({ - id: 'test-3', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: rejectFn, - }); - - expect(result.success).toBe(false); - expect(result.reason).toContain('Queue is full'); - expect(rejectFn).toHaveBeenCalled(); - expect(smallQueue.getStats().droppedCount).toBe(1); - - smallQueue.dispose(); - }); - }); - - describe('size and isEmpty', () => { - it('should report correct size', () => { - expect(queue.size).toBe(0); - expect(queue.isEmpty).toBe(true); - - queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - - expect(queue.size).toBe(1); - expect(queue.isEmpty).toBe(false); - }); - - it('should report isFull correctly', () => { - const smallQueue = new CommandQueue(logger, { maxSize: 1 }); - - expect(smallQueue.isFull).toBe(false); - - smallQueue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - - expect(smallQueue.isFull).toBe(true); - - smallQueue.dispose(); - }); - }); - - describe('drain', () => { - it('should return all queued commands and clear the queue', () => { - const resolve1 = jest.fn(); - const resolve2 = jest.fn(); - - queue.enqueue({ - id: 'test-1', - request: { method: 'method1', params: {} }, - resolve: resolve1, - reject: jest.fn(), - }); - queue.enqueue({ - id: 'test-2', - request: { method: 'method2', params: {} }, - resolve: resolve2, - reject: jest.fn(), - }); - - expect(queue.size).toBe(2); - - const commands = queue.drain(); - - expect(commands.length).toBe(2); - expect(commands[0].id).toBe('test-1'); - expect(commands[1].id).toBe('test-2'); - expect(queue.size).toBe(0); - expect(queue.isEmpty).toBe(true); - }); - - it('should return empty array when queue is empty', () => { - const commands = queue.drain(); - expect(commands).toEqual([]); - }); - }); - - describe('peek', () => { - it('should return the first command without removing it', () => { - queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - - const peeked = queue.peek(); - - expect(peeked).toBeDefined(); - expect(peeked?.id).toBe('test-1'); - expect(queue.size).toBe(1); // Still in queue - }); - - it('should return undefined for empty queue', () => { - const peeked = queue.peek(); - expect(peeked).toBeUndefined(); - }); - }); - - describe('clear', () => { - it('should clear all commands and reject them', () => { - const reject1 = jest.fn(); - const reject2 = jest.fn(); - - queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: reject1, - }); - queue.enqueue({ - id: 'test-2', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: reject2, - }); - - queue.clear('Test clear'); - - expect(queue.size).toBe(0); - expect(reject1).toHaveBeenCalled(); - expect(reject2).toHaveBeenCalled(); - - // Check that rejection includes correct error type - const error1 = reject1.mock.calls[0][0] as McpUnityError; - expect(error1.type).toBe(ErrorType.CONNECTION); - expect(error1.message).toBe('Test clear'); - }); - - it('should handle clearing empty queue', () => { - expect(() => queue.clear()).not.toThrow(); - }); - }); - - describe('cleanupExpired', () => { - it('should remove expired commands', async () => { - const shortTimeoutQueue = new CommandQueue(logger, { - defaultTimeout: 50, // 50ms timeout - cleanupInterval: 100000, // Long interval so we control cleanup - }); - - const rejectFn = jest.fn(); - - shortTimeoutQueue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: rejectFn, - }); - - expect(shortTimeoutQueue.size).toBe(1); - - // Wait for expiration - await new Promise(resolve => setTimeout(resolve, 100)); - - const expiredCount = shortTimeoutQueue.cleanupExpired(); - - expect(expiredCount).toBe(1); - expect(shortTimeoutQueue.size).toBe(0); - expect(rejectFn).toHaveBeenCalled(); - - const error = rejectFn.mock.calls[0][0] as McpUnityError; - expect(error.type).toBe(ErrorType.TIMEOUT); - expect(shortTimeoutQueue.getStats().expiredCount).toBe(1); - - shortTimeoutQueue.dispose(); - }); - - it('should not remove non-expired commands', () => { - queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - - const expiredCount = queue.cleanupExpired(); - - expect(expiredCount).toBe(0); - expect(queue.size).toBe(1); - }); - - it('should respect per-command timeout', async () => { - const rejectFn = jest.fn(); - - // Default timeout is 60s, but we set a short custom timeout - queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: rejectFn, - timeout: 50, // 50ms timeout - }); - - await new Promise(resolve => setTimeout(resolve, 100)); - - const expiredCount = queue.cleanupExpired(); - - expect(expiredCount).toBe(1); - expect(rejectFn).toHaveBeenCalled(); - }); - }); - - describe('statistics', () => { - it('should track statistics correctly', () => { - // Initial stats - let stats = queue.getStats(); - expect(stats.size).toBe(0); - expect(stats.droppedCount).toBe(0); - expect(stats.expiredCount).toBe(0); - expect(stats.replayedCount).toBe(0); - - // Add a command - queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: jest.fn(), - }); - - stats = queue.getStats(); - expect(stats.size).toBe(1); - - // Record replay success - queue.recordReplaySuccess(); - stats = queue.getStats(); - expect(stats.replayedCount).toBe(1); - }); - - it('should reset statistics', () => { - queue.recordReplaySuccess(); - queue.recordReplaySuccess(); - - let stats = queue.getStats(); - expect(stats.replayedCount).toBe(2); - - queue.resetStats(); - - stats = queue.getStats(); - expect(stats.droppedCount).toBe(0); - expect(stats.expiredCount).toBe(0); - expect(stats.replayedCount).toBe(0); - }); - }); - - describe('updateConfig', () => { - it('should update configuration dynamically', () => { - queue.updateConfig({ maxSize: 50 }); - - const stats = queue.getStats(); - expect(stats.maxSize).toBe(50); - }); - }); - - describe('dispose', () => { - it('should clean up resources and reject pending commands', () => { - const rejectFn = jest.fn(); - - queue.enqueue({ - id: 'test-1', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: rejectFn, - }); - - queue.dispose(); - - expect(queue.size).toBe(0); - expect(rejectFn).toHaveBeenCalled(); - }); - }); - - describe('drain filters expired commands', () => { - it('should filter out expired commands when draining', async () => { - const shortTimeoutQueue = new CommandQueue(logger, { - defaultTimeout: 50, - cleanupInterval: 100000, - }); - - const rejectFn = jest.fn(); - const resolveFn = jest.fn(); - - // Add an expired command - shortTimeoutQueue.enqueue({ - id: 'expired', - request: { method: 'test', params: {} }, - resolve: jest.fn(), - reject: rejectFn, - timeout: 10, - }); - - // Add a non-expired command - shortTimeoutQueue.enqueue({ - id: 'valid', - request: { method: 'test', params: {} }, - resolve: resolveFn, - reject: jest.fn(), - timeout: 60000, - }); - - // Wait for first command to expire - await new Promise(resolve => setTimeout(resolve, 50)); - - const commands = shortTimeoutQueue.drain(); - - expect(commands.length).toBe(1); - expect(commands[0].id).toBe('valid'); - expect(rejectFn).toHaveBeenCalled(); - - shortTimeoutQueue.dispose(); - }); - }); -}); diff --git a/Server~/src/__tests__/companionCli.test.ts b/Server~/src/__tests__/companionCli.test.ts new file mode 100644 index 00000000..b281ded9 --- /dev/null +++ b/Server~/src/__tests__/companionCli.test.ts @@ -0,0 +1,274 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { jest } from '@jest/globals'; +import { + CLI_DOCUMENTATION_URL, + checkUnityCli, + parseCompanionArguments, + resolveUnityCliPath, + runUnityCliVersion, +} from '../cli/companionCli.js'; + +async function withTimeout( + operation: Promise, + timeoutMs: number, + message: string, +): Promise { + let timeout: NodeJS.Timeout | undefined; + try { + return await Promise.race([ + operation, + new Promise((_, reject) => { + timeout = setTimeout(() => reject(new Error(message)), timeoutMs); + }), + ]); + } finally { + if (timeout) clearTimeout(timeout); + } +} + +describe('companion arguments', () => { + const projectPath = path.resolve('/tmp', 'unity-project'); + + test('requires an absolute existing project path', () => { + expect(() => parseCompanionArguments([], () => true)).toThrow('--project-path'); + expect(() => parseCompanionArguments(['--project-path', 'relative'], () => true)).toThrow('absolute'); + expect(() => parseCompanionArguments(['--project-path', projectPath], () => false)).toThrow('existing Unity project'); + }); + + test('accepts the required project path and optional CLI path', () => { + expect( + parseCompanionArguments( + ['--project-path', projectPath, '--unity-cli-path', '/opt/unity-cli'], + () => true, + ), + ).toEqual({ + projectPath, + unityCliPath: '/opt/unity-cli', + }); + }); + + test('requires an explicitly supplied CLI path to be absolute', () => { + expect(() => + parseCompanionArguments( + ['--project-path', projectPath, '--unity-cli-path', 'relative/unity'], + () => true, + ), + ).toThrow('--unity-cli-path must be absolute'); + }); + + test('rejects unknown, duplicate, or valueless arguments', () => { + expect(() => parseCompanionArguments(['--wat'], () => true)).toThrow('Unknown argument'); + expect(() => + parseCompanionArguments( + ['--project-path', projectPath, '--project-path', projectPath], + () => true, + ), + ).toThrow('Duplicate'); + expect(() => + parseCompanionArguments(['--project-path', projectPath, '--unity-cli-path'], () => true), + ).toThrow('requires a value'); + }); +}); + +describe('Unity CLI resolution and compatibility', () => { + test('resolves explicit argument, then environment, then PATH command', () => { + expect(resolveUnityCliPath('/explicit/unity', { UNITY_CLI_PATH: '/env/unity' })).toBe( + '/explicit/unity', + ); + expect(resolveUnityCliPath(undefined, { UNITY_CLI_PATH: '/env/unity' })).toBe('/env/unity'); + expect(resolveUnityCliPath(undefined, {})).toBe('unity'); + }); + + test('trims a configured environment path and ignores empty values', () => { + expect(resolveUnityCliPath(undefined, { UNITY_CLI_PATH: ' /env/unity ' })).toBe( + '/env/unity', + ); + expect(resolveUnityCliPath(undefined, { UNITY_CLI_PATH: ' ' })).toBe('unity'); + }); + + test.each([ + ['Unity CLI 1.0.0-beta.2', '1.0.0-beta.2'], + ['unity version 1.0.0', '1.0.0'], + ['2.4.1', '2.4.1'], + ['Unity CLI 1.0.0-beta.2.alpha+build.01.sha-abc', '1.0.0-beta.2.alpha+build.01.sha-abc'], + ['999999999999999999999999.0.0-dev.1', '999999999999999999999999.0.0-dev.1'], + ])('accepts compatible version output %s', async (stdout, version) => { + const result = await checkUnityCli('/opt/unity', async (command, args) => { + expect(command).toBe('/opt/unity'); + expect(args).toEqual(['--version']); + return { stdout, stderr: '' }; + }); + + expect(result.version).toBe(version); + expect(result.warning).toBe( + version.startsWith('2.') + ? 'Unity CLI 2.4.1 is newer than the tested major version 1.' + : version.startsWith('999') + ? `Unity CLI ${version} is newer than the tested major version 1.` + : undefined, + ); + }); + + test('accepts the leading v emitted by node --version for portable smoke CLIs', async () => { + const result = await checkUnityCli(process.execPath, async () => ({ + stdout: 'v20.20.2', + stderr: '', + })); + + expect(result.version).toBe('20.20.2'); + expect(result.warning).toBe( + 'Unity CLI 20.20.2 is newer than the tested major version 1.', + ); + }); + + test.each(['0.9.9', '1.0.0-alpha.9', '1.0.0-beta.1'])( + 'rejects incompatible version %s with documentation', + async (version) => { + await expect( + checkUnityCli('unity', async () => ({ stdout: version, stderr: '' })), + ).rejects.toThrow(CLI_DOCUMENTATION_URL); + }, + ); + + test.each([ + 'not a version', + '1.0', + '01.0.0', + '1.00.0', + '1.0.00', + '1.0.0-beta.01', + '1.0.0-', + '1.0.0-beta..2', + '1.0.0+', + '1.0.0+build..sha', + '1.0.0+bad_meta', + '1.0.0-beta.2..garbage', + ])( + 'rejects malformed version output %s', + async (version) => { + await expect( + checkUnityCli('unity', async () => ({ stdout: version, stderr: '' })), + ).rejects.toThrow(CLI_DOCUMENTATION_URL); + }, + ); + + test('turns a missing executable into an actionable error', async () => { + const missing = Object.assign(new Error('spawn unity ENOENT'), { code: 'ENOENT' }); + + await expect( + checkUnityCli('unity', async () => { + throw missing; + }), + ).rejects.toThrow(CLI_DOCUMENTATION_URL); + }); + + test('does not truncate a valid longer prerelease to the minimum prefix', async () => { + const result = await checkUnityCli('unity', async () => ({ + stdout: '1.0.0-beta.2foo', + stderr: '', + })); + + expect(result.version).toBe('1.0.0-beta.2foo'); + }); + + test('invokes the real version process without a shell and with only --version', async () => { + const calls: Array> = []; + const fakeChild = { + stdout: null, + stderr: null, + pid: 123, + once(event: string, listener: (...args: unknown[]) => void) { + if (event === 'close') queueMicrotask(() => listener(0, null)); + return this; + }, + kill: jest.fn(), + }; + + await runUnityCliVersion( + '/opt/unity', + ['--version'], + { timeoutMs: 100 }, + ((command: string, args: readonly string[], options: Record) => { + calls.push({ command, args, options }); + return fakeChild; + }) as never, + ); + + expect(calls).toEqual([ + { + command: '/opt/unity', + args: ['--version'], + options: expect.objectContaining({ shell: false }), + }, + ]); + }); + + test('observes cancellation that occurs while the version process is spawning', async () => { + const controller = new AbortController(); + const fakeChild = { + stdout: null, + stderr: null, + pid: 123, + once() { + return this; + }, + kill: jest.fn(), + }; + + await expect( + withTimeout( + runUnityCliVersion( + '/opt/unity', + ['--version'], + { timeoutMs: 1000, signal: controller.signal }, + (() => { + controller.abort(); + return fakeChild; + }) as never, + ), + 100, + 'cancelled version invocation did not settle', + ), + ).rejects.toThrow('cancelled'); + expect(fakeChild.kill).toHaveBeenCalled(); + }); + + const posixTest = process.platform === 'win32' ? test.skip : test; + posixTest( + 'times out promptly when a descendant retains the version process stdio', + async () => { + const fixtureDirectory = fs.mkdtempSync( + path.join(os.tmpdir(), 'mcp-unity-cli-version-'), + ); + const executable = path.join(fixtureDirectory, 'unity-version-fixture'); + fs.writeFileSync( + executable, + `#!/usr/bin/env node +const { spawn } = require('node:child_process'); +spawn(process.execPath, ['-e', 'setTimeout(() => {}, 1500)'], { + stdio: ['ignore', process.stdout, process.stderr] +}); +process.stdout.write('1.0.0-beta.2\\n'); +`, + { mode: 0o755 }, + ); + + const startedAt = Date.now(); + try { + await expect( + withTimeout( + runUnityCliVersion(executable, ['--version'], { timeoutMs: 100 }), + 1000, + 'version invocation did not settle', + ), + ).rejects.toThrow('timed out'); + expect(Date.now() - startedAt).toBeLessThan(1000); + } finally { + fs.rmSync(fixtureDirectory, { recursive: true, force: true }); + } + }, + 3000, + ); +}); diff --git a/Server~/src/__tests__/companionEntrypoint.test.ts b/Server~/src/__tests__/companionEntrypoint.test.ts new file mode 100644 index 00000000..3a58ea94 --- /dev/null +++ b/Server~/src/__tests__/companionEntrypoint.test.ts @@ -0,0 +1,35 @@ +import { EventEmitter } from 'node:events'; +import { jest } from '@jest/globals'; +import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; +import { startCompanion } from '../companionEntrypoint.js'; + +describe('companion entrypoint', () => { + test('checks the resolved CLI before serving and reports newer-major warnings', async () => { + const checkCli = jest.fn(async (command: string) => ({ + command, + version: '2.0.0', + warning: 'Unity CLI 2.0.0 is newer than the tested major version 1.', + })); + const stderr = { write: jest.fn(() => true) }; + const projectPath = '/projects/game'; + const runtime = await startCompanion({ + argv: ['--project-path', projectPath], + environment: { UNITY_CLI_PATH: '/env/unity' }, + isUnityProject: () => true, + checkCli, + transport: new InMemoryTransport(), + signals: new EventEmitter(), + stdin: new EventEmitter(), + stderr, + }); + + expect(checkCli).toHaveBeenCalledWith('/env/unity'); + expect(stderr.write).toHaveBeenCalledWith( + 'Warning: Unity CLI 2.0.0 is newer than the tested major version 1.\n', + ); + expect(runtime.officialClient.state).toBe('disconnected'); + + await runtime.shutdown(); + expect(runtime.officialClient.state).toBe('closed'); + }); +}); diff --git a/Server~/src/__tests__/companionLifecycle.test.ts b/Server~/src/__tests__/companionLifecycle.test.ts new file mode 100644 index 00000000..c232da03 --- /dev/null +++ b/Server~/src/__tests__/companionLifecycle.test.ts @@ -0,0 +1,52 @@ +import { EventEmitter } from 'node:events'; +import { jest } from '@jest/globals'; +import { installShutdownHandlers } from '../companionLifecycle.js'; + +describe('companion process shutdown', () => { + test.each(['SIGINT', 'SIGTERM'] as const)( + 'closes the official child/client and outer server on %s', + async (signal) => { + const signals = new EventEmitter(); + const stdin = new EventEmitter(); + const closeOfficialClient = jest.fn(async () => undefined); + const closeServer = jest.fn(async () => undefined); + const handlers = installShutdownHandlers({ + signals, + stdin, + closeOfficialClient, + closeServer, + }); + + signals.emit(signal); + await handlers.shutdown(); + + expect(closeOfficialClient).toHaveBeenCalledTimes(1); + expect(closeServer).toHaveBeenCalledTimes(1); + handlers.dispose(); + }, + ); + + test.each(['close', 'end'] as const)( + 'closes once when stdin emits %s', + async (event) => { + const signals = new EventEmitter(); + const stdin = new EventEmitter(); + const closeOfficialClient = jest.fn(async () => undefined); + const closeServer = jest.fn(async () => undefined); + const handlers = installShutdownHandlers({ + signals, + stdin, + closeOfficialClient, + closeServer, + }); + + stdin.emit(event); + stdin.emit(event); + await handlers.shutdown(); + + expect(closeOfficialClient).toHaveBeenCalledTimes(1); + expect(closeServer).toHaveBeenCalledTimes(1); + handlers.dispose(); + }, + ); +}); diff --git a/Server~/src/__tests__/companionPackageContract.test.ts b/Server~/src/__tests__/companionPackageContract.test.ts new file mode 100644 index 00000000..2b363dcb --- /dev/null +++ b/Server~/src/__tests__/companionPackageContract.test.ts @@ -0,0 +1,156 @@ +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { execFileSync, spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +const serverRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', + '..', +); +const packageJson = JSON.parse( + fs.readFileSync(path.join(serverRoot, 'package.json'), 'utf8'), +) as Record; + +describe('private companion package contract', () => { + test('uses exact private metadata and runtime dependencies', () => { + expect(packageJson).toMatchObject({ + name: 'mcp-unity-companion', + version: '2.0.0', + private: true, + engines: { node: '>=20' }, + dependencies: { + '@modelcontextprotocol/sdk': '1.26.0', + '@modelcontextprotocol/ext-apps': '1.0.1', + zod: '3.25.76', + }, + }); + expect(Object.keys(packageJson.dependencies as object).sort()).toEqual([ + '@modelcontextprotocol/ext-apps', + '@modelcontextprotocol/sdk', + 'zod', + ]); + expect(packageJson).not.toHaveProperty('bin'); + expect(packageJson).not.toHaveProperty('files'); + expect(packageJson).not.toHaveProperty('publishConfig'); + expect(packageJson).not.toHaveProperty('mcpName'); + }); + + test('contains no legacy transport, mutation proxy, Docker, or Smithery source', () => { + for (const obsoletePath of [ + 'Dockerfile', + '.dockerignore', + 'smithery.yaml', + 'src/tools', + 'src/unity/mcpUnity.ts', + 'src/unity/unityConnection.ts', + 'src/unity/commandQueue.ts', + 'scripts/clean-build.mjs', + 'scripts/copy-ui.mjs', + ]) { + expect(fs.existsSync(path.join(serverRoot, obsoletePath))).toBe(false); + } + + const source = walkFiles(path.join(serverRoot, 'src')) + .filter( + (file) => + file.endsWith('.ts') && !file.includes(`${path.sep}__tests__${path.sep}`), + ) + .map((file) => fs.readFileSync(file, 'utf8')) + .join('\n'); + for (const forbidden of [ + 'WebSocket', + 'ws://', + 'localhost:8090', + 'set_play_mode_status', + 'update_gameobject', + 'add_package', + ]) { + expect(source).not.toContain(forbidden); + } + }); + + test('ships a companion-only build for Git/UPM installations', () => { + expect(fs.existsSync(path.join(serverRoot, 'build', 'index.js'))).toBe(true); + expect( + fs.existsSync(path.join(serverRoot, 'build', 'ui', 'unity-dashboard.html')), + ).toBe(true); + for (const obsoleteBuildPath of [ + 'build/tools', + 'build/unity/mcpUnity.js', + 'build/unity/unityConnection.js', + 'build/unity/commandQueue.js', + ]) { + expect(fs.existsSync(path.join(serverRoot, obsoleteBuildPath))).toBe(false); + } + }); + + test('ships a self-contained Node 20 bundle with notices', () => { + const entrypoint = fs.readFileSync( + path.join(serverRoot, 'build', 'index.js'), + 'utf8', + ); + const bareImports = [ + ...entrypoint.matchAll( + /^import(?:[\s\S]*?\sfrom\s+|\s*)['"]([^./][^'"]*)['"];?$/gm, + ), + ].map((match) => match[1]); + + expect(bareImports.every((specifier) => specifier.startsWith('node:'))).toBe( + true, + ); + expect(entrypoint).toContain('MCP Unity Companion could not start'); + expect( + fs.readFileSync(path.join(serverRoot, 'THIRD_PARTY_NOTICES.md'), 'utf8'), + ).toEqual(expect.stringContaining('@modelcontextprotocol/sdk 1.26.0')); + }); + + test('starts from a clean copied package with no reachable node_modules', () => { + const isolatedRoot = fs.mkdtempSync( + path.join(os.tmpdir(), 'mcp-unity-companion-clean-'), + ); + const packageRoot = path.join(isolatedRoot, 'package'); + try { + fs.mkdirSync(packageRoot); + fs.cpSync(path.join(serverRoot, 'build'), path.join(packageRoot, 'build'), { + recursive: true, + }); + for (const file of ['package.json', 'THIRD_PARTY_NOTICES.md']) { + fs.copyFileSync(path.join(serverRoot, file), path.join(packageRoot, file)); + } + const result = spawnSync( + process.execPath, + [path.join(packageRoot, 'build', 'index.js')], + { + cwd: packageRoot, + encoding: 'utf8', + env: { PATH: process.env.PATH ?? '' }, + timeout: 10_000, + }, + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain('--project-path'); + expect(result.stderr).not.toContain('ERR_MODULE_NOT_FOUND'); + } finally { + fs.rmSync(isolatedRoot, { recursive: true, force: true }); + } + }); + + test('tracked build is reproducible from source', () => { + expect(() => + execFileSync(process.execPath, ['scripts/build-bundle.mjs', '--check'], { + cwd: serverRoot, + stdio: 'pipe', + }), + ).not.toThrow(); + }); +}); + +function walkFiles(directory: string): string[] { + return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const resolved = path.join(directory, entry.name); + return entry.isDirectory() ? walkFiles(resolved) : [resolved]; + }); +} diff --git a/Server~/src/__tests__/companionResources.test.ts b/Server~/src/__tests__/companionResources.test.ts new file mode 100644 index 00000000..51246fe5 --- /dev/null +++ b/Server~/src/__tests__/companionResources.test.ts @@ -0,0 +1,529 @@ +import { jest } from '@jest/globals'; +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import { + CompanionResourceService, + type UnityReadClient, +} from '../resources/companionResources.js'; + +function toolResult(payload: unknown): CallToolResult { + return { + content: [], + structuredContent: payload as Record, + }; +} + +function textResult(payload: unknown): CallToolResult { + return { + content: [{ type: 'text', text: JSON.stringify(payload) }], + }; +} + +function clientWith( + implementation: UnityReadClient['readTool'], +): UnityReadClient & { readTool: jest.MockedFunction } { + return { readTool: jest.fn(implementation) }; +} + +describe('companion resource mappings', () => { + test.each([ + ['unity://logs?limit=1000', { logs: [{ message: 'x'.repeat(16 * 1024 * 1024) }] }], + ['unity://gameobject/Player', { root: { name: 'x'.repeat(16 * 1024 * 1024) } }], + ['unity://packages', { packages: [{ name: 'x'.repeat(16 * 1024 * 1024) }] }], + ['unity://tests/all', { tests: [{ name: 'x'.repeat(16 * 1024 * 1024) }] }], + ])('aggregate-bounds %s payloads at 512 KiB', async (uri, payload) => { + const resources = new CompanionResourceService( + clientWith(async () => toolResult(payload)), + ); + + const result = await resources.read(uri); + const serializedBytes = Buffer.byteLength(JSON.stringify(result.payload)); + + expect(serializedBytes).toBeLessThanOrEqual(512 * 1024); + expect(result.payload.projection).toMatchObject({ + truncated: true, + payloadBudgetBytes: 512 * 1024, + projectedBytes: serializedBytes, + }); + }); + + test('projects adversarial deep and wide payloads without stack overflow', async () => { + let deep: Record = { value: 'leaf' }; + for (let depth = 0; depth < 20_000; depth++) { + deep = { child: deep }; + } + const payload = { + deep, + ['k'.repeat(100_000)]: 'bounded-key', + wide: Object.fromEntries( + Array.from({ length: 50_000 }, (_, index) => [ + `key-${index}`, + 'x'.repeat(256), + ]), + ), + }; + const resources = new CompanionResourceService( + clientWith(async () => toolResult(payload)), + ); + + const result = await resources.read('unity://logs'); + + expect(Buffer.byteLength(JSON.stringify(result.payload))).toBeLessThanOrEqual( + 512 * 1024, + ); + expect(result.payload.projection).toMatchObject({ + truncated: true, + depthLimitReached: true, + truncatedKeys: 1, + }); + }); + + test('maps logs with defaults and clamped limits', async () => { + const client = clientWith(async () => toolResult({ logs: [] })); + const resources = new CompanionResourceService(client); + + await resources.read('unity://logs'); + await resources.read('unity://logs?severity=warning&limit=9999'); + await resources.read('unity://logs?limit=0'); + + expect(client.readTool).toHaveBeenNthCalledWith(1, 'get_console_logs', { + severity: 'all', + limit: 100, + }); + expect(client.readTool).toHaveBeenNthCalledWith(2, 'get_console_logs', { + severity: 'warning', + limit: 1000, + }); + expect(client.readTool).toHaveBeenNthCalledWith(3, 'get_console_logs', { + severity: 'all', + limit: 1, + }); + }); + + test('validates log severity and numeric limits', async () => { + const resources = new CompanionResourceService(clientWith(async () => toolResult({}))); + + await expect(resources.read('unity://logs?severity=debug')).rejects.toThrow('severity'); + await expect(resources.read('unity://logs?limit=many')).rejects.toThrow('limit'); + }); + + test('maps scene hierarchy and deterministically truncates depth-first', async () => { + const hierarchy = { + sceneName: 'Main', + roots: [ + { + name: 'A', + children: [ + { name: 'A1', children: [{ name: 'A1a', children: [] }] }, + { name: 'A2', children: [] }, + ], + }, + { name: 'B', children: [] }, + ], + }; + const client = clientWith(async () => textResult(hierarchy)); + const resources = new CompanionResourceService(client); + + const result = await resources.read( + 'unity://scenes-hierarchy?path=Assets%2FMain.unity&max_nodes=3', + ); + + expect(client.readTool).toHaveBeenCalledWith('get_scene_hierarchy', { + path: 'Assets/Main.unity', + }); + expect(result.payload).toMatchObject({ + sceneName: 'Main', + roots: [ + { + name: 'A', + children: [ + { + name: 'A1', + children: [{ name: 'A1a', children: [] }], + childrenTruncated: false, + }, + ], + childrenTruncated: true, + omittedDescendants: 1, + }, + ], + truncation: { + truncated: true, + maxNodes: 3, + returnedNodes: 3, + totalNodesKnown: true, + totalNodes: 5, + omittedNodes: 2, + }, + }); + }); + + test('uses hierarchy defaults and clamps max_nodes', async () => { + const client = clientWith(async () => toolResult({ roots: [] })); + const resources = new CompanionResourceService(client); + + expect((await resources.read('unity://scenes-hierarchy')).payload).toMatchObject({ + truncation: { maxNodes: 500 }, + }); + expect( + (await resources.read('unity://scenes-hierarchy?max_nodes=99999')).payload, + ).toMatchObject({ truncation: { maxNodes: 2000 } }); + await expect( + resources.read('unity://scenes-hierarchy?max_nodes=nope'), + ).rejects.toThrow('max_nodes'); + }); + + test('bounds a 15k-deep hierarchy with stack-safe iterative traversal', async () => { + let node: Record = { name: 'leaf', children: [] }; + for (let depth = 0; depth < 15_000; depth++) { + node = { name: `node-${depth}`, children: [node] }; + } + const client = clientWith(async () => toolResult({ roots: [node] })); + const resources = new CompanionResourceService(client); + + const result = await resources.read( + 'unity://scenes-hierarchy?max_nodes=2000', + ); + const truncation = result.payload.truncation as Record; + + expect(truncation).toMatchObject({ + truncated: true, + maxNodes: 2000, + returnedNodes: 2000, + totalNodesKnown: false, + totalNodesAtLeast: 8001, + omittedNodesAtLeast: 6001, + traversalBudget: 8000, + visitedNodes: 8000, + }); + + let output = (result.payload.roots as Array>)[0]; + let outputCount = 1; + while ((output.children as unknown[]).length > 0) { + output = (output.children as Array>)[0]; + outputCount++; + } + expect(outputCount).toBe(2000); + expect(output.childrenTruncated).toBe(true); + expect(output.omittedDescendantsKnown).toBe(false); + }); + + test('bounds very wide hierarchies without scanning or cloning every child', async () => { + const children = Array.from({ length: 50_000 }, (_, index) => ({ + name: `child-${index}`, + components: ['Transform'], + children: [], + })); + const client = clientWith(async () => + toolResult({ roots: [{ name: 'root', children }] }), + ); + const resources = new CompanionResourceService(client); + + const result = await resources.read( + 'unity://scenes-hierarchy?max_nodes=2', + ); + const truncation = result.payload.truncation as Record; + const root = (result.payload.roots as Array>)[0]; + + expect(truncation).toMatchObject({ + returnedNodes: 2, + totalNodesKnown: false, + totalNodesAtLeast: 1027, + omittedNodesAtLeast: 1025, + traversalBudget: 1026, + visitedNodes: 1026, + }); + expect((root.children as unknown[])).toHaveLength(1); + expect(root.childrenTruncated).toBe(true); + expect(root.omittedDescendants).toBe(1024); + expect(root.omittedDescendantsKnown).toBe(false); + }); + + test('bounds every projected hierarchy value from one malicious node', async () => { + const huge = 'x'.repeat(2_000_000); + const components: unknown[] = Array.from( + { length: 100_000 }, + (_, index) => + index === 0 + ? huge + : index === 1 + ? { name: huge, nested: { surprise: huge } } + : 'Transform', + ); + const hierarchy = { + sceneName: huge, + scenePath: huge, + isDirty: { nested: true }, + isActive: true, + metadataSurprise: { payload: huge }, + roots: [ + { + name: huge, + hierarchyPath: huge, + instanceId: { nested: 42 }, + activeSelf: 'true', + components, + objectSurprise: { payload: huge }, + children: [], + }, + ], + }; + const resources = new CompanionResourceService( + clientWith(async () => toolResult(hierarchy)), + ); + + const result = await resources.read( + 'unity://scenes-hierarchy?max_nodes=1', + ); + const root = (result.payload.roots as Array>)[0]; + const outputComponents = root.components as string[]; + + expect((result.payload.sceneName as string).length).toBeLessThanOrEqual(256); + expect((result.payload.scenePath as string).length).toBeLessThanOrEqual(1024); + expect((root.name as string).length).toBeLessThanOrEqual(256); + expect((root.hierarchyPath as string).length).toBeLessThanOrEqual(1024); + expect(outputComponents.length).toBeLessThanOrEqual(32); + expect(outputComponents.every((value) => value.length <= 128)).toBe(true); + expect(root).not.toHaveProperty('instanceId'); + expect(root).not.toHaveProperty('activeSelf'); + expect(root).not.toHaveProperty('objectSurprise'); + expect(result.payload).not.toHaveProperty('metadataSurprise'); + expect(root.projection).toMatchObject({ + truncatedStringCount: 2, + truncatedStrings: { + name: { originalLength: 2_000_000, returnedLength: 256 }, + hierarchyPath: { originalLength: 2_000_000, returnedLength: 1024 }, + }, + omittedKnownFieldCount: 2, + omittedKnownFields: ['instanceId', 'activeSelf'], + components: { + sourceCount: 100_000, + returnedCount: 32, + omittedCount: 99_968, + namesTruncated: 2, + scanTruncated: true, + }, + }); + expect(result.payload.metadataProjection).toMatchObject({ + truncatedStringCount: 2, + truncatedStrings: { + sceneName: { originalLength: 2_000_000, returnedLength: 256 }, + scenePath: { originalLength: 2_000_000, returnedLength: 1024 }, + }, + omittedKnownFieldCount: 1, + omittedKnownFields: ['isDirty'], + }); + expect(Buffer.byteLength(JSON.stringify(result.payload))).toBeLessThan( + 10_000, + ); + }); + + test('bounds the aggregate serialized hierarchy payload at 512 KiB', async () => { + const escapedName = '"\n\\'.repeat(100); + const escapedPath = '"\n\\'.repeat(400); + const escapedComponent = '"\n\\'.repeat(60); + const components = Array.from( + { length: 32 }, + () => escapedComponent, + ); + const roots = Array.from({ length: 2000 }, (_, index) => ({ + name: `${index}-${escapedName}`, + hierarchyPath: `${escapedPath}/${index}`, + instanceId: index, + activeSelf: true, + components, + children: [], + })); + const resources = new CompanionResourceService( + clientWith(async () => + toolResult({ + sceneName: escapedName, + scenePath: escapedPath, + isDirty: false, + isActive: true, + roots, + }), + ), + ); + + const result = await resources.read( + 'unity://scenes-hierarchy?max_nodes=2000', + ); + const serializedBytes = Buffer.byteLength(JSON.stringify(result.payload)); + const truncation = result.payload.truncation as Record; + + expect(serializedBytes).toBeLessThanOrEqual(512 * 1024); + expect(truncation).toMatchObject({ + truncated: true, + maxNodes: 2000, + totalNodesKnown: true, + totalNodes: 2000, + payloadBudgetReached: true, + payloadBudgetBytes: 512 * 1024, + projectedBytes: serializedBytes, + }); + expect(truncation.returnedNodes as number).toBeLessThan(2000); + expect(truncation.omittedAtBudgetNodes).toBe( + 2000 - (truncation.returnedNodes as number), + ); + expect(truncation.omittedAtBudgetComponents as number).toBeGreaterThan(0); + expect(truncation.rootsTruncated).toBe(true); + expect((result.payload.roots as unknown[]).length).toBe( + truncation.returnedNodes, + ); + expect( + (result.payload.roots as Array>).some((node) => { + const projection = node.projection as + | { components?: { payloadBudgetReached?: boolean } } + | undefined; + return projection?.components?.payloadBudgetReached === true; + }), + ).toBe(true); + }); + + test('maps a GameObject target to bounded inspect_gameobject defaults', async () => { + const client = clientWith(async () => toolResult({ name: 'Player' })); + const resources = new CompanionResourceService(client); + + await resources.read('unity://gameobject/%2FPlayer%2FCamera'); + + expect(client.readTool).toHaveBeenCalledWith('inspect_gameobject', { + target: '/Player/Camera', + max_depth: 2, + max_nodes: 200, + include_components: true, + include_properties: true, + max_properties_per_component: 100, + }); + await expect(resources.read('unity://gameobject/')).rejects.toThrow('target'); + }); + + test('maps installed packages and validates include_indirect', async () => { + const client = clientWith(async () => toolResult({ packages: [] })); + const resources = new CompanionResourceService(client); + + await resources.read('unity://packages'); + await resources.read('unity://packages?include_indirect=false'); + + expect(client.readTool).toHaveBeenNthCalledWith(1, 'package_list', { + scope: 'installed', + include_indirect: true, + }); + expect(client.readTool).toHaveBeenNthCalledWith(2, 'package_list', { + scope: 'installed', + include_indirect: false, + }); + await expect( + resources.read('unity://packages?include_indirect=sometimes'), + ).rejects.toThrow('include_indirect'); + }); + + test.each(['all', 'editor', 'playmode'])('maps test mode %s', async (mode) => { + const client = clientWith(async () => toolResult({ tests: [] })); + const resources = new CompanionResourceService(client); + + await resources.read(`unity://tests/${mode}`); + + expect(client.readTool).toHaveBeenCalledWith('list_tests', { mode }); + }); + + test('rejects invalid test modes and unknown resources', async () => { + const resources = new CompanionResourceService(clientWith(async () => toolResult({}))); + + await expect(resources.read('unity://tests/runtime')).rejects.toThrow('mode'); + await expect(resources.read('unity://assets')).rejects.toThrow('Unknown companion resource'); + }); +}); + +describe('official tool response decoding', () => { + test.each([ + [ + 'tool error', + clientWith(async () => ({ + isError: true, + content: [ + { + type: 'text', + text: `tool-start-${'x'.repeat(16 * 1024 * 1024)}-tool-secret-tail`, + }, + ], + })), + ], + [ + 'transport error', + clientWith(async () => { + throw new Error( + `transport-start-${'x'.repeat(16 * 1024 * 1024)}-transport-secret-tail`, + ); + }), + ], + ])('bounds 16 MiB %s details with an explicit marker', async (_, client) => { + const resources = new CompanionResourceService(client); + + let message = ''; + try { + await resources.read('unity://logs'); + throw new Error('Expected the resource read to fail.'); + } catch (error) { + message = error instanceof Error ? error.message : String(error); + } + + expect(message.length).toBeLessThanOrEqual(4200); + expect(message).toContain('[truncated]'); + expect(message).not.toContain('secret-tail'); + }); + + test('prefers object structuredContent and falls back to the first JSON text item', async () => { + const structuredClient = clientWith(async () => + toolResult({ source: 'structured' }), + ); + const textClient = clientWith(async () => ({ + content: [ + { type: 'image', data: 'AA==', mimeType: 'image/png' }, + { type: 'text', text: '{"source":"text"}' }, + ], + })); + + await expect( + new CompanionResourceService(structuredClient).read('unity://logs'), + ).resolves.toMatchObject({ payload: { source: 'structured' } }); + await expect( + new CompanionResourceService(textClient).read('unity://logs'), + ).resolves.toMatchObject({ payload: { source: 'text' } }); + }); + + test('returns clear errors for tool errors and malformed payloads', async () => { + const toolError = clientWith(async () => ({ + isError: true, + content: [{ type: 'text', text: 'Editor is not connected' }], + })); + const malformed = clientWith(async () => ({ + content: [{ type: 'text', text: 'not-json' }], + })); + const empty = clientWith(async () => ({ content: [] })); + + await expect( + new CompanionResourceService(toolError).read('unity://logs'), + ).rejects.toThrow('Editor is not connected'); + await expect( + new CompanionResourceService(malformed).read('unity://logs'), + ).rejects.toThrow('malformed'); + await expect( + new CompanionResourceService(empty).read('unity://logs'), + ).rejects.toThrow('no JSON payload'); + }); + + test('adds actionable context for missing commands, Pipeline disconnects, and CLI exit', async () => { + for (const message of [ + 'Method not found: inspect_gameobject', + 'Pipeline connection refused', + 'Unity CLI process exited with code 1', + ]) { + const client = clientWith(async () => { + throw new Error(message); + }); + await expect( + new CompanionResourceService(client).read('unity://gameobject/Player'), + ).rejects.toThrow(`inspect_gameobject failed: ${message}`); + } + }); +}); diff --git a/Server~/src/__tests__/companionServer.test.ts b/Server~/src/__tests__/companionServer.test.ts new file mode 100644 index 00000000..ca0c8eae --- /dev/null +++ b/Server~/src/__tests__/companionServer.test.ts @@ -0,0 +1,265 @@ +import { jest } from '@jest/globals'; +import fs from 'node:fs'; +import path from 'node:path'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import { LATEST_PROTOCOL_VERSION } from '@modelcontextprotocol/ext-apps'; +import { createCompanionServer } from '../companionServer.js'; +import { + CompanionResourceService, + type UnityReadClient, +} from '../resources/companionResources.js'; + +async function connectedCompanion( + implementation: UnityReadClient['readTool'] = async (): Promise => ({ + content: [], + structuredContent: { ok: true }, + }), + dashboardReader?: () => { text: string; mimeType: string }, +) { + const readTool = jest.fn(implementation); + const unityClient: UnityReadClient = { readTool }; + const server = createCompanionServer( + new CompanionResourceService(unityClient), + dashboardReader ? { readDashboardHtml: dashboardReader } : undefined, + ); + const client = new Client( + { name: 'companion-test', version: '1.0.0' }, + { capabilities: {} }, + ); + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + await Promise.all([ + server.connect(serverTransport), + client.connect(clientTransport), + ]); + return { client, server, readTool }; +} + +describe('outer MCP companion catalog', () => { + test('advertises exactly one dashboard tool, two prompts, and six resources', async () => { + const { client, server } = await connectedCompanion(); + try { + const tools = await client.listTools(); + const prompts = await client.listPrompts(); + const resources = await client.listResources(); + const templates = await client.listResourceTemplates(); + + expect(tools.tools.map((tool) => tool.name)).toEqual(['show_unity_dashboard']); + expect(prompts.prompts.map((prompt) => prompt.name).sort()).toEqual([ + 'gameobject_handling_strategy', + 'unity_dashboard', + ]); + expect(resources.resources.map((resource) => resource.uri)).toEqual([ + 'ui://unity-dashboard', + ]); + expect(templates.resourceTemplates.map((template) => template.uriTemplate).sort()).toEqual([ + 'unity://gameobject/{target}', + 'unity://logs{?severity,limit}', + 'unity://packages{?include_indirect}', + 'unity://scenes-hierarchy{?path,max_nodes}', + 'unity://tests/{mode}', + ]); + + const forbiddenMutationTools = [ + 'assign_material', + 'duplicate_gameobject', + 'editor_step', + 'execute_menu_item', + 'package_add', + 'run_tests', + 'unload_scene', + ]; + expect(tools.tools.map((tool) => tool.name)).not.toEqual( + expect.arrayContaining(forbiddenMutationTools), + ); + } finally { + await client.close(); + await server.close(); + } + }); + + test('routes concrete resource reads without advertising official tools', async () => { + const { client, server, readTool } = await connectedCompanion(); + try { + const result = await client.readResource({ + uri: 'unity://logs?severity=error&limit=4', + }); + expect(readTool).toHaveBeenCalledWith('get_console_logs', { + severity: 'error', + limit: 4, + }); + expect(JSON.parse(result.contents[0].text as string)).toMatchObject({ + ok: true, + projection: { + truncated: false, + payloadBudgetBytes: 512 * 1024, + }, + }); + } finally { + await client.close(); + await server.close(); + } + }); + + test('bounds the serialized outer MCP resource error response', async () => { + const { client, server } = await connectedCompanion(async () => { + throw new Error( + `outer-start-${'x'.repeat(16 * 1024 * 1024)}-outer-secret-tail`, + ); + }); + try { + let serialized = ''; + try { + await client.readResource({ uri: 'unity://logs?severity=error&limit=4' }); + throw new Error('Expected the outer MCP read to fail.'); + } catch (error) { + serialized = JSON.stringify({ + error: { + message: error instanceof Error ? error.message : String(error), + }, + }); + } + + expect(Buffer.byteLength(serialized)).toBeLessThanOrEqual(5 * 1024); + expect(serialized).toContain('[truncated]'); + expect(serialized).not.toContain('outer-secret-tail'); + } finally { + await client.close(); + await server.close(); + } + }); + + test('bounds the serialized outer MCP dashboard error response', async () => { + const { client, server } = await connectedCompanion( + undefined, + () => { + throw new Error( + `dashboard-start-${'x'.repeat(16 * 1024 * 1024)}-dashboard-secret-tail`, + ); + }, + ); + try { + let serialized = ''; + try { + await client.readResource({ uri: 'ui://unity-dashboard' }); + throw new Error('Expected the dashboard read to fail.'); + } catch (error) { + serialized = JSON.stringify({ + error: { + message: error instanceof Error ? error.message : String(error), + }, + }); + } + + expect(Buffer.byteLength(serialized)).toBeLessThanOrEqual(5 * 1024); + expect(serialized).toContain('[truncated]'); + expect(serialized).not.toContain('dashboard-secret-tail'); + } finally { + await client.close(); + await server.close(); + } + }); + + test('dashboard tool and app resource expose MCP App metadata and bundled HTML', async () => { + const { client, server } = await connectedCompanion(); + try { + const tools = await client.listTools(); + expect(tools.tools[0]._meta).toMatchObject({ + ui: { resourceUri: 'ui://unity-dashboard' }, + 'ui/resourceUri': 'ui://unity-dashboard', + }); + + const toolResult = await client.callTool({ name: 'show_unity_dashboard' }); + expect(toolResult.isError).not.toBe(true); + const app = await client.readResource({ uri: 'ui://unity-dashboard' }); + expect(app.contents[0].mimeType).toBe('text/html;profile=mcp-app'); + expect(app.contents[0]._meta).toEqual({ + ui: { + csp: { + connectDomains: [], + resourceDomains: [], + frameDomains: [], + baseUriDomains: [], + }, + }, + }); + const html = app.contents[0].text as string; + for (const resource of [ + 'unity://logs', + 'unity://scenes-hierarchy', + 'unity://gameobject/', + 'unity://packages', + 'unity://tests/', + 'ui://unity-dashboard', + ]) { + expect(html).toContain(resource); + } + expect(html).toContain('refreshInFlight'); + expect(html).toContain('MIN_REFRESH_MS'); + expect(html).toContain('Pipeline'); + expect(html).toContain('truncation'); + expect(html).toContain('totalNodesKnown'); + expect(html).toContain('totalNodesAtLeast'); + expect(html).toContain('renderProjection'); + expect(html).toContain('projection-note'); + expect(html).toContain('Projection truncated'); + expect(LATEST_PROTOCOL_VERSION).toBe('2026-01-26'); + expect(html).toContain("const PROTOCOL_VERSION = '2026-01-26'"); + expect(html).toContain('event.source !== window.parent'); + expect(html).not.toContain('set_play_mode_status'); + expect(html).not.toContain('tools/call'); + } finally { + await client.close(); + await server.close(); + } + }); + + test('built dashboard retains the validated app protocol and parent-source guard', () => { + const html = fs.readFileSync( + path.join(process.cwd(), 'build', 'ui', 'unity-dashboard.html'), + 'utf8', + ); + expect(html).toContain("const PROTOCOL_VERSION = '2026-01-26'"); + expect(html).toContain('event.source !== window.parent'); + }); + + test('prompts use official Pipeline and five extension command names without aliases', async () => { + const { client, server } = await connectedCompanion(); + try { + const strategy = await client.getPrompt({ + name: 'gameobject_handling_strategy', + }); + const dashboard = await client.getPrompt({ name: 'unity_dashboard' }); + const promptText = [...strategy.messages, ...dashboard.messages] + .map((message) => + message.content.type === 'text' ? message.content.text : '', + ) + .join('\n'); + + for (const command of [ + 'get_scene_hierarchy', + 'package_list', + 'list_tests', + 'inspect_gameobject', + 'duplicate_gameobject', + 'unload_scene', + 'editor_step', + 'assign_material', + ]) { + expect(promptText).toContain(command); + } + for (const legacyAlias of [ + 'get_gameobject', + 'get_scene_info', + 'set_play_mode_status', + 'update_gameobject', + ]) { + expect(promptText).not.toContain(legacyAlias); + } + } finally { + await client.close(); + await server.close(); + } + }); +}); diff --git a/Server~/src/__tests__/dashboardSupportTools.test.ts b/Server~/src/__tests__/dashboardSupportTools.test.ts deleted file mode 100644 index 47c3eb5e..00000000 --- a/Server~/src/__tests__/dashboardSupportTools.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { jest, describe, it, expect, beforeEach } from '@jest/globals'; -import { ErrorType } from '../utils/errors.js'; -import { registerGetConsoleLogsTool } from '../tools/getConsoleLogsTool.js'; -import { registerGetScenesHierarchyTool } from '../tools/getScenesHierarchyTool.js'; - -const mockSendRequest = jest.fn(); -const mockMcpUnity = { - sendRequest: mockSendRequest -}; - -const mockLogger = { - info: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn() -}; - -const mockServerTool = jest.fn(); -const mockServer = { - tool: mockServerTool -}; - -describe('Dashboard Support Tools', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('get_console_logs', () => { - it('forwards pagination options and returns structured log data', async () => { - registerGetConsoleLogsTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - const handler = mockServerTool.mock.calls[0][3] as (params: any) => Promise; - const logs = [{ type: 'error', message: 'Boom' }]; - - mockSendRequest.mockResolvedValue({ - success: true, - logs - }); - - const result = await handler({ - logType: 'error', - offset: 10, - limit: 25, - includeStackTrace: false - }); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'get_console_logs', - params: { - logType: 'error', - offset: 10, - limit: 25, - includeStackTrace: false - } - }); - expect(result.data).toEqual({ - logs, - offset: 10, - limit: 25, - logType: 'error', - includeStackTrace: false - }); - }); - }); - - describe('get_scenes_hierarchy', () => { - it('forwards to Unity and returns hierarchy data', async () => { - registerGetScenesHierarchyTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - const handler = mockServerTool.mock.calls[0][3] as (params: any) => Promise; - const hierarchy = [{ name: 'SampleScene', children: [] }]; - - mockSendRequest.mockResolvedValue({ - success: true, - hierarchy - }); - - const result = await handler({}); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'get_scenes_hierarchy', - params: {} - }); - expect(result.content[0].text).toContain('SampleScene'); - expect(result.data).toEqual({ hierarchy }); - }); - - it('throws a tool execution error when Unity fails', async () => { - registerGetScenesHierarchyTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - const handler = mockServerTool.mock.calls[0][3] as (params: any) => Promise; - - mockSendRequest.mockResolvedValue({ - success: false, - message: 'Hierarchy unavailable' - }); - - await expect(handler({})).rejects.toMatchObject({ - type: ErrorType.TOOL_EXECUTION, - message: 'Hierarchy unavailable' - }); - }); - }); -}); diff --git a/Server~/src/__tests__/errors.test.ts b/Server~/src/__tests__/errors.test.ts deleted file mode 100644 index da619817..00000000 --- a/Server~/src/__tests__/errors.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { McpUnityError, ErrorType, handleError } from '../utils/errors.js'; - -describe('McpUnityError', () => { - describe('constructor', () => { - it('should create error with type and message', () => { - const error = new McpUnityError(ErrorType.CONNECTION, 'Connection failed'); - - expect(error.type).toBe(ErrorType.CONNECTION); - expect(error.message).toBe('Connection failed'); - expect(error.name).toBe('McpUnityError'); - expect(error.details).toBeUndefined(); - }); - - it('should create error with details', () => { - const details = { host: 'localhost', port: 8090 }; - const error = new McpUnityError(ErrorType.CONNECTION, 'Connection failed', details); - - expect(error.details).toEqual(details); - }); - - it('should be an instance of Error', () => { - const error = new McpUnityError(ErrorType.INTERNAL, 'Test error'); - - expect(error).toBeInstanceOf(Error); - expect(error).toBeInstanceOf(McpUnityError); - }); - }); - - describe('toJSON', () => { - it('should serialize error to JSON object', () => { - const error = new McpUnityError(ErrorType.VALIDATION, 'Invalid input', { field: 'name' }); - - const json = error.toJSON(); - - expect(json).toEqual({ - type: ErrorType.VALIDATION, - message: 'Invalid input', - details: { field: 'name' }, - }); - }); - - it('should serialize error without details', () => { - const error = new McpUnityError(ErrorType.TIMEOUT, 'Request timed out'); - - const json = error.toJSON(); - - expect(json).toEqual({ - type: ErrorType.TIMEOUT, - message: 'Request timed out', - details: undefined, - }); - }); - }); -}); - -describe('handleError', () => { - it('should return McpUnityError as-is', () => { - const originalError = new McpUnityError(ErrorType.TOOL_EXECUTION, 'Tool failed'); - - const result = handleError(originalError, 'test'); - - expect(result).toBe(originalError); - }); - - it('should wrap standard Error in McpUnityError', () => { - const standardError = new Error('Something went wrong'); - - const result = handleError(standardError, 'TestContext'); - - expect(result).toBeInstanceOf(McpUnityError); - expect(result.type).toBe(ErrorType.INTERNAL); - expect(result.message).toBe('TestContext error: Something went wrong'); - expect(result.details).toBe(standardError); - }); - - it('should handle error without message', () => { - const errorWithoutMessage = {}; - - const result = handleError(errorWithoutMessage, 'Context'); - - expect(result.message).toBe('Context error: Unknown error'); - }); -}); - -describe('ErrorType', () => { - it('should have all expected error types', () => { - expect(ErrorType.CONNECTION).toBe('connection_error'); - expect(ErrorType.TOOL_EXECUTION).toBe('tool_execution_error'); - expect(ErrorType.RESOURCE_FETCH).toBe('resource_fetch_error'); - expect(ErrorType.VALIDATION).toBe('validation_error'); - expect(ErrorType.INTERNAL).toBe('internal_error'); - expect(ErrorType.TIMEOUT).toBe('timeout_error'); - }); -}); diff --git a/Server~/src/__tests__/fixtures/legacy-1.4.0-inventory.json b/Server~/src/__tests__/fixtures/legacy-1.4.0-inventory.json new file mode 100644 index 00000000..733e3112 --- /dev/null +++ b/Server~/src/__tests__/fixtures/legacy-1.4.0-inventory.json @@ -0,0 +1,235 @@ +{ + "schemaVersion": 1, + "sourceTag": "1.4.0", + "sourceCommit": "bbfb1c0681519ced5b357ce7cc3c1ee68c9dc64e", + "tools": [ + "add_asset_to_scene", + "add_package", + "assign_material", + "batch_execute", + "create_material", + "create_prefab", + "create_scene", + "delete_gameobject", + "delete_scene", + "duplicate_gameobject", + "execute_menu_item", + "get_console_logs", + "get_gameobject", + "get_material_info", + "get_play_mode_status", + "get_scene_info", + "get_scenes_hierarchy", + "load_scene", + "modify_material", + "move_gameobject", + "recompile_scripts", + "reparent_gameobject", + "rotate_gameobject", + "run_tests", + "save_scene", + "scale_gameobject", + "select_gameobject", + "send_console_log", + "set_play_mode_status", + "set_transform", + "show_unity_dashboard", + "unload_scene", + "update_component", + "update_gameobject" + ], + "resources": [ + "get_assets", + "get_console_logs", + "get_gameobject", + "get_menu_items", + "get_packages", + "get_scenes_hierarchy", + "get_tests", + "unity_dashboard_app", + "unity_dashboard_app_legacy" + ], + "uris": [ + "ui://unity-dashboard", + "unity://assets", + "unity://gameobject/{idOrName}", + "unity://logs/{logType}?offset={offset}&limit={limit}&includeStackTrace={includeStackTrace}", + "unity://menu-items", + "unity://packages", + "unity://scenes_hierarchy", + "unity://tests/{testMode}", + "unity://ui/dashboard" + ], + "prompts": [ + "gameobject_handling_strategy", + "unity_dashboard" + ], + "settings": [ + "Port", + "RequestTimeoutSeconds", + "AutoStartServer", + "EnableInfoLogs", + "NpmExecutablePath", + "AllowRemoteConnections" + ], + "integrations": [ + { + "id": "env:UNITY_HOST", + "evidence": [ + { + "path": "Server~/src/unity/mcpUnity.ts", + "contains": "process.env.UNITY_HOST" + } + ] + }, + { + "id": "env:LOGGING", + "evidence": [ + { + "path": "Server~/src/utils/logger.ts", + "contains": "process.env.LOGGING === 'true'" + } + ] + }, + { + "id": "env:LOGGING_FILE", + "evidence": [ + { + "path": "Server~/src/utils/logger.ts", + "contains": "process.env.LOGGING_FILE === 'true'" + } + ] + }, + { + "id": "path:ProjectSettings/McpUnitySettings.json", + "evidence": [ + { + "path": "Editor/UnityBridge/McpUnitySettings.cs", + "contains": "ProjectSettings/McpUnitySettings.json" + } + ] + }, + { + "id": "integration:Unity-driven npm install/build", + "evidence": [ + { + "path": "Editor/UnityBridge/McpUnityServer.cs", + "contains": "npm install" + }, + { + "path": "Editor/UnityBridge/McpUnityServer.cs", + "contains": "npm run build" + } + ] + }, + { + "id": "integration:automatic MCP-client configuration", + "evidence": [ + { + "path": "Editor/UnityBridge/McpUnityEditorWindow.cs", + "contains": "ShowConfigButton(\"Windsurf\"" + }, + { + "path": "Editor/Utils/McpUtils.cs", + "contains": "AddToConfigFile" + } + ] + }, + { + "id": "integration:PackedCache mutation", + "evidence": [ + { + "path": "Editor/UnityBridge/McpUnityEditorWindow.cs", + "contains": "Library/PackedCache" + }, + { + "path": "Editor/Utils/VsCodeWorkspaceUtils.cs", + "contains": "Library/PackageCache" + } + ] + }, + { + "id": "integration:custom WebSocket endpoint/port", + "evidence": [ + { + "path": "Server~/src/unity/mcpUnity.ts", + "contains": "private port: number = 8090" + }, + { + "path": "Editor/UnityBridge/McpUnitySettings.cs", + "contains": "public int Port = 8090" + } + ] + }, + { + "id": "integration:Docker deployment/Dockerfile/exposed ports", + "evidence": [ + { + "path": "Server~/Dockerfile", + "contains": "EXPOSE 8090 3000" + }, + { + "path": "Server~/.dockerignore", + "contains": "# Node.js" + } + ] + }, + { + "id": "integration:Smithery configuration", + "evidence": [ + { + "path": "Server~/smithery.yaml", + "contains": "Smithery.ai configuration" + } + ] + }, + { + "id": "integration:Node npm executable/bin/publication surface", + "evidence": [ + { + "path": "Editor/UnityBridge/McpUnitySettings.cs", + "contains": "NpmExecutablePath" + }, + { + "path": "Server~/package.json", + "contains": "\"bin\"" + }, + { + "path": "Server~/package.json", + "contains": "\"files\"" + } + ] + }, + { + "id": "integration:MCP registry server.json", + "evidence": [ + { + "path": "server.json", + "absent": true + } + ] + }, + { + "id": "integration:MCP registry mcpName/mcpname", + "evidence": [ + { + "path": "Server~/package.json", + "contains": "\"mcpName\"" + }, + { + "path": "package.json", + "contains": "\"mcpname\"" + } + ] + }, + { + "id": "integration:Glama registry metadata", + "evidence": [ + { + "path": "glama.json", + "contains": "glama.ai/mcp/schemas/server.json" + } + ] + } + ] +} diff --git a/Server~/src/__tests__/logger.test.ts b/Server~/src/__tests__/logger.test.ts deleted file mode 100644 index d6531f79..00000000 --- a/Server~/src/__tests__/logger.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { jest } from '@jest/globals'; -import { Logger, LogLevel } from '../utils/logger.js'; - -describe('Logger', () => { - let consoleSpy: jest.SpiedFunction; - - beforeEach(() => { - consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); - }); - - afterEach(() => { - consoleSpy.mockRestore(); - }); - - describe('constructor', () => { - it('should create logger with prefix and default level', () => { - const logger = new Logger('TestPrefix'); - - expect(logger).toBeInstanceOf(Logger); - }); - - it('should create logger with custom level', () => { - const logger = new Logger('TestPrefix', LogLevel.DEBUG); - - expect(logger).toBeInstanceOf(Logger); - }); - }); - - describe('log level filtering', () => { - it('should not log messages below the configured level', () => { - // Set environment variable for logging - const originalEnv = process.env.LOGGING; - process.env.LOGGING = 'true'; - - const logger = new Logger('Test', LogLevel.WARN); - - logger.debug('debug message'); - logger.info('info message'); - - expect(consoleSpy).not.toHaveBeenCalled(); - - process.env.LOGGING = originalEnv; - }); - - it('should respect log level hierarchy', () => { - // This test verifies the Logger respects log levels - // Note: Actual console output depends on LOGGING env var set at module load time - const logger = new Logger('Test', LogLevel.DEBUG); - - // All methods should be callable without throwing - expect(() => { - logger.debug('debug'); - logger.info('info'); - logger.warn('warn'); - logger.error('error'); - }).not.toThrow(); - }); - }); - - describe('isLoggingEnabled', () => { - it('should return a boolean value', () => { - const logger = new Logger('Test'); - expect(typeof logger.isLoggingEnabled()).toBe('boolean'); - }); - }); - - describe('isLoggingFileEnabled', () => { - it('should return a boolean value', () => { - const logger = new Logger('Test'); - expect(typeof logger.isLoggingFileEnabled()).toBe('boolean'); - }); - }); - - describe('logging methods', () => { - it('should have debug method', () => { - const logger = new Logger('Test'); - expect(typeof logger.debug).toBe('function'); - }); - - it('should have info method', () => { - const logger = new Logger('Test'); - expect(typeof logger.info).toBe('function'); - }); - - it('should have warn method', () => { - const logger = new Logger('Test'); - expect(typeof logger.warn).toBe('function'); - }); - - it('should have error method', () => { - const logger = new Logger('Test'); - expect(typeof logger.error).toBe('function'); - }); - }); -}); - -describe('LogLevel', () => { - it('should have correct numeric values for ordering', () => { - expect(LogLevel.DEBUG).toBe(0); - expect(LogLevel.INFO).toBe(1); - expect(LogLevel.WARN).toBe(2); - expect(LogLevel.ERROR).toBe(3); - }); - - it('should allow level comparison', () => { - expect(LogLevel.DEBUG < LogLevel.INFO).toBe(true); - expect(LogLevel.INFO < LogLevel.WARN).toBe(true); - expect(LogLevel.WARN < LogLevel.ERROR).toBe(true); - }); -}); diff --git a/Server~/src/__tests__/materialTools.test.ts b/Server~/src/__tests__/materialTools.test.ts deleted file mode 100644 index 4df4a044..00000000 --- a/Server~/src/__tests__/materialTools.test.ts +++ /dev/null @@ -1,436 +0,0 @@ -import { jest, describe, it, expect, beforeEach } from '@jest/globals'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { - registerCreateMaterialTool, - registerAssignMaterialTool, - registerModifyMaterialTool, - registerGetMaterialInfoTool -} from '../tools/materialTools.js'; - -// Mock the McpUnity class -const mockSendRequest = jest.fn(); -const mockMcpUnity = { - sendRequest: mockSendRequest -}; - -// Mock the Logger -const mockLogger = { - info: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn() -}; - -// Mock the McpServer -const mockServerTool = jest.fn(); -const mockServer = { - tool: mockServerTool -}; - -describe('Material Tools', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('registerCreateMaterialTool', () => { - it('should register the create_material tool with the server', () => { - registerCreateMaterialTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledTimes(1); - expect(mockServerTool).toHaveBeenCalledWith( - 'create_material', - expect.any(String), - expect.any(Object), - expect.any(Function) - ); - expect(mockLogger.info).toHaveBeenCalledWith('Registering tool: create_material'); - }); - - it('should have correct tool description', () => { - registerCreateMaterialTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - const [, description] = mockServerTool.mock.calls[0]; - expect(description).toContain('material'); - expect(description).toContain('shader'); - }); - }); - - describe('registerAssignMaterialTool', () => { - it('should register the assign_material tool with the server', () => { - registerAssignMaterialTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledTimes(1); - expect(mockServerTool).toHaveBeenCalledWith( - 'assign_material', - expect.any(String), - expect.any(Object), - expect.any(Function) - ); - expect(mockLogger.info).toHaveBeenCalledWith('Registering tool: assign_material'); - }); - }); - - describe('registerModifyMaterialTool', () => { - it('should register the modify_material tool with the server', () => { - registerModifyMaterialTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledTimes(1); - expect(mockServerTool).toHaveBeenCalledWith( - 'modify_material', - expect.any(String), - expect.any(Object), - expect.any(Function) - ); - expect(mockLogger.info).toHaveBeenCalledWith('Registering tool: modify_material'); - }); - }); - - describe('registerGetMaterialInfoTool', () => { - it('should register the get_material_info tool with the server', () => { - registerGetMaterialInfoTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledTimes(1); - expect(mockServerTool).toHaveBeenCalledWith( - 'get_material_info', - expect.any(String), - expect.any(Object), - expect.any(Function) - ); - expect(mockLogger.info).toHaveBeenCalledWith('Registering tool: get_material_info'); - }); - }); - - describe('create_material handler', () => { - let handler: Function; - - beforeEach(() => { - registerCreateMaterialTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - handler = mockServerTool.mock.calls[0][3]; - }); - - it('should throw validation error when name is missing', async () => { - const params = { savePath: 'Assets/Materials/Test.mat' }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.VALIDATION, - message: expect.stringContaining('name') - }); - }); - - it('should throw validation error when savePath is missing', async () => { - const params = { name: 'TestMaterial' }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.VALIDATION, - message: expect.stringContaining('savePath') - }); - }); - - it('should send request to Unity with correct parameters', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material created' - }); - - const params = { - name: 'TestMaterial', - shader: 'Standard', - savePath: 'Assets/Materials/Test.mat', - properties: { _Color: { r: 1, g: 0, b: 0, a: 1 } } - }; - - await handler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'create_material', - params: { - name: 'TestMaterial', - shader: 'Standard', - savePath: 'Assets/Materials/Test.mat', - properties: { _Color: { r: 1, g: 0, b: 0, a: 1 } } - } - }); - }); - - it('should use default shader when not specified', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material created' - }); - - const params = { - name: 'TestMaterial', - savePath: 'Assets/Materials/Test.mat' - }; - - await handler(params); - - // Shader should be undefined - Unity auto-detects based on render pipeline - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'create_material', - params: expect.objectContaining({ - shader: undefined - }) - }); - }); - - it('should throw tool execution error on Unity failure', async () => { - mockSendRequest.mockResolvedValue({ - success: false, - message: 'Shader not found' - }); - - const params = { - name: 'TestMaterial', - shader: 'NonExistentShader', - savePath: 'Assets/Materials/Test.mat' - }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.TOOL_EXECUTION - }); - }); - }); - - describe('assign_material handler', () => { - let handler: Function; - - beforeEach(() => { - registerAssignMaterialTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - handler = mockServerTool.mock.calls[0][3]; - }); - - it('should throw validation error when neither instanceId nor objectPath provided', async () => { - const params = { materialPath: 'Assets/Materials/Test.mat' }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.VALIDATION, - message: expect.stringContaining('instanceId') - }); - }); - - it('should throw validation error when materialPath is missing', async () => { - const params = { objectPath: '/Player' }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.VALIDATION, - message: expect.stringContaining('materialPath') - }); - }); - - it('should send request with instanceId', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material assigned' - }); - - const params = { - instanceId: 12345, - materialPath: 'Assets/Materials/Test.mat', - slot: 0 - }; - - await handler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'assign_material', - params: { - instanceId: 12345, - objectPath: undefined, - materialPath: 'Assets/Materials/Test.mat', - slot: 0 - } - }); - }); - - it('should send request with objectPath', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material assigned' - }); - - const params = { - objectPath: '/Player/Body', - materialPath: 'Assets/Materials/Test.mat' - }; - - await handler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'assign_material', - params: expect.objectContaining({ - objectPath: '/Player/Body', - slot: 0 - }) - }); - }); - - it('should use default slot of 0 when not specified', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material assigned' - }); - - const params = { - instanceId: 12345, - materialPath: 'Assets/Materials/Test.mat' - }; - - await handler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'assign_material', - params: expect.objectContaining({ - slot: 0 - }) - }); - }); - }); - - describe('modify_material handler', () => { - let handler: Function; - - beforeEach(() => { - registerModifyMaterialTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - handler = mockServerTool.mock.calls[0][3]; - }); - - it('should throw validation error when materialPath is missing', async () => { - const params = { properties: { _Color: { r: 1, g: 0, b: 0, a: 1 } } }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.VALIDATION, - message: expect.stringContaining('materialPath') - }); - }); - - it('should throw validation error when properties is empty', async () => { - const params = { materialPath: 'Assets/Materials/Test.mat', properties: {} }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.VALIDATION, - message: expect.stringContaining('properties') - }); - }); - - it('should throw validation error when properties is missing', async () => { - const params = { materialPath: 'Assets/Materials/Test.mat' }; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - }); - - it('should send request with correct properties', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material modified' - }); - - const params = { - materialPath: 'Assets/Materials/Test.mat', - properties: { - _Color: { r: 1, g: 0.5, b: 0, a: 1 }, - _Metallic: 0.5, - _MainTex: 'Assets/Textures/Wood.png' - } - }; - - await handler(params); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'modify_material', - params: { - materialPath: 'Assets/Materials/Test.mat', - properties: { - _Color: { r: 1, g: 0.5, b: 0, a: 1 }, - _Metallic: 0.5, - _MainTex: 'Assets/Textures/Wood.png' - } - } - }); - }); - }); - - describe('get_material_info handler', () => { - let handler: Function; - - beforeEach(() => { - registerGetMaterialInfoTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - handler = mockServerTool.mock.calls[0][3]; - }); - - it('should throw validation error when materialPath is missing', async () => { - const params = {}; - - await expect(handler(params)).rejects.toThrow(McpUnityError); - await expect(handler(params)).rejects.toMatchObject({ - type: ErrorType.VALIDATION, - message: expect.stringContaining('materialPath') - }); - }); - - it('should send request and return formatted response', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material info', - materialName: 'TestMaterial', - materialPath: 'Assets/Materials/Test.mat', - shaderName: 'Standard', - renderQueue: 2000, - renderQueueCategory: 'Geometry', - enableInstancing: false, - doubleSidedGI: false, - passCount: 1, - properties: [ - { name: '_Color', type: 'Color', value: { r: 1, g: 1, b: 1, a: 1 }, description: 'Main Color' }, - { name: '_Metallic', type: 'Float', value: 0, description: 'Metallic' } - ] - }); - - const params = { materialPath: 'Assets/Materials/Test.mat' }; - const result = await handler(params); - - expect(result.content[0].text).toContain('Material: TestMaterial'); - expect(result.content[0].text).toContain('Shader: Standard'); - expect(result.content[0].text).toContain('_Color'); - expect(result.content[0].text).toContain('_Metallic'); - }); - - it('should include data object in response', async () => { - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - message: 'Material info', - materialName: 'TestMaterial', - materialPath: 'Assets/Materials/Test.mat', - shaderName: 'Standard', - renderQueue: 2000, - renderQueueCategory: 'Geometry', - enableInstancing: false, - doubleSidedGI: false, - passCount: 1, - properties: [] - }); - - const params = { materialPath: 'Assets/Materials/Test.mat' }; - const result = await handler(params); - - expect(result.data).toBeDefined(); - expect(result.data.materialName).toBe('TestMaterial'); - expect(result.data.shaderName).toBe('Standard'); - }); - }); -}); diff --git a/Server~/src/__tests__/mcpUnity.test.ts b/Server~/src/__tests__/mcpUnity.test.ts deleted file mode 100644 index 605c2132..00000000 --- a/Server~/src/__tests__/mcpUnity.test.ts +++ /dev/null @@ -1,273 +0,0 @@ -import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals'; -import { Logger, LogLevel } from '../utils/logger.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { McpUnity, ConnectionState } from '../unity/mcpUnity.js'; -import { registerTransformTools } from '../tools/transformTools.js'; -import path from 'path'; -import { z } from 'zod'; -import { zodToJsonSchema } from 'zod-to-json-schema'; - -describe('McpUnityError integration', () => { - it('should create proper error for connection issues', () => { - const error = new McpUnityError(ErrorType.CONNECTION, 'Failed to connect to Unity'); - - expect(error.type).toBe('connection_error'); - expect(error.message).toBe('Failed to connect to Unity'); - }); - - it('should create proper error for timeout', () => { - const error = new McpUnityError(ErrorType.TIMEOUT, 'Request timed out'); - - expect(error.type).toBe('timeout_error'); - }); -}); - -describe('Path handling in configuration', () => { - it('should handle paths with spaces in config file path', () => { - // The config path uses path.resolve which handles spaces correctly - const pathWithSpaces = '/Users/John Doe/My Project/ProjectSettings/McpUnitySettings.json'; - - // Verify path module handles spaces - const resolved = path.resolve(pathWithSpaces); - - expect(resolved).toContain('John Doe'); - expect(resolved).toContain('My Project'); - }); - - it('should handle Windows-style paths with spaces', () => { - const windowsPath = 'C:\\Users\\John Doe\\My Project\\ProjectSettings'; - - // path.normalize handles both styles - const normalized = path.normalize(windowsPath); - - expect(normalized).toContain('John Doe'); - }); - - it('should properly construct WebSocket URL', () => { - // WebSocket URLs don't need special encoding for host/port - const host = 'localhost'; - const port = 8090; - const wsUrl = `ws://${host}:${port}/McpUnity`; - - expect(wsUrl).toBe('ws://localhost:8090/McpUnity'); - }); - - it('should handle path.join with spaces', () => { - const basePath = '/Users/John Doe/Projects'; - const subPath = 'My Unity Game'; - const fileName = 'settings.json'; - - const fullPath = path.join(basePath, subPath, fileName); - - expect(fullPath).toContain('John Doe'); - expect(fullPath).toContain('My Unity Game'); - expect(fullPath).toContain('settings.json'); - }); - - it('should handle path.resolve with relative paths containing spaces', () => { - const cwd = '/Users/Test User/Current Dir'; - const relativePath = '../Other Project/file.txt'; - - // path.resolve will work correctly with spaces - const resolved = path.resolve(cwd, relativePath); - - expect(resolved).toContain('Test User'); - }); -}); - -describe('Logger with path-related messages', () => { - it('should log messages containing paths with spaces', () => { - const logger = new Logger('Test', LogLevel.ERROR); - const pathWithSpaces = '/Users/John Doe/My Project/file.txt'; - - // Logger should handle any string including paths with spaces - // This is a smoke test to ensure no exceptions are thrown - expect(() => { - logger.error(`Failed to read file: ${pathWithSpaces}`); - }).not.toThrow(); - }); -}); - -describe('Request timeout handling', () => { - beforeEach(() => { - jest.useFakeTimers(); - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('rejects timed out requests without forcing a reconnect', async () => { - const logger = new Logger('Test', LogLevel.ERROR); - const unity = new McpUnity(logger, { queueingEnabled: false }); - const connection = { - isConnected: true, - isConnecting: false, - connectionState: ConnectionState.Connected, - send: jest.fn(), - connect: jest.fn(), - disconnect: jest.fn(), - removeAllListeners: jest.fn(), - forceReconnect: jest.fn(), - getStats: jest.fn(() => ({ - state: ConnectionState.Connected, - reconnectAttempt: 0, - timeSinceLastPong: 0 - })) - }; - - (unity as any).connection = connection; - - const request = { - id: 'request-timeout', - method: 'run_tests', - params: { mode: 'edit' } - }; - - const promise = unity.sendRequest(request, { timeout: 50 }); - const timeoutResult = expect(promise).rejects.toMatchObject({ - type: ErrorType.TIMEOUT, - message: 'Request timed out' - }); - - expect(connection.send).toHaveBeenCalledWith(JSON.stringify(request)); - - await jest.advanceTimersByTimeAsync(50); - await timeoutResult; - - expect(connection.forceReconnect).not.toHaveBeenCalled(); - expect(unity.getConnectionStats().pendingRequests).toBe(0); - expect(unity.connectionState).toBe(ConnectionState.Connected); - - await unity.stop(); - }); -}); - -describe('Unity request/response diagnostics', () => { - const createMockLogger = () => ({ - debug: jest.fn(), - info: jest.fn(), - warn: jest.fn(), - error: jest.fn() - }); - - it('logs request id and connection state when sending to Unity', async () => { - const logger = createMockLogger(); - const unity = new McpUnity(logger as any, { queueingEnabled: false }); - const connection = { - isConnected: true, - isConnecting: false, - connectionState: ConnectionState.Connected, - send: jest.fn(), - connect: jest.fn(), - disconnect: jest.fn(), - removeAllListeners: jest.fn(), - forceReconnect: jest.fn(), - getStats: jest.fn(() => ({ - state: ConnectionState.Connected, - reconnectAttempt: 0, - timeSinceLastPong: 0 - })) - }; - - (unity as any).connection = connection; - - const promise = unity.sendRequest({ - id: 'request-diagnostics', - method: 'get_scene_info', - params: {} - }); - - (unity as any).handleMessage(JSON.stringify({ - id: 'request-diagnostics', - result: { success: true } - })); - - await expect(promise).resolves.toEqual({ success: true }); - expect(logger.info).toHaveBeenCalledWith( - 'Sending Unity request request-diagnostics (get_scene_info) while connection state is connected; pending requests before send: 0' - ); - expect(logger.info).toHaveBeenCalledWith( - 'Received Unity response for request request-diagnostics; pending requests before match: 1' - ); - - await unity.stop(); - }); - - it('logs ignored Unity responses without a matching pending request', async () => { - const logger = createMockLogger(); - const unity = new McpUnity(logger as any, { queueingEnabled: false }); - - (unity as any).handleMessage(JSON.stringify({ - id: 'unknown-request', - result: { success: true } - })); - - expect(logger.warn).toHaveBeenCalledWith( - 'Ignoring Unity response for unknown request unknown-request; pending requests: 0' - ); - - await unity.stop(); - }); -}); - -describe('Transform schema compatibility', () => { - const mockSendRequest = jest.fn(); - const mockMcpUnity = { sendRequest: mockSendRequest }; - const mockLogger = { - info: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn() - }; - const mockServerTool = jest.fn(); - const mockServer = { tool: mockServerTool }; - - function collectLocalPropertyRefs(node: unknown, refs: string[] = []): string[] { - if (Array.isArray(node)) { - for (const item of node) { - collectLocalPropertyRefs(item, refs); - } - return refs; - } - - if (!node || typeof node !== 'object') { - return refs; - } - - for (const [key, value] of Object.entries(node)) { - if (key === '$ref' && typeof value === 'string' && value.startsWith('#/properties/')) { - refs.push(value); - } - collectLocalPropertyRefs(value, refs); - } - - return refs; - } - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('registers transform tools', () => { - registerTransformTools(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledTimes(4); - expect(mockServerTool).toHaveBeenCalledWith('move_gameobject', expect.any(String), expect.any(Object), expect.any(Function)); - expect(mockServerTool).toHaveBeenCalledWith('rotate_gameobject', expect.any(String), expect.any(Object), expect.any(Function)); - expect(mockServerTool).toHaveBeenCalledWith('scale_gameobject', expect.any(String), expect.any(Object), expect.any(Function)); - expect(mockServerTool).toHaveBeenCalledWith('set_transform', expect.any(String), expect.any(Object), expect.any(Function)); - }); - - it('does not emit local #/properties refs for transform tool schemas', () => { - registerTransformTools(mockServer as any, mockMcpUnity as any, mockLogger as any); - - for (const call of mockServerTool.mock.calls) { - const paramsShape = call[2]; - const schemaJson = zodToJsonSchema(z.object(paramsShape), { strictUnions: true }); - const refs = collectLocalPropertyRefs(schemaJson); - - expect(refs).toEqual([]); - } - }); -}); diff --git a/Server~/src/__tests__/officialUnityMcpClient.test.ts b/Server~/src/__tests__/officialUnityMcpClient.test.ts new file mode 100644 index 00000000..593a5602 --- /dev/null +++ b/Server~/src/__tests__/officialUnityMcpClient.test.ts @@ -0,0 +1,599 @@ +import { spawn } from 'node:child_process'; +import { once } from 'node:events'; +import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; +import { jest } from '@jest/globals'; +import { + createOfficialUnitySessionStart, + OfficialUnityMcpClient, + type OfficialUnitySession, + type OfficialUnitySessionFactory, + type OfficialUnitySessionStart, +} from '../unity/officialUnityMcpClient.js'; + +function deferred() { + let resolve!: (value: T) => void; + const promise = new Promise((resolver) => { + resolve = resolver; + }); + return { promise, resolve }; +} + +async function completesWithin( + operation: Promise, + timeoutMs: number, +): Promise { + let timeout: NodeJS.Timeout | undefined; + try { + return await Promise.race([ + operation, + new Promise<'timed-out'>((resolve) => { + timeout = setTimeout(() => resolve('timed-out'), timeoutMs); + }), + ]); + } finally { + if (timeout) clearTimeout(timeout); + } +} + +function session( + callTool: OfficialUnitySession['callTool'] = async () => ({ + content: [{ type: 'text', text: '{"ok":true}' }], + }), +): OfficialUnitySession { + return { callTool }; +} + +function sessionStart( + ready: Promise | OfficialUnitySession, + close: jest.Mock = jest.fn(async () => undefined), +): OfficialUnitySessionStart & { close: jest.Mock } { + return { ready: Promise.resolve(ready), close }; +} + +function sdkTransport(close: () => Promise): Transport { + return { + start: async () => undefined, + send: async () => undefined, + close, + }; +} + +describe('OfficialUnityMcpClient', () => { + test('lazily starts exactly one official MCP process for concurrent first reads', async () => { + const gate = deferred(); + const start = sessionStart(gate.promise); + const factory: jest.MockedFunction = jest.fn( + () => start, + ); + const client = new OfficialUnityMcpClient({ + cliPath: '/opt/unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + + expect(factory).not.toHaveBeenCalled(); + + const first = client.readTool('get_console_logs', { limit: 10 }); + const second = client.readTool('package_list', { scope: 'installed' }); + expect(factory).toHaveBeenCalledTimes(1); + expect(factory).toHaveBeenCalledWith({ + cliPath: '/opt/unity', + projectPath: '/projects/game', + }); + + gate.resolve(session()); + await expect(Promise.all([first, second])).resolves.toHaveLength(2); + expect(factory).toHaveBeenCalledTimes(1); + expect(client.state).toBe('connected'); + }); + + test('reconnects once and retries the same read-only call exactly once', async () => { + const firstSession = session(jest.fn(async () => { + throw new Error('transport closed'); + })); + const secondCall = jest.fn(async () => ({ + content: [{ type: 'text' as const, text: '{"recovered":true}' }], + })); + const secondSession = session(secondCall); + const firstStart = sessionStart(firstSession); + const secondStart = sessionStart(secondSession); + const factory = jest + .fn, Parameters>() + .mockReturnValueOnce(firstStart) + .mockReturnValueOnce(secondStart); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + + await expect(client.readTool('list_tests', { mode: 'editor' })).resolves.toEqual({ + content: [{ type: 'text', text: '{"recovered":true}' }], + }); + expect(factory).toHaveBeenCalledTimes(2); + expect(firstSession.callTool).toHaveBeenCalledTimes(1); + expect(secondCall).toHaveBeenCalledTimes(1); + expect(secondCall).toHaveBeenCalledWith('list_tests', { mode: 'editor' }); + expect(firstStart.close).toHaveBeenCalledTimes(1); + }); + + test('does not retry a second failed read', async () => { + const factory = jest + .fn, Parameters>() + .mockImplementation(() => + sessionStart( + session(async () => { + throw new Error('Connection closed'); + }), + ), + ); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + + await expect(client.readTool('package_list', {})).rejects.toThrow('Connection closed'); + expect(factory).toHaveBeenCalledTimes(2); + }); + + test('does not reconnect for a non-transport command error', async () => { + const activeSession = session(async () => { + throw new Error('Method not found: inspect_gameobject'); + }); + const activeStart = sessionStart(activeSession); + const factory = jest.fn(() => activeStart); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + + await expect(client.readTool('inspect_gameobject', {})).rejects.toThrow( + 'Method not found', + ); + expect(factory).toHaveBeenCalledTimes(1); + expect(activeStart.close).not.toHaveBeenCalled(); + }); + + test('closes the active child/client and prevents later startup', async () => { + const activeSession = session(); + const activeStart = sessionStart(activeSession); + const factory = jest.fn(() => activeStart); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + await client.readTool('get_console_logs', {}); + + await client.close(); + await client.close(); + + expect(activeStart.close).toHaveBeenCalledTimes(1); + expect(client.state).toBe('closed'); + await expect(client.readTool('get_console_logs', {})).rejects.toThrow('closed'); + expect(factory).toHaveBeenCalledTimes(1); + }); + + test('close rejects pending reads promptly but waits for actual teardown', async () => { + const gate = deferred(); + const teardown = deferred(); + const start = sessionStart(gate.promise, jest.fn(() => teardown.promise)); + const factory = jest.fn(() => start); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + const read = client.readTool('get_console_logs', {}); + let closeSettled = false; + const close = client.close().then(() => { + closeSettled = true; + }); + + await expect( + Promise.race([ + read, + new Promise((_, reject) => + setTimeout(() => reject(new Error('read did not stop')), 100), + ), + ]), + ).rejects.toThrow('closed'); + await new Promise((resolve) => setImmediate(resolve)); + expect(closeSettled).toBe(false); + expect(start.close).toHaveBeenCalledTimes(1); + + teardown.resolve(); + await close; + expect(closeSettled).toBe(true); + + gate.resolve(session()); + await new Promise((resolve) => setImmediate(resolve)); + expect(start.close).toHaveBeenCalledTimes(1); + }); + + test('close does not resolve before an active child teardown resolves', async () => { + const teardown = deferred(); + const start = sessionStart(session(), jest.fn(() => teardown.promise)); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: () => start, + }); + await client.readTool('get_console_logs', {}); + + let closeSettled = false; + const close = client.close().then(() => { + closeSettled = true; + }); + await new Promise((resolve) => setImmediate(resolve)); + + expect(closeSettled).toBe(false); + expect(start.close).toHaveBeenCalledTimes(1); + + teardown.resolve(); + await close; + await client.close(); + expect(start.close).toHaveBeenCalledTimes(1); + }); + + test('shutdown interrupts a read whose tool call never resolves', async () => { + const callGate = deferred>>(); + const start = sessionStart(session(() => callGate.promise)); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: () => start, + }); + const read = client.readTool('get_console_logs', {}); + await new Promise((resolve) => setImmediate(resolve)); + + await client.close(); + + await expect( + Promise.race([ + read, + new Promise((_, reject) => + setTimeout(() => reject(new Error('read did not stop')), 100), + ), + ]), + ).rejects.toThrow('closed'); + expect(start.close).toHaveBeenCalledTimes(1); + }); + + test('close wins a transport-failure retry race without spawning again', async () => { + let client!: OfficialUnityMcpClient; + const firstStart = sessionStart( + session(async () => { + throw new Error('Connection closed'); + }), + jest.fn(async () => { + void client.close(); + }), + ); + const factory = jest.fn(() => firstStart); + client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + + await expect(client.readTool('package_list', {})).rejects.toThrow('closed'); + expect(factory).toHaveBeenCalledTimes(1); + expect(firstStart.close).toHaveBeenCalledTimes(1); + }); + + test('shutdown closes a pending retry start exactly once', async () => { + const retryGate = deferred(); + const firstStart = sessionStart( + session(async () => { + throw new Error('Connection closed'); + }), + ); + const retryStart = sessionStart(retryGate.promise); + const factory = jest + .fn, Parameters>() + .mockReturnValueOnce(firstStart) + .mockReturnValueOnce(retryStart); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + const read = client.readTool('package_list', {}); + while (factory.mock.calls.length < 2) { + await new Promise((resolve) => setImmediate(resolve)); + } + + await client.close(); + await expect(read).rejects.toThrow('closed'); + expect(firstStart.close).toHaveBeenCalledTimes(1); + expect(retryStart.close).toHaveBeenCalledTimes(1); + + retryGate.resolve(session()); + await new Promise((resolve) => setImmediate(resolve)); + expect(retryStart.close).toHaveBeenCalledTimes(1); + }); + + test('preserves a healthy reconnected session after a command-level retry error', async () => { + const firstStart = sessionStart( + session(async () => { + throw new Error('Connection closed'); + }), + ); + const retryCall = jest + .fn, Parameters>() + .mockRejectedValueOnce(new Error('Method not found: inspect_gameobject')) + .mockResolvedValueOnce({ + content: [{ type: 'text', text: '{"ok":true}' }], + }); + const retryStart = sessionStart(session(retryCall)); + const factory = jest + .fn, Parameters>() + .mockReturnValueOnce(firstStart) + .mockReturnValueOnce(retryStart); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + + await expect(client.readTool('inspect_gameobject', {})).rejects.toThrow( + 'Method not found', + ); + await expect(client.readTool('get_console_logs', {})).resolves.toMatchObject({ + content: [{ type: 'text', text: '{"ok":true}' }], + }); + expect(factory).toHaveBeenCalledTimes(2); + expect(retryStart.close).not.toHaveBeenCalled(); + }); + + test('completes old child teardown before invoking the replacement factory', async () => { + const teardown = deferred(); + const firstStart = sessionStart( + session(async () => { + throw new Error('Connection closed'); + }), + jest.fn(() => teardown.promise), + ); + const secondStart = sessionStart(session()); + const factory = jest + .fn, Parameters>() + .mockReturnValueOnce(firstStart) + .mockReturnValueOnce(secondStart); + const client = new OfficialUnityMcpClient({ + cliPath: 'unity', + projectPath: '/projects/game', + sessionFactory: factory, + }); + + const read = client.readTool('get_console_logs', {}); + await new Promise((resolve) => setImmediate(resolve)); + + expect(firstStart.close).toHaveBeenCalledTimes(1); + expect(factory).toHaveBeenCalledTimes(1); + + teardown.resolve(); + await expect(read).resolves.toMatchObject({ + content: [{ type: 'text', text: '{"ok":true}' }], + }); + expect(factory).toHaveBeenCalledTimes(2); + }); +}); + +describe('official Unity SDK session ownership', () => { + test('aborts pending SDK initialization and awaits one memoized client teardown', async () => { + let connectOptions: + | { signal?: AbortSignal; timeout?: number; maxTotalTimeout?: number } + | undefined; + const clientClose = deferred(); + const client = { + connect: jest.fn( + async ( + _transport: unknown, + options: { + signal?: AbortSignal; + timeout?: number; + maxTotalTimeout?: number; + }, + ) => { + connectOptions = options; + await new Promise((_resolve, reject) => { + options.signal?.addEventListener( + 'abort', + () => reject(new Error('initialize aborted')), + { once: true }, + ); + }); + }, + ), + callTool: jest.fn(), + close: jest.fn(() => clientClose.promise), + }; + const transport = { + pid: null, + start: async () => undefined, + send: async () => undefined, + close: jest.fn(async () => undefined), + }; + const start = createOfficialUnitySessionStart( + { cliPath: '/opt/unity', projectPath: '/projects/game' }, + { + createClient: () => client, + createTransport: () => transport, + }, + ); + const ready = start.ready.catch((error: unknown) => error); + + const firstClose = start.close(); + const secondClose = start.close(); + expect(firstClose).toBe(secondClose); + expect(connectOptions).toMatchObject({ + timeout: 10_000, + maxTotalTimeout: 10_000, + }); + expect(connectOptions?.signal?.aborted).toBe(true); + expect(client.close).toHaveBeenCalledTimes(1); + + let closeSettled = false; + void firstClose.then(() => { + closeSettled = true; + }); + await new Promise((resolve) => setImmediate(resolve)); + expect(closeSettled).toBe(false); + + clientClose.resolve(); + await firstClose; + await expect(ready).resolves.toBeInstanceOf(Error); + expect(client.close).toHaveBeenCalledTimes(1); + expect(transport.close).not.toHaveBeenCalled(); + }); + + test('forces direct bounded transport cleanup when SDK client close stalls', async () => { + const transportClose = deferred(); + const client = { + connect: jest.fn(async () => undefined), + callTool: jest.fn(), + close: jest.fn(() => new Promise(() => undefined)), + }; + const transport = { + get pid(): never { + throw new Error('teardown must not read a raw PID'); + }, + start: async () => undefined, + send: async () => undefined, + close: jest.fn(() => transportClose.promise), + }; + const start = createOfficialUnitySessionStart( + { cliPath: '/opt/unity', projectPath: '/projects/game' }, + { + createClient: () => client, + createTransport: () => transport, + sdkCloseGraceMs: 10, + transportCloseTimeoutMs: 100, + }, + ); + await start.ready; + + let closeSettled = false; + const close = start.close().then(() => { + closeSettled = true; + }); + await new Promise((resolve) => setTimeout(resolve, 25)); + + expect(client.close).toHaveBeenCalledTimes(1); + expect(transport.close).toHaveBeenCalledTimes(1); + expect(closeSettled).toBe(false); + + transportClose.resolve(); + await close; + expect(closeSettled).toBe(true); + }); + + test('reports an actionable error when owned transport cleanup rejects', async () => { + const start = createOfficialUnitySessionStart( + { cliPath: '/opt/unity', projectPath: '/projects/game' }, + { + createClient: () => ({ + connect: jest.fn(async () => undefined), + callTool: jest.fn(), + close: jest.fn(async () => { + throw new Error('SDK close failed'); + }), + }), + createTransport: () => + sdkTransport(jest.fn(async () => { + throw new Error('owned child cleanup failed'); + })), + sdkCloseGraceMs: 10, + transportCloseTimeoutMs: 10, + }, + ); + await start.ready; + + await expect(start.close()).rejects.toThrow( + 'Unity CLI transport teardown failed: owned child cleanup failed', + ); + }); + + test('reports an actionable error when owned transport cleanup times out', async () => { + const start = createOfficialUnitySessionStart( + { cliPath: '/opt/unity', projectPath: '/projects/game' }, + { + createClient: () => ({ + connect: jest.fn(async () => undefined), + callTool: jest.fn(), + close: jest.fn(() => new Promise(() => undefined)), + }), + createTransport: () => + sdkTransport(jest.fn(() => new Promise(() => undefined))), + sdkCloseGraceMs: 5, + transportCloseTimeoutMs: 10, + }, + ); + await start.ready; + + await expect(start.close()).rejects.toThrow( + 'Unity CLI transport teardown timed out after 10ms', + ); + }); + + const posixTest = process.platform === 'win32' ? test.skip : test; + posixTest( + 'awaits transport-owned cleanup of a real stubborn child close event', + async () => { + const child = spawn( + process.execPath, + [ + '-e', + "process.on('SIGTERM', () => {}); process.stdout.write('ready\\n'); setInterval(() => {}, 1000);", + ], + { stdio: ['ignore', 'pipe', 'ignore'] }, + ); + await once(child, 'spawn'); + await once(child.stdout!, 'data'); + const childClosed = once(child, 'close'); + const transportClose = jest.fn(async () => { + child.kill('SIGTERM'); + const force = setTimeout(() => child.kill('SIGKILL'), 50); + try { + await childClosed; + } finally { + clearTimeout(force); + } + }); + const client = { + connect: jest.fn(async () => undefined), + callTool: jest.fn(), + close: jest.fn(() => new Promise(() => undefined)), + }; + const start = createOfficialUnitySessionStart( + { cliPath: '/unused/unity', projectPath: '/projects/game' }, + { + createClient: () => client, + createTransport: () => sdkTransport(transportClose), + sdkCloseGraceMs: 10, + transportCloseTimeoutMs: 1000, + }, + ); + await start.ready; + try { + const closeStartedAt = Date.now(); + await expect( + completesWithin(start.close().then(() => 'closed'), 1500), + ).resolves.toBe('closed'); + expect(Date.now() - closeStartedAt).toBeGreaterThanOrEqual(40); + expect(transportClose).toHaveBeenCalledTimes(1); + expect(child.exitCode !== null || child.signalCode !== null).toBe(true); + } finally { + await start.close(); + if (child.exitCode === null && child.signalCode === null) { + child.kill('SIGKILL'); + await childClosed; + } + } + }, + 3000, + ); +}); diff --git a/Server~/src/__tests__/ownedStdioClientTransport.test.ts b/Server~/src/__tests__/ownedStdioClientTransport.test.ts new file mode 100644 index 00000000..cd2686a3 --- /dev/null +++ b/Server~/src/__tests__/ownedStdioClientTransport.test.ts @@ -0,0 +1,177 @@ +import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; +import type { + Transport, + TransportSendOptions, +} from '@modelcontextprotocol/sdk/shared/transport.js'; +import type { + JSONRPCMessage, + MessageExtraInfo, +} from '@modelcontextprotocol/sdk/types.js'; +import { jest } from '@jest/globals'; +import { OwnedStdioClientTransport } from '../unity/officialUnityMcpClient.js'; + +function deferred() { + let resolve!: (value: T) => void; + const promise = new Promise((resolver) => { + resolve = resolver; + }); + return { promise, resolve }; +} + +class FakeTransport implements Transport { + onclose?: () => void; + onerror?: (error: Error) => void; + onmessage?: ( + message: T, + extra?: MessageExtraInfo, + ) => void; + sessionId = 'fake-session'; + readonly start = jest.fn(async () => undefined); + readonly send = jest.fn( + async (_message: JSONRPCMessage, _options?: TransportSendOptions) => + undefined, + ); + readonly close = jest.fn(async () => undefined); + readonly setProtocolVersion = jest.fn((_version: string) => undefined); +} + +describe('OwnedStdioClientTransport', () => { + test('delegates the Transport contract and forwards callbacks', async () => { + const underlying = new FakeTransport(); + const transport = new OwnedStdioClientTransport(underlying, 100); + const onclose = jest.fn(); + const onerror = jest.fn(); + const onmessage = jest.fn(); + transport.onclose = onclose; + transport.onerror = onerror; + transport.onmessage = onmessage; + const message = { + jsonrpc: '2.0' as const, + method: 'notifications/initialized', + }; + const options = { relatedRequestId: 7 }; + + await transport.start(); + await transport.send(message, options); + transport.setProtocolVersion?.('2025-06-18'); + const error = new Error('stderr warning'); + underlying.onmessage?.(message); + underlying.onerror?.(error); + underlying.onclose?.(); + transport.sessionId = 'updated-session'; + + expect(underlying.start).toHaveBeenCalledTimes(1); + expect(underlying.send).toHaveBeenCalledWith(message, options); + expect(underlying.setProtocolVersion).toHaveBeenCalledWith('2025-06-18'); + expect(underlying.sessionId).toBe('updated-session'); + expect(transport.sessionId).toBe('updated-session'); + expect(onmessage).toHaveBeenCalledWith(message, undefined); + expect(onerror).toHaveBeenCalledWith(error); + expect(onclose).toHaveBeenCalledTimes(1); + }); + + test('memoizes close and waits for the actual child close callback', async () => { + const underlying = new FakeTransport(); + const transport = new OwnedStdioClientTransport(underlying, 100); + await transport.start(); + + const firstClose = transport.close(); + const secondClose = transport.close(); + expect(firstClose).toBe(secondClose); + + let settled = false; + void firstClose.then(() => { + settled = true; + }); + await new Promise((resolve) => setImmediate(resolve)); + expect(underlying.close).toHaveBeenCalledTimes(1); + expect(settled).toBe(false); + + underlying.onclose?.(); + await firstClose; + expect(settled).toBe(true); + }); + + test('reports an actionable error if a started child never reports close', async () => { + const underlying = new FakeTransport(); + const transport = new OwnedStdioClientTransport(underlying, 10); + await transport.start(); + + await expect(transport.close()).rejects.toThrow( + 'Unity CLI child did not report process closure within 10ms', + ); + }); + + test('does not await a child close event when start failed', async () => { + const underlying = new FakeTransport(); + underlying.start.mockRejectedValueOnce(new Error('spawn failed')); + const transport = new OwnedStdioClientTransport(underlying, 10); + + await expect(transport.start()).rejects.toThrow('spawn failed'); + await expect(transport.close()).resolves.toBeUndefined(); + expect(underlying.close).toHaveBeenCalledTimes(1); + }); + + test('spontaneous close resolves the barrier and forwards onclose exactly once', async () => { + const underlying = new FakeTransport(); + const transport = new OwnedStdioClientTransport(underlying, 100); + const onclose = jest.fn(); + transport.onclose = onclose; + await transport.start(); + + underlying.onclose?.(); + underlying.onclose?.(); + + await expect(transport.close()).resolves.toBeUndefined(); + expect(onclose).toHaveBeenCalledTimes(1); + expect(underlying.close).toHaveBeenCalledTimes(1); + }); + + const posixTest = process.platform === 'win32' ? test.skip : test; + posixTest( + 'waits for a real pinned Stdio child close event and leaves no child handle', + async () => { + const underlying = new StdioClientTransport({ + command: process.execPath, + args: [ + '-e', + [ + "process.on('SIGTERM', () => {});", + "process.stderr.write('ready\\n');", + 'setInterval(() => {}, 1000);', + ].join(''), + ], + stderr: 'pipe', + }); + const transport = new OwnedStdioClientTransport(underlying, 1_000); + const childClosed = deferred(); + let closeEventObserved = false; + transport.onclose = () => { + closeEventObserved = true; + childClosed.resolve(); + }; + + const ready = new Promise((resolve) => { + underlying.stderr?.once('data', () => resolve()); + }); + await transport.start(); + await ready; + + try { + let closeResolvedBeforeEvent = false; + const close = transport.close().then(() => { + closeResolvedBeforeEvent = !closeEventObserved; + }); + await close; + await childClosed.promise; + + expect(closeResolvedBeforeEvent).toBe(false); + expect(closeEventObserved).toBe(true); + } finally { + await Promise.allSettled([transport.close()]); + await childClosed.promise; + } + }, + 7_000, + ); +}); diff --git a/Server~/src/__tests__/playModeTools.test.ts b/Server~/src/__tests__/playModeTools.test.ts deleted file mode 100644 index fef521ab..00000000 --- a/Server~/src/__tests__/playModeTools.test.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { jest, describe, it, expect, beforeEach } from '@jest/globals'; -import { ErrorType, McpUnityError } from '../utils/errors.js'; -import { registerGetPlayModeStatusTool } from '../tools/getPlayModeStatusTool.js'; -import { registerSetPlayModeStatusTool } from '../tools/setPlayModeStatusTool.js'; - -const mockSendRequest = jest.fn(); -const mockMcpUnity = { - sendRequest: mockSendRequest -}; - -const mockLogger = { - info: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn() -}; - -const mockServerTool = jest.fn(); -const mockServer = { - tool: mockServerTool -}; - -describe('Play Mode Tools', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('get_play_mode_status', () => { - it('registers the tool with the MCP server', () => { - registerGetPlayModeStatusTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledWith( - 'get_play_mode_status', - expect.any(String), - expect.any(Object), - expect.any(Function) - ); - }); - - it('returns structured play mode state from Unity', async () => { - registerGetPlayModeStatusTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - const handler = mockServerTool.mock.calls[0][3] as (params: any) => Promise; - - mockSendRequest.mockResolvedValue({ - success: true, - type: 'text', - isPlaying: true, - isPaused: false - }); - - const result = await handler({}); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'get_play_mode_status', - params: {} - }); - expect(result.content[0].text).toBe('Play mode'); - expect(result.data).toEqual({ isPlaying: true, isPaused: false }); - }); - - it('throws a tool execution error when Unity reports failure', async () => { - registerGetPlayModeStatusTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - const handler = mockServerTool.mock.calls[0][3] as (params: any) => Promise; - - mockSendRequest.mockResolvedValue({ - success: false, - message: 'Unity unavailable' - }); - - await expect(handler({})).rejects.toMatchObject({ - type: ErrorType.TOOL_EXECUTION, - message: 'Unity unavailable' - } as Partial); - }); - }); - - describe('set_play_mode_status', () => { - it('registers the tool with the MCP server', () => { - registerSetPlayModeStatusTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - - expect(mockServerTool).toHaveBeenCalledWith( - 'set_play_mode_status', - expect.any(String), - expect.objectContaining({ action: expect.any(Object) }), - expect.any(Function) - ); - }); - - it('sends validated play mode actions to Unity', async () => { - registerSetPlayModeStatusTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - const handler = mockServerTool.mock.calls[0][3] as (params: any) => Promise; - - mockSendRequest.mockResolvedValue({ - success: true, - isPlaying: false, - isPaused: false, - action: 'stop' - }); - - const result = await handler({ action: 'stop' }); - - expect(mockSendRequest).toHaveBeenCalledWith({ - method: 'set_play_mode_status', - params: { action: 'stop' } - }); - expect(result.content[0].text).toContain("Play mode action 'stop'"); - expect(result.data).toEqual({ - action: 'stop', - isPlaying: false, - isPaused: false - }); - }); - - it('rejects invalid actions before calling Unity', async () => { - registerSetPlayModeStatusTool(mockServer as any, mockMcpUnity as any, mockLogger as any); - const handler = mockServerTool.mock.calls[0][3] as (params: any) => Promise; - - await expect(handler({ action: 'restart' })).rejects.toThrow(); - expect(mockSendRequest).not.toHaveBeenCalled(); - }); - }); -}); diff --git a/Server~/src/__tests__/releaseContract.test.ts b/Server~/src/__tests__/releaseContract.test.ts new file mode 100644 index 00000000..37d94620 --- /dev/null +++ b/Server~/src/__tests__/releaseContract.test.ts @@ -0,0 +1,914 @@ +import { execFileSync } from 'node:child_process'; +import { createHash } from 'node:crypto'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const serverRoot = path.resolve( + path.dirname(fileURLToPath(import.meta.url)), + '..', + '..', +); +const repositoryRoot = path.resolve(serverRoot, '..'); +const legacySnapshotPath = path.join( + serverRoot, + 'src', + '__tests__', + 'fixtures', + 'legacy-1.4.0-inventory.json', +); +const legacySnapshotSha256 = + 'd6c1c9aa87f69febf8034e290e57d60f668f8abe9b540a02a27ac375fbaa1227'; + +const readRepositoryFile = (relativePath: string): string => + fs.readFileSync(path.join(repositoryRoot, relativePath), 'utf8'); + +const rootPackage = JSON.parse(readRepositoryFile('package.json')) as Record< + string, + unknown +>; +const companionPackage = JSON.parse( + readRepositoryFile('Server~/package.json'), +) as Record; +const readme = readRepositoryFile('README.md'); +const agents = readRepositoryFile('AGENTS.md'); + +const legacyTools = [ + 'add_asset_to_scene', + 'add_package', + 'assign_material', + 'batch_execute', + 'create_material', + 'create_prefab', + 'create_scene', + 'delete_gameobject', + 'delete_scene', + 'duplicate_gameobject', + 'execute_menu_item', + 'get_console_logs', + 'get_gameobject', + 'get_material_info', + 'get_play_mode_status', + 'get_scene_info', + 'get_scenes_hierarchy', + 'load_scene', + 'modify_material', + 'move_gameobject', + 'recompile_scripts', + 'reparent_gameobject', + 'rotate_gameobject', + 'run_tests', + 'save_scene', + 'scale_gameobject', + 'select_gameobject', + 'send_console_log', + 'set_play_mode_status', + 'set_transform', + 'show_unity_dashboard', + 'unload_scene', + 'update_component', + 'update_gameobject', +] as const; + +const legacyResources = [ + 'get_assets', + 'get_console_logs', + 'get_gameobject', + 'get_menu_items', + 'get_packages', + 'get_scenes_hierarchy', + 'get_tests', + 'unity_dashboard_app', + 'unity_dashboard_app_legacy', +] as const; + +const legacyUris = [ + 'ui://unity-dashboard', + 'unity://assets', + 'unity://gameobject/{idOrName}', + 'unity://logs/{logType}?offset={offset}&limit={limit}&includeStackTrace={includeStackTrace}', + 'unity://menu-items', + 'unity://packages', + 'unity://scenes_hierarchy', + 'unity://tests/{testMode}', + 'unity://ui/dashboard', +] as const; + +const legacyPrompts = [ + 'gameobject_handling_strategy', + 'unity_dashboard', +] as const; + +const legacySettings = [ + 'Port', + 'RequestTimeoutSeconds', + 'AutoStartServer', + 'EnableInfoLogs', + 'NpmExecutablePath', + 'AllowRemoteConnections', +] as const; + +const legacyConcepts = [ + 'env:UNITY_HOST', + 'env:LOGGING', + 'env:LOGGING_FILE', + 'path:ProjectSettings/McpUnitySettings.json', + 'integration:Unity-driven npm install/build', + 'integration:automatic MCP-client configuration', + 'integration:PackedCache mutation', + 'integration:custom WebSocket endpoint/port', + 'integration:Docker deployment/Dockerfile/exposed ports', + 'integration:Smithery configuration', + 'integration:Node npm executable/bin/publication surface', + 'integration:MCP registry server.json', + 'integration:MCP registry mcpName/mcpname', + 'integration:Glama registry metadata', +] as const; + +const legacyEvidenceCounts: Record<(typeof legacyConcepts)[number], number> = { + 'env:UNITY_HOST': 1, + 'env:LOGGING': 1, + 'env:LOGGING_FILE': 1, + 'path:ProjectSettings/McpUnitySettings.json': 1, + 'integration:Unity-driven npm install/build': 2, + 'integration:automatic MCP-client configuration': 2, + 'integration:PackedCache mutation': 2, + 'integration:custom WebSocket endpoint/port': 2, + 'integration:Docker deployment/Dockerfile/exposed ports': 2, + 'integration:Smithery configuration': 1, + 'integration:Node npm executable/bin/publication surface': 3, + 'integration:MCP registry server.json': 1, + 'integration:MCP registry mcpName/mcpname': 2, + 'integration:Glama registry metadata': 1, +}; + +const extensionCommands = [ + 'assign_material', + 'duplicate_gameobject', + 'editor_step', + 'inspect_gameobject', + 'unload_scene', +] as const; + +const companionResources = [ + 'unity://logs{?severity,limit}', + 'unity://scenes-hierarchy{?path,max_nodes}', + 'unity://gameobject/{target}', + 'unity://packages{?include_indirect}', + 'unity://tests/{mode}', + 'ui://unity-dashboard', +] as const; + +const companionTools = ['show_unity_dashboard'] as const; +const companionPrompts = [ + 'gameobject_handling_strategy', + 'unity_dashboard', +] as const; + +interface LegacyEvidence { + path: string; + contains?: string; + absent?: boolean; +} + +interface LegacySnapshot { + schemaVersion: number; + sourceTag: string; + sourceCommit: string; + tools: string[]; + resources: string[]; + uris: string[]; + prompts: string[]; + settings: string[]; + integrations: Array<{ + id: string; + evidence: LegacyEvidence[]; + }>; +} + +describe('2.0 release contract', () => { + test('keeps release metadata synchronized and private', () => { + expect(rootPackage).toMatchObject({ + version: '2.0.0', + unity: '6000.0', + dependencies: { + 'com.unity.pipeline': '0.3.1-exp.1', + 'com.unity.test-framework': '1.3.3', + }, + }); + expect(companionPackage).toMatchObject({ + name: 'mcp-unity-companion', + version: '2.0.0', + private: true, + }); + for (const property of ['mcpName', 'mcpname']) { + expect(rootPackage).not.toHaveProperty(property); + expect(companionPackage).not.toHaveProperty(property); + } + for (const property of ['bin', 'files', 'publishConfig']) { + expect(companionPackage).not.toHaveProperty(property); + } + expect(fs.existsSync(path.join(repositoryRoot, 'server.json'))).toBe(false); + expect(fs.existsSync(path.join(repositoryRoot, 'glama.json'))).toBe(false); + + for (const relativePath of [ + 'Server~/package-lock.json', + 'Server~/src/companionServer.ts', + 'Server~/src/unity/officialUnityMcpClient.ts', + 'Server~/src/ui/unity-dashboard.html', + ]) { + expect(readRepositoryFile(relativePath)).toContain('2.0.0'); + } + }); + + test('ships an immutable 1.4.0 snapshot and validates it when the tag is available', () => { + expect(fs.existsSync(legacySnapshotPath)).toBe(true); + if (!fs.existsSync(legacySnapshotPath)) return; + + const snapshotBytes = fs.readFileSync(legacySnapshotPath); + expect(fixtureDigest(snapshotBytes)).toBe(legacySnapshotSha256); + const snapshot = JSON.parse(snapshotBytes.toString('utf8')) as LegacySnapshot; + expect(snapshot).toMatchObject({ + schemaVersion: 1, + sourceTag: '1.4.0', + sourceCommit: 'bbfb1c0681519ced5b357ce7cc3c1ee68c9dc64e', + }); + expect(snapshot.tools.sort()).toEqual([...legacyTools].sort()); + expect(snapshot.resources.sort()).toEqual([...legacyResources].sort()); + expect(snapshot.uris.sort()).toEqual([...legacyUris].sort()); + expect(snapshot.prompts.sort()).toEqual([...legacyPrompts].sort()); + expect(snapshot.settings.sort()).toEqual([...legacySettings].sort()); + expect(snapshot.integrations.map(({ id }) => id).sort()).toEqual( + [...legacyConcepts].sort(), + ); + for (const integration of snapshot.integrations) { + expect(integration.evidence).toHaveLength( + legacyEvidenceCounts[ + integration.id as (typeof legacyConcepts)[number] + ], + ); + for (const evidence of integration.evidence) { + expect(evidence.path).toEqual(expect.any(String)); + expect(evidence.path.length).toBeGreaterThan(0); + const evidenceKeys = Object.keys(evidence).sort(); + if (evidence.absent !== undefined) { + expect(evidenceKeys).toEqual(['absent', 'path']); + expect(evidence.absent).toBe(true); + } else { + expect(evidenceKeys).toEqual(['contains', 'path']); + expect(evidence.contains).toEqual(expect.any(String)); + expect(evidence.contains?.length).toBeGreaterThan(0); + } + } + } + + // Shallow clones and packaged UPM copies may not contain the tag object. + // In those environments the checked-in snapshot above remains mandatory + // and continues to drive every migration-table assertion. + if (!gitObjectExists('1.4.0^{commit}')) return; + + expect(git('rev-parse', '1.4.0^{commit}').trim()).toBe( + snapshot.sourceCommit, + ); + const files = registeredCatalogModules( + git('show', '1.4.0:Server~/src/index.ts'), + ); + const inventory = { + tools: new Set(), + resources: new Set(), + uris: new Set(), + prompts: new Set(), + }; + + for (const file of files) { + const source = git('show', `1.4.0:${file}`); + if (file.includes('/tools/')) { + collectMatches( + inventory.tools, + source, + /const\s+\w*(?:toolName|ToolName)\s*=\s*['"]([a-z0-9_]+)['"]/g, + ); + } else if (file.includes('/resources/')) { + collectMatches( + inventory.resources, + source, + /const\s+(?:resourceName|legacyResourceName)\s*=\s*['"]([a-z0-9_]+)['"]/g, + ); + collectMatches( + inventory.uris, + source, + /const\s+\w*(?:Uri|URI)\s*=\s*['"]((?:unity|ui):\/\/[^'"]+)['"]/g, + ); + } else if (file.includes('/prompts/')) { + collectMatches( + inventory.prompts, + source, + /server\.prompt\(\s*['"]([a-z0-9_]+)['"]/g, + ); + } + } + + const settingsSource = git( + 'show', + '1.4.0:Editor/UnityBridge/McpUnitySettings.cs', + ); + const discoveredSettings = new Set(); + collectMatches( + discoveredSettings, + settingsSource, + /public\s+(?:int|bool|string)\s+([A-Za-z0-9_]+)\s*=/g, + ); + + expect([...inventory.tools].sort()).toEqual([...legacyTools].sort()); + expect([...inventory.resources].sort()).toEqual([...legacyResources].sort()); + expect([...inventory.uris].sort()).toEqual([...legacyUris].sort()); + expect([...inventory.prompts].sort()).toEqual([...legacyPrompts].sort()); + expect([...discoveredSettings].sort()).toEqual([...legacySettings].sort()); + + for (const integration of snapshot.integrations) { + for (const evidence of integration.evidence) { + const exists = gitObjectExists(`1.4.0:${evidence.path}`); + expect(exists).toBe(!evidence.absent); + if (!evidence.absent && evidence.contains) { + expect(git('show', `1.4.0:${evidence.path}`)).toContain( + evidence.contains, + ); + } + } + } + }); + + test('normalizes fixture line endings without hiding content changes', () => { + const lf = '{\n "schemaVersion": 1,\n "value": "legacy"\n}\n'; + const crlf = lf.replace(/\n/g, '\r\n'); + const cr = lf.replace(/\n/g, '\r'); + const changed = lf.replace('"legacy"', '"changed"'); + + expect(fixtureDigest(crlf)).toBe(fixtureDigest(lf)); + expect(fixtureDigest(cr)).toBe(fixtureDigest(lf)); + expect(fixtureDigest(changed)).not.toBe(fixtureDigest(lf)); + }); + + test('maps every 1.4.0 catalog and configuration concept', () => { + for (const tool of legacyTools) expectMigrationRow(`tool:${tool}`); + for (const resource of legacyResources) { + expectMigrationRow(`resource:${resource}`); + } + for (const uri of legacyUris) expectMigrationRow(`uri:${uri}`); + for (const prompt of legacyPrompts) expectMigrationRow(`prompt:${prompt}`); + for (const setting of legacySettings) { + expectMigrationRow(`config:${setting}`); + } + for (const concept of legacyConcepts) { + expectMigrationRow(`concept:${concept}`); + } + }); + + test('advertises only the 2.0 extension and companion catalogs', () => { + const extensionSection = markdownSection( + readme, + '## MCP Unity extension commands', + '## Optional MCP companion', + ); + const companionSection = markdownSection( + readme, + '## Optional MCP companion', + '## Migration from 1.4.0', + ); + + expect(markdownDashCatalog(extensionSection)).toEqual( + sorted(extensionCommands), + ); + expect(markdownLabeledCatalog(companionSection, 'Tool')).toEqual( + sorted(companionTools), + ); + expect(markdownNestedCatalog(companionSection, 'Resources')).toEqual( + sorted(companionResources), + ); + expect(markdownNestedCatalog(companionSection, 'Prompts')).toEqual( + sorted(companionPrompts), + ); + }); + + test('keeps runtime and documented public catalogs exact', () => { + const commandNames = new Set(); + for (const file of walkFiles(path.join(repositoryRoot, 'Editor', 'Commands'))) { + if (!file.endsWith('.cs')) continue; + collectMatches( + commandNames, + fs.readFileSync(file, 'utf8'), + /\[CliCommand\(\s*"([a-z0-9_]+)"/g, + ); + } + expect(sorted(commandNames)).toEqual(sorted(extensionCommands)); + + const companionSource = readRepositoryFile('Server~/src/companionServer.ts'); + const dashboardSource = readRepositoryFile( + 'Server~/src/resources/dashboardResource.ts', + ); + const promptSource = readRepositoryFile( + 'Server~/src/prompts/companionPrompts.ts', + ); + const runtimeTools = new Set(); + const runtimeResources = new Set(); + const runtimePrompts = new Set(); + collectMatches( + runtimeTools, + companionSource, + /registerAppTool\(\s*server,\s*'([^']+)'/g, + ); + collectMatches( + runtimeResources, + companionSource, + /template:\s*'((?:unity|ui):\/\/[^']+)'/g, + ); + collectMatches( + runtimeResources, + dashboardSource, + /DASHBOARD_URI\s*=\s*'((?:unity|ui):\/\/[^']+)'/g, + ); + collectMatches( + runtimePrompts, + promptSource, + /server\.registerPrompt\(\s*'([^']+)'/g, + ); + + expect(sorted(runtimeTools)).toEqual(sorted(companionTools)); + expect(sorted(runtimeResources)).toEqual(sorted(companionResources)); + expect(sorted(runtimePrompts)).toEqual(sorted(companionPrompts)); + + const agentsCatalog = markdownSection( + agents, + '## Public catalogs', + '## Adding or changing an extension command', + ); + const [agentsExtensions, agentsCompanion = ''] = agentsCatalog.split( + 'The optional companion exposes only:', + ); + expect(markdownPlainCatalog(agentsExtensions)).toEqual( + sorted(extensionCommands), + ); + expect(markdownLowercaseLabeledCatalog(agentsCompanion, 'tool')).toEqual( + sorted(companionTools), + ); + expect(markdownNestedCatalog(agentsCompanion, 'resources')).toEqual( + sorted(companionResources), + ); + expect(markdownNestedCatalog(agentsCompanion, 'prompts')).toEqual( + sorted(companionPrompts), + ); + }); + + test('keeps README and AGENTS aligned on pins and architecture', () => { + for (const document of [readme, agents]) { + expect(document).toContain('2.0.0'); + expect(document).toContain('com.unity.pipeline'); + expect(document).toContain('0.3.1-exp.1'); + expect(document).toContain('Unity CLI 1.0.0-beta.2'); + expect(document).toContain('Unity 6000.0'); + expect(document).toContain('Unity 6000.3'); + expect(document).toContain('Unity 6000.5'); + expect(document).toContain('Window > MCP Unity > Setup'); + expect(document).toContain('unity mcp --project-path'); + expect(document).toContain('npm audit --omit=dev'); + } + expect(readme).toContain( + 'current project path and the resolved Pipeline version and compatibility state', + ); + expect(readme).toContain( + 'uses the package resolver path internally when it generates companion configuration', + ); + expect(readme).not.toContain('shows the project and resolved Pipeline paths'); + expect(readme).not.toContain('package path shown by'); + + const primaryConfiguration = markdownSection( + readme, + '## Configure the primary MCP server', + '## MCP Unity extension commands', + ); + expect(primaryConfiguration).not.toContain('UNITY_CLI_PATH'); + expect(primaryConfiguration).toContain('absolute executable path'); + expect(primaryConfiguration).toContain('`PATH`'); + }); + + test('documents and enforces clean companion and aggregate-bound release claims', () => { + for (const document of [readme, agents]) { + expect(document).toContain('shared aggregate conversion-work budget'); + expect(document).toContain('4 KiB'); + expect(document).toContain('clean archive'); + } + + const cleanSmoke = readRepositoryFile( + 'Server~/scripts/clean-archive-mcp-smoke.mjs', + ); + expect(companionPackage).toHaveProperty( + 'scripts.test:clean-archive-mcp', + 'node scripts/clean-archive-mcp-smoke.mjs', + ); + expect(cleanSmoke).toContain("readResource({ uri: 'ui://unity-dashboard' })"); + expect(cleanSmoke).toContain("'text/html;profile=mcp-app'"); + expect(cleanSmoke).toContain('assertNoAncestorNodeModules'); + expect(cleanSmoke).toContain("path.join(cleanServer, 'mcp')"); + expect(cleanSmoke).toContain("'--unity-cli-path',\n process.execPath"); + expect(cleanSmoke).toContain( + "uri: 'unity://logs?severity=all&limit=1'", + ); + expect(cleanSmoke).toContain('cwd: cleanServer'); + expect(cleanSmoke).not.toContain('chmod'); + expect(cleanSmoke).not.toContain('#!/bin/sh'); + expect(cleanSmoke).not.toContain('.cmd'); + expect(cleanSmoke).not.toContain('shell: true'); + }); + + test('keeps every AI guidance file anchored to the 2.0 maintainer guide', () => { + const guidanceFiles = walkFiles(repositoryRoot) + .map((file) => path.relative(repositoryRoot, file)) + .filter(isAiGuidanceFile); + + expect(guidanceFiles).toEqual( + expect.arrayContaining(['.windsurfrules', 'AGENTS.md', 'CLAUDE.md', 'llms.txt']), + ); + for (const relativePath of guidanceFiles) { + if (relativePath === 'AGENTS.md') continue; + const content = readRepositoryFile(relativePath); + expect(content).toContain('AGENTS.md'); + for (const staleMarker of [ + 'McpUnityServer.cs', + 'McpToolBase', + 'McpUnitySettings.json', + 'websocket-sharp', + 'WebSocket Bridge', + 'default 8090', + ]) { + expect(content).not.toContain(staleMarker); + } + } + }); + + test('prevents the legacy bridge and publication surface from returning', () => { + const forbiddenPaths = [ + 'server.json', + 'glama.json', + 'Editor/UnityBridge', + 'Editor/Tools', + 'Editor/Resources', + 'Editor/Services', + 'Server~/src/tools', + 'Server~/src/unity/mcpUnity.ts', + 'Server~/src/unity/commandQueue.ts', + 'Server~/Dockerfile', + 'Server~/smithery.yaml', + ]; + const repositoryFiles = walkFiles(repositoryRoot); + const normalizedFiles = repositoryFiles.map((file) => + path.relative(repositoryRoot, file).split(path.sep).join('/').toLowerCase(), + ); + for (const relativePath of forbiddenPaths) { + const forbidden = relativePath.toLowerCase(); + expect( + normalizedFiles.some( + (file) => file === forbidden || file.startsWith(`${forbidden}/`), + ), + ).toBe(false); + } + expect( + normalizedFiles.some( + (file) => + file.includes('websocket-sharp') || + file.includes('websocketsharp'), + ), + ).toBe(false); + + const productionText = repositoryFiles + .filter((file) => { + const relative = path.relative(repositoryRoot, file); + const normalized = relative.split(path.sep).join('/').toLowerCase(); + return ( + !normalized.includes('/__tests__/') && + !normalized.startsWith('docs/') && + !normalized.endsWith('.md') && + !normalized.endsWith('.meta') && + !normalized.includes('/build/') && + !normalized.includes('/node_modules/') && + isProductionSourceOrConfig(normalized) + ); + }) + .map((file) => fs.readFileSync(file, 'utf8')) + .join('\n') + .toLowerCase(); + + expect(findForbiddenLegacyMarkers(productionText)).toEqual([]); + expect(productionText).not.toMatch(/(^|[^0-9])8090([^0-9]|$)/); + }); + + test('detects mixed-case legacy markers after normalization', () => { + expect( + findForbiddenLegacyMarkers( + 'WEBSOCKET-SHARP WebSocketSharp McPuNiTySeTtInGs LOCALHOST:8090 PACKEDCACHE', + ), + ).toEqual( + expect.arrayContaining([ + 'websocket-sharp', + 'websocketsharp', + 'mcpunitysettings', + 'localhost:8090', + 'packedcache', + ]), + ); + }); + + test('does not trust a 1.4.0 tag owned by an unrelated consumer repository', () => { + if (process.env.MCP_UNITY_NESTED_CONSUMER_CHECK === '1') return; + + const consumerRoot = fs.mkdtempSync( + path.join(os.tmpdir(), 'mcp-unity-consumer-git-'), + ); + const packageRoot = path.join( + consumerRoot, + 'Packages', + 'com.gamelovers.mcp-unity', + ); + try { + gitAt(consumerRoot, 'init', '--quiet'); + fs.writeFileSync( + path.join(consumerRoot, 'consumer.txt'), + 'unrelated consumer repository\n', + ); + gitAt(consumerRoot, 'add', 'consumer.txt'); + gitAt( + consumerRoot, + '-c', + 'user.name=Contract Test', + '-c', + 'user.email=contract-test@example.invalid', + 'commit', + '--quiet', + '-m', + 'consumer fixture', + ); + gitAt(consumerRoot, 'tag', '1.4.0'); + fs.cpSync(repositoryRoot, packageRoot, { + recursive: true, + filter: (source) => { + const relative = path + .relative(repositoryRoot, source) + .split(path.sep) + .join('/'); + return ![ + '.git', + 'Server~/build', + 'Server~/node_modules', + ].some( + (excluded) => + relative === excluded || relative.startsWith(`${excluded}/`), + ); + }, + }); + fs.symlinkSync( + path.join(serverRoot, 'node_modules'), + path.join(packageRoot, 'Server~', 'node_modules'), + packageCopyLinkType(), + ); + + expect(gitObjectExistsAt(packageRoot, '1.4.0^{commit}')).toBe(false); + expect(() => + execFileSync( + process.execPath, + [ + '--experimental-vm-modules', + path.join(serverRoot, 'node_modules', 'jest', 'bin', 'jest.js'), + '--runInBand', + '--config', + path.join(packageRoot, 'Server~', 'jest.config.js'), + path.join( + packageRoot, + 'Server~', + 'src', + '__tests__', + 'releaseContract.test.ts', + ), + ], + { + cwd: path.join(packageRoot, 'Server~'), + env: { + ...process.env, + MCP_UNITY_NESTED_CONSUMER_CHECK: '1', + }, + stdio: 'pipe', + }, + ), + ).not.toThrow(); + } finally { + fs.rmSync(consumerRoot, { recursive: true, force: true }); + } + }); + + test('uses an absolute package-copy target and a Windows-safe junction', () => { + expect(path.isAbsolute(path.join(serverRoot, 'node_modules'))).toBe(true); + expect(packageCopyLinkType('win32')).toBe('junction'); + expect(packageCopyLinkType('darwin')).toBe('dir'); + expect(packageCopyLinkType('linux')).toBe('dir'); + }); + + test('marks untranslated readmes as legacy documentation', () => { + for (const relativePath of ['README-ja.md', 'README_zh-CN.md']) { + const localizedReadme = readRepositoryFile(relativePath); + expect(localizedReadme.slice(0, 1000)).toContain( + 'MCP Unity 2.0 documentation', + ); + expect(localizedReadme.slice(0, 1000)).toContain('README.md'); + } + }); +}); + +function git(...args: string[]): string { + return gitAt(repositoryRoot, ...args); +} + +function gitAt(cwd: string, ...args: string[]): string { + return execFileSync('git', args, { + cwd, + encoding: 'utf8', + }); +} + +function gitObjectExists(objectName: string): boolean { + return gitObjectExistsAt(repositoryRoot, objectName); +} + +function gitObjectExistsAt(cwd: string, objectName: string): boolean { + if (!ownsGitRepository(cwd)) return false; + try { + execFileSync('git', ['cat-file', '-e', objectName], { + cwd, + stdio: 'ignore', + }); + return true; + } catch { + return false; + } +} + +function ownsGitRepository(packageRoot: string): boolean { + try { + const topLevel = execFileSync( + 'git', + ['rev-parse', '--show-toplevel'], + { + cwd: packageRoot, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'ignore'], + }, + ).trim(); + return fs.realpathSync(topLevel) === fs.realpathSync(packageRoot); + } catch { + return false; + } +} + +function fixtureDigest(source: string | Buffer): string { + const normalized = source.toString().replace(/\r\n?/g, '\n'); + return createHash('sha256').update(normalized, 'utf8').digest('hex'); +} + +function packageCopyLinkType( + platform: NodeJS.Platform = process.platform, +): 'dir' | 'junction' { + return platform === 'win32' ? 'junction' : 'dir'; +} + +function findForbiddenLegacyMarkers(source: string): string[] { + const normalizedSource = source.toLowerCase(); + return [ + 'websocket-sharp', + 'websocketsharp', + 'localhost:8090', + 'mcpunitysettings', + 'com.unity.editorcoroutines', + 'com.unity.nuget.newtonsoft-json', + 'packedcache', + ].filter((marker) => normalizedSource.includes(marker)); +} + +function collectMatches( + target: Set, + source: string, + pattern: RegExp, +): void { + for (const match of source.matchAll(pattern)) target.add(match[1]); +} + +function registeredCatalogModules(indexSource: string): string[] { + const modules = new Set(); + for (const match of indexSource.matchAll( + /import\s+\{([^}]+)\}\s+from\s+['"]\.\/(tools|resources|prompts)\/([^'"]+)\.js['"]/g, + )) { + const importedNames = match[1] + .split(',') + .map((name) => name.trim()) + .filter(Boolean); + const remainingIndex = indexSource.slice(match.index + match[0].length); + for (const importedName of importedNames) { + expect(remainingIndex).toMatch( + new RegExp(`\\b${escapeRegExp(importedName)}\\s*\\(`), + ); + } + modules.add(`Server~/src/${match[2]}/${match[3]}.ts`); + } + return [...modules].sort(); +} + +function expectMigrationRow(concept: string): void { + expect(readme).toContain(`| \`${concept}\` |`); +} + +function markdownDashCatalog(markdown: string): string[] { + return [...markdown.matchAll(/^- `([^`]+)` —/gm)] + .map((match) => match[1]) + .sort(); +} + +function markdownPlainCatalog(markdown: string): string[] { + return [...markdown.matchAll(/^- `([^`]+)`$/gm)] + .map((match) => match[1]) + .sort(); +} + +function markdownLabeledCatalog(markdown: string, label: string): string[] { + const pattern = new RegExp(`^- ${escapeRegExp(label)}: \`([^\`]+)\`$`, 'gm'); + return [...markdown.matchAll(pattern)].map((match) => match[1]).sort(); +} + +function markdownLowercaseLabeledCatalog( + markdown: string, + label: string, +): string[] { + const pattern = new RegExp(`^- ${escapeRegExp(label)} \`([^\`]+)\`$`, 'gm'); + return [...markdown.matchAll(pattern)].map((match) => match[1]).sort(); +} + +function markdownNestedCatalog(markdown: string, label: string): string[] { + const lines = markdown.split(/\r?\n/); + const start = lines.findIndex((line) => line === `- ${label}:`); + expect(start).toBeGreaterThanOrEqual(0); + const values: string[] = []; + for (let index = start + 1; index < lines.length; index += 1) { + const match = lines[index].match(/^ - `([^`]+)`$/); + if (!match) break; + values.push(match[1]); + } + return values.sort(); +} + +function sorted(values: Iterable): string[] { + return [...values].sort(); +} + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +function isProductionSourceOrConfig(relativePath: string): boolean { + const baseName = path.posix.basename(relativePath); + return ( + /\.(?:asmdef|cs|js|json|mjs|ps1|sh|ts|xml|yaml|yml)$/.test(relativePath) || + baseName === 'dockerfile' + ); +} + +function markdownSection( + markdown: string, + startHeading: string, + endHeading: string, +): string { + const start = markdown.indexOf(startHeading); + const end = markdown.indexOf(endHeading, start + startHeading.length); + expect(start).toBeGreaterThanOrEqual(0); + expect(end).toBeGreaterThan(start); + return markdown.slice(start, end); +} + +function isAiGuidanceFile(relativePath: string): boolean { + const normalized = relativePath.split(path.sep).join('/').toLowerCase(); + const baseName = path.posix.basename(normalized); + return ( + ['.cursorrules', '.windsurfrules', 'agents.md', 'claude.md', 'gemini.md', 'llms.txt'].includes( + baseName, + ) || normalized.endsWith('/copilot-instructions.md') + ); +} + +function walkFiles(directory: string): string[] { + return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + if ( + ['.git', '.superpowers', 'build', 'node_modules'].includes( + entry.name.toLowerCase(), + ) + ) { + return []; + } + const resolved = path.join(directory, entry.name); + return entry.isDirectory() ? walkFiles(resolved) : [resolved]; + }); +} diff --git a/Server~/src/__tests__/releaseMetadata.test.ts b/Server~/src/__tests__/releaseMetadata.test.ts deleted file mode 100644 index c80e0653..00000000 --- a/Server~/src/__tests__/releaseMetadata.test.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { existsSync, readFileSync } from 'node:fs'; -import { join } from 'node:path'; - -const serverDirectory = process.cwd(); -const repositoryDirectory = join(serverDirectory, '..'); - -describe('release 1.4.0 metadata', () => { - it('publishes 1.4.0 from both package manifests and the MCP protocol', () => { - const unityPackage = JSON.parse(readFileSync(join(repositoryDirectory, 'package.json'), 'utf8')); - const nodePackage = JSON.parse(readFileSync(join(serverDirectory, 'package.json'), 'utf8')); - const lockfile = JSON.parse(readFileSync(join(serverDirectory, 'package-lock.json'), 'utf8')); - const serverSource = readFileSync(join(serverDirectory, 'src', 'index.ts'), 'utf8'); - const dashboardSource = readFileSync(join(serverDirectory, 'src', 'ui', 'unity-dashboard.html'), 'utf8'); - - expect(unityPackage.version).toBe('1.4.0'); - expect(unityPackage.unity).toBe('2022.3'); - expect(nodePackage.version).toBe('1.4.0'); - expect(lockfile.version).toBe('1.4.0'); - expect(lockfile.packages[''].version).toBe('1.4.0'); - expect(serverSource).toContain('version: "1.4.0"'); - expect(dashboardSource).toContain("appInfo: { name: 'unity-dashboard', version: '1.4.0' }"); - }); - - it('does not keep a registry manifest for an unpublished npm package', () => { - expect(existsSync(join(repositoryDirectory, 'server.json'))).toBe(false); - }); -}); diff --git a/Server~/src/__tests__/unityConnection.test.ts b/Server~/src/__tests__/unityConnection.test.ts deleted file mode 100644 index 7d1d9c60..00000000 --- a/Server~/src/__tests__/unityConnection.test.ts +++ /dev/null @@ -1,276 +0,0 @@ -import { jest, describe, it, expect, beforeEach, afterEach } from '@jest/globals'; - -// Mock WebSocket before importing modules that use it -const mockWebSocketInstances: any[] = []; - -const createMockWebSocket = (overrides: Record = {}) => ({ - readyState: 1, - onopen: null, - onclose: null, - onerror: null, - onmessage: null, - send: jest.fn(), - close: jest.fn(), - terminate: jest.fn(), - ping: jest.fn(), - on: jest.fn(), - removeAllListeners: jest.fn(), - ...overrides -}); - -const mockWebSocketConstructor = jest.fn(() => { - const socket = createMockWebSocket(); - mockWebSocketInstances.push(socket); - return socket; -}); - -const mockWebSocketModule = Object.assign(mockWebSocketConstructor, { - CONNECTING: 0, - OPEN: 1, - CLOSING: 2, - CLOSED: 3 -}); - -jest.unstable_mockModule('ws', () => ({ - default: mockWebSocketModule, - WebSocket: mockWebSocketModule -})); - -// Dynamic imports after mocking -const { UnityConnection, ConnectionState } = await import('../unity/unityConnection'); -const { Logger, LogLevel } = await import('../utils/logger'); -const { McpUnityError, ErrorType } = await import('../utils/errors'); - -// Type imports -import type { ConnectionStateChange } from '../unity/unityConnection'; - -// Create a logger that doesn't output anything (for testing) -const createTestLogger = () => { - process.env.LOGGING = 'false'; - process.env.LOGGING_FILE = 'false'; - return new Logger('Test', LogLevel.ERROR); -}; - -describe('UnityConnection', () => { - let connection: InstanceType; - let testLogger: InstanceType; - - beforeEach(() => { - testLogger = createTestLogger(); - mockWebSocketConstructor.mockImplementation(() => { - const socket = createMockWebSocket(); - mockWebSocketInstances.push(socket); - return socket; - }); - mockWebSocketConstructor.mockClear(); - mockWebSocketInstances.length = 0; - - connection = new UnityConnection(testLogger, { - host: 'localhost', - port: 8090, - requestTimeout: 5000, - clientName: 'TestClient', - minReconnectDelay: 100, - maxReconnectDelay: 1000, - heartbeatInterval: 0 - }); - }); - - afterEach(() => { - connection.disconnect(); - jest.clearAllMocks(); - }); - - describe('Initial State', () => { - it('should start in disconnected state', () => { - expect(connection.connectionState).toBe(ConnectionState.Disconnected); - }); - - it('should not be connected initially', () => { - expect(connection.isConnected).toBe(false); - }); - - it('should not be connecting initially', () => { - expect(connection.isConnecting).toBe(false); - }); - - it('should have -1 for timeSinceLastPong before any connection', () => { - expect(connection.timeSinceLastPong).toBe(-1); - }); - }); - - describe('State Change Events', () => { - it('should emit stateChange event when connect is called', (done) => { - let firstEvent = true; - connection.on('stateChange', (change: ConnectionStateChange) => { - // Only check the first state change event - if (firstEvent && change.currentState === ConnectionState.Connecting) { - firstEvent = false; - expect(change.previousState).toBe(ConnectionState.Disconnected); - expect(change.currentState).toBe(ConnectionState.Connecting); - done(); - } - }); - - connection.connect().catch(() => {}); - }); - - it('should include reason in state change', (done) => { - let eventReceived = false; - connection.on('stateChange', (change: ConnectionStateChange) => { - if (!eventReceived && change.currentState === ConnectionState.Connecting) { - eventReceived = true; - expect(change.reason).toBeDefined(); - done(); - } - }); - - connection.connect().catch(() => {}); - }); - }); - - describe('Configuration', () => { - it('should update configuration dynamically', () => { - connection.updateConfig({ heartbeatInterval: 60000 }); - expect(connection.connectionState).toBe(ConnectionState.Disconnected); - }); - }); - - describe('getStats', () => { - it('should return correct stats in initial state', () => { - const stats = connection.getStats(); - expect(stats.state).toBe(ConnectionState.Disconnected); - expect(stats.reconnectAttempt).toBe(0); - expect(stats.timeSinceLastPong).toBe(-1); - expect(stats.isAwaitingPong).toBe(false); - }); - }); - - describe('Disconnect', () => { - it('should set state to disconnected on manual disconnect', () => { - connection.disconnect('Test disconnect'); - expect(connection.connectionState).toBe(ConnectionState.Disconnected); - }); - - it('should emit stateChange event when disconnecting from connecting state', (done) => { - // First start connecting, then disconnect - connection.on('stateChange', (change: ConnectionStateChange) => { - if (change.currentState === ConnectionState.Disconnected && - change.previousState !== ConnectionState.Disconnected) { - done(); - } - }); - - // Start connection then immediately disconnect - connection.connect().catch(() => {}); - // Give time for the connecting state to be set - setTimeout(() => { - connection.disconnect('Test disconnect'); - }, 10); - }); - }); - - describe('Send', () => { - it('should throw error when not connected', () => { - expect(() => connection.send('test')).toThrow(McpUnityError); - }); - }); - - describe('WebSocket options', () => { - it('sends the MCP client name as a header without setting WebSocket origin', async () => { - const connectPromise = connection.connect(); - - expect(mockWebSocketConstructor).toHaveBeenCalledWith( - 'ws://localhost:8090/McpUnity', - { - headers: { - 'X-Client-Name': 'TestClient' - } - } - ); - - const [, options] = mockWebSocketConstructor.mock.calls[0]; - expect(options).not.toHaveProperty('origin'); - - mockWebSocketInstances[0].onopen(); - await connectPromise; - }); - }); - - describe('forceReconnect', () => { - it('should trigger connecting state', () => { - connection.forceReconnect(); - expect(connection.isConnecting).toBe(true); - }); - }); -}); - -describe('ConnectionState Enum', () => { - it('should have correct values', () => { - expect(ConnectionState.Disconnected).toBe('disconnected'); - expect(ConnectionState.Connecting).toBe('connecting'); - expect(ConnectionState.Connected).toBe('connected'); - expect(ConnectionState.Reconnecting).toBe('reconnecting'); - }); -}); - -describe('Exponential Backoff Configuration', () => { - it('should accept backoff configuration', () => { - const testLogger = createTestLogger(); - const connection = new UnityConnection(testLogger, { - host: 'localhost', - port: 8090, - requestTimeout: 5000, - minReconnectDelay: 1000, - maxReconnectDelay: 30000, - reconnectBackoffMultiplier: 2 - }); - - expect(connection.connectionState).toBe(ConnectionState.Disconnected); - connection.disconnect(); - }); -}); - -describe('Connection timeout handling', () => { - beforeEach(() => { - jest.useFakeTimers(); - mockWebSocketConstructor.mockImplementation(() => { - const socket = createMockWebSocket({ readyState: 0 }); - mockWebSocketInstances.push(socket); - return socket; - }); - mockWebSocketConstructor.mockClear(); - mockWebSocketInstances.length = 0; - }); - - afterEach(() => { - jest.useRealTimers(); - }); - - it('uses a dedicated connect timeout instead of the request timeout', async () => { - const testLogger = createTestLogger(); - const connection = new UnityConnection(testLogger, { - host: 'localhost', - port: 8090, - requestTimeout: 60000, - connectTimeout: 250, - minReconnectDelay: 100, - maxReconnectDelay: 1000, - heartbeatInterval: 0 - }); - - const connectPromise = connection.connect(); - const connectResult = expect(connectPromise).rejects.toMatchObject({ - type: ErrorType.CONNECTION, - message: 'Connection timeout' - }); - - await jest.advanceTimersByTimeAsync(250); - - await connectResult; - expect(mockWebSocketConstructor).toHaveBeenCalledTimes(1); - expect(connection.connectionState).toBe(ConnectionState.Reconnecting); - - connection.disconnect(); - }); -}); diff --git a/Server~/src/__tests__/unityDashboardAppResource.test.ts b/Server~/src/__tests__/unityDashboardAppResource.test.ts deleted file mode 100644 index d96a0a96..00000000 --- a/Server~/src/__tests__/unityDashboardAppResource.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { jest, describe, it, expect, beforeEach } from '@jest/globals'; -import { RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps/server'; -import { registerShowUnityDashboardTool } from '../tools/showUnityDashboardTool.js'; -import { - readUnityDashboardHtml, - registerUnityDashboardAppResource -} from '../resources/unityDashboardAppResource.js'; - -const mockLogger = { - info: jest.fn(), - debug: jest.fn(), - warn: jest.fn(), - error: jest.fn() -}; - -const mockRegisterTool = jest.fn(); -const mockRegisterResource = jest.fn(); -const mockResource = jest.fn(); - -describe('Unity Dashboard MCP App', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('loads the bundled dashboard HTML from a module-relative path', () => { - const result = readUnityDashboardHtml(); - - expect(result.mimeType).toBe(RESOURCE_MIME_TYPE); - expect(result.text).toContain('Unity Dashboard'); - expect(result.text).toContain('set_play_mode_status'); - }); - - it('registers app and legacy dashboard resources', async () => { - registerUnityDashboardAppResource({ - registerResource: mockRegisterResource, - resource: mockResource - } as any, mockLogger as any); - - expect(mockRegisterResource).toHaveBeenCalledWith( - 'unity_dashboard_app', - 'ui://unity-dashboard', - expect.objectContaining({ - description: expect.stringContaining('Unity dashboard') - }), - expect.any(Function) - ); - expect(mockResource).toHaveBeenCalledWith( - 'unity_dashboard_app_legacy', - 'unity://ui/dashboard', - expect.objectContaining({ - mimeType: RESOURCE_MIME_TYPE - }), - expect.any(Function) - ); - - const appRead = mockRegisterResource.mock.calls[0][3] as () => Promise; - const legacyRead = mockResource.mock.calls[0][3] as () => Promise; - - await expect(appRead()).resolves.toMatchObject({ - contents: [ - expect.objectContaining({ - uri: 'ui://unity-dashboard', - mimeType: RESOURCE_MIME_TYPE - }) - ] - }); - await expect(legacyRead()).resolves.toMatchObject({ - contents: [ - expect.objectContaining({ - uri: 'unity://ui/dashboard', - mimeType: RESOURCE_MIME_TYPE - }) - ] - }); - }); - - it('registers the show dashboard app tool with normalized UI metadata', async () => { - registerShowUnityDashboardTool({ - registerTool: mockRegisterTool - } as any, mockLogger as any); - - expect(mockRegisterTool).toHaveBeenCalledWith( - 'show_unity_dashboard', - expect.objectContaining({ - description: expect.stringContaining('Unity dashboard'), - _meta: expect.objectContaining({ - ui: expect.objectContaining({ resourceUri: 'ui://unity-dashboard' }), - 'ui/resourceUri': 'ui://unity-dashboard' - }) - }), - expect.any(Function) - ); - - const handler = mockRegisterTool.mock.calls[0][2] as () => Promise; - const result = await handler(); - - expect(result.content[0]).toMatchObject({ - type: 'resource', - resource: { - uri: 'ui://unity-dashboard', - mimeType: RESOURCE_MIME_TYPE - } - }); - expect(result._meta.ui).toEqual({ - resourceUri: 'ui://unity-dashboard', - title: 'Unity Dashboard' - }); - }); -}); diff --git a/Server~/src/cli/companionCli.ts b/Server~/src/cli/companionCli.ts new file mode 100644 index 00000000..01cedb56 --- /dev/null +++ b/Server~/src/cli/companionCli.ts @@ -0,0 +1,383 @@ +import { + spawn, + type ChildProcess, + type SpawnOptions, +} from 'node:child_process'; +import fs from 'node:fs'; +import path from 'node:path'; +import { + boundedErrorDetail, + boundedErrorMessage, + boundedErrorText, +} from '../utils/boundedError.js'; + +export const CLI_DOCUMENTATION_URL = + 'https://docs.unity.com/en-us/unity-cli/use-unity-cli'; + +export interface CompanionArguments { + projectPath: string; + unityCliPath?: string; +} + +type PathValidator = (candidate: string) => boolean; + +export function parseCompanionArguments( + argv: readonly string[], + isUnityProject: PathValidator = defaultUnityProjectValidator, +): CompanionArguments { + let projectPath: string | undefined; + let unityCliPath: string | undefined; + + for (let index = 0; index < argv.length; index += 2) { + const flag = argv[index]; + const value = argv[index + 1]; + if (flag !== '--project-path' && flag !== '--unity-cli-path') { + throw new Error( + boundedErrorMessage('Unknown argument: ', flag ?? ''), + ); + } + if (!value || value.startsWith('--')) { + throw new Error(`${flag} requires a value.`); + } + + if (flag === '--project-path') { + if (projectPath !== undefined) { + throw new Error('Duplicate --project-path argument.'); + } + projectPath = value; + } else { + if (unityCliPath !== undefined) { + throw new Error('Duplicate --unity-cli-path argument.'); + } + unityCliPath = value; + } + } + + if (!projectPath) { + throw new Error('--project-path is required.'); + } + if (!path.isAbsolute(projectPath)) { + throw new Error('--project-path must be absolute.'); + } + if (!isUnityProject(projectPath)) { + throw new Error( + boundedErrorMessage( + '--project-path must identify an existing Unity project: ', + projectPath, + ), + ); + } + if (unityCliPath && !path.isAbsolute(unityCliPath)) { + throw new Error('--unity-cli-path must be absolute.'); + } + + return { projectPath: path.resolve(projectPath), unityCliPath }; +} + +export function resolveUnityCliPath( + explicitPath: string | undefined, + environment: NodeJS.ProcessEnv = process.env, +): string { + const environmentPath = environment.UNITY_CLI_PATH?.trim(); + return explicitPath || environmentPath || 'unity'; +} + +export interface VersionCommandResult { + stdout: string; + stderr: string; +} + +export type VersionRunner = ( + command: string, + args: readonly string[], + options?: VersionRunOptions, +) => Promise; + +export interface VersionRunOptions { + timeoutMs?: number; + signal?: AbortSignal; +} + +export interface CheckedUnityCli { + command: string; + version: string; + warning?: string; +} + +export async function checkUnityCli( + command: string, + runVersion: VersionRunner = runUnityCliVersion, + options: VersionRunOptions = {}, +): Promise { + let output: VersionCommandResult; + try { + output = await runVersion(command, ['--version'], options); + } catch (error) { + throw actionableCliError( + boundedErrorMessage( + `Unity CLI could not be started at "${boundedErrorDetail(command)}": `, + error, + ), + ); + } + + const version = parseVersion(`${output.stdout}\n${output.stderr}`); + if (!version) { + throw actionableCliError( + `Unity CLI returned an unrecognized version from "${command}".`, + ); + } + + if (compareVersion(version, MINIMUM_VERSION) < 0) { + throw actionableCliError( + `Unity CLI ${version.raw} is incompatible; version ${MINIMUM_VERSION.raw} or newer is required.`, + ); + } + + return { + command, + version: version.raw, + warning: + version.major > 1n + ? `Unity CLI ${version.raw} is newer than the tested major version 1.` + : undefined, + }; +} + +interface ParsedVersion { + raw: string; + major: bigint; + minor: bigint; + patch: bigint; + prerelease: SemVerIdentifier[]; + build: string[]; +} + +type SemVerIdentifier = + | { numeric: true; value: bigint; raw: string } + | { numeric: false; value: string; raw: string }; + +const SEMVER_PATTERN = + /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/; + +const MINIMUM_VERSION: ParsedVersion = { + raw: '1.0.0-beta.2', + major: 1n, + minor: 0n, + patch: 0n, + prerelease: [ + { numeric: false, value: 'beta', raw: 'beta' }, + { numeric: true, value: 2n, raw: '2' }, + ], + build: [], +}; + +function parseVersion(output: string): ParsedVersion | undefined { + for (const token of output.trim().split(/\s+/)) { + const parsed = parseSemVerToken( + /^v[0-9]/.test(token) ? token.slice(1) : token, + ); + if (parsed) return parsed; + } + return undefined; +} + +function parseSemVerToken(token: string): ParsedVersion | undefined { + const match = SEMVER_PATTERN.exec(token); + if (!match) return undefined; + const prereleaseTokens = match[4]?.split('.') ?? []; + const prerelease: SemVerIdentifier[] = []; + for (const identifier of prereleaseTokens) { + if (/^[0-9]+$/.test(identifier)) { + if (identifier.length > 1 && identifier.startsWith('0')) return undefined; + prerelease.push({ + numeric: true, + value: BigInt(identifier), + raw: identifier, + }); + } else { + prerelease.push({ + numeric: false, + value: identifier, + raw: identifier, + }); + } + } + return { + raw: token, + major: BigInt(match[1]), + minor: BigInt(match[2]), + patch: BigInt(match[3]), + prerelease, + build: match[5]?.split('.') ?? [], + }; +} + +function compareVersion(left: ParsedVersion, right: ParsedVersion): number { + for (const key of ['major', 'minor', 'patch'] as const) { + if (left[key] !== right[key]) { + return left[key] > right[key] ? 1 : -1; + } + } + + if (left.prerelease.length === 0 && right.prerelease.length === 0) return 0; + if (left.prerelease.length === 0) return 1; + if (right.prerelease.length === 0) return -1; + + const identifierCount = Math.max( + left.prerelease.length, + right.prerelease.length, + ); + for (let index = 0; index < identifierCount; index++) { + const leftIdentifier = left.prerelease[index]; + const rightIdentifier = right.prerelease[index]; + if (!leftIdentifier) return -1; + if (!rightIdentifier) return 1; + if (leftIdentifier.numeric && rightIdentifier.numeric) { + if (leftIdentifier.value === rightIdentifier.value) continue; + return leftIdentifier.value > rightIdentifier.value ? 1 : -1; + } + if (leftIdentifier.numeric !== rightIdentifier.numeric) { + return leftIdentifier.numeric ? -1 : 1; + } + const leftValue = leftIdentifier.value as string; + const rightValue = rightIdentifier.value as string; + if (leftValue === rightValue) continue; + return leftValue > rightValue ? 1 : -1; + } + return 0; +} + +function defaultUnityProjectValidator(candidate: string): boolean { + try { + return ( + fs.statSync(candidate).isDirectory() && + fs.statSync(path.join(candidate, 'Assets')).isDirectory() && + fs.statSync(path.join(candidate, 'ProjectSettings')).isDirectory() + ); + } catch { + return false; + } +} + +type SpawnVersionProcess = ( + command: string, + args: readonly string[], + options: SpawnOptions, +) => Pick; + +export async function runUnityCliVersion( + command: string, + args: readonly string[], + options: VersionRunOptions = {}, + spawnProcess: SpawnVersionProcess = spawn, +): Promise { + if (args.length !== 1 || args[0] !== '--version') { + throw new Error('Unity CLI validation may invoke only --version.'); + } + if (options.signal?.aborted) { + throw new Error('Unity CLI version check was cancelled.'); + } + + const timeoutMs = options.timeoutMs ?? 10_000; + const detached = process.platform !== 'win32'; + return new Promise((resolve, reject) => { + let child: ReturnType; + try { + child = spawnProcess(command, [...args], { + shell: false, + detached, + windowsHide: true, + stdio: ['ignore', 'pipe', 'pipe'], + }); + } catch (error) { + reject(error); + return; + } + + let stdout = ''; + let stderr = ''; + let settled = false; + const maxOutputBytes = 64 * 1024; + + const cleanup = (): void => { + clearTimeout(timeout); + options.signal?.removeEventListener('abort', cancel); + }; + const finish = ( + error?: Error, + result?: VersionCommandResult, + ): void => { + if (settled) return; + settled = true; + cleanup(); + if (error) reject(error); + else resolve(result ?? { stdout, stderr }); + }; + const terminate = (): void => { + child.stdout?.destroy(); + child.stderr?.destroy(); + if (detached && child.pid) { + try { + process.kill(-child.pid, 'SIGKILL'); + return; + } catch { + // The process group may already have exited. + } + } + try { + child.kill('SIGKILL'); + } catch { + // The child already exited. + } + }; + const append = (target: 'stdout' | 'stderr', chunk: unknown): void => { + if (settled) return; + const value = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk); + if (target === 'stdout') stdout += value; + else stderr += value; + if (Buffer.byteLength(stdout) + Buffer.byteLength(stderr) > maxOutputBytes) { + terminate(); + finish(new Error('Unity CLI version output exceeded 64 KiB.')); + } + }; + const cancel = (): void => { + terminate(); + finish(new Error('Unity CLI version check was cancelled.')); + }; + const timeout = setTimeout(() => { + terminate(); + finish(new Error(`Unity CLI version check timed out after ${timeoutMs}ms.`)); + }, timeoutMs); + + child.stdout?.on('data', (chunk) => append('stdout', chunk)); + child.stderr?.on('data', (chunk) => append('stderr', chunk)); + child.once('error', (error) => finish(error)); + child.once('close', (code, signal) => { + if (code === 0) { + finish(undefined, { stdout, stderr }); + } else { + finish( + new Error( + `Unity CLI --version exited with ${ + signal ? `signal ${signal}` : `code ${code ?? 'unknown'}` + }.`, + ), + ); + } + }); + if (options.signal?.aborted) { + cancel(); + } else { + options.signal?.addEventListener('abort', cancel, { once: true }); + } + }); +} + +function actionableCliError(message: string): Error { + return new Error( + boundedErrorText( + `${message} Install or update Unity CLI: ${CLI_DOCUMENTATION_URL}`, + ), + ); +} diff --git a/Server~/src/companionEntrypoint.ts b/Server~/src/companionEntrypoint.ts new file mode 100644 index 00000000..59c10175 --- /dev/null +++ b/Server~/src/companionEntrypoint.ts @@ -0,0 +1,69 @@ +import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; +import { + checkUnityCli, + type CheckedUnityCli, + parseCompanionArguments, + resolveUnityCliPath, +} from './cli/companionCli.js'; +import { + installShutdownHandlers, + type EventSource, +} from './companionLifecycle.js'; +import { createCompanionServer } from './companionServer.js'; +import { CompanionResourceService } from './resources/companionResources.js'; +import { OfficialUnityMcpClient } from './unity/officialUnityMcpClient.js'; +import { boundedErrorDetail } from './utils/boundedError.js'; + +export interface CompanionEntrypointOptions { + argv: readonly string[]; + environment: NodeJS.ProcessEnv; + isUnityProject?: (candidate: string) => boolean; + checkCli?: (command: string) => Promise; + transport: Transport; + signals: EventSource; + stdin: EventSource; + stderr: { write(text: string): unknown }; +} + +export interface CompanionRuntime { + officialClient: OfficialUnityMcpClient; + shutdown(): Promise; +} + +export async function startCompanion( + options: CompanionEntrypointOptions, +): Promise { + const args = parseCompanionArguments(options.argv, options.isUnityProject); + const cliPath = resolveUnityCliPath(args.unityCliPath, options.environment); + const checked = await (options.checkCli ?? checkUnityCli)(cliPath); + if (checked.warning) { + options.stderr.write(`Warning: ${checked.warning}\n`); + } + + const officialClient = new OfficialUnityMcpClient({ + cliPath: checked.command, + projectPath: args.projectPath, + }); + const server = createCompanionServer( + new CompanionResourceService(officialClient), + ); + await server.connect(options.transport); + + const handlers = installShutdownHandlers({ + signals: options.signals, + stdin: options.stdin, + closeOfficialClient: () => officialClient.close(), + closeServer: () => server.close(), + onError: (error) => { + options.stderr.write(`Shutdown error: ${boundedErrorDetail(error)}\n`); + }, + }); + + return { + officialClient, + shutdown: async () => { + await handlers.shutdown(); + handlers.dispose(); + }, + }; +} diff --git a/Server~/src/companionLifecycle.ts b/Server~/src/companionLifecycle.ts new file mode 100644 index 00000000..eaee8f79 --- /dev/null +++ b/Server~/src/companionLifecycle.ts @@ -0,0 +1,59 @@ +export interface EventSource { + on(event: string, listener: () => void): unknown; + off(event: string, listener: () => void): unknown; +} + +export interface ShutdownHandlerOptions { + signals: EventSource; + stdin: EventSource; + closeOfficialClient(): Promise; + closeServer(): Promise; + onError?(error: unknown): void; +} + +export interface ShutdownHandlers { + shutdown(): Promise; + dispose(): void; +} + +export function installShutdownHandlers( + options: ShutdownHandlerOptions, +): ShutdownHandlers { + let shutdownPromise: Promise | undefined; + + const shutdown = (): Promise => { + shutdownPromise ??= (async () => { + const results = await Promise.allSettled([ + options.closeOfficialClient(), + options.closeServer(), + ]); + for (const result of results) { + if (result.status === 'rejected') { + options.onError?.(result.reason); + } + } + })(); + return shutdownPromise; + }; + const trigger = (): void => { + void shutdown(); + }; + const bindings: Array<[EventSource, string]> = [ + [options.signals, 'SIGINT'], + [options.signals, 'SIGTERM'], + [options.stdin, 'close'], + [options.stdin, 'end'], + ]; + for (const [source, event] of bindings) { + source.on(event, trigger); + } + + return { + shutdown, + dispose: () => { + for (const [source, event] of bindings) { + source.off(event, trigger); + } + }, + }; +} diff --git a/Server~/src/companionServer.ts b/Server~/src/companionServer.ts new file mode 100644 index 00000000..84bdb5d9 --- /dev/null +++ b/Server~/src/companionServer.ts @@ -0,0 +1,144 @@ +import { registerAppResource, registerAppTool } from '@modelcontextprotocol/ext-apps/server'; +import { + McpServer, + ResourceTemplate, +} from '@modelcontextprotocol/sdk/server/mcp.js'; +import { registerCompanionPrompts } from './prompts/companionPrompts.js'; +import type { CompanionResourceService } from './resources/companionResources.js'; +import { + DASHBOARD_URI, + readDashboardHtml, +} from './resources/dashboardResource.js'; +import { boundedError } from './utils/boundedError.js'; + +const RESOURCE_TEMPLATES = [ + { + name: 'unity_logs', + template: 'unity://logs{?severity,limit}', + description: 'Recent Unity Editor logs.', + }, + { + name: 'unity_scenes_hierarchy', + template: 'unity://scenes-hierarchy{?path,max_nodes}', + description: 'Bounded hierarchy of an open Unity scene.', + }, + { + name: 'unity_gameobject', + template: 'unity://gameobject/{target}', + description: 'Bounded GameObject inspection.', + }, + { + name: 'unity_packages', + template: 'unity://packages{?include_indirect}', + description: 'Installed Unity packages.', + }, + { + name: 'unity_tests', + template: 'unity://tests/{mode}', + description: 'Available Unity tests.', + }, +] as const; + +export interface CompanionServerOptions { + readDashboardHtml?: typeof readDashboardHtml; +} + +export function createCompanionServer( + resources: CompanionResourceService, + options: CompanionServerOptions = {}, +): McpServer { + const server = new McpServer( + { name: 'MCP Unity Companion', version: '2.0.0' }, + { capabilities: { tools: {}, resources: {}, prompts: {} } }, + ); + + registerAppTool( + server, + 'show_unity_dashboard', + { + description: 'Open the read-only Unity CLI and Pipeline dashboard.', + annotations: { readOnlyHint: true }, + _meta: { + ui: { + resourceUri: DASHBOARD_URI, + }, + }, + }, + async () => ({ + content: [ + { + type: 'text', + text: 'Unity dashboard opened. Its views are read-only.', + }, + ], + }), + ); + + registerAppResource( + server, + 'unity_dashboard', + DASHBOARD_URI, + { + description: 'Read-only Unity CLI and Pipeline dashboard.', + _meta: { ui: { prefersBorder: true } }, + }, + async () => { + try { + const dashboard = ( + options.readDashboardHtml ?? readDashboardHtml + )(); + return { + contents: [ + { + uri: DASHBOARD_URI, + mimeType: dashboard.mimeType, + text: dashboard.text, + _meta: { + ui: { + csp: { + connectDomains: [], + resourceDomains: [], + frameDomains: [], + baseUriDomains: [], + }, + }, + }, + }, + ], + }; + } catch (error) { + throw boundedError(error); + } + }, + ); + + for (const definition of RESOURCE_TEMPLATES) { + server.registerResource( + definition.name, + new ResourceTemplate(definition.template, { list: undefined }), + { + description: definition.description, + mimeType: 'application/json', + }, + async (uri) => { + try { + const result = await resources.read(uri.toString()); + return { + contents: [ + { + uri: result.uri, + mimeType: 'application/json', + text: JSON.stringify(result.payload), + }, + ], + }; + } catch (error) { + throw boundedError(error); + } + }, + ); + } + + registerCompanionPrompts(server); + return server; +} diff --git a/Server~/src/index.ts b/Server~/src/index.ts index 3770e538..3a4d3c5d 100644 --- a/Server~/src/index.ts +++ b/Server~/src/index.ts @@ -1,186 +1,20 @@ -// Import MCP SDK components -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +#!/usr/bin/env node import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { McpUnity } from './unity/mcpUnity.js'; -import { Logger, LogLevel } from './utils/logger.js'; -import { registerCreateSceneTool } from './tools/createSceneTool.js'; -import { registerMenuItemTool } from './tools/menuItemTool.js'; -import { registerSelectGameObjectTool } from './tools/selectGameObjectTool.js'; -import { registerAddPackageTool } from './tools/addPackageTool.js'; -import { registerRunTestsTool } from './tools/runTestsTool.js'; -import { registerSendConsoleLogTool } from './tools/sendConsoleLogTool.js'; -import { registerGetConsoleLogsTool } from './tools/getConsoleLogsTool.js'; -import { registerUpdateComponentTool } from './tools/updateComponentTool.js'; -import { registerAddAssetToSceneTool } from './tools/addAssetToSceneTool.js'; -import { registerUpdateGameObjectTool } from './tools/updateGameObjectTool.js'; -import { registerCreatePrefabTool } from './tools/createPrefabTool.js'; -import { registerDeleteSceneTool } from './tools/deleteSceneTool.js'; -import { registerLoadSceneTool } from './tools/loadSceneTool.js'; -import { registerSaveSceneTool } from './tools/saveSceneTool.js'; -import { registerGetSceneInfoTool } from './tools/getSceneInfoTool.js'; -import { registerGetPlayModeStatusTool } from './tools/getPlayModeStatusTool.js'; -import { registerSetPlayModeStatusTool } from './tools/setPlayModeStatusTool.js'; -import { registerUnloadSceneTool } from './tools/unloadSceneTool.js'; -import { registerRecompileScriptsTool } from './tools/recompileScriptsTool.js'; -import { registerGetGameObjectTool } from './tools/getGameObjectTool.js'; -import { registerTransformTools } from './tools/transformTools.js'; -import { registerCreateMaterialTool, registerAssignMaterialTool, registerModifyMaterialTool, registerGetMaterialInfoTool } from './tools/materialTools.js'; -import { registerDuplicateGameObjectTool, registerDeleteGameObjectTool, registerReparentGameObjectTool } from './tools/gameObjectTools.js'; -import { registerBatchExecuteTool } from './tools/batchExecuteTool.js'; -import { registerShowUnityDashboardTool } from './tools/showUnityDashboardTool.js'; -import { registerGetScenesHierarchyTool } from './tools/getScenesHierarchyTool.js'; -import { registerGetMenuItemsResource } from './resources/getMenuItemResource.js'; -import { registerGetConsoleLogsResource } from './resources/getConsoleLogsResource.js'; -import { registerGetHierarchyResource } from './resources/getScenesHierarchyResource.js'; -import { registerGetPackagesResource } from './resources/getPackagesResource.js'; -import { registerGetAssetsResource } from './resources/getAssetsResource.js'; -import { registerGetTestsResource } from './resources/getTestsResource.js'; -import { registerGetGameObjectResource } from './resources/getGameObjectResource.js'; -import { registerUnityDashboardAppResource } from './resources/unityDashboardAppResource.js'; -import { registerGameObjectHandlingPrompt } from './prompts/gameobjectHandlingPrompt.js'; -import { registerUnityDashboardPrompt } from './prompts/unityDashboardPrompt.js'; - -// Initialize loggers -const serverLogger = new Logger('Server', LogLevel.INFO); -const unityLogger = new Logger('Unity', LogLevel.INFO); -const toolLogger = new Logger('Tools', LogLevel.INFO); -const resourceLogger = new Logger('Resources', LogLevel.INFO); - -// Initialize the MCP server -const server = new McpServer ( - { - name: "MCP Unity Server", - version: "1.4.0" - }, - { - capabilities: { - tools: {}, - resources: {}, - prompts: {}, - }, - } -); - -// Initialize MCP HTTP bridge with Unity editor -const mcpUnity = new McpUnity(unityLogger); - -// Register all tools into the MCP server -registerMenuItemTool(server, mcpUnity, toolLogger); -registerSelectGameObjectTool(server, mcpUnity, toolLogger); -registerAddPackageTool(server, mcpUnity, toolLogger); -registerRunTestsTool(server, mcpUnity, toolLogger); -registerSendConsoleLogTool(server, mcpUnity, toolLogger); -registerGetConsoleLogsTool(server, mcpUnity, toolLogger); -registerUpdateComponentTool(server, mcpUnity, toolLogger); -registerAddAssetToSceneTool(server, mcpUnity, toolLogger); -registerUpdateGameObjectTool(server, mcpUnity, toolLogger); -registerCreatePrefabTool(server, mcpUnity, toolLogger); -registerCreateSceneTool(server, mcpUnity, toolLogger); -registerDeleteSceneTool(server, mcpUnity, toolLogger); -registerLoadSceneTool(server, mcpUnity, toolLogger); -registerSaveSceneTool(server, mcpUnity, toolLogger); -registerGetSceneInfoTool(server, mcpUnity, toolLogger); -registerGetPlayModeStatusTool(server, mcpUnity, toolLogger); -registerSetPlayModeStatusTool(server, mcpUnity, toolLogger); -registerShowUnityDashboardTool(server, toolLogger); -registerGetScenesHierarchyTool(server, mcpUnity, toolLogger); -registerUnloadSceneTool(server, mcpUnity, toolLogger); -registerRecompileScriptsTool(server, mcpUnity, toolLogger); -registerGetGameObjectTool(server, mcpUnity, toolLogger); -registerTransformTools(server, mcpUnity, toolLogger); -registerDuplicateGameObjectTool(server, mcpUnity, toolLogger); -registerDeleteGameObjectTool(server, mcpUnity, toolLogger); -registerReparentGameObjectTool(server, mcpUnity, toolLogger); - -// Register Material Tools -registerCreateMaterialTool(server, mcpUnity, toolLogger); -registerAssignMaterialTool(server, mcpUnity, toolLogger); -registerModifyMaterialTool(server, mcpUnity, toolLogger); -registerGetMaterialInfoTool(server, mcpUnity, toolLogger); - -// Register Batch Execute Tool (high-priority for performance) -registerBatchExecuteTool(server, mcpUnity, toolLogger); - -// Register all resources into the MCP server -registerGetTestsResource(server, mcpUnity, resourceLogger); -registerGetGameObjectResource(server, mcpUnity, resourceLogger); -registerGetMenuItemsResource(server, mcpUnity, resourceLogger); -registerGetConsoleLogsResource(server, mcpUnity, resourceLogger); -registerGetHierarchyResource(server, mcpUnity, resourceLogger); -registerGetPackagesResource(server, mcpUnity, resourceLogger); -registerGetAssetsResource(server, mcpUnity, resourceLogger); -registerUnityDashboardAppResource(server, resourceLogger); - -// Register all prompts into the MCP server -registerGameObjectHandlingPrompt(server); -registerUnityDashboardPrompt(server); - -// Server startup function -async function startServer() { - try { - // Initialize STDIO transport for MCP client communication - const stdioTransport = new StdioServerTransport(); - - // Connect the server to the transport - await server.connect(stdioTransport); - - serverLogger.info('MCP Server started'); - - // Get the client name from the MCP server - const clientName = server.server.getClientVersion()?.name || 'Unknown MCP Client'; - serverLogger.info(`Connected MCP client: ${clientName}`); - - // Start Unity Bridge connection with client name in headers - await mcpUnity.start(clientName); - - } catch (error) { - serverLogger.error('Failed to start server', error); - process.exit(1); - } +import { startCompanion } from './companionEntrypoint.js'; +import { boundedErrorMessage } from './utils/boundedError.js'; + +try { + await startCompanion({ + argv: process.argv.slice(2), + environment: process.env, + transport: new StdioServerTransport(), + signals: process, + stdin: process.stdin, + stderr: process.stderr, + }); +} catch (error) { + process.stderr.write( + `${boundedErrorMessage('MCP Unity Companion could not start: ', error)}\n`, + ); + process.exitCode = 1; } - -// Graceful shutdown handler -let isShuttingDown = false; -async function shutdown() { - if (isShuttingDown) return; - isShuttingDown = true; - - try { - serverLogger.info('Shutting down...'); - await mcpUnity.stop(); - await server.close(); - } catch (error) { - // Ignore errors during shutdown - } - process.exit(0); -} - -// Start the server -startServer(); - -// Handle shutdown signals -process.on('SIGINT', shutdown); -process.on('SIGTERM', shutdown); -process.on('SIGHUP', shutdown); - -// Handle stdin close (when MCP client disconnects) -process.stdin.on('close', shutdown); -process.stdin.on('end', shutdown); -process.stdin.on('error', shutdown); - -// Handle uncaught exceptions - exit cleanly if it's just a closed pipe -process.on('uncaughtException', (error: NodeJS.ErrnoException) => { - // EPIPE/EOF errors are expected when the MCP client disconnects - if (error.code === 'EPIPE' || error.code === 'EOF' || error.code === 'ERR_USE_AFTER_CLOSE') { - shutdown(); - return; - } - serverLogger.error('Uncaught exception', error); - process.exit(1); -}); - -// Handle unhandled promise rejections -process.on('unhandledRejection', (reason) => { - serverLogger.error('Unhandled rejection', reason); - process.exit(1); -}); diff --git a/Server~/src/prompts/companionPrompts.ts b/Server~/src/prompts/companionPrompts.ts new file mode 100644 index 00000000..3a68592a --- /dev/null +++ b/Server~/src/prompts/companionPrompts.ts @@ -0,0 +1,45 @@ +import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; + +const GAMEOBJECT_STRATEGY = `Use Pipeline read commands before mutations: +1. Read get_scene_hierarchy or find_gameobjects to obtain a stable target. +2. Use inspect_gameobject for bounded component and serialized-property inspection. +3. Use official Pipeline authoring commands for standard changes. +4. Use only the MCP Unity extensions when needed: duplicate_gameobject, unload_scene, editor_step, and assign_material. +5. Re-read get_scene_hierarchy or inspect_gameobject to verify the result.`; + +const DASHBOARD_GUIDE = `Open show_unity_dashboard for a read-only project overview. +The companion resources map to official Pipeline commands: get_console_logs, get_scene_hierarchy, package_list, and list_tests. +GameObject details use inspect_gameobject. The other MCP Unity extensions are duplicate_gameobject, unload_scene, editor_step, and assign_material. +The dashboard never invokes mutations; execute any authoring command explicitly through Unity CLI/Pipeline.`; + +export function registerCompanionPrompts(server: McpServer): void { + server.registerPrompt( + 'gameobject_handling_strategy', + { + description: 'A safe discovery, targeting, mutation, and verification workflow.', + }, + async () => ({ + messages: [ + { + role: 'user', + content: { type: 'text', text: GAMEOBJECT_STRATEGY }, + }, + ], + }), + ); + + server.registerPrompt( + 'unity_dashboard', + { + description: 'Guidance for the read-only Unity dashboard and Pipeline commands.', + }, + async () => ({ + messages: [ + { + role: 'user', + content: { type: 'text', text: DASHBOARD_GUIDE }, + }, + ], + }), + ); +} diff --git a/Server~/src/prompts/gameobjectHandlingPrompt.ts b/Server~/src/prompts/gameobjectHandlingPrompt.ts deleted file mode 100644 index 3079e2c6..00000000 --- a/Server~/src/prompts/gameobjectHandlingPrompt.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import * as z from "zod"; - -/** - * Registers the gameobject handling prompt with the MCP server. - * This prompt defines the proper workflow for handling GameObjects within the Unity Editor. - * - * @param server The McpServer instance to register the prompt with. - */ -export function registerGameObjectHandlingPrompt(server: McpServer) { - server.prompt( - 'gameobject_handling_strategy', - 'Defines the proper workflow for handling gameobjects in Unity', - { - gameObjectIdOrName: z.string().describe("The resource to identify the GameObject intended to handle. It can be the **instance ID**, the **name** or the **path** to the GameObject."), - }, - async ({ gameObjectIdOrName }) => ({ - messages: [ - { - role: 'user', - content: { - type: 'text', - text: `You are an expert AI assistant integrated with Unity via a MCP server. - -When working directly with GameObjects or any of their components in Unity scenes, you have access to the following resources and tools: -- Resource "get_scenes_hierarchy" (unity://scenes_hierarchy) to list all GameObjects. -- Resource "get_gameobject" (unity://gameobject/{idOrName}) to fetch detailed GameObject info, with the *idOrName* being either the **instance ID**, the **name** or the **path** to the GameObject. -- Tool "select_gameobject" to select a GameObject by **instance ID**, the **name** or the **path** of the GameObject. -- Tool "update_gameobject" to update a GameObject's core properties (name, tag, layer, active state, static state), or create the GameObject if it does not exist. -- Tool "update_component" to update or add a component on a GameObject, including common frequently used components (e.g. Transform, RectTransform, BoxCollider, Rigidbody, etc). -- Tool "create_prefab" to create a prefab from a GameObject in the scene with optional MonoBehaviour script and serialized field values. - -Workflow: -1. Use "get_scenes_hierarchy" to confirm the GameObject ID, name or path for "${gameObjectIdOrName}". -2. If you need to update the GameObject's core properties (name, tag, layer, active state, static state), or create the GameObject if it does not exist, use "update_gameobject". -3. To focus the Unity Editor on the target GameObject, invoke "select_gameobject". -4. Optionally, use "unity://gameobject/${gameObjectIdOrName}" to retrieve detailed properties. -5. To update or add a component on the GameObject, use "update_component". -6. Confirm success and report any errors. - -Guidance: -- Use "update_gameobject" for creating GameObjects in the scene or to change a GameObject's core properties. -- Use "update_component" for adding or modifying components on an existing GameObject in the scene. -- Use "create_prefab" for creating prefabs from GameObjects in the scene. -- Component Scripts must be compiled in the Unity project before using "update_component" or "create_prefab". -- Always validate inputs and request clarification if the identifier is ambiguous.` - } - }, - { - role: 'user', - content: { - type: 'text', - text: `Handle GameObject "${gameObjectIdOrName}" through the above workflow.` - } - } - ] - }) - ); -} diff --git a/Server~/src/prompts/unityDashboardPrompt.ts b/Server~/src/prompts/unityDashboardPrompt.ts deleted file mode 100644 index 62c029a3..00000000 --- a/Server~/src/prompts/unityDashboardPrompt.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; - -/** - * Registers the Unity dashboard prompt with the MCP server. - * This prompt provides an easy way to open and interact with the Unity dashboard MCP app. - * - * @param server The McpServer instance to register the prompt with. - */ -export function registerUnityDashboardPrompt(server: McpServer) { - server.prompt( - 'unity_dashboard', - 'Opens the Unity dashboard MCP app in VS Code with real-time Unity Editor information', - {}, - async () => ({ - messages: [ - { - role: 'user', - content: { - type: 'text', - text: `You are an expert AI assistant integrated with Unity via an MCP server. - -The Unity Dashboard is a powerful MCP app that provides real-time access to Unity Editor information through an interactive interface in VS Code. - -Dashboard Features: -- **Play Mode Controls**: Start, pause, stop, and step through Play Mode -- **Scene Hierarchy**: Browse and interact with GameObjects in your scene -- **Console Logs**: View Unity console messages (info, warnings, errors) -- **Package Manager**: List installed packages and their versions -- **Scene Management**: View loaded scenes and their status - -To open the Unity Dashboard: -Use the "show_unity_dashboard" tool to launch the dashboard app in VS Code. - -Requirements: -- VS Code 1.109 or later (MCP Apps support) -- Unity Editor with MCP Unity server running and connected - -Usage Scenarios: -- Monitor Unity Editor state while working with AI -- Quick access to scene hierarchy and GameObject information -- Debug console logs without switching to Unity -- Control Play Mode from within your coding environment -- View package dependencies at a glance - -Once opened, the dashboard remains available in your VS Code editor tabs and updates in real-time as changes occur in Unity.` - } - }, - { - role: 'user', - content: { - type: 'text', - text: `Open the Unity dashboard app now.` - } - } - ] - }) - ); -} diff --git a/Server~/src/resources/companionResources.ts b/Server~/src/resources/companionResources.ts new file mode 100644 index 00000000..29c460b7 --- /dev/null +++ b/Server~/src/resources/companionResources.ts @@ -0,0 +1,973 @@ +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; +import { + boundedError, + boundedErrorMessage, +} from '../utils/boundedError.js'; + +export interface UnityReadClient { + readTool( + name: string, + args: Record, + ): Promise; +} + +export interface CompanionResourcePayload { + uri: string; + payload: Record; +} + +const LOG_SEVERITIES = new Set(['all', 'log', 'warning', 'error']); +const TEST_MODES = new Set(['all', 'editor', 'playmode']); +const NODE_NAME_MAX_LENGTH = 256; +const HIERARCHY_PATH_MAX_LENGTH = 1024; +const SCENE_NAME_MAX_LENGTH = 256; +const SCENE_PATH_MAX_LENGTH = 1024; +const INSTANCE_ID_MAX_LENGTH = 128; +const COMPONENT_MAX_COUNT = 32; +const COMPONENT_SCAN_LIMIT = 128; +const COMPONENT_NAME_MAX_LENGTH = 128; +// Keep hierarchy resources comfortably below common MCP host payload limits. +// The content allowance reserves fixed envelope/metadata space plus worst-case +// post-projection omission markers for every returned node. +const HIERARCHY_PAYLOAD_BUDGET_BYTES = 512 * 1024; +const HIERARCHY_ENVELOPE_RESERVE_BYTES = 16 * 1024; +const HIERARCHY_DYNAMIC_MARKER_RESERVE_PER_NODE = 128; +const RESOURCE_PAYLOAD_BUDGET_BYTES = 512 * 1024; +const RESOURCE_PROJECTION_RESERVE_BYTES = 16 * 1024; +const RESOURCE_MAX_STRING_LENGTH = 16 * 1024; +const RESOURCE_MAX_KEY_LENGTH = 256; +const RESOURCE_MAX_ARRAY_ITEMS = 1000; +const RESOURCE_MAX_OBJECT_KEYS = 256; +const RESOURCE_MAX_DEPTH = 32; +const RESOURCE_MAX_VALUES = 20_000; + +export class CompanionResourceService { + constructor(private readonly client: UnityReadClient) {} + + async read(uri: string): Promise { + try { + return await this.readInternal(uri); + } catch (error) { + throw boundedError(error); + } + } + + private async readInternal(uri: string): Promise { + const parsed = parseResourceUri(uri); + switch (parsed.hostname) { + case 'logs': + return this.call(uri, 'get_console_logs', { + severity: parseEnum( + parsed.searchParams.get('severity') ?? 'all', + LOG_SEVERITIES, + 'severity', + ), + limit: parseBoundedInteger( + parsed.searchParams.get('limit'), + 100, + 1, + 1000, + 'limit', + ), + }); + case 'scenes-hierarchy': { + const maxNodes = parseBoundedInteger( + parsed.searchParams.get('max_nodes'), + 500, + 1, + 2000, + 'max_nodes', + ); + const path = parsed.searchParams.get('path'); + const args = path ? { path } : {}; + const result = await this.call(uri, 'get_scene_hierarchy', args, false); + return { + uri, + payload: truncateHierarchy(result.payload, maxNodes), + }; + } + case 'gameobject': { + const target = decodeURIComponent(parsed.pathname.replace(/^\/+/, '')); + if (!target) { + throw new Error('unity://gameobject/{target} requires a target.'); + } + return this.call(uri, 'inspect_gameobject', { + target, + max_depth: 2, + max_nodes: 200, + include_components: true, + include_properties: true, + max_properties_per_component: 100, + }); + } + case 'packages': + return this.call(uri, 'package_list', { + scope: 'installed', + include_indirect: parseBoolean( + parsed.searchParams.get('include_indirect'), + true, + 'include_indirect', + ), + }); + case 'tests': { + const mode = decodeURIComponent(parsed.pathname.replace(/^\/+/, '')); + parseEnum(mode, TEST_MODES, 'mode'); + return this.call(uri, 'list_tests', { mode }); + } + default: + throw new Error(`Unknown companion resource: ${uri}`); + } + } + + private async call( + uri: string, + command: string, + args: Record, + project = true, + ): Promise { + let result: CallToolResult; + try { + result = await this.client.readTool(command, args); + } catch (error) { + throw new Error(boundedErrorMessage(`${command} failed: `, error)); + } + return { + uri, + payload: project + ? projectResourcePayload(decodeToolPayload(command, result)) + : decodeToolPayload(command, result), + }; + } +} + +export function decodeToolPayload( + command: string, + result: CallToolResult, +): Record { + if (result.isError) { + const detail = firstText(result) ?? 'Unity command returned an error.'; + throw new Error(boundedErrorMessage(`${command} failed: `, detail)); + } + + if (isRecord(result.structuredContent)) { + return result.structuredContent; + } + + const text = firstText(result); + if (text === undefined) { + throw new Error(`${command} returned no JSON payload.`); + } + try { + const parsed: unknown = JSON.parse(text); + if (!isRecord(parsed)) { + throw new Error('payload is not a JSON object'); + } + return parsed; + } catch (error) { + throw new Error( + boundedErrorMessage(`${command} returned malformed JSON: `, error), + ); + } +} + +function parseResourceUri(uri: string): URL { + let parsed: URL; + try { + parsed = new URL(uri); + } catch { + throw new Error(`Invalid companion resource URI: ${uri}`); + } + if (parsed.protocol !== 'unity:') { + throw new Error(`Unknown companion resource: ${uri}`); + } + return parsed; +} + +function parseBoundedInteger( + value: string | null, + fallback: number, + minimum: number, + maximum: number, + name: string, +): number { + if (value === null || value === '') return fallback; + if (!/^-?[0-9]+$/.test(value)) { + throw new Error(`${name} must be an integer.`); + } + const parsed = Number(value); + if (!Number.isSafeInteger(parsed)) { + return value.startsWith('-') ? minimum : maximum; + } + return Math.min(maximum, Math.max(minimum, parsed)); +} + +function parseBoolean( + value: string | null, + fallback: boolean, + name: string, +): boolean { + if (value === null || value === '') return fallback; + if (value === 'true') return true; + if (value === 'false') return false; + throw new Error(`${name} must be true or false.`); +} + +function parseEnum( + value: string, + allowed: ReadonlySet, + name: string, +): string { + if (!allowed.has(value)) { + throw new Error(`${name} must be one of: ${[...allowed].join(', ')}.`); + } + return value; +} + +function firstText(result: CallToolResult): string | undefined { + const item = result.content.find( + (content): content is Extract<(typeof result.content)[number], { type: 'text' }> => + content.type === 'text', + ); + return item?.text; +} + +interface ResourceProjectionNotice { + truncated: boolean; + payloadBudgetBytes: number; + projectedBytes: number; + payloadBudgetReached?: boolean; + depthLimitReached?: boolean; + valueLimitReached?: boolean; + collectionLimitReached?: boolean; + keyLimitReached?: boolean; + keyCollisionDetected?: boolean; + cycleDetected?: boolean; + truncatedStrings?: number; + truncatedKeys?: number; + omittedValues?: number; +} + +interface ResourceProjectionFrame { + source: Record | unknown[]; + output: Record | unknown[]; + entries: Array<[string | number, unknown]>; + nextEntry: number; + depth: number; +} + +function projectResourcePayload( + source: Record, +): Record { + const output: Record = {}; + const notice: ResourceProjectionNotice = { + truncated: false, + payloadBudgetBytes: RESOURCE_PAYLOAD_BUDGET_BYTES, + projectedBytes: 0, + }; + const seen = new WeakSet(); + seen.add(source); + const frames: ResourceProjectionFrame[] = [ + { + source, + output, + entries: boundedEntries(source, notice), + nextEntry: 0, + depth: 0, + }, + ]; + let projectedBytes = 2; + let valuesVisited = 0; + + while (frames.length > 0) { + const frame = frames[frames.length - 1]; + if (frame.nextEntry >= frame.entries.length) { + frames.pop(); + continue; + } + if (valuesVisited >= RESOURCE_MAX_VALUES) { + markResourceProjection(notice, 'valueLimitReached'); + notice.omittedValues = + (notice.omittedValues ?? 0) + + remainingFrameEntries(frames); + break; + } + + const [rawKey, sourceValue] = frame.entries[frame.nextEntry++]; + valuesVisited++; + let outputKey: string | number = rawKey; + if ( + typeof rawKey === 'string' && + rawKey.length > RESOURCE_MAX_KEY_LENGTH + ) { + outputKey = rawKey.slice(0, RESOURCE_MAX_KEY_LENGTH); + markResourceProjection(notice); + notice.truncatedKeys = (notice.truncatedKeys ?? 0) + 1; + } + const keyCost = + Array.isArray(frame.output) + ? frame.output.length > 0 + ? 1 + : 0 + : (Object.keys(frame.output).length > 0 ? 1 : 0) + + jsonBytes(String(outputKey)) + + 1; + if ( + !Array.isArray(frame.output) && + Object.prototype.hasOwnProperty.call(frame.output, String(outputKey)) + ) { + markResourceProjection(notice, 'keyCollisionDetected'); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + + let projectedValue: unknown; + let childFrame: ResourceProjectionFrame | undefined; + if (typeof sourceValue === 'string') { + const bounded = sourceValue.slice(0, RESOURCE_MAX_STRING_LENGTH); + if (bounded.length !== sourceValue.length) { + markResourceProjection(notice); + notice.truncatedStrings = (notice.truncatedStrings ?? 0) + 1; + } + projectedValue = bounded; + } else if ( + sourceValue === null || + typeof sourceValue === 'boolean' || + typeof sourceValue === 'number' + ) { + projectedValue = + typeof sourceValue === 'number' && !Number.isFinite(sourceValue) + ? null + : sourceValue; + } else if ( + Array.isArray(sourceValue) || + isRecord(sourceValue) + ) { + if (frame.depth >= RESOURCE_MAX_DEPTH) { + markResourceProjection(notice, 'depthLimitReached'); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + if (seen.has(sourceValue)) { + markResourceProjection(notice, 'cycleDetected'); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + seen.add(sourceValue); + const childOutput: Record | unknown[] = + Array.isArray(sourceValue) ? [] : {}; + projectedValue = childOutput; + childFrame = { + source: sourceValue, + output: childOutput, + entries: boundedEntries(sourceValue, notice), + nextEntry: 0, + depth: frame.depth + 1, + }; + } else { + markResourceProjection(notice); + notice.omittedValues = (notice.omittedValues ?? 0) + 1; + continue; + } + + const valueCost = + childFrame === undefined ? jsonBytes(projectedValue) : 2; + if ( + projectedBytes + keyCost + valueCost > + RESOURCE_PAYLOAD_BUDGET_BYTES - RESOURCE_PROJECTION_RESERVE_BYTES + ) { + markResourceProjection(notice, 'payloadBudgetReached'); + notice.omittedValues = + (notice.omittedValues ?? 0) + + 1 + + remainingFrameEntries(frames); + break; + } + + if (Array.isArray(frame.output)) { + frame.output.push(projectedValue); + } else { + frame.output[String(outputKey)] = projectedValue; + } + projectedBytes += keyCost + valueCost; + if (childFrame) frames.push(childFrame); + } + + output.projection = notice; + stabilizeResourceProjection(output, notice); + return output; +} + +function boundedEntries( + source: Record | unknown[], + notice: ResourceProjectionNotice, +): Array<[string | number, unknown]> { + if (Array.isArray(source)) { + const count = Math.min(source.length, RESOURCE_MAX_ARRAY_ITEMS); + if (count < source.length) { + markResourceProjection(notice, 'collectionLimitReached'); + notice.omittedValues = + (notice.omittedValues ?? 0) + source.length - count; + } + return Array.from({ length: count }, (_, index) => [index, source[index]]); + } + + const keys = Object.keys(source); + const count = Math.min(keys.length, RESOURCE_MAX_OBJECT_KEYS); + if (count < keys.length) { + markResourceProjection(notice, 'keyLimitReached'); + notice.omittedValues = (notice.omittedValues ?? 0) + keys.length - count; + } + return keys.slice(0, count).map((key) => [key, source[key]]); +} + +function remainingFrameEntries(frames: ResourceProjectionFrame[]): number { + return frames.reduce( + (total, frame) => total + frame.entries.length - frame.nextEntry, + 0, + ); +} + +function markResourceProjection( + notice: ResourceProjectionNotice, + flag?: + | 'payloadBudgetReached' + | 'depthLimitReached' + | 'valueLimitReached' + | 'collectionLimitReached' + | 'keyLimitReached' + | 'keyCollisionDetected' + | 'cycleDetected', +): void { + notice.truncated = true; + if (flag) notice[flag] = true; +} + +function stabilizeResourceProjection( + output: Record, + notice: ResourceProjectionNotice, +): void { + for (let attempt = 0; attempt < 6; attempt++) { + const bytes = jsonBytes(output); + if (notice.projectedBytes === bytes) return; + notice.projectedBytes = bytes; + } +} + +function truncateHierarchy( + hierarchy: Record, + maxNodes: number, +): Record { + const sourceRoots = Array.isArray(hierarchy.roots) ? hierarchy.roots : []; + const traversalBudget = Math.min( + 10_000, + Math.max(maxNodes * 4, maxNodes + 1024), + ); + const roots: HierarchyOutputNode[] = []; + const frames: HierarchyTraversalFrame[] = []; + const omissionOwners = new Set(); + const projectedContentBudget = Math.max( + 0, + HIERARCHY_PAYLOAD_BUDGET_BYTES - + HIERARCHY_ENVELOPE_RESERVE_BYTES - + maxNodes * HIERARCHY_DYNAMIC_MARKER_RESERVE_PER_NODE, + ); + let rootIndex = 0; + let visitedNodes = 0; + let returnedNodes = 0; + let rootsTruncated = false; + let projectedContentBytes = 0; + let payloadBudgetReached = false; + let omittedAtBudgetNodes = 0; + let omittedAtBudgetComponents = 0; + + while (visitedNodes < traversalBudget) { + let source: unknown; + let parentOutput: HierarchyOutputNode | undefined; + let inheritedOmissionOwner: HierarchyOutputNode | undefined; + let isRoot = false; + + while (frames.length > 0) { + const frame = frames[frames.length - 1]; + if (frame.nextChild < frame.children.length) { + source = frame.children[frame.nextChild++]; + parentOutput = frame.output; + inheritedOmissionOwner = frame.omissionOwner; + break; + } + frames.pop(); + } + + if (source === undefined) { + if (rootIndex >= sourceRoots.length) break; + source = sourceRoots[rootIndex++]; + isRoot = true; + } + + visitedNodes++; + if (!isRecord(source)) continue; + + let output: HierarchyOutputNode | undefined; + let omissionOwner: HierarchyOutputNode | undefined; + let countedBudgetOmission = false; + const outputEligible = + returnedNodes < maxNodes && (isRoot || parentOutput !== undefined); + if (outputEligible && !payloadBudgetReached) { + const destination = parentOutput?.children ?? roots; + const separatorBytes = destination.length > 0 ? 1 : 0; + const remainingBytes = + projectedContentBudget - projectedContentBytes - separatorBytes; + const candidate = projectHierarchyNode(source); + const fitted = fitProjectedNodeToBudget(candidate, remainingBytes); + if (fitted.output) { + output = fitted.output; + returnedNodes++; + projectedContentBytes += separatorBytes + fitted.serializedBytes; + omittedAtBudgetComponents += fitted.omittedAtBudgetComponents; + destination.push(output); + if (fitted.payloadBudgetReached) { + payloadBudgetReached = true; + } + } else { + payloadBudgetReached = true; + omittedAtBudgetNodes++; + omittedAtBudgetComponents += sourceComponentCount(source); + countedBudgetOmission = true; + } + } + if (!output) { + if ( + payloadBudgetReached && + !countedBudgetOmission && + isRecord(source) + ) { + omittedAtBudgetNodes++; + omittedAtBudgetComponents += sourceComponentCount(source); + } + omissionOwner = parentOutput ?? inheritedOmissionOwner; + if (omissionOwner) { + omissionOwner.childrenTruncated = true; + omissionOwner.omittedDescendants = + (omissionOwner.omittedDescendants ?? 0) + 1; + omissionOwners.add(omissionOwner); + } else { + rootsTruncated = true; + } + } + + const children = Array.isArray(source.children) ? source.children : []; + if (children.length > 0) { + frames.push({ + children, + nextChild: 0, + output, + omissionOwner: output ? undefined : omissionOwner, + }); + } + } + + const hasRemaining = + rootIndex < sourceRoots.length || + frames.some((frame) => frame.nextChild < frame.children.length); + const totalNodesKnown = !hasRemaining; + + if (totalNodesKnown) { + for (const owner of omissionOwners) { + owner.omittedDescendantsKnown = true; + } + } else { + if (rootIndex < sourceRoots.length) rootsTruncated = true; + for (const owner of omissionOwners) { + owner.omittedDescendantsKnown = false; + } + for (const frame of frames) { + if (frame.nextChild >= frame.children.length) continue; + const owner = frame.output ?? frame.omissionOwner; + if (owner) { + owner.childrenTruncated = true; + owner.omittedDescendantsKnown = false; + } else { + rootsTruncated = true; + } + } + } + + const truncation: Record = totalNodesKnown + ? { + truncated: returnedNodes < visitedNodes || payloadBudgetReached, + maxNodes, + traversalBudget, + visitedNodes, + returnedNodes, + totalNodesKnown: true, + totalNodes: visitedNodes, + omittedNodes: visitedNodes - returnedNodes, + rootsTruncated, + } + : { + truncated: true, + maxNodes, + traversalBudget, + visitedNodes, + returnedNodes, + totalNodesKnown: false, + totalNodesAtLeast: visitedNodes + 1, + omittedNodesAtLeast: visitedNodes + 1 - returnedNodes, + rootsTruncated, + }; + + Object.assign(truncation, { + payloadBudgetReached, + payloadBudgetBytes: HIERARCHY_PAYLOAD_BUDGET_BYTES, + projectedBytes: 0, + omittedAtBudgetNodes, + omittedAtBudgetComponents, + }); + + const result: Record = { + ...projectHierarchyMetadata(hierarchy), + roots, + truncation, + }; + stabilizeProjectedByteCount(result, truncation); + return result; +} + +interface HierarchyOutputNode extends Record { + children: HierarchyOutputNode[]; + childrenTruncated: boolean; + omittedDescendants?: number; + omittedDescendantsKnown?: boolean; +} + +interface HierarchyTraversalFrame { + children: unknown[]; + nextChild: number; + output?: HierarchyOutputNode; + omissionOwner?: HierarchyOutputNode; +} + +interface FittedHierarchyNode { + output?: HierarchyOutputNode; + serializedBytes: number; + payloadBudgetReached: boolean; + omittedAtBudgetComponents: number; +} + +function fitProjectedNodeToBudget( + candidate: HierarchyOutputNode, + maxBytes: number, +): FittedHierarchyNode { + let serializedBytes = jsonBytes(candidate); + if (serializedBytes <= maxBytes) { + return { + output: candidate, + serializedBytes, + payloadBudgetReached: false, + omittedAtBudgetComponents: 0, + }; + } + + const components = Array.isArray(candidate.components) + ? candidate.components as string[] + : undefined; + if (!components || components.length === 0) { + return { + serializedBytes, + payloadBudgetReached: true, + omittedAtBudgetComponents: 0, + }; + } + + const initialReturnedCount = components.length; + while (components.length > 0) { + components.pop(); + markComponentsOmittedAtBudget( + candidate, + initialReturnedCount - components.length, + initialReturnedCount, + ); + serializedBytes = jsonBytes(candidate); + if (serializedBytes <= maxBytes) { + return { + output: candidate, + serializedBytes, + payloadBudgetReached: true, + omittedAtBudgetComponents: + initialReturnedCount - components.length, + }; + } + } + + return { + serializedBytes, + payloadBudgetReached: true, + omittedAtBudgetComponents: initialReturnedCount, + }; +} + +function markComponentsOmittedAtBudget( + node: HierarchyOutputNode, + omittedAtBudgetCount: number, + initialReturnedCount: number, +): void { + const projection = isRecord(node.projection) + ? node.projection as ProjectionNotice + : {}; + node.projection = projection; + const existing = projection.components; + const returnedCount = initialReturnedCount - omittedAtBudgetCount; + projection.components = { + sourceCount: existing?.sourceCount ?? initialReturnedCount, + scannedCount: existing?.scannedCount ?? initialReturnedCount, + returnedCount, + omittedCount: + (existing?.sourceCount ?? initialReturnedCount) - returnedCount, + invalidScanned: existing?.invalidScanned ?? 0, + namesTruncated: existing?.namesTruncated ?? 0, + scanTruncated: existing?.scanTruncated ?? false, + payloadBudgetReached: true, + omittedAtBudgetCount, + }; +} + +function sourceComponentCount(source: Record): number { + return Array.isArray(source.components) ? source.components.length : 0; +} + +function stabilizeProjectedByteCount( + result: Record, + truncation: Record, +): void { + for (let attempt = 0; attempt < 4; attempt++) { + const projectedBytes = jsonBytes(result); + if (truncation.projectedBytes === projectedBytes) return; + truncation.projectedBytes = projectedBytes; + } +} + +function jsonBytes(value: unknown): number { + return Buffer.byteLength(JSON.stringify(value)); +} + +function projectHierarchyNode( + source: Record, +): HierarchyOutputNode { + const output: HierarchyOutputNode = { + children: [], + childrenTruncated: source.childrenTruncated === true, + }; + const projection: ProjectionNotice = {}; + copyBoundedString( + source, + output, + projection, + 'name', + NODE_NAME_MAX_LENGTH, + ); + copyBoundedString( + source, + output, + projection, + 'hierarchyPath', + HIERARCHY_PATH_MAX_LENGTH, + ); + copyBoundedInstanceId(source, output, projection); + copyBoolean(source, output, projection, 'activeSelf'); + copyBoundedComponents(source, output, projection); + if (hasProjectionNotice(projection)) { + output.projection = projection; + } + return output; +} + +function projectHierarchyMetadata( + hierarchy: Record, +): Record { + const metadata: Record = {}; + const projection: ProjectionNotice = {}; + copyBoundedString( + hierarchy, + metadata, + projection, + 'sceneName', + SCENE_NAME_MAX_LENGTH, + ); + copyBoundedString( + hierarchy, + metadata, + projection, + 'scenePath', + SCENE_PATH_MAX_LENGTH, + ); + copyBoolean(hierarchy, metadata, projection, 'isDirty'); + copyBoolean(hierarchy, metadata, projection, 'isActive'); + if (hasProjectionNotice(projection)) { + metadata.metadataProjection = projection; + } + return metadata; +} + +interface ProjectionNotice { + truncatedStringCount?: number; + truncatedStrings?: Record< + string, + { originalLength: number; returnedLength: number } + >; + omittedKnownFieldCount?: number; + omittedKnownFields?: string[]; + components?: { + sourceCount: number; + scannedCount: number; + returnedCount: number; + omittedCount: number; + invalidScanned: number; + namesTruncated: number; + scanTruncated: boolean; + payloadBudgetReached?: boolean; + omittedAtBudgetCount?: number; + }; +} + +function copyBoundedString( + source: Record, + output: Record, + projection: ProjectionNotice, + field: string, + maxLength: number, +): void { + if (!(field in source)) return; + const value = source[field]; + if (typeof value !== 'string') { + markOmittedField(projection, field); + return; + } + output[field] = boundedString(value, maxLength, projection, field); +} + +function copyBoundedInstanceId( + source: Record, + output: Record, + projection: ProjectionNotice, +): void { + if (!('instanceId' in source)) return; + const value = source.instanceId; + if (typeof value === 'number' && Number.isSafeInteger(value)) { + output.instanceId = value; + return; + } + if (typeof value === 'string') { + output.instanceId = boundedString( + value, + INSTANCE_ID_MAX_LENGTH, + projection, + 'instanceId', + ); + return; + } + markOmittedField(projection, 'instanceId'); +} + +function copyBoolean( + source: Record, + output: Record, + projection: ProjectionNotice, + field: string, +): void { + if (!(field in source)) return; + const value = source[field]; + if (typeof value === 'boolean') { + output[field] = value; + } else { + markOmittedField(projection, field); + } +} + +function copyBoundedComponents( + source: Record, + output: Record, + projection: ProjectionNotice, +): void { + if (!('components' in source)) return; + if (!Array.isArray(source.components)) { + markOmittedField(projection, 'components'); + return; + } + + const sourceComponents = source.components; + const components: string[] = []; + let scannedCount = 0; + let invalidScanned = 0; + let namesTruncated = 0; + const scanCount = Math.min(sourceComponents.length, COMPONENT_SCAN_LIMIT); + while ( + scannedCount < scanCount && + components.length < COMPONENT_MAX_COUNT + ) { + const candidate = sourceComponents[scannedCount++]; + const name = + typeof candidate === 'string' + ? candidate + : isRecord(candidate) && typeof candidate.name === 'string' + ? candidate.name + : undefined; + if (name === undefined) { + invalidScanned++; + continue; + } + if (name.length > COMPONENT_NAME_MAX_LENGTH) namesTruncated++; + components.push(name.slice(0, COMPONENT_NAME_MAX_LENGTH)); + } + output.components = components; + + const omittedCount = sourceComponents.length - components.length; + if (omittedCount > 0 || namesTruncated > 0 || invalidScanned > 0) { + projection.components = { + sourceCount: sourceComponents.length, + scannedCount, + returnedCount: components.length, + omittedCount, + invalidScanned, + namesTruncated, + scanTruncated: scannedCount < sourceComponents.length, + }; + } +} + +function boundedString( + value: string, + maxLength: number, + projection: ProjectionNotice, + field: string, +): string { + if (value.length <= maxLength) return value; + projection.truncatedStringCount = + (projection.truncatedStringCount ?? 0) + 1; + projection.truncatedStrings ??= {}; + projection.truncatedStrings[field] = { + originalLength: value.length, + returnedLength: maxLength, + }; + return value.slice(0, maxLength); +} + +function markOmittedField( + projection: ProjectionNotice, + field: string, +): void { + projection.omittedKnownFieldCount = + (projection.omittedKnownFieldCount ?? 0) + 1; + projection.omittedKnownFields ??= []; + projection.omittedKnownFields.push(field); +} + +function hasProjectionNotice(projection: ProjectionNotice): boolean { + return ( + projection.truncatedStrings !== undefined || + projection.omittedKnownFields !== undefined || + projection.components !== undefined + ); +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} diff --git a/Server~/src/resources/dashboardResource.ts b/Server~/src/resources/dashboardResource.ts new file mode 100644 index 00000000..ca4b5936 --- /dev/null +++ b/Server~/src/resources/dashboardResource.ts @@ -0,0 +1,23 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps/server'; + +export const DASHBOARD_URI = 'ui://unity-dashboard'; + +export function readDashboardHtml(): { text: string; mimeType: string } { + const moduleDirectory = path.dirname(fileURLToPath(import.meta.url)); + const candidates = [ + path.join(moduleDirectory, 'ui', 'unity-dashboard.html'), + path.join(moduleDirectory, '..', 'ui', 'unity-dashboard.html'), + path.join(moduleDirectory, '..', '..', 'src', 'ui', 'unity-dashboard.html'), + ]; + const dashboardPath = candidates.find((candidate) => fs.existsSync(candidate)); + if (!dashboardPath) { + throw new Error(`Unity dashboard HTML is missing. Checked: ${candidates.join(', ')}`); + } + return { + text: fs.readFileSync(dashboardPath, 'utf8'), + mimeType: RESOURCE_MIME_TYPE, + }; +} diff --git a/Server~/src/resources/getAssetsResource.ts b/Server~/src/resources/getAssetsResource.ts deleted file mode 100644 index 614dd875..00000000 --- a/Server~/src/resources/getAssetsResource.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the resource -const resourceName = 'get_assets'; -const resourceUri = 'unity://assets'; -const resourceMimeType = 'application/json'; - -/** - * Creates and registers the Assets resource with the MCP server - * This resource provides access to assets in the Unity project - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetAssetsResource(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering resource: ${resourceName}`); - - // Register this resource with the MCP server - server.resource( - resourceName, - resourceUri, - { - description: 'Retrieve assets from the Unity Asset Database', - mimeType: resourceMimeType - }, - async () => { - try { - return await resourceHandler(mcpUnity); - } catch (error) { - logger.error(`Error handling resource ${resourceName}: ${error}`); - throw error; - } - } - ); -} - -/** - * Handles requests for asset information from Unity's Asset Database - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @returns A promise that resolves to the assets data - * @throws McpUnityError if the request to Unity fails - */ -async function resourceHandler(mcpUnity: McpUnity): Promise { - // Since we're using a non-templated ResourceDefinition, we need to handle all assets without parameters - const response = await mcpUnity.sendRequest({ - method: resourceName, - params: {} - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.RESOURCE_FETCH, - response.message || 'Failed to fetch assets from Unity Asset Database' - ); - } - - // Transform the data into a structured format - const assets = response.assets || []; - - const assetsData = { - assets: assets.map((asset: any) => ({ - name: asset.name, - filename: asset.filename, - path: asset.path, - type: asset.type, - extension: asset.extension, - guid: asset.guid, - size: asset.size - })) - }; - - return { - contents: [ - { - uri: resourceUri, - mimeType: resourceMimeType, - text: JSON.stringify(assetsData, null, 2) - } - ] - }; -} diff --git a/Server~/src/resources/getConsoleLogsResource.ts b/Server~/src/resources/getConsoleLogsResource.ts deleted file mode 100644 index 9cbd019d..00000000 --- a/Server~/src/resources/getConsoleLogsResource.ts +++ /dev/null @@ -1,123 +0,0 @@ -import { Logger } from '../utils/logger.js'; -import { ResourceTemplate, McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { Variables } from '@modelcontextprotocol/sdk/shared/uriTemplate.js'; - -// Constants for the resource -const resourceName = 'get_console_logs'; -const resourceMimeType = 'application/json'; -const resourceUri = 'unity://logs/{logType}?offset={offset}&limit={limit}&includeStackTrace={includeStackTrace}'; -const resourceTemplate = new ResourceTemplate(resourceUri, { - list: () => listLogTypes(resourceMimeType) -}); - -function listLogTypes(resourceMimeType: string) { - return { - resources: [ - { - uri: `unity://logs/?offset=0&limit=50&includeStackTrace=true`, - name: "All logs", - description: "All Unity console logs (newest first). ⚠️ Set includeStackTrace=false to save 80-90% tokens. Use limit=50 to avoid token limits.", - mimeType: resourceMimeType - }, - { - uri: `unity://logs/error?offset=0&limit=20&includeStackTrace=true`, - name: "Error logs", - description: "Error logs only. ⚠️ Start with includeStackTrace=false for quick overview, then true only if debugging specific errors.", - mimeType: resourceMimeType - }, - { - uri: `unity://logs/warning?offset=0&limit=30&includeStackTrace=true`, - name: "Warning logs", - description: "Warning logs only. ⚠️ Use includeStackTrace=false by default to save tokens.", - mimeType: resourceMimeType - }, - { - uri: `unity://logs/info?offset=0&limit=25&includeStackTrace=false`, - name: "Info logs", - description: "Info logs only. Stack traces excluded by default to minimize tokens.", - mimeType: resourceMimeType - } - ] - }; -} - -/** - * Registers the get_console_logs resource with the MCP server - */ -export function registerGetConsoleLogsResource(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering resource: ${resourceName}`); - - server.resource( - resourceName, - resourceTemplate, - { - description: 'Retrieve Unity console logs by type with pagination support. See individual log type descriptions for optimal settings.', - mimeType: resourceMimeType - }, - async (uri, variables) => { - try { - return await resourceHandler(mcpUnity, uri, variables, logger); - } catch (error) { - logger.error(`Error handling resource ${resourceName}: ${error}`); - throw error; - } - } - ); -} - -/** - * Handles requests for Unity console logs by log type - */ -async function resourceHandler(mcpUnity: McpUnity, uri: URL, variables: Variables, logger: Logger): Promise { - // Extract and convert the parameter from the template variables - let logType = variables["logType"] ? decodeURIComponent(variables["logType"] as string) : undefined; - if (logType === '') logType = undefined; - - // Extract pagination parameters with validation - const offset = variables["offset"] ? parseInt(variables["offset"] as string, 10) : 0; - const limit = variables["limit"] ? parseInt(variables["limit"] as string, 10) : 100; - - // Extract includeStackTrace parameter - let includeStackTrace = true; // Default to true for backward compatibility - if (variables["includeStackTrace"] !== undefined) { - const value = variables["includeStackTrace"] as string; - includeStackTrace = value === 'true' || value === '1' || value === 'yes'; - } - - // Validate pagination parameters - if (isNaN(offset) || offset < 0) { - throw new McpUnityError(ErrorType.VALIDATION, 'Invalid offset parameter: must be a non-negative integer'); - } - if (isNaN(limit) || limit <= 0) { - throw new McpUnityError(ErrorType.VALIDATION, 'Invalid limit parameter: must be a positive integer'); - } - - // Send request to Unity - const response = await mcpUnity.sendRequest({ - method: resourceName, - params: { - logType: logType, - offset: offset, - limit: limit, - includeStackTrace: includeStackTrace - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.RESOURCE_FETCH, - response.message || 'Failed to fetch logs from Unity' - ); - } - - return { - contents: [{ - uri: `unity://logs/${logType ?? ''}?offset=${offset}&limit=${limit}&includeStackTrace=${includeStackTrace}`, - mimeType: resourceMimeType, - text: JSON.stringify(response, null, 2) - }] - }; -} diff --git a/Server~/src/resources/getGameObjectResource.ts b/Server~/src/resources/getGameObjectResource.ts deleted file mode 100644 index 48f14c55..00000000 --- a/Server~/src/resources/getGameObjectResource.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { Logger } from '../utils/logger.js'; -import { ResourceTemplate, McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { Variables } from '@modelcontextprotocol/sdk/shared/uriTemplate.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { resourceName as hierarchyResourceName } from './getScenesHierarchyResource.js'; - -// Constants for the resource -const resourceName = 'get_gameobject'; -const resourceUri = 'unity://gameobject/{idOrName}'; -const resourceMimeType = 'application/json'; - -/** - * Creates and registers the GameObject resource with the MCP server - * This resource provides access to GameObjects in Unity scenes - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetGameObjectResource(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - // Create a resource template with the MCP SDK - const resourceTemplate = new ResourceTemplate( - resourceUri, - { - // This list method is commented because is calling getHierarchyResource every second to the MCP client in the current format. - // TODO: Find a new way to implement this so that it doesn't request the list of game objects so often - list: undefined//async () => listGameObjects(mcpUnity, logger, resourceMimeType) - } - ); - logger.info(`Registering resource: ${resourceName}`); - - // Register this resource with the MCP server - server.resource( - resourceName, - resourceTemplate, - { - description: 'Retrieve a GameObject by instance ID, name, or hierarchical path (e.g., "Parent/Child/MyObject")', - mimeType: resourceMimeType - }, - async (uri, variables) => { - try { - return await resourceHandler(mcpUnity, uri, variables, logger); - } catch (error) { - logger.error(`Error handling resource ${resourceName}: ${error}`); - throw error; - } - } - ); -} -/** - * Handles requests for GameObject information from Unity - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param uri The requested resource URI - * @param variables Variables extracted from the URI template - * @param logger The logger instance for diagnostic information - * @returns A promise that resolves to the GameObject data - * @throws McpUnityError if the request to Unity fails - */ -async function resourceHandler(mcpUnity: McpUnity, uri: URL, variables: Variables, logger: Logger): Promise { - // Extract and convert the parameter from the template variables - const idOrName = decodeURIComponent(variables["idOrName"] as string); - - // Send request to Unity - const response = await mcpUnity.sendRequest({ - method: resourceName, - params: { - idOrName: idOrName - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.RESOURCE_FETCH, - response.message || 'Failed to fetch GameObject from Unity' - ); - } - - return { - contents: [{ - uri: `unity://gameobject/${idOrName}`, - mimeType: resourceMimeType, - text: JSON.stringify(response, null, 2) - }] - }; -} - -/** - * Get a list of all GameObjects in the scene - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance - * @param resourceMimeType The MIME type for the resource - * @returns A promise that resolves to a list of GameObject resources - */ -async function listGameObjects(mcpUnity: McpUnity, logger: Logger, resourceMimeType: string) { - const hierarchyResponse = await mcpUnity.sendRequest({ - method: hierarchyResourceName, - params: {} - }); - - if (!hierarchyResponse.success) { - logger.error(`Failed to fetch hierarchy: ${hierarchyResponse.message}`); - throw new Error(hierarchyResponse.message || 'Failed to fetch hierarchy'); - } - - // Process the hierarchy to create a list of GameObject references - const gameObjects = processHierarchyToGameObjectList(hierarchyResponse.hierarchy || []); - - logger.info(`[getGameObjectResource] Fetched hierarchy with ${gameObjects.length} GameObjects ${hierarchyResponse.hierarchy}`); - - // Create resources array with both instance ID and path URIs - const resources: Array<{ - uri: string; - name: string; - description: string; - mimeType: string; - }> = []; - - // Add resources for each GameObject - gameObjects.forEach(obj => { - // Add resource with instance ID URI - resources.push({ - uri: `unity://gameobject/${obj.instanceId}`, - name: obj.name, - description: `GameObject with instance ID ${obj.instanceId} at path: ${obj.path}`, - mimeType: resourceMimeType - }); - - // Add resource with path URI if path exists - if (obj.path) { - resources.push({ - uri: `unity://gameobject/${encodeURIComponent(obj.path)}`, - name: obj.name, - description: `GameObject with instance ID ${obj.instanceId} at path: ${obj.path}`, - mimeType: resourceMimeType - }); - } - }); - - return { resources }; -} - -/** - * Process the hierarchy data to create a list of GameObject references - * @param hierarchyData The hierarchy data from Unity - * @returns An array of GameObject references with their instance IDs and paths - */ -function processHierarchyToGameObjectList(hierarchyData: any): any[] { - const gameObjects: any[] = []; - - // Helper function to traverse the hierarchy recursively - function traverseHierarchy(node: any, path: string = ''): void { - if (!node) return; - - // Current path is parent path + node name - const currentPath = path ? `${path}/${node.name}` : node.name; - - // Add this GameObject to the list - gameObjects.push({ - instanceId: node.instanceId, - name: node.name, - path: currentPath, - active: node.active, - uri: `unity://gameobject/${node.instanceId}` - }); - - // Process children recursively - if (node.children && Array.isArray(node.children)) { - for (const child of node.children) { - traverseHierarchy(child, currentPath); - } - } - } - - // Start traversal with each root GameObject - if (Array.isArray(hierarchyData)) { - for (const rootNode of hierarchyData) { - traverseHierarchy(rootNode); - } - } - - return gameObjects; -} diff --git a/Server~/src/resources/getMenuItemResource.ts b/Server~/src/resources/getMenuItemResource.ts deleted file mode 100644 index 3ab4a80e..00000000 --- a/Server~/src/resources/getMenuItemResource.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the resource -const resourceName = 'get_menu_items'; -const resourceUri = 'unity://menu-items'; -const resourceMimeType = 'application/json'; - -/** - * Creates and registers the Menu Items resource with the MCP server - * This resource provides access to the Unity menu items - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetMenuItemsResource(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering resource: ${resourceName}`); - - // Register this resource with the MCP server - server.resource( - resourceName, - resourceUri, - { - description: 'List of available menu items in Unity to execute', - mimeType: resourceMimeType - }, - async () => { - try { - return await resourceHandler(mcpUnity); - } catch (error) { - logger.error(`Error handling resource ${resourceName}: ${error}`); - throw error; - } - } - ); -} - -/** - * Handles requests for menu items information from Unity - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @returns A promise that resolves to the menu items data - * @throws McpUnityError if the request to Unity fails - */ -async function resourceHandler(mcpUnity: McpUnity): Promise { - const response = await mcpUnity.sendRequest({ - method: resourceName, - params: {} - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.RESOURCE_FETCH, - response.message || 'Failed to fetch menu items from Unity' - ); - } - - return { - contents: [{ - uri: resourceUri, - mimeType: resourceMimeType, - text: JSON.stringify(response.menuItems, null, 2) - }] - }; -} diff --git a/Server~/src/resources/getPackagesResource.ts b/Server~/src/resources/getPackagesResource.ts deleted file mode 100644 index c3c5fb25..00000000 --- a/Server~/src/resources/getPackagesResource.ts +++ /dev/null @@ -1,98 +0,0 @@ -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the resource -const resourceName = 'get_packages'; -const resourceUri = 'unity://packages'; -const resourceMimeType = 'application/json'; - -/** - * Creates and registers the Packages resource with the MCP server - * This resource provides access to the Unity Package Manager packages - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetPackagesResource(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering resource: ${resourceName}`); - - // Register this resource with the MCP server - server.resource( - resourceName, - resourceUri, - { - description: 'Retrieve all packages from the Unity Package Manager', - mimeType: resourceMimeType - }, - async () => { - try { - return await resourceHandler(mcpUnity); - } catch (error) { - logger.error(`Error handling resource ${resourceName}: ${error}`); - throw error; - } - } - ); -} - -/** - * Handles requests for package information from Unity Package Manager - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @returns A promise that resolves to the packages data - * @throws McpUnityError if the request to Unity fails - */ -async function resourceHandler(mcpUnity: McpUnity): Promise { - const response = await mcpUnity.sendRequest({ - method: resourceName, - params: {} - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.RESOURCE_FETCH, - response.message || 'Failed to fetch packages from Unity Package Manager' - ); - } - - // Transform the data into a structured format - const projectPackages = response.projectPackages || []; - const registryPackages = response.registryPackages || []; - - const packagesData = { - projectPackages: projectPackages.map((pkg: any) => ({ - name: pkg.name, - displayName: pkg.displayName, - version: pkg.version, - description: pkg.description, - category: pkg.category, - source: pkg.source, - state: pkg.state, - author: pkg.author - })), - registryPackages: registryPackages.map((pkg: any) => ({ - name: pkg.name, - displayName: pkg.displayName, - version: pkg.version, - description: pkg.description, - category: pkg.category, - source: pkg.source, - state: pkg.state, - author: pkg.author - })) - }; - - return { - contents: [ - { - uri: resourceUri, - text: JSON.stringify(packagesData, null, 2), - mimeType: resourceMimeType - } - ] - }; -} diff --git a/Server~/src/resources/getScenesHierarchyResource.ts b/Server~/src/resources/getScenesHierarchyResource.ts deleted file mode 100644 index b61bcafc..00000000 --- a/Server~/src/resources/getScenesHierarchyResource.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the resource -export const resourceName = 'get_scenes_hierarchy'; -export const resourceUri = 'unity://scenes_hierarchy'; -export const resourceMimeType = 'application/json'; - -/** - * Creates and registers the Scenes Hierarchy resource with the MCP server - * This resource provides access to the Unity scene hierarchy - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetHierarchyResource(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering resource: ${resourceName}`); - - // Register this resource with the MCP server - server.resource( - resourceName, - resourceUri, - { - description: 'Retrieve all GameObjects in the Unity loaded scenes with their active state (scenes hierarchy)', - mimeType: resourceMimeType - }, - async () => { - try { - return await resourceHandler(mcpUnity); - } catch (error) { - logger.error(`Error handling resource ${resourceName}: ${error}`); - throw error; - } - } - ); -} - -/** - * Handles requests for hierarchy information from Unity - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @returns A promise that resolves to the hierarchy data - * @throws McpUnityError if the request to Unity fails - */ -async function resourceHandler(mcpUnity: McpUnity): Promise { - const response = await mcpUnity.sendRequest({ - method: resourceName, - params: {} - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.RESOURCE_FETCH, - response.message || 'Failed to fetch hierarchy from Unity' - ); - } - - return { - contents: [{ - uri: resourceUri, - mimeType: resourceMimeType, - text: JSON.stringify(response.hierarchy, null, 2) - }] - }; -} diff --git a/Server~/src/resources/getTestsResource.ts b/Server~/src/resources/getTestsResource.ts deleted file mode 100644 index f46b06e5..00000000 --- a/Server~/src/resources/getTestsResource.ts +++ /dev/null @@ -1,119 +0,0 @@ -import { Logger } from '../utils/logger.js'; -import { ResourceTemplate, McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { Variables } from '@modelcontextprotocol/sdk/shared/uriTemplate.js'; - -// Constants for the resource -const resourceName = 'get_tests'; -const resourceMimeType = 'application/json'; -const resourceUri = 'unity://tests/{testMode}'; -const resourceTemplate = new ResourceTemplate(resourceUri, { - list: () => listTestModes(resourceMimeType) -}); - -export interface TestItem { - name: string; - fullName: string; - path: string; - testMode: string; - runState: string; -} - -/** - * Get a list of all test modes (EditMode and PlayMode) - * @param resourceMimeType The MIME type for the resource - * @returns A list of resources for each test mode - */ -function listTestModes(resourceMimeType: string) { - return { - resources: [ - { - uri: `unity://tests/EditMode`, - name: "List only 'EditMode' tests", - description: "List only 'EditMode' tests from Unity's test runner", - mimeType: resourceMimeType - }, - { - uri: `unity://tests/PlayMode`, - name: "List only 'PlayMode' tests", - description: "List only 'PlayMode' tests from Unity's test runner", - mimeType: resourceMimeType - }, - { - uri: `unity://tests/`, - name: "List all tests", - description: "List of all tests in Unity's test runner, this includes PlayMode and EditMode tests", - mimeType: resourceMimeType - } - ] - }; -} - -/** - * Creates and registers the Tests resource with the MCP server - * This resource provides access to Unity's Test Runner tests - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetTestsResource(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering resource: ${resourceName}`); - - // Register this resource with the MCP server - server.resource( - resourceName, - resourceTemplate, - { - description: 'Retrieve tests from Unity\'s Test Runner', - mimeType: resourceMimeType - }, - async (uri, variables) => { - try { - return await resourceHandler(mcpUnity, uri, variables); - } catch (error) { - logger.error(`Error handling resource ${resourceName}: ${error}`); - throw error; - } - } - ); -} - -/** - * Handles requests for test information from Unity's Test Runner - * Retrieves tests filtered by test mode (EditMode or PlayMode) - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param uri The requested resource URI - * @param variables Variables extracted from the URI template - * @returns A promise that resolves to the test results - * @throws McpUnityError if the request to Unity fails - */ -async function resourceHandler(mcpUnity: McpUnity, uri: URL, variables: Variables): Promise { - // Convert the new handler signature to work with our existing code - const testMode = variables["testMode"]; - - const response = await mcpUnity.sendRequest({ - method: resourceName, - params: { - testMode - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.RESOURCE_FETCH, - response.message || `Failed to fetch the ${testMode} tests from Unity` - ); - } - - return { - contents: [{ - uri: `unity://tests/${testMode}`, - mimeType: resourceMimeType, - text: JSON.stringify(response, null, 2) - }] - }; -} \ No newline at end of file diff --git a/Server~/src/resources/unityDashboardAppResource.ts b/Server~/src/resources/unityDashboardAppResource.ts deleted file mode 100644 index f3a3a353..00000000 --- a/Server~/src/resources/unityDashboardAppResource.ts +++ /dev/null @@ -1,107 +0,0 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { ReadResourceResult } from '@modelcontextprotocol/sdk/types.js'; -import { registerAppResource, RESOURCE_MIME_TYPE } from '@modelcontextprotocol/ext-apps/server'; -import { Logger } from '../utils/logger.js'; - -const resourceName = 'unity_dashboard_app'; -const appResourceUri = 'ui://unity-dashboard'; -const legacyResourceName = 'unity_dashboard_app_legacy'; -const legacyResourceUri = 'unity://ui/dashboard'; -const resourceMimeType = RESOURCE_MIME_TYPE; - -export function registerUnityDashboardAppResource(server: McpServer, logger: Logger) { - logger.info(`Registering resource: ${resourceName}`); - - registerAppResource( - server, - resourceName, - appResourceUri, - { - description: 'Unity dashboard MCP App UI', - }, - async () => { - try { - return readDashboardHtml(); - } catch (error) { - logger.error(`Error reading dashboard HTML: ${error}`); - throw error; - } - } - ); - - // Legacy URI for compatibility with older hosts / docs that expect unity://ui/dashboard - server.resource( - legacyResourceName, - legacyResourceUri, - { - description: 'Unity dashboard MCP App UI (legacy resource URI)', - mimeType: resourceMimeType - }, - async () => { - try { - return readDashboardHtml(legacyResourceUri); - } catch (error) { - logger.error(`Error reading dashboard HTML (legacy uri): ${error}`); - throw error; - } - } - ); -} - -function readDashboardHtml(uriOverride?: string): ReadResourceResult { - const { text } = readUnityDashboardHtml(); - const uri = uriOverride ?? appResourceUri; - - return { - contents: [ - { - uri, - mimeType: resourceMimeType, - text, - _meta: { - // For hosts that still look for legacy view hints. - view: 'mcp-app', - ui: { - prefersBorder: true, - } - } - } - ] - }; -} - -export function readUnityDashboardHtml(): { text: string; mimeType: string } { - const htmlPath = resolveDashboardPath(); - const text = fs.readFileSync(htmlPath, 'utf8'); - - return { text, mimeType: resourceMimeType }; -} - -function resolveDashboardPath(): string { - // IMPORTANT: do not use process.cwd() here. - // VS Code runs MCP servers with the CWD of the *client workspace*, which may be - // unrelated to the server's install location. - const moduleDir = path.dirname(fileURLToPath(import.meta.url)); - - const candidates = [ - // Works when running TS directly (src/resources -> src/ui) - // and when running built JS with copied assets (build/resources -> build/ui) - path.join(moduleDir, '..', 'ui', 'unity-dashboard.html'), - - // Fallback for dev repos where build output exists but UI wasn't copied. - path.join(moduleDir, '..', '..', 'src', 'ui', 'unity-dashboard.html'), - ]; - - for (const candidate of candidates) { - if (fs.existsSync(candidate)) { - return candidate; - } - } - - throw new Error( - `Unity dashboard UI file is missing. Checked: ${candidates.join(', ')}` - ); -} diff --git a/Server~/src/tools/addAssetToSceneTool.ts b/Server~/src/tools/addAssetToSceneTool.ts deleted file mode 100644 index 4a2e4e88..00000000 --- a/Server~/src/tools/addAssetToSceneTool.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; - -// Constants for the tool -const toolName = 'add_asset_to_scene'; -const toolDescription = 'Adds an asset from the AssetDatabase to the Unity scene'; - -// Parameter schema for the tool -const paramsSchema = z.object({ - assetPath: z.string().optional().describe('The path of the asset in the AssetDatabase'), - guid: z.string().optional().describe('The GUID of the asset'), - position: z.object({ - x: z.number().default(0).describe('X position in the scene'), - y: z.number().default(0).describe('Y position in the scene'), - z: z.number().default(0).describe('Z position in the scene') - }).optional().describe('Position in the scene (defaults to Vector3.zero)'), - parentPath: z.string().optional().describe('The path of the parent GameObject in the hierarchy'), - parentId: z.number().optional().describe('The instance ID of the parent GameObject') -}); - -/** - * Creates and registers the AddAssetToScene tool with the MCP server - * - * @param server The MCP server to register the tool with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerAddAssetToSceneTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handler function for the AddAssetToScene tool - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The validated parameters for the tool - * @param logger The logger instance for diagnostic information - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if validation fails or the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any) { - if (!params.assetPath && !params.guid) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'assetPath' or 'guid' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to add asset to scene` - ); - } - - return { - content: [{ - type: response.type, - text: response.message || `Successfully added asset to scene` - }] - }; -} diff --git a/Server~/src/tools/addPackageTool.ts b/Server~/src/tools/addPackageTool.ts deleted file mode 100644 index bc7d6de1..00000000 --- a/Server~/src/tools/addPackageTool.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'add_package'; -const toolDescription = 'Adds packages into the Unity Package Manager'; -const paramsSchema = z.object({ - source: z.string().describe('The source to use (registry, github, or disk) to add the package'), - packageName: z.string().optional().describe('The package name to add from Unity registry (e.g. com.unity.textmeshpro)'), - version: z.string().optional().describe('The version to use for registry packages (optional)'), - repositoryUrl: z.string().optional().describe('The GitHub repository URL (e.g. https://github.com/username/repo.git)'), - branch: z.string().optional().describe('The branch to use for GitHub packages (optional)'), - path: z.string().optional().describe('The path to use (folder path for disk method or subfolder for GitHub)') -}); - -/** - * Creates and registers the Add Package tool with the MCP server - * This tool allows adding packages to the Unity Package Manager - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerAddPackageTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles add package tool requests - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - const { source, packageName, version, repositoryUrl, branch, path } = params; - - // Validate required parameters based on method - if (source === 'registry' && !packageName) { - throw new McpUnityError( - ErrorType.VALIDATION, - 'Required parameter "packageName" not provided for registry source' - ); - } else if (source === 'github' && !repositoryUrl) { - throw new McpUnityError( - ErrorType.VALIDATION, - 'Required parameter "repositoryUrl" not provided for github source' - ); - } else if (source === 'disk' && !path) { - throw new McpUnityError( - ErrorType.VALIDATION, - 'Required parameter "path" not provided for disk source' - ); - } - - // Send to Unity - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to manage package with source: ${source}` - ); - } - - return { - content: [{ - type: response.type, - text: response.message - }] - }; -} diff --git a/Server~/src/tools/batchExecuteTool.ts b/Server~/src/tools/batchExecuteTool.ts deleted file mode 100644 index af36b0e7..00000000 --- a/Server~/src/tools/batchExecuteTool.ts +++ /dev/null @@ -1,175 +0,0 @@ -import * as z from 'zod'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -const toolName = 'batch_execute'; -const toolDescription = `Executes multiple tool operations in a single batch request. -Reduces network round-trips and enables atomic operations with rollback support. -Performance improvement: 10-100x for repetitive operations.`; - -const operationSchema = z.object({ - tool: z.string().describe('The name of the tool to execute'), - params: z.record(z.any()).optional().default({}).describe('Parameters to pass to the tool'), - id: z.string().optional().describe('Optional identifier for this operation (for tracking in results)') -}); - -const paramsSchema = z.object({ - operations: z.array(operationSchema) - .min(1, 'At least one operation is required') - .max(100, 'Maximum of 100 operations allowed per batch') - .describe('Array of operations to execute sequentially'), - stopOnError: z.boolean() - .default(true) - .describe('If true, stops execution on the first error. Default: true'), - atomic: z.boolean() - .default(false) - .describe('If true, rolls back all operations if any fails (uses Unity Undo system). Default: false') -}); - -/** - * Result of a single operation in the batch - */ -interface OperationResult { - index: number; - id: string; - success: boolean; - result?: any; - error?: string; -} - -/** - * Summary of batch execution - */ -interface BatchSummary { - total: number; - succeeded: number; - failed: number; - executed: number; -} - -/** - * Response from the batch execute tool - */ -interface BatchExecuteResponse { - success: boolean; - type: string; - message: string; - results: OperationResult[]; - summary: BatchSummary; -} - -/** - * Creates and registers the Batch Execute tool with the MCP server - */ -export function registerBatchExecuteTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, { - operationCount: params.operations?.length, - stopOnError: params.stopOnError, - atomic: params.atomic - }); - const result = await batchExecuteHandler(mcpUnity, params, logger); - logger.info(`Tool execution completed: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function batchExecuteHandler( - mcpUnity: McpUnity, - params: z.infer, - logger: Logger -): Promise { - // Validate operations array - if (!params.operations || params.operations.length === 0) { - throw new McpUnityError( - ErrorType.VALIDATION, - "The 'operations' array is required and must contain at least one operation" - ); - } - - if (params.operations.length > 100) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Maximum of 100 operations allowed per batch" - ); - } - - // Validate no nested batch_execute operations - for (const op of params.operations) { - if (op.tool === 'batch_execute') { - throw new McpUnityError( - ErrorType.VALIDATION, - "Cannot nest batch_execute operations" - ); - } - } - - logger.info(`Sending batch with ${params.operations.length} operations to Unity`); - - // Send the batch request to Unity - const response = await mcpUnity.sendRequest({ - method: toolName, - params: { - operations: params.operations.map((op, index) => ({ - tool: op.tool, - params: op.params ?? {}, - id: op.id ?? index.toString() - })), - stopOnError: params.stopOnError ?? true, - atomic: params.atomic ?? false - } - }) as BatchExecuteResponse; - - // Format the response message - let resultText = response.message || 'Batch execution completed'; - - // Add summary details - if (response.summary) { - resultText += `\n\nSummary: ${response.summary.succeeded}/${response.summary.total} succeeded`; - if (response.summary.failed > 0) { - resultText += `, ${response.summary.failed} failed`; - } - } - - // Add individual results if there are failures or detailed info - if (response.results && response.results.length > 0) { - const failures = response.results.filter(r => !r.success); - if (failures.length > 0) { - resultText += '\n\nFailed operations:'; - for (const failure of failures) { - resultText += `\n - [${failure.id}] ${failure.error || 'Unknown error'}`; - } - } - } - - // Determine if we should throw an error or return success - if (!response.success && params.stopOnError) { - // When stopOnError is true and we failed, throw to signal the error clearly - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - resultText - ); - } - - return { - content: [{ - type: 'text', - text: resultText - }] - }; -} diff --git a/Server~/src/tools/createPrefabTool.ts b/Server~/src/tools/createPrefabTool.ts deleted file mode 100644 index 400cf29c..00000000 --- a/Server~/src/tools/createPrefabTool.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; - -// Constants for the tool -const toolName = 'create_prefab'; -const toolDescription = 'Creates a prefab with optional MonoBehaviour script and serialized field values'; - -// Parameter schema for the tool -const paramsSchema = z.object({ - componentName: z.string().optional().describe('The name of the MonoBehaviour Component to add to the prefab (optional)'), - prefabName: z.string().describe('The name of the prefab to create'), - fieldValues: z.record(z.any()).optional().describe('Optional JSON object of serialized field values to apply to the prefab') -}); - -/** - * Creates and registers the CreatePrefab tool with the MCP server - * - * @param server The MCP server to register the tool with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerCreatePrefabTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handler function for the CreatePrefab tool - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The validated parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if validation fails or the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any) { - if (!params.prefabName) { - throw new McpUnityError( - ErrorType.VALIDATION, - "'prefabName' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to create prefab` - ); - } - - return { - content: [{ - type: response.type, - text: response.message || `Successfully created prefab` - }], - // Include the prefab path in the result for programmatic access - data: { - prefabPath: response.prefabPath - } - }; -} diff --git a/Server~/src/tools/createSceneTool.ts b/Server~/src/tools/createSceneTool.ts deleted file mode 100644 index 3c804e69..00000000 --- a/Server~/src/tools/createSceneTool.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { McpUnity } from "../unity/mcpUnity.js"; -import { McpUnityError, ErrorType } from "../utils/errors.js"; -import * as z from "zod"; -import { Logger } from "../utils/logger.js"; - -const toolName = "create_scene"; -const toolDescription = - "Creates a new scene and saves it to the specified path"; - -const paramsSchema = z.object({ - sceneName: z - .string() - .describe("The name of the scene to create (without extension)"), - folderPath: z - .string() - .optional() - .describe("The folder path under 'Assets' to save into (default: Assets)"), - addToBuildSettings: z - .boolean() - .optional() - .describe("Whether to add the scene to Build Settings"), - makeActive: z - .boolean() - .optional() - .describe("Whether to open/make the new scene active after creating it"), -}); - -export function registerCreateSceneTool( - server: McpServer, - mcpUnity: McpUnity, - logger: Logger -) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any) { - if (!params.sceneName) { - throw new McpUnityError( - ErrorType.VALIDATION, - "'sceneName' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params, - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || "Failed to create scene" - ); - } - - return { - content: [ - { - type: response.type, - text: response.message || "Successfully created scene", - }, - ], - data: { - scenePath: response.scenePath, - }, - }; -} diff --git a/Server~/src/tools/deleteSceneTool.ts b/Server~/src/tools/deleteSceneTool.ts deleted file mode 100644 index 9b633393..00000000 --- a/Server~/src/tools/deleteSceneTool.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; - -const toolName = 'delete_scene'; -const toolDescription = 'Deletes a scene by path or name and removes it from Build Settings'; - -const paramsSchema = z.object({ - scenePath: z.string().optional().describe("Full asset path to the scene (e.g., 'Assets/Scenes/MyScene.unity')"), - sceneName: z.string().optional().describe('Scene name without extension (used if scenePath not provided)'), - folderPath: z.string().optional().describe("Optional folder scope to resolve sceneName under 'Assets'") -}); - -export function registerDeleteSceneTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any) { - if (!params.scenePath && !params.sceneName) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'scenePath' or 'sceneName' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to delete scene' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'Successfully deleted scene' - }], - data: { - scenePath: response.scenePath - } - }; -} - - diff --git a/Server~/src/tools/gameObjectTools.ts b/Server~/src/tools/gameObjectTools.ts deleted file mode 100644 index 0dbfdd6b..00000000 --- a/Server~/src/tools/gameObjectTools.ts +++ /dev/null @@ -1,226 +0,0 @@ -import * as z from 'zod'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// ============================================================================ -// Duplicate GameObject Tool -// ============================================================================ - -const duplicateToolName = 'duplicate_gameobject'; -const duplicateToolDescription = 'Duplicates a GameObject in the Unity scene. Can create multiple copies and optionally rename or reparent them.'; -const duplicateParamsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to duplicate'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy to duplicate (alternative to instanceId)'), - newName: z.string().optional().describe('New name for the duplicated GameObject(s). If count > 1, numbers will be appended.'), - newParent: z.string().optional().describe('Path to the new parent GameObject. If not specified, uses the same parent as the original.'), - newParentId: z.number().optional().describe('Instance ID of the new parent GameObject (alternative to newParent path).'), - count: z.number().int().min(1).max(100).default(1).describe('Number of copies to create. Default: 1, Max: 100'), -}); - -/** - * Creates and registers the Duplicate GameObject tool with the MCP server - */ -export function registerDuplicateGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${duplicateToolName}`); - - server.tool( - duplicateToolName, - duplicateToolDescription, - duplicateParamsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${duplicateToolName}`, params); - const result = await duplicateHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${duplicateToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${duplicateToolName}`, error); - throw error; - } - } - ); -} - -async function duplicateHandler(mcpUnity: McpUnity, params: any): Promise { - // Validate parameters - require either instanceId or objectPath - if ((params.instanceId === undefined || params.instanceId === null) && - (!params.objectPath || params.objectPath.trim() === '')) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'instanceId' or 'objectPath' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: duplicateToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - newName: params.newName, - newParent: params.newParent, - newParentId: params.newParentId, - count: params.count ?? 1, - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to duplicate the GameObject' - ); - } - - return { - content: [{ - type: response.type || 'text', - text: response.message || 'Successfully duplicated the GameObject' - }] - }; -} - -// ============================================================================ -// Delete GameObject Tool -// ============================================================================ - -const deleteToolName = 'delete_gameobject'; -const deleteToolDescription = 'Deletes a GameObject from the Unity scene. By default, also deletes all children.'; -const deleteParamsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to delete'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy to delete (alternative to instanceId)'), - includeChildren: z.boolean().default(true).describe('If true (default), deletes all children. If false, children are moved to the deleted object\'s parent.'), -}); - -/** - * Creates and registers the Delete GameObject tool with the MCP server - */ -export function registerDeleteGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${deleteToolName}`); - - server.tool( - deleteToolName, - deleteToolDescription, - deleteParamsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${deleteToolName}`, params); - const result = await deleteHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${deleteToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${deleteToolName}`, error); - throw error; - } - } - ); -} - -async function deleteHandler(mcpUnity: McpUnity, params: any): Promise { - // Validate parameters - require either instanceId or objectPath - if ((params.instanceId === undefined || params.instanceId === null) && - (!params.objectPath || params.objectPath.trim() === '')) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'instanceId' or 'objectPath' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: deleteToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - includeChildren: params.includeChildren ?? true, - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to delete the GameObject' - ); - } - - return { - content: [{ - type: response.type || 'text', - text: response.message || 'Successfully deleted the GameObject' - }] - }; -} - -// ============================================================================ -// Reparent GameObject Tool -// ============================================================================ - -const reparentToolName = 'reparent_gameobject'; -const reparentToolDescription = 'Changes the parent of a GameObject. Can move to a new parent or to the root level (null parent).'; -const reparentParamsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to reparent'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy to reparent (alternative to instanceId)'), - newParent: z.string().nullable().optional().describe('Path to the new parent GameObject. Use null to move to root level.'), - newParentId: z.number().nullable().optional().describe('Instance ID of the new parent GameObject. Use null to move to root level.'), - worldPositionStays: z.boolean().default(true).describe('If true (default), the world position is preserved. If false, local position is reset to zero.'), -}); - -/** - * Creates and registers the Reparent GameObject tool with the MCP server - */ -export function registerReparentGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${reparentToolName}`); - - server.tool( - reparentToolName, - reparentToolDescription, - reparentParamsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${reparentToolName}`, params); - const result = await reparentHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${reparentToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${reparentToolName}`, error); - throw error; - } - } - ); -} - -async function reparentHandler(mcpUnity: McpUnity, params: any): Promise { - // Validate parameters - require either instanceId or objectPath - if ((params.instanceId === undefined || params.instanceId === null) && - (!params.objectPath || params.objectPath.trim() === '')) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'instanceId' or 'objectPath' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: reparentToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - newParent: params.newParent, - newParentId: params.newParentId, - worldPositionStays: params.worldPositionStays ?? true, - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to reparent the GameObject' - ); - } - - return { - content: [{ - type: response.type || 'text', - text: response.message || 'Successfully reparented the GameObject' - }] - }; -} diff --git a/Server~/src/tools/getConsoleLogsTool.ts b/Server~/src/tools/getConsoleLogsTool.ts deleted file mode 100644 index b492d3bf..00000000 --- a/Server~/src/tools/getConsoleLogsTool.ts +++ /dev/null @@ -1,125 +0,0 @@ -import * as z from "zod"; -import { Logger } from "../utils/logger.js"; -import { McpUnity } from "../unity/mcpUnity.js"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { McpUnityError, ErrorType } from "../utils/errors.js"; -import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; - -// Constants for the tool -const toolName = "get_console_logs"; -const toolDescription = "Retrieves logs from the Unity console with pagination support to avoid token limits"; -const paramsSchema = z.object({ - logType: z - .enum(["info", "warning", "error"]) - .optional() - .describe( - "The type of logs to retrieve (info, warning, error) - defaults to all logs if not specified" - ), - offset: z - .number() - .int() - .min(0) - .optional() - .describe("Starting index for pagination (0-based, defaults to 0)"), - limit: z - .number() - .int() - .min(1) - .max(500) - .optional() - .describe("Maximum number of logs to return (defaults to 50, max 500 to avoid token limits)"), - includeStackTrace: z - .boolean() - .optional() - .describe("Whether to include stack trace in logs. ⚠️ ALWAYS SET TO FALSE to save 80-90% tokens, unless you specifically need stack traces for debugging. Default: true (except info logs in resource)") -}); - -/** - * Creates and registers the Get Console Logs tool with the MCP server - * This tool allows retrieving messages from the Unity console - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetConsoleLogsTool( - server: McpServer, - mcpUnity: McpUnity, - logger: Logger -) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: z.infer) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles requests for Unity console logs - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler( - mcpUnity: McpUnity, - params: z.infer -): Promise { - const { logType, offset = 0, limit = 50, includeStackTrace = true } = params; - - // Send request to Unity using the same method name as the resource - // This allows reusing the existing Unity-side implementation - const response = await mcpUnity.sendRequest({ - method: "get_console_logs", - params: { - logType: logType, - offset: offset, - limit: limit, - includeStackTrace: includeStackTrace, - }, - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || "Failed to fetch logs from Unity" - ); - } - - const logs = response.data ?? response.logs ?? response; - - return { - content: [ - { - type: "text", - text: JSON.stringify( - logs, - null, - 2 - ), - }, - ], - data: { - logs, - offset, - limit, - logType, - includeStackTrace, - }, - }; -} diff --git a/Server~/src/tools/getGameObjectTool.ts b/Server~/src/tools/getGameObjectTool.ts deleted file mode 100644 index e4b4f6ef..00000000 --- a/Server~/src/tools/getGameObjectTool.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as z from "zod"; -import { Logger } from "../utils/logger.js"; -import { McpUnity } from "../unity/mcpUnity.js"; -import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; -import { McpUnityError, ErrorType } from "../utils/errors.js"; -import { CallToolResult } from "@modelcontextprotocol/sdk/types.js"; - -// Constants for the tool -const toolName = "get_gameobject"; -const toolDescription = - "Retrieves detailed information about a specific GameObject by instance ID, name, or hierarchical path (e.g., 'Parent/Child/MyObject'). Returns component info plus a scoped child hierarchy. Use 'maxDepth', 'includeComponents', and 'includeComponentProperties' to control response size and avoid token/response limits. If a node carries '_truncated: true' with reason 'depth_limit' or 'size_limit_exceeded', re-query that node directly with narrower parameters."; -const paramsSchema = z.object({ - idOrName: z - .string() - .describe( - "The instance ID (integer), name, or hierarchical path of the GameObject to retrieve. Use hierarchical paths like 'Canvas/Panel/Button' for nested objects." - ), - maxDepth: z - .number() - .int() - .min(0) - .max(50) - .optional() - .describe( - "Maximum child hierarchy depth to traverse. 0 = no children, 1 = direct children, 2 = grandchildren. Default: 2. Increase only when you actually need a deeper tree — large scenes can exceed the MCP 15MB response cap." - ), - includeComponents: z - .boolean() - .optional() - .describe( - "Include the component list on each node. Set false to get a hierarchy-only outline. Default: true." - ), - includeComponentProperties: z - .boolean() - .optional() - .describe( - "Include serialized property values for each component. Set false to keep component type names only (saves substantial tokens). Default: true." - ), -}); - -/** - * Creates and registers the Get GameObject tool with the MCP server - * This tool allows retrieving detailed information about GameObjects in Unity scenes - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerGetGameObjectTool( - server: McpServer, - mcpUnity: McpUnity, - logger: Logger -) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: z.infer) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles requests for GameObject information from Unity - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler( - mcpUnity: McpUnity, - params: z.infer -): Promise { - const { idOrName, maxDepth, includeComponents, includeComponentProperties } = params; - - // Send request to Unity - const response = await mcpUnity.sendRequest({ - method: toolName, - params: { - idOrName, - maxDepth, - includeComponents, - includeComponentProperties, - }, - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || "Failed to fetch GameObject from Unity" - ); - } - - return { - content: [ - { - type: "text", - text: JSON.stringify(response, null, 2), - }, - ], - }; -} - - diff --git a/Server~/src/tools/getPlayModeStatusTool.ts b/Server~/src/tools/getPlayModeStatusTool.ts deleted file mode 100644 index 6b2f1a1e..00000000 --- a/Server~/src/tools/getPlayModeStatusTool.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -const toolName = 'get_play_mode_status'; -const toolDescription = 'Gets Unity play mode status (isPlaying, isPaused).'; - -const paramsSchema = z.object({}); - -export function registerGetPlayModeStatusTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to get play mode status' - ); - } - - const statusText = response.isPlaying - ? (response.isPaused ? 'Play mode (paused)' : 'Play mode') - : 'Edit mode'; - - return { - content: [ - { - type: response.type as 'text', - text: statusText - } - ], - data: { - isPlaying: response.isPlaying, - isPaused: response.isPaused - } - }; -} diff --git a/Server~/src/tools/getSceneInfoTool.ts b/Server~/src/tools/getSceneInfoTool.ts deleted file mode 100644 index bef4d7eb..00000000 --- a/Server~/src/tools/getSceneInfoTool.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; - -const toolName = 'get_scene_info'; -const toolDescription = 'Gets information about the active scene including name, path, dirty state, root object count, and loaded state. Also returns info about all currently loaded scenes.'; - -const paramsSchema = z.object({}); - -export function registerGetSceneInfoTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any) { - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to get scene info' - ); - } - - // Format the scene info for display - const activeScene = response.activeScene; - let text = `Active Scene: ${activeScene.name}\n`; - text += ` Path: ${activeScene.path || '(unsaved)'}\n`; - text += ` Build Index: ${activeScene.buildIndex}\n`; - text += ` Is Dirty: ${activeScene.isDirty}\n`; - text += ` Is Loaded: ${activeScene.isLoaded}\n`; - text += ` Root Count: ${activeScene.rootCount}\n`; - - if (response.loadedSceneCount > 1) { - text += `\nLoaded Scenes (${response.loadedSceneCount}):\n`; - for (const scene of response.loadedScenes) { - text += ` - ${scene.name}${scene.isActive ? ' (active)' : ''}: ${scene.path || '(unsaved)'}\n`; - } - } - - return { - content: [{ - type: response.type as "text", - text: text - }], - data: { - activeScene: response.activeScene, - loadedSceneCount: response.loadedSceneCount, - loadedScenes: response.loadedScenes - } - }; -} diff --git a/Server~/src/tools/getScenesHierarchyTool.ts b/Server~/src/tools/getScenesHierarchyTool.ts deleted file mode 100644 index e11897ec..00000000 --- a/Server~/src/tools/getScenesHierarchyTool.ts +++ /dev/null @@ -1,59 +0,0 @@ -import * as z from 'zod'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; - -const toolName = 'get_scenes_hierarchy'; -const toolDescription = 'Retrieves all GameObjects in the Unity loaded scenes (scenes hierarchy).'; -const paramsSchema = z.object({}); - -export function registerGetScenesHierarchyTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: z.infer) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity): Promise { - const response = await mcpUnity.sendRequest({ - method: toolName, - params: {}, - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to fetch hierarchy from Unity' - ); - } - - const hierarchy = response.hierarchy ?? response.data ?? response; - - return { - content: [ - { - type: 'text', - text: JSON.stringify(hierarchy, null, 2), - }, - ], - data: { - hierarchy, - }, - }; -} diff --git a/Server~/src/tools/loadSceneTool.ts b/Server~/src/tools/loadSceneTool.ts deleted file mode 100644 index b2203ebf..00000000 --- a/Server~/src/tools/loadSceneTool.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; - -const toolName = 'load_scene'; -const toolDescription = 'Loads a scene by path or name. Supports additive loading (default: false)'; - -const paramsSchema = z.object({ - scenePath: z.string().optional().describe("Full asset path to the scene (e.g., 'Assets/Scenes/MyScene.unity')"), - sceneName: z.string().optional().describe('Scene name without extension (used if scenePath not provided)'), - folderPath: z.string().optional().describe("Optional folder scope to resolve sceneName under 'Assets'"), - additive: z.boolean().optional().describe('Load additively if true; default false') -}); - -export function registerLoadSceneTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any) { - if (!params.scenePath && !params.sceneName) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'scenePath' or 'sceneName' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to load scene' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'Successfully loaded scene' - }], - data: { - scenePath: response.scenePath, - additive: response.additive - } - }; -} - - diff --git a/Server~/src/tools/materialTools.ts b/Server~/src/tools/materialTools.ts deleted file mode 100644 index d21f69fb..00000000 --- a/Server~/src/tools/materialTools.ts +++ /dev/null @@ -1,354 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Color schema for material properties -const colorSchema = z.object({ - r: z.number().min(0).max(1).describe('Red component (0-1)'), - g: z.number().min(0).max(1).describe('Green component (0-1)'), - b: z.number().min(0).max(1).describe('Blue component (0-1)'), - a: z.number().min(0).max(1).optional().default(1).describe('Alpha component (0-1)') -}); - -// Vector4 schema for material properties -const vector4Schema = z.object({ - x: z.number().describe('X component'), - y: z.number().describe('Y component'), - z: z.number().describe('Z component'), - w: z.number().optional().default(0).describe('W component') -}); - -// ============================================================================ -// CREATE MATERIAL TOOL -// ============================================================================ - -const createMaterialToolName = 'create_material'; -const createMaterialToolDescription = 'Creates a new material with the specified shader and saves it to the project. Use the "color" parameter for easy color setting.'; -const createMaterialParamsSchema = z.object({ - name: z.string().describe('The name of the material'), - shader: z.string().optional().describe('The shader name. Auto-detects render pipeline if not specified (URP: "Universal Render Pipeline/Lit", Built-in: "Standard")'), - savePath: z.string().describe('The asset path to save the material (e.g., "Assets/Materials/MyMaterial.mat")'), - color: colorSchema.optional().describe('The base color of the material. Auto-detects correct property name (_BaseColor for URP, _Color for Standard)'), - properties: z.record(z.any()).optional().describe('Optional initial property values as key-value pairs (advanced usage)') -}); - -/** - * Registers the Create Material tool with the MCP server - */ -export function registerCreateMaterialTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${createMaterialToolName}`); - - server.tool( - createMaterialToolName, - createMaterialToolDescription, - createMaterialParamsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${createMaterialToolName}`, params); - const result = await createMaterialHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${createMaterialToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${createMaterialToolName}`, error); - throw error; - } - } - ); -} - -async function createMaterialHandler(mcpUnity: McpUnity, params: any): Promise { - if (!params.name) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Required parameter 'name' must be provided" - ); - } - - if (!params.savePath) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Required parameter 'savePath' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: createMaterialToolName, - params: { - name: params.name, - shader: params.shader, // Let Unity auto-detect if not specified - savePath: params.savePath, - color: params.color, // Auto-maps to correct shader property (_BaseColor, _Color, etc.) - properties: params.properties - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to create material' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || `Successfully created material '${params.name}'` - }] - }; -} - -// ============================================================================ -// ASSIGN MATERIAL TOOL -// ============================================================================ - -const assignMaterialToolName = 'assign_material'; -const assignMaterialToolDescription = 'Assigns a material to a GameObject\'s Renderer component at a specific material slot'; -const assignMaterialParamsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy (alternative to instanceId)'), - materialPath: z.string().describe('The asset path to the material (e.g., "Assets/Materials/MyMaterial.mat")'), - slot: z.number().int().min(0).optional().default(0).describe('The material slot index (default: 0)') -}); - -/** - * Registers the Assign Material tool with the MCP server - */ -export function registerAssignMaterialTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${assignMaterialToolName}`); - - server.tool( - assignMaterialToolName, - assignMaterialToolDescription, - assignMaterialParamsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${assignMaterialToolName}`, params); - const result = await assignMaterialHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${assignMaterialToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${assignMaterialToolName}`, error); - throw error; - } - } - ); -} - -async function assignMaterialHandler(mcpUnity: McpUnity, params: any): Promise { - if ((params.instanceId === undefined || params.instanceId === null) && - (!params.objectPath || params.objectPath.trim() === '')) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'instanceId' or 'objectPath' must be provided" - ); - } - - if (!params.materialPath) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Required parameter 'materialPath' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: assignMaterialToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - materialPath: params.materialPath, - slot: params.slot ?? 0 - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to assign material' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || `Successfully assigned material` - }] - }; -} - -// ============================================================================ -// MODIFY MATERIAL TOOL -// ============================================================================ - -const modifyMaterialToolName = 'modify_material'; -const modifyMaterialToolDescription = 'Modifies properties of an existing material. Supports colors (e.g., _Color), floats (e.g., _Metallic), and textures (e.g., _MainTex path)'; -const modifyMaterialParamsSchema = z.object({ - materialPath: z.string().describe('The asset path to the material (e.g., "Assets/Materials/MyMaterial.mat")'), - properties: z.record(z.any()).describe('Property name to value mapping. Colors: {r,g,b,a}, Vectors: {x,y,z,w}, Floats: number, Textures: asset path string') -}); - -/** - * Registers the Modify Material tool with the MCP server - */ -export function registerModifyMaterialTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${modifyMaterialToolName}`); - - server.tool( - modifyMaterialToolName, - modifyMaterialToolDescription, - modifyMaterialParamsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${modifyMaterialToolName}`, params); - const result = await modifyMaterialHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${modifyMaterialToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${modifyMaterialToolName}`, error); - throw error; - } - } - ); -} - -async function modifyMaterialHandler(mcpUnity: McpUnity, params: any): Promise { - if (!params.materialPath) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Required parameter 'materialPath' must be provided" - ); - } - - if (!params.properties || Object.keys(params.properties).length === 0) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Required parameter 'properties' must be provided and contain at least one property" - ); - } - - const response = await mcpUnity.sendRequest({ - method: modifyMaterialToolName, - params: { - materialPath: params.materialPath, - properties: params.properties - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to modify material' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || `Successfully modified material` - }] - }; -} - -// ============================================================================ -// GET MATERIAL INFO TOOL -// ============================================================================ - -const getMaterialInfoToolName = 'get_material_info'; -const getMaterialInfoToolDescription = 'Gets detailed information about a material including its shader and all properties with current values'; -const getMaterialInfoParamsSchema = z.object({ - materialPath: z.string().describe('The asset path to the material (e.g., "Assets/Materials/MyMaterial.mat")') -}); - -/** - * Registers the Get Material Info tool with the MCP server - */ -export function registerGetMaterialInfoTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${getMaterialInfoToolName}`); - - server.tool( - getMaterialInfoToolName, - getMaterialInfoToolDescription, - getMaterialInfoParamsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${getMaterialInfoToolName}`, params); - const result = await getMaterialInfoHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${getMaterialInfoToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${getMaterialInfoToolName}`, error); - throw error; - } - } - ); -} - -async function getMaterialInfoHandler(mcpUnity: McpUnity, params: any): Promise { - if (!params.materialPath) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Required parameter 'materialPath' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: getMaterialInfoToolName, - params: { - materialPath: params.materialPath - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to get material info' - ); - } - - // Format the response with material details - let text = `Material: ${response.materialName}\n`; - text += `Shader: ${response.shaderName}\n`; - text += `Render Queue: ${response.renderQueue} (${response.renderQueueCategory})\n`; - text += `Instancing: ${response.enableInstancing}\n`; - text += `Pass Count: ${response.passCount}\n\n`; - text += `Properties:\n`; - - if (response.properties && Array.isArray(response.properties)) { - for (const prop of response.properties) { - let valueStr = ''; - if (prop.value === null || prop.value === undefined) { - valueStr = 'null'; - } else if (typeof prop.value === 'object') { - valueStr = JSON.stringify(prop.value); - } else { - valueStr = String(prop.value); - } - - text += ` ${prop.name} (${prop.type}): ${valueStr}`; - if (prop.description) { - text += ` - ${prop.description}`; - } - text += '\n'; - } - } - - return { - content: [{ - type: 'text', - text: text - }], - data: { - materialName: response.materialName, - materialPath: response.materialPath, - shaderName: response.shaderName, - renderQueue: response.renderQueue, - renderQueueCategory: response.renderQueueCategory, - enableInstancing: response.enableInstancing, - doubleSidedGI: response.doubleSidedGI, - passCount: response.passCount, - properties: response.properties - } - }; -} diff --git a/Server~/src/tools/menuItemTool.ts b/Server~/src/tools/menuItemTool.ts deleted file mode 100644 index d9c07d49..00000000 --- a/Server~/src/tools/menuItemTool.ts +++ /dev/null @@ -1,73 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'execute_menu_item'; -const toolDescription = 'Executes a Unity menu item by path'; -const paramsSchema = z.object({ - menuPath: z.string().describe('The path to the menu item to execute (e.g. "GameObject/Create Empty")') -}); - -/** - * Creates and registers the Menu Item tool with the MCP server - * This tool allows executing menu items in the Unity Editor - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerMenuItemTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles menu item execution requests - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - const { menuPath } = params; - const response = await mcpUnity.sendRequest({ - method: toolName, - params: { menuPath } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to execute menu item: ${menuPath}` - ); - } - - return { - content: [{ - type: response.type, - text: response.message || `Successfully executed menu item: ${menuPath}` - }] - }; -} diff --git a/Server~/src/tools/recompileScriptsTool.ts b/Server~/src/tools/recompileScriptsTool.ts deleted file mode 100644 index 4b121903..00000000 --- a/Server~/src/tools/recompileScriptsTool.ts +++ /dev/null @@ -1,89 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'recompile_scripts'; -const toolDescription = 'Recompiles all scripts in the Unity project.'; -const paramsSchema = z.object({ - returnWithLogs: z.boolean().optional().default(true).describe('Whether to return compilation logs'), - logsLimit: z.number().int().min(0).max(1000).optional().default(100).describe('Maximum number of compilation logs to return') -}); - -/** - * Creates and registers the Recompile Scripts tool with the MCP server - * This tool allows recompiling all scripts in the Unity project - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerRecompileScriptsTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles recompile scripts tool requests - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: z.infer): Promise { - // Validate and prepare parameters - const returnWithLogs = params.returnWithLogs ?? true; - const logsLimit = Math.max(0, Math.min(1000, params.logsLimit || 100)); - - // Send to Unity with validated parameters - const response = await mcpUnity.sendRequest({ - method: toolName, - params: { - returnWithLogs, - logsLimit - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to recompile scripts` - ); - } - - return { - content: [ - { - type: 'text', - text: response.message - }, - { - type: 'text', - text: JSON.stringify({ - logs: response.logs - }, null, 2) - } - ] - }; -} diff --git a/Server~/src/tools/runTestsTool.ts b/Server~/src/tools/runTestsTool.ts deleted file mode 100644 index f5ad715f..00000000 --- a/Server~/src/tools/runTestsTool.ts +++ /dev/null @@ -1,108 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'run_tests'; -const toolDescription = 'Runs Unity\'s Test Runner tests'; -const paramsSchema = z.object({ - testMode: z.string().optional().default('EditMode').describe('The test mode to run (EditMode or PlayMode) - defaults to EditMode (optional)'), - testFilter: z.string().optional().default('').describe('The specific test filter to run (e.g. specific test name or class name, must include namespace) (optional)'), - returnOnlyFailures: z.boolean().optional().default(true).describe('Whether to show only failed tests in the results (optional)'), - returnWithLogs: z.boolean().optional().default(false).describe('Whether to return the test logs in the results (optional)') -}); - -/** - * Creates and registers the Run Tests tool with the MCP server - * This tool allows running tests in the Unity Test Runner - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerRunTestsTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any = {}) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles running tests in Unity - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any = {}): Promise { - const { - testMode = 'EditMode', - testFilter = '', - returnOnlyFailures = true, - returnWithLogs = false - } = params; - - // Create and wait for the test run - const response = await mcpUnity.sendRequest({ - method: toolName, - params: { - testMode, - testFilter, - returnOnlyFailures, - returnWithLogs - } - }); - - // Process the test results - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to run tests: Mode=${testMode}, Filter=${testFilter || 'none'}` - ); - } - - // Extract test results - const testResults = response.results || []; - const testCount = response.testCount || 0; - const passCount = response.passCount || 0; - const failCount = response.failCount || 0; - const skipCount = response.skipCount || 0; - - return { - content: [ - { - type: 'text', - text: response.message - }, - { - type: 'text', - text: JSON.stringify({ - testCount, - passCount, - failCount, - skipCount, - results: testResults - }, null, 2) - } - ] - }; -} diff --git a/Server~/src/tools/saveSceneTool.ts b/Server~/src/tools/saveSceneTool.ts deleted file mode 100644 index b58f2fc0..00000000 --- a/Server~/src/tools/saveSceneTool.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; - -const toolName = 'save_scene'; -const toolDescription = 'Saves the current active scene. Optionally saves to a new path (Save As)'; - -const paramsSchema = z.object({ - scenePath: z.string().optional().describe("The path to save the scene to (e.g., 'Assets/Scenes/MyScene.unity'). Required if saveAs is true"), - saveAs: z.boolean().optional().describe('If true, saves to a new path specified by scenePath. Default: false') -}); - -export function registerSaveSceneTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any) { - // Validate that scenePath is provided when saveAs is true - if (params.saveAs && !params.scenePath) { - throw new McpUnityError( - ErrorType.VALIDATION, - "'scenePath' is required when 'saveAs' is true" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to save scene' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'Successfully saved scene' - }], - data: { - scenePath: response.scenePath, - sceneName: response.sceneName - } - }; -} diff --git a/Server~/src/tools/selectGameObjectTool.ts b/Server~/src/tools/selectGameObjectTool.ts deleted file mode 100644 index 40818a5f..00000000 --- a/Server~/src/tools/selectGameObjectTool.ts +++ /dev/null @@ -1,82 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'select_gameobject'; -const toolDescription = 'Sets the selected GameObject in the Unity editor by path, name or instance ID'; -const paramsSchema = z.object({ - objectPath: z.string().optional().describe('The path or name of the GameObject to select (e.g. "Main Camera")'), - objectName: z.string().optional().describe('The name of the GameObject to select'), - instanceId: z.number().optional().describe('The instance ID of the GameObject to select') -}); - -/** - * Creates and registers the Select GameObject tool with the MCP server - * This tool allows selecting a GameObject in the Unity Editor - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerSelectGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles selecting a GameObject in Unity - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - // Custom validation since we can't use refine/superRefine while maintaining ZodObject type - if (params.objectPath === undefined && params.objectName === undefined && params.instanceId === undefined) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'objectPath', 'objectName' or 'instanceId' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to select GameObject` - ); - } - - return { - content: [{ - type: response.type, - text: response.message || `Successfully selected GameObject` - }] - }; -} diff --git a/Server~/src/tools/sendConsoleLogTool.ts b/Server~/src/tools/sendConsoleLogTool.ts deleted file mode 100644 index 80010c32..00000000 --- a/Server~/src/tools/sendConsoleLogTool.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'send_console_log'; -const toolDescription = 'Sends console log messages to the Unity console'; -const paramsSchema = z.object({ - message: z.string().describe('The message to display in the Unity console'), - type: z.string().optional().describe('The type of message (info, warning, error) - defaults to info (optional)') -}); - -/** - * Creates and registers the Send Console Log tool with the MCP server - * This tool allows sending messages to the Unity console - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerSendConsoleLogTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles notification message requests - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - const { message, type = 'info' } = params; - // Send to Unity - const response = await mcpUnity.sendRequest({ - method: toolName, - params: { - message, - type - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to send message to Unity console` - ); - } - - return { - content: [{ - type: response.type, - text: response.message - }] - }; -} diff --git a/Server~/src/tools/setPlayModeStatusTool.ts b/Server~/src/tools/setPlayModeStatusTool.ts deleted file mode 100644 index 67ca0716..00000000 --- a/Server~/src/tools/setPlayModeStatusTool.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -const toolName = 'set_play_mode_status'; -const toolDescription = "Controls Unity play mode. Actions: 'play' (start or unpause), 'pause' (toggle pause), 'stop' (exit play mode), 'step' (advance one frame while paused)."; - -const paramsSchema = z.object({ - action: z.enum(['play', 'pause', 'stop', 'step']).describe("The play mode action to execute: 'play', 'pause', 'stop', or 'step'") -}); - -export function registerSetPlayModeStatusTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - const validatedParams = paramsSchema.parse(params); - - const response = await mcpUnity.sendRequest({ - method: toolName, - params: validatedParams - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to execute play mode action: ${validatedParams.action}` - ); - } - - const statusText = response.isPlaying - ? (response.isPaused ? 'Playing (paused)' : 'Playing') - : 'Edit mode'; - - return { - content: [ - { - type: 'text', - text: `Play mode action '${validatedParams.action}' executed successfully. Current state: ${statusText}` - } - ], - data: { - action: validatedParams.action, - isPlaying: response.isPlaying, - isPaused: response.isPaused - }, - isError: false - }; -} diff --git a/Server~/src/tools/showUnityDashboardTool.ts b/Server~/src/tools/showUnityDashboardTool.ts deleted file mode 100644 index e4cf26ca..00000000 --- a/Server~/src/tools/showUnityDashboardTool.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as z from 'zod'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; -import { Logger } from '../utils/logger.js'; -import { registerAppTool } from '@modelcontextprotocol/ext-apps/server'; -import { readUnityDashboardHtml } from '../resources/unityDashboardAppResource.js'; - -const toolName = 'show_unity_dashboard'; -const toolDescription = 'Opens the Unity dashboard MCP App in VS Code.'; -const paramsSchema = z.object({}); - -export function registerShowUnityDashboardTool(server: McpServer, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - registerAppTool(server, toolName, { - description: toolDescription, - inputSchema: paramsSchema.shape, - _meta: { - ui: { - resourceUri: 'ui://unity-dashboard', - } - } - }, async () => { - try { - logger.info(`Executing tool: ${toolName}`); - const result = await toolHandler(); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - }); -} - -async function toolHandler(): Promise { - const { text, mimeType } = readUnityDashboardHtml(); - const appUri = 'ui://unity-dashboard'; - - return { - content: [ - { - type: 'resource', - resource: { - uri: appUri, - mimeType, - text, - _meta: { - view: 'mcp-app', - ui: true - } - } - } - ], - _meta: { - ui: { - resourceUri: appUri, - title: 'Unity Dashboard' - } - } - }; -} diff --git a/Server~/src/tools/transformTools.ts b/Server~/src/tools/transformTools.ts deleted file mode 100644 index e3aa6bb3..00000000 --- a/Server~/src/tools/transformTools.ts +++ /dev/null @@ -1,329 +0,0 @@ -import * as z from 'zod'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Build a fresh Vector3 schema per field to avoid local JSON pointer refs -// like "#/properties/position" in generated JSON schema. -function createVector3Schema() { - return z.object({ - x: z.number().describe('X component'), - y: z.number().describe('Y component'), - z: z.number().describe('Z component') - }); -} - -// ============================================================================ -// move_gameobject Tool -// ============================================================================ - -const moveToolName = 'move_gameobject'; -const moveToolDescription = 'Moves a GameObject to a new position. Supports world/local space and absolute/relative positioning.'; -const moveParamsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to move'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy (alternative to instanceId)'), - position: createVector3Schema().describe('The target position'), - space: z.enum(['world', 'local']).default('world').describe('Coordinate space: "world" or "local"'), - relative: z.boolean().default(false).describe('If true, adds to current position instead of setting absolute position') -}); - -/** - * Registers the move_gameobject tool with the MCP server - */ -export function registerMoveGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${moveToolName}`); - - server.tool( - moveToolName, - moveToolDescription, - moveParamsSchema.shape, - async (params: z.infer) => { - try { - logger.info(`Executing tool: ${moveToolName}`, params); - const result = await moveToolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${moveToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${moveToolName}`, error); - throw error; - } - } - ); -} - -async function moveToolHandler(mcpUnity: McpUnity, params: z.infer): Promise { - validateGameObjectIdentifier(params); - - const response = await mcpUnity.sendRequest({ - method: moveToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - position: params.position, - space: params.space, - relative: params.relative - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to move GameObject' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'GameObject moved successfully' - }] - }; -} - -// ============================================================================ -// rotate_gameobject Tool -// ============================================================================ - -const rotateToolName = 'rotate_gameobject'; -const rotateToolDescription = 'Rotates a GameObject using Euler angles. Supports world/local space and absolute/relative rotation.'; -const rotateParamsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to rotate'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy (alternative to instanceId)'), - rotation: createVector3Schema().describe('The rotation in Euler angles (degrees)'), - space: z.enum(['world', 'local']).default('world').describe('Coordinate space: "world" or "local"'), - relative: z.boolean().default(false).describe('If true, adds to current rotation instead of setting absolute rotation') -}); - -/** - * Registers the rotate_gameobject tool with the MCP server - */ -export function registerRotateGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${rotateToolName}`); - - server.tool( - rotateToolName, - rotateToolDescription, - rotateParamsSchema.shape, - async (params: z.infer) => { - try { - logger.info(`Executing tool: ${rotateToolName}`, params); - const result = await rotateToolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${rotateToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${rotateToolName}`, error); - throw error; - } - } - ); -} - -async function rotateToolHandler(mcpUnity: McpUnity, params: z.infer): Promise { - validateGameObjectIdentifier(params); - - const response = await mcpUnity.sendRequest({ - method: rotateToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - rotation: params.rotation, - space: params.space, - relative: params.relative - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to rotate GameObject' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'GameObject rotated successfully' - }] - }; -} - -// ============================================================================ -// scale_gameobject Tool -// ============================================================================ - -const scaleToolName = 'scale_gameobject'; -const scaleToolDescription = 'Scales a GameObject. Supports absolute and relative (multiplicative) scaling.'; -const scaleParamsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to scale'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy (alternative to instanceId)'), - scale: createVector3Schema().describe('The scale values'), - relative: z.boolean().default(false).describe('If true, multiplies current scale instead of setting absolute scale') -}); - -/** - * Registers the scale_gameobject tool with the MCP server - */ -export function registerScaleGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${scaleToolName}`); - - server.tool( - scaleToolName, - scaleToolDescription, - scaleParamsSchema.shape, - async (params: z.infer) => { - try { - logger.info(`Executing tool: ${scaleToolName}`, params); - const result = await scaleToolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${scaleToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${scaleToolName}`, error); - throw error; - } - } - ); -} - -async function scaleToolHandler(mcpUnity: McpUnity, params: z.infer): Promise { - validateGameObjectIdentifier(params); - - const response = await mcpUnity.sendRequest({ - method: scaleToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - scale: params.scale, - relative: params.relative - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to scale GameObject' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'GameObject scaled successfully' - }] - }; -} - -// ============================================================================ -// set_transform Tool -// ============================================================================ - -const setTransformToolName = 'set_transform'; -const setTransformToolDescription = 'Sets a GameObject\'s transform (position, rotation, scale) in one operation. All transform properties are optional.'; -function createSetTransformParamsShape() { - return { - instanceId: z.number().optional().describe('The instance ID of the GameObject'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy (alternative to instanceId)'), - position: createVector3Schema().optional().describe('The position to set'), - rotation: createVector3Schema().optional().describe('The rotation in Euler angles (degrees)'), - scale: createVector3Schema().optional().describe('The scale to set'), - space: z.enum(['world', 'local']).default('world').describe('Coordinate space for position and rotation: "world" or "local"') - }; -} - -const setTransformParamsSchema = z.object({ - ...createSetTransformParamsShape() -}).refine( - data => data.position !== undefined || data.rotation !== undefined || data.scale !== undefined, - { message: 'At least one of position, rotation, or scale must be provided' } -); - -/** - * Registers the set_transform tool with the MCP server - */ -export function registerSetTransformTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${setTransformToolName}`); - - server.tool( - setTransformToolName, - setTransformToolDescription, - // Use base shape without refine for MCP schema registration - createSetTransformParamsShape(), - async (params: any) => { - try { - logger.info(`Executing tool: ${setTransformToolName}`, params); - const result = await setTransformToolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${setTransformToolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${setTransformToolName}`, error); - throw error; - } - } - ); -} - -async function setTransformToolHandler(mcpUnity: McpUnity, params: any): Promise { - validateGameObjectIdentifier(params); - - // Validate that at least one transform property is provided - if (!params.position && !params.rotation && !params.scale) { - throw new McpUnityError( - ErrorType.VALIDATION, - 'At least one of position, rotation, or scale must be provided' - ); - } - - const response = await mcpUnity.sendRequest({ - method: setTransformToolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - position: params.position, - rotation: params.rotation, - scale: params.scale, - space: params.space - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to set transform' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'Transform updated successfully' - }] - }; -} - -// ============================================================================ -// Helper Functions -// ============================================================================ - -/** - * Validates that either instanceId or objectPath is provided - */ -function validateGameObjectIdentifier(params: { instanceId?: number; objectPath?: string }) { - if ((params.instanceId === undefined || params.instanceId === null) && - (!params.objectPath || params.objectPath.trim() === '')) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'instanceId' or 'objectPath' must be provided" - ); - } -} - -/** - * Registers all transform tools with the MCP server - */ -export function registerTransformTools(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - registerMoveGameObjectTool(server, mcpUnity, logger); - registerRotateGameObjectTool(server, mcpUnity, logger); - registerScaleGameObjectTool(server, mcpUnity, logger); - registerSetTransformTool(server, mcpUnity, logger); -} diff --git a/Server~/src/tools/unloadSceneTool.ts b/Server~/src/tools/unloadSceneTool.ts deleted file mode 100644 index b9bc3752..00000000 --- a/Server~/src/tools/unloadSceneTool.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; - -const toolName = 'unload_scene'; -const toolDescription = 'Unloads a scene by path or name (does not delete the scene asset, just closes it from the hierarchy)'; - -const paramsSchema = z.object({ - scenePath: z.string().optional().describe("Full asset path to the scene (e.g., 'Assets/Scenes/MyScene.unity')"), - sceneName: z.string().optional().describe('Scene name without extension (used if scenePath not provided)'), - saveIfDirty: z.boolean().optional().describe('If true, saves the scene before unloading if it has unsaved changes. Default: true'), - removeScene: z.boolean().optional().describe('If true, removes the scene from the hierarchy. If false, keeps it but unloaded. Default: true') -}); - -export function registerUnloadSceneTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -async function toolHandler(mcpUnity: McpUnity, params: any) { - if (!params.scenePath && !params.sceneName) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'scenePath' or 'sceneName' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: toolName, - params - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || 'Failed to unload scene' - ); - } - - return { - content: [{ - type: response.type, - text: response.message || 'Successfully unloaded scene' - }], - data: { - sceneName: response.sceneName, - scenePath: response.scenePath, - wasDirty: response.wasDirty, - removed: response.removed - } - }; -} diff --git a/Server~/src/tools/updateComponentTool.ts b/Server~/src/tools/updateComponentTool.ts deleted file mode 100644 index 37538502..00000000 --- a/Server~/src/tools/updateComponentTool.ts +++ /dev/null @@ -1,102 +0,0 @@ -import * as z from 'zod'; -import { Logger } from '../utils/logger.js'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'update_component'; -const toolDescription = 'Updates component fields on a GameObject or adds it to the GameObject if it does not contain the component'; -const paramsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to update'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy to update (alternative to instanceId)'), - componentName: z.string().describe('The name of the component to update or add'), - componentData: z.record(z.any()).optional().describe('An object containing the fields to update on the component (optional)') -}); - -/** - * Creates and registers the Update Component tool with the MCP server - * This tool allows updating or adding components to GameObjects in the Unity Editor - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerUpdateComponentTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} - -/** - * Handles updating or adding a component to a GameObject in Unity - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - // Validate parameters - require either instanceId or objectPath - if ((params.instanceId === undefined || params.instanceId === null) && - (!params.objectPath || params.objectPath.trim() === '')) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'instanceId' or 'objectPath' must be provided" - ); - } - - if (!params.componentName) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Required parameter 'componentName' must be provided" - ); - } - - // Send request to Unity - const response = await mcpUnity.sendRequest({ - method: toolName, - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - componentName: params.componentName, - componentData: params.componentData - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to update component on GameObject` - ); - } - - // Create a description of which GameObject was targeted - const targetDescription = params.objectPath - ? `path '${params.objectPath}'` - : `ID ${params.instanceId}`; - - return { - content: [{ - type: response.type, - text: response.message || `Successfully updated component on GameObject with ${targetDescription}` - }] - }; -} diff --git a/Server~/src/tools/updateGameObjectTool.ts b/Server~/src/tools/updateGameObjectTool.ts deleted file mode 100644 index 46b2124e..00000000 --- a/Server~/src/tools/updateGameObjectTool.ts +++ /dev/null @@ -1,98 +0,0 @@ -import * as z from 'zod'; -import { McpUnity } from '../unity/mcpUnity.js'; -import { Logger } from '../utils/logger.js'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; - -// Constants for the tool -const toolName = 'update_gameobject'; -const toolDescription = 'Updates properties of a GameObject in the Unity scene by its instance ID or path. If the GameObject does not exist at the specified path, it will be created.'; -const paramsSchema = z.object({ - instanceId: z.number().optional().describe('The instance ID of the GameObject to update'), - objectPath: z.string().optional().describe('The path of the GameObject in the hierarchy to update (alternative to instanceId)'), - gameObjectData: z.object({ - name: z.string().optional().describe('New name for the GameObject'), - tag: z.string().optional().describe('New tag for the GameObject'), - layer: z.number().int().optional().describe('New layer for the GameObject'), - activeSelf: z.boolean().optional().describe('Set the active state of the GameObject (GameObject.SetActive(value))'), - isStatic: z.boolean().optional().describe('Set the static state of the GameObject (GameObject.isStatic = value)'), - }).describe('An object containing the fields to update on the GameObject. If the GameObject does not exist at objectPath, it will be created.') - .refine(data => Object.keys(data).length > 0, { message: 'gameObjectData must contain at least one property to update.' }), -}); - -/** - * Creates and registers the Update GameObject tool with the MCP server - * This tool allows updating or creating GameObjects into the Unity Editor active scene - * - * @param server The MCP server instance to register with - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param logger The logger instance for diagnostic information - */ -export function registerUpdateGameObjectTool(server: McpServer, mcpUnity: McpUnity, logger: Logger) { - logger.info(`Registering tool: ${toolName}`); - - // Register this tool with the MCP server - server.tool( - toolName, - toolDescription, - paramsSchema.shape, - async (params: any) => { - try { - logger.info(`Executing tool: ${toolName}`, params); - const result = await toolHandler(mcpUnity, params); - logger.info(`Tool execution successful: ${toolName}`); - return result; - } catch (error) { - logger.error(`Tool execution failed: ${toolName}`, error); - throw error; - } - } - ); -} -/** - * Handles updating or creating GameObjects into the Unity Editor active scene - * - * @param mcpUnity The McpUnity instance to communicate with Unity - * @param params The parameters for the tool - * @returns A promise that resolves to the tool execution result - * @throws McpUnityError if the request to Unity fails - */ -async function toolHandler(mcpUnity: McpUnity, params: any): Promise { - // Validate parameters - require either instanceId or objectPath - if ((params.instanceId === undefined || params.instanceId === null) && - (!params.objectPath || params.objectPath.trim() === '')) { - throw new McpUnityError( - ErrorType.VALIDATION, - "Either 'instanceId' or 'objectPath' must be provided" - ); - } - - const response = await mcpUnity.sendRequest({ - method: 'update_gameobject', - params: { - instanceId: params.instanceId, - objectPath: params.objectPath, - gameObjectData: params.gameObjectData, - } - }); - - if (!response.success) { - throw new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.message || `Failed to update the GameObject` - ); - } - - // Create a description of which GameObject was targeted - const targetDescription = params.objectPath - ? `path '${params.objectPath}'` - : `ID ${params.instanceId}`; - - return { - content: [{ - type: response.type, - text: response.message || `Successfully updated the GameObject with ${targetDescription}` - }] - }; -} diff --git a/Server~/src/ui/unity-dashboard.html b/Server~/src/ui/unity-dashboard.html index 32ca3059..de029b5c 100644 --- a/Server~/src/ui/unity-dashboard.html +++ b/Server~/src/ui/unity-dashboard.html @@ -1,1573 +1,166 @@ - - - - - - Unity Dashboard - - - -
-
-

Unity Dashboard

-
-
- - - - - - - - - - 3s -
-
-
- -
-
-
-
Scene
-
Loading scene info...
-
- -
-
Hierarchy
-
- - -
-
Loading hierarchy...
-
-
- -
-
-
Inspector
-
Select a focused GameObject to inspect.
-
-
-
-
- - - - -
-
-
Console Logs
- -
-
-
- -
-
-
Debug Console
- -
-
-
- - - - + + + + + + Unity Dashboard + + + +
+
+

Unity CLI + Pipeline

+
+ Not checked + Never refreshed +
+
+ +
+
+
+

Scene hierarchy

Waiting…
+

Console logs

Waiting…
+

Packages

Waiting…
+

Tests

Waiting…
+

GameObject inspector

Select a target URI such as unity://gameobject/%2FPlayer in your MCP client.
+

Companion resources

unity://logs{?severity,limit}
+unity://scenes-hierarchy{?path,max_nodes}
+unity://gameobject/{target}
+unity://packages{?include_indirect}
+unity://tests/{mode}
+ui://unity-dashboard
+
+ + + diff --git a/Server~/src/unity/commandQueue.ts b/Server~/src/unity/commandQueue.ts deleted file mode 100644 index fb4ab389..00000000 --- a/Server~/src/unity/commandQueue.ts +++ /dev/null @@ -1,321 +0,0 @@ -import { Logger } from '../utils/logger.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; - -/** - * Represents a queued command with its metadata - */ -export interface QueuedCommand { - /** Unique identifier for the command */ - id: string; - /** The request to send to Unity */ - request: { - id?: string; - method: string; - params: any; - }; - /** Resolve callback for the promise */ - resolve: (value: any) => void; - /** Reject callback for the promise */ - reject: (reason: any) => void; - /** Timestamp when the command was queued */ - queuedAt: number; - /** Optional custom timeout for this specific command (in ms) */ - timeout?: number; -} - -/** - * Configuration options for the CommandQueue - */ -export interface CommandQueueConfig { - /** Maximum number of commands to queue (default: 100) */ - maxSize?: number; - /** Default timeout in milliseconds for queued commands (default: 60000) */ - defaultTimeout?: number; - /** Interval in milliseconds to check for expired commands (default: 5000) */ - cleanupInterval?: number; -} - -/** - * Statistics about the command queue - */ -export interface CommandQueueStats { - /** Current number of queued commands */ - size: number; - /** Maximum queue size */ - maxSize: number; - /** Number of commands that were dropped due to queue overflow */ - droppedCount: number; - /** Number of commands that expired while queued */ - expiredCount: number; - /** Number of commands successfully replayed */ - replayedCount: number; -} - -/** - * Result of attempting to enqueue a command - */ -export interface EnqueueResult { - /** Whether the command was successfully queued */ - success: boolean; - /** Position in queue (1-indexed) if successful */ - position?: number; - /** Reason for failure if not successful */ - reason?: string; -} - -/** - * Default configuration values - */ -const DEFAULT_CONFIG = { - maxSize: 100, - defaultTimeout: 60000, // 60 seconds - cleanupInterval: 5000 // 5 seconds -}; - -/** - * CommandQueue manages commands that are queued when the Unity connection is unavailable. - * Commands are stored and replayed when the connection is restored. - */ -export class CommandQueue { - private queue: QueuedCommand[] = []; - private config: Required; - private cleanupTimer: NodeJS.Timeout | null = null; - private logger: Logger; - - // Statistics - private droppedCount: number = 0; - private expiredCount: number = 0; - private replayedCount: number = 0; - - constructor(logger: Logger, config: CommandQueueConfig = {}) { - this.logger = logger; - this.config = { - ...DEFAULT_CONFIG, - ...config - }; - - // Start cleanup timer - this.startCleanupTimer(); - } - - /** - * Enqueue a command to be sent when the connection is restored - * @param command The command to queue (without queuedAt, which will be added automatically) - * @returns Result indicating whether the command was queued successfully - */ - public enqueue(command: Omit): EnqueueResult { - // Check if queue is full - if (this.queue.length >= this.config.maxSize) { - this.droppedCount++; - this.logger.warn(`Command queue full (${this.config.maxSize}), dropping command: ${command.request.method}`); - - // Reject the command immediately - command.reject(new McpUnityError( - ErrorType.CONNECTION, - `Command queue full (${this.config.maxSize} commands). Try again later.` - )); - - return { - success: false, - reason: `Queue is full (max: ${this.config.maxSize})` - }; - } - - const queuedCommand: QueuedCommand = { - ...command, - queuedAt: Date.now(), - timeout: command.timeout ?? this.config.defaultTimeout - }; - - this.queue.push(queuedCommand); - const position = this.queue.length; - - this.logger.debug(`Queued command ${command.id} (${command.request.method}), position: ${position}/${this.config.maxSize}`); - - return { - success: true, - position - }; - } - - /** - * Get the number of commands currently in the queue - */ - public get size(): number { - return this.queue.length; - } - - /** - * Check if the queue is empty - */ - public get isEmpty(): boolean { - return this.queue.length === 0; - } - - /** - * Check if the queue is full - */ - public get isFull(): boolean { - return this.queue.length >= this.config.maxSize; - } - - /** - * Get all queued commands and clear the queue. - * Used when connection is restored to replay commands. - * Expired commands are filtered out and rejected before returning. - * @returns Array of valid (non-expired) queued commands - */ - public drain(): QueuedCommand[] { - // Clean up expired commands first - this.cleanupExpired(); - - const commands = [...this.queue]; - this.queue = []; - - if (commands.length > 0) { - this.logger.info(`Draining ${commands.length} commands from queue for replay`); - } - - return commands; - } - - /** - * Peek at the next command without removing it - * @returns The next command in the queue, or undefined if empty - */ - public peek(): QueuedCommand | undefined { - return this.queue[0]; - } - - /** - * Clear all queued commands, rejecting each with an error - * @param reason The reason for clearing the queue - */ - public clear(reason: string = 'Queue cleared'): void { - const count = this.queue.length; - - for (const command of this.queue) { - command.reject(new McpUnityError( - ErrorType.CONNECTION, - reason - )); - } - - this.queue = []; - - if (count > 0) { - this.logger.info(`Cleared ${count} commands from queue: ${reason}`); - } - } - - /** - * Remove expired commands from the queue - * @returns Number of commands that were expired and removed - */ - public cleanupExpired(): number { - const now = Date.now(); - const initialSize = this.queue.length; - - this.queue = this.queue.filter(command => { - const timeout = command.timeout ?? this.config.defaultTimeout; - const isExpired = (now - command.queuedAt) > timeout; - - if (isExpired) { - this.expiredCount++; - this.logger.debug(`Command ${command.id} (${command.request.method}) expired after ${timeout}ms`); - - command.reject(new McpUnityError( - ErrorType.TIMEOUT, - `Command expired after ${timeout}ms in queue` - )); - - return false; - } - - return true; - }); - - const expiredCount = initialSize - this.queue.length; - if (expiredCount > 0) { - this.logger.info(`Cleaned up ${expiredCount} expired commands from queue`); - } - - return expiredCount; - } - - /** - * Start the periodic cleanup timer - */ - private startCleanupTimer(): void { - if (this.cleanupTimer) { - clearInterval(this.cleanupTimer); - } - - this.cleanupTimer = setInterval(() => { - this.cleanupExpired(); - }, this.config.cleanupInterval); - - // Don't prevent process exit - this.cleanupTimer.unref(); - } - - /** - * Stop the cleanup timer - */ - public stopCleanupTimer(): void { - if (this.cleanupTimer) { - clearInterval(this.cleanupTimer); - this.cleanupTimer = null; - } - } - - /** - * Record that a command was successfully replayed - */ - public recordReplaySuccess(): void { - this.replayedCount++; - } - - /** - * Get statistics about the command queue - */ - public getStats(): CommandQueueStats { - return { - size: this.queue.length, - maxSize: this.config.maxSize, - droppedCount: this.droppedCount, - expiredCount: this.expiredCount, - replayedCount: this.replayedCount - }; - } - - /** - * Reset statistics counters - */ - public resetStats(): void { - this.droppedCount = 0; - this.expiredCount = 0; - this.replayedCount = 0; - } - - /** - * Update configuration dynamically - * Note: maxSize changes won't affect already-queued commands - */ - public updateConfig(config: Partial): void { - this.config = { ...this.config, ...config }; - - // Restart cleanup timer if interval changed - if (config.cleanupInterval !== undefined) { - this.startCleanupTimer(); - } - } - - /** - * Clean up resources - */ - public dispose(): void { - this.stopCleanupTimer(); - this.clear('Command queue disposed'); - } -} diff --git a/Server~/src/unity/mcpUnity.ts b/Server~/src/unity/mcpUnity.ts deleted file mode 100644 index aabd80ff..00000000 --- a/Server~/src/unity/mcpUnity.ts +++ /dev/null @@ -1,541 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { Logger } from '../utils/logger.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; -import { promises as fs } from 'fs'; -import path from 'path'; -import { UnityConnection, ConnectionState, ConnectionStateChange, UnityConnectionConfig } from './unityConnection.js'; -import { CommandQueue, CommandQueueConfig, CommandQueueStats, QueuedCommand } from './commandQueue.js'; - -// Top-level constant for the Unity settings JSON path -const MCP_UNITY_SETTINGS_PATH = path.resolve(process.cwd(), './ProjectSettings/McpUnitySettings.json'); - -interface PendingRequest { - resolve: (value: any) => void; - reject: (reason: any) => void; - timeout: NodeJS.Timeout; -} - -interface UnityRequest { - id?: string; - method: string; - params: any; -} - -interface UnityResponse { - jsonrpc: string; - id: string; - result?: any; - error?: { - message: string; - type: string; - details?: any; - }; -} - -/** - * Connection state change callback type - */ -export type ConnectionStateCallback = (change: ConnectionStateChange) => void; - -// Re-export connection types for consumers -export { ConnectionState, type ConnectionStateChange } from './unityConnection.js'; -export { type CommandQueueConfig, type CommandQueueStats } from './commandQueue.js'; - -/** - * Options for sending a request - */ -export interface SendRequestOptions { - /** If true, queue the command when disconnected instead of failing immediately (default: uses queueingEnabled setting) */ - queueIfDisconnected?: boolean; - /** Custom timeout for this request in milliseconds */ - timeout?: number; -} - -/** - * Configuration for McpUnity - */ -export interface McpUnityConfig { - /** Command queue configuration */ - queue?: CommandQueueConfig; - /** Whether command queuing is enabled by default (default: true) */ - queueingEnabled?: boolean; -} - -export class McpUnity { - private logger: Logger; - private port: number = 8090; - private host: string = 'localhost'; - private requestTimeout = 10000; - - private connection: UnityConnection | null = null; - private pendingRequests: Map = new Map(); - private clientName: string = ''; - - // Connection state listeners - private stateListeners: Set = new Set(); - - // Command queue for handling commands during disconnection - private commandQueue: CommandQueue; - private queueingEnabled: boolean; - - // Flag to track if we're currently replaying queued commands - private isReplayingQueue: boolean = false; - - constructor(logger: Logger, config?: McpUnityConfig) { - this.logger = logger; - this.commandQueue = new CommandQueue(logger, config?.queue); - this.queueingEnabled = config?.queueingEnabled ?? true; - } - - /** - * Enable or disable command queuing - */ - public setQueueingEnabled(enabled: boolean): void { - this.queueingEnabled = enabled; - this.logger.info(`Command queuing ${enabled ? 'enabled' : 'disabled'}`); - } - - /** - * Check if command queuing is enabled - */ - public get isQueueingEnabled(): boolean { - return this.queueingEnabled; - } - - /** - * Get command queue statistics - */ - public getQueueStats(): CommandQueueStats { - return this.commandQueue.getStats(); - } - - /** - * Get number of commands currently queued - */ - public get queuedCommandCount(): number { - return this.commandQueue.size; - } - - /** - * Start the Unity connection - * @param clientName Optional name of the MCP client connecting to Unity - */ - public async start(clientName?: string): Promise { - try { - this.logger.info('Attempting to read startup parameters...'); - await this.parseAndSetConfig(); - - this.clientName = clientName || ''; - - // Create connection with configuration - const config: UnityConnectionConfig = { - host: this.host, - port: this.port, - requestTimeout: this.requestTimeout, - clientName: this.clientName, - // Use defaults for reconnection and heartbeat from UnityConnection - }; - - this.connection = new UnityConnection(this.logger, config); - - // Set up event handlers - this.connection.on('stateChange', (change: ConnectionStateChange) => { - this.handleStateChange(change); - }); - - this.connection.on('message', (data: string) => { - this.handleMessage(data); - }); - - this.connection.on('error', (error: McpUnityError) => { - this.logger.error(`Connection error: ${error.message}`); - // Reject pending requests on connection error - this.rejectAllPendingRequests(error); - }); - - this.logger.info('Attempting to connect to Unity WebSocket...'); - await this.connection.connect(); - this.logger.info('Successfully connected to Unity WebSocket'); - - if (clientName) { - this.logger.info(`Client identified to Unity as: ${clientName}`); - } - } catch (error) { - this.logger.warn(`Could not connect to Unity WebSocket: ${error instanceof Error ? error.message : String(error)}`); - this.logger.warn('Will retry connection on next request (with automatic reconnection)'); - } - - return Promise.resolve(); - } - - /** - * Reads our configuration file and sets parameters of the server based on them. - */ - private async parseAndSetConfig() { - const config = await this.readConfigFileAsJson(); - - const configPort = config.Port; - this.port = configPort ? parseInt(configPort, 10) : 8090; - this.logger.info(`Using port: ${this.port} for Unity WebSocket connection`); - - // Check environment variable first, then config file, then default to localhost - const configHost = process.env.UNITY_HOST || config.Host; - this.host = configHost || 'localhost'; - - // Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds) - const configTimeout = config.RequestTimeoutSeconds; - this.requestTimeout = configTimeout ? parseInt(configTimeout, 10) * 1000 : 10000; - this.logger.info(`Using request timeout: ${this.requestTimeout / 1000} seconds`); - } - - /** - * Handle connection state changes - */ - private handleStateChange(change: ConnectionStateChange): void { - this.logger.debug(`Connection state changed: ${change.previousState} -> ${change.currentState}`); - - // Notify all listeners - for (const listener of this.stateListeners) { - try { - listener(change); - } catch (err) { - this.logger.error(`Error in state listener: ${err instanceof Error ? err.message : String(err)}`); - } - } - - // Handle specific state transitions - if (change.currentState === ConnectionState.Connected && - (change.previousState === ConnectionState.Reconnecting || - change.previousState === ConnectionState.Connecting)) { - // Connection restored - replay queued commands - this.replayQueuedCommands(); - } else if (change.currentState === ConnectionState.Disconnected) { - // Clear the queue when we're fully disconnected (not reconnecting) - // This happens when max reconnection attempts are reached - if (change.reason?.includes('Max reconnection attempts')) { - this.commandQueue.clear(change.reason); - } - // Reject all pending requests when disconnected - this.rejectAllPendingRequests( - new McpUnityError(ErrorType.CONNECTION, change.reason || 'Connection lost') - ); - } - } - - /** - * Replay queued commands after connection is restored - */ - private async replayQueuedCommands(): Promise { - if (this.isReplayingQueue) { - this.logger.debug('Already replaying queue, skipping'); - return; - } - - const commands = this.commandQueue.drain(); - - if (commands.length === 0) { - return; - } - - this.isReplayingQueue = true; - this.logger.info(`Replaying ${commands.length} queued commands`); - - for (const command of commands) { - try { - // Send the command directly using internal method - const result = await this.sendRequestInternal(command.request, command.timeout); - command.resolve(result); - this.commandQueue.recordReplaySuccess(); - } catch (error) { - command.reject(error); - } - } - - this.isReplayingQueue = false; - this.logger.info(`Finished replaying queued commands (${this.commandQueue.getStats().replayedCount} successful)`); - } - - /** - * Handle messages received from Unity - */ - private handleMessage(data: string): void { - try { - const response = JSON.parse(data) as UnityResponse; - const pendingCount = this.pendingRequests.size; - - if (!response.id) { - this.logger.warn(`Ignoring Unity WebSocket message without request id; pending requests: ${pendingCount}`); - return; - } - - if (!this.pendingRequests.has(response.id)) { - this.logger.warn(`Ignoring Unity response for unknown request ${response.id}; pending requests: ${pendingCount}`); - return; - } - - this.logger.info(`Received Unity response for request ${response.id}; pending requests before match: ${pendingCount}`); - const request = this.pendingRequests.get(response.id)!; - clearTimeout(request.timeout); - this.pendingRequests.delete(response.id); - - if (response.error) { - request.reject(new McpUnityError( - ErrorType.TOOL_EXECUTION, - response.error.message || 'Unknown error', - response.error.details - )); - } else { - request.resolve(response.result); - } - } catch (e) { - this.logger.error(`Error parsing WebSocket message: ${e instanceof Error ? e.message : String(e)}`); - } - } - - /** - * Reject all pending requests with an error - */ - private rejectAllPendingRequests(error: McpUnityError): void { - for (const [id, request] of this.pendingRequests.entries()) { - clearTimeout(request.timeout); - request.reject(error); - this.pendingRequests.delete(id); - } - } - - /** - * Stop the Unity connection and clean up resources - */ - public async stop(): Promise { - // Dispose the command queue - this.commandQueue.dispose(); - - if (this.connection) { - this.connection.disconnect('Server stopping'); - this.connection.removeAllListeners(); - this.connection = null; - } - this.rejectAllPendingRequests(new McpUnityError(ErrorType.CONNECTION, 'Server stopped')); - this.logger.info('Unity WebSocket client stopped'); - return Promise.resolve(); - } - - /** - * Send a request to the Unity server - * @param request The request to send - * @param options Optional settings for the request - */ - public async sendRequest(request: UnityRequest, options: SendRequestOptions = {}): Promise { - const { queueIfDisconnected = this.queueingEnabled, timeout } = options; - const requestId = request.id as string || uuidv4(); - const message: UnityRequest = { - ...request, - id: requestId - }; - - // If connected, send directly - if (this.isConnected) { - return this.sendRequestInternal(message, timeout); - } - - // If not started, throw error - if (!this.connection) { - throw new McpUnityError(ErrorType.CONNECTION, 'Not started - call start() first'); - } - - // If reconnecting and queuing is enabled, queue the command - if (queueIfDisconnected && this.connectionState === ConnectionState.Reconnecting) { - this.logger.debug(`Queuing command ${requestId} (${request.method}) - reconnecting...`); - - return new Promise((resolve, reject) => { - const result = this.commandQueue.enqueue({ - id: requestId, - request: message, - resolve, - reject, - timeout - }); - - if (result.success) { - this.logger.info(`Command ${requestId} queued at position ${result.position}`); - } - // If queuing failed, the command was already rejected by the queue - }); - } - - // If connecting and queuing is enabled, queue the command - if (queueIfDisconnected && this.connectionState === ConnectionState.Connecting) { - this.logger.debug(`Queuing command ${requestId} (${request.method}) - connecting...`); - - return new Promise((resolve, reject) => { - const result = this.commandQueue.enqueue({ - id: requestId, - request: message, - resolve, - reject, - timeout - }); - - if (result.success) { - this.logger.info(`Command ${requestId} queued at position ${result.position}`); - } - }); - } - - // Not connected - try to connect first - this.logger.info('Not connected to Unity, connecting first...'); - - try { - await this.connection.connect(); - // Connection successful, send the request - return this.sendRequestInternal(message, timeout); - } catch (error) { - // Connection failed - if queuing is enabled, queue the command - if (queueIfDisconnected) { - this.logger.debug(`Queuing command ${requestId} (${request.method}) - connection failed, will retry`); - - return new Promise((resolve, reject) => { - const result = this.commandQueue.enqueue({ - id: requestId, - request: message, - resolve, - reject, - timeout - }); - - if (result.success) { - this.logger.info(`Command ${requestId} queued at position ${result.position}, waiting for reconnection`); - } - }); - } - - throw new McpUnityError( - ErrorType.CONNECTION, - `Not connected to Unity: ${error instanceof Error ? error.message : String(error)}` - ); - } - } - - /** - * Internal method to send a request directly to Unity - * Bypasses queuing logic - assumes connection is already established - */ - private sendRequestInternal(request: UnityRequest, customTimeout?: number): Promise { - const requestId = request.id as string; - const timeoutMs = customTimeout ?? this.requestTimeout; - - return new Promise((resolve, reject) => { - if (!this.connection || !this.isConnected) { - reject(new McpUnityError(ErrorType.CONNECTION, 'Not connected to Unity')); - return; - } - - // Create timeout for the request - const timeout = setTimeout(() => { - if (this.pendingRequests.has(requestId)) { - this.logger.error(`Request ${requestId} timed out after ${timeoutMs}ms`); - this.pendingRequests.delete(requestId); - reject(new McpUnityError(ErrorType.TIMEOUT, 'Request timed out')); - } - }, timeoutMs); - - // Store pending request - const pendingBeforeSend = this.pendingRequests.size; - this.pendingRequests.set(requestId, { - resolve, - reject, - timeout - }); - - try { - this.logger.info(`Sending Unity request ${requestId} (${request.method}) while connection state is ${this.connectionState}; pending requests before send: ${pendingBeforeSend}`); - this.connection.send(JSON.stringify(request)); - this.logger.debug(`Request sent: ${requestId}`); - } catch (err) { - clearTimeout(timeout); - this.pendingRequests.delete(requestId); - reject(new McpUnityError(ErrorType.CONNECTION, `Send failed: ${err instanceof Error ? err.message : String(err)}`)); - } - }); - } - - /** - * Check if connected to Unity - * Only returns true if the connection is guaranteed to be active - */ - public get isConnected(): boolean { - return this.connection !== null && this.connection.isConnected; - } - - /** - * Get current connection state - */ - public get connectionState(): ConnectionState { - return this.connection?.connectionState ?? ConnectionState.Disconnected; - } - - /** - * Check if currently connecting or reconnecting - */ - public get isConnecting(): boolean { - return this.connection?.isConnecting ?? false; - } - - /** - * Add a listener for connection state changes - * @param callback Function to call when connection state changes - * @returns Function to remove the listener - */ - public onConnectionStateChange(callback: ConnectionStateCallback): () => void { - this.stateListeners.add(callback); - return () => { - this.stateListeners.delete(callback); - }; - } - - /** - * Force a reconnection to Unity - * Useful when Unity has reloaded and the connection may be stale - */ - public forceReconnect(): void { - if (this.connection) { - this.connection.forceReconnect(); - } else { - this.logger.warn('Cannot force reconnect - not started'); - } - } - - /** - * Get connection statistics - */ - public getConnectionStats(): { - state: ConnectionState; - pendingRequests: number; - reconnectAttempt?: number; - timeSinceLastPong?: number; - } { - const stats = this.connection?.getStats(); - return { - state: stats?.state ?? ConnectionState.Disconnected, - pendingRequests: this.pendingRequests.size, - reconnectAttempt: stats?.reconnectAttempt, - timeSinceLastPong: stats?.timeSinceLastPong - }; - } - - /** - * Read the McpUnitySettings.json file and return its contents as a JSON object. - * @returns a JSON object with the contents of the McpUnitySettings.json file. - */ - private async readConfigFileAsJson(): Promise { - const configPath = MCP_UNITY_SETTINGS_PATH; - try { - const content = await fs.readFile(configPath, 'utf-8'); - const json = JSON.parse(content); - return json; - } catch (err) { - this.logger.debug(`McpUnitySettings.json not found or unreadable: ${err instanceof Error ? err.message : String(err)}`); - return {}; - } - } -} diff --git a/Server~/src/unity/officialUnityMcpClient.ts b/Server~/src/unity/officialUnityMcpClient.ts new file mode 100644 index 00000000..9f999318 --- /dev/null +++ b/Server~/src/unity/officialUnityMcpClient.ts @@ -0,0 +1,507 @@ +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; +import type { + Transport, + TransportSendOptions, +} from '@modelcontextprotocol/sdk/shared/transport.js'; +import { + CallToolResultSchema, + ErrorCode, + McpError, + type CallToolResult, + type JSONRPCMessage, + type MessageExtraInfo, +} from '@modelcontextprotocol/sdk/types.js'; +import { boundedErrorMessage } from '../utils/boundedError.js'; + +export interface OfficialUnitySession { + callTool(name: string, args: Record): Promise; +} + +export interface OfficialUnitySessionStart { + readonly ready: Promise; + close(): Promise; +} + +export interface OfficialUnitySessionOptions { + cliPath: string; + projectPath: string; +} + +export type OfficialUnitySessionFactory = ( + options: OfficialUnitySessionOptions, +) => OfficialUnitySessionStart; + +export type UnityConnectionState = + | 'disconnected' + | 'connecting' + | 'connected' + | 'closed'; + +export interface OfficialUnityMcpClientOptions extends OfficialUnitySessionOptions { + sessionFactory?: OfficialUnitySessionFactory; +} + +interface OwnedConnection { + start: OfficialUnitySessionStart; + session: OfficialUnitySession; +} + +const CLOSED_MESSAGE = 'Official Unity MCP client is closed.'; + +export class OfficialUnityMcpClient { + private readonly options: OfficialUnitySessionOptions; + private readonly sessionFactory: OfficialUnitySessionFactory; + private readonly startTeardowns = + new WeakMap>(); + private readonly closeSignal: Promise; + private signalClose!: () => void; + private active?: OwnedConnection; + private activeStart?: OfficialUnitySessionStart; + private startupPromise?: Promise; + private teardownPromise?: Promise; + private closePromise?: Promise; + private connectionState: UnityConnectionState = 'disconnected'; + private closed = false; + + constructor(options: OfficialUnityMcpClientOptions) { + this.options = { + cliPath: options.cliPath, + projectPath: options.projectPath, + }; + this.sessionFactory = options.sessionFactory ?? createOfficialUnitySessionStart; + this.closeSignal = new Promise((resolve) => { + this.signalClose = resolve; + }); + } + + get state(): UnityConnectionState { + return this.connectionState; + } + + async readTool( + name: string, + args: Record, + ): Promise { + this.assertOpen(); + const firstConnection = await this.getConnection(); + try { + return await this.raceWithClose(firstConnection.session.callTool(name, args)); + } catch (firstError) { + if (!isTransportInterruption(firstError)) { + throw firstError; + } + + await this.discardConnection(firstConnection); + this.assertOpen(); + const retryConnection = await this.getConnection(); + try { + return await this.raceWithClose(retryConnection.session.callTool(name, args)); + } catch (retryError) { + if (isTransportInterruption(retryError)) { + await this.discardConnection(retryConnection); + } + throw retryError; + } + } + } + + close(): Promise { + if (this.closePromise) return this.closePromise; + this.closed = true; + this.connectionState = 'closed'; + this.signalClose(); + + const start = this.activeStart; + const teardown = this.teardownPromise; + this.active = undefined; + this.activeStart = undefined; + const pending = new Set>(); + if (teardown) pending.add(teardown); + if (start) pending.add(this.closeStart(start)); + this.closePromise = Promise.all([...pending]).then(() => undefined); + return this.closePromise; + } + + private async getConnection(): Promise { + this.assertOpen(); + if (this.teardownPromise) { + await this.raceWithClose(this.teardownPromise); + this.assertOpen(); + } + if (this.active) return this.active; + if (this.startupPromise) { + return this.raceWithClose(this.startupPromise); + } + + this.connectionState = 'connecting'; + let start: OfficialUnitySessionStart; + try { + start = this.sessionFactory(this.options); + } catch (error) { + this.connectionState = 'disconnected'; + throw error; + } + + this.activeStart = start; + const pending = start.ready + .then((session): OwnedConnection => { + if (this.closed || this.activeStart !== start) { + throw new Error(CLOSED_MESSAGE); + } + const connection = { start, session }; + this.active = connection; + this.connectionState = 'connected'; + return connection; + }) + .catch(async (error: unknown) => { + if (this.activeStart === start) { + this.activeStart = undefined; + if (!this.closed) { + this.connectionState = 'disconnected'; + } + } + await this.trackTeardown(start); + throw error; + }) + .finally(() => { + if (this.startupPromise === pending) { + this.startupPromise = undefined; + } + }); + this.startupPromise = pending; + + return this.raceWithClose(pending); + } + + private async discardConnection(candidate: OwnedConnection): Promise { + if (this.active?.start === candidate.start) { + this.active = undefined; + } + if (this.activeStart === candidate.start) { + this.activeStart = undefined; + } + if (!this.closed) { + this.connectionState = 'disconnected'; + } + await this.trackTeardown(candidate.start); + } + + private closeStart(start: OfficialUnitySessionStart): Promise { + const existing = this.startTeardowns.get(start); + if (existing) return existing; + const teardown = Promise.resolve().then(() => start.close()); + this.startTeardowns.set(start, teardown); + return teardown; + } + + private async trackTeardown(start: OfficialUnitySessionStart): Promise { + const teardown = this.closeStart(start); + this.teardownPromise = teardown; + try { + await teardown; + } finally { + if (this.teardownPromise === teardown) { + this.teardownPromise = undefined; + } + } + } + + private async raceWithClose(operation: Promise): Promise { + return Promise.race([ + operation, + this.closeSignal.then(() => { + throw new Error(CLOSED_MESSAGE); + }), + ]); + } + + private assertOpen(): void { + if (this.closed) { + throw new Error(CLOSED_MESSAGE); + } + } +} + +function isTransportInterruption(error: unknown): boolean { + if (error instanceof McpError && error.code === ErrorCode.ConnectionClosed) { + return true; + } + const code = (error as { code?: unknown } | null)?.code; + if ( + typeof code === 'string' && + ['EPIPE', 'ECONNRESET', 'ECONNREFUSED', 'ENOTCONN', 'ERR_STREAM_DESTROYED'].includes( + code, + ) + ) { + return true; + } + const message = error instanceof Error ? error.message : String(error); + return /^(Connection closed|Not connected)$/i.test(message.trim()) || + /transport (?:is )?closed/i.test(message) || + /Unity CLI process exited/i.test(message); +} + +type UnitySdkTransport = Transport; + +interface UnitySdkClient { + connect( + transport: UnitySdkTransport, + options: { + signal: AbortSignal; + timeout: number; + maxTotalTimeout: number; + }, + ): Promise; + callTool( + request: { name: string; arguments: Record }, + schema: typeof CallToolResultSchema, + ): Promise; + close(): Promise; +} + +export interface OfficialUnitySessionDependencies { + createTransport(options: OfficialUnitySessionOptions): UnitySdkTransport; + createClient(): UnitySdkClient; + sdkCloseGraceMs?: number; + transportCloseTimeoutMs?: number; +} + +const INITIALIZE_TIMEOUT_MS = 10_000; +// StdioClientTransport returns immediately after its final owned-child signal. +// A close event should follow in the next event-loop turns; allow a bounded +// scheduling margin so shutdown cannot claim success before process closure. +const CHILD_CLOSE_OBSERVATION_TIMEOUT_MS = 500; +// StdioClientTransport uses two successive 2-second graceful/SIGTERM windows. +// Let both finish so its unref'ed timers expire before forced cleanup begins. +const SDK_CLOSE_GRACE_MS = 4_250; +// A direct StdioClientTransport.close() fallback needs the same complete +// graceful/SIGTERM sequence plus the wrapper's close-event observation margin. +// The transport retains its owned ChildProcess handle throughout that sequence. +const TRANSPORT_CLOSE_TIMEOUT_MS = 4_750; + +export class OwnedStdioClientTransport implements Transport { + onclose?: () => void; + onerror?: (error: Error) => void; + onmessage?: ( + message: T, + extra?: MessageExtraInfo, + ) => void; + + private readonly childClosed: Promise; + private resolveChildClosed!: () => void; + private closePromise?: Promise; + private startSucceeded = false; + private childCloseObserved = false; + private closeForwarded = false; + + constructor( + private readonly underlying: Transport, + private readonly closeObservationTimeoutMs = + CHILD_CLOSE_OBSERVATION_TIMEOUT_MS, + ) { + this.childClosed = new Promise((resolve) => { + this.resolveChildClosed = resolve; + }); + this.underlying.onclose = () => { + if (!this.childCloseObserved) { + this.childCloseObserved = true; + this.resolveChildClosed(); + } + if (!this.closeForwarded) { + this.closeForwarded = true; + this.onclose?.(); + } + }; + this.underlying.onerror = (error) => this.onerror?.(error); + this.underlying.onmessage = (message, extra) => + this.onmessage?.(message, extra); + } + + get sessionId(): string | undefined { + return this.underlying.sessionId; + } + + set sessionId(value: string | undefined) { + this.underlying.sessionId = value; + } + + async start(): Promise { + await this.underlying.start(); + this.startSucceeded = true; + } + + send( + message: JSONRPCMessage, + options?: TransportSendOptions, + ): Promise { + return this.underlying.send(message, options); + } + + setProtocolVersion(version: string): void { + this.underlying.setProtocolVersion?.(version); + } + + close(): Promise { + if (this.closePromise) return this.closePromise; + this.closePromise = this.closeOwnedChild(); + return this.closePromise; + } + + private async closeOwnedChild(): Promise { + await this.underlying.close(); + if (!this.startSucceeded || this.childCloseObserved) return; + + const observation = await settleWithin( + this.childClosed, + this.closeObservationTimeoutMs, + ); + if (observation.status === 'fulfilled') return; + throw new Error( + `Unity CLI child did not report process closure within ${this.closeObservationTimeoutMs}ms after stdio transport teardown.`, + ); + } +} + +const DEFAULT_SESSION_DEPENDENCIES: OfficialUnitySessionDependencies = { + createTransport: (options) => + new OwnedStdioClientTransport( + new StdioClientTransport({ + command: options.cliPath, + args: ['mcp', '--project-path', options.projectPath], + stderr: 'inherit', + }), + ), + createClient: () => { + const client = new Client( + { name: 'mcp-unity-companion', version: '2.0.0' }, + { capabilities: {} }, + ); + return { + connect: (transport, options) => + client.connect(transport, options), + callTool: (request, schema) => client.callTool(request, schema), + close: () => client.close(), + }; + }, +}; + +export function createOfficialUnitySessionStart( + options: OfficialUnitySessionOptions, + dependencies: OfficialUnitySessionDependencies = DEFAULT_SESSION_DEPENDENCIES, +): OfficialUnitySessionStart { + const transport = dependencies.createTransport(options); + const client = dependencies.createClient(); + const initializeAbort = new AbortController(); + let closeRequested = false; + let teardownPromise: Promise | undefined; + + // Client.connect takes ownership of the transport synchronously before its first + // await, so close() can terminate startup even while MCP initialization is pending. + const ready = client + .connect(transport, { + signal: initializeAbort.signal, + timeout: INITIALIZE_TIMEOUT_MS, + maxTotalTimeout: INITIALIZE_TIMEOUT_MS, + }) + .then((): OfficialUnitySession => { + if (closeRequested) { + throw new Error(CLOSED_MESSAGE); + } + return { + callTool: async (name, args) => { + const result = await client.callTool( + { name, arguments: args }, + CallToolResultSchema, + ); + return CallToolResultSchema.parse(result); + }, + }; + }); + + return { + ready, + close(): Promise { + if (teardownPromise) return teardownPromise; + closeRequested = true; + initializeAbort.abort(); + teardownPromise = teardownSdkSession(client, transport, { + sdkCloseGraceMs: + dependencies.sdkCloseGraceMs ?? SDK_CLOSE_GRACE_MS, + transportCloseTimeoutMs: + dependencies.transportCloseTimeoutMs ?? TRANSPORT_CLOSE_TIMEOUT_MS, + }); + return teardownPromise; + }, + }; +} + +interface TeardownDeadlines { + sdkCloseGraceMs: number; + transportCloseTimeoutMs: number; +} + +async function teardownSdkSession( + client: UnitySdkClient, + transport: UnitySdkTransport, + deadlines: TeardownDeadlines, +): Promise { + const clientClose = invokeClose(() => client.close()); + const clientResult = await settleWithin(clientClose, deadlines.sdkCloseGraceMs); + + if (clientResult.status === 'fulfilled') return; + + const transportClose = invokeClose(() => transport.close()); + const transportResult = await settleWithin( + transportClose, + deadlines.transportCloseTimeoutMs, + ); + if (transportResult.status === 'fulfilled') return; + if (transportResult.status === 'timed-out') { + throw new Error( + `Unity CLI transport teardown timed out after ${deadlines.transportCloseTimeoutMs}ms.`, + ); + } + throw new Error( + boundedErrorMessage( + 'Unity CLI transport teardown failed: ', + transportResult.reason, + ), + ); +} + +type Settlement = + | { status: 'fulfilled' } + | { status: 'rejected'; reason: unknown } + | { status: 'timed-out' }; + +function invokeClose(operation: () => Promise): Promise { + try { + return Promise.resolve(operation()); + } catch (error) { + return Promise.reject(error); + } +} + +function settleWithin( + operation: Promise, + timeoutMs: number, +): Promise { + return new Promise((resolve) => { + let settled = false; + const finish = (result: Settlement): void => { + if (settled) return; + settled = true; + clearTimeout(timeout); + resolve(result); + }; + const timeout = setTimeout( + () => finish({ status: 'timed-out' }), + Math.max(0, timeoutMs), + ); + operation.then( + () => finish({ status: 'fulfilled' }), + (reason: unknown) => finish({ status: 'rejected', reason }), + ); + }); +} diff --git a/Server~/src/unity/unityConnection.ts b/Server~/src/unity/unityConnection.ts deleted file mode 100644 index 7d311656..00000000 --- a/Server~/src/unity/unityConnection.ts +++ /dev/null @@ -1,561 +0,0 @@ -import WebSocket from 'ws'; -import { EventEmitter } from 'events'; -import { Logger } from '../utils/logger.js'; -import { McpUnityError, ErrorType } from '../utils/errors.js'; - -/** - * Connection states for the Unity WebSocket connection - */ -export enum ConnectionState { - Disconnected = 'disconnected', - Connecting = 'connecting', - Connected = 'connected', - Reconnecting = 'reconnecting' -} - -/** - * Custom WebSocket close codes for Unity-specific events - * Range 4000-4999 is reserved for application use - */ -export const UnityCloseCode = { - /** Unity is entering Play mode - use fast polling instead of backoff */ - PLAY_MODE: 4001 -} as const; - -/** - * Connection state change event data - */ -export interface ConnectionStateChange { - previousState: ConnectionState; - currentState: ConnectionState; - reason?: string; - attemptNumber?: number; -} - -/** - * Configuration for the Unity connection - */ -export interface UnityConnectionConfig { - host: string; - port: number; - requestTimeout: number; - connectTimeout?: number; - clientName?: string; - - // Reconnection settings - minReconnectDelay?: number; // Default: 1000ms - maxReconnectDelay?: number; // Default: 30000ms - reconnectBackoffMultiplier?: number; // Default: 2 - maxReconnectAttempts?: number; // Default: unlimited (-1) - - // Heartbeat settings - heartbeatInterval?: number; // Default: 30000ms (30 seconds) - heartbeatTimeout?: number; // Default: 5000ms (5 seconds) - - // Play mode settings - playModePollingInterval?: number; // Default: 3000ms (3 seconds) - used instead of backoff during Play mode -} - -/** - * Default configuration values - */ -const DEFAULT_CONFIG = { - connectTimeout: 5000, - minReconnectDelay: 1000, - maxReconnectDelay: 30000, - reconnectBackoffMultiplier: 2, - maxReconnectAttempts: 50, // Prevent unbounded file descriptor accumulation (see #110) - heartbeatInterval: 30000, - heartbeatTimeout: 5000, - playModePollingInterval: 3000 // Fixed 3 second polling during Play mode -}; - -/** - * UnityConnection manages the WebSocket connection to Unity Editor - * with automatic reconnection, exponential backoff, and heartbeat monitoring. - * - * Events: - * - 'stateChange': Emitted when connection state changes - * - 'message': Emitted when a message is received from Unity - * - 'error': Emitted when an error occurs - */ -export class UnityConnection extends EventEmitter { - private logger: Logger; - private config: Required; - private ws: WebSocket | null = null; - private state: ConnectionState = ConnectionState.Disconnected; - - // Reconnection state - private reconnectAttempt: number = 0; - private reconnectTimer: NodeJS.Timeout | null = null; - private connectionTimeoutTimer: NodeJS.Timeout | null = null; - private isManualDisconnect: boolean = false; - private isPlayModeReconnect: boolean = false; // True when reconnecting due to Unity Play mode - - // Heartbeat state - private heartbeatTimer: NodeJS.Timeout | null = null; - private heartbeatTimeoutTimer: NodeJS.Timeout | null = null; - private lastPongTime: number = 0; - private awaitingPong: boolean = false; - - constructor(logger: Logger, config: UnityConnectionConfig) { - super(); - this.logger = logger; - this.config = { - ...DEFAULT_CONFIG, - ...config - } as Required; - } - - /** - * Get the current connection state - */ - public get connectionState(): ConnectionState { - return this.state; - } - - /** - * Check if currently connected - */ - public get isConnected(): boolean { - return this.state === ConnectionState.Connected && - this.ws !== null && - this.ws.readyState === WebSocket.OPEN; - } - - /** - * Check if currently connecting or reconnecting - */ - public get isConnecting(): boolean { - return this.state === ConnectionState.Connecting || - this.state === ConnectionState.Reconnecting; - } - - /** - * Get time since last successful heartbeat response (pong) - */ - public get timeSinceLastPong(): number { - if (this.lastPongTime === 0) return -1; - return Date.now() - this.lastPongTime; - } - - /** - * Update configuration dynamically - */ - public updateConfig(config: Partial): void { - this.config = { ...this.config, ...config }; - } - - /** - * Connect to Unity WebSocket server - */ - public async connect(): Promise { - if (this.isConnected) { - this.logger.debug('Already connected to Unity'); - return; - } - - if (this.isConnecting) { - this.logger.debug('Connection already in progress'); - return; - } - - this.isManualDisconnect = false; - return this.doConnect(); - } - - /** - * Disconnect from Unity WebSocket server - */ - public disconnect(reason?: string): void { - this.isManualDisconnect = true; - this.stopReconnectTimer(); - this.stopHeartbeat(); - this.closeWebSocket(reason || 'Manual disconnect'); - this.setState(ConnectionState.Disconnected, reason || 'Manual disconnect'); - } - - /** - * Send a message to Unity - */ - public send(message: string): void { - if (!this.isConnected || !this.ws) { - throw new McpUnityError(ErrorType.CONNECTION, 'Not connected to Unity'); - } - - try { - this.ws.send(message); - } catch (err) { - const errorMessage = err instanceof Error ? err.message : String(err); - throw new McpUnityError(ErrorType.CONNECTION, `Send failed: ${errorMessage}`); - } - } - - /** - * Get WebSocket instance (for advanced use) - */ - public get webSocket(): WebSocket | null { - return this.ws; - } - - /** - * Internal: Perform the actual connection - */ - private async doConnect(): Promise { - const isReconnecting = this.reconnectAttempt > 0; - this.setState( - isReconnecting ? ConnectionState.Reconnecting : ConnectionState.Connecting, - isReconnecting ? `Reconnection attempt ${this.reconnectAttempt}` : 'Connecting' - ); - - return new Promise((resolve, reject) => { - const wsUrl = `ws://${this.config.host}:${this.config.port}/McpUnity`; - this.logger.debug(`Connecting to ${wsUrl}...`); - - // Create connection options with headers for client identification - const options: WebSocket.ClientOptions = { - headers: { - 'X-Client-Name': this.config.clientName || '' - } - }; - - // Clean up existing socket first - this.closeWebSocket('Preparing new connection'); - - // Create new WebSocket - this.ws = new WebSocket(wsUrl, options); - - // Connection timeout - this.clearConnectionTimeout(); - this.connectionTimeoutTimer = setTimeout(() => { - if (this.ws && this.ws.readyState === WebSocket.CONNECTING) { - this.logger.warn('Connection timeout'); - this.closeWebSocket('Connection timeout'); - - const error = new McpUnityError(ErrorType.CONNECTION, 'Connection timeout'); - this.handleConnectionFailure(error); - reject(error); - } - }, this.config.connectTimeout); - - this.ws.onopen = () => { - this.clearConnectionTimeout(); - this.logger.info('WebSocket connected to Unity'); - - // Reset reconnection state on successful connection - this.reconnectAttempt = 0; - this.isPlayModeReconnect = false; // Clear Play mode flag - this.lastPongTime = Date.now(); - - this.setState(ConnectionState.Connected, 'Connection established'); - this.startHeartbeat(); - resolve(); - }; - - this.ws.onerror = (err) => { - this.clearConnectionTimeout(); - const errorMessage = err.message || 'Unknown error'; - this.logger.error(`WebSocket error: ${errorMessage}`); - - const error = new McpUnityError(ErrorType.CONNECTION, `Connection failed: ${errorMessage}`); - this.emit('error', error); - - // Don't reject here - let onclose handle cleanup and reconnection - }; - - this.ws.onmessage = (event) => { - this.emit('message', event.data.toString()); - }; - - this.ws.onclose = (event) => { - this.clearConnectionTimeout(); - this.stopHeartbeat(); - - const reason = event.reason || `Code: ${event.code}`; - this.logger.debug(`WebSocket closed: ${reason}`); - - // Check if Unity is entering Play mode (custom close code 4001) - if (event.code === UnityCloseCode.PLAY_MODE) { - this.logger.info('Unity entering Play mode - using fast polling for reconnection'); - this.isPlayModeReconnect = true; - } - - // Clear WebSocket reference - this.ws = null; - - // Handle reconnection if not manual disconnect - if (!this.isManualDisconnect) { - this.handleConnectionFailure(new McpUnityError(ErrorType.CONNECTION, reason)); - } else { - this.setState(ConnectionState.Disconnected, reason); - } - - // Reject if we were in initial connection - if (this.state === ConnectionState.Connecting) { - reject(new McpUnityError(ErrorType.CONNECTION, reason)); - } - }; - - // Handle WebSocket ping/pong for heartbeat - this.ws.on('pong', () => { - this.handlePong(); - }); - }); - } - - /** - * Handle connection failure and schedule reconnection - */ - private handleConnectionFailure(error: McpUnityError): void { - if (this.isManualDisconnect) { - this.setState(ConnectionState.Disconnected, 'Manual disconnect'); - return; - } - - // Check max reconnect attempts (skip for Play mode - unlimited retries) - if (!this.isPlayModeReconnect && - this.config.maxReconnectAttempts !== -1 && - this.reconnectAttempt >= this.config.maxReconnectAttempts) { - this.logger.error(`Max reconnection attempts (${this.config.maxReconnectAttempts}) reached`); - this.setState(ConnectionState.Disconnected, 'Max reconnection attempts reached'); - this.emit('error', new McpUnityError(ErrorType.CONNECTION, 'Max reconnection attempts reached')); - return; - } - - // Use fixed polling interval for Play mode, exponential backoff otherwise - const delay = this.isPlayModeReconnect - ? this.config.playModePollingInterval - : this.calculateBackoffDelay(); - - this.reconnectAttempt++; - - const modeInfo = this.isPlayModeReconnect ? ' (Play mode polling)' : ''; - this.logger.info(`Scheduling reconnection attempt ${this.reconnectAttempt} in ${delay}ms${modeInfo}`); - this.setState(ConnectionState.Reconnecting, `Waiting ${delay}ms before attempt ${this.reconnectAttempt}${modeInfo}`); - - this.reconnectTimer = setTimeout(() => { - this.reconnectTimer = null; - this.doConnect().catch((err) => { - this.logger.warn(`Reconnection attempt ${this.reconnectAttempt} failed: ${err.message}`); - }); - }, delay); - } - - /** - * Calculate exponential backoff delay - */ - private calculateBackoffDelay(): number { - const baseDelay = this.config.minReconnectDelay; - const multiplier = this.config.reconnectBackoffMultiplier; - const maxDelay = this.config.maxReconnectDelay; - - // Exponential backoff: base * multiplier^attempt - const delay = baseDelay * Math.pow(multiplier, this.reconnectAttempt); - - // Add jitter (0-20% random variation) to prevent thundering herd - const jitter = delay * 0.2 * Math.random(); - - return Math.min(delay + jitter, maxDelay); - } - - /** - * Stop reconnection timer - */ - private stopReconnectTimer(): void { - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer); - this.reconnectTimer = null; - } - this.reconnectAttempt = 0; - } - - /** - * Start heartbeat monitoring - */ - private startHeartbeat(): void { - this.stopHeartbeat(); - - if (this.config.heartbeatInterval <= 0) { - this.logger.debug('Heartbeat disabled'); - return; - } - - this.logger.debug(`Starting heartbeat with ${this.config.heartbeatInterval}ms interval`); - - this.heartbeatTimer = setInterval(() => { - this.sendHeartbeat(); - }, this.config.heartbeatInterval); - } - - /** - * Stop heartbeat monitoring - */ - private stopHeartbeat(): void { - if (this.heartbeatTimer) { - clearInterval(this.heartbeatTimer); - this.heartbeatTimer = null; - } - if (this.heartbeatTimeoutTimer) { - clearTimeout(this.heartbeatTimeoutTimer); - this.heartbeatTimeoutTimer = null; - } - this.awaitingPong = false; - } - - /** - * Send heartbeat ping - */ - private sendHeartbeat(): void { - if (!this.isConnected || !this.ws) { - return; - } - - // If we're still waiting for a pong from the last ping, connection may be stale - if (this.awaitingPong) { - this.logger.warn('No pong received for previous ping, connection may be stale'); - this.handleStaleConnection(); - return; - } - - try { - this.awaitingPong = true; - this.ws.ping(); - this.logger.debug('Heartbeat ping sent'); - - // Set timeout for pong response - this.heartbeatTimeoutTimer = setTimeout(() => { - if (this.awaitingPong) { - this.logger.warn('Heartbeat timeout - no pong received'); - this.handleStaleConnection(); - } - }, this.config.heartbeatTimeout); - } catch (err) { - this.logger.error(`Failed to send heartbeat: ${err instanceof Error ? err.message : String(err)}`); - this.awaitingPong = false; - } - } - - /** - * Handle pong response - */ - private handlePong(): void { - this.awaitingPong = false; - this.lastPongTime = Date.now(); - - if (this.heartbeatTimeoutTimer) { - clearTimeout(this.heartbeatTimeoutTimer); - this.heartbeatTimeoutTimer = null; - } - - this.logger.debug('Heartbeat pong received'); - } - - /** - * Handle stale connection detected by heartbeat - */ - private handleStaleConnection(): void { - this.logger.warn('Stale connection detected, forcing reconnection'); - this.awaitingPong = false; - - // Force close and trigger reconnection - this.closeWebSocket('Stale connection detected'); - this.handleConnectionFailure(new McpUnityError(ErrorType.CONNECTION, 'Stale connection detected')); - } - - /** - * Close WebSocket immediately - * - * Always uses terminate() instead of close() to prevent file descriptor - * accumulation. A graceful close (ws.close()) leaves the socket alive - * during the TCP close handshake, which can overlap with the next - * connection attempt and accumulate file descriptors on the Unity side. - * websocket-sharp uses Mono's IOSelector/select(), which crashes when - * file descriptor values exceed ~1024 (POSIX FD_SETSIZE limit). - * See: https://github.com/CoderGamester/mcp-unity/issues/110 - */ - private closeWebSocket(reason?: string): void { - if (!this.ws) return; - - this.logger.debug(`Closing WebSocket: ${reason || 'No reason'}`); - this.clearConnectionTimeout(); - - // Capture reference and null the field first to prevent any - // event handler from seeing a stale socket during teardown - const socket = this.ws; - this.ws = null; - - // Remove all event handlers before terminating - socket.onopen = null; - socket.onmessage = null; - socket.onerror = null; - socket.onclose = null; - socket.removeAllListeners('pong'); - - try { - // Always terminate immediately — no graceful close handshake. - // This ensures the underlying socket FD is released right away. - socket.terminate(); - } catch (err) { - this.logger.error(`Error closing WebSocket: ${err instanceof Error ? err.message : String(err)}`); - } - } - - private clearConnectionTimeout(): void { - if (this.connectionTimeoutTimer) { - clearTimeout(this.connectionTimeoutTimer); - this.connectionTimeoutTimer = null; - } - } - - /** - * Set connection state and emit event - */ - private setState(newState: ConnectionState, reason?: string): void { - if (this.state === newState) return; - - const previousState = this.state; - this.state = newState; - - const change: ConnectionStateChange = { - previousState, - currentState: newState, - reason, - attemptNumber: this.reconnectAttempt > 0 ? this.reconnectAttempt : undefined - }; - - this.logger.debug(`Connection state: ${previousState} -> ${newState} (${reason || 'no reason'})`); - this.emit('stateChange', change); - } - - /** - * Force a reconnection (useful after Unity domain reload) - */ - public forceReconnect(): void { - this.logger.info('Forcing reconnection...'); - this.isManualDisconnect = false; - this.stopReconnectTimer(); - this.closeWebSocket('Force reconnect'); - this.reconnectAttempt = 0; // Reset attempts for fresh reconnect - - this.doConnect().catch((err) => { - this.logger.warn(`Force reconnect failed: ${err.message}`); - }); - } - - /** - * Get connection statistics - */ - public getStats(): { - state: ConnectionState; - reconnectAttempt: number; - timeSinceLastPong: number; - isAwaitingPong: boolean; - } { - return { - state: this.state, - reconnectAttempt: this.reconnectAttempt, - timeSinceLastPong: this.timeSinceLastPong, - isAwaitingPong: this.awaitingPong - }; - } -} diff --git a/Server~/src/utils/boundedError.ts b/Server~/src/utils/boundedError.ts new file mode 100644 index 00000000..0730cc1a --- /dev/null +++ b/Server~/src/utils/boundedError.ts @@ -0,0 +1,54 @@ +export const ERROR_DETAIL_BUDGET_BYTES = 4 * 1024; +export const ERROR_TRUNCATION_MARKER = ' [truncated]'; + +export function boundedErrorDetail(error: unknown): string { + return boundedErrorText( + error instanceof Error ? error.message : String(error), + ); +} + +export function boundedErrorMessage( + prefix: string, + detail?: unknown, +): string { + return boundedErrorParts( + detail === undefined + ? [prefix] + : [ + prefix, + detail instanceof Error ? detail.message : String(detail), + ], + ); +} + +export function boundedError(error: unknown, prefix = ''): Error { + return new Error( + prefix + ? boundedErrorMessage(prefix, error) + : boundedErrorDetail(error), + ); +} + +export function boundedErrorText(value: string): string { + return boundedErrorParts([value]); +} + +function boundedErrorParts(parts: readonly string[]): string { + const markerBytes = Buffer.byteLength(ERROR_TRUNCATION_MARKER); + const contentBudget = ERROR_DETAIL_BUDGET_BYTES - markerBytes; + let output = ''; + let outputBytes = 0; + + for (const part of parts) { + for (const character of part) { + const characterBytes = Buffer.byteLength(character); + if (outputBytes + characterBytes > contentBudget) { + return output + ERROR_TRUNCATION_MARKER; + } + output += character; + outputBytes += characterBytes; + } + } + + return output; +} diff --git a/Server~/src/utils/errors.ts b/Server~/src/utils/errors.ts deleted file mode 100644 index 793fdfb9..00000000 --- a/Server~/src/utils/errors.ts +++ /dev/null @@ -1,41 +0,0 @@ -export enum ErrorType { - CONNECTION = 'connection_error', - TOOL_EXECUTION = 'tool_execution_error', - RESOURCE_FETCH = 'resource_fetch_error', - VALIDATION = 'validation_error', - INTERNAL = 'internal_error', - TIMEOUT = 'timeout_error' -} - -export class McpUnityError extends Error { - type: ErrorType; - details?: any; - - constructor(type: ErrorType, message: string, details?: any) { - super(message); - this.type = type; - this.details = details; - this.name = 'McpUnityError'; - } - - toJSON() { - return { - type: this.type, - message: this.message, - details: this.details - }; - } -} - -export function handleError(error: any, context: string): McpUnityError { - if (error instanceof McpUnityError) { - return error; - } - - // Handle standard errors - return new McpUnityError( - ErrorType.INTERNAL, - `${context} error: ${error.message || 'Unknown error'}`, - error - ); -} diff --git a/Server~/src/utils/logger.ts b/Server~/src/utils/logger.ts deleted file mode 100644 index 5c515d8c..00000000 --- a/Server~/src/utils/logger.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { appendFileSync } from 'fs'; - -export enum LogLevel { - DEBUG = 0, - INFO = 1, - WARN = 2, - ERROR = 3 -} - -// Check environment variable for logging -const isLoggingEnabled = process.env.LOGGING === 'true'; - -// Check environment variable for logging in a file -const isLoggingFileEnabled = process.env.LOGGING_FILE === 'true'; - -export class Logger { - private level: LogLevel; - private prefix: string; - - constructor(prefix: string, level: LogLevel = LogLevel.INFO) { - this.prefix = prefix; - this.level = level; - } - - debug(message: string, data?: any) { - this.log(LogLevel.DEBUG, message, data); - } - - info(message: string, data?: any) { - this.log(LogLevel.INFO, message, data); - } - - warn(message: string, data?: any) { - this.log(LogLevel.WARN, message, data); - } - - error(message: string, error?: any) { - this.log(LogLevel.ERROR, message, error); - } - - isLoggingEnabled(): boolean { - return isLoggingEnabled; - } - - isLoggingFileEnabled(): boolean { - return isLoggingFileEnabled; - } - - private log(level: LogLevel, message: string, data?: any) { - if (level < this.level) return; - - const timestamp = new Date().toISOString(); - const levelStr = LogLevel[level]; - const logMessage = `[${timestamp}] [${levelStr}] [${this.prefix}] ${message}`; - - // Write to file if file logging is enabled - if (this.isLoggingFileEnabled()) { - try { - appendFileSync('log.txt', logMessage + '\n'); - if (data) { - appendFileSync('log.txt', JSON.stringify(data, null, 2) + '\n'); - } - } catch (error) { - console.error('Failed to write to log file:', error); - } - } - - // Write to console if logging is enabled - if (this.isLoggingEnabled()) { - if (data) { - console.log(logMessage, data); - } else { - console.log(logMessage); - } - } - } -} diff --git a/glama.json b/glama.json deleted file mode 100644 index 7c69768f..00000000 --- a/glama.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "https://glama.ai/mcp/schemas/server.json", - "maintainers": [ - "CoderGamester" - ] - } \ No newline at end of file diff --git a/glama.json.meta b/glama.json.meta deleted file mode 100644 index 09c28439..00000000 --- a/glama.json.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 01bf5db62995e1841be2ee42450be6b3 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/llms.txt b/llms.txt index 8cb88c9e..f4a0734e 100644 --- a/llms.txt +++ b/llms.txt @@ -1,46 +1,9 @@ -# MCP Unity +# MCP Unity 2.0 -> MCP Unity is an open-source implementation of the Model Context Protocol (MCP) for the Unity Editor. -> It connects a C# Unity Editor package with a Node.js/TypeScript server over WebSockets and exposes -> high-level "tools" and "resources" so language-model agents and other automated clients can safely -> inspect, test, and modify Unity projects in real-time. +MCP Unity 2.0 extends Unity CLI and `com.unity.pipeline`; it does not run a custom WebSocket bridge. -This repository consists of two coordinated halves that communicate using JSON-encoded MCP messages: +- User setup, exact public catalogs, and the complete 1.4 migration table: [README.md](README.md) +- Maintainer architecture, invariants, and test matrix: [AGENTS.md](AGENTS.md) +- Release notes: [CHANGELOG.md](CHANGELOG.md) -- **Editor/** – Unity package that starts an internal WebSocket server and registers C# MCP tools & resources. -- **Server/** – Node.js/TypeScript application that runs outside Unity, connects to the Editor server, and - re-exposes identical MCP tools/resources for AI assistants. - -Together they allow AI coding assistants (e.g. Windsurf, Cursor, Claude Code, GitHub Copilot) to drive the Unity Editor headlessly, -automate scene and asset management, run tests, install packages, and gather diagnostics. - -## Docs -- [Project README](README.md): Full feature list, installation, and getting-started guide. -- [Installation guide](README.md#installation): Step-by-step setup instructions. -- [Unity Editor Tools source](Editor/Tools/): C# classes that implement individual editor actions. -- [Node.js Tools source](Server~/src/tools/): TypeScript wrappers that forward requests to Unity. - -## Tools -- [`execute_menu_item`](README.md#mcp-server-tools): Executes Unity Editor menu items by path. -- [`select_gameobject`](README.md#mcp-server-tools): Selects GameObjects in the Unity scene by path or instance ID. -- [`update_gameobject`](README.md#mcp-server-tools): Creates or updates GameObjects (name, tag, layer, active/static). -- [`update_component`](README.md#mcp-server-tools): Adds or edits component fields on GameObjects. -- [`add_package`](README.md#mcp-server-tools): Installs Unity packages via the Package Manager. -- [`run_tests`](README.md#mcp-server-tools): Runs Unity Test Runner tests (EditMode/PlayMode). -- [`send_console_log`](README.md#mcp-server-tools): Sends a console message to the Unity Editor log. -- [`add_asset_to_scene`](README.md#mcp-server-tools): Adds an AssetDatabase asset to the current scene. - -## Resources -- [unity://menu-items](README.md#mcp-server-resources): Returns all available Unity menu items. -- [unity://scenes-hierarchy](README.md#mcp-server-resources): Current scene hierarchy tree. -- [unity://gameobject/{id}](README.md#mcp-server-resources): Detailed info for a specific GameObject. -- [unity://logs](README.md#mcp-server-resources): Unity console log entries. -- [unity://packages](README.md#mcp-server-resources): Installed/available Unity packages. -- [unity://assets](README.md#mcp-server-resources): AssetDatabase search results. -- [unity://tests/{testMode}](README.md#mcp-server-resources): Test metadata for EditMode/PlayMode. - -## Optional -- [Model Context Protocol specification](https://modelcontextprotocol.io/spec): Formal protocol definition for MCP. -- [Unity Editor](https://unity.com/releases/editor/archive): Official Unity Editor download and documentation. -- [Node.js](https://nodejs.org/en/download/): JavaScript runtime used for the MCP server backend. -- [WebSocket-Sharp (C#)](https://github.com/sta/websocket-sharp/tree/master/websocket-sharp/Server): C# WebSocket server library used in the Unity package. +Use `unity mcp --project-path ` as the primary MCP entrypoint. Unity CLI is installed explicitly by the user or CI. The optional private companion is bundled under `Server~`. diff --git a/package.json b/package.json index 2bd66856..d61b9ee7 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,13 @@ { "name": "com.gamelovers.mcp-unity", - "displayName": "MCP Unity Server", + "displayName": "MCP Unity Extensions for Unity CLI", "author": "CoderGamester", - "version": "1.4.0", - "mcpname": "io.github.codergamester/mcp-unity", - "unity": "2022.3", + "version": "2.0.0", + "unity": "6000.0", "license": "MIT", - "description": "The purpose of this package is to provide a MCP Unity Server for executing Unity operations and request Editor information from AI MCP enabled hosts", + "description": "MCP Unity Extensions for Unity CLI adds focused, typed Pipeline commands for Unity Editor authoring workflows.", "dependencies": { - "com.unity.nuget.newtonsoft-json": "3.2.1", - "com.unity.editorcoroutines": "1.0.0", + "com.unity.pipeline": "0.3.1-exp.1", "com.unity.test-framework": "1.3.3" }, "type": "library",