diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..4939f76 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +## [Unreleased] +### Enhanced +- Improved message transformation in CallTranscriptOutput to handle optional properties +- Enhanced type safety for message handling diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bdc8298 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile +FROM node:lts-alpine + +# Create app directory +WORKDIR /app + +# Install app dependencies +COPY package.json package-lock.json ./ +RUN npm install --ignore-scripts + +# Copy source code +COPY . . + +# Build the project +RUN npm run build + +# Expose port if needed (optional, not specified in source, so leaving as is) + +# Command to run the MCP server +CMD ["node", "dist/index.js"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4a728d2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Vapi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index f681f37..f6efb50 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,13 @@ # Vapi MCP Server +[![smithery badge](https://smithery.ai/badge/@VapiAI/vapi-mcp-server)](https://smithery.ai/server/@VapiAI/vapi-mcp-server) + The Vapi [Model Context Protocol](https://modelcontextprotocol.com/) server allows you to integrate with Vapi APIs through function calling. + + Vapi Server MCP server + + ## Claude Desktop Setup 1. Open `Claude Desktop` and press `CMD + ,` to go to `Settings`. @@ -12,6 +18,8 @@ The Vapi [Model Context Protocol](https://modelcontextprotocol.com/) server allo 6. Add the following to your `claude_desktop_config.json` file. See [here](https://modelcontextprotocol.io/quickstart/user) for more details. 7. Restart the Claude Desktop after editing the config file. +### Local Configuration + ```json { "mcpServers": { @@ -27,7 +35,27 @@ The Vapi [Model Context Protocol](https://modelcontextprotocol.com/) server allo } } } +``` + +### Remote Configuration +```json +{ + "mcpServers": { + "vapi-mcp": { + "command": "npx", + "args": [ + "mcp-remote", + "https://mcp.vapi.ai/mcp", + "--header", + "Authorization: Bearer ${VAPI_TOKEN}" + ], + "env": { + "VAPI_TOKEN": "" + } + } + } +} ``` ### Example Usage with Claude Desktop @@ -49,9 +77,55 @@ I'd like to speak with my ShopHelper assistant to talk about my recent order. Ca I need to schedule a call with Mary assistant for next Tuesday at 3:00 PM. My phone number is +1555123456. ``` -## Remote SSE Connection +**Example 3:** Make a call with dynamic variables + +```md +I want to call +1234567890 with my appointment reminder assistant. Use these details: +- Customer name: Sarah Johnson +- Appointment date: March 25th +- Appointment time: 2:30 PM +- Doctor name: Dr. Smith +``` + +## Using Variable Values in Assistant Prompts -To connect to Vapi's MCP server via Server-Sent Events (SSE) Transport: +The `create_call` action supports passing dynamic variables through `assistantOverrides.variableValues`. These variables can be used in your assistant's prompts using double curly braces: `{{variableName}}`. + +### Example Assistant Prompt with Variables + +``` +Hello {{customerName}}, this is a reminder about your appointment on {{appointmentDate}} at {{appointmentTime}} with {{doctorName}}. +``` + +### Default Variables + +The following variables are automatically available (no need to pass in variableValues): + +- `{{now}}` - Current date and time (UTC) +- `{{date}}` - Current date (UTC) +- `{{time}}` - Current time (UTC) +- `{{month}}` - Current month (UTC) +- `{{day}}` - Current day of month (UTC) +- `{{year}}` - Current year (UTC) +- `{{customer.number}}` - Customer's phone number + +For more details on default variables and advanced date/time formatting, see the [official Vapi documentation](https://docs.vapi.ai/assistants/dynamic-variables#default-variables). + +## Remote MCP + +To connect to Vapi's MCP server remotely: + +### Streamable HTTP (Recommended) + +The default and recommended way to connect is via Streamable HTTP Transport: + +- Connect to `https://mcp.vapi.ai/mcp` from any MCP client using Streamable HTTP Transport +- Include your Vapi API key as a bearer token in the request headers +- Example header: `Authorization: Bearer your_vapi_api_key_here` + +### SSE (Deprecated) + +Server-Sent Events (SSE) Transport is still supported but deprecated: - Connect to `https://mcp.vapi.ai/sse` from any MCP client using SSE Transport - Include your Vapi API key as a bearer token in the request headers @@ -67,6 +141,9 @@ npm install # Build the server npm run build + +# Use inspector to test the server +npm run inspector ``` Update your `claude_desktop_config.json` to use the local server. @@ -126,6 +203,7 @@ npm test - [VAPI Remote MCP Server](https://mcp.vapi.ai/) - [VAPI MCP Tool](https://docs.vapi.ai/tools/mcp) +- [VAPI MCP Server SDK](https://docs.vapi.ai/sdk/mcp-server) - [Model Context Protocol](https://modelcontextprotocol.com/) - [Claude Desktop](https://modelcontextprotocol.io/quickstart/user) @@ -137,17 +215,26 @@ The Vapi MCP Server provides the following tools for integration: - `list_assistants`: Lists all Vapi assistants - `create_assistant`: Creates a new Vapi assistant +- `update_assistant`: Updates an existing Vapi assistant - `get_assistant`: Gets a Vapi assistant by ID ### Call Tools - `list_calls`: Lists all Vapi calls -- `create_call`: Creates an outbound call +- `create_call`: Creates an outbound call with support for: + - Immediate or scheduled calls + - Dynamic variable values through `assistantOverrides` - `get_call`: Gets details of a specific call +- `get_call_transcript`: Gets the full transcript and conversation data of a specific call -> **Note:** The `create_call` action supports scheduling calls for immediate execution or for a future time. +> **Note:** The `create_call` action supports scheduling calls for immediate execution or for a future time. You can also pass dynamic variables using `assistantOverrides.variableValues` to personalize assistant messages. ### Phone Number Tools - `list_phone_numbers`: Lists all Vapi phone numbers - `get_phone_number`: Gets details of a specific phone number + +### Vapi Tools + +- `list_tools`: Lists all Vapi tools +- `get_tool`: Gets details of a specific tool diff --git a/examples/shttp-client.ts b/examples/shttp-client.ts new file mode 100644 index 0000000..db51f61 --- /dev/null +++ b/examples/shttp-client.ts @@ -0,0 +1,154 @@ +#!/usr/bin/env node + +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import dotenv from 'dotenv'; +import { parseToolResponse } from '../src/utils/response.js'; + +// Load environment variables from .env file +dotenv.config(); + +// Ensure API key is available +if (!process.env.VAPI_TOKEN) { + console.error('Error: VAPI_TOKEN environment variable is required'); + process.exit(1); +} + +async function main() { + try { + // Initialize MCP client + const mcpClient = new Client({ + name: 'vapi-client-example', + version: '1.0.0', + }); + + // Create Streamable HTTP transport for connection to remote Vapi MCP server + const serverUrl = 'https://mcp.vapi.ai/mcp'; + const headers = { + Authorization: `Bearer ${process.env.VAPI_TOKEN}`, + }; + + const transport = new StreamableHTTPClientTransport(new URL(serverUrl), { + requestInit: { headers }, + }); + + console.log('Connecting to Vapi MCP server via Streamable HTTP...'); + await mcpClient.connect(transport); + console.log('Connected successfully'); + + try { + // List available tools + const toolsResult = await mcpClient.listTools(); + console.log('Available tools:'); + toolsResult.tools.forEach((tool) => { + console.log(`- ${tool.name}: ${tool.description}`); + }); + + // List assistants + console.log('\nListing assistants...'); + const assistantsResponse = await mcpClient.callTool({ + name: 'list_assistants', + arguments: {}, + }); + + const assistants = parseToolResponse(assistantsResponse); + + if (!(Array.isArray(assistants) && assistants.length > 0)) { + console.log( + 'No assistants found. Please create an assistant in the Vapi dashboard first.' + ); + return; + } + + console.log('Your assistants:'); + assistants.forEach((assistant: any) => { + console.log(`- ${assistant.name} (${assistant.id})`); + }); + + // List phone numbers + console.log('\nListing phone numbers...'); + const phoneNumbersResponse = await mcpClient.callTool({ + name: 'list_phone_numbers', + arguments: {}, + }); + + const phoneNumbers = parseToolResponse(phoneNumbersResponse); + + if (!(Array.isArray(phoneNumbers) && phoneNumbers.length > 0)) { + console.log( + 'No phone numbers found. Please add a phone number in the Vapi dashboard first.' + ); + return; + } + + console.log('Your phone numbers:'); + phoneNumbers.forEach((phoneNumber: any) => { + console.log(`- ${phoneNumber.phoneNumber} (${phoneNumber.id})`); + }); + + // Create a call using the first assistant and first phone number + const phoneNumberId = phoneNumbers[0].id; + const assistantId = assistants[0].id; + + console.log( + `\nCreating a call using assistant (${assistantId}) and phone number (${phoneNumberId})...` + ); + const createCallResponse = await mcpClient.callTool({ + name: 'create_call', + arguments: { + assistantId: assistantId, + phoneNumberId: phoneNumberId, + customer: { + phoneNumber: '+1234567890', // Replace with actual customer phone number + }, + assistantOverrides: { + variableValues: { + name: 'John Doe', + age: '25', + }, + }, + // Optional: schedule a call for the future + // scheduledAt: "2025-04-15T15:30:00Z" + }, + }); + + const createdCall = parseToolResponse(createCallResponse); + console.log('Call created:', JSON.stringify(createdCall, null, 2)); + + // List calls + console.log('\nListing calls...'); + const callsResponse = await mcpClient.callTool({ + name: 'list_calls', + arguments: {}, + }); + + const calls = parseToolResponse(callsResponse); + + if (Array.isArray(calls) && calls.length > 0) { + console.log('Your calls:'); + calls.forEach((call: any) => { + const createdAt = call.createdAt + ? new Date(call.createdAt).toLocaleString() + : 'N/A'; + const customerPhone = call.customer?.phoneNumber || 'N/A'; + const endedReason = call.endedReason || 'N/A'; + + console.log( + `- ID: ${call.id} | Status: ${call.status} | Created: ${createdAt} | Customer: ${customerPhone} | Ended reason: ${endedReason}` + ); + }); + } else { + console.log('No calls found. Try creating a call first.'); + } + } finally { + console.log('\nDisconnecting from server...'); + await mcpClient.close(); + console.log('Disconnected'); + } + } catch (error) { + console.error('Error:', error); + process.exit(1); + } +} + +main(); diff --git a/package-lock.json b/package-lock.json index 332a329..8b8d379 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,14 @@ { "name": "@vapi-ai/mcp-server", - "version": "0.0.3", + "version": "0.0.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@vapi-ai/mcp-server", - "version": "0.0.3", + "version": "0.0.9", "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", + "@modelcontextprotocol/sdk": "^1.12.3", "@vapi-ai/server-sdk": "^0.5.2", "dotenv": "^16.4.7", "zod": "^3.24.2", @@ -18,7 +18,7 @@ "mcp-server": "dist/index.js" }, "devDependencies": { - "@modelcontextprotocol/inspector": "^0.8.2", + "@modelcontextprotocol/inspector": "^0.14.2", "@types/jest": "^29.5.14", "@types/node": "^22.14.0", "jest": "^29.7.0", @@ -998,9 +998,9 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", "dev": true, "license": "MIT", "dependencies": { @@ -1008,20 +1008,20 @@ } }, "node_modules/@floating-ui/dom": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", "dev": true, "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", + "@floating-ui/core": "^1.7.1", "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz", + "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==", "dev": true, "license": "MIT", "dependencies": { @@ -1412,35 +1412,55 @@ } }, "node_modules/@modelcontextprotocol/inspector": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector/-/inspector-0.8.2.tgz", - "integrity": "sha512-ewowTh84QVUrVnIVJLx5Jhh2yJrgWa/LrcVlNhtm8Y2Uk2bgW0y0catXyeyR6dqqyDadMXq1hbPRq0Lo1zPFnQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector/-/inspector-0.14.2.tgz", + "integrity": "sha512-TZWSzKSdpAImviRchrJuaRLubSZWGGPToT4ns/T3S6BQ+CuXqcH5cUGYp+SrqNwlUIJkzjucFRTDme+T37GJeg==", "dev": true, "license": "MIT", "workspaces": [ "client", - "server" + "server", + "cli" ], "dependencies": { - "@modelcontextprotocol/inspector-client": "^0.8.2", - "@modelcontextprotocol/inspector-server": "^0.8.2", + "@modelcontextprotocol/inspector-cli": "^0.14.2", + "@modelcontextprotocol/inspector-client": "^0.14.2", + "@modelcontextprotocol/inspector-server": "^0.14.2", + "@modelcontextprotocol/sdk": "^1.12.1", "concurrently": "^9.0.1", + "open": "^10.1.0", "shell-quote": "^1.8.2", "spawn-rx": "^5.1.2", - "ts-node": "^10.9.2" + "ts-node": "^10.9.2", + "zod": "^3.23.8" }, "bin": { - "mcp-inspector": "bin/cli.js" + "mcp-inspector": "cli/build/cli.js" + } + }, + "node_modules/@modelcontextprotocol/inspector-cli": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-cli/-/inspector-cli-0.14.2.tgz", + "integrity": "sha512-zOR5lNzrtWUYEAE1HpZFZKva5Y7fDspB8U4JK9LULXWzaVh1o9zvvKMRi+yVH+Uz5EtJ8+tG1Vi2yX74NUYnPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.12.1", + "commander": "^13.1.0", + "spawn-rx": "^5.1.2" + }, + "bin": { + "mcp-inspector-cli": "build/cli.js" } }, "node_modules/@modelcontextprotocol/inspector-client": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-client/-/inspector-client-0.8.2.tgz", - "integrity": "sha512-eqsrA4eOXBadVTU8qFr2IYnxyY97+DfMDhl9LYuEHMIuEWjRObNGI/H3JFwDFpCkV4GOgWSBjfdIQ5yzUy7LKA==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-client/-/inspector-client-0.14.2.tgz", + "integrity": "sha512-TPT4fvnrbK7zOuRoeEyIJiz+EZ8kkWTn8XxEiL5tBR0UaEVDVQe2sLhucm7fT1DzRvoWXl4JBrDmon4+tDTs9g==", "dev": true, "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", + "@modelcontextprotocol/sdk": "^1.12.1", "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-icons": "^1.3.0", @@ -1451,7 +1471,7 @@ "@radix-ui/react-tabs": "^1.1.1", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.8", - "@types/prismjs": "^1.26.5", + "ajv": "^6.12.6", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cmdk": "^1.0.4", @@ -1467,7 +1487,7 @@ "zod": "^3.23.8" }, "bin": { - "mcp-inspector-client": "bin/cli.js" + "mcp-inspector-client": "bin/start.js" } }, "node_modules/@modelcontextprotocol/inspector-client/node_modules/pkce-challenge": { @@ -1481,15 +1501,15 @@ } }, "node_modules/@modelcontextprotocol/inspector-server": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-server/-/inspector-server-0.8.2.tgz", - "integrity": "sha512-1gSgWAO20nT8YSdNi/3b4czELYWoc+Sur5+v2tX9ru1UIXQ10y3YMGDtmxm1hd3U5SXkuzeg6toXQyGTzcvvAQ==", + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/inspector-server/-/inspector-server-0.14.2.tgz", + "integrity": "sha512-KdGYnYO/VbYtKnhUyK8SzXcpOyOCXyWUfMmGzsWOjMS+EBoh8fWLLFpJFHsJNeLewcyZU8JvX3V02xNFzWsINQ==", "dev": true, "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", + "@modelcontextprotocol/sdk": "^1.12.1", "cors": "^2.8.5", - "express": "^4.21.0", + "express": "^5.1.0", "ws": "^8.18.0", "zod": "^3.23.8" }, @@ -1497,347 +1517,16 @@ "mcp-inspector-server": "build/index.js" } }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@modelcontextprotocol/inspector-server/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.9.0.tgz", - "integrity": "sha512-Jq2EUCQpe0iyO5FGpzVYDNFR6oR53AIrwph9yWl7uSc7IWUMsrmpmSaTGra5hQNunXpM+9oit85p924jWuHzUA==", + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.3.tgz", + "integrity": "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ==", "license": "MIT", "dependencies": { + "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", - "cross-spawn": "^7.0.3", + "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", @@ -1903,13 +1592,13 @@ "license": "MIT" }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.3.tgz", - "integrity": "sha512-2dvVU4jva0qkNZH6HHWuSz5FN5GeU5tymvCgutF8WaXz9WnD1NgUhy73cqzkjkN4Zkn8lfTPv5JIfrC221W+Nw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.3" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -1927,18 +1616,18 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.5.tgz", - "integrity": "sha512-B0gYIVxl77KYDR25AY9EGe/G//ef85RVBIxQvK+m5pxAC7XihAc/8leMHhDvjvhDu02SBSb6BuytlWr/G7F3+g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", + "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.0.3", - "@radix-ui/react-use-controllable-state": "1.1.1", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" }, @@ -1958,16 +1647,16 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.3.tgz", - "integrity": "sha512-mM2pxoQw5HJ49rkzwOs7Y6J4oYH22wS8BfK2/bBxROlI4xuR0c4jEenQP63LlTlDkO6Buj2Vt+QYAYcOgqtrXA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.0.3", - "@radix-ui/react-slot": "1.2.0" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2017,24 +1706,24 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.7.tgz", - "integrity": "sha512-EIdma8C0C/I6kL6sO02avaCRqi3fmWJpxH6mqbVScorW6nNktzKJT/le7VPho3o/7wCsyRg3z0+Q+Obr0Gy/VQ==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.6", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.5", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.0.3", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-use-controllable-state": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -2070,15 +1759,15 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.6.tgz", - "integrity": "sha512-7gpgMT2gyKym9Jz2ZhlRXSg2y6cNQIK8d/cqBZ0RBCaps8pFryCWXiUKI+uHGFrhMrbGUP7U6PWgiXzIxoyF3Q==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, @@ -2114,14 +1803,14 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.3.tgz", - "integrity": "sha512-4XaDlq0bPt7oJwR+0k0clCiCO/7lO7NKZTAaJBYxDNQT/vj4ig0/UvctrRscZaFREpRvUTkpKR96ov1e6jptQg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { @@ -2169,13 +1858,13 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.3.tgz", - "integrity": "sha512-zwSQ1NzSKG95yA0tvBMgv6XPHoqapJCcg9nsUBaQQ66iRBhZNhlpaQG2ERYYX4O4stkYFK5rxj5NsWfO9CS+Hg==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.3" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2193,25 +1882,25 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.7.tgz", - "integrity": "sha512-I38OYWDmJF2kbO74LX8UsFydSHWOJuQ7LxPnTefjxxvdvPLempvAnmsyX9UsBlywcbSGpRH7oMLfkUf+ij4nrw==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.6", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.3", - "@radix-ui/react-portal": "1.1.5", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.0.3", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-use-controllable-state": "1.1.1", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -2231,17 +1920,17 @@ } }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.3.tgz", - "integrity": "sha512-iNb9LYUMkne9zIahukgQmHlSBp9XWGeQQ7FvUGNk45ywzOb6kQa+Ca38OphXlWDiKvyneo9S+KSJsLfLt8812A==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", "dev": true, "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.3", + "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", @@ -2264,13 +1953,13 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.5.tgz", - "integrity": "sha512-ps/67ZqsFm+Mb6lSPJpfhRLrVL2i2fntgCmGMqqth4eaGUf+knAuuRtWVJrNjUhExgmdRqftSgzpf0DF0n6yXA==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { @@ -2289,9 +1978,9 @@ } }, "node_modules/@radix-ui/react-presence": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.3.tgz", - "integrity": "sha512-IrVLIhskYhH3nLvtcBLQFZr61tBG7wx7O3kEmdzcYwRGAEBmBicGGL7ATzNgruYJ3xBTbuzEEq9OXJM3PAX3tA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz", + "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==", "dev": true, "license": "MIT", "dependencies": { @@ -2314,13 +2003,13 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.3.tgz", - "integrity": "sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2338,21 +2027,21 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.3.tgz", - "integrity": "sha512-ufbpLUjZiOg4iYgb2hQrWXEPYX6jOLBbR27bDyAff5GYMRrCzcze8lukjuXVUQvJ6HZe8+oL+hhswDcjmcgVyg==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.3", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.1" + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2370,31 +2059,31 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.7.tgz", - "integrity": "sha512-exzGIRtc7S8EIM2KjFg+7lJZsH7O7tpaBaJbBNVDnOZNhtoQ2iV+iSNfi2Wth0m6h3trJkMVvzAehB3c6xj/3Q==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.3", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.6", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.3", - "@radix-ui/react-portal": "1.1.5", - "@radix-ui/react-primitive": "2.0.3", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.1.3", + "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -2414,9 +2103,9 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "dev": true, "license": "MIT", "dependencies": { @@ -2433,9 +2122,9 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.4.tgz", - "integrity": "sha512-fuHMHWSf5SRhXke+DbHXj2wVMo+ghVH30vhX3XVacdXqDl+J4XWafMIGOOER861QpBx1jxgwKXL2dQnfrsd8MQ==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", + "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", "dev": true, "license": "MIT", "dependencies": { @@ -2443,10 +2132,10 @@ "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.0.3", - "@radix-ui/react-roving-focus": "1.1.3", - "@radix-ui/react-use-controllable-state": "1.1.1" + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", @@ -2464,24 +2153,24 @@ } }, "node_modules/@radix-ui/react-toast": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.7.tgz", - "integrity": "sha512-0IWTbAUKvzdpOaWDMZisXZvScXzF0phaQjWspK8RUMEUxjLbli+886mB/kXTIC3F+t5vQ0n0vYn+dsX8s+WdfA==", + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.14.tgz", + "integrity": "sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.3", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.6", - "@radix-ui/react-portal": "1.1.5", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.0.3", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", - "@radix-ui/react-visually-hidden": "1.1.3" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2499,24 +2188,24 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.0.tgz", - "integrity": "sha512-b1Sdc75s7zN9B8ONQTGBSHL3XS8+IcjcOIY51fhM4R1Hx8s0YbgqgyNZiri4qcYMVZK8hfCZVBiyCm7N9rs0rw==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz", + "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==", "dev": true, "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.6", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.3", - "@radix-ui/react-portal": "1.1.5", - "@radix-ui/react-presence": "1.1.3", - "@radix-ui/react-primitive": "2.0.3", - "@radix-ui/react-slot": "1.2.0", - "@radix-ui/react-use-controllable-state": "1.1.1", - "@radix-ui/react-visually-hidden": "1.1.3" + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.4", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2550,13 +2239,33 @@ } }, "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.1.tgz", - "integrity": "sha512-YnEXIy8/ga01Y1PN0VfaNH//MhA91JlEGVBDxDzROqwrAtG5Yr2QGEPz8A/rJA3C7ZAHryOYGaUv8fLSW2H/mg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-use-callback-ref": "1.1.1" + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -2658,13 +2367,13 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.3.tgz", - "integrity": "sha512-oXSF3ZQRd5fvomd9hmUCb2EHSZbPp3ZSHAHJJU/DlF9XoFkJBBW8RHU/E8WEH+RbSfJd/QFA0sl8ClJXknBwHQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "dev": true, "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.3" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2846,13 +2555,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/@types/prismjs": { - "version": "1.26.5", - "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz", - "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -2943,6 +2645,22 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -3017,9 +2735,9 @@ } }, "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "dev": true, "license": "MIT", "dependencies": { @@ -3029,13 +2747,6 @@ "node": ">=10" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true, - "license": "MIT" - }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -3333,6 +3044,22 @@ "dev": true, "license": "MIT" }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -3567,6 +3294,16 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3760,6 +3497,49 @@ "node": ">=0.10.0" } }, + "node_modules/default-browser": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", + "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", + "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3778,17 +3558,6 @@ "node": ">= 0.8" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4269,6 +4038,12 @@ "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -4290,7 +4065,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, "license": "MIT" }, "node_modules/fastq": { @@ -4864,6 +4638,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4907,6 +4697,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -4933,6 +4742,22 @@ "node": ">=0.10.0" } }, + "node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -5748,6 +5573,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5941,16 +5772,6 @@ "node": ">= 8" } }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -5965,19 +5786,6 @@ "node": ">=8.6" } }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -6186,6 +5994,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/open": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", + "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -6468,6 +6295,15 @@ "once": "^1.3.1" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", @@ -6580,9 +6416,9 @@ "license": "MIT" }, "node_modules/react-remove-scroll": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", - "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "dev": true, "license": "MIT", "dependencies": { @@ -6791,6 +6627,19 @@ "node": ">= 18" } }, + "node_modules/run-applescript": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", + "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7349,9 +7198,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz", - "integrity": "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==", + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", + "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==", "dev": true, "license": "MIT", "peer": true @@ -7671,6 +7520,15 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -7722,16 +7580,6 @@ } } }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -7843,9 +7691,9 @@ } }, "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 97e054a..cc96a24 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@vapi-ai/mcp-server", "description": "Vapi MCP Server", - "version": "0.0.5", + "version": "0.0.9", "main": "dist/index.js", "types": "dist/index.d.ts", "type": "module", @@ -39,6 +39,7 @@ "dev": "tsx watch src/index.ts", "dev:stdio-example": "tsx examples/stdio-client.ts", "dev:sse-example": "tsx examples/sse-client.ts", + "dev:shttp-example": "tsx examples/shttp-client.ts", "inspector": "mcp-inspector", "test": "NODE_OPTIONS=--experimental-vm-modules jest", "test:unit": "NODE_OPTIONS=--experimental-vm-modules jest src/tests/mcp-server-mock.test.ts", @@ -50,14 +51,14 @@ "dist/**/*.d.ts.map" ], "dependencies": { - "@modelcontextprotocol/sdk": "^1.9.0", + "@modelcontextprotocol/sdk": "^1.12.3", "@vapi-ai/server-sdk": "^0.5.2", "dotenv": "^16.4.7", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5" }, "devDependencies": { - "@modelcontextprotocol/inspector": "^0.8.2", + "@modelcontextprotocol/inspector": "^0.14.2", "@types/jest": "^29.5.14", "@types/node": "^22.14.0", "jest": "^29.7.0", diff --git a/smithery.yaml b/smithery.yaml new file mode 100644 index 0000000..1aec5f6 --- /dev/null +++ b/smithery.yaml @@ -0,0 +1,24 @@ +# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml + +startCommand: + type: stdio + configSchema: + # JSON Schema defining the configuration options for the MCP. + type: object + required: + - VAPI_TOKEN + properties: + VAPI_TOKEN: + type: string + default: "" + description: Vapi API token used for making API calls + commandFunction: + # A JS function that produces the CLI command based on the given config to start the MCP on stdio. + |- + (config) => ({ + command: 'node', + args: ['dist/index.js'], + env: { VAPI_TOKEN: config.VAPI_TOKEN } + }) + exampleConfig: + VAPI_TOKEN: dummy_vapi_token_123 diff --git a/src/schemas/index.ts b/src/schemas/index.ts index e92d7e4..85db71e 100644 --- a/src/schemas/index.ts +++ b/src/schemas/index.ts @@ -141,6 +141,10 @@ export const CreateAssistantInputSchema = z.object({ ]) .default(DEFAULT_LLM) .describe('LLM configuration'), + toolIds: z + .array(z.string()) + .optional() + .describe('IDs of tools to use with this assistant'), transcriber: z .object({ provider: z.string().describe('Provider to use for transcription'), @@ -160,7 +164,7 @@ export const CreateAssistantInputSchema = z.object({ .string() .optional() .default('Hello, how can I help you today?') - .describe('First message to send'), + .describe('First message to say to the user'), firstMessageMode: z .enum([ 'assistant-speaks-first', @@ -169,7 +173,7 @@ export const CreateAssistantInputSchema = z.object({ ]) .default('assistant-speaks-first') .optional() - .describe('First message mode'), + .describe('This determines who speaks first, either assistant or user'), }); export const AssistantOutputSchema = BaseResponseSchema.extend({ @@ -187,12 +191,66 @@ export const AssistantOutputSchema = BaseResponseSchema.extend({ provider: z.string(), model: z.string(), }), + toolIds: z.array(z.string()).optional(), }); export const GetAssistantInputSchema = z.object({ assistantId: z.string().describe('ID of the assistant to get'), }); +export const UpdateAssistantInputSchema = z.object({ + assistantId: z.string().describe('ID of the assistant to update'), + name: z.string().optional().describe('New name for the assistant'), + instructions: z + .string() + .optional() + .describe('New instructions for the assistant'), + llm: z + .union([ + LLMSchema, + z.string().transform((str) => { + try { + return JSON.parse(str); + } catch (e) { + throw new Error(`Invalid LLM JSON string: ${str}`); + } + }), + ]) + .optional() + .describe('New LLM configuration'), + toolIds: z + .array(z.string()) + .optional() + .describe('New IDs of tools to use with this assistant'), + transcriber: z + .object({ + provider: z.string().describe('Provider to use for transcription'), + model: z.string().describe('Transcription model to use'), + }) + .optional() + .describe('New transcription configuration'), + voice: z + .object({ + provider: VoiceProviderSchema.describe('Provider to use for voice'), + voiceId: z.string().describe('Voice ID to use'), + model: z.string().optional().describe('Voice model to use'), + }) + .optional() + .describe('New voice configuration'), + firstMessage: z + .string() + .optional() + .describe('First message to say to the user'), + firstMessageMode: z + .enum([ + 'assistant-speaks-first', + 'assistant-waits-for-user', + 'assistant-speaks-first-with-model-generated-message', + ]) + .optional() + .describe('This determines who speaks first, either assistant or user'), +}); + // ===== Call Schemas ===== export const CallInputSchema = z.object({ @@ -206,14 +264,27 @@ export const CallInputSchema = z.object({ .describe('ID of the phone number to use for the call'), customer: z .object({ - phoneNumber: z.string().describe('Customer phone number'), + number: z.string().describe('Customer phone number'), }) .optional() .describe('Customer information'), scheduledAt: z .string() .optional() - .describe('ISO datetime string for when the call should be scheduled (e.g. "2025-03-25T22:39:27.771Z")'), + .describe( + 'ISO datetime string for when the call should be scheduled (e.g. "2025-03-25T22:39:27.771Z")' + ), + assistantOverrides: z + .object({ + variableValues: z + .record(z.string(), z.any()) + .optional() + .describe( + 'Key-value pairs for dynamic variables to use in the assistant\'s prompts (e.g. {"name": "Joe", "age": "24"})' + ), + }) + .optional() + .describe('Overrides for the assistant configuration'), }); export const CallOutputSchema = BaseResponseSchema.extend({ @@ -223,7 +294,7 @@ export const CallOutputSchema = BaseResponseSchema.extend({ phoneNumberId: z.string().optional(), customer: z .object({ - phoneNumber: z.string(), + number: z.string(), }) .optional(), scheduledAt: z.string().optional(), @@ -233,8 +304,22 @@ export const GetCallInputSchema = z.object({ callId: z.string().describe('ID of the call to get'), }); -// ===== Phone Number Schemas ===== +export const CallTranscriptOutputSchema = BaseResponseSchema.extend({ + id: z.string(), + transcript: z.string().optional(), + messages: z.array(z.object({ + role: z.string(), + message: z.string().optional(), + time: z.number().optional(), + duration: z.number().optional(), + })).optional(), + recordingUrl: z.string().optional(), + summary: z.string().optional(), +}); +export const GetCallTranscriptInputSchema = GetCallInputSchema; + +// ===== Phone Number Schemas ===== export const GetPhoneNumberInputSchema = z.object({ phoneNumberId: z.string().describe('ID of the phone number to get'), @@ -251,3 +336,98 @@ export const PhoneNumberOutputSchema = BaseResponseSchema.extend({ }) .optional(), }); + +// ===== Tool Schemas ===== + +export const GetToolInputSchema = z.object({ + toolId: z.string().describe('ID of the tool to get'), +}); + +const TransferCallDestinationSchema = z.object({ + type: z.literal('number'), + number: z.string().describe('Phone number to transfer to (e.g., "+16054440129"). It can be any phone number in E.164 format.'), + extension: z.string().optional().describe('Extension number if applicable'), + callerId: z.string().optional().describe('Caller ID to use for the transfer'), + description: z.string().optional().describe('Description of the transfer destination'), +}); + +// Generic custom tool schemas +const JsonSchemaProperty = z.object({ + type: z.string(), + description: z.string().optional(), + enum: z.array(z.string()).optional(), + items: z.any().optional(), + properties: z.record(z.any()).optional(), + required: z.array(z.string()).optional(), +}); + +const JsonSchema = z.object({ + type: z.literal('object'), + properties: z.record(JsonSchemaProperty), + required: z.array(z.string()).optional(), +}); + +const ServerSchema = z.object({ + url: z.string().url().describe('Server URL where the function will be called'), + headers: z.record(z.string()).optional().describe('Headers to send with the request'), +}); + +const BackoffPlanSchema = z.object({ + type: z.enum(['fixed', 'exponential']).default('fixed'), + maxRetries: z.number().default(3).describe('Maximum number of retries'), + baseDelaySeconds: z.number().default(1).describe('Base delay between retries in seconds'), +}); + +// Base tool configuration schema (reusable for both create and update) +const BaseToolConfigSchema = z.object({ + // Common fields for all tools + name: z.string().optional().describe('Name of the function/tool'), + description: z.string().optional().describe('Description of what the function/tool does'), + + // SMS tool configuration + sms: z.object({ + metadata: z.object({ + from: z.string().describe('Phone number to send SMS from (e.g., "+15551234567"). It must be a twilio number in E.164 format.'), + }).describe('SMS configuration metadata'), + }).optional().describe('SMS tool configuration - to send text messages'), + + // Transfer call tool configuration + transferCall: z.object({ + destinations: z.array(TransferCallDestinationSchema).describe('Array of possible transfer destinations'), + }).optional().describe('Transfer call tool configuration - to transfer calls to destinations'), + + // Function tool configuration (custom functions with parameters) + function: z.object({ + parameters: JsonSchema.describe('JSON schema for function parameters'), + server: ServerSchema.describe('Server configuration with URL where the function will be called'), + }).optional().describe('Custom function tool configuration - for custom server-side functions'), + + // API Request tool configuration + apiRequest: z.object({ + url: z.string().url().describe('URL to make the API request to'), + method: z.enum(['GET', 'POST']).default('POST').describe('HTTP method for the API request'), + headers: z.record(z.string()).optional().describe('Headers to send with the request (key-value pairs)'), + body: JsonSchema.optional().describe('Body schema for the API request in JSON Schema format'), + backoffPlan: BackoffPlanSchema.optional().describe('Retry configuration for failed API requests'), + timeoutSeconds: z.number().default(20).describe('Request timeout in seconds'), + }).optional().describe('API Request tool configuration - for HTTP API integration'), +}); + +export const CreateToolInputSchema = BaseToolConfigSchema.extend({ + type: z.enum(['sms', 'transferCall', 'function', 'apiRequest']) + .describe('Type of the tool to create'), +}); + +export const UpdateToolInputSchema = BaseToolConfigSchema.extend({ + toolId: z.string().describe('ID of the tool to update'), +}); + +export const ToolOutputSchema = BaseResponseSchema.extend({ + type: z + .string() + .describe('Type of the tool (dtmf, function, mcp, query, etc.)'), + name: z.string().describe('Name of the tool'), + description: z.string().describe('Description of the tool'), + parameters: z.record(z.any()).describe('Parameters of the tool'), + server: ServerSchema.describe('Server of the tool'), +}); diff --git a/src/tools/assistant.ts b/src/tools/assistant.ts index 0d22573..9341ef1 100644 --- a/src/tools/assistant.ts +++ b/src/tools/assistant.ts @@ -3,10 +3,12 @@ import { VapiClient, Vapi } from '@vapi-ai/server-sdk'; import { CreateAssistantInputSchema, GetAssistantInputSchema, + UpdateAssistantInputSchema, } from '../schemas/index.js'; import { transformAssistantInput, transformAssistantOutput, + transformUpdateAssistantInput, } from '../transformers/index.js'; import { createToolHandler } from './utils.js'; @@ -57,4 +59,34 @@ export const registerAssistantTools = ( } }) ); + + server.tool( + 'update_assistant', + 'Updates an existing Vapi assistant', + UpdateAssistantInputSchema.shape, + createToolHandler(async (data) => { + const assistantId = data.assistantId; + try { + // First check if the assistant exists + const existingAssistant = await vapiClient.assistants.get(assistantId); + if (!existingAssistant) { + throw new Error(`Assistant with ID ${assistantId} not found`); + } + + // Transform the update data + const updateAssistantDto = transformUpdateAssistantInput(data); + + // Update the assistant + const updatedAssistant = await vapiClient.assistants.update( + assistantId, + updateAssistantDto + ); + + return transformAssistantOutput(updatedAssistant); + } catch (error: any) { + console.error(`Error updating assistant: ${error.message}`); + throw error; + } + }) + ); }; diff --git a/src/tools/call.ts b/src/tools/call.ts index 408e347..1160d20 100644 --- a/src/tools/call.ts +++ b/src/tools/call.ts @@ -1,10 +1,11 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { VapiClient, Vapi } from '@vapi-ai/server-sdk'; -import { CallInputSchema, GetCallInputSchema } from '../schemas/index.js'; +import { CallInputSchema, GetCallInputSchema, GetCallTranscriptInputSchema } from '../schemas/index.js'; import { transformCallInput, transformCallOutput, + transformCallTranscriptOutput, } from '../transformers/index.js'; import { createToolHandler } from './utils.js'; @@ -42,4 +43,14 @@ export const registerCallTools = ( return transformCallOutput(call); }) ); + + server.tool( + 'get_call_transcript', + 'Gets the full transcript and conversation data of a specific call', + GetCallTranscriptInputSchema.shape, + createToolHandler(async (data) => { + const call = await vapiClient.calls.get(data.callId); + return transformCallTranscriptOutput(call); + }) + ); }; diff --git a/src/tools/index.ts b/src/tools/index.ts index 4bde82e..f19b2a5 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -4,9 +4,11 @@ import { VapiClient } from '@vapi-ai/server-sdk'; import { registerAssistantTools } from './assistant.js'; import { registerCallTools } from './call.js'; import { registerPhoneNumberTools } from './phone-number.js'; +import { registerToolTools } from './tool.js'; export const registerAllTools = (server: McpServer, vapiClient: VapiClient) => { registerAssistantTools(server, vapiClient); registerCallTools(server, vapiClient); registerPhoneNumberTools(server, vapiClient); + registerToolTools(server, vapiClient); }; diff --git a/src/tools/tool.ts b/src/tools/tool.ts new file mode 100644 index 0000000..d487e1e --- /dev/null +++ b/src/tools/tool.ts @@ -0,0 +1,53 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { VapiClient, Vapi } from '@vapi-ai/server-sdk'; + +import { GetToolInputSchema, CreateToolInputSchema, UpdateToolInputSchema } from '../schemas/index.js'; +import { transformToolInput, transformUpdateToolInput, transformToolOutput } from '../transformers/index.js'; +import { createToolHandler } from './utils.js'; + +export const registerToolTools = ( + server: McpServer, + vapiClient: VapiClient +) => { + server.tool( + 'list_tools', + 'Lists all Vapi tools', + {}, + createToolHandler(async () => { + const tools = await vapiClient.tools.list({ limit: 10 }); + return tools.map(transformToolOutput); + }) + ); + + server.tool( + 'get_tool', + 'Gets details of a specific tool', + GetToolInputSchema.shape, + createToolHandler(async (data) => { + const tool = await vapiClient.tools.get(data.toolId); + return transformToolOutput(tool); + }) + ); + + server.tool( + 'create_tool', + 'Creates a new Vapi tool', + CreateToolInputSchema.shape, + createToolHandler(async (data) => { + const createToolDto = transformToolInput(data); + const tool = await vapiClient.tools.create(createToolDto); + return transformToolOutput(tool); + }) + ); + + server.tool( + 'update_tool', + 'Updates an existing Vapi tool', + UpdateToolInputSchema.shape, + createToolHandler(async (data) => { + const updateToolDto = transformUpdateToolInput(data); + const tool = await vapiClient.tools.update(data.toolId, updateToolDto); + return transformToolOutput(tool); + }) + ); +}; diff --git a/src/transformers/index.ts b/src/transformers/index.ts index f143904..e89e9ad 100644 --- a/src/transformers/index.ts +++ b/src/transformers/index.ts @@ -5,7 +5,12 @@ import { CallInputSchema, AssistantOutputSchema, CallOutputSchema, + CallTranscriptOutputSchema, PhoneNumberOutputSchema, + ToolOutputSchema, + UpdateAssistantInputSchema, + CreateToolInputSchema, + UpdateToolInputSchema, } from '../schemas/index.js'; // ===== Assistant Transformers ===== @@ -22,6 +27,10 @@ export function transformAssistantInput( model: input.llm.model, }; + if (input.toolIds && input.toolIds.length > 0) { + assistantDto.model.toolIds = input.toolIds; + } + if (input.instructions) { assistantDto.model.messages = [ { @@ -53,6 +62,75 @@ export function transformAssistantInput( return assistantDto as Vapi.CreateAssistantDto; } +export function transformUpdateAssistantInput( + input: z.infer +): Vapi.UpdateAssistantDto { + const updateDto: any = {}; + + if (input.name) { + updateDto.name = input.name; + } + + if (input.llm) { + updateDto.model = { + provider: input.llm.provider as any, + model: input.llm.model, + }; + + if (input.toolIds && input.toolIds.length > 0) { + updateDto.model.toolIds = input.toolIds; + } + + if (input.instructions) { + updateDto.model.messages = [ + { + role: 'system', + content: input.instructions, + }, + ]; + } + } else { + if (input.toolIds && input.toolIds.length > 0) { + updateDto.model = { toolIds: input.toolIds }; + } + + if (input.instructions) { + if (!updateDto.model) updateDto.model = {}; + updateDto.model.messages = [ + { + role: 'system', + content: input.instructions, + }, + ]; + } + } + + if (input.transcriber) { + updateDto.transcriber = { + provider: input.transcriber.provider, + ...(input.transcriber.model ? { model: input.transcriber.model } : {}), + }; + } + + if (input.voice) { + updateDto.voice = { + provider: input.voice.provider as any, + voiceId: input.voice.voiceId, + ...(input.voice.model ? { model: input.voice.model } : {}), + }; + } + + if (input.firstMessage) { + updateDto.firstMessage = input.firstMessage; + } + + if (input.firstMessageMode) { + updateDto.firstMessageMode = input.firstMessageMode; + } + + return updateDto as Vapi.UpdateAssistantDto; +} + export function transformAssistantOutput( assistant: Vapi.Assistant ): z.infer { @@ -74,6 +152,7 @@ export function transformAssistantOutput( provider: assistant.transcriber?.provider || 'deepgram', model: getAssistantTranscriberModel(assistant.transcriber) || 'nova-3', }, + toolIds: assistant.model?.toolIds || [], }; } @@ -111,7 +190,7 @@ export function transformCallInput( ...(input.customer ? { customer: { - number: input.customer.phoneNumber, + number: input.customer.number, }, } : {}), @@ -122,6 +201,11 @@ export function transformCallInput( }, } : {}), + ...(input.assistantOverrides + ? { + assistantOverrides: input.assistantOverrides, + } + : {}), }; } @@ -138,13 +222,51 @@ export function transformCallOutput( phoneNumberId: call.phoneNumberId, customer: call.customer ? { - phoneNumber: call.customer.number || '', + number: call.customer.number || '', } : undefined, scheduledAt: call.schedulePlan?.earliestAt, }; } +export function transformCallTranscriptOutput( + call: Vapi.Call +): z.infer { + return { + id: call.id, + createdAt: call.createdAt, + updatedAt: call.updatedAt, + transcript: call.artifact?.transcript, + messages: call.artifact?.messages?.map(msg => { + // Handle different message types safely + const transformedMessage: any = { + role: msg.role, + time: msg.time || 0, + }; + + // Check if message has a 'message' property (UserMessage, BotMessage, SystemMessage, ToolCallMessage) + if ('message' in msg && typeof msg.message === 'string') { + transformedMessage.message = msg.message; + } else if ('result' in msg && typeof msg.result === 'string') { + // For ToolCallResultMessage, use result as message + transformedMessage.message = msg.result; + } else { + // Fallback for unknown message types - set to undefined since schema allows optional + transformedMessage.message = undefined; + } + + // Check if message has a 'duration' property (UserMessage, BotMessage) + if ('duration' in msg && typeof msg.duration === 'number') { + transformedMessage.duration = msg.duration; + } + + return transformedMessage; + }), + recordingUrl: call.artifact?.recordingUrl, + summary: call.analysis?.summary, + }; +} + // ===== Phone Number Transformers ===== export function transformPhoneNumberOutput( @@ -159,3 +281,142 @@ export function transformPhoneNumberOutput( status: phoneNumber.status, }; } + +// ===== Tool Transformers ===== + +export function transformToolInput( + input: z.infer +): any { + let toolDto: any = { + type: input.type, + }; + + // Add function definition if name and description are provided + if (input.name || input.description) { + toolDto.function = { + ...(input.name && { name: input.name }), + ...(input.description && { description: input.description }), + }; + } + + // Handle different tool types using the new nested structure + switch (input.type) { + case 'sms': + if (input.sms?.metadata) { + toolDto.metadata = input.sms.metadata; + } + break; + + case 'transferCall': + if (input.transferCall?.destinations) { + toolDto.destinations = input.transferCall.destinations; + } + break; + + case 'function': + if (input.function?.parameters && input.function?.server) { + // For function tools, add parameters to the existing function object + if (toolDto.function) { + toolDto.function.parameters = input.function.parameters; + } else { + toolDto.function = { + parameters: input.function.parameters, + }; + } + + toolDto.server = { + url: input.function.server.url, + ...(input.function.server.headers && { headers: input.function.server.headers }), + }; + } + break; + + case 'apiRequest': + if (input.apiRequest?.url) { + toolDto.url = input.apiRequest.url; + toolDto.method = input.apiRequest.method || 'POST'; + + if (input.apiRequest.headers) toolDto.headers = input.apiRequest.headers; + if (input.apiRequest.body) toolDto.body = input.apiRequest.body; + if (input.apiRequest.backoffPlan) toolDto.backoffPlan = input.apiRequest.backoffPlan; + if (input.apiRequest.timeoutSeconds) toolDto.timeoutSeconds = input.apiRequest.timeoutSeconds; + } + break; + + default: + throw new Error(`Unsupported tool type: ${(input as any).type}`); + } + + return toolDto; +} + +export function transformUpdateToolInput( + input: z.infer +): any { + let updateDto: any = {}; + + // Add function definition if name and description are provided + if (input.name || input.description) { + updateDto.function = { + ...(input.name && { name: input.name }), + ...(input.description && { description: input.description }), + }; + } + + // Handle SMS tool configuration + if (input.sms?.metadata) { + updateDto.metadata = input.sms.metadata; + } + + // Handle Transfer call tool configuration + if (input.transferCall?.destinations) { + updateDto.destinations = input.transferCall.destinations; + } + + // Handle Function tool configuration + if (input.function?.parameters && input.function?.server) { + // For function tools, add parameters to the existing function object + if (updateDto.function) { + updateDto.function.parameters = input.function.parameters; + } else { + updateDto.function = { + parameters: input.function.parameters, + }; + } + + updateDto.server = { + url: input.function.server.url, + ...(input.function.server.headers && { headers: input.function.server.headers }), + }; + } + + // Handle API Request tool configuration + if (input.apiRequest) { + if (input.apiRequest.url) updateDto.url = input.apiRequest.url; + if (input.apiRequest.method) updateDto.method = input.apiRequest.method; + if (input.apiRequest.headers) updateDto.headers = input.apiRequest.headers; + if (input.apiRequest.body) updateDto.body = input.apiRequest.body; + if (input.apiRequest.backoffPlan) updateDto.backoffPlan = input.apiRequest.backoffPlan; + if (input.apiRequest.timeoutSeconds) updateDto.timeoutSeconds = input.apiRequest.timeoutSeconds; + } + + return updateDto; +} + +export function transformToolOutput( + tool: Vapi.ToolsGetResponse +): z.infer { + return { + id: tool.id, + createdAt: tool.createdAt, + updatedAt: tool.updatedAt, + type: tool.type || '', + name: tool.function?.name || '', + description: tool.function?.description || '', + parameters: tool.function?.parameters || {}, + server: { + url: tool.server?.url || '', + headers: tool.server?.headers as Record || {}, + } + }; +}