From eeeb9313b6d549abc5646982c9639d64f9f672f8 Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Thu, 26 Feb 2026 12:37:49 -0500 Subject: [PATCH 1/4] feat(chat): add MCP client support for tool discovery and invocation Add `mcpServers` to ChatConfig and expose `chat.mcp` with `listTools()`, `callTool()`, and `refresh()` methods. Supports SSE and Streamable HTTP transports with static or dynamic auth headers. Uses dynamic imports so users who don't configure MCP pay zero cost. Failed server connections log warnings and don't block initialization. Co-Authored-By: Claude Opus 4.6 --- .changeset/add-mcp-client-support.md | 5 + packages/chat/package.json | 9 + packages/chat/src/chat.ts | 21 + packages/chat/src/index.ts | 10 + packages/chat/src/mcp-types.ts | 75 +++ packages/chat/src/mcp.test.ts | 325 ++++++++++++ packages/chat/src/mcp.ts | 297 +++++++++++ packages/chat/src/types.ts | 3 + pnpm-lock.yaml | 749 ++++++++++++++++++++++----- 9 files changed, 1366 insertions(+), 128 deletions(-) create mode 100644 .changeset/add-mcp-client-support.md create mode 100644 packages/chat/src/mcp-types.ts create mode 100644 packages/chat/src/mcp.test.ts create mode 100644 packages/chat/src/mcp.ts 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/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..b0e683cf 100644 --- a/packages/chat/src/index.ts +++ b/packages/chat/src/index.ts @@ -169,6 +169,16 @@ export { toPlainText, walkAst, } from "./markdown"; +// MCP types +export type { + 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..25421435 --- /dev/null +++ b/packages/chat/src/mcp-types.ts @@ -0,0 +1,75 @@ +/** + * 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; +} + +/** + * Manager interface for interacting with MCP servers. + */ +export interface McpManager { + /** Call a tool by name with optional arguments. */ + callTool( + name: string, + args?: Record + ): 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..562f2d54 --- /dev/null +++ b/packages/chat/src/mcp.test.ts @@ -0,0 +1,325 @@ +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 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..f668c61c --- /dev/null +++ b/packages/chat/src/mcp.ts @@ -0,0 +1,297 @@ +/** + * MCP (Model Context Protocol) client manager implementation. + */ + +import { ChatError } from "./errors"; +import type { Logger } from "./logger"; +import type { + McpContentBlock, + McpHeaders, + McpManager, + McpServerConfig, + McpTool, + McpToolResult, + McpTransportConfig, +} from "./mcp-types"; + +interface ConnectedServer { + client: McpClient; + name: string; + tools: McpTool[]; + transport: McpTransport; +} + +// 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 servers: ConnectedServer[] = []; + + constructor(configs: McpServerConfig[], logger: Logger) { + this.configs = configs; + this.logger = logger; + } + + async initialize(): Promise { + const sdk = await loadMcpSdk(); + + 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, + 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 + ): 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" + ); + } + + const result = await server.client.callTool({ name, arguments: args }); + return { + content: result.content as McpContentBlock[], + isError: result.isError, + }; + } + + 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): 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: {} From 47707e4d98bb94e9bfede64b4f6155978302bdbd Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Thu, 26 Feb 2026 13:56:16 -0500 Subject: [PATCH 2/4] feat(mcp): add per-call auth headers and documentation Add McpCallToolOptions with per-call headers override for per-user/per-tenant auth scenarios. When headers are provided, a temporary client+transport is created for the duration of the call. Also adds MCP feature docs and API reference updates. Co-Authored-By: Claude Opus 4.6 --- apps/docs/content/docs/api/chat.mdx | 44 +++++++ apps/docs/content/docs/mcp.mdx | 181 ++++++++++++++++++++++++++++ apps/docs/content/docs/meta.json | 1 + packages/chat/src/index.ts | 1 + packages/chat/src/mcp-types.ts | 14 ++- packages/chat/src/mcp.test.ts | 73 +++++++++++ packages/chat/src/mcp.ts | 43 ++++++- 7 files changed, 353 insertions(+), 4 deletions(-) create mode 100644 apps/docs/content/docs/mcp.mdx 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..a1398f56 --- /dev/null +++ b/apps/docs/content/docs/mcp.mdx @@ -0,0 +1,181 @@ +--- +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); +}); +``` + +## 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/src/index.ts b/packages/chat/src/index.ts index b0e683cf..a77be1a0 100644 --- a/packages/chat/src/index.ts +++ b/packages/chat/src/index.ts @@ -171,6 +171,7 @@ export { } from "./markdown"; // MCP types export type { + McpCallToolOptions, McpContentBlock, McpHeaders, McpManager, diff --git a/packages/chat/src/mcp-types.ts b/packages/chat/src/mcp-types.ts index 25421435..f278e9d6 100644 --- a/packages/chat/src/mcp-types.ts +++ b/packages/chat/src/mcp-types.ts @@ -58,14 +58,24 @@ export interface McpToolResult { 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. */ + /** Call a tool by name with optional arguments and per-call options. */ callTool( name: string, - args?: Record + args?: Record, + options?: McpCallToolOptions ): Promise; /** List all tools available across connected MCP servers. */ listTools(): Promise; diff --git a/packages/chat/src/mcp.test.ts b/packages/chat/src/mcp.test.ts index 562f2d54..9da2d69b 100644 --- a/packages/chat/src/mcp.test.ts +++ b/packages/chat/src/mcp.test.ts @@ -209,6 +209,79 @@ describe("McpClientManager", () => { 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[] = [ { diff --git a/packages/chat/src/mcp.ts b/packages/chat/src/mcp.ts index f668c61c..dc871382 100644 --- a/packages/chat/src/mcp.ts +++ b/packages/chat/src/mcp.ts @@ -5,6 +5,7 @@ import { ChatError } from "./errors"; import type { Logger } from "./logger"; import type { + McpCallToolOptions, McpContentBlock, McpHeaders, McpManager, @@ -19,6 +20,7 @@ interface ConnectedServer { name: string; tools: McpTool[]; transport: McpTransport; + transportConfig: McpTransportConfig; } // Minimal interfaces for the MCP SDK types we use, to avoid importing at module level. @@ -156,6 +158,7 @@ async function createTransport( 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) { @@ -165,6 +168,7 @@ export class McpClientManager implements McpManager { async initialize(): Promise { const sdk = await loadMcpSdk(); + this.sdk = sdk; const results = await Promise.allSettled( this.configs.map(async (config) => { @@ -188,6 +192,7 @@ export class McpClientManager implements McpManager { name: config.name, client, transport, + transportConfig: config.transport, tools: mappedTools, } as ConnectedServer; }) @@ -218,7 +223,8 @@ export class McpClientManager implements McpManager { async callTool( name: string, - args?: Record + args?: Record, + options?: McpCallToolOptions ): Promise { const server = this.servers.find((s) => s.tools.some((t) => t.name === name) @@ -230,6 +236,10 @@ export class McpClientManager implements McpManager { ); } + 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[], @@ -237,6 +247,31 @@ export class McpClientManager implements McpManager { }; } + 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) => { @@ -284,7 +319,11 @@ export class NoopMcpManager implements McpManager { return []; } - async callTool(name: string): Promise { + async callTool( + name: string, + _args?: Record, + _options?: McpCallToolOptions + ): Promise { throw new ChatError( `MCP is not configured. Cannot call tool "${name}"`, "MCP_NOT_CONFIGURED" From 0d66401c80f4c77fd515f0443723fa3292ac9ccb Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Mon, 2 Mar 2026 10:14:42 -0500 Subject: [PATCH 3/4] docs(mcp): add AI SDK integration section with provider examples Co-Authored-By: Claude Opus 4.6 --- apps/docs/content/docs/mcp.mdx | 107 +++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/apps/docs/content/docs/mcp.mdx b/apps/docs/content/docs/mcp.mdx index a1398f56..9ca20387 100644 --- a/apps/docs/content/docs/mcp.mdx +++ b/apps/docs/content/docs/mcp.mdx @@ -77,6 +77,113 @@ bot.onNewMention(async (thread, message) => { }); ``` +## 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-clients) 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 | From f965808bcc0a91e9e50f2af2f700971ce44a976a Mon Sep 17 00:00:00 2001 From: Sergiy Dybskiy Date: Mon, 2 Mar 2026 10:25:18 -0500 Subject: [PATCH 4/4] fix(docs): set default tab and correct AI SDK MCP link Co-Authored-By: Claude Opus 4.6 --- apps/docs/content/docs/mcp.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/docs/content/docs/mcp.mdx b/apps/docs/content/docs/mcp.mdx index 9ca20387..54819898 100644 --- a/apps/docs/content/docs/mcp.mdx +++ b/apps/docs/content/docs/mcp.mdx @@ -79,7 +79,7 @@ bot.onNewMention(async (thread, message) => { ## 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-clients) 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. +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 @@ -87,7 +87,7 @@ pnpm add @ai-sdk/mcp ai Create an MCP client and pass its tools to a `ToolLoopAgent`: - + AI Gateway Anthropic