From ec9dff898616ee3203844b93e2328e9fb9ddd6e3 Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 11:23:43 -0500 Subject: [PATCH 01/10] First commit for mcp-server template --- mcp-server/.env.example | 0 mcp-server/.owners | 4 + mcp-server/CHANGELOG.md | 8 + mcp-server/README.md | 64 ++ mcp-server/assets/index.html | 88 +++ mcp-server/functions/mcp.protected.js | 133 ++++ mcp-server/functions/req.private.js | 68 ++ mcp-server/functions/res.private.js | 60 ++ mcp-server/functions/server.private.js | 130 ++++ mcp-server/package.json | 14 + package-lock.json | 975 ++++++++----------------- package.json | 3 +- templates.json | 5 + 13 files changed, 877 insertions(+), 675 deletions(-) create mode 100644 mcp-server/.env.example create mode 100644 mcp-server/.owners create mode 100644 mcp-server/CHANGELOG.md create mode 100644 mcp-server/README.md create mode 100644 mcp-server/assets/index.html create mode 100644 mcp-server/functions/mcp.protected.js create mode 100644 mcp-server/functions/req.private.js create mode 100644 mcp-server/functions/res.private.js create mode 100644 mcp-server/functions/server.private.js create mode 100644 mcp-server/package.json diff --git a/mcp-server/.env.example b/mcp-server/.env.example new file mode 100644 index 000000000..e69de29bb diff --git a/mcp-server/.owners b/mcp-server/.owners new file mode 100644 index 000000000..e2c1252c4 --- /dev/null +++ b/mcp-server/.owners @@ -0,0 +1,4 @@ +dkundel +alisontanu +pthirumurthi +# Insert your Github username here diff --git a/mcp-server/CHANGELOG.md b/mcp-server/CHANGELOG.md new file mode 100644 index 000000000..3982d4615 --- /dev/null +++ b/mcp-server/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## [Unreleased] + +## [1.0.0] +### Added +- Initial release. + diff --git a/mcp-server/README.md b/mcp-server/README.md new file mode 100644 index 000000000..3b763b508 --- /dev/null +++ b/mcp-server/README.md @@ -0,0 +1,64 @@ +# mcp-server + +Generates a remote MCP server for Twilio API tools + +## Pre-requisites + +### Environment variables + +This project requires some environment variables to be set. A file named `.env` is used to store the values for those environment variables. To keep your tokens and secrets secure, make sure to not commit the `.env` file in git. When setting up the project with `twilio serverless:init ...` the Twilio CLI will create a `.gitignore` file that excludes `.env` from the version history. + +In your `.env` file, set the following values: + +| Variable | Description | Required | +| :------- | :---------- | :------- | + + +### Function Parameters + +`/blank` expects the following parameters: + +| Parameter | Description | Required | +| :-------- | :---------- | :------- | + + +`/hello-messaging` is protected and requires a valid Twilio signature as well as the following parameters: + +| Parameter | Description | Required | +| :-------- | :---------- | :------- | + + +## Create a new project with the template + +1. Install the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart#install-twilio-cli) +2. Install the [serverless toolkit](https://www.twilio.com/docs/labs/serverless-toolkit/getting-started) + +```shell +twilio plugins:install @twilio-labs/plugin-serverless +``` + +3. Initiate a new project + +``` +twilio serverless:init example --template=mcp-server && cd example +``` + +4. Start the server with the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart): + +``` +twilio serverless:start +``` + +5. Open the web page at https://localhost:3000/index.html and enter your phone number to test + +ℹ️ Check the developer console and terminal for any errors, make sure you've set your environment variables. + +## Deploying + +Deploy your functions and assets with either of the following commands. Note: you must run these commands from inside your project folder. [More details in the docs.](https://www.twilio.com/docs/labs/serverless-toolkit) + +With the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart): + +``` +twilio serverless:deploy +``` diff --git a/mcp-server/assets/index.html b/mcp-server/assets/index.html new file mode 100644 index 000000000..14f4606fe --- /dev/null +++ b/mcp-server/assets/index.html @@ -0,0 +1,88 @@ + + + + + + + Get started with your Twilio Functions! + + + + + + + +
+
+ + +
+
+
+
+

+ +
+

Welcome!

+

Your live application with Twilio is ready to use!

+
+

+
+

Get started with your application

+

+ Follow these steps to try out your new app: +

+

+ This app will return the + TwiML + required to respond "Hello World" to incoming SMS messages. +

+
    +
  1. Text any message to your Twilio phone number
  2. +
  3. You should receive a response saying "Hello World"
  4. +
+
+
+ +
+
+

Troubleshooting

+
    +
  • + Check the + + phone number configuration + + and make sure the Twilio phone number you want for your app has a SMS webhook + configured to point at the following URL +
    + + +
    +
  • +
+
+
+
+ + + diff --git a/mcp-server/functions/mcp.protected.js b/mcp-server/functions/mcp.protected.js new file mode 100644 index 000000000..a19fba22b --- /dev/null +++ b/mcp-server/functions/mcp.protected.js @@ -0,0 +1,133 @@ +/* eslint-disable callback-return */ + +const modules = Runtime.getFunctions(); +const createServer = require(modules.server.path); +const createReq = require(modules.req.path); +const createRes = require(modules.res.path); + +const TWILIO_TAG_MAP = { + Messaging: ['Api20100401Message', 'Api20100401IncomingPhoneNumber'], + Voice: ['Api20100401Call'], + VoiceAddOns: [ + 'Api20100401Recording', + 'Api20100401Transcription', + 'Api20100401Conference', + ], + Conversations: [ + 'ConversationsV1Conversation', + 'ConversationsV1Message', + 'ConversationsV1Participant', + 'ConversationsV1Service', + 'ConversationsV1User', + ], + Studio: [ + 'StudioV2Execution', + 'StudioV2ExecutionContext', + 'StudioV2ExecutionStep', + 'StudioV2Flow', + 'StudioV2FlowRevision', + ], + TaskRouter: [ + 'TaskrouterV1Activity', + 'TaskrouterV1Event', + 'TaskrouterV1Task', + 'TaskrouterV1TaskChannel', + 'TaskrouterV1TaskQueue', + 'TaskrouterV1TaskReservation', + 'TaskrouterV1Worker', + 'TaskrouterV1WorkerChannel', + 'TaskrouterV1WorkerReservation', + 'TaskrouterV1Workflow', + 'TaskrouterV1Workspace', + 'TaskrouterV1WorkspaceStatistics', + ], + Serverless: [ + 'ServerlessV1Asset', + 'ServerlessV1AssetVersion', + 'ServerlessV1Build', + 'ServerlessV1Deployment', + 'ServerlessV1Environment', + 'ServerlessV1Function', + 'ServerlessV1Service', + 'ServerlessV1Variable', + ], + Account: ['Api20100401Account'], + PhoneNumbers: ['Api20100401IncomingPhoneNumber', 'Api20100401Address'], + Applications: ['Api20100401Application'], + Auth: ['Api20100401Token'], + AddOns: ['Api20100401AddOnResult'], + Usage: ['Api20100401Usage'], +}; + +const validateContext = (context, callback) => { + if (!context.ACCOUNT_SID || !context.API_KEY || !context.API_SECRET) { + const response = new Twilio.Response(); + response.setStatusCode(400); + response.setBody({ + error: + 'required context variables ACCOUNT_SID or API_KEY or API_SECRET not found', + }); + + callback(null, response); + + return false; + } + + return true; +}; + +const getTags = (event) => { + if (!event.services) { + return ['Api20100401Message']; + } + + const services = + typeof event.services === 'string' ? [event.services] : event.services; + // MCP does not like additoinal keys + delete event.services; + + const tags = []; + services.forEach((service) => { + if (TWILIO_TAG_MAP[service]) { + tags.push(...TWILIO_TAG_MAP[service]); + } + }); + + if (tags.length === 0) { + return ['Api20100401Message']; + } + + return tags; +}; + +exports.handler = async function (context, event, callback) { + console.log('MCPServer called with method', event.method); + if (event.method === 'tools/call') { + console.log( + 'Calling tool', + event.params.name, + 'with arguments', + event.params.arguments + ); + } + + if (!validateContext(context, callback)) { + return; + } + + try { + const tags = getTags(event); + const { transport } = await createServer(context, event, tags); + const req = createReq(event); + const res = createRes(callback); + + await transport.handleRequest(req, res); + } catch (error) { + console.log(error); + console.error('Error handling request:', error); + const twilioResponse = new Twilio.Response(); + twilioResponse.setStatusCode(500); + twilioResponse.setBody({ error: error.message }); + callback(twilioResponse); + } +}; diff --git a/mcp-server/functions/req.private.js b/mcp-server/functions/req.private.js new file mode 100644 index 000000000..4a51a4c7f --- /dev/null +++ b/mcp-server/functions/req.private.js @@ -0,0 +1,68 @@ +const { Readable } = require('stream'); +const { EventEmitter } = require('events'); + +class BodyStream extends Readable { + constructor(data) { + super(); + this._data = data; + this._sent = false; + } + + _read() { + if (!this._sent) { + this.emit('data', Buffer.from(this._data)); + this.push(this._data); + this.push(null); + this._sent = true; + } + } +} + +/** + * Create a request object to use as mock express + * @param {*} event + * @param {*} body + * @returns + */ +module.exports = function createReq(event, body) { + if (event.method === 'initialize' && !body) { + event.params = { + protocolVersion: '2024-11-05', + capabilities: { sampling: {}, roots: { listChanged: true } }, + clientInfo: { name: 'internal-initialization', version: '0.1.0' }, + }; + } + + if (!body) { + body = { ...event }; + delete body.request; + } + const bodyStream = new BodyStream(JSON.stringify(body)); + + const req = Object.assign(new EventEmitter(), { + method: event.method || 'GET', + url: event.url || '/', + headers: { + ...event.request.headers, + accept: 'application/json, text/event-stream', + 'content-type': 'application/json', + }, + readable: true, + pipe: bodyStream.pipe.bind(bodyStream), + on: bodyStream.on.bind(bodyStream), + once: bodyStream.once.bind(bodyStream), + emit: bodyStream.emit.bind(bodyStream), + removeListener: bodyStream.removeListener.bind(bodyStream), + read: bodyStream.read.bind(bodyStream), + _readableState: bodyStream._readableState, + }); + + req.constructor = Readable; + bodyStream._read(); + + if (event.method) { + req.method = 'POST'; + } + + return req; +}; diff --git a/mcp-server/functions/res.private.js b/mcp-server/functions/res.private.js new file mode 100644 index 000000000..e2085790d --- /dev/null +++ b/mcp-server/functions/res.private.js @@ -0,0 +1,60 @@ +const { Writable } = require('stream'); + +/** + * Create a response object to use as mock express + * @param {*} callback + * @returns + */ +module.exports = function createRes(callback) { + let responseData = ''; + let statusCode = 200; + const responseHeaders = {}; + const stream = new Writable({ + write(chunk, encoding, cb) { + responseData += chunk.toString(); + cb(); + }, + final(cb) { + cb(); + }, + }); + + const res = { + statusCode: 200, + write: (chunk) => { + return stream.write(chunk); + }, + end: (chunk) => { + if (chunk) { + stream.write(chunk); + } + stream.end(); + + const response = new Twilio.Response(); + response.setStatusCode(statusCode); + response.setBody(responseData); + response.setHeaders(responseHeaders); + + callback(null, response); + }, + writeHead: (code, headers) => { + statusCode = code; + if (headers) { + Object.assign(responseHeaders, headers); + } + return res; + }, + setHeader: (name, value) => { + responseHeaders[name] = value; + return res; + }, + flushHeaders: () => { + return res; + }, + on: stream.on.bind(stream), + once: stream.once.bind(stream), + emit: stream.emit.bind(stream), + }; + + return res; +}; diff --git a/mcp-server/functions/server.private.js b/mcp-server/functions/server.private.js new file mode 100644 index 000000000..c221de50e --- /dev/null +++ b/mcp-server/functions/server.private.js @@ -0,0 +1,130 @@ +/* eslint-disable import/extensions, import/no-unresolved */ + +const { default: TwilioMcp } = require('@twilio-alpha/mcp/build/server'); +const { + StreamableHTTPServerTransport, +} = require('@modelcontextprotocol/sdk/server/streamableHttp.js'); +const { randomUUID } = require('crypto'); + +const modules = Runtime.getFunctions(); +const createReq = require(modules.req.path); +const createRes = require(modules.res.path); + +const TAG_TO_FILE_MAP = { + Api20100401Message: 'twilio_api_v2010', + Api20100401Call: 'twilio_api_v2010', + Api20100401Recording: 'twilio_api_v2010', + Api20100401Transcription: 'twilio_api_v2010', + Api20100401Conference: 'twilio_api_v2010', + + ConversationsV1Conversation: 'twilio_conversations_v1', + ConversationsV1Message: 'twilio_conversations_v1', + ConversationsV1Participant: 'twilio_conversations_v1', + ConversationsV1Service: 'twilio_conversations_v1', + ConversationsV1User: 'twilio_conversations_v1', + + StudioV2Execution: 'twilio_studio_v2', + StudioV2ExecutionContext: 'twilio_studio_v2', + StudioV2ExecutionStep: 'twilio_studio_v2', + StudioV2Flow: 'twilio_studio_v2', + StudioV2FlowRevision: 'twilio_studio_v2', + + TaskrouterV1Activity: 'twilio_taskrouter_v1', + TaskrouterV1Event: 'twilio_taskrouter_v1', + TaskrouterV1Task: 'twilio_taskrouter_v1', + TaskrouterV1TaskChannel: 'twilio_taskrouter_v1', + TaskrouterV1TaskQueue: 'twilio_taskrouter_v1', + TaskrouterV1TaskReservation: 'twilio_taskrouter_v1', + TaskrouterV1Worker: 'twilio_taskrouter_v1', + TaskrouterV1WorkerChannel: 'twilio_taskrouter_v1', + TaskrouterV1WorkerReservation: 'twilio_taskrouter_v1', + TaskrouterV1Workflow: 'twilio_taskrouter_v1', + TaskrouterV1Workspace: 'twilio_taskrouter_v1', + TaskrouterV1WorkspaceStatistics: 'twilio_taskrouter_v1', + + ServerlessV1Asset: 'twilio_serverless_v1', + ServerlessV1AssetVersion: 'twilio_serverless_v1', + ServerlessV1Build: 'twilio_serverless_v1', + ServerlessV1Deployment: 'twilio_serverless_v1', + ServerlessV1Environment: 'twilio_serverless_v1', + ServerlessV1Function: 'twilio_serverless_v1', + ServerlessV1Service: 'twilio_serverless_v1', + ServerlessV1Variable: 'twilio_serverless_v1', + + Api20100401Account: 'twilio_api_v2010', + Api20100401IncomingPhoneNumber: 'twilio_api_v2010', + Api20100401Address: 'twilio_api_v2010', + Api20100401Application: 'twilio_api_v2010', + Api20100401Token: 'twilio_api_v2010', + Api20100401AddOnResult: 'twilio_api_v2010', + Api20100401Usage: 'twilio_api_v2010', +}; + +/** + * Creates an MCP server + * @param {*} context + * @param {*} event + * @returns + */ +module.exports = async function createServer(context, event, tags) { + const services = [ + ...new Set(tags.map((tag) => TAG_TO_FILE_MAP[tag]).filter(Boolean)), + ]; + + const server = new TwilioMcp({ + server: { + name: 'Twilio MCP Server', + version: '1.0.0', + }, + filters: { + tags, + services, + }, + accountSid: context.ACCOUNT_SID, + credentials: { + apiKey: context.API_KEY, + apiSecret: context.API_SECRET, + }, + }); + + const sessionId = randomUUID(); + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => sessionId, + onsessioninitialized: (sessionId) => { + console.log('session initialized', sessionId); + }, + }); + await server.start(transport); + + // Because this is serverless, we need to re-initialize the server everytime + if (event.method !== 'initialize') { + const req = createReq(event, { + jsonrpc: '2.0', + id: 0, + method: 'initialize', + params: { + protocolVersion: '2024-11-05', + capabilities: { sampling: {}, roots: { listChanged: true } }, + clientInfo: { name: 'internal-initialization', version: '0.1.0' }, + }, + }); + req.method = 'POST'; + + const promise = new Promise(async (resolve) => { + delete req.headers['mcp-session-id']; + const res = createRes((err, data) => { + if (err) { + console.error('Error initializing session:', err); + } + // now set the mcp session id + event.request.headers['mcp-session-id'] = + data.headers['mcp-session-id']; + resolve(); + }); + await transport.handleRequest(req, res); + }); + await promise; + } + + return { server, transport }; +}; diff --git a/mcp-server/package.json b/mcp-server/package.json new file mode 100644 index 000000000..a80a4e732 --- /dev/null +++ b/mcp-server/package.json @@ -0,0 +1,14 @@ +{ + "name": "mcp-server", + "version": "1.0.0", + "private": true, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.11.2", + "@twilio-alpha/mcp": "^0.5.1", + "@twilio/runtime-handler": "2.0.1", + "crypto": "^1.0.1", + "dotenv": "^16.5.0", + "twilio": "^5.6", + "zod": "^3.24.4" + } +} diff --git a/package-lock.json b/package-lock.json index b68ce729e..9a552eecf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,9 +24,6 @@ "forward-call", "forward-message", "forward-message-mailgun", - "forward-message-multiple", - "forward-message-sendgrid", - "forward-message-sparkpost", "frontline-quickstart", "funlet-call-me", "funlet-echo", @@ -79,7 +76,8 @@ "reminder-message", "passkeys-backend", "block-spam-calls", - "ai-assistants-samples" + "ai-assistants-samples", + "openai-mcp-server" ], "devDependencies": { "@twilio-labs/runtime-helpers": "^0.1.2", @@ -94,7 +92,6 @@ "eslint-config-twilio": "^2.0.0", "eslint-plugin-prettier": "^5.1.3", "get-port": "^7.1.0", - "got": "^14.3.0", "husky": "^9.0.11", "inquirer": "^8.2.4", "jest-cli": "^29.7.0", @@ -1895,7 +1892,7 @@ "version": "1.0.0" }, "forward-message": { - "version": "1.0.0" + "version": "1.0.1" }, "forward-message-mailgun": { "version": "1.0.0", @@ -1904,62 +1901,23 @@ } }, "forward-message-multiple": { - "version": "1.0.0" + "version": "1.0.0", + "extraneous": true }, "forward-message-sendgrid": { "version": "1.0.0", + "extraneous": true, "dependencies": { "got": "^6.7.1" } }, - "forward-message-sendgrid/node_modules/got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", - "dependencies": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "forward-message-sparkpost": { "version": "1.0.0", + "extraneous": true, "dependencies": { "got": "^6.7.1" } }, - "forward-message-sparkpost/node_modules/got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", - "dependencies": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, "frontline-quickstart": { "version": "1.0.0", "dependencies": { @@ -4448,12 +4406,6 @@ } } }, - "node_modules/@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "dev": true - }, "node_modules/@sendgrid/client": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.6.0.tgz", @@ -6404,6 +6356,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/caller-id-forwarding": { "resolved": "caller-id-forwarding", "link": true @@ -6444,17 +6408,6 @@ } ] }, - "node_modules/capture-stack-trace": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", - "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==", - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -7039,17 +6992,6 @@ "resolved": "covid-vaccine-faq-bot", "link": true }, - "node_modules/create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", - "dependencies": { - "capture-stack-trace": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -7835,10 +7777,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/duplexify": { "version": "4.1.1", @@ -8042,12 +7992,9 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -8061,10 +8008,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { "es-errors": "^1.3.0" }, @@ -8073,14 +8019,14 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -9413,15 +9359,6 @@ "node": ">= 0.12" } }, - "node_modules/form-data-encoder": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", - "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==", - "dev": true, - "engines": { - "node": ">= 18" - } - }, "node_modules/formdata-node": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", @@ -9454,18 +9391,6 @@ "resolved": "forward-message-mailgun", "link": true }, - "node_modules/forward-message-multiple": { - "resolved": "forward-message-multiple", - "link": true - }, - "node_modules/forward-message-sendgrid": { - "resolved": "forward-message-sendgrid", - "link": true - }, - "node_modules/forward-message-sparkpost": { - "resolved": "forward-message-sparkpost", - "link": true - }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -9656,15 +9581,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9694,12 +9624,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, "engines": { - "node": ">=4" + "node": ">= 0.4" } }, "node_modules/get-symbol-description": { @@ -9994,203 +9928,14 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/got": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/got/-/got-14.3.0.tgz", - "integrity": "sha512-vZkrXdq5BtPWTXqvjXSpl6zky3zpHaOVfSug/RfFHu3YrtSsvYzopVMDqrh2do77WnGoCSSRCHW25zXOSAQ9zw==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^6.3.1", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^12.0.1", - "decompress-response": "^6.0.0", - "form-data-encoder": "^4.0.2", - "get-stream": "^8.0.1", - "http2-wrapper": "^2.2.1", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^4.0.1", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=20" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/@sindresorhus/is": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-6.3.1.tgz", - "integrity": "sha512-FX4MfcifwJyFOI2lPoX7PQxCqx8BG1HCho7WdiXwpEQx1Ycij0JxkfYtGK7yqNScrZGSlt6RE6sw8QYoH7eKnQ==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/got/node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/got/node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "dev": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/got/node_modules/cacheable-request": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-12.0.1.tgz", - "integrity": "sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==", - "dev": true, - "dependencies": { - "@types/http-cache-semantics": "^4.0.4", - "get-stream": "^9.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.4", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.1", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/got/node_modules/cacheable-request/node_modules/get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "dev": true, - "dependencies": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got/node_modules/http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/got/node_modules/is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "dev": true, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got/node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got/node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got/node_modules/normalize-url": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/got/node_modules/p-cancelable": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", - "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", - "dev": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/got/node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "dev": true, - "dependencies": { - "lowercase-keys": "^3.0.0" - }, + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "engines": { - "node": ">=14.16" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graceful-fs": { @@ -10304,6 +10049,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -10312,9 +10058,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -10326,7 +10072,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -11241,14 +10986,6 @@ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", "dev": true }, - "node_modules/is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -11265,14 +11002,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-shared-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", @@ -11292,6 +11021,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -14333,14 +14063,6 @@ "resolved": "lookup", "link": true }, - "node_modules/lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -14432,6 +14154,14 @@ "resolved": "masked-number", "link": true }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/maxstache": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/maxstache/-/maxstache-1.0.7.tgz", @@ -15630,14 +15360,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/prettier": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", @@ -17386,14 +17108,6 @@ "xtend": "~4.0.1" } }, - "node_modules/timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -17990,14 +17704,6 @@ "node": ">=8" } }, - "node_modules/unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha512-N0XH6lqDtFH84JxptQoZYmloF4nzrQqqrAymNj+/gW60AO2AZgOcf4O/nUXJcYfyQkqvMo9lSupBZmmgvuVXlw==", - "engines": { - "node": ">=4" - } - }, "node_modules/update-browserslist-db": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", @@ -18066,17 +17772,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", - "dependencies": { - "prepend-http": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", @@ -18682,8 +18377,91 @@ "passkeys-backend": { "version": "1.0.0", "dependencies": { - "@twilio-labs/runtime-helpers": "^0.1.2", - "twilio": "^3.61.0" + "axios": "^1.7.7", + "twilio": "^5.3.3" + } + }, + "passkeys-backend/node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "passkeys-backend/node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "passkeys-backend/node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "passkeys-backend/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "passkeys-backend/node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "passkeys-backend/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "passkeys-backend/node_modules/twilio": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/twilio/-/twilio-5.6.1.tgz", + "integrity": "sha512-rCwjDEmYl4mK4fNiTXf00YHOg2Xq5hejpwOBg/lhP8QSyDwHLLXj3igQ+cv9NAQcN+8RsJEY7rG4FysBUP8UKQ==", + "dependencies": { + "axios": "^1.8.3", + "dayjs": "^1.11.9", + "https-proxy-agent": "^5.0.0", + "jsonwebtoken": "^9.0.2", + "qs": "^6.9.4", + "scmp": "^2.1.0", + "xmlbuilder": "^13.0.2" + }, + "engines": { + "node": ">=14.0" } }, "patient-appointment-management": { @@ -20816,12 +20594,6 @@ "any-observable": "^0.3.0" } }, - "@sec-ant/readable-stream": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", - "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "dev": true - }, "@sendgrid/client": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.6.0.tgz", @@ -23006,6 +22778,15 @@ "set-function-length": "^1.2.1" } }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, "caller-id-forwarding": { "version": "file:caller-id-forwarding", "requires": { @@ -23028,11 +22809,6 @@ "integrity": "sha512-4zgNiB8nTyV/tHhwZrFs88ryjls/lHiqFhrxCW4qSTeuRByBVnPYpDInchOIySWknznucaf31Z4KYqjfbrecVw==", "dev": true }, - "capture-stack-trace": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.2.tgz", - "integrity": "sha512-X/WM2UQs6VMHUtjUDnZTRI+i1crWteJySFzr9UpGoQa4WQffXVTTXuekjl7TjZRlcF2XfjgITT0HxZ9RnxeT0w==" - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -23507,14 +23283,6 @@ "twilio": "^3.64.0" } }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha512-gYTKKexFO3kh200H1Nit76sRwRtOY32vQd3jpAQKpLtZqyNsSQNfI4N7o3eP2wUjV35pTWKRYqFUDBvUha/Pkw==", - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, "create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", @@ -24080,10 +23848,15 @@ "is-obj": "^2.0.0" } }, - "duplexer3": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", - "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==" + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } }, "duplexify": { "version": "4.1.1", @@ -24256,12 +24029,9 @@ } }, "es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "requires": { - "get-intrinsic": "^1.2.4" - } + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" }, "es-errors": { "version": "1.3.0", @@ -24269,23 +24039,22 @@ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" }, "es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "requires": { "es-errors": "^1.3.0" } }, "es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", - "dev": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "requires": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" } }, "es-shim-unscopables": { @@ -25261,12 +25030,6 @@ "mime-types": "^2.1.12" } }, - "form-data-encoder": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", - "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==", - "dev": true - }, "formdata-node": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", @@ -25295,61 +25058,6 @@ "mailgun.js": "^3.3.0" } }, - "forward-message-multiple": { - "version": "file:forward-message-multiple" - }, - "forward-message-sendgrid": { - "version": "file:forward-message-sendgrid", - "requires": { - "got": "^6.7.1" - }, - "dependencies": { - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - } - } - } - }, - "forward-message-sparkpost": { - "version": "file:forward-message-sparkpost", - "requires": { - "got": "^6.7.1" - }, - "dependencies": { - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha512-Y/K3EDuiQN9rTZhBvPRWMLXIKdeD1Rj0nzunfoi0Yyn5WBEbzxXKU9Ub2X41oZBagVWOBU3MuDonFMgPWQFnwg==", - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - } - } - } - }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -25488,15 +25196,20 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, "get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" } }, "get-package-type": { @@ -25511,10 +25224,14 @@ "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", "dev": true }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==" + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } }, "get-symbol-description": { "version": "1.0.2", @@ -25747,136 +25464,9 @@ } }, "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "got": { - "version": "14.3.0", - "resolved": "https://registry.npmjs.org/got/-/got-14.3.0.tgz", - "integrity": "sha512-vZkrXdq5BtPWTXqvjXSpl6zky3zpHaOVfSug/RfFHu3YrtSsvYzopVMDqrh2do77WnGoCSSRCHW25zXOSAQ9zw==", - "dev": true, - "requires": { - "@sindresorhus/is": "^6.3.1", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^12.0.1", - "decompress-response": "^6.0.0", - "form-data-encoder": "^4.0.2", - "get-stream": "^8.0.1", - "http2-wrapper": "^2.2.1", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^4.0.1", - "responselike": "^3.0.0" - }, - "dependencies": { - "@sindresorhus/is": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-6.3.1.tgz", - "integrity": "sha512-FX4MfcifwJyFOI2lPoX7PQxCqx8BG1HCho7WdiXwpEQx1Ycij0JxkfYtGK7yqNScrZGSlt6RE6sw8QYoH7eKnQ==", - "dev": true - }, - "@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.1" - } - }, - "cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "dev": true - }, - "cacheable-request": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-12.0.1.tgz", - "integrity": "sha512-Yo9wGIQUaAfIbk+qY0X4cDQgCosecfBe3V9NSyeY4qPC2SAkbCS4Xj79VP8WOzitpJUZKc/wsRCYF5ariDIwkg==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "^4.0.4", - "get-stream": "^9.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.4", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.1", - "responselike": "^3.0.0" - }, - "dependencies": { - "get-stream": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", - "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", - "dev": true, - "requires": { - "@sec-ant/readable-stream": "^0.4.1", - "is-stream": "^4.0.1" - } - } - } - }, - "get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true - }, - "http2-wrapper": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", - "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", - "dev": true, - "requires": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - } - }, - "is-stream": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", - "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", - "dev": true - }, - "lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "dev": true - }, - "mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "dev": true - }, - "normalize-url": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.1.tgz", - "integrity": "sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==", - "dev": true - }, - "p-cancelable": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", - "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", - "dev": true - }, - "responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "dev": true, - "requires": { - "lowercase-keys": "^3.0.0" - } - } - } + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" }, "graceful-fs": { "version": "4.2.10", @@ -25968,18 +25558,18 @@ "has-proto": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true }, "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" }, "has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "requires": { "has-symbols": "^1.0.3" } @@ -26722,11 +26312,6 @@ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", "dev": true }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==" - }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -26737,11 +26322,6 @@ "has-tostringtag": "^1.0.0" } }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" - }, "is-shared-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", @@ -26754,7 +26334,8 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==" + "integrity": "sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==", + "dev": true }, "is-stream-ended": { "version": "0.1.4", @@ -29082,11 +28663,6 @@ } } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" - }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -29160,6 +28736,11 @@ "masked-number": { "version": "file:masked-number" }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" + }, "maxstache": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/maxstache/-/maxstache-1.0.7.tgz", @@ -29939,8 +29520,77 @@ "passkeys-backend": { "version": "file:passkeys-backend", "requires": { - "@twilio-labs/runtime-helpers": "^0.1.2", - "twilio": "^3.61.0" + "axios": "^1.7.7", + "twilio": "^5.3.3" + }, + "dependencies": { + "axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + } + }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==" + }, + "twilio": { + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/twilio/-/twilio-5.6.1.tgz", + "integrity": "sha512-rCwjDEmYl4mK4fNiTXf00YHOg2Xq5hejpwOBg/lhP8QSyDwHLLXj3igQ+cv9NAQcN+8RsJEY7rG4FysBUP8UKQ==", + "requires": { + "axios": "^1.8.3", + "dayjs": "^1.11.9", + "https-proxy-agent": "^5.0.0", + "jsonwebtoken": "^9.0.2", + "qs": "^6.9.4", + "scmp": "^2.1.0", + "xmlbuilder": "^13.0.2" + } + } } }, "path-exists": { @@ -30054,11 +29704,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==" - }, "prettier": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.0.tgz", @@ -31399,11 +31044,6 @@ "xtend": "~4.0.1" } }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA==" - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -31837,11 +31477,6 @@ "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", "dev": true }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha512-N0XH6lqDtFH84JxptQoZYmloF4nzrQqqrAymNj+/gW60AO2AZgOcf4O/nUXJcYfyQkqvMo9lSupBZmmgvuVXlw==" - }, "update-browserslist-db": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", @@ -31896,14 +31531,6 @@ "requires-port": "^1.0.0" } }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha512-BVA4lR5PIviy2PMseNd2jbFQ+jwSwQGdJejf5ctd1rEXt0Ypd7yanUK9+lYechVlN5VaTJGsu2U/3MDDu6KgBA==", - "requires": { - "prepend-http": "^1.0.1" - } - }, "url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", diff --git a/package.json b/package.json index 2e06b6b8e..86ad84992 100644 --- a/package.json +++ b/package.json @@ -140,6 +140,7 @@ "reminder-message", "passkeys-backend", "block-spam-calls", - "ai-assistants-samples" + "ai-assistants-samples", + "mcp-server" ] } diff --git a/templates.json b/templates.json index 2c93e9f73..56829a79f 100644 --- a/templates.json +++ b/templates.json @@ -349,6 +349,11 @@ "id": "ai-assistants-samples", "name": "AI Assistants Samples", "description": "Functions to connect AI Assistants to various Channels and Tools" + }, + { + "id": "mcp-server", + "name": "MCP Server", + "description": "Generates an MCP server" } ] } From 0275d81ad075868228868c4f055b4035915be445 Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 11:35:57 -0500 Subject: [PATCH 02/10] Updating README and .env --- mcp-server/.env.example | 0 mcp-server/README.md | 60 +++++++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 20 deletions(-) delete mode 100644 mcp-server/.env.example diff --git a/mcp-server/.env.example b/mcp-server/.env.example deleted file mode 100644 index e69de29bb..000000000 diff --git a/mcp-server/README.md b/mcp-server/README.md index 3b763b508..8b3c75600 100644 --- a/mcp-server/README.md +++ b/mcp-server/README.md @@ -1,6 +1,6 @@ # mcp-server -Generates a remote MCP server for Twilio API tools +Functions to run a remote MCP server for Twilio API tools ## Pre-requisites @@ -10,23 +10,10 @@ This project requires some environment variables to be set. A file named `.env` In your `.env` file, set the following values: -| Variable | Description | Required | -| :------- | :---------- | :------- | - - -### Function Parameters - -`/blank` expects the following parameters: - -| Parameter | Description | Required | -| :-------- | :---------- | :------- | - - -`/hello-messaging` is protected and requires a valid Twilio signature as well as the following parameters: - -| Parameter | Description | Required | -| :-------- | :---------- | :------- | - +* ACCOUNT_SID +* AUTH_TOKEN +* API_KEY +* API_SECRET ## Create a new project with the template @@ -49,8 +36,6 @@ twilio serverless:init example --template=mcp-server && cd example twilio serverless:start ``` -5. Open the web page at https://localhost:3000/index.html and enter your phone number to test - ℹ️ Check the developer console and terminal for any errors, make sure you've set your environment variables. ## Deploying @@ -62,3 +47,38 @@ With the [Twilio CLI](https://www.twilio.com/docs/twilio-cli/quickstart): ``` twilio serverless:deploy ``` + +## Integration with MCP clients + +`https://.twil.io/mcp?services=` + +Header: x-twilio-signature + +@TODO: Code samples to generate x-twilio-signature + +Available services +* Message (default) +* Voice +* VoiceAddOns +* Conversations +* Studio +* TaskRouter +* Serverless +* Account +* PhoneNumbers +* Applications +* Auth +* AddOns +* Usage + +## Example prompts + +@TODO + +## Security recommendations + +This remote MCP server function will provide Tools to your LLM that provide access to your Twilio account. We recommend the following considerations when giving clients access to your server: + +- Always the requires_approval field to ensure that there are no unintended actions taken within your account. +- Use scoped permissions for your Twilio API Key. Not all endpoints support scoped permissions, but some do. See https://www.twilio.com/docs/iam/api-keys/restricted-api-keys for more information about which actions are supported per API Service. +- To ensure privacy, do not use other MCP servers in conjunction with your Twilio MCP server. From 5b903a113d116ec1077250ec0e2e9da9951f2ae9 Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 11:38:07 -0500 Subject: [PATCH 03/10] Updating GitHub owner usernames --- mcp-server/.owners | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp-server/.owners b/mcp-server/.owners index e2c1252c4..168084292 100644 --- a/mcp-server/.owners +++ b/mcp-server/.owners @@ -1,4 +1,4 @@ -dkundel -alisontanu -pthirumurthi +ktalebian +vingiarrusso +bpartridge # Insert your Github username here From b4bb106a3868b08484c79a55ca40d85d797efcf1 Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 11:41:29 -0500 Subject: [PATCH 04/10] Adding tests directory --- mcp-server/tests/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 mcp-server/tests/.gitkeep diff --git a/mcp-server/tests/.gitkeep b/mcp-server/tests/.gitkeep new file mode 100644 index 000000000..e69de29bb From 2ea6fa2737832e5017b6a845239c4a771e36689f Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 11:45:40 -0500 Subject: [PATCH 05/10] Updating template description --- templates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates.json b/templates.json index 56829a79f..1db87b08a 100644 --- a/templates.json +++ b/templates.json @@ -353,7 +353,7 @@ { "id": "mcp-server", "name": "MCP Server", - "description": "Generates an MCP server" + "description": "Functions to run a remote MCP server for Twilio API tools" } ] } From 370a328551706c079b311cd0a87b9e4eb597447b Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 11:46:49 -0500 Subject: [PATCH 06/10] Moving .env to .env.example --- mcp-server/.env.example | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 mcp-server/.env.example diff --git a/mcp-server/.env.example b/mcp-server/.env.example new file mode 100644 index 000000000..89b36c195 --- /dev/null +++ b/mcp-server/.env.example @@ -0,0 +1,19 @@ +# description: Your Twilio Account SID +# format: text +# required: true +ACCOUNT_SID=ACxxx + +# description: Your Twilio Auth Token +# format: text +# required: true +AUTH_TOKEN=abc + +# description: Your Twilio API Key +# format: text +# required: true +API_KEY=SKxxx + +# description: Your Twilio API Secret +# format: text +# required: true +API_SECRET=abc From 3a5ac6dbbdf89b159f3d181c8a270b29e195fc30 Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 11:52:24 -0500 Subject: [PATCH 07/10] Adding links for .env.example --- mcp-server/.env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mcp-server/.env.example b/mcp-server/.env.example index 89b36c195..e1b1f5717 100644 --- a/mcp-server/.env.example +++ b/mcp-server/.env.example @@ -1,19 +1,23 @@ # description: Your Twilio Account SID # format: text +# link: https://www.twilio.com/console # required: true ACCOUNT_SID=ACxxx # description: Your Twilio Auth Token # format: text +# link: https://www.twilio.com/console # required: true AUTH_TOKEN=abc # description: Your Twilio API Key # format: text +# link: https://www.twilio.com/console/project/api-keys # required: true API_KEY=SKxxx # description: Your Twilio API Secret # format: text +# link: https://www.twilio.com/console/project/api-keys # required: true API_SECRET=abc From 8f4ad013c42bd20247b7d0d89c5fe0ce8be52ddb Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 12:30:51 -0500 Subject: [PATCH 08/10] Updating README, was missing word --- mcp-server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp-server/README.md b/mcp-server/README.md index 8b3c75600..b647b9808 100644 --- a/mcp-server/README.md +++ b/mcp-server/README.md @@ -79,6 +79,6 @@ Available services This remote MCP server function will provide Tools to your LLM that provide access to your Twilio account. We recommend the following considerations when giving clients access to your server: -- Always the requires_approval field to ensure that there are no unintended actions taken within your account. +- Always set the `requires_approval` field to ensure that there are no unintended actions taken within your account. - Use scoped permissions for your Twilio API Key. Not all endpoints support scoped permissions, but some do. See https://www.twilio.com/docs/iam/api-keys/restricted-api-keys for more information about which actions are supported per API Service. - To ensure privacy, do not use other MCP servers in conjunction with your Twilio MCP server. From 40d7367e1bc4192664209384a35231197ef3305f Mon Sep 17 00:00:00 2001 From: Brian Partridge Date: Mon, 19 May 2025 12:39:26 -0500 Subject: [PATCH 09/10] Updating README and index.html --- mcp-server/README.md | 2 +- mcp-server/assets/index.html | 47 +++++++++++++++--------------------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/mcp-server/README.md b/mcp-server/README.md index b647b9808..aded05475 100644 --- a/mcp-server/README.md +++ b/mcp-server/README.md @@ -57,7 +57,7 @@ Header: x-twilio-signature @TODO: Code samples to generate x-twilio-signature Available services -* Message (default) +* Messaging (default) * Voice * VoiceAddOns * Conversations diff --git a/mcp-server/assets/index.html b/mcp-server/assets/index.html index 14f4606fe..5cc5138e0 100644 --- a/mcp-server/assets/index.html +++ b/mcp-server/assets/index.html @@ -39,7 +39,7 @@

Welcome!

-

Your live application with Twilio is ready to use!

+

Your remote MCP server!

@@ -47,38 +47,31 @@

Get started with your application

Follow these steps to try out your new app:

-

- This app will return the - TwiML - required to respond "Hello World" to incoming SMS messages. -

    -
  1. Text any message to your Twilio phone number
  2. -
  3. You should receive a response saying "Hello World"
  4. +
  5. Configure your MCP client application to use the /mcp endpoint
  6. +
  7. Make sure you're generating a signature to pass with your requests using the `x-twilio-signature` header.
  8. +
  9. Filter the MCP tools by passing a `?service` query parameter, using one or more of the services below.
+

Available services

+
    +
  • Messaging (default)
  • +
  • Voice
  • +
  • VoiceAddOns
  • +
  • Conversations
  • +
  • Studio
  • +
  • TaskRouter
  • +
  • Serverless
  • +
  • Account
  • +
  • PhoneNumbers
  • +
  • Applications
  • +
  • Auth
  • +
  • AddOns
  • +
  • Usage
  • +
-
-

Troubleshooting

-
    -
  • - Check the - - phone number configuration - - and make sure the Twilio phone number you want for your app has a SMS webhook - configured to point at the following URL -
    - - -
    -
  • -
-