├── .gitignore ├── Dockerfile ├── LICENCE ├── README.md ├── assets ├── Banner_NEW.png ├── claude-desktop-ref.png ├── cursor-reference.png └── demo_new.gif ├── package-lock.json ├── package.json ├── smithery.yaml ├── src └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | .env* 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22.12-alpine AS builder 2 | 3 | COPY . /app 4 | 5 | WORKDIR /app 6 | 7 | RUN --mount=type=cache,target=/root/.npm npm install 8 | 9 | FROM node:22-alpine AS release 10 | 11 | WORKDIR /app 12 | 13 | COPY --from=builder /app/build /app/build 14 | COPY --from=builder /app/package.json /app/package.json 15 | COPY --from=builder /app/package-lock.json /app/package-lock.json 16 | 17 | ENV NODE_ENV=production 18 | ENV TAVILY_API_KEY=your-api-key-here 19 | 20 | RUN npm ci --ignore-scripts --omit-dev 21 | 22 | ENTRYPOINT ["node", "build/index.js"] 23 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Alpha AI Technologies Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![Tavily Crawl Beta](./assets/Banner_NEW.png) 2 | 3 | 4 | ![GitHub Repo stars](https://img.shields.io/github/stars/tavily-ai/tavily-mcp?style=social) 5 | ![npm](https://img.shields.io/npm/dt/tavily-mcp) 6 | ![smithery badge](https://smithery.ai/badge/@tavily-ai/tavily-mcp) 7 | 8 | ## 🎉 **Introducing [tavily-crawl](https://docs.tavily.com/documentation/api-reference/endpoint/crawl) + [tavily-map](https://docs.tavily.com/documentation/api-reference/endpoint/map) in v0.2.1!** 🎉 9 | 10 | 11 | ![MCP demo](./assets/demo_new.gif) 12 | 13 | The Model Context Protocol (MCP) is an open standard that enables AI systems to interact seamlessly with various data sources and tools, facilitating secure, two-way connections. 14 | 15 | Developed by Anthropic, the Model Context Protocol (MCP) enables AI assistants like Claude to seamlessly integrate with Tavily's advanced search and data extraction capabilities. This integration provides AI models with real-time access to web information, complete with sophisticated filtering options and domain-specific search features. 16 | 17 | The Tavily MCP server provides: 18 | - search, extract, map, crawl tools 19 | - Real-time web search capabilities through the tavily-search tool 20 | - Intelligent data extraction from web pages via the tavily-extract tool 21 | - Powerful web mapping tool that creates a structured map of website 22 | - Web crawler that systematically explores websites 23 | 24 | 25 | ### 📚 Helpful Resources 26 | - [Tutorial](https://medium.com/@dustin_36183/building-a-knowledge-graph-assistant-combining-tavily-and-neo4j-mcp-servers-with-claude-db92de075df9) on combining Tavily MCP with Neo4j MCP server 27 | - [Tutorial](https://medium.com/@dustin_36183/connect-your-coding-assistant-to-the-web-integrating-tavily-mcp-with-cline-in-vs-code-5f923a4983d1) on integrating Tavily MCP with Cline in VS Code 28 | 29 | ## Prerequisites 🔧 30 | 31 | Before you begin, ensure you have: 32 | 33 | - [Tavily API key](https://app.tavily.com/home) 34 | - If you don't have a Tavily API key, you can sign up for a free account [here](https://app.tavily.com/home) 35 | - [Claude Desktop](https://claude.ai/download) or [Cursor](https://cursor.sh) 36 | - [Node.js](https://nodejs.org/) (v20 or higher) 37 | - You can verify your Node.js installation by running: 38 | - `node --version` 39 | - [Git](https://git-scm.com/downloads) installed (only needed if using Git installation method) 40 | - On macOS: `brew install git` 41 | - On Linux: 42 | - Debian/Ubuntu: `sudo apt install git` 43 | - RedHat/CentOS: `sudo yum install git` 44 | - On Windows: Download [Git for Windows](https://git-scm.com/download/win) 45 | 46 | ## Tavily MCP server installation ⚡ 47 | 48 | ### Running with NPX 49 | 50 | ```bash 51 | npx -y tavily-mcp@0.2.1 52 | ``` 53 | 54 | ### Installing via Smithery 55 | 56 | To install Tavily MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@tavily-ai/tavily-mcp): 57 | 58 | ```bash 59 | npx -y @smithery/cli install @tavily-ai/tavily-mcp --client claude 60 | ``` 61 | 62 | Although you can launch a server on its own, it's not particularly helpful in isolation. Instead, you should integrate it into an MCP client. Below is an example of how to configure the Claude Desktop app to work with the tavily-mcp server. 63 | 64 | 65 | ## Configuring MCP Clients ⚙️ 66 | 67 | This repository will explain how to configure [VS Code](https://code.visualstudio.com), [Cursor](https://cursor.sh) and [Claude Desktop](https://claude.ai/desktop) to work with the tavily-mcp server. 68 | 69 | ### Configuring VS Code 💻 70 | 71 | For one-click installation, click one of the install buttons below: 72 | 73 | [![Install with NPX in VS Code](https://img.shields.io/badge/VS_Code-NPM-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=tavily&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22tavily-mcp%400.1.4%22%5D%2C%22env%22%3A%7B%22TAVILY_API_KEY%22%3A%22%24%7Binput%3Atavily_api_key%7D%22%7D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22tavily_api_key%22%2C%22description%22%3A%22Tavily+API+Key%22%2C%22password%22%3Atrue%7D%5D) [![Install with NPX in VS Code Insiders](https://img.shields.io/badge/VS_Code_Insiders-NPM-24bfa5?style=flat-square&logo=visualstudiocode&logoColor=white)](https://insiders.vscode.dev/redirect/mcp/install?name=tavily&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22tavily-mcp%400.1.4%22%5D%2C%22env%22%3A%7B%22TAVILY_API_KEY%22%3A%22%24%7Binput%3Atavily_api_key%7D%22%7D%7D&inputs=%5B%7B%22type%22%3A%22promptString%22%2C%22id%22%3A%22tavily_api_key%22%2C%22description%22%3A%22Tavily+API+Key%22%2C%22password%22%3Atrue%7D%5D&quality=insiders) 74 | 75 | ### Manual Installation 76 | 77 | First check if there are install buttons at the top of this section that match your needs. If you prefer manual installation, follow these steps: 78 | 79 | Add the following JSON block to your User Settings (JSON) file in VS Code. You can do this by pressing `Ctrl + Shift + P` (or `Cmd + Shift + P` on macOS) and typing `Preferences: Open User Settings (JSON)`. 80 | 81 | ```json 82 | { 83 | "mcp": { 84 | "inputs": [ 85 | { 86 | "type": "promptString", 87 | "id": "tavily_api_key", 88 | "description": "Tavily API Key", 89 | "password": true 90 | } 91 | ], 92 | "servers": { 93 | "tavily": { 94 | "command": "npx", 95 | "args": ["-y", "tavily-mcp@0.2.1"], 96 | "env": { 97 | "TAVILY_API_KEY": "${input:tavily_api_key}" 98 | } 99 | } 100 | } 101 | } 102 | } 103 | ``` 104 | 105 | Optionally, you can add it to a file called `.vscode/mcp.json` in your workspace: 106 | 107 | ```json 108 | { 109 | "inputs": [ 110 | { 111 | "type": "promptString", 112 | "id": "tavily_api_key", 113 | "description": "Tavily API Key", 114 | "password": true 115 | } 116 | ], 117 | "servers": { 118 | "tavily": { 119 | "command": "npx", 120 | "args": ["-y", "tavily-mcp@0.2.1"], 121 | "env": { 122 | "TAVILY_API_KEY": "${input:tavily_api_key}" 123 | } 124 | } 125 | } 126 | } 127 | ``` 128 | 129 | ### Configuring Cline 🤖 130 | 131 | The easiest way to set up the Tavily MCP server in Cline is through the marketplace with a single click: 132 | 133 | 1. Open Cline in VS Code 134 | 2. Click on the Cline icon in the sidebar 135 | 3. Navigate to the "MCP Servers" tab ( 4 squares ) 136 | 4. Search "Tavily" and click "install" 137 | 5. When prompted, enter your Tavily API key 138 | 139 | Alternatively, you can manually set up the Tavily MCP server in Cline: 140 | 141 | 1. Open the Cline MCP settings file: 142 | 143 | ### For macOS: 144 | ```bash 145 | # Using Visual Studio Code 146 | code ~/Library/Application\ Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json 147 | 148 | # Or using TextEdit 149 | open -e ~/Library/Application\ Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json 150 | ``` 151 | 152 | ### For Windows: 153 | ```bash 154 | code %APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\settings\cline_mcp_settings.json 155 | ``` 156 | 157 | 2. Add the Tavily server configuration to the file: 158 | 159 | Replace `your-api-key-here` with your actual [Tavily API key](https://tavily.com/api-keys). 160 | 161 | ```json 162 | { 163 | "mcpServers": { 164 | "tavily-mcp": { 165 | "command": "npx", 166 | "args": ["-y", "tavily-mcp@0.2.1"], 167 | "env": { 168 | "TAVILY_API_KEY": "your-api-key-here" 169 | }, 170 | "disabled": false, 171 | "autoApprove": [] 172 | } 173 | } 174 | } 175 | ``` 176 | 177 | 3. Save the file and restart Cline if it's already running. 178 | 179 | 4. When using Cline, you'll now have access to the Tavily MCP tools. You can ask Cline to use the tavily-search and tavily-extract tools directly in your conversations. 180 | 181 | 182 | ### Configuring Cursor 🖥️ 183 | 184 | > **Note**: Requires Cursor version 0.45.6 or higher 185 | 186 | To set up the Tavily MCP server in Cursor: 187 | 188 | 1. Open Cursor Settings 189 | 2. Navigate to Features > MCP Servers 190 | 3. Click on the "+ Add New MCP Server" button 191 | 4. Fill out the following information: 192 | - **Name**: Enter a nickname for the server (e.g., "tavily-mcp") 193 | - **Type**: Select "command" as the type 194 | - **Command**: Enter the command to run the server: 195 | ```bash 196 | env TAVILY_API_KEY=your-api-key npx -y tavily-mcp@0.2.1 197 | ``` 198 | > **Important**: Replace `your-api-key` with your Tavily API key. You can get one at [app.tavily.com/home](https://app.tavily.com/home) 199 | 200 | After adding the server, it should appear in the list of MCP servers. You may need to manually press the refresh button in the top right corner of the MCP server to populate the tool list. 201 | 202 | The Composer Agent will automatically use the Tavily MCP tools when relevant to your queries. It is better to explicitly request to use the tools by describing what you want to do (e.g., "User tavily-search to search the web for the latest news on AI"). On mac press command + L to open the chat, select the composer option at the top of the screen, beside the submit button select agent and submit the query when ready. 203 | 204 | ![Cursor Interface Example](./assets/cursor-reference.png) 205 | 206 | ### Configuring the Claude Desktop app 🖥️ 207 | ### For macOS: 208 | 209 | ```bash 210 | # Create the config file if it doesn't exist 211 | touch "$HOME/Library/Application Support/Claude/claude_desktop_config.json" 212 | 213 | # Opens the config file in TextEdit 214 | open -e "$HOME/Library/Application Support/Claude/claude_desktop_config.json" 215 | 216 | # Alternative method using Visual Studio Code (requires VS Code to be installed) 217 | code "$HOME/Library/Application Support/Claude/claude_desktop_config.json" 218 | ``` 219 | 220 | ### For Windows: 221 | ```bash 222 | code %APPDATA%\Claude\claude_desktop_config.json 223 | ``` 224 | 225 | ### Add the Tavily server configuration: 226 | 227 | Replace `your-api-key-here` with your actual [Tavily API key](https://tavily.com/api-keys). 228 | 229 | ```json 230 | { 231 | "mcpServers": { 232 | "tavily-mcp": { 233 | "command": "npx", 234 | "args": ["-y", "tavily-mcp@0.2.1"], 235 | "env": { 236 | "TAVILY_API_KEY": "your-api-key-here" 237 | } 238 | } 239 | } 240 | } 241 | ``` 242 | 243 | ### 2. Git Installation 244 | 245 | 1. Clone the repository: 246 | ```bash 247 | git clone https://github.com/tavily-ai/tavily-mcp.git 248 | cd tavily-mcp 249 | ``` 250 | 251 | 2. Install dependencies: 252 | ```bash 253 | npm install 254 | ``` 255 | 256 | 3. Build the project: 257 | ```bash 258 | npm run build 259 | ``` 260 | ### Configuring the Claude Desktop app ⚙️ 261 | Follow the configuration steps outlined in the [Configuring the Claude Desktop app](#configuring-the-claude-desktop-app-️) section above, using the below JSON configuration. 262 | 263 | Replace `your-api-key-here` with your actual [Tavily API key](https://tavily.com/api-keys) and `/path/to/tavily-mcp` with the actual path where you cloned the repository on your system. 264 | 265 | ```json 266 | { 267 | "mcpServers": { 268 | "tavily": { 269 | "command": "npx", 270 | "args": ["/path/to/tavily-mcp/build/index.js"], 271 | "env": { 272 | "TAVILY_API_KEY": "your-api-key-here" 273 | } 274 | } 275 | } 276 | } 277 | ``` 278 | 279 | ## Usage in Claude Desktop App 🎯 280 | 281 | Once the installation is complete, and the Claude desktop app is configured, you must completely close and re-open the Claude desktop app to see the tavily-mcp server. You should see a hammer icon in the bottom left of the app, indicating available MCP tools, you can click on the hammer icon to see more detial on the tavily-search and tavily-extract tools. 282 | 283 | ![Alt text](./assets/claude-desktop-ref.png) 284 | 285 | Now claude will have complete access to the tavily-mcp server, including the tavily-search and tavily-extract tools. If you insert the below examples into the Claude desktop app, you should see the tavily-mcp server tools in action. 286 | 287 | ### Tavily Search Examples 288 | 289 | 1. **General Web Search**: 290 | ``` 291 | Can you search for recent developments in quantum computing? 292 | ``` 293 | 294 | 2. **News Search**: 295 | ``` 296 | Search for news articles about AI startups from the last 7 days. 297 | ``` 298 | 299 | 3. **Domain-Specific Search**: 300 | ``` 301 | Search for climate change research on nature.com and sciencedirect.com 302 | ``` 303 | 304 | ### Tavily Extract Examples 305 | 306 | 1. **Extract Article Content**: 307 | ``` 308 | Extract the main content from this article: https://example.com/article 309 | ``` 310 | 311 | ### ✨ Combine Search and Extract ✨ 312 | 313 | You can also combine the tavily-search and tavily-extract tools to perform more complex tasks. 314 | 315 | ``` 316 | Search for news articles about AI startups from the last 7 days and extract the main content from each article to generate a detailed report. 317 | ``` 318 | 319 | ## Troubleshooting 🛠️ 320 | 321 | ### Common Issues 322 | 323 | 1. **Server Not Found** 324 | - Verify the npm installation by running `npm --verison` 325 | - Check Claude Desktop configuration syntax by running `code ~/Library/Application\ Support/Claude/claude_desktop_config.json` 326 | - Ensure Node.js is properly installed by running `node --version` 327 | 328 | 2. **NPX related issues** 329 | - If you encounter errors related to `npx`, you may need to use the full path to the npx executable instead. 330 | - You can find this path by running `which npx` in your terminal, then replace the `"command": "npx"` line with `"command": "/full/path/to/npx"` in your configuration. 331 | 332 | 3. **API Key Issues** 333 | - Confirm your Tavily API key is valid 334 | - Check the API key is correctly set in the config 335 | - Verify no spaces or quotes around the API key 336 | 337 | ## Acknowledgments ✨ 338 | 339 | - [Model Context Protocol](https://modelcontextprotocol.io) for the MCP specification 340 | - [Anthropic](https://anthropic.com) for Claude Desktop 341 | -------------------------------------------------------------------------------- /assets/Banner_NEW.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tavily-ai/tavily-mcp/e9e9b1b2b34d3ff6d374ad5a57bdf8904ca908b1/assets/Banner_NEW.png -------------------------------------------------------------------------------- /assets/claude-desktop-ref.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tavily-ai/tavily-mcp/e9e9b1b2b34d3ff6d374ad5a57bdf8904ca908b1/assets/claude-desktop-ref.png -------------------------------------------------------------------------------- /assets/cursor-reference.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tavily-ai/tavily-mcp/e9e9b1b2b34d3ff6d374ad5a57bdf8904ca908b1/assets/cursor-reference.png -------------------------------------------------------------------------------- /assets/demo_new.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tavily-ai/tavily-mcp/e9e9b1b2b34d3ff6d374ad5a57bdf8904ca908b1/assets/demo_new.gif -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tavily-mcp", 3 | "version": "0.2.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "tavily-mcp", 9 | "version": "0.2.1", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@modelcontextprotocol/sdk": "0.6.0", 13 | "axios": "^1.6.7", 14 | "dotenv": "^16.4.5", 15 | "yargs": "^17.7.2" 16 | }, 17 | "bin": { 18 | "tavily-mcp": "build/index.js" 19 | }, 20 | "devDependencies": { 21 | "@types/node": "^20.11.24", 22 | "@types/yargs": "^17.0.32", 23 | "typescript": "^5.3.3" 24 | } 25 | }, 26 | "node_modules/@modelcontextprotocol/sdk": { 27 | "version": "0.6.0", 28 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz", 29 | "integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==", 30 | "license": "MIT", 31 | "dependencies": { 32 | "content-type": "^1.0.5", 33 | "raw-body": "^3.0.0", 34 | "zod": "^3.23.8" 35 | } 36 | }, 37 | "node_modules/@types/node": { 38 | "version": "20.17.46", 39 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.46.tgz", 40 | "integrity": "sha512-0PQHLhZPWOxGW4auogW0eOQAuNIlCYvibIpG67ja0TOJ6/sehu+1en7sfceUn+QQtx4Rk3GxbLNwPh0Cav7TWw==", 41 | "dev": true, 42 | "license": "MIT", 43 | "dependencies": { 44 | "undici-types": "~6.19.2" 45 | } 46 | }, 47 | "node_modules/@types/yargs": { 48 | "version": "17.0.33", 49 | "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", 50 | "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", 51 | "dev": true, 52 | "license": "MIT", 53 | "dependencies": { 54 | "@types/yargs-parser": "*" 55 | } 56 | }, 57 | "node_modules/@types/yargs-parser": { 58 | "version": "21.0.3", 59 | "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", 60 | "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", 61 | "dev": true, 62 | "license": "MIT" 63 | }, 64 | "node_modules/ansi-regex": { 65 | "version": "5.0.1", 66 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 67 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 68 | "license": "MIT", 69 | "engines": { 70 | "node": ">=8" 71 | } 72 | }, 73 | "node_modules/ansi-styles": { 74 | "version": "4.3.0", 75 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 76 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 77 | "license": "MIT", 78 | "dependencies": { 79 | "color-convert": "^2.0.1" 80 | }, 81 | "engines": { 82 | "node": ">=8" 83 | }, 84 | "funding": { 85 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 86 | } 87 | }, 88 | "node_modules/asynckit": { 89 | "version": "0.4.0", 90 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 91 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 92 | "license": "MIT" 93 | }, 94 | "node_modules/axios": { 95 | "version": "1.9.0", 96 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", 97 | "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", 98 | "license": "MIT", 99 | "dependencies": { 100 | "follow-redirects": "^1.15.6", 101 | "form-data": "^4.0.0", 102 | "proxy-from-env": "^1.1.0" 103 | } 104 | }, 105 | "node_modules/bytes": { 106 | "version": "3.1.2", 107 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 108 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 109 | "license": "MIT", 110 | "engines": { 111 | "node": ">= 0.8" 112 | } 113 | }, 114 | "node_modules/call-bind-apply-helpers": { 115 | "version": "1.0.2", 116 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 117 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 118 | "license": "MIT", 119 | "dependencies": { 120 | "es-errors": "^1.3.0", 121 | "function-bind": "^1.1.2" 122 | }, 123 | "engines": { 124 | "node": ">= 0.4" 125 | } 126 | }, 127 | "node_modules/cliui": { 128 | "version": "8.0.1", 129 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 130 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 131 | "license": "ISC", 132 | "dependencies": { 133 | "string-width": "^4.2.0", 134 | "strip-ansi": "^6.0.1", 135 | "wrap-ansi": "^7.0.0" 136 | }, 137 | "engines": { 138 | "node": ">=12" 139 | } 140 | }, 141 | "node_modules/color-convert": { 142 | "version": "2.0.1", 143 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 144 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 145 | "license": "MIT", 146 | "dependencies": { 147 | "color-name": "~1.1.4" 148 | }, 149 | "engines": { 150 | "node": ">=7.0.0" 151 | } 152 | }, 153 | "node_modules/color-name": { 154 | "version": "1.1.4", 155 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 156 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 157 | "license": "MIT" 158 | }, 159 | "node_modules/combined-stream": { 160 | "version": "1.0.8", 161 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 162 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 163 | "license": "MIT", 164 | "dependencies": { 165 | "delayed-stream": "~1.0.0" 166 | }, 167 | "engines": { 168 | "node": ">= 0.8" 169 | } 170 | }, 171 | "node_modules/content-type": { 172 | "version": "1.0.5", 173 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 174 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 175 | "license": "MIT", 176 | "engines": { 177 | "node": ">= 0.6" 178 | } 179 | }, 180 | "node_modules/delayed-stream": { 181 | "version": "1.0.0", 182 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 183 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 184 | "license": "MIT", 185 | "engines": { 186 | "node": ">=0.4.0" 187 | } 188 | }, 189 | "node_modules/depd": { 190 | "version": "2.0.0", 191 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 192 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 193 | "license": "MIT", 194 | "engines": { 195 | "node": ">= 0.8" 196 | } 197 | }, 198 | "node_modules/dotenv": { 199 | "version": "16.5.0", 200 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", 201 | "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", 202 | "license": "BSD-2-Clause", 203 | "engines": { 204 | "node": ">=12" 205 | }, 206 | "funding": { 207 | "url": "https://dotenvx.com" 208 | } 209 | }, 210 | "node_modules/dunder-proto": { 211 | "version": "1.0.1", 212 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 213 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 214 | "license": "MIT", 215 | "dependencies": { 216 | "call-bind-apply-helpers": "^1.0.1", 217 | "es-errors": "^1.3.0", 218 | "gopd": "^1.2.0" 219 | }, 220 | "engines": { 221 | "node": ">= 0.4" 222 | } 223 | }, 224 | "node_modules/emoji-regex": { 225 | "version": "8.0.0", 226 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 227 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 228 | "license": "MIT" 229 | }, 230 | "node_modules/es-define-property": { 231 | "version": "1.0.1", 232 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 233 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 234 | "license": "MIT", 235 | "engines": { 236 | "node": ">= 0.4" 237 | } 238 | }, 239 | "node_modules/es-errors": { 240 | "version": "1.3.0", 241 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 242 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 243 | "license": "MIT", 244 | "engines": { 245 | "node": ">= 0.4" 246 | } 247 | }, 248 | "node_modules/es-object-atoms": { 249 | "version": "1.1.1", 250 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 251 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 252 | "license": "MIT", 253 | "dependencies": { 254 | "es-errors": "^1.3.0" 255 | }, 256 | "engines": { 257 | "node": ">= 0.4" 258 | } 259 | }, 260 | "node_modules/es-set-tostringtag": { 261 | "version": "2.1.0", 262 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 263 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 264 | "license": "MIT", 265 | "dependencies": { 266 | "es-errors": "^1.3.0", 267 | "get-intrinsic": "^1.2.6", 268 | "has-tostringtag": "^1.0.2", 269 | "hasown": "^2.0.2" 270 | }, 271 | "engines": { 272 | "node": ">= 0.4" 273 | } 274 | }, 275 | "node_modules/escalade": { 276 | "version": "3.2.0", 277 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 278 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 279 | "license": "MIT", 280 | "engines": { 281 | "node": ">=6" 282 | } 283 | }, 284 | "node_modules/follow-redirects": { 285 | "version": "1.15.9", 286 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 287 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 288 | "funding": [ 289 | { 290 | "type": "individual", 291 | "url": "https://github.com/sponsors/RubenVerborgh" 292 | } 293 | ], 294 | "license": "MIT", 295 | "engines": { 296 | "node": ">=4.0" 297 | }, 298 | "peerDependenciesMeta": { 299 | "debug": { 300 | "optional": true 301 | } 302 | } 303 | }, 304 | "node_modules/form-data": { 305 | "version": "4.0.2", 306 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", 307 | "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", 308 | "license": "MIT", 309 | "dependencies": { 310 | "asynckit": "^0.4.0", 311 | "combined-stream": "^1.0.8", 312 | "es-set-tostringtag": "^2.1.0", 313 | "mime-types": "^2.1.12" 314 | }, 315 | "engines": { 316 | "node": ">= 6" 317 | } 318 | }, 319 | "node_modules/function-bind": { 320 | "version": "1.1.2", 321 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 322 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 323 | "license": "MIT", 324 | "funding": { 325 | "url": "https://github.com/sponsors/ljharb" 326 | } 327 | }, 328 | "node_modules/get-caller-file": { 329 | "version": "2.0.5", 330 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 331 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 332 | "license": "ISC", 333 | "engines": { 334 | "node": "6.* || 8.* || >= 10.*" 335 | } 336 | }, 337 | "node_modules/get-intrinsic": { 338 | "version": "1.3.0", 339 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 340 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 341 | "license": "MIT", 342 | "dependencies": { 343 | "call-bind-apply-helpers": "^1.0.2", 344 | "es-define-property": "^1.0.1", 345 | "es-errors": "^1.3.0", 346 | "es-object-atoms": "^1.1.1", 347 | "function-bind": "^1.1.2", 348 | "get-proto": "^1.0.1", 349 | "gopd": "^1.2.0", 350 | "has-symbols": "^1.1.0", 351 | "hasown": "^2.0.2", 352 | "math-intrinsics": "^1.1.0" 353 | }, 354 | "engines": { 355 | "node": ">= 0.4" 356 | }, 357 | "funding": { 358 | "url": "https://github.com/sponsors/ljharb" 359 | } 360 | }, 361 | "node_modules/get-proto": { 362 | "version": "1.0.1", 363 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 364 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 365 | "license": "MIT", 366 | "dependencies": { 367 | "dunder-proto": "^1.0.1", 368 | "es-object-atoms": "^1.0.0" 369 | }, 370 | "engines": { 371 | "node": ">= 0.4" 372 | } 373 | }, 374 | "node_modules/gopd": { 375 | "version": "1.2.0", 376 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 377 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 378 | "license": "MIT", 379 | "engines": { 380 | "node": ">= 0.4" 381 | }, 382 | "funding": { 383 | "url": "https://github.com/sponsors/ljharb" 384 | } 385 | }, 386 | "node_modules/has-symbols": { 387 | "version": "1.1.0", 388 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 389 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 390 | "license": "MIT", 391 | "engines": { 392 | "node": ">= 0.4" 393 | }, 394 | "funding": { 395 | "url": "https://github.com/sponsors/ljharb" 396 | } 397 | }, 398 | "node_modules/has-tostringtag": { 399 | "version": "1.0.2", 400 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 401 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 402 | "license": "MIT", 403 | "dependencies": { 404 | "has-symbols": "^1.0.3" 405 | }, 406 | "engines": { 407 | "node": ">= 0.4" 408 | }, 409 | "funding": { 410 | "url": "https://github.com/sponsors/ljharb" 411 | } 412 | }, 413 | "node_modules/hasown": { 414 | "version": "2.0.2", 415 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 416 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 417 | "license": "MIT", 418 | "dependencies": { 419 | "function-bind": "^1.1.2" 420 | }, 421 | "engines": { 422 | "node": ">= 0.4" 423 | } 424 | }, 425 | "node_modules/http-errors": { 426 | "version": "2.0.0", 427 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 428 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 429 | "license": "MIT", 430 | "dependencies": { 431 | "depd": "2.0.0", 432 | "inherits": "2.0.4", 433 | "setprototypeof": "1.2.0", 434 | "statuses": "2.0.1", 435 | "toidentifier": "1.0.1" 436 | }, 437 | "engines": { 438 | "node": ">= 0.8" 439 | } 440 | }, 441 | "node_modules/iconv-lite": { 442 | "version": "0.6.3", 443 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 444 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 445 | "license": "MIT", 446 | "dependencies": { 447 | "safer-buffer": ">= 2.1.2 < 3.0.0" 448 | }, 449 | "engines": { 450 | "node": ">=0.10.0" 451 | } 452 | }, 453 | "node_modules/inherits": { 454 | "version": "2.0.4", 455 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 456 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 457 | "license": "ISC" 458 | }, 459 | "node_modules/is-fullwidth-code-point": { 460 | "version": "3.0.0", 461 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 462 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 463 | "license": "MIT", 464 | "engines": { 465 | "node": ">=8" 466 | } 467 | }, 468 | "node_modules/math-intrinsics": { 469 | "version": "1.1.0", 470 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 471 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 472 | "license": "MIT", 473 | "engines": { 474 | "node": ">= 0.4" 475 | } 476 | }, 477 | "node_modules/mime-db": { 478 | "version": "1.52.0", 479 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 480 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 481 | "license": "MIT", 482 | "engines": { 483 | "node": ">= 0.6" 484 | } 485 | }, 486 | "node_modules/mime-types": { 487 | "version": "2.1.35", 488 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 489 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 490 | "license": "MIT", 491 | "dependencies": { 492 | "mime-db": "1.52.0" 493 | }, 494 | "engines": { 495 | "node": ">= 0.6" 496 | } 497 | }, 498 | "node_modules/proxy-from-env": { 499 | "version": "1.1.0", 500 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 501 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 502 | "license": "MIT" 503 | }, 504 | "node_modules/raw-body": { 505 | "version": "3.0.0", 506 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", 507 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", 508 | "license": "MIT", 509 | "dependencies": { 510 | "bytes": "3.1.2", 511 | "http-errors": "2.0.0", 512 | "iconv-lite": "0.6.3", 513 | "unpipe": "1.0.0" 514 | }, 515 | "engines": { 516 | "node": ">= 0.8" 517 | } 518 | }, 519 | "node_modules/require-directory": { 520 | "version": "2.1.1", 521 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 522 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 523 | "license": "MIT", 524 | "engines": { 525 | "node": ">=0.10.0" 526 | } 527 | }, 528 | "node_modules/safer-buffer": { 529 | "version": "2.1.2", 530 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 531 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 532 | "license": "MIT" 533 | }, 534 | "node_modules/setprototypeof": { 535 | "version": "1.2.0", 536 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 537 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 538 | "license": "ISC" 539 | }, 540 | "node_modules/statuses": { 541 | "version": "2.0.1", 542 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 543 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 544 | "license": "MIT", 545 | "engines": { 546 | "node": ">= 0.8" 547 | } 548 | }, 549 | "node_modules/string-width": { 550 | "version": "4.2.3", 551 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 552 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 553 | "license": "MIT", 554 | "dependencies": { 555 | "emoji-regex": "^8.0.0", 556 | "is-fullwidth-code-point": "^3.0.0", 557 | "strip-ansi": "^6.0.1" 558 | }, 559 | "engines": { 560 | "node": ">=8" 561 | } 562 | }, 563 | "node_modules/strip-ansi": { 564 | "version": "6.0.1", 565 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 566 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 567 | "license": "MIT", 568 | "dependencies": { 569 | "ansi-regex": "^5.0.1" 570 | }, 571 | "engines": { 572 | "node": ">=8" 573 | } 574 | }, 575 | "node_modules/toidentifier": { 576 | "version": "1.0.1", 577 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 578 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 579 | "license": "MIT", 580 | "engines": { 581 | "node": ">=0.6" 582 | } 583 | }, 584 | "node_modules/typescript": { 585 | "version": "5.8.3", 586 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 587 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 588 | "dev": true, 589 | "license": "Apache-2.0", 590 | "bin": { 591 | "tsc": "bin/tsc", 592 | "tsserver": "bin/tsserver" 593 | }, 594 | "engines": { 595 | "node": ">=14.17" 596 | } 597 | }, 598 | "node_modules/undici-types": { 599 | "version": "6.19.8", 600 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", 601 | "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", 602 | "dev": true, 603 | "license": "MIT" 604 | }, 605 | "node_modules/unpipe": { 606 | "version": "1.0.0", 607 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 608 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 609 | "license": "MIT", 610 | "engines": { 611 | "node": ">= 0.8" 612 | } 613 | }, 614 | "node_modules/wrap-ansi": { 615 | "version": "7.0.0", 616 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 617 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 618 | "license": "MIT", 619 | "dependencies": { 620 | "ansi-styles": "^4.0.0", 621 | "string-width": "^4.1.0", 622 | "strip-ansi": "^6.0.0" 623 | }, 624 | "engines": { 625 | "node": ">=10" 626 | }, 627 | "funding": { 628 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 629 | } 630 | }, 631 | "node_modules/y18n": { 632 | "version": "5.0.8", 633 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 634 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 635 | "license": "ISC", 636 | "engines": { 637 | "node": ">=10" 638 | } 639 | }, 640 | "node_modules/yargs": { 641 | "version": "17.7.2", 642 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 643 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 644 | "license": "MIT", 645 | "dependencies": { 646 | "cliui": "^8.0.1", 647 | "escalade": "^3.1.1", 648 | "get-caller-file": "^2.0.5", 649 | "require-directory": "^2.1.1", 650 | "string-width": "^4.2.3", 651 | "y18n": "^5.0.5", 652 | "yargs-parser": "^21.1.1" 653 | }, 654 | "engines": { 655 | "node": ">=12" 656 | } 657 | }, 658 | "node_modules/yargs-parser": { 659 | "version": "21.1.1", 660 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 661 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 662 | "license": "ISC", 663 | "engines": { 664 | "node": ">=12" 665 | } 666 | }, 667 | "node_modules/zod": { 668 | "version": "3.24.4", 669 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", 670 | "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", 671 | "license": "MIT", 672 | "funding": { 673 | "url": "https://github.com/sponsors/colinhacks" 674 | } 675 | } 676 | } 677 | } 678 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tavily-mcp", 3 | "version": "0.2.1", 4 | "description": "MCP server for advanced web search using Tavily", 5 | "type": "module", 6 | "bin": { 7 | "tavily-mcp": "./build/index.js" 8 | }, 9 | "files": [ 10 | "build" 11 | ], 12 | "scripts": { 13 | "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"", 14 | "prepare": "npm run build", 15 | "watch": "tsc --watch", 16 | "inspector": "npx @modelcontextprotocol/inspector build/index.js", 17 | "prepublishOnly": "npm run build" 18 | }, 19 | "keywords": [ 20 | "tavily-mcp", 21 | "tavily", 22 | "mcp", 23 | "crawl", 24 | "model-context-protocol", 25 | "websearch", 26 | "claude", 27 | "claude-desktop", 28 | "search-api", 29 | "web-search", 30 | "ai-search", 31 | "anthropic", 32 | "real-time-search", 33 | "search-tools", 34 | "tavily-api", 35 | "tavily-search", 36 | "tavily-extract", 37 | "web-extraction", 38 | "data-extraction", 39 | "search-integration" 40 | ], 41 | "author": "Tavily", 42 | "license": "MIT", 43 | "dependencies": { 44 | "@modelcontextprotocol/sdk": "0.6.0", 45 | "dotenv": "^16.4.5", 46 | "axios": "^1.6.7", 47 | "yargs": "^17.7.2" 48 | }, 49 | "devDependencies": { 50 | "@types/node": "^20.11.24", 51 | "@types/yargs": "^17.0.32", 52 | "typescript": "^5.3.3" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - tavilyApiKey 10 | properties: 11 | tavilyApiKey: 12 | type: string 13 | description: The API key for the Tavily MCP server. 14 | commandFunction: 15 | # A function that produces the CLI command to start the MCP on stdio. 16 | |- 17 | config => ({ command: 'node', args: ['build/index.js'], env: { TAVILY_API_KEY: config.tavilyApiKey } }) -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js"; 4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 5 | import {CallToolRequestSchema, ListToolsRequestSchema, Tool} from "@modelcontextprotocol/sdk/types.js"; 6 | import axios from "axios"; 7 | import dotenv from "dotenv"; 8 | import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js"; 9 | import yargs from 'yargs'; 10 | import { hideBin } from 'yargs/helpers'; 11 | 12 | dotenv.config(); 13 | 14 | const API_KEY = process.env.TAVILY_API_KEY; 15 | if (!API_KEY) { 16 | throw new Error("TAVILY_API_KEY environment variable is required"); 17 | } 18 | 19 | 20 | interface TavilyResponse { 21 | // Response structure from Tavily API 22 | query: string; 23 | follow_up_questions?: Array; 24 | answer?: string; 25 | images?: Array; 29 | results: Array<{ 30 | title: string; 31 | url: string; 32 | content: string; 33 | score: number; 34 | published_date?: string; 35 | raw_content?: string; 36 | }>; 37 | } 38 | 39 | interface TavilyCrawlResponse { 40 | base_url: string; 41 | results: Array<{ 42 | url: string; 43 | raw_content: string; 44 | }>; 45 | response_time: number; 46 | } 47 | 48 | interface TavilyMapResponse { 49 | base_url: string; 50 | results: string[]; 51 | response_time: number; 52 | } 53 | 54 | class TavilyClient { 55 | // Core client properties 56 | private server: Server; 57 | private axiosInstance; 58 | private baseURLs = { 59 | search: 'https://api.tavily.com/search', 60 | extract: 'https://api.tavily.com/extract', 61 | crawl: 'https://api.tavily.com/crawl', 62 | map: 'https://api.tavily.com/map' 63 | }; 64 | 65 | constructor() { 66 | this.server = new Server( 67 | { 68 | name: "tavily-mcp", 69 | version: "0.2.1", 70 | }, 71 | { 72 | capabilities: { 73 | resources: {}, 74 | tools: {}, 75 | prompts: {}, 76 | }, 77 | } 78 | ); 79 | 80 | this.axiosInstance = axios.create({ 81 | headers: { 82 | 'accept': 'application/json', 83 | 'content-type': 'application/json', 84 | 'Authorization': `Bearer ${API_KEY}` 85 | } 86 | }); 87 | 88 | this.setupHandlers(); 89 | this.setupErrorHandling(); 90 | } 91 | 92 | private setupErrorHandling(): void { 93 | this.server.onerror = (error) => { 94 | console.error("[MCP Error]", error); 95 | }; 96 | 97 | process.on('SIGINT', async () => { 98 | await this.server.close(); 99 | process.exit(0); 100 | }); 101 | } 102 | 103 | private setupHandlers(): void { 104 | this.setupToolHandlers(); 105 | } 106 | 107 | private setupToolHandlers(): void { 108 | this.server.setRequestHandler(ListToolsRequestSchema, async () => { 109 | // Define available tools: tavily-search and tavily-extract 110 | const tools: Tool[] = [ 111 | { 112 | name: "tavily-search", 113 | description: "A powerful web search tool that provides comprehensive, real-time results using Tavily's AI search engine. Returns relevant web content with customizable parameters for result count, content type, and domain filtering. Ideal for gathering current information, news, and detailed web content analysis.", 114 | inputSchema: { 115 | type: "object", 116 | properties: { 117 | query: { 118 | type: "string", 119 | description: "Search query" 120 | }, 121 | search_depth: { 122 | type: "string", 123 | enum: ["basic","advanced"], 124 | description: "The depth of the search. It can be 'basic' or 'advanced'", 125 | default: "basic" 126 | }, 127 | topic : { 128 | type: "string", 129 | enum: ["general","news"], 130 | description: "The category of the search. This will determine which of our agents will be used for the search", 131 | default: "general" 132 | }, 133 | days: { 134 | type: "number", 135 | description: "The number of days back from the current date to include in the search results. This specifies the time frame of data to be retrieved. Please note that this feature is only available when using the 'news' search topic", 136 | default: 3 137 | }, 138 | time_range: { 139 | type: "string", 140 | description: "The time range back from the current date to include in the search results. This feature is available for both 'general' and 'news' search topics", 141 | enum: ["day", "week", "month", "year", "d", "w", "m", "y"], 142 | }, 143 | max_results: { 144 | type: "number", 145 | description: "The maximum number of search results to return", 146 | default: 10, 147 | minimum: 5, 148 | maximum: 20 149 | }, 150 | include_images: { 151 | type: "boolean", 152 | description: "Include a list of query-related images in the response", 153 | default: false, 154 | }, 155 | include_image_descriptions: { 156 | type: "boolean", 157 | description: "Include a list of query-related images and their descriptions in the response", 158 | default: false, 159 | }, 160 | /* 161 | // Since the mcp server is using AI clients to generate answers form the search results, we don't need to include this feature. 162 | include_answer: { 163 | type: ["boolean", "string"], 164 | enum: [true, false, "basic", "advanced"], 165 | description: "Include an answer to original query, generated by an LLM based on Tavily's search results. Can be boolean or string ('basic'/'advanced'). 'basic'/true answer will be quick but less detailed, 'advanced' answer will be more detailed but take longer to generate", 166 | default: false, 167 | }, 168 | */ 169 | include_raw_content: { 170 | type: "boolean", 171 | description: "Include the cleaned and parsed HTML content of each search result", 172 | default: false, 173 | }, 174 | include_domains: { 175 | type: "array", 176 | items: { type: "string" }, 177 | description: "A list of domains to specifically include in the search results, if the user asks to search on specific sites set this to the domain of the site", 178 | default: [] 179 | }, 180 | exclude_domains: { 181 | type: "array", 182 | items: { type: "string" }, 183 | description: "List of domains to specifically exclude, if the user asks to exclude a domain set this to the domain of the site", 184 | default: [] 185 | } 186 | }, 187 | required: ["query"] 188 | } 189 | }, 190 | { 191 | name: "tavily-extract", 192 | description: "A powerful web content extraction tool that retrieves and processes raw content from specified URLs, ideal for data collection, content analysis, and research tasks.", 193 | inputSchema: { 194 | type: "object", 195 | properties: { 196 | urls: { 197 | type: "array", 198 | items: { type: "string" }, 199 | description: "List of URLs to extract content from" 200 | }, 201 | extract_depth: { 202 | type: "string", 203 | enum: ["basic","advanced"], 204 | description: "Depth of extraction - 'basic' or 'advanced', if usrls are linkedin use 'advanced' or if explicitly told to use advanced", 205 | default: "basic" 206 | }, 207 | include_images: { 208 | type: "boolean", 209 | description: "Include a list of images extracted from the urls in the response", 210 | default: false, 211 | } 212 | }, 213 | required: ["urls"] 214 | } 215 | }, 216 | { 217 | name: "tavily-crawl", 218 | description: "A powerful web crawler that initiates a structured web crawl starting from a specified base URL. The crawler expands from that point like a tree, following internal links across pages. You can control how deep and wide it goes, and guide it to focus on specific sections of the site.", 219 | inputSchema: { 220 | type: "object", 221 | properties: { 222 | url: { 223 | type: "string", 224 | description: "The root URL to begin the crawl" 225 | }, 226 | max_depth: { 227 | type: "integer", 228 | description: "Max depth of the crawl. Defines how far from the base URL the crawler can explore.", 229 | default: 1, 230 | minimum: 1 231 | }, 232 | max_breadth: { 233 | type: "integer", 234 | description: "Max number of links to follow per level of the tree (i.e., per page)", 235 | default: 20, 236 | minimum: 1 237 | }, 238 | limit: { 239 | type: "integer", 240 | description: "Total number of links the crawler will process before stopping", 241 | default: 50, 242 | minimum: 1 243 | }, 244 | instructions: { 245 | type: "string", 246 | description: "Natural language instructions for the crawler" 247 | }, 248 | select_paths: { 249 | type: "array", 250 | items: { type: "string" }, 251 | description: "Regex patterns to select only URLs with specific path patterns (e.g., /docs/.*, /api/v1.*)", 252 | default: [] 253 | }, 254 | select_domains: { 255 | type: "array", 256 | items: { type: "string" }, 257 | description: "Regex patterns to select crawling to specific domains or subdomains (e.g., ^docs\\.example\\.com$)", 258 | default: [] 259 | }, 260 | allow_external: { 261 | type: "boolean", 262 | description: "Whether to allow following links that go to external domains", 263 | default: false 264 | }, 265 | categories: { 266 | type: "array", 267 | items: { 268 | type: "string", 269 | enum: ["Careers", "Blog", "Documentation", "About", "Pricing", "Community", "Developers", "Contact", "Media"] 270 | }, 271 | description: "Filter URLs using predefined categories like documentation, blog, api, etc", 272 | default: [] 273 | }, 274 | extract_depth: { 275 | type: "string", 276 | enum: ["basic", "advanced"], 277 | description: "Advanced extraction retrieves more data, including tables and embedded content, with higher success but may increase latency", 278 | default: "basic" 279 | } 280 | }, 281 | required: ["url"] 282 | } 283 | }, 284 | { 285 | name: "tavily-map", 286 | description: "A powerful web mapping tool that creates a structured map of website URLs, allowing you to discover and analyze site structure, content organization, and navigation paths. Perfect for site audits, content discovery, and understanding website architecture.", 287 | inputSchema: { 288 | type: "object", 289 | properties: { 290 | url: { 291 | type: "string", 292 | description: "The root URL to begin the mapping" 293 | }, 294 | max_depth: { 295 | type: "integer", 296 | description: "Max depth of the mapping. Defines how far from the base URL the crawler can explore", 297 | default: 1, 298 | minimum: 1 299 | }, 300 | max_breadth: { 301 | type: "integer", 302 | description: "Max number of links to follow per level of the tree (i.e., per page)", 303 | default: 20, 304 | minimum: 1 305 | }, 306 | limit: { 307 | type: "integer", 308 | description: "Total number of links the crawler will process before stopping", 309 | default: 50, 310 | minimum: 1 311 | }, 312 | instructions: { 313 | type: "string", 314 | description: "Natural language instructions for the crawler" 315 | }, 316 | select_paths: { 317 | type: "array", 318 | items: { type: "string" }, 319 | description: "Regex patterns to select only URLs with specific path patterns (e.g., /docs/.*, /api/v1.*)", 320 | default: [] 321 | }, 322 | select_domains: { 323 | type: "array", 324 | items: { type: "string" }, 325 | description: "Regex patterns to select crawling to specific domains or subdomains (e.g., ^docs\\.example\\.com$)", 326 | default: [] 327 | }, 328 | allow_external: { 329 | type: "boolean", 330 | description: "Whether to allow following links that go to external domains", 331 | default: false 332 | }, 333 | categories: { 334 | type: "array", 335 | items: { 336 | type: "string", 337 | enum: ["Careers", "Blog", "Documentation", "About", "Pricing", "Community", "Developers", "Contact", "Media"] 338 | }, 339 | description: "Filter URLs using predefined categories like documentation, blog, api, etc", 340 | default: [] 341 | } 342 | }, 343 | required: ["url"] 344 | } 345 | }, 346 | ]; 347 | return { tools }; 348 | }); 349 | 350 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => { 351 | try { 352 | let response: TavilyResponse; 353 | const args = request.params.arguments ?? {}; 354 | 355 | switch (request.params.name) { 356 | case "tavily-search": 357 | response = await this.search({ 358 | query: args.query, 359 | search_depth: args.search_depth, 360 | topic: args.topic, 361 | days: args.days, 362 | time_range: args.time_range, 363 | max_results: args.max_results, 364 | include_images: args.include_images, 365 | include_image_descriptions: args.include_image_descriptions, 366 | include_raw_content: args.include_raw_content, 367 | include_domains: Array.isArray(args.include_domains) ? args.include_domains : [], 368 | exclude_domains: Array.isArray(args.exclude_domains) ? args.exclude_domains : [] 369 | }); 370 | break; 371 | 372 | case "tavily-extract": 373 | response = await this.extract({ 374 | urls: args.urls, 375 | extract_depth: args.extract_depth, 376 | include_images: args.include_images 377 | }); 378 | break; 379 | 380 | case "tavily-crawl": 381 | const crawlResponse = await this.crawl({ 382 | url: args.url, 383 | max_depth: args.max_depth, 384 | max_breadth: args.max_breadth, 385 | limit: args.limit, 386 | instructions: args.instructions, 387 | select_paths: Array.isArray(args.select_paths) ? args.select_paths : [], 388 | select_domains: Array.isArray(args.select_domains) ? args.select_domains : [], 389 | allow_external: args.allow_external, 390 | categories: Array.isArray(args.categories) ? args.categories : [], 391 | extract_depth: args.extract_depth 392 | }); 393 | return { 394 | content: [{ 395 | type: "text", 396 | text: formatCrawlResults(crawlResponse) 397 | }] 398 | }; 399 | 400 | case "tavily-map": 401 | const mapResponse = await this.map({ 402 | url: args.url, 403 | max_depth: args.max_depth, 404 | max_breadth: args.max_breadth, 405 | limit: args.limit, 406 | instructions: args.instructions, 407 | select_paths: Array.isArray(args.select_paths) ? args.select_paths : [], 408 | select_domains: Array.isArray(args.select_domains) ? args.select_domains : [], 409 | allow_external: args.allow_external, 410 | categories: Array.isArray(args.categories) ? args.categories : [] 411 | }); 412 | return { 413 | content: [{ 414 | type: "text", 415 | text: formatMapResults(mapResponse) 416 | }] 417 | }; 418 | 419 | default: 420 | throw new McpError( 421 | ErrorCode.MethodNotFound, 422 | `Unknown tool: ${request.params.name}` 423 | ); 424 | } 425 | 426 | return { 427 | content: [{ 428 | type: "text", 429 | text: formatResults(response) 430 | }] 431 | }; 432 | } catch (error: any) { 433 | if (axios.isAxiosError(error)) { 434 | return { 435 | content: [{ 436 | type: "text", 437 | text: `Tavily API error: ${error.response?.data?.message ?? error.message}` 438 | }], 439 | isError: true, 440 | } 441 | } 442 | throw error; 443 | } 444 | }); 445 | } 446 | 447 | 448 | async run(): Promise { 449 | const transport = new StdioServerTransport(); 450 | await this.server.connect(transport); 451 | console.error("Tavily MCP server running on stdio"); 452 | } 453 | 454 | async search(params: any): Promise { 455 | try { 456 | // Choose endpoint based on whether it's an extract request 457 | const endpoint = params.url ? this.baseURLs.extract : this.baseURLs.search; 458 | 459 | // Add topic: "news" if query contains the word "news" 460 | const searchParams = { 461 | ...params, 462 | api_key: API_KEY, 463 | topic: params.query.toLowerCase().includes('news') ? 'news' : undefined 464 | }; 465 | 466 | const response = await this.axiosInstance.post(endpoint, searchParams); 467 | return response.data; 468 | } catch (error: any) { 469 | if (error.response?.status === 401) { 470 | throw new Error('Invalid API key'); 471 | } else if (error.response?.status === 429) { 472 | throw new Error('Usage limit exceeded'); 473 | } 474 | throw error; 475 | } 476 | } 477 | 478 | async extract(params: any): Promise { 479 | try { 480 | const response = await this.axiosInstance.post(this.baseURLs.extract, { 481 | ...params, 482 | api_key: API_KEY 483 | }); 484 | return response.data; 485 | } catch (error: any) { 486 | if (error.response?.status === 401) { 487 | throw new Error('Invalid API key'); 488 | } else if (error.response?.status === 429) { 489 | throw new Error('Usage limit exceeded'); 490 | } 491 | throw error; 492 | } 493 | } 494 | 495 | async crawl(params: any): Promise { 496 | try { 497 | const response = await this.axiosInstance.post(this.baseURLs.crawl, { 498 | ...params, 499 | api_key: API_KEY 500 | }); 501 | return response.data; 502 | } catch (error: any) { 503 | if (error.response?.status === 401) { 504 | throw new Error('Invalid API key'); 505 | } else if (error.response?.status === 429) { 506 | throw new Error('Usage limit exceeded'); 507 | } 508 | throw error; 509 | } 510 | } 511 | 512 | async map(params: any): Promise { 513 | try { 514 | const response = await this.axiosInstance.post(this.baseURLs.map, { 515 | ...params, 516 | api_key: API_KEY 517 | }); 518 | return response.data; 519 | } catch (error: any) { 520 | if (error.response?.status === 401) { 521 | throw new Error('Invalid API key'); 522 | } else if (error.response?.status === 429) { 523 | throw new Error('Usage limit exceeded'); 524 | } 525 | throw error; 526 | } 527 | } 528 | } 529 | 530 | function formatResults(response: TavilyResponse): string { 531 | // Format API response into human-readable text 532 | const output: string[] = []; 533 | 534 | // Include answer if available 535 | if (response.answer) { 536 | output.push(`Answer: ${response.answer}`); 537 | } 538 | 539 | // Format detailed search results 540 | output.push('Detailed Results:'); 541 | response.results.forEach(result => { 542 | output.push(`\nTitle: ${result.title}`); 543 | output.push(`URL: ${result.url}`); 544 | output.push(`Content: ${result.content}`); 545 | if (result.raw_content) { 546 | output.push(`Raw Content: ${result.raw_content}`); 547 | } 548 | }); 549 | 550 | // Add images section if available 551 | if (response.images && response.images.length > 0) { 552 | output.push('\nImages:'); 553 | response.images.forEach((image, index) => { 554 | if (typeof image === 'string') { 555 | output.push(`\n[${index + 1}] URL: ${image}`); 556 | } else { 557 | output.push(`\n[${index + 1}] URL: ${image.url}`); 558 | if (image.description) { 559 | output.push(` Description: ${image.description}`); 560 | } 561 | } 562 | }); 563 | } 564 | 565 | return output.join('\n'); 566 | } 567 | 568 | function formatCrawlResults(response: TavilyCrawlResponse): string { 569 | const output: string[] = []; 570 | 571 | output.push(`Crawl Results:`); 572 | output.push(`Base URL: ${response.base_url}`); 573 | 574 | output.push('\nCrawled Pages:'); 575 | response.results.forEach((page, index) => { 576 | output.push(`\n[${index + 1}] URL: ${page.url}`); 577 | if (page.raw_content) { 578 | // Truncate content if it's too long 579 | const contentPreview = page.raw_content.length > 200 580 | ? page.raw_content.substring(0, 200) + "..." 581 | : page.raw_content; 582 | output.push(`Content: ${contentPreview}`); 583 | } 584 | }); 585 | 586 | return output.join('\n'); 587 | } 588 | 589 | function formatMapResults(response: TavilyMapResponse): string { 590 | const output: string[] = []; 591 | 592 | output.push(`Site Map Results:`); 593 | output.push(`Base URL: ${response.base_url}`); 594 | 595 | output.push('\nMapped Pages:'); 596 | response.results.forEach((page, index) => { 597 | output.push(`\n[${index + 1}] URL: ${page}`); 598 | }); 599 | 600 | return output.join('\n'); 601 | } 602 | 603 | function listTools(): void { 604 | const tools = [ 605 | { 606 | name: "tavily-search", 607 | description: "A real-time web search tool powered by Tavily's AI engine. Features include customizable search depth (basic/advanced), domain filtering, time-based filtering, and support for both general and news-specific searches. Returns comprehensive results with titles, URLs, content snippets, and optional image results." 608 | }, 609 | { 610 | name: "tavily-extract", 611 | description: "Extracts and processes content from specified URLs with advanced parsing capabilities. Supports both basic and advanced extraction modes, with the latter providing enhanced data retrieval including tables and embedded content. Ideal for data collection, content analysis, and research tasks." 612 | }, 613 | { 614 | name: "tavily-crawl", 615 | description: "A sophisticated web crawler that systematically explores websites starting from a base URL. Features include configurable depth and breadth limits, domain filtering, path pattern matching, and category-based filtering. Perfect for comprehensive site analysis, content discovery, and structured data collection." 616 | }, 617 | { 618 | name: "tavily-map", 619 | description: "Creates detailed site maps by analyzing website structure and navigation paths. Offers configurable exploration depth, domain restrictions, and category filtering. Ideal for site audits, content organization analysis, and understanding website architecture and navigation patterns." 620 | } 621 | ]; 622 | 623 | console.log("Available tools:"); 624 | tools.forEach(tool => { 625 | console.log(`\n- ${tool.name}`); 626 | console.log(` Description: ${tool.description}`); 627 | }); 628 | process.exit(0); 629 | } 630 | 631 | // Add this interface before the command line parsing 632 | interface Arguments { 633 | 'list-tools': boolean; 634 | _: (string | number)[]; 635 | $0: string; 636 | } 637 | 638 | // Modify the command line parsing section to use proper typing 639 | const argv = yargs(hideBin(process.argv)) 640 | .option('list-tools', { 641 | type: 'boolean', 642 | description: 'List all available tools and exit', 643 | default: false 644 | }) 645 | .help() 646 | .parse() as Arguments; 647 | 648 | // List tools if requested 649 | if (argv['list-tools']) { 650 | listTools(); 651 | } 652 | 653 | // Otherwise start the server 654 | const server = new TavilyClient(); 655 | server.run().catch(console.error); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./build", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } 16 | --------------------------------------------------------------------------------