diff --git a/.changeset/add-mcp-client-support.md b/.changeset/add-mcp-client-support.md new file mode 100644 index 00000000..7ff6c9e2 --- /dev/null +++ b/.changeset/add-mcp-client-support.md @@ -0,0 +1,5 @@ +--- +"chat": minor +--- + +Add MCP (Model Context Protocol) client support for tool discovery and invocation from remote MCP servers. Configure via `mcpServers` in `ChatConfig` and access tools through `chat.mcp.listTools()` and `chat.mcp.callTool()`. diff --git a/apps/docs/content/docs/api/chat.mdx b/apps/docs/content/docs/api/chat.mdx index 2fd9e291..e58fef68 100644 --- a/apps/docs/content/docs/api/chat.mdx +++ b/apps/docs/content/docs/api/chat.mdx @@ -45,6 +45,11 @@ const bot = new Chat(config); type: 'number', default: '500', }, + mcpServers: { + description: 'MCP server configurations for tool discovery and invocation. Requires @modelcontextprotocol/sdk.', + type: 'McpServerConfig[]', + default: 'undefined', + }, }} /> @@ -412,6 +417,45 @@ bot.onAppHomeOpened(async (event) => { }} /> +## mcp + +The `McpManager` instance for interacting with connected MCP servers. Returns a `NoopMcpManager` if no `mcpServers` were configured. + +```typescript +// List all available tools +const tools = await bot.mcp.listTools(); + +// Call a tool +const result = await bot.mcp.callTool("search_issues", { query: "error" }); + +// Call a tool with per-call auth headers +const result = await bot.mcp.callTool( + "search_issues", + { query: "error" }, + { headers: { Authorization: `Bearer ${userToken}` } } +); + +// Refresh tool lists from all servers +await bot.mcp.refresh(); +``` + +', + }, + 'callTool(name, args?, options?)': { + description: 'Invoke a tool by name. Pass options.headers to override auth for this call.', + type: 'Promise', + }, + 'refresh()': { + description: 'Re-fetch tool lists from all connected servers.', + type: 'Promise', + }, + }} +/> + ## Utility methods ### webhooks diff --git a/apps/docs/content/docs/mcp.mdx b/apps/docs/content/docs/mcp.mdx new file mode 100644 index 00000000..54819898 --- /dev/null +++ b/apps/docs/content/docs/mcp.mdx @@ -0,0 +1,288 @@ +--- +title: MCP (Model Context Protocol) +description: Discover and invoke tools from remote MCP servers directly from your chat bot handlers. +type: guide +prerequisites: + - /docs/usage +--- + +Chat SDK can connect to remote [MCP](https://modelcontextprotocol.io/) servers to discover and invoke tools. This lets your bot call APIs like Sentry, Linear, Notion, and Figma through a standard protocol. + +## Installation + +The MCP SDK is an optional peer dependency: + +```bash +pnpm add @modelcontextprotocol/sdk +``` + +## Configuration + +Pass `mcpServers` when creating your `Chat` instance: + +```typescript title="lib/bot.ts" lineNumbers +import { Chat } from "chat"; + +const bot = new Chat({ + userName: "mybot", + adapters: { slack }, + state, + mcpServers: [ + { + name: "sentry", + transport: { + type: "http", + url: "https://mcp.sentry.dev/mcp", + headers: { + Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}`, + }, + }, + }, + ], +}); +``` + +MCP servers are connected lazily on the first webhook request. + +## Listing tools + +Discover all tools available across connected servers: + +```typescript title="lib/bot.ts" lineNumbers +const tools = await bot.mcp.listTools(); + +for (const tool of tools) { + console.log(`${tool.serverName}/${tool.name}: ${tool.description}`); +} +``` + +Each tool includes `name`, `description`, `inputSchema` (JSON Schema), and `serverName`. + +## Calling tools + +Invoke a tool by name from any handler: + +```typescript title="lib/bot.ts" lineNumbers +bot.onNewMention(async (thread, message) => { + const result = await bot.mcp.callTool("search_issues", { + query: message.text, + }); + + const text = result.content + .filter((c) => c.type === "text") + .map((c) => c.text) + .join("\n"); + + await thread.post(text); +}); +``` + +## Using with AI SDK + +If you're building an AI-powered bot with [AI SDK](https://ai-sdk.dev), use [`@ai-sdk/mcp`](https://ai-sdk.dev/docs/ai-sdk-core/mcp-tools) to let the model decide which MCP tools to call. This is complementary to the built-in `bot.mcp` client — use `@ai-sdk/mcp` when an LLM should choose tools, and `bot.mcp` when your code should call tools directly. + +```bash +pnpm add @ai-sdk/mcp ai +``` + +Create an MCP client and pass its tools to a `ToolLoopAgent`: + + + + AI Gateway + Anthropic + OpenAI + + + +```typescript title="lib/bot.ts" lineNumbers +import { ToolLoopAgent } from "ai"; +import { gateway } from "@ai-sdk/gateway"; +import { createMCPClient } from "@ai-sdk/mcp"; + +const sentry = await createMCPClient({ + transport: { + type: "sse", + url: "https://mcp.sentry.dev/sse", + headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` }, + }, +}); + +const agent = new ToolLoopAgent({ + model: gateway("anthropic/claude-sonnet-4-5"), + tools: await sentry.tools(), + instructions: "You are a helpful assistant that can look up Sentry issues.", +}); + +bot.onNewMention(async (thread, message) => { + const result = await agent.stream({ prompt: message.text }); + await thread.post(result.textStream); +}); +``` + + + +```typescript title="lib/bot.ts" lineNumbers +import { ToolLoopAgent } from "ai"; +import { anthropic } from "@ai-sdk/anthropic"; +import { createMCPClient } from "@ai-sdk/mcp"; + +const sentry = await createMCPClient({ + transport: { + type: "sse", + url: "https://mcp.sentry.dev/sse", + headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` }, + }, +}); + +const agent = new ToolLoopAgent({ + model: anthropic("claude-sonnet-4-5"), + tools: await sentry.tools(), + instructions: "You are a helpful assistant that can look up Sentry issues.", +}); + +bot.onNewMention(async (thread, message) => { + const result = await agent.stream({ prompt: message.text }); + await thread.post(result.textStream); +}); +``` + + + +```typescript title="lib/bot.ts" lineNumbers +import { ToolLoopAgent } from "ai"; +import { openai } from "@ai-sdk/openai"; +import { createMCPClient } from "@ai-sdk/mcp"; + +const sentry = await createMCPClient({ + transport: { + type: "sse", + url: "https://mcp.sentry.dev/sse", + headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` }, + }, +}); + +const agent = new ToolLoopAgent({ + model: openai("gpt-4o"), + tools: await sentry.tools(), + instructions: "You are a helpful assistant that can look up Sentry issues.", +}); + +bot.onNewMention(async (thread, message) => { + const result = await agent.stream({ prompt: message.text }); + await thread.post(result.textStream); +}); +``` + + + + +**When to use which approach:** +- **`bot.mcp`** (this page) — Your code decides which tools to call. Good for deterministic workflows, slash commands, and when you need per-call auth headers or Chat SDK lifecycle management. +- **`@ai-sdk/mcp`** (above) — The LLM decides which tools to call. Good for AI agents that need to reason about which tools to use based on the user's message. + +Both approaches can be used together in the same bot. + + +## Transport types + +| Type | Config value | Description | +|------|-------------|-------------| +| Streamable HTTP | `"http"` | Default. Modern bidirectional transport over HTTP. | +| Server-Sent Events | `"sse"` | Legacy SSE-based transport. Use if the server doesn't support Streamable HTTP. | + +## Authentication + +### Service-level (static headers) + +The bot uses a shared API key for all requests. Configure headers directly in the transport: + +```typescript +mcpServers: [ + { + name: "sentry", + transport: { + type: "http", + url: "https://mcp.sentry.dev/mcp", + headers: { Authorization: `Bearer ${process.env.SENTRY_AUTH_TOKEN}` }, + }, + }, +]; +``` + +### Per-tenant + +Look up the tenant's token at call time and pass it via the `headers` option. A temporary connection is created with those headers for the duration of the call: + +```typescript title="lib/bot.ts" lineNumbers +bot.onNewMention(async (thread, message) => { + const tenantToken = await lookupTenantToken(thread.id); + + const result = await bot.mcp.callTool( + "search_issues", + { query: message.text }, + { headers: { Authorization: `Bearer ${tenantToken}` } } + ); + + await thread.post(result.content[0].text); +}); +``` + +### Per-user + +Same pattern — resolve the user's OAuth token and pass it per-call: + +```typescript title="lib/bot.ts" lineNumbers +bot.onNewMention(async (thread, message) => { + const userToken = await getUserOAuthToken(message.author.id); + + const result = await bot.mcp.callTool( + "create_issue", + { title: message.text }, + { headers: { Authorization: `Bearer ${userToken}` } } + ); + + await thread.post(`Created: ${result.content[0].text}`); +}); +``` + +## Multiple servers + +Tools from all configured servers are merged. The SDK routes each `callTool` to the correct server automatically: + +```typescript +mcpServers: [ + { + name: "sentry", + transport: { type: "http", url: "https://mcp.sentry.dev/mcp" }, + }, + { + name: "linear", + transport: { + type: "http", + url: "https://mcp.linear.app/mcp", + headers: { Authorization: `Bearer ${process.env.LINEAR_API_KEY}` }, + }, + }, +]; +``` + +## Refreshing tools + +If a server's tool list changes at runtime, re-fetch it: + +```typescript +await bot.mcp.refresh(); +``` + +## Graceful degradation + +If a server fails to connect during initialization, a warning is logged and the remaining servers continue normally. Your bot won't crash because one MCP server is down. + +## Error codes + +| Code | When | +|------|------| +| `MCP_TOOL_NOT_FOUND` | `callTool` is called with a tool name that doesn't exist on any connected server. | +| `MCP_NOT_CONFIGURED` | `callTool` is called but no `mcpServers` were configured. | +| `MCP_SDK_NOT_INSTALLED` | `@modelcontextprotocol/sdk` is not installed. | diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json index f23a0db0..c2bc9dbf 100644 --- a/apps/docs/content/docs/meta.json +++ b/apps/docs/content/docs/meta.json @@ -7,6 +7,7 @@ "posting-messages", "---Features---", "...", + "mcp", "error-handling", "---Platform Adapters---", "...adapters", diff --git a/packages/chat/package.json b/packages/chat/package.json index aea8bf4f..90d70e70 100644 --- a/packages/chat/package.json +++ b/packages/chat/package.json @@ -41,6 +41,7 @@ "unified": "^11.0.5" }, "devDependencies": { + "@modelcontextprotocol/sdk": "^1.27.1", "@types/mdast": "^4.0.4", "@types/node": "^25.3.2", "tsup": "^8.3.5", @@ -67,5 +68,13 @@ "google-chat", "bot" ], + "peerDependencies": { + "@modelcontextprotocol/sdk": "^1.15.0" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } + }, "license": "MIT" } diff --git a/packages/chat/src/chat.ts b/packages/chat/src/chat.ts index 27b3bcec..610decbc 100644 --- a/packages/chat/src/chat.ts +++ b/packages/chat/src/chat.ts @@ -5,6 +5,8 @@ import { setChatSingleton, } from "./chat-singleton"; import { isJSX, toModalElement } from "./jsx-runtime"; +import { McpClientManager, NoopMcpManager } from "./mcp"; +import type { McpManager } from "./mcp-types"; import { Message, type SerializedMessage } from "./message"; import type { ModalElement } from "./modals"; import { type SerializedThread, ThreadImpl } from "./thread"; @@ -182,6 +184,7 @@ export class Chat< private readonly _streamingUpdateIntervalMs: number; private readonly _fallbackStreamingPlaceholderText: string | null; private readonly _dedupeTtlMs: number; + private readonly mcpManager: McpManager; private readonly mentionHandlers: MentionHandler[] = []; private readonly messagePatterns: MessagePattern[] = []; @@ -209,6 +212,11 @@ export class Chat< */ readonly webhooks: Webhooks; + /** MCP manager for tool discovery and invocation. */ + get mcp(): McpManager { + return this.mcpManager; + } + constructor(config: ChatConfig) { this.userName = config.userName; this._stateAdapter = config.state; @@ -237,6 +245,11 @@ export class Chat< } this.webhooks = webhooks as Webhooks; + // Initialize MCP manager + this.mcpManager = config.mcpServers?.length + ? new McpClientManager(config.mcpServers, this.logger) + : new NoopMcpManager(); + this.logger.debug("Chat instance created", { adapters: Object.keys(config.adapters), }); @@ -294,6 +307,11 @@ export class Chat< ); await Promise.all(initPromises); + // Initialize MCP servers (if configured) + if (this.mcpManager instanceof McpClientManager) { + await this.mcpManager.initialize(); + } + this.initialized = true; this.logger.info("Chat instance initialized", { adapters: Array.from(this.adapters.keys()), @@ -305,6 +323,9 @@ export class Chat< */ async shutdown(): Promise { this.logger.info("Shutting down chat instance..."); + if (this.mcpManager instanceof McpClientManager) { + await this.mcpManager.close(); + } await this._stateAdapter.disconnect(); this.initialized = false; this.initPromise = null; diff --git a/packages/chat/src/index.ts b/packages/chat/src/index.ts index cde5a7c3..a77be1a0 100644 --- a/packages/chat/src/index.ts +++ b/packages/chat/src/index.ts @@ -169,6 +169,17 @@ export { toPlainText, walkAst, } from "./markdown"; +// MCP types +export type { + McpCallToolOptions, + McpContentBlock, + McpHeaders, + McpManager, + McpServerConfig, + McpTool, + McpToolResult, + McpTransportConfig, +} from "./mcp-types"; // Modal types export type { ModalChild, diff --git a/packages/chat/src/mcp-types.ts b/packages/chat/src/mcp-types.ts new file mode 100644 index 00000000..f278e9d6 --- /dev/null +++ b/packages/chat/src/mcp-types.ts @@ -0,0 +1,85 @@ +/** + * Types for MCP (Model Context Protocol) client support. + */ + +/** + * Headers for MCP server authentication. + * Can be a static record or an async function for token rotation. + */ +export type McpHeaders = + | Record + | (() => Record) + | (() => Promise>); + +/** + * Transport configuration for an MCP server. + * Supports SSE and Streamable HTTP transports only (no stdio). + */ +export interface McpTransportConfig { + headers?: McpHeaders; + type: "sse" | "http"; + url: string; +} + +/** + * Configuration for a single MCP server. + */ +export interface McpServerConfig { + name: string; + transport: McpTransportConfig; +} + +/** + * A tool exposed by an MCP server. + */ +export interface McpTool { + description?: string; + inputSchema: Record; + name: string; + serverName: string; +} + +/** + * A content block returned by an MCP tool call. + */ +export type McpContentBlock = + | { type: "text"; text: string } + | { type: "image"; data: string; mimeType: string } + | { + type: "resource"; + resource: { uri: string; mimeType?: string; text?: string }; + }; + +/** + * Result of calling an MCP tool. + */ +export interface McpToolResult { + content: McpContentBlock[]; + isError?: boolean; +} + +/** + * Options for a single `callTool` invocation. + * Pass `headers` to override the server's default auth for this call + * (e.g. per-user or per-tenant tokens). + */ +export interface McpCallToolOptions { + headers?: Record; +} + +/** + * Manager interface for interacting with MCP servers. + */ +export interface McpManager { + /** Call a tool by name with optional arguments and per-call options. */ + callTool( + name: string, + args?: Record, + options?: McpCallToolOptions + ): Promise; + /** List all tools available across connected MCP servers. */ + listTools(): Promise; + + /** Re-fetch tool lists from all connected servers. */ + refresh(): Promise; +} diff --git a/packages/chat/src/mcp.test.ts b/packages/chat/src/mcp.test.ts new file mode 100644 index 00000000..9da2d69b --- /dev/null +++ b/packages/chat/src/mcp.test.ts @@ -0,0 +1,398 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { Chat } from "./chat"; +import { ChatError } from "./errors"; +import { McpClientManager, NoopMcpManager } from "./mcp"; +import type { McpServerConfig } from "./mcp-types"; +import { createMockAdapter, createMockState, mockLogger } from "./mock-adapter"; +import type { Adapter, StateAdapter } from "./types"; + +// Mock MCP SDK +const mockConnect = vi.fn().mockResolvedValue(undefined); +const mockListTools = vi.fn().mockResolvedValue({ + tools: [ + { + name: "search_issues", + description: "Search issues", + inputSchema: { type: "object" }, + }, + { + name: "get_event", + description: "Get event details", + inputSchema: { type: "object" }, + }, + ], +}); +const mockCallTool = vi.fn().mockResolvedValue({ + content: [{ type: "text", text: "result" }], + isError: false, +}); +const mockClientClose = vi.fn().mockResolvedValue(undefined); +const mockTransportClose = vi.fn().mockResolvedValue(undefined); + +const MockClient = vi.fn().mockImplementation(() => ({ + connect: mockConnect, + listTools: mockListTools, + callTool: mockCallTool, + close: mockClientClose, +})); + +const MockSSETransport = vi.fn().mockImplementation(() => ({ + close: mockTransportClose, +})); + +const MockHTTPTransport = vi.fn().mockImplementation(() => ({ + close: mockTransportClose, +})); + +vi.mock("@modelcontextprotocol/sdk/client/index.js", () => ({ + Client: MockClient, +})); + +vi.mock("@modelcontextprotocol/sdk/client/sse.js", () => ({ + SSEClientTransport: MockSSETransport, +})); + +vi.mock("@modelcontextprotocol/sdk/client/streamableHttp.js", () => ({ + StreamableHTTPClientTransport: MockHTTPTransport, +})); + +const serverConfigs: McpServerConfig[] = [ + { + name: "sentry", + transport: { type: "http", url: "https://mcp.sentry.io" }, + }, +]; + +describe("McpClientManager", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockListTools.mockResolvedValue({ + tools: [ + { + name: "search_issues", + description: "Search issues", + inputSchema: { type: "object" }, + }, + { + name: "get_event", + description: "Get event details", + inputSchema: { type: "object" }, + }, + ], + }); + }); + + it("should initialize and list tools with serverName", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + const tools = await manager.listTools(); + expect(tools).toHaveLength(2); + expect(tools[0]).toEqual({ + name: "search_issues", + description: "Search issues", + inputSchema: { type: "object" }, + serverName: "sentry", + }); + expect(tools[1]).toEqual({ + name: "get_event", + description: "Get event details", + inputSchema: { type: "object" }, + serverName: "sentry", + }); + }); + + it("should call a tool and route to the correct server", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + const result = await manager.callTool("search_issues", { query: "slow" }); + expect(mockCallTool).toHaveBeenCalledWith({ + name: "search_issues", + arguments: { query: "slow" }, + }); + expect(result).toEqual({ + content: [{ type: "text", text: "result" }], + isError: false, + }); + }); + + it("should throw MCP_TOOL_NOT_FOUND for unknown tool", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + await expect(manager.callTool("nonexistent")).rejects.toThrow(ChatError); + await expect(manager.callTool("nonexistent")).rejects.toMatchObject({ + code: "MCP_TOOL_NOT_FOUND", + }); + }); + + it("should close all connections", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + await manager.close(); + expect(mockClientClose).toHaveBeenCalled(); + + // After close, listTools should return empty + const tools = await manager.listTools(); + expect(tools).toHaveLength(0); + }); + + it("should log warning for failed server connection and continue", async () => { + mockConnect.mockRejectedValueOnce(new Error("connection refused")); + + const twoServers: McpServerConfig[] = [ + { + name: "failing", + transport: { type: "http", url: "https://fail.example.com" }, + }, + { + name: "working", + transport: { type: "http", url: "https://work.example.com" }, + }, + ]; + + const manager = new McpClientManager(twoServers, mockLogger); + await manager.initialize(); + + expect(mockLogger.warn).toHaveBeenCalledWith( + expect.stringContaining("failing"), + expect.any(Object) + ); + + // The working server's tools should still be available + const tools = await manager.listTools(); + expect(tools).toHaveLength(2); + }); + + it("should refresh tool lists", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + // Update mock to return different tools + mockListTools.mockResolvedValue({ + tools: [ + { + name: "new_tool", + description: "New tool", + inputSchema: { type: "object" }, + }, + ], + }); + + await manager.refresh(); + const tools = await manager.listTools(); + expect(tools).toHaveLength(1); + expect(tools[0].name).toBe("new_tool"); + }); + + it("should create SSE transport for sse type", async () => { + const sseConfig: McpServerConfig[] = [ + { + name: "sse-server", + transport: { type: "sse", url: "https://sse.example.com" }, + }, + ]; + + const manager = new McpClientManager(sseConfig, mockLogger); + await manager.initialize(); + + expect(MockSSETransport).toHaveBeenCalled(); + expect(MockHTTPTransport).not.toHaveBeenCalled(); + }); + + it("should create HTTP transport for http type", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + expect(MockHTTPTransport).toHaveBeenCalled(); + }); + + it("should use cached client when callTool is called without headers", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + await manager.callTool("search_issues", { query: "slow" }); + + // Should use the client created during initialize, not create a new one + // MockClient is called once during initialize + expect(MockClient).toHaveBeenCalledTimes(1); + expect(mockCallTool).toHaveBeenCalledWith({ + name: "search_issues", + arguments: { query: "slow" }, + }); + }); + + it("should create a temporary client with per-call headers", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + // Reset mocks after initialize + MockClient.mockClear(); + MockHTTPTransport.mockClear(); + mockConnect.mockClear(); + mockCallTool.mockClear(); + mockClientClose.mockClear(); + + await manager.callTool( + "search_issues", + { query: "test" }, + { + headers: { Authorization: "Bearer user-token-123" }, + } + ); + + // Should create a new transport with the override headers + expect(MockHTTPTransport).toHaveBeenCalledWith( + expect.any(URL), + expect.objectContaining({ + requestInit: { headers: { Authorization: "Bearer user-token-123" } }, + }) + ); + + // Should create a new client, connect, call, and close + expect(MockClient).toHaveBeenCalledTimes(1); + expect(mockConnect).toHaveBeenCalledTimes(1); + expect(mockCallTool).toHaveBeenCalledWith({ + name: "search_issues", + arguments: { query: "test" }, + }); + expect(mockClientClose).toHaveBeenCalledTimes(1); + }); + + it("should close temporary client even if callTool throws", async () => { + const manager = new McpClientManager(serverConfigs, mockLogger); + await manager.initialize(); + + mockClientClose.mockClear(); + mockCallTool.mockRejectedValueOnce(new Error("tool error")); + + await expect( + manager.callTool( + "search_issues", + {}, + { + headers: { Authorization: "Bearer fail-token" }, + } + ) + ).rejects.toThrow("tool error"); + + // Temporary client should still be closed + expect(mockClientClose).toHaveBeenCalled(); + }); + + it("should pass static headers to transport", async () => { + const configWithHeaders: McpServerConfig[] = [ + { + name: "auth-server", + transport: { + type: "http", + url: "https://auth.example.com", + headers: { Authorization: "Bearer token123" }, + }, + }, + ]; + + const manager = new McpClientManager(configWithHeaders, mockLogger); + await manager.initialize(); + + expect(MockHTTPTransport).toHaveBeenCalledWith( + expect.any(URL), + expect.objectContaining({ + requestInit: { headers: { Authorization: "Bearer token123" } }, + }) + ); + }); +}); + +describe("NoopMcpManager", () => { + it("should return empty tools list", async () => { + const manager = new NoopMcpManager(); + const tools = await manager.listTools(); + expect(tools).toEqual([]); + }); + + it("should throw on callTool", async () => { + const manager = new NoopMcpManager(); + await expect(manager.callTool("anything")).rejects.toThrow(ChatError); + await expect(manager.callTool("anything")).rejects.toMatchObject({ + code: "MCP_NOT_CONFIGURED", + }); + }); + + it("should not throw on refresh", async () => { + const manager = new NoopMcpManager(); + await expect(manager.refresh()).resolves.toBeUndefined(); + }); +}); + +describe("Chat MCP wiring", () => { + let mockAdapter: Adapter; + let mockState: StateAdapter; + + beforeEach(() => { + vi.clearAllMocks(); + mockAdapter = createMockAdapter("slack"); + mockState = createMockState(); + }); + + it("should expose mcp getter with NoopMcpManager when no servers configured", () => { + const chat = new Chat({ + userName: "testbot", + adapters: { slack: mockAdapter }, + state: mockState, + logger: mockLogger, + }); + + expect(chat.mcp).toBeInstanceOf(NoopMcpManager); + }); + + it("should expose mcp getter with McpClientManager when servers configured", () => { + const chat = new Chat({ + userName: "testbot", + adapters: { slack: mockAdapter }, + state: mockState, + logger: mockLogger, + mcpServers: serverConfigs, + }); + + expect(chat.mcp).toBeInstanceOf(McpClientManager); + }); + + it("should initialize MCP on first webhook", async () => { + const chat = new Chat({ + userName: "testbot", + adapters: { slack: mockAdapter }, + state: mockState, + logger: mockLogger, + mcpServers: serverConfigs, + }); + + await chat.webhooks.slack( + new Request("http://test.com", { method: "POST" }) + ); + + // MCP client should have been created and connected + expect(MockClient).toHaveBeenCalled(); + expect(mockConnect).toHaveBeenCalled(); + }); + + it("should close MCP on shutdown", async () => { + const chat = new Chat({ + userName: "testbot", + adapters: { slack: mockAdapter }, + state: mockState, + logger: mockLogger, + mcpServers: serverConfigs, + }); + + // Initialize first + await chat.webhooks.slack( + new Request("http://test.com", { method: "POST" }) + ); + + await chat.shutdown(); + expect(mockClientClose).toHaveBeenCalled(); + }); +}); diff --git a/packages/chat/src/mcp.ts b/packages/chat/src/mcp.ts new file mode 100644 index 00000000..dc871382 --- /dev/null +++ b/packages/chat/src/mcp.ts @@ -0,0 +1,336 @@ +/** + * MCP (Model Context Protocol) client manager implementation. + */ + +import { ChatError } from "./errors"; +import type { Logger } from "./logger"; +import type { + McpCallToolOptions, + McpContentBlock, + McpHeaders, + McpManager, + McpServerConfig, + McpTool, + McpToolResult, + McpTransportConfig, +} from "./mcp-types"; + +interface ConnectedServer { + client: McpClient; + name: string; + tools: McpTool[]; + transport: McpTransport; + transportConfig: McpTransportConfig; +} + +// Minimal interfaces for the MCP SDK types we use, to avoid importing at module level. +// These are intentionally loose — the real SDK types are richer but we only use a subset. +interface McpClient { + callTool(params: { + name: string; + arguments?: Record; + }): Promise<{ + content: unknown[]; + isError?: boolean; + }>; + close(): Promise; + connect(transport: McpTransport): Promise; + listTools(): Promise<{ + tools: Array<{ name: string; description?: string; inputSchema?: unknown }>; + }>; +} + +interface McpTransport { + close(): Promise; +} + +interface McpSdk { + Client: new (info: { name: string; version: string }) => McpClient; + SSEClientTransport: new ( + url: URL, + options?: Record + ) => McpTransport; + StreamableHTTPClientTransport: new ( + url: URL, + options?: Record + ) => McpTransport; +} + +async function loadMcpSdk(): Promise { + try { + const [clientMod, sseMod, httpMod] = await Promise.all([ + import("@modelcontextprotocol/sdk/client/index.js"), + import("@modelcontextprotocol/sdk/client/sse.js"), + import("@modelcontextprotocol/sdk/client/streamableHttp.js"), + ]); + return { + Client: clientMod.Client as McpSdk["Client"], + SSEClientTransport: + sseMod.SSEClientTransport as McpSdk["SSEClientTransport"], + StreamableHTTPClientTransport: + httpMod.StreamableHTTPClientTransport as McpSdk["StreamableHTTPClientTransport"], + }; + } catch { + throw new ChatError( + "MCP support requires @modelcontextprotocol/sdk. Install it with: pnpm add @modelcontextprotocol/sdk", + "MCP_SDK_NOT_INSTALLED" + ); + } +} + +function resolveHeaders( + headers: McpHeaders | undefined +): Record | undefined { + if (!headers) { + return undefined; + } + if (typeof headers === "function") { + const result = headers(); + // If it returns a promise, we can't resolve it synchronously — return undefined + // Dynamic headers are handled per-request via custom fetch + if (result instanceof Promise) { + return undefined; + } + return result; + } + return headers; +} + +function createCustomFetch(headers: McpHeaders): typeof globalThis.fetch { + return async (input, init) => { + const resolved = typeof headers === "function" ? await headers() : headers; + const mergedHeaders = { + ...resolved, + ...(init?.headers as Record), + }; + return globalThis.fetch(input, { ...init, headers: mergedHeaders }); + }; +} + +function needsCustomFetch( + headers: McpHeaders | undefined +): headers is McpHeaders { + return typeof headers === "function"; +} + +async function createTransport( + config: McpTransportConfig, + sdk: McpSdk +): Promise { + const url = new URL(config.url); + + if (config.type === "sse") { + const options: Record = {}; + if (needsCustomFetch(config.headers)) { + options.eventSourceInit = { fetch: createCustomFetch(config.headers) }; + } else { + const staticHeaders = resolveHeaders(config.headers); + if (staticHeaders) { + options.requestInit = { headers: staticHeaders }; + } + } + return new sdk.SSEClientTransport(url, options); + } + + // Streamable HTTP + const options: Record = {}; + if (needsCustomFetch(config.headers)) { + // StreamableHTTPClientTransport doesn't support custom fetch natively, + // but we can pass headers via requestInit for static resolution + const resolved = + typeof config.headers === "function" + ? await config.headers() + : config.headers; + options.requestInit = { headers: resolved }; + } else { + const staticHeaders = resolveHeaders(config.headers); + if (staticHeaders) { + options.requestInit = { headers: staticHeaders }; + } + } + return new sdk.StreamableHTTPClientTransport(url, options); +} + +/** + * MCP client manager that connects to multiple MCP servers + * and provides unified tool discovery and invocation. + */ +export class McpClientManager implements McpManager { + private readonly configs: McpServerConfig[]; + private readonly logger: Logger; + private sdk: McpSdk | null = null; + private servers: ConnectedServer[] = []; + + constructor(configs: McpServerConfig[], logger: Logger) { + this.configs = configs; + this.logger = logger; + } + + async initialize(): Promise { + const sdk = await loadMcpSdk(); + this.sdk = sdk; + + const results = await Promise.allSettled( + this.configs.map(async (config) => { + const client = new sdk.Client({ name: "chat-sdk", version: "1.0.0" }); + const transport = await createTransport(config.transport, sdk); + await client.connect(transport); + + const { tools } = await client.listTools(); + const mappedTools: McpTool[] = tools.map((t) => ({ + name: t.name, + description: t.description, + inputSchema: (t.inputSchema as Record) ?? {}, + serverName: config.name, + })); + + this.logger.debug(`MCP server "${config.name}" connected`, { + tools: mappedTools.length, + }); + + return { + name: config.name, + client, + transport, + transportConfig: config.transport, + tools: mappedTools, + } as ConnectedServer; + }) + ); + + for (const [i, result] of results.entries()) { + if (result.status === "fulfilled") { + this.servers.push(result.value); + } else { + this.logger.warn( + `Failed to connect to MCP server "${this.configs[i].name}"`, + { + error: result.reason, + } + ); + } + } + + this.logger.info("MCP initialized", { + connected: this.servers.length, + total: this.configs.length, + }); + } + + async listTools(): Promise { + return this.servers.flatMap((s) => s.tools); + } + + async callTool( + name: string, + args?: Record, + options?: McpCallToolOptions + ): Promise { + const server = this.servers.find((s) => + s.tools.some((t) => t.name === name) + ); + if (!server) { + throw new ChatError( + `MCP tool "${name}" not found on any connected server`, + "MCP_TOOL_NOT_FOUND" + ); + } + + if (options?.headers) { + return this.callToolWithHeaders(server, name, args, options.headers); + } + + const result = await server.client.callTool({ name, arguments: args }); + return { + content: result.content as McpContentBlock[], + isError: result.isError, + }; + } + + private async callToolWithHeaders( + server: ConnectedServer, + name: string, + args: Record | undefined, + headers: Record + ): Promise { + const sdk = this.sdk ?? (await loadMcpSdk()); + const transportConfig: McpTransportConfig = { + ...server.transportConfig, + headers, + }; + const transport = await createTransport(transportConfig, sdk); + const client = new sdk.Client({ name: "chat-sdk", version: "1.0.0" }); + await client.connect(transport); + try { + const result = await client.callTool({ name, arguments: args }); + return { + content: result.content as McpContentBlock[], + isError: result.isError, + }; + } finally { + await client.close().catch(() => {}); + } + } + + async refresh(): Promise { + const results = await Promise.allSettled( + this.servers.map(async (server) => { + const { tools } = await server.client.listTools(); + server.tools = tools.map((t) => ({ + name: t.name, + description: t.description, + inputSchema: (t.inputSchema as Record) ?? {}, + serverName: server.name, + })); + }) + ); + + for (const [i, result] of results.entries()) { + if (result.status === "rejected") { + this.logger.warn( + `Failed to refresh tools from MCP server "${this.servers[i].name}"`, + { + error: result.reason, + } + ); + } + } + } + + async close(): Promise { + await Promise.allSettled( + this.servers.map(async (server) => { + try { + await server.client.close(); + } catch { + // Ignore close errors + } + }) + ); + this.servers = []; + } +} + +/** + * No-op MCP manager used when no MCP servers are configured. + */ +export class NoopMcpManager implements McpManager { + async listTools(): Promise { + return []; + } + + async callTool( + name: string, + _args?: Record, + _options?: McpCallToolOptions + ): Promise { + throw new ChatError( + `MCP is not configured. Cannot call tool "${name}"`, + "MCP_NOT_CONFIGURED" + ); + } + + async refresh(): Promise { + // No-op + } +} diff --git a/packages/chat/src/types.ts b/packages/chat/src/types.ts index ac4c29b9..3d8c91cb 100644 --- a/packages/chat/src/types.ts +++ b/packages/chat/src/types.ts @@ -6,6 +6,7 @@ import type { Root } from "mdast"; import type { CardElement } from "./cards"; import type { CardJSXElement } from "./jsx-runtime"; import type { Logger, LogLevel } from "./logger"; +import type { McpServerConfig } from "./mcp-types"; import type { Message } from "./message"; import type { ModalElement } from "./modals"; @@ -54,6 +55,8 @@ export interface ChatConfig< * Pass "silent" to disable all logging. */ logger?: Logger | LogLevel; + /** MCP servers to connect to for tool discovery and invocation */ + mcpServers?: McpServerConfig[]; /** State adapter for subscriptions and locking */ state: StateAdapter; /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 44b14249..530ad592 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,16 +10,16 @@ importers: devDependencies: '@biomejs/biome': specifier: ^2.4.4 - version: 2.4.4 + version: 2.4.5 '@changesets/cli': specifier: ^2.29.8 - version: 2.29.8(@types/node@25.3.2) + version: 2.29.8(@types/node@25.3.3) '@vitest/coverage-v8': specifier: ^4.0.18 - version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + version: 4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) knip: specifier: ^5.85.0 - version: 5.85.0(@types/node@25.3.2)(typescript@5.9.3) + version: 5.85.0(@types/node@25.3.3)(typescript@5.9.3) turbo: specifier: ^2.8.12 version: 2.8.12 @@ -31,7 +31,7 @@ importers: version: 7.2.4 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) apps/docs: dependencies: @@ -82,7 +82,7 @@ importers: version: 16.2.2(@types/react@19.2.7)(lucide-react@0.555.0(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3) fumadocs-mdx: specifier: 14.0.4 - version: 14.0.4(fumadocs-core@16.2.2(@types/react@19.2.7)(lucide-react@0.555.0(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + version: 14.0.4(fumadocs-core@16.2.2(@types/react@19.2.7)(lucide-react@0.555.0(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) fumadocs-ui: specifier: 16.2.2 version: 16.2.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(lucide-react@0.555.0(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(tailwindcss@4.1.18) @@ -149,7 +149,7 @@ importers: version: 2.0.13 '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 '@types/react': specifier: ^19.2.7 version: 19.2.7 @@ -219,7 +219,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 '@types/react': specifier: ^19.0.1 version: 19.2.7 @@ -256,7 +256,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -265,7 +265,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/adapter-gchat: dependencies: @@ -284,7 +284,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -293,7 +293,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/adapter-github: dependencies: @@ -312,7 +312,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -321,7 +321,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/adapter-linear: dependencies: @@ -337,7 +337,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -346,7 +346,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/adapter-shared: dependencies: @@ -356,7 +356,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -365,7 +365,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/adapter-slack: dependencies: @@ -381,7 +381,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -390,7 +390,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/adapter-teams: dependencies: @@ -412,7 +412,7 @@ importers: version: 3.0.7(@azure/identity@4.13.0) '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -421,7 +421,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/adapter-telegram: dependencies: @@ -434,7 +434,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -443,7 +443,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/chat: dependencies: @@ -466,12 +466,15 @@ importers: specifier: ^11.0.5 version: 11.0.5 devDependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.27.1 + version: 1.27.1(zod@4.3.3) '@types/mdast': specifier: ^4.0.4 version: 4.0.4 '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -480,7 +483,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/integration-tests: dependencies: @@ -511,13 +514,13 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 typescript: specifier: ^5.7.2 version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/state-ioredis: dependencies: @@ -530,7 +533,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -539,7 +542,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/state-memory: dependencies: @@ -549,7 +552,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -558,7 +561,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages/state-redis: dependencies: @@ -571,7 +574,7 @@ importers: devDependencies: '@types/node': specifier: ^25.3.2 - version: 25.3.2 + version: 25.3.3 tsup: specifier: ^8.3.5 version: 8.5.1(jiti@2.6.1)(postcss@8.5.6)(tsx@4.21.0)(typescript@5.9.3) @@ -580,7 +583,7 @@ importers: version: 5.9.3 vitest: specifier: ^4.0.18 - version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) packages: @@ -714,59 +717,59 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@biomejs/biome@2.4.4': - resolution: {integrity: sha512-tigwWS5KfJf0cABVd52NVaXyAVv4qpUXOWJ1rxFL8xF1RVoeS2q/LK+FHgYoKMclJCuRoCWAPy1IXaN9/mS61Q==} + '@biomejs/biome@2.4.5': + resolution: {integrity: sha512-OWNCyMS0Q011R6YifXNOg6qsOg64IVc7XX6SqGsrGszPbkVCoaO7Sr/lISFnXZ9hjQhDewwZ40789QmrG0GYgQ==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.4.4': - resolution: {integrity: sha512-jZ+Xc6qvD6tTH5jM6eKX44dcbyNqJHssfl2nnwT6vma6B1sj7ZLTGIk6N5QwVBs5xGN52r3trk5fgd3sQ9We9A==} + '@biomejs/cli-darwin-arm64@2.4.5': + resolution: {integrity: sha512-lGS4Nd5O3KQJ6TeWv10mElnx1phERhBxqGP/IKq0SvZl78kcWDFMaTtVK+w3v3lusRFxJY78n07PbKplirsU5g==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.4.4': - resolution: {integrity: sha512-Dh1a/+W+SUCXhEdL7TiX3ArPTFCQKJTI1mGncZNWfO+6suk+gYA4lNyJcBB+pwvF49uw0pEbUS49BgYOY4hzUg==} + '@biomejs/cli-darwin-x64@2.4.5': + resolution: {integrity: sha512-6MoH4tyISIBNkZ2Q5T1R7dLd5BsITb2yhhhrU9jHZxnNSNMWl+s2Mxu7NBF8Y3a7JJcqq9nsk8i637z4gqkJxQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.4.4': - resolution: {integrity: sha512-+sPAXq3bxmFwhVFJnSwkSF5Rw2ZAJMH3MF6C9IveAEOdSpgajPhoQhbbAK12SehN9j2QrHpk4J/cHsa/HqWaYQ==} + '@biomejs/cli-linux-arm64-musl@2.4.5': + resolution: {integrity: sha512-iqLDgpzobG7gpBF0fwEVS/LT8kmN7+S0E2YKFDtqliJfzNLnAiV2Nnyb+ehCDCJgAZBASkYHR2o60VQWikpqIg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [musl] - '@biomejs/cli-linux-arm64@2.4.4': - resolution: {integrity: sha512-V/NFfbWhsUU6w+m5WYbBenlEAz8eYnSqRMDMAW3K+3v0tYVkNyZn8VU0XPxk/lOqNXLSCCrV7FmV/u3SjCBShg==} + '@biomejs/cli-linux-arm64@2.4.5': + resolution: {integrity: sha512-U1GAG6FTjhAO04MyH4xn23wRNBkT6H7NentHh+8UxD6ShXKBm5SY4RedKJzkUThANxb9rUKIPc7B8ew9Xo/cWg==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] libc: [glibc] - '@biomejs/cli-linux-x64-musl@2.4.4': - resolution: {integrity: sha512-gGvFTGpOIQDb5CQ2VC0n9Z2UEqlP46c4aNgHmAMytYieTGEcfqhfCFnhs6xjt0S3igE6q5GLuIXtdQt3Izok+g==} + '@biomejs/cli-linux-x64-musl@2.4.5': + resolution: {integrity: sha512-NlKa7GpbQmNhZf9kakQeddqZyT7itN7jjWdakELeXyTU3pg/83fTysRRDPJD0akTfKDl6vZYNT9Zqn4MYZVBOA==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [musl] - '@biomejs/cli-linux-x64@2.4.4': - resolution: {integrity: sha512-R4+ZCDtG9kHArasyBO+UBD6jr/FcFCTH8QkNTOCu0pRJzCWyWC4EtZa2AmUZB5h3e0jD7bRV2KvrENcf8rndBg==} + '@biomejs/cli-linux-x64@2.4.5': + resolution: {integrity: sha512-NdODlSugMzTlENPTa4z0xB82dTUlCpsrOxc43///aNkTLblIYH4XpYflBbf5ySlQuP8AA4AZd1qXhV07IdrHdQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] libc: [glibc] - '@biomejs/cli-win32-arm64@2.4.4': - resolution: {integrity: sha512-trzCqM7x+Gn832zZHgr28JoYagQNX4CZkUZhMUac2YxvvyDRLJDrb5m9IA7CaZLlX6lTQmADVfLEKP1et1Ma4Q==} + '@biomejs/cli-win32-arm64@2.4.5': + resolution: {integrity: sha512-EBfrTqRIWOFSd7CQb/0ttjHMR88zm3hGravnDwUA9wHAaCAYsULKDebWcN5RmrEo1KBtl/gDVJMrFjNR0pdGUw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.4.4': - resolution: {integrity: sha512-gnOHKVPFAAPrpoPt2t+Q6FZ7RPry/FDV3GcpU53P3PtLNnQjBmKyN2Vh/JtqXet+H4pme8CC76rScwdjDcT1/A==} + '@biomejs/cli-win32-x64@2.4.5': + resolution: {integrity: sha512-Pmhv9zT95YzECfjEHNl3mN9Vhusw9VA5KHY0ZvlGsxsjwS5cb7vpRnHzJIv0vG7jB0JI7xEaMH9ddfZm/RozBw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -884,9 +887,6 @@ packages: '@emnapi/runtime@1.7.1': resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} - '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -1077,6 +1077,12 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@hono/node-server@1.19.9': + resolution: {integrity: sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -1308,6 +1314,16 @@ packages: stream-browserify: optional: true + '@modelcontextprotocol/sdk@1.27.1': + resolution: {integrity: sha512-sr6GbP+4edBwFndLbM60gf07z0FQ79gaExpnsjMGePXqFcSSb7t6iscpjk9DhFhwd+mTEQrzNafGP8/iGGFYaA==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@mux/mux-data-google-ima@0.3.4': resolution: {integrity: sha512-j8IOD5kw1qIOkbpipEQRGQ7vXB6+CArrhIAvtvj8YFqy0PHi7JcHk4WR3ZBVy5+5yaRCH+nzHkmJmGsg8g6O5g==} @@ -2822,8 +2838,8 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@25.3.2': - resolution: {integrity: sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==} + '@types/node@25.3.3': + resolution: {integrity: sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} @@ -2963,6 +2979,10 @@ packages: '@workflow/serde@4.1.0-beta.2': resolution: {integrity: sha512-8kkeoQKLDaKXefjV5dbhBj2aErfKp1Mc4pb6tj8144cF+Em5SPbyMbyLCHp+BVrFfFVCBluCtMx+jjvaFVZGww==} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2992,6 +3012,17 @@ packages: peerDependencies: zod: ^3.25.76 || ^4.1.8 + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.18.0: + resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -3063,9 +3094,8 @@ packages: resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} engines: {node: '>=6.0.0'} - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} - engines: {node: '>=6.0.0'} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true bcp-47-match@2.0.3: @@ -3087,6 +3117,10 @@ packages: bignumber.js@9.3.1: resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + botbuilder-core@4.23.3: resolution: {integrity: sha512-48iW739I24piBH683b/Unvlu1fSzjB69ViOwZ0PbTkN2yW5cTvHJWlW7bXntO8GSqJfssgPaVthKfyaCW457ig==} @@ -3135,6 +3169,10 @@ packages: peerDependencies: esbuild: '>=0.18' + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -3147,8 +3185,8 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - caniuse-lite@1.0.30001775: - resolution: {integrity: sha512-s3Qv7Lht9zbVKE9XoTyRG6wVDCKdtOFIjBGg3+Yhn6JaytuNKPIjBMTMIY1AnOH3seL5mvF+x33oGAyK3hVt3A==} + caniuse-lite@1.0.30001761: + resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} castable-video@1.1.11: resolution: {integrity: sha512-LCRTK6oe7SB1SiUQFzZCo6D6gcEzijqBTVIuj3smKpQdesXM18QTbCVqWgh9MfOeQgTx/i9ji5jGcdqNPeWg2g==} @@ -3272,6 +3310,26 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -3504,6 +3562,10 @@ packages: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dependency-graph@1.0.0: resolution: {integrity: sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==} engines: {node: '>=4'} @@ -3584,12 +3646,19 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + enhanced-resolve@5.19.0: resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} engines: {node: '>=10.13.0'} @@ -3636,6 +3705,9 @@ packages: engines: {node: '>=18'} hasBin: true + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -3669,6 +3741,10 @@ packages: estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} @@ -3679,10 +3755,24 @@ packages: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + express-rate-limit@8.2.1: + resolution: {integrity: sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -3699,6 +3789,9 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -3734,6 +3827,10 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -3767,6 +3864,10 @@ packages: resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} engines: {node: '>=12.20.0'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + framer-motion@12.34.0: resolution: {integrity: sha512-+/H49owhzkzQyxtn7nZeF4kdH++I2FWrESQ184Zbcw5cEqNHYkE5yxWxcTLSj5lNx3NWdbIRy5FHqUvetD8FWg==} peerDependencies: @@ -3781,6 +3882,10 @@ packages: react-dom: optional: true + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-extra@11.3.3: resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} engines: {node: '>=14.14'} @@ -3878,6 +3983,10 @@ packages: resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} engines: {node: '>=18'} + gaxios@7.1.4: + resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==} + engines: {node: '>=18'} + gcp-metadata@8.1.2: resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} engines: {node: '>=18'} @@ -4002,6 +4111,10 @@ packages: hls.js@1.6.15: resolution: {integrity: sha512-E3a5VwgXimGHwpRGV+WxRTKeSp2DW5DI5MWv34ulL3t5UNmyJWCQ1KmLEHbYzcfThfXG8amBL+fCYPneGHC4VA==} + hono@4.12.3: + resolution: {integrity: sha512-SFsVSjp8sj5UumXOOFlkZOG6XS9SJDKw0TbwFeV+AJ8xlST8kxK5Z/5EYa111UY8732lK2S/xB653ceuaoGwpg==} + engines: {node: '>=16.9.0'} + html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} @@ -4017,6 +4130,10 @@ packages: htmlparser2@9.1.0: resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -4055,6 +4172,9 @@ packages: imsc@1.1.5: resolution: {integrity: sha512-V8je+CGkcvGhgl2C1GlhqFFiUOIEdwXbXLiu1Fcubvvbo+g9inauqT3l0pNYXGoLPBj3jxtZz9t+wCopMkwadQ==} + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inline-style-parser@0.2.7: resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} @@ -4069,6 +4189,14 @@ packages: resolution: {integrity: sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==} engines: {node: '>=12.22.0'} + ip-address@10.0.1: + resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} @@ -4114,6 +4242,9 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -4152,6 +4283,9 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + jotai@2.17.1: resolution: {integrity: sha512-TFNZZDa/0ewCLQyRC/Sq9crtixNj/Xdf/wmj9631xxMuKToVJZDbqcHIYN0OboH+7kh6P6tpIK7uKWClj86PKw==} engines: {node: '>=12.20.0'} @@ -4191,11 +4325,17 @@ packages: json-bigint@1.0.0: resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} - json-with-bigint@3.5.3: - resolution: {integrity: sha512-QObKu6nxy7NsxqR0VK4rkXnsNr5L9ElJaGEg+ucJ6J7/suoKZ0n+p76cu9aCqowytxEbwYNzvrMerfMkXneF5A==} + json-with-bigint@3.5.7: + resolution: {integrity: sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==} jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} @@ -4491,6 +4631,14 @@ packages: media-tracks@0.3.4: resolution: {integrity: sha512-5SUElzGMYXA7bcyZBL1YzLTxH9Iyw1AeYNJxzByqbestrrtB0F3wfiWUr7aROpwodO4fwnxOt78Xjb3o3ONNQg==} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -4640,10 +4788,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + minimatch@10.2.2: resolution: {integrity: sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==} engines: {node: 18 || 20 || >=22} @@ -4780,6 +4936,13 @@ packages: obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + oniguruma-parser@0.12.1: resolution: {integrity: sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==} @@ -4850,6 +5013,10 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -4901,6 +5068,10 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -4954,6 +5125,10 @@ packages: property-information@7.1.0: resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -4961,6 +5136,10 @@ packages: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} + qs@6.15.0: + resolution: {integrity: sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==} + engines: {node: '>=0.6'} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -4980,6 +5159,14 @@ packages: '@types/react-dom': optional: true + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + react-dom@19.2.3: resolution: {integrity: sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==} peerDependencies: @@ -5135,6 +5322,10 @@ packages: remend@1.2.0: resolution: {integrity: sha512-NbKrdWweTRuByPYErzQCNpNtsR9M1QQ0hK2UzmnmlSaEqHnkQ5Korlyi8KpdbOJ0rImJfRy4EAY0uDxYnL9Plw==} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} @@ -5165,6 +5356,10 @@ packages: roughjs@4.6.6: resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rsa-pem-from-mod-exp@0.8.6: resolution: {integrity: sha512-c5ouQkOvGHF1qomUUDJGFcXsomeSO2gbEs6hVhMAtlkE1CuaZase/WzoaKFG/EZQuNmq6pw/EMCeEnDvOgCJYQ==} @@ -5202,10 +5397,16 @@ packages: engines: {node: '>=10'} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} @@ -5288,6 +5489,10 @@ packages: standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -5416,6 +5621,10 @@ packages: resolution: {integrity: sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==} engines: {node: '>=12'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -5506,6 +5715,10 @@ packages: twitch-video-element@0.1.6: resolution: {integrity: sha512-X7l8gy+DEFKJ/EztUwaVnAYwQN9fUJxPkOVJj2sE62sGvGU4DNLyvmOsmVulM+8Plc5dMg6hYIMNRAPaH+39Uw==} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -5575,6 +5788,10 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + url-template@2.0.8: resolution: {integrity: sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==} @@ -5623,6 +5840,10 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vaul@1.1.2: resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} peerDependencies: @@ -5777,6 +5998,9 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + ws@7.5.10: resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} engines: {node: '>=8.3.0'} @@ -5812,6 +6036,11 @@ packages: youtube-video-element@1.8.1: resolution: {integrity: sha512-+5UuAGaj+5AnBf39huLVpy/4dLtR0rmJP1TxOHVZ81bac4ZHFpTtQ4Dz2FAn2GPnfXISezvUEaQoAdFW4hH9Xg==} + zod-to-json-schema@3.25.1: + resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} + peerDependencies: + zod: ^3.25 || ^4 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -5992,39 +6221,39 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@biomejs/biome@2.4.4': + '@biomejs/biome@2.4.5': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.4.4 - '@biomejs/cli-darwin-x64': 2.4.4 - '@biomejs/cli-linux-arm64': 2.4.4 - '@biomejs/cli-linux-arm64-musl': 2.4.4 - '@biomejs/cli-linux-x64': 2.4.4 - '@biomejs/cli-linux-x64-musl': 2.4.4 - '@biomejs/cli-win32-arm64': 2.4.4 - '@biomejs/cli-win32-x64': 2.4.4 + '@biomejs/cli-darwin-arm64': 2.4.5 + '@biomejs/cli-darwin-x64': 2.4.5 + '@biomejs/cli-linux-arm64': 2.4.5 + '@biomejs/cli-linux-arm64-musl': 2.4.5 + '@biomejs/cli-linux-x64': 2.4.5 + '@biomejs/cli-linux-x64-musl': 2.4.5 + '@biomejs/cli-win32-arm64': 2.4.5 + '@biomejs/cli-win32-x64': 2.4.5 - '@biomejs/cli-darwin-arm64@2.4.4': + '@biomejs/cli-darwin-arm64@2.4.5': optional: true - '@biomejs/cli-darwin-x64@2.4.4': + '@biomejs/cli-darwin-x64@2.4.5': optional: true - '@biomejs/cli-linux-arm64-musl@2.4.4': + '@biomejs/cli-linux-arm64-musl@2.4.5': optional: true - '@biomejs/cli-linux-arm64@2.4.4': + '@biomejs/cli-linux-arm64@2.4.5': optional: true - '@biomejs/cli-linux-x64-musl@2.4.4': + '@biomejs/cli-linux-x64-musl@2.4.5': optional: true - '@biomejs/cli-linux-x64@2.4.4': + '@biomejs/cli-linux-x64@2.4.5': optional: true - '@biomejs/cli-win32-arm64@2.4.4': + '@biomejs/cli-win32-arm64@2.4.5': optional: true - '@biomejs/cli-win32-x64@2.4.4': + '@biomejs/cli-win32-x64@2.4.5': optional: true '@braintree/sanitize-url@7.1.2': {} @@ -6058,7 +6287,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.8(@types/node@25.3.2)': + '@changesets/cli@2.29.8(@types/node@25.3.3)': dependencies: '@changesets/apply-release-plan': 7.0.14 '@changesets/assemble-release-plan': 6.0.9 @@ -6074,7 +6303,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.3.2) + '@inquirer/external-editor': 1.0.3(@types/node@25.3.3) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 ci-info: 3.9.0 @@ -6261,11 +6490,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emnapi/runtime@1.8.1': - dependencies: - tslib: 2.8.1 - optional: true - '@emnapi/wasi-threads@1.1.0': dependencies: tslib: 2.8.1 @@ -6386,6 +6610,10 @@ snapshots: dependencies: graphql: 15.10.1 + '@hono/node-server@1.19.9(hono@4.12.3)': + dependencies: + hono: 4.12.3 + '@iconify/types@2.0.0': {} '@iconify/utils@3.1.0': @@ -6483,7 +6711,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.8.1 + '@emnapi/runtime': 1.7.1 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -6495,12 +6723,12 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/external-editor@1.0.3(@types/node@25.3.2)': + '@inquirer/external-editor@1.0.3(@types/node@25.3.3)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.1 optionalDependencies: - '@types/node': 25.3.2 + '@types/node': 25.3.3 '@ioredis/commands@1.4.0': {} @@ -6595,6 +6823,28 @@ snapshots: optionalDependencies: '@azure/identity': 4.13.0 + '@modelcontextprotocol/sdk@1.27.1(zod@4.3.3)': + dependencies: + '@hono/node-server': 1.19.9(hono@4.12.3) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.2.1(express@5.2.1) + hono: 4.12.3 + jose: 6.1.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.3.3 + zod-to-json-schema: 3.25.1(zod@4.3.3) + transitivePeerDependencies: + - supports-color + '@mux/mux-data-google-ima@0.3.4': dependencies: mux-embed: 5.16.1 @@ -6769,7 +7019,7 @@ snapshots: '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 fast-content-type-parse: 3.0.0 - json-with-bigint: 3.5.3 + json-with-bigint: 3.5.7 universal-user-agent: 7.0.3 '@octokit/rest@22.0.1': @@ -7763,7 +8013,7 @@ snapshots: '@slack/logger@4.0.0': dependencies: - '@types/node': 25.3.2 + '@types/node': 25.3.3 '@slack/types@2.19.0': {} @@ -7771,7 +8021,7 @@ snapshots: dependencies: '@slack/logger': 4.0.0 '@slack/types': 2.19.0 - '@types/node': 25.3.2 + '@types/node': 25.3.3 '@types/retry': 0.12.0 axios: 1.13.2 eventemitter3: 5.0.1 @@ -8065,7 +8315,7 @@ snapshots: '@types/jsonwebtoken@9.0.6': dependencies: - '@types/node': 25.3.2 + '@types/node': 25.3.3 '@types/mdast@4.0.4': dependencies: @@ -8077,7 +8327,7 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@25.3.2': + '@types/node@25.3.3': dependencies: undici-types: 7.18.2 @@ -8100,11 +8350,11 @@ snapshots: '@types/ws@6.0.4': dependencies: - '@types/node': 25.3.2 + '@types/node': 25.3.3 '@types/ws@8.18.1': dependencies: - '@types/node': 25.3.2 + '@types/node': 25.3.3 '@typespec/ts-http-runtime@0.3.2': dependencies: @@ -8135,7 +8385,7 @@ snapshots: native-promise-only: 0.8.1 weakmap-polyfill: 2.0.4 - '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + '@vitest/coverage-v8@4.0.18(vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': dependencies: '@bcoe/v8-coverage': 1.0.2 '@vitest/utils': 4.0.18 @@ -8147,7 +8397,7 @@ snapshots: obug: 2.1.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) '@vitest/expect@4.0.18': dependencies: @@ -8158,13 +8408,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) '@vitest/pretty-format@4.0.18': dependencies: @@ -8192,6 +8442,11 @@ snapshots: '@workflow/serde@4.1.0-beta.2': {} + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -8218,6 +8473,17 @@ snapshots: '@opentelemetry/api': 1.9.0 zod: 4.3.3 + ajv-formats@3.0.1(ajv@8.18.0): + optionalDependencies: + ajv: 8.18.0 + + ajv@8.18.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-colors@4.1.3: {} ansi-regex@5.0.1: {} @@ -8274,7 +8540,7 @@ snapshots: base64url@3.0.1: {} - baseline-browser-mapping@2.10.0: {} + baseline-browser-mapping@2.9.11: {} bcp-47-match@2.0.3: {} @@ -8297,6 +8563,20 @@ snapshots: bignumber.js@9.3.1: {} + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.1 + on-finished: 2.4.1 + qs: 6.15.0 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + botbuilder-core@4.23.3: dependencies: botbuilder-dialogs-adaptive-runtime-core: 4.23.3-preview @@ -8414,6 +8694,8 @@ snapshots: esbuild: 0.27.2 load-tsconfig: 0.2.5 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -8426,7 +8708,7 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - caniuse-lite@1.0.30001775: {} + caniuse-lite@1.0.30001761: {} castable-video@1.1.11: dependencies: @@ -8530,6 +8812,19 @@ snapshots: consola@3.4.2: {} + content-disposition@1.0.1: {} + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -8804,6 +9099,8 @@ snapshots: denque@2.1.0: {} + depd@2.0.0: {} + dependency-graph@1.0.0: {} dequal@2.0.3: {} @@ -8891,10 +9188,14 @@ snapshots: dependencies: safe-buffer: 5.2.1 + ee-first@1.1.1: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} + encodeurl@2.0.0: {} + enhanced-resolve@5.19.0: dependencies: graceful-fs: 4.2.11 @@ -8969,6 +9270,8 @@ snapshots: '@esbuild/win32-ia32': 0.27.2 '@esbuild/win32-x64': 0.27.2 + escape-html@1.0.3: {} + escape-string-regexp@5.0.0: {} esprima@4.0.1: {} @@ -9010,14 +9313,58 @@ snapshots: dependencies: '@types/estree': 1.0.8 + etag@1.8.1: {} + eventemitter3@4.0.7: {} eventemitter3@5.0.1: {} eventsource-parser@3.0.6: {} + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + expect-type@1.3.0: {} + express-rate-limit@8.2.1(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.0.1 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.0.1 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extend@3.0.2: {} extendable-error@0.1.7: {} @@ -9034,6 +9381,8 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-uri@3.1.0: {} + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -9065,6 +9414,17 @@ snapshots: dependencies: to-regex-range: 5.0.1 + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -9099,6 +9459,8 @@ snapshots: dependencies: fetch-blob: 3.2.0 + forwarded@0.2.0: {} + framer-motion@12.34.0(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: motion-dom: 12.34.0 @@ -9108,6 +9470,8 @@ snapshots: react: 19.2.3 react-dom: 19.2.3(react@19.2.3) + fresh@2.0.0: {} + fs-extra@11.3.3: dependencies: graceful-fs: 4.2.11 @@ -9158,7 +9522,7 @@ snapshots: transitivePeerDependencies: - supports-color - fumadocs-mdx@14.0.4(fumadocs-core@16.2.2(@types/react@19.2.7)(lucide-react@0.555.0(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): + fumadocs-mdx@14.0.4(fumadocs-core@16.2.2(@types/react@19.2.7)(lucide-react@0.555.0(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(next@16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)): dependencies: '@mdx-js/mdx': 3.1.1 '@standard-schema/spec': 1.1.0 @@ -9182,7 +9546,7 @@ snapshots: optionalDependencies: next: 16.1.5(@opentelemetry/api@1.9.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) react: 19.2.3 - vite: 7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) transitivePeerDependencies: - supports-color @@ -9234,9 +9598,17 @@ snapshots: transitivePeerDependencies: - supports-color + gaxios@7.1.4: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + node-fetch: 3.3.2 + transitivePeerDependencies: + - supports-color + gcp-metadata@8.1.2: dependencies: - gaxios: 7.1.3 + gaxios: 7.1.4 google-logging-utils: 1.1.3 json-bigint: 1.0.0 transitivePeerDependencies: @@ -9314,9 +9686,9 @@ snapshots: googleapis-common@8.0.1: dependencies: extend: 3.0.2 - gaxios: 7.1.3 + gaxios: 7.1.4 google-auth-library: 10.6.1 - qs: 6.14.0 + qs: 6.15.0 url-template: 2.0.8 transitivePeerDependencies: - supports-color @@ -9467,6 +9839,8 @@ snapshots: hls.js@1.6.15: {} + hono@4.12.3: {} + html-entities@2.6.0: {} html-escaper@2.0.2: {} @@ -9482,6 +9856,14 @@ snapshots: domutils: 3.2.2 entities: 4.5.0 + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -9518,6 +9900,8 @@ snapshots: dependencies: sax: 1.2.1 + inherits@2.0.4: {} + inline-style-parser@0.2.7: {} internmap@1.0.1: {} @@ -9538,6 +9922,10 @@ snapshots: transitivePeerDependencies: - supports-color + ip-address@10.0.1: {} + + ipaddr.js@1.9.1: {} + is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: @@ -9569,6 +9957,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-promise@4.0.0: {} + is-stream@2.0.1: {} is-subdir@1.2.0: @@ -9604,6 +9994,8 @@ snapshots: jiti@2.6.1: {} + jose@6.1.3: {} + jotai@2.17.1(@types/react@19.2.7)(react@19.2.3): optionalDependencies: '@types/react': 19.2.7 @@ -9628,9 +10020,13 @@ snapshots: dependencies: bignumber.js: 9.3.1 + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} - json-with-bigint@3.5.3: {} + json-with-bigint@3.5.7: {} jsonc-parser@3.3.1: {} @@ -9655,7 +10051,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.7.4 + semver: 7.7.3 jwa@2.0.1: dependencies: @@ -9674,10 +10070,10 @@ snapshots: khroma@2.1.0: {} - knip@5.85.0(@types/node@25.3.2)(typescript@5.9.3): + knip@5.85.0(@types/node@25.3.3)(typescript@5.9.3): dependencies: '@nodelib/fs.walk': 1.2.8 - '@types/node': 25.3.2 + '@types/node': 25.3.3 fast-glob: 3.3.3 formatly: 0.3.0 jiti: 2.6.1 @@ -9828,7 +10224,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.7.4 + semver: 7.7.3 markdown-extensions@2.0.0: {} @@ -10017,6 +10413,10 @@ snapshots: media-tracks@0.3.4: {} + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + merge2@1.4.1: {} mermaid@11.12.2: @@ -10345,10 +10745,16 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + minimatch@10.2.2: dependencies: brace-expansion: 5.0.2 @@ -10413,8 +10819,8 @@ snapshots: dependencies: '@next/env': 16.1.5 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001775 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 postcss: 8.4.31 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -10460,6 +10866,14 @@ snapshots: obug@2.1.1: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + oniguruma-parser@0.12.1: {} oniguruma-to-es@4.3.4: @@ -10556,6 +10970,8 @@ snapshots: dependencies: entities: 6.0.1 + parseurl@1.3.3: {} + path-browserify@1.0.1: {} path-data-parser@0.1.0: {} @@ -10590,6 +11006,8 @@ snapshots: pirates@4.0.7: {} + pkce-challenge@5.0.1: {} + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -10644,12 +11062,21 @@ snapshots: property-information@7.1.0: {} + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + proxy-from-env@1.1.0: {} qs@6.14.0: dependencies: side-channel: 1.1.0 + qs@6.15.0: + dependencies: + side-channel: 1.1.0 + quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -10717,6 +11144,15 @@ snapshots: '@types/react': 19.2.7 '@types/react-dom': 19.2.3(@types/react@19.2.7) + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.1 + unpipe: 1.0.0 + react-dom@19.2.3(react@19.2.3): dependencies: react: 19.2.3 @@ -10940,6 +11376,8 @@ snapshots: remend@1.2.0: {} + require-from-string@2.0.2: {} + resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} @@ -10989,6 +11427,16 @@ snapshots: points-on-curve: 0.2.0 points-on-path: 0.2.1 + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.3.0 + transitivePeerDependencies: + - supports-color + rsa-pem-from-mod-exp@0.8.6: {} run-applescript@7.1.0: {} @@ -11015,13 +11463,38 @@ snapshots: semver@7.7.3: {} - semver@7.7.4: {} + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} sharp@0.34.5: dependencies: '@img/colour': 1.0.0 detect-libc: 2.1.2 - semver: 7.7.4 + semver: 7.7.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -11128,6 +11601,8 @@ snapshots: standard-as-callback@2.1.0: {} + statuses@2.0.2: {} + std-env@3.10.0: {} streamdown@2.2.0(react@19.2.3): @@ -11256,6 +11731,8 @@ snapshots: toad-cache@3.7.0: {} + toidentifier@1.0.1: {} + tr46@0.0.3: {} tree-kill@1.2.2: {} @@ -11338,6 +11815,12 @@ snapshots: twitch-video-element@0.1.6: {} + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + typescript@5.9.3: {} ua-parser-js@1.0.41: {} @@ -11413,6 +11896,8 @@ snapshots: universalify@2.0.1: {} + unpipe@1.0.0: {} + url-template@2.0.8: {} use-callback-ref@1.3.3(@types/react@19.2.7)(react@19.2.3): @@ -11446,6 +11931,8 @@ snapshots: uuid@8.3.2: {} + vary@1.1.2: {} + vaul@1.1.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -11474,7 +11961,7 @@ snapshots: dependencies: '@vimeo/player': 2.29.0 - vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -11483,16 +11970,16 @@ snapshots: rollup: 4.54.0 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.3.2 + '@types/node': 25.3.3 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 tsx: 4.21.0 - vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): + vitest@4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -11509,11 +11996,11 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.3.2)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) + vite: 7.3.1(@types/node@25.3.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.0 - '@types/node': 25.3.2 + '@types/node': 25.3.3 transitivePeerDependencies: - jiti - less @@ -11584,6 +12071,8 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.1.2 + wrappy@1.0.2: {} + ws@7.5.10: {} ws@8.18.3: {} @@ -11598,6 +12087,10 @@ snapshots: youtube-video-element@1.8.1: {} + zod-to-json-schema@3.25.1(zod@4.3.3): + dependencies: + zod: 4.3.3 + zod@3.25.76: {} zod@4.3.3: {}