diff --git a/.changeset/mcp-tools-port.md b/.changeset/mcp-tools-port.md new file mode 100644 index 000000000..c3f53c977 --- /dev/null +++ b/.changeset/mcp-tools-port.md @@ -0,0 +1,19 @@ +--- +'@livekit/agents': patch +--- + +feat(mcp): add MCP (Model Context Protocol) server integration + +Ports MCP tool support from `livekit-agents` (Python). Tools exposed by an +MCP server are fetched on activity start and merged into the agent's tool +context. Both stdio and HTTP transports (SSE + streamable HTTP) are supported. + +```ts +const session = new voice.AgentSession({ + mcpServers: [new llm.MCPServerHTTP({ url: 'http://localhost:8000/sse' })], + // ... +}); +``` + +`@modelcontextprotocol/sdk` is an optional peer dependency: install it with +`pnpm add @modelcontextprotocol/sdk` to use this feature. diff --git a/agents/package.json b/agents/package.json index 0cd795d37..63753691e 100644 --- a/agents/package.json +++ b/agents/package.json @@ -83,6 +83,12 @@ }, "peerDependencies": { "@livekit/rtc-node": "catalog:", + "@modelcontextprotocol/sdk": "^1.25.0", "zod": "^3.25.76 || ^4.1.8" + }, + "peerDependenciesMeta": { + "@modelcontextprotocol/sdk": { + "optional": true + } } } diff --git a/agents/src/llm/index.ts b/agents/src/llm/index.ts index ae6f75b9c..f032c18f7 100644 --- a/agents/src/llm/index.ts +++ b/agents/src/llm/index.ts @@ -83,3 +83,17 @@ export { type AvailabilityChangedEvent, type FallbackAdapterOptions, } from './fallback_adapter.js'; + +export { + MCPServer, + MCPServerHTTP, + MCPServerStdio, + type MCPHTTPTransportType, + type MCPServerHTTPOptions, + type MCPServerOptions, + type MCPServerStdioOptions, + type MCPToolCallResult, + type MCPToolContent, + type MCPToolResultContext, + type MCPToolResultResolver, +} from './mcp.js'; diff --git a/agents/src/llm/mcp.ts b/agents/src/llm/mcp.ts new file mode 100644 index 000000000..f72913c81 --- /dev/null +++ b/agents/src/llm/mcp.ts @@ -0,0 +1,415 @@ +// SPDX-FileCopyrightText: 2026 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import type { JSONSchema7 } from 'json-schema'; +import { log } from '../log.js'; +import { + type FunctionTool, + type JSONObject, + type ToolContext, + ToolError, + tool, +} from './tool_context.js'; + +// `@modelcontextprotocol/sdk` is an optional peer dependency. The types below +// describe the minimal subset of the SDK we rely on so the rest of the file +// can typecheck without the SDK being present at build time. +type MCPRequestOptions = { timeout?: number; signal?: AbortSignal }; + +type MCPClient = { + connect: (transport: MCPTransport) => Promise; + close: () => Promise; + listTools: ( + params?: unknown, + options?: MCPRequestOptions, + ) => Promise<{ tools: MCPToolDescriptor[] }>; + callTool: ( + params: { name: string; arguments?: JSONObject }, + resultSchema?: unknown, + options?: MCPRequestOptions, + ) => Promise; +}; + +type MCPTransport = unknown; + +interface MCPToolDescriptor { + name: string; + description?: string; + inputSchema: { + type: 'object'; + properties?: JSONObject; + required?: string[]; + [k: string]: unknown; + }; + _meta?: JSONObject; +} + +interface MCPTextContent { + type: 'text'; + text: string; + [k: string]: unknown; +} + +interface MCPImageContent { + type: 'image'; + data: string; + mimeType: string; + [k: string]: unknown; +} + +interface MCPOtherContent { + type: string; + [k: string]: unknown; +} + +export type MCPToolContent = MCPTextContent | MCPImageContent | MCPOtherContent; + +export interface MCPToolCallResult { + content: MCPToolContent[]; + isError?: boolean; + structuredContent?: unknown; + [k: string]: unknown; +} + +export interface MCPToolResultContext { + toolName: string; + arguments: JSONObject; + result: MCPToolCallResult; +} + +/** + * Resolver invoked to convert an MCP tool result into the value that is + * returned to the LLM. The default implementation serializes the content + * blocks to JSON. + */ +export type MCPToolResultResolver = (ctx: MCPToolResultContext) => unknown | Promise; + +const defaultToolResultResolver: MCPToolResultResolver = (ctx) => { + const content = ctx.result.content; + if (content.length === 1) { + return JSON.stringify(content[0]); + } + if (content.length > 1) { + return JSON.stringify(content); + } + throw new ToolError( + `Tool '${ctx.toolName}' completed without producing a result. ` + + 'This might indicate an issue with internal processing.', + ); +}; + +export interface MCPServerOptions { + /** + * Per-request timeout (in milliseconds) for the underlying MCP client + * session. `null` disables the timeout. + */ + clientSessionTimeout?: number | null; + /** + * Callback used to convert the raw MCP tool result into a value returned to + * the LLM. Defaults to a JSON serializer. + */ + toolResultResolver?: MCPToolResultResolver; +} + +const DEFAULT_CLIENT_SESSION_TIMEOUT = 5000; + +/** + * Lazily-imported handle to the MCP SDK so users that do not need MCP do not + * have to install `@modelcontextprotocol/sdk`. We only resolve the SDK when + * an `MCPServer` is initialized. + */ +async function loadMCPClient(): Promise< + new (info: { name: string; version: string }) => MCPClient +> { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- dynamic import of optional peer + const mod: any = await import('@modelcontextprotocol/sdk/client/index.js'); + return mod.Client; + } catch (err) { + throw new Error( + "The '@modelcontextprotocol/sdk' package is required to use MCP servers but is not installed.\n" + + 'Install it with: pnpm add @modelcontextprotocol/sdk', + ); + } +} + +/** + * Base class for an MCP server connection. Concrete subclasses provide a + * transport (HTTP/SSE/stdio) by implementing :meth:`createTransport`. + */ +export abstract class MCPServer { + protected logger = log(); + private _client: MCPClient | null = null; + private _initializing: Promise | null = null; + private _cacheDirty = true; + private _cachedTools: ToolContext | null = null; + private readonly _clientSessionTimeout: number | null; + private readonly _toolResultResolver: MCPToolResultResolver; + + constructor(options: MCPServerOptions = {}) { + this._clientSessionTimeout = + options.clientSessionTimeout === undefined + ? DEFAULT_CLIENT_SESSION_TIMEOUT + : options.clientSessionTimeout; + this._toolResultResolver = options.toolResultResolver ?? defaultToolResultResolver; + } + + get initialized(): boolean { + return this._client !== null; + } + + invalidateCache(): void { + this._cacheDirty = true; + } + + /** + * Connect to the MCP server. Subsequent calls while a connection is active + * are a no-op. + */ + async initialize(): Promise { + if (this._client) return; + if (this._initializing) { + await this._initializing; + return; + } + + this._initializing = (async () => { + const ClientCtor = await loadMCPClient(); + const client = new ClientCtor({ name: 'livekit-agents', version: '1.0.0' }); + const transport = await this.createTransport(); + await client.connect(transport); + this._client = client; + })(); + + try { + await this._initializing; + } finally { + this._initializing = null; + } + } + + /** Close the MCP server connection. */ + async aclose(): Promise { + const client = this._client; + this._client = null; + this._cachedTools = null; + this._cacheDirty = true; + if (client) { + try { + await client.close(); + } catch (err) { + this.logger.warn({ err }, 'error closing MCP client'); + } + } + } + + /** + * List the tools exposed by the server, returned as a {@link ToolContext} + * keyed by tool name. The result is cached until {@link invalidateCache} is + * called. + */ + async listTools(): Promise { + if (!this._client) { + throw new Error('MCPServer is not initialized'); + } + if (!this._cacheDirty && this._cachedTools) { + return this._cachedTools; + } + + const result = await this._client.listTools(undefined, this._requestOptions()); + const tools: ToolContext = {}; + for (const t of result.tools) { + tools[t.name] = this._makeFunctionTool(t); + } + this._cachedTools = tools; + this._cacheDirty = false; + return tools; + } + + protected abstract createTransport(): Promise; + + protected get clientSessionTimeout(): number | null { + return this._clientSessionTimeout; + } + + private _requestOptions(): MCPRequestOptions | undefined { + return this._clientSessionTimeout != null ? { timeout: this._clientSessionTimeout } : undefined; + } + + private _makeFunctionTool(descriptor: MCPToolDescriptor): FunctionTool { + const name = descriptor.name; + return tool({ + description: descriptor.description ?? '', + parameters: descriptor.inputSchema as unknown as JSONSchema7, + execute: async (args: JSONObject) => { + const client = this._client; + if (!client) { + throw new ToolError( + 'Tool invocation failed: internal service is unavailable. ' + + 'Please check that the MCPServer is still running.', + ); + } + + const result = await client.callTool( + { name, arguments: args }, + undefined, + this._requestOptions(), + ); + + if (result.isError) { + const text = result.content + .map((part) => + 'text' in part && typeof part.text === 'string' ? part.text : JSON.stringify(part), + ) + .join('\n'); + throw new ToolError(text); + } + + return await this._toolResultResolver({ + toolName: name, + arguments: args, + result, + }); + }, + }) as FunctionTool; + } +} + +export type MCPHTTPTransportType = 'sse' | 'streamable_http'; + +export interface MCPServerHTTPOptions extends MCPServerOptions { + /** URL of the MCP server. */ + url: string; + /** + * Explicit transport type. If omitted, the type is inferred from the URL + * path: paths ending with `/mcp` use `streamable_http`, all others fall + * back to `sse`. + * + * Note: SSE is being deprecated in favor of streamable HTTP. See + * https://github.com/modelcontextprotocol/modelcontextprotocol/pull/206. + */ + transportType?: MCPHTTPTransportType; + /** Optional list of tool names to expose. When set, all other tools are filtered out. */ + allowedTools?: string[]; + /** HTTP headers to include in requests. */ + headers?: Record; +} + +/** + * HTTP-based MCP server, supporting SSE and streamable HTTP transports. + */ +export class MCPServerHTTP extends MCPServer { + readonly url: string; + readonly transportType: MCPHTTPTransportType; + private readonly _headers: Record; + private readonly _allowedTools: Set | null; + + constructor(options: MCPServerHTTPOptions) { + super(options); + this.url = options.url; + this._headers = options.headers ?? {}; + this._allowedTools = options.allowedTools ? new Set(options.allowedTools) : null; + this.transportType = options.transportType ?? this._inferTransportType(options.url); + } + + protected async createTransport(): Promise { + const requestInit: RequestInit = { + headers: this._headers, + }; + const url = new URL(this.url); + + if (this.transportType === 'streamable_http') { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional peer dep + const mod: any = await import('@modelcontextprotocol/sdk/client/streamableHttp.js'); + return new mod.StreamableHTTPClientTransport(url, { requestInit }); + } catch (err) { + throw new Error( + "Failed to load '@modelcontextprotocol/sdk/client/streamableHttp.js'. " + + 'Install with: pnpm add @modelcontextprotocol/sdk', + ); + } + } + + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional peer dep + const mod: any = await import('@modelcontextprotocol/sdk/client/sse.js'); + return new mod.SSEClientTransport(url, { requestInit }); + } catch (err) { + throw new Error( + "Failed to load '@modelcontextprotocol/sdk/client/sse.js'. " + + 'Install with: pnpm add @modelcontextprotocol/sdk', + ); + } + } + + override async listTools(): Promise { + const all = await super.listTools(); + if (!this._allowedTools) return all; + + const filtered: ToolContext = {}; + for (const [name, t] of Object.entries(all)) { + if (this._allowedTools.has(name)) { + filtered[name] = t; + } + } + return filtered; + } + + private _inferTransportType(url: string): MCPHTTPTransportType { + try { + const path = new URL(url).pathname.toLowerCase().replace(/\/$/, ''); + return path.endsWith('/mcp') ? 'streamable_http' : 'sse'; + } catch { + return 'sse'; + } + } +} + +export interface MCPServerStdioOptions extends MCPServerOptions { + /** The executable to run to start the server. */ + command: string; + /** Command line arguments. */ + args?: string[]; + /** Environment variables for the spawned process. */ + env?: Record; + /** Working directory for the spawned process. */ + cwd?: string; +} + +/** + * Stdio-based MCP server. Spawns a subprocess and communicates with it over + * stdin/stdout. + */ +export class MCPServerStdio extends MCPServer { + readonly command: string; + readonly args: string[]; + readonly env?: Record; + readonly cwd?: string; + + constructor(options: MCPServerStdioOptions) { + super(options); + this.command = options.command; + this.args = options.args ?? []; + this.env = options.env; + this.cwd = options.cwd; + } + + protected async createTransport(): Promise { + try { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional peer dep + const mod: any = await import('@modelcontextprotocol/sdk/client/stdio.js'); + return new mod.StdioClientTransport({ + command: this.command, + args: this.args, + env: this.env, + cwd: this.cwd, + }); + } catch (err) { + throw new Error( + "Failed to load '@modelcontextprotocol/sdk/client/stdio.js'. " + + 'Install with: pnpm add @modelcontextprotocol/sdk', + ); + } + } +} diff --git a/agents/src/voice/agent.ts b/agents/src/voice/agent.ts index 82ad8ba31..6a74129bf 100644 --- a/agents/src/voice/agent.ts +++ b/agents/src/voice/agent.ts @@ -13,7 +13,7 @@ import { type TTSModelString, } from '../inference/index.js'; import { ReadonlyChatContext } from '../llm/chat_context.js'; -import type { ChatMessage, FunctionCall } from '../llm/index.js'; +import type { ChatMessage, FunctionCall, MCPServer } from '../llm/index.js'; import { type ChatChunk, ChatContext, @@ -118,6 +118,13 @@ export interface AgentOptions { instructions: string; chatCtx?: ChatContext; tools?: ToolContext; + /** + * List of MCP servers providing additional tools for this agent. Tools + * fetched from these servers are merged into the agent's tool context when + * the {@link AgentActivity} starts. When set on an Agent, these override the + * MCP servers configured on the {@link AgentSession}. + */ + mcpServers?: MCPServer[]; stt?: STT | STTModelString; vad?: VAD; llm?: LLM | RealtimeModel | LLMModels; @@ -154,11 +161,15 @@ export class Agent { /** @internal */ _tools?: ToolContext; + /** @internal */ + _mcpServers?: MCPServer[]; + constructor({ id, instructions, chatCtx, tools, + mcpServers, turnDetection, stt, vad, @@ -185,6 +196,7 @@ export class Agent { this._instructions = instructions; this._tools = { ...tools }; + this._mcpServers = mcpServers; this._chatCtx = chatCtx ? chatCtx.copy({ toolCtx: this._tools, @@ -261,6 +273,10 @@ export class Agent { return { ...this._tools }; } + get mcpServers(): MCPServer[] | undefined { + return this._mcpServers; + } + get session(): AgentSession { return this.getActivityOrThrow().agentSession as AgentSession; } diff --git a/agents/src/voice/agent_activity.ts b/agents/src/voice/agent_activity.ts index 778463ea7..0df222fc0 100644 --- a/agents/src/voice/agent_activity.ts +++ b/agents/src/voice/agent_activity.ts @@ -28,6 +28,7 @@ import { type InputSpeechStoppedEvent, type InputTranscriptionCompleted, LLM, + type MCPServer, RealtimeModel, type RealtimeModelError, type RealtimeSession, @@ -121,6 +122,15 @@ export function isSchedulingPausedError(error: unknown): error is SchedulingPaus return error instanceof SchedulingPausedError; } +function sameMcpServerRefs(a: MCPServer[], b: MCPServer[]): boolean { + if (a === b) return true; + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; +} + export async function cleanupReusableResources( resources: ReusableResources, logger: Logger, @@ -258,6 +268,11 @@ export class AgentActivity implements RecognitionHooks { _onExitTask?: Task; _userTurnCompletedTask?: Task; + // Tools fetched from MCP servers configured on the active Agent (overrides + // session-level configuration). These are merged with the agent-defined + // tools whenever ``tools`` / ``toolCtx`` is read. + private _mcpTools: ToolContext = {}; + constructor(agent: Agent, agentSession: AgentSession) { this.agent = agent; this.agentSession = agentSession; @@ -402,6 +417,8 @@ export class AgentActivity implements RecognitionHooks { this.agent._agentActivity = this; + await this._initializeMcpServers(); + if (this.llm instanceof RealtimeModel) { const rtReused = reuseResources?.rtSession !== undefined; @@ -587,10 +604,16 @@ export class AgentActivity implements RecognitionHooks { (capabilities.midSessionInstructionsUpdate || this.agent.instructions === newActivity.agent.instructions); - // tools update is supported or tools are the same + // tools update is supported or tools are the same. Compare agent-level + // tools and MCP server references separately: newActivity._mcpTools is + // still empty here (it's populated later in _startSession), so reading + // newActivity.tools would always look different when MCP servers are + // configured. + const sameMcpServers = sameMcpServerRefs(this.mcpServers, newActivity.mcpServers); reusable = reusable && - (capabilities.midSessionToolsUpdate || isSameToolContext(this.tools, newActivity.tools)); + (capabilities.midSessionToolsUpdate || + (isSameToolContext(this.agent.toolCtx, newActivity.agent.toolCtx) && sameMcpServers)); if (reusable) { // detach: remove event listeners but don't close the session @@ -647,7 +670,7 @@ export class AgentActivity implements RecognitionHooks { } get tools(): ToolContext { - return this.agent.toolCtx; + return { ...this.agent.toolCtx, ...this._mcpTools }; } get schedulingPaused(): boolean { @@ -707,7 +730,15 @@ export class AgentActivity implements RecognitionHooks { // } get toolCtx(): ToolContext { - return this.agent.toolCtx; + return { ...this.agent.toolCtx, ...this._mcpTools }; + } + + /** + * MCP servers active for this activity. Agent-level configuration takes + * precedence over session-level configuration. + */ + get mcpServers(): MCPServer[] { + return this.agent.mcpServers ?? this.agentSession.mcpServers ?? []; } /** @internal */ @@ -741,7 +772,9 @@ export class AgentActivity implements RecognitionHooks { } async updateTools(tools: ToolContext): Promise { - const oldToolNames = new Set(Object.keys(this.tools)); + // diff is computed against the agent-level tools only; MCP-provided tools + // are managed separately and not affected by this call. + const oldToolNames = new Set(Object.keys(this.agent.toolCtx)); const newToolNames = new Set(Object.keys(tools)); const toolsAdded = [...newToolNames].filter((name) => !oldToolNames.has(name)); const toolsRemoved = [...oldToolNames].filter((name) => !newToolNames.has(name)); @@ -758,12 +791,13 @@ export class AgentActivity implements RecognitionHooks { } if (this.realtimeSession) { - await this.realtimeSession.updateTools(tools); + // include MCP tools so the realtime model sees the full tool set. + await this.realtimeSession.updateTools(this.tools); } if (this.llm instanceof LLM) { // for realtime LLM, we assume the server will remove unvalid tool messages - await this.updateChatCtx(this.agent._chatCtx.copy({ toolCtx: tools })); + await this.updateChatCtx(this.agent._chatCtx.copy({ toolCtx: this.tools })); } } @@ -1682,11 +1716,11 @@ export class AgentActivity implements RecognitionHooks { const tools = shouldFilterTools ? Object.fromEntries( - Object.entries(this.agent.toolCtx).filter( + Object.entries(this.tools).filter( ([, fnTool]) => !(fnTool.flags & ToolFlag.IGNORE_ON_ENTER), ), ) - : this.agent.toolCtx; + : this.tools; const task = this.createSpeechTask({ taskFn: (abortController: AbortController) => @@ -3317,12 +3351,52 @@ export class AgentActivity implements RecognitionHooks { this.interruptionDetector.off('error', this.onInterruptionError); } + // close agent-scoped MCP servers; session-scoped ones are kept open for + // the next activity and closed when the session shuts down. + const agentMcpServers = this.agent.mcpServers; + if (agentMcpServers && agentMcpServers.length > 0) { + await Promise.allSettled(agentMcpServers.map((server) => server.aclose())); + } + this._mcpTools = {}; + this.agent._agentActivity = undefined; } finally { unlock(); } } + private async _initializeMcpServers(): Promise { + const servers = this.mcpServers; + if (servers.length === 0) { + this._mcpTools = {}; + return; + } + + const results = await Promise.allSettled( + servers.map(async (server) => { + if (!server.initialized) { + await server.initialize(); + } + return server.listTools(); + }), + ); + + const merged: ToolContext = {}; + for (let i = 0; i < results.length; i++) { + const result = results[i]; + if (!result) continue; + if (result.status === 'fulfilled') { + Object.assign(merged, result.value); + } else { + this.logger.error( + { err: result.reason }, + 'failed to initialize MCP server, its tools will not be available', + ); + } + } + this._mcpTools = merged; + } + private resolveInterruptionDetector(): AdaptiveInterruptionDetector | undefined { const agentInterruptionDetection = this.agent.turnHandling?.interruption?.mode; const sessionInterruptionDetection = this.agentSession.interruptionDetection; diff --git a/agents/src/voice/agent_session.ts b/agents/src/voice/agent_session.ts index 46901b4c0..bb6e20412 100644 --- a/agents/src/voice/agent_session.ts +++ b/agents/src/voice/agent_session.ts @@ -23,7 +23,13 @@ import type { OverlappingSpeechEvent } from '../inference/interruption/types.js' import { getJobContext } from '../job.js'; import type { FunctionCall, FunctionCallOutput } from '../llm/chat_context.js'; import { AgentHandoffItem, ChatContext, ChatMessage } from '../llm/chat_context.js'; -import type { LLM, RealtimeModel, RealtimeModelError, ToolChoice } from '../llm/index.js'; +import type { + LLM, + MCPServer, + RealtimeModel, + RealtimeModelError, + ToolChoice, +} from '../llm/index.js'; import type { LLMError } from '../llm/llm.js'; import { log } from '../log.js'; import { type ModelUsage, ModelUsageCollector, filterZeroValues } from '../metrics/model_usage.js'; @@ -155,6 +161,13 @@ export type AgentSessionOptions = { tts?: TTS | TTSModelString; userData?: UserData; connOptions?: SessionConnectOptions; + /** + * List of MCP servers providing tools for the agent. Tools fetched from + * these servers are merged with the agent's tool context whenever an + * {@link AgentActivity} starts. When the active agent specifies its own + * ``mcpServers``, the agent's value takes precedence. + */ + mcpServers?: MCPServer[]; /** @deprecated use turnHandling.turnDetection instead */ turnDetection?: TurnDetectionMode; @@ -205,6 +218,8 @@ export class AgentSession< llm?: LLM | RealtimeModel; tts?: TTS; turnDetection?: TurnDetectionMode; + /** @internal */ + _mcpServers?: MCPServer[]; /** @deprecated use {@link sessionOptions } instead */ readonly options: VoiceOptions; @@ -296,7 +311,8 @@ export class AgentSession< const { agentSessionOptions: opts, legacyVoiceOptions } = migrateLegacyOptions(options); - const { vad, stt, llm, tts, userData, connOptions, ...resolvedSessionOptions } = opts; + const { vad, stt, llm, tts, userData, connOptions, mcpServers, ...resolvedSessionOptions } = + opts; // Merge user-provided connOptions with defaults this._connOptions = { sttConnOptions: { ...DEFAULT_API_CONNECT_OPTIONS, ...connOptions?.sttConnOptions }, @@ -330,6 +346,7 @@ export class AgentSession< this.turnDetection = resolvedSessionOptions.turnHandling.turnDetection; this._interruptionDetection = resolvedSessionOptions.turnHandling.interruption?.mode; this._userData = userData; + this._mcpServers = mcpServers; // configurable IO this._input = new AgentInput(this.onAudioInputChanged); @@ -395,6 +412,11 @@ export class AgentSession< return this.sessionOptions.useTtsAlignedTranscript; } + /** MCP servers configured at the session level. */ + get mcpServers(): MCPServer[] | undefined { + return this._mcpServers; + } + set userData(value: UserData) { this._userData = value; } @@ -1304,6 +1326,11 @@ export class AgentSession< await this.activity?.close(); this.activity = undefined; + // close session-scoped MCP servers + if (this._mcpServers && this._mcpServers.length > 0) { + await Promise.allSettled(this._mcpServers.map((server) => server.aclose())); + } + if (this.sessionSpan) { this.sessionSpan.end(); this.sessionSpan = undefined; diff --git a/examples/src/mcp_agent.ts b/examples/src/mcp_agent.ts new file mode 100644 index 000000000..0a0320927 --- /dev/null +++ b/examples/src/mcp_agent.ts @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2026 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import { + type JobContext, + type JobProcess, + ServerOptions, + cli, + defineAgent, + inference, + llm, + voice, +} from '@livekit/agents'; +import * as livekit from '@livekit/agents-plugin-livekit'; +import * as silero from '@livekit/agents-plugin-silero'; +import { fileURLToPath } from 'node:url'; + +export default defineAgent({ + prewarm: async (proc: JobProcess) => { + proc.userData.vad = await silero.VAD.load(); + }, + entry: async (ctx: JobContext) => { + const agent = new voice.Agent({ + instructions: + 'You can retrieve data via the MCP server. The interface is voice-based: ' + + 'accept spoken user queries and respond with synthesized speech.', + }); + + const session = new voice.AgentSession({ + vad: ctx.proc.userData.vad! as silero.VAD, + stt: new inference.STT({ model: 'deepgram/nova-3', language: 'multi' }), + llm: new inference.LLM({ model: 'openai/gpt-4.1-mini' }), + tts: new inference.TTS({ + model: 'cartesia/sonic-3', + voice: '9626c31c-bec5-4cca-baa8-f8ba9e84c8bc', + }), + turnHandling: { + turnDetection: new livekit.turnDetector.MultilingualModel(), + }, + mcpServers: [new llm.MCPServerHTTP({ url: 'http://localhost:8000/sse' })], + }); + + await session.start({ agent, room: ctx.room }); + + session.generateReply({ instructions: 'greet the user and introduce yourself' }); + }, +}); + +cli.runApp(new ServerOptions({ agent: fileURLToPath(import.meta.url) })); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6e63cef8..9c666f77e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,6 +124,9 @@ importers: '@livekit/typed-emitter': specifier: ^3.0.0 version: 3.0.0 + '@modelcontextprotocol/sdk': + specifier: ^1.25.0 + version: 1.29.0(zod@3.25.76) '@opentelemetry/api': specifier: ^1.9.0 version: 1.9.0 @@ -671,7 +674,7 @@ importers: dependencies: '@google/genai': specifier: ^1.44.0 - version: 1.50.1 + version: 1.50.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6)) '@livekit/mutex': specifier: ^1.1.1 version: 1.1.1 @@ -745,10 +748,10 @@ importers: version: 0.13.27 '@microsoft/api-extractor': specifier: ^7.35.0 - version: 7.43.7(@types/node@22.19.1) + version: 7.43.7(@types/node@25.6.0) tsup: specifier: ^8.3.5 - version: 8.4.0(@microsoft/api-extractor@7.43.7(@types/node@22.19.1))(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3) + version: 8.4.0(@microsoft/api-extractor@7.43.7(@types/node@25.6.0))(postcss@8.5.9)(tsx@4.21.0)(typescript@5.9.3) typescript: specifier: ^5.0.0 version: 5.9.3 @@ -1942,6 +1945,12 @@ packages: '@modelcontextprotocol/sdk': optional: true + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@huggingface/hub@2.4.1': resolution: {integrity: sha512-g/EJG091aIdP1whpSjhqBOL25/m60NKXhYGz3wqp7hLX57r4Fx7QVFfXRbtxI0ZMQjLQV3GYrPtldz38mvOr+w==} engines: {node: '>=18'} @@ -2251,6 +2260,16 @@ packages: '@mistralai/mistralai@2.2.1': resolution: {integrity: sha512-uKU8CZmL2RzYKmplsU01hii4p3pe4HqJefpWNRWXm1Tcm0Sm4xXfwSLIy4k7ZCPlbETCGcp69E7hZs+WOJ5itQ==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@msgpack/msgpack@3.1.3': resolution: {integrity: sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA==} engines: {node: '>= 18'} @@ -2880,6 +2899,10 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -2907,9 +2930,20 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -3042,6 +3076,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'} + boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. @@ -3079,6 +3117,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'} @@ -3091,6 +3133,10 @@ packages: resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} engines: {node: '>= 0.4'} + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} @@ -3182,6 +3228,26 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + 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'} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -3260,6 +3326,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} @@ -3316,12 +3386,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'} + end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} @@ -3385,6 +3462,9 @@ packages: engines: {node: '>=18'} hasBin: true + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -3575,6 +3655,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -3583,6 +3667,14 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource-parser@3.0.8: + resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} @@ -3591,6 +3683,16 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + express-rate-limit@8.5.1: + resolution: {integrity: sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==} + 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==} @@ -3623,6 +3725,9 @@ packages: fast-safe-stringify@2.1.1: resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fastest-levenshtein@1.0.16: resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} engines: {node: '>= 4.9.1'} @@ -3651,6 +3756,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'} @@ -3689,6 +3798,14 @@ 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'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -3857,9 +3974,17 @@ packages: help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} + hono@4.12.18: + resolution: {integrity: sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ==} + engines: {node: '>=16.9.0'} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -3913,6 +4038,14 @@ packages: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + is-array-buffer@3.0.4: resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} engines: {node: '>= 0.4'} @@ -3986,6 +4119,9 @@ packages: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} engines: {node: '>=8'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -4070,6 +4206,9 @@ packages: jose@5.2.4: resolution: {integrity: sha512-6ScbIk2WWCeXkmzF6bRPmEuaqy1m8SbsRFMa/FLrSCkGIhj8OLVG/IH+XHVmNMx/KUo8cVWEE6oKR4dJ+S0Rkg==} + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -4102,6 +4241,12 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + 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==} @@ -4254,6 +4399,14 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + 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'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -4269,10 +4422,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'} + mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} @@ -4326,6 +4487,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -4358,6 +4523,10 @@ packages: object-inspect@1.13.1: resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -4396,6 +4565,10 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} + 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==} @@ -4481,6 +4654,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -4508,6 +4685,9 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -4580,6 +4760,10 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pkg-types@1.1.1: resolution: {integrity: sha512-ko14TjmDuQJ14zsotODv7dBlwxKhUKQEhuhmbqo1uCi9BB0Z2alo/wAXg6q1dTR5TyuqYyWhjtfe/Tsh+X28jQ==} @@ -4655,6 +4839,10 @@ packages: resolution: {integrity: sha512-M71sTMB146U3u0di3yup8iM+zv8yPRNQVr1KK4tyBitl3qFvEGucq/rGDRShD2rsJhtN02RJaJ7j5X5hmy8SJg==} engines: {node: '>=12.0.0'} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + pump@3.0.0: resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} @@ -4662,6 +4850,10 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + engines: {node: '>=0.6'} + quansync@0.2.11: resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} @@ -4675,6 +4867,14 @@ packages: resolution: {integrity: sha512-AAFUA5O1d83pIHEhJwWCq/RQcRukCkn/NSm2QsTEMle5f2hP0ChI2+3Xb051PZCkLryI/Ir1MVKviT2FIloaTQ==} engines: {node: '>=12'} + 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-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -4708,6 +4908,10 @@ packages: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-in-the-middle@7.5.2: resolution: {integrity: sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==} engines: {node: '>=8.6.0'} @@ -4756,6 +4960,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -4797,10 +5005,18 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + serialize-error@7.0.1: resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} engines: {node: '>=10'} + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -4809,6 +5025,9 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sharp@0.34.5: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -4827,10 +5046,26 @@ packages: shimmer@1.2.1: resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==} + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + side-channel@1.0.6: resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} engines: {node: '>= 0.4'} + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} @@ -4881,6 +5116,10 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -5036,6 +5275,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + 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==} @@ -5142,6 +5385,10 @@ packages: resolution: {integrity: sha512-+dbmiyliDY/2TTcjCS7NpI9yV2iEFlUDk5TKnsbkN7ZoRu5s7bT+zvYtNFhFXC2oLwURGT2frACAZvbbyNBI+w==} engines: {node: '>=16'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.2: resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} engines: {node: '>= 0.4'} @@ -5191,6 +5438,10 @@ packages: resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} engines: {node: '>= 4.0.0'} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -5198,6 +5449,10 @@ packages: resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} engines: {node: '>= 0.10'} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vite-node@1.6.0: resolution: {integrity: sha512-de6HJgzC+TFzOu0NTC4RAIsyf/DY/ibWDYQUcuEA84EMHhcefTUGkjFHKKEJhQN4A+6I0u++kr3l36ZF2d7XRw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -5550,7 +5805,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.5 '@babel/parser': 7.28.6 '@babel/types': 7.28.6 - debug: 4.4.1 + debug: 4.4.3 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5960,7 +6215,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.3 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -6008,17 +6263,23 @@ snapshots: '@ffmpeg-installer/win32-x64@4.1.0': optional: true - '@google/genai@1.50.1': + '@google/genai@1.50.1(@modelcontextprotocol/sdk@1.29.0(zod@4.3.6))': dependencies: google-auth-library: 10.6.2 p-retry: 4.6.2 protobufjs: 7.5.6 ws: 8.20.0 + optionalDependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@4.3.6) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate + '@hono/node-server@1.19.14(hono@4.12.18)': + dependencies: + hono: 4.12.18 + '@huggingface/hub@2.4.1': dependencies: '@huggingface/tasks': 0.19.36 @@ -6040,7 +6301,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.1 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -6349,6 +6610,51 @@ snapshots: - bufferutil - utf-8-validate + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.18) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.8 + express: 5.2.1 + express-rate-limit: 8.5.1(express@5.2.1) + hono: 4.12.18 + jose: 6.2.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + + '@modelcontextprotocol/sdk@1.29.0(zod@4.3.6)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.18) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.8 + express: 5.2.1 + express-rate-limit: 8.5.1(express@5.2.1) + hono: 4.12.18 + jose: 6.2.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 4.3.6 + zod-to-json-schema: 3.25.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color + optional: true + '@msgpack/msgpack@3.1.3': {} '@next/eslint-plugin-next@14.2.3': @@ -6899,7 +7205,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.4.1 + debug: 4.4.3 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -7066,6 +7372,11 @@ snapshots: dependencies: event-target-shim: 5.0.1 + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + acorn-import-attributes@1.9.5(acorn@8.15.0): dependencies: acorn: 8.15.0 @@ -7082,6 +7393,10 @@ snapshots: agent-base@7.1.4: {} + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -7089,6 +7404,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-colors@4.1.3: {} ansi-regex@5.0.1: {} @@ -7235,6 +7557,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.2 + on-finished: 2.4.1 + qs: 6.15.1 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + boolean@3.2.0: {} brace-expansion@1.1.11: @@ -7272,6 +7608,8 @@ snapshots: esbuild: 0.25.2 load-tsconfig: 0.2.5 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -7287,6 +7625,11 @@ snapshots: get-intrinsic: 1.3.0 set-function-length: 1.2.2 + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + callsites@3.1.0: {} camelcase-keys@9.1.3: @@ -7374,6 +7717,19 @@ snapshots: consola@3.4.2: {} + content-disposition@1.1.0: {} + + 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 + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -7440,6 +7796,8 @@ snapshots: delayed-stream@1.0.0: {} + depd@2.0.0: {} + dequal@2.0.3: {} destr@2.0.5: {} @@ -7482,10 +7840,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: {} + end-of-stream@1.4.4: dependencies: once: 1.4.0 @@ -7678,6 +8040,8 @@ snapshots: '@esbuild/win32-ia32': 0.27.7 '@esbuild/win32-x64': 0.27.7 + escape-html@1.0.3: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} @@ -7731,7 +8095,7 @@ snapshots: eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: - debug: 4.4.1 + debug: 4.4.3 enhanced-resolve: 5.16.1 eslint: 8.57.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) @@ -7953,10 +8317,18 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + event-target-shim@5.0.1: {} events@3.3.0: {} + eventsource-parser@3.0.8: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.8 + execa@8.0.1: dependencies: cross-spawn: 7.0.6 @@ -7971,6 +8343,44 @@ snapshots: expect-type@1.3.0: {} + express-rate-limit@8.5.1(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.2.0 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + 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.15.1 + 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: {} @@ -7997,6 +8407,8 @@ snapshots: fast-safe-stringify@2.1.1: {} + fast-uri@3.1.2: {} + fastest-levenshtein@1.0.16: {} fastq@1.17.1: @@ -8020,6 +8432,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 @@ -8066,6 +8489,10 @@ snapshots: dependencies: fetch-blob: 3.2.0 + forwarded@0.2.0: {} + + fresh@2.0.0: {} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -8262,8 +8689,18 @@ snapshots: help-me@5.0.0: {} + hono@4.12.18: {} + html-escaper@2.0.2: {} + 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 + https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -8314,6 +8751,10 @@ snapshots: hasown: 2.0.2 side-channel: 1.0.6 + ip-address@10.2.0: {} + + ipaddr.js@1.9.1: {} + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 @@ -8378,6 +8819,8 @@ snapshots: is-path-inside@3.0.3: {} + is-promise@4.0.0: {} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -8463,6 +8906,8 @@ snapshots: jose@5.2.4: {} + jose@6.2.3: {} + joycon@3.1.1: {} js-tokens@4.0.0: {} @@ -8488,6 +8933,10 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -8625,6 +9074,10 @@ snapshots: math-intrinsics@1.1.0: {} + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -8636,10 +9089,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 + mimic-fn@4.0.0: {} minimatch@10.2.4: @@ -8689,6 +9148,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + node-domexception@1.0.0: {} node-fetch-native@1.6.7: {} @@ -8711,6 +9172,8 @@ snapshots: object-inspect@1.13.1: {} + object-inspect@1.13.4: {} + object-keys@1.1.1: {} object.assign@4.1.5: @@ -8761,6 +9224,10 @@ snapshots: on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -8852,6 +9319,8 @@ snapshots: dependencies: callsites: 3.1.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -8872,6 +9341,8 @@ snapshots: lru-cache: 11.2.7 minipass: 7.1.3 + path-to-regexp@8.4.2: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -8977,6 +9448,8 @@ snapshots: pirates@4.0.6: {} + pkce-challenge@5.0.1: {} + pkg-types@1.1.1: dependencies: confbox: 0.1.7 @@ -9058,6 +9531,11 @@ snapshots: '@types/node': 22.19.1 long: 5.3.2 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + pump@3.0.0: dependencies: end-of-stream: 1.4.4 @@ -9065,6 +9543,10 @@ snapshots: punycode@2.3.1: {} + qs@6.15.1: + dependencies: + side-channel: 1.1.0 + quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -9073,6 +9555,15 @@ snapshots: quick-lru@6.1.2: {} + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + react-is@16.13.1: {} react-is@18.3.1: {} @@ -9115,6 +9606,8 @@ snapshots: es-errors: 1.3.0 set-function-name: 2.0.2 + require-from-string@2.0.2: {} + require-in-the-middle@7.5.2: dependencies: debug: 4.4.1 @@ -9194,6 +9687,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.1 fsevents: 2.3.3 + 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.4.2 + transitivePeerDependencies: + - supports-color + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -9229,10 +9732,35 @@ snapshots: semver@7.7.3: {} + 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 + serialize-error@7.0.1: dependencies: type-fest: 0.13.1 + 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 + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -9249,6 +9777,8 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + setprototypeof@1.2.0: {} + sharp@0.34.5: dependencies: '@img/colour': 1.0.0 @@ -9295,6 +9825,26 @@ snapshots: shimmer@1.2.1: {} + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + side-channel@1.0.6: dependencies: call-bind: 1.0.7 @@ -9302,6 +9852,14 @@ snapshots: get-intrinsic: 1.3.0 object-inspect: 1.13.1 + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + siginfo@2.0.0: {} signal-exit@4.1.0: {} @@ -9339,6 +9897,8 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.2: {} + std-env@3.10.0: {} string-argv@0.3.2: {} @@ -9493,6 +10053,8 @@ snapshots: dependencies: is-number: 7.0.0 + toidentifier@1.0.1: {} + tr46@0.0.3: {} tr46@1.0.1: @@ -9620,6 +10182,12 @@ snapshots: type-fest@4.18.0: {} + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 @@ -9680,16 +10248,20 @@ snapshots: universalify@0.1.2: {} + unpipe@1.0.0: {} + uri-js@4.4.1: dependencies: punycode: 2.3.1 validator@13.12.0: {} + vary@1.1.2: {} + vite-node@1.6.0(@types/node@22.19.1): dependencies: cac: 6.7.14 - debug: 4.4.1 + debug: 4.4.3 pathe: 1.1.2 picocolors: 1.1.1 vite: 5.4.21(@types/node@22.19.1) @@ -9707,7 +10279,7 @@ snapshots: vite-node@3.2.2(@types/node@22.19.1)(tsx@4.21.0): dependencies: cac: 6.7.14 - debug: 4.4.1 + debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 vite: 7.3.2(@types/node@22.19.1)(tsx@4.21.0) @@ -10024,6 +10596,10 @@ snapshots: dependencies: zod: 3.25.76 + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod-to-json-schema@3.25.2(zod@4.3.6): dependencies: zod: 4.3.6