├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── assets ├── Demo.gif ├── Demo2.gif ├── Demo3.gif ├── Tools.md └── logo.png ├── browser_session.js ├── browser_tools.js ├── examples └── README.md ├── package-lock.json ├── package.json ├── server.js └── smithery.yaml /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [2.0.0] - 2025-05-26 6 | 7 | ### Changed 8 | - Updated browser authentication to use API_TOKEN instead of previous authentication method 9 | - BROWSER_ZONE is now an optional parameter, the deafult zone is `mcp_browser` 10 | - Removed duplicate web_data_ tools 11 | 12 | ## [1.9.2] - 2025-05-23 13 | 14 | ### Fixed 15 | - Fixed GitHub references and repository settings 16 | 17 | ## [1.9.1] - 2025-05-21 18 | 19 | ### Fixed 20 | - Fixed spelling errors and improved coding conventions 21 | - Converted files back to Unix line endings for consistency 22 | 23 | ## [1.9.0] - 2025-05-21 24 | 25 | ### Added 26 | - Added 23 new web data tools for enhanced data collection capabilities 27 | - Added progress reporting functionality for better user feedback 28 | - Added default parameter handling for improved tool usability 29 | 30 | ### Changed 31 | - Improved coding conventions and file formatting 32 | - Enhanced web data API endpoints integration 33 | 34 | ## [1.8.3] - 2025-05-21 35 | 36 | ### Added 37 | - Added Bright Data MCP with Claude demo video to README.md 38 | 39 | ### Changed 40 | - Updated documentation with video demonstrations 41 | 42 | ## [1.8.2] - 2025-05-13 43 | 44 | ### Changed 45 | - Bumped FastMCP version for improved performance 46 | - Updated README.md with additional documentation 47 | 48 | ## [1.8.1] - 2025-05-05 49 | 50 | ### Added 51 | - Added 12 new WSAPI endpoints for enhanced functionality 52 | - Changed to polling mechanism for better reliability 53 | 54 | ### Changed 55 | - Applied dos2unix formatting for consistency 56 | - Updated Docker configuration 57 | - Updated smithery.yaml configuration 58 | 59 | ## [1.8.0] - 2025-05-03 60 | 61 | ### Added 62 | - Added domain-based browser sessions to avoid navigation limit issues 63 | - Added automatic creation of required unlocker zone when not present 64 | 65 | ### Fixed 66 | - Fixed browser context maintenance across tool calls with current domain tracking 67 | - Minor lint fixes 68 | 69 | ## [1.0.0] - 2025-04-29 70 | 71 | ### Added 72 | - Initial release of Bright Data MCP server 73 | - Browser automation capabilities with Bright Data integration 74 | - Core web scraping and data collection tools 75 | - Smithery.yaml configuration for deployment in Smithery.ai 76 | - MIT License 77 | - Demo materials and documentation 78 | 79 | ### Documentation 80 | - Created comprehensive README.md 81 | - Added demo.md with usage examples 82 | - Created examples/README.md for sample implementations 83 | - Added Tools.md documentation for available tools 84 | 85 | --- 86 | 87 | ## Release Notes 88 | 89 | ### Version 1.9.x Series 90 | The 1.9.x series focuses on expanding web data collection capabilities and improving authentication mechanisms. Key highlights include the addition of 23 new web data tools. 91 | 92 | ### Version 1.8.x Series 93 | The 1.8.x series introduced significant improvements to browser session management, WSAPI endpoints, and overall system reliability. Notable features include domain-based sessions and automatic zone creation. 94 | 95 | ### Version 1.0.0 96 | Initial stable release providing core MCP server functionality for Bright Data integration with comprehensive browser automation and web scraping capabilities. 97 | 98 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:22.12-alpine AS builder 2 | 3 | 4 | COPY . /app 5 | WORKDIR /app 6 | 7 | 8 | RUN --mount=type=cache,target=/root/.npm npm install 9 | 10 | FROM node:22-alpine AS release 11 | 12 | WORKDIR /app 13 | 14 | 15 | COPY --from=builder /app/server.js /app/ 16 | COPY --from=builder /app/browser_tools.js /app/ 17 | COPY --from=builder /app/browser_session.js /app/ 18 | COPY --from=builder /app/package.json /app/ 19 | COPY --from=builder /app/package-lock.json /app/ 20 | 21 | 22 | ENV NODE_ENV=production 23 | 24 | 25 | RUN npm ci --ignore-scripts --omit-dev 26 | 27 | 28 | ENTRYPOINT ["node", "server.js"] 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Bright Data 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Bright Data Logo 4 | 5 |

6 | 7 |

Bright Data MCP

8 |

Enhance AI Agents with Real-Time Web Data

9 | 10 |
11 | 12 |

13 | npm version 15 |

16 | 17 |

18 | npm downloads 20 | 21 | Smithery score 23 | 24 |

25 | 26 |
27 | 28 | ## 🌟 Overview 29 | 30 | Welcome to the official Bright Data Model Context Protocol (MCP) server, enabling LLMs, agents and apps to access, discover and extract web data in real-time. This server allows MCP clients, such as Claude Desktop, Cursor, Windsurf and others, to seamlessly search the web, navigate websites, take action and retrieve data - without getting blocked - perfect for scraping tasks. 31 | 32 | ![MCP](https://github.com/user-attachments/assets/b949cb3e-c80a-4a43-b6a5-e0d6cec619a7) 33 | 34 | ## Table of Content 35 | - [🎬 Demo](#-demo) 36 | - [✨ Features](#-features) 37 | - [🚀 Quickstart with Claude Desktop](#-quickstart-with-claude-desktop) 38 | - [🔧 Available Tools](#-available-tools) 39 | - [⚠️ Security Best Practices](#%EF%B8%8F-security-best-practices) 40 | - [🔧 Account Setup](#-account-setup) 41 | - [🔌 Other MCP Clients](#-other-mcp-clients) 42 | - [🎮 Try Bright Data MCP Playgrounds](#-try-bright-data-mcp-playgrounds) 43 | - [💡 Usage Examples](#-usage-examples) 44 | - [⚠️ Troubleshooting](#%EF%B8%8F-troubleshooting) 45 | - [👨‍💻 Contributing](#-contributing) 46 | - [📞 Support](#-support) 47 | 48 | 49 | ## 🎬 Demo 50 | 51 | The videos below demonstrate a minimal use case for Claude Desktop: 52 | 53 | https://github.com/user-attachments/assets/59f6ebba-801a-49ab-8278-1b2120912e33 54 | 55 | https://github.com/user-attachments/assets/61ab0bee-fdfa-4d50-b0de-5fab96b4b91d 56 | 57 | For YouTube tutorials and demos: [Demo](https://github.com/brightdata-com/brightdata-mcp/blob/main/examples/README.md) 58 | 59 | ## ✨ Features 60 | 61 | - **Real-time Web Access**: Access up-to-date information directly from the web 62 | - **Bypass Geo-restrictions**: Access content regardless of location constraints 63 | - **Web Unlocker**: Navigate websites with bot detection protection 64 | - **Browser Control**: Optional remote browser automation capabilities 65 | - **Seamless Integration**: Works with all MCP-compatible AI assistants 66 | 67 | ## 🚀 Quickstart with Claude Desktop 68 | 69 | 1. Install `nodejs` to get the `npx` command (node.js module runner). Installation instructions can be found on the [node.js website](https://nodejs.org/en/download) 70 | 71 | 2. Go to Claude > Settings > Developer > Edit Config > claude_desktop_config.json to include the following: 72 | 73 | ```json 74 | { 75 | "mcpServers": { 76 | "Bright Data": { 77 | "command": "npx", 78 | "args": ["@brightdata/mcp"], 79 | "env": { 80 | "API_TOKEN": "", 81 | "WEB_UNLOCKER_ZONE": "", 82 | "BROWSER_ZONE": "" 83 | } 84 | } 85 | } 86 | } 87 | ``` 88 | ## 🔧 Available Tools 89 | 90 | [List of Available Tools](https://github.com/brightdata-com/brightdata-mcp/blob/main/assets/Tools.md) 91 | 92 | ## ⚠️ Security Best Practices 93 | 94 | **Important:** Always treat scraped web content as untrusted data. Never use raw scraped content directly in LLM prompts to avoid potential prompt injection risks. 95 | Instead: 96 | - Filter and validate all web data before processing 97 | - Use structured data extraction rather than raw text (web_data tools) 98 | 99 | ## 🔧 Account Setup 100 | 101 | 1. Make sure you have an account on [brightdata.com](https://brightdata.com) (new users get free credit for testing, and pay as you go options are available) 102 | 103 | 2. Get your API key from the [user settings page](https://brightdata.com/cp/setting/users) 104 | 105 | 3. (Optional) Create a custom Web Unlocker zone 106 | - By default, we create a Web Unlocker zone automatically using your API token 107 | - For more control, you can create your own Web Unlocker zone in your [control panel](https://brightdata.com/cp/zones) and specify it with the `WEB_UNLOCKER_ZONE` environment variable 108 | 109 | 4. (Optional) To enable browser control tools: 110 | - By default, the MCP tries to fetch credentials of `mcp_browser` zone. 111 | - If you don't have an `mcp_browser` zone, you can : 112 | - Create a Browser API zone in your [control panel](https://brightdata.com/cp/zones) or use an existing one and specify its name using the `BROWSER_ZONE` environment variable 113 | 114 | ![Browser API Setup](https://github.com/user-attachments/assets/cb494aa8-d84d-4bb4-a509-8afb96872afe) 115 | 116 | ## 🔌 Other MCP Clients 117 | 118 | To use this MCP server with other agent types, you should adapt the following to your specific software: 119 | 120 | - The full command to run the MCP server is `npx @brightdata/mcp` 121 | - The environment variable `API_TOKEN=` must exist when running the server 122 | - (Optional) Set `BROWSER_ZONE=` to specify a custom Browser API zone name (defaults to `mcp_browser`) 123 | 124 | ## 🔄 Breaking Changes 125 | 126 | ### Browser Authentication Update 127 | 128 | **BREAKING CHANGE:** The `BROWSER_AUTH` environment variable has been replaced with `BROWSER_ZONE`. 129 | 130 | - **Before:** Users needed to provide `BROWSER_AUTH="user:pass"` from the Browser API zone 131 | - **Now:** Users only need to specify the browser zone name with `BROWSER_ZONE="zone_name"` 132 | - **Default:** If not specified, the system uses `mcp_browser` zone automatically 133 | - **Migration:** Replace `BROWSER_AUTH` with `BROWSER_ZONE` in your configuration and specify your Browser API zone name if `mcp_browser` doesn't exists 134 | 135 | ## 🔄 Changelog 136 | 137 | [CHANGELOG.md](https://github.com/brightdata-com/brightdata-mcp/blob/main/CHANGELOG.md) 138 | 139 | ## 🎮 Try Bright Data MCP Playgrounds 140 | 141 | Want to try Bright Data MCP without setting up anything? 142 | 143 | Check out this playground on [Smithery](https://smithery.ai/server/@luminati-io/brightdata-mcp/tools): 144 | 145 | [![2025-05-06_10h44_20](https://github.com/user-attachments/assets/52517fa6-827d-4b28-b53d-f2020a13c3c4)](https://smithery.ai/server/@luminati-io/brightdata-mcp/tools) 146 | 147 | This platform provide an easy way to explore the capabilities of Bright Data MCP without any local setup. Just sign in and start experimenting with web data collection! 148 | 149 | ## 💡 Usage Examples 150 | 151 | Some example queries that this MCP server will be able to help with: 152 | 153 | - "Google some movies that are releasing soon in [your area]" 154 | - "What's Tesla's current market cap?" 155 | - "What's the Wikipedia article of the day?" 156 | - "What's the 7-day weather forecast in [your location]?" 157 | - "Of the 3 highest paid tech CEOs, how long have their careers been?" 158 | 159 | ## ⚠️ Troubleshooting 160 | 161 | ### Timeouts when using certain tools 162 | 163 | Some tools can involve reading web data, and the amount of time needed to load the page can vary by quite a lot in extreme circumstances. 164 | 165 | To ensure that your agent will be able to consume the data, set a high enough timeout in your agent settings. 166 | 167 | A value of `180s` should be enough for 99% of requests, but some sites load slower than others, so tune this to your needs. 168 | 169 | ### spawn npx ENOENT 170 | 171 | This error occurs when your system cannot find the `npx` command. To fix it: 172 | 173 | #### Finding npm/Node Path 174 | 175 | **macOS:** 176 | 177 | ``` 178 | which node 179 | ``` 180 | 181 | Shows path like `/usr/local/bin/node` 182 | 183 | **Windows:** 184 | 185 | ``` 186 | where node 187 | ``` 188 | 189 | Shows path like `C:\Program Files\nodejs\node.exe` 190 | 191 | #### Update your MCP configuration: 192 | 193 | Replace the `npx` command with the full path to Node, for example, on mac, it will look as follows: 194 | 195 | ``` 196 | "command": "/usr/local/bin/node" 197 | ``` 198 | 199 | ## 👨‍💻 Contributing 200 | 201 | We welcome contributions to help improve the Bright Data MCP! Here's how you can help: 202 | 203 | 1. **Report Issues**: If you encounter any bugs or have feature requests, please open an issue on our GitHub repository. 204 | 2. **Submit Pull Requests**: Feel free to fork the repository and submit pull requests with enhancements or bug fixes. 205 | 3. **Coding Style**: All JavaScript code should follow [Bright Data's JavaScript coding conventions](https://brightdata.com/dna/js_code). This ensures consistency across the codebase. 206 | 4. **Documentation**: Improvements to documentation, including this README, are always appreciated. 207 | 5. **Examples**: Share your use cases by contributing examples to help other users. 208 | 209 | For major changes, please open an issue first to discuss your proposed changes. This ensures your time is well spent and aligned with project goals. 210 | 211 | ## 📞 Support 212 | 213 | If you encounter any issues or have questions, please reach out to the Bright Data support team or open an issue in the repository. 214 | -------------------------------------------------------------------------------- /assets/Demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brightdata-com/brightdata-mcp/f6a59f23a1529e304f1f6c47bfbfa1a912283df4/assets/Demo.gif -------------------------------------------------------------------------------- /assets/Demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brightdata-com/brightdata-mcp/f6a59f23a1529e304f1f6c47bfbfa1a912283df4/assets/Demo2.gif -------------------------------------------------------------------------------- /assets/Demo3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brightdata-com/brightdata-mcp/f6a59f23a1529e304f1f6c47bfbfa1a912283df4/assets/Demo3.gif -------------------------------------------------------------------------------- /assets/Tools.md: -------------------------------------------------------------------------------- 1 | |Feature|Description| 2 | |---|---| 3 | |search_engine|Scrape search results from Google, Bing or Yandex. Returns SERP results in markdown (URL, title, description)| 4 | |scrape_as_markdown|Scrape a single webpage URL with advanced options for content extraction and get back the results in MarkDown language. This tool can unlock any webpage even if it uses bot detection or CAPTCHA.| 5 | |scrape_as_html|Scrape a single webpage URL with advanced options for content extraction and get back the results in HTML. This tool can unlock any webpage even if it uses bot detection or CAPTCHA.| 6 | |session_stats|Tell the user about the tool usage during this session| 7 | |web_data_amazon_product|Quickly read structured amazon product data. Requires a valid product URL with /dp/ in it. This can be a cache lookup, so it can be more reliable than scraping| 8 | |web_data_amazon_product_reviews|Quickly read structured amazon product review data. Requires a valid product URL with /dp/ in it. This can be a cache lookup, so it can be more reliable than scraping| 9 | |web_data_linkedin_person_profile|Quickly read structured linkedin people profile data. This can be a cache lookup, so it can be more reliable than scraping| 10 | |web_data_linkedin_company_profile|Quickly read structured linkedin company profile data. This can be a cache lookup, so it can be more reliable than scraping| 11 | |web_data_zoominfo_company_profile|Quickly read structured ZoomInfo company profile data. Requires a valid ZoomInfo company URL. This can be a cache lookup, so it can be more reliable than scraping| 12 | |web_data_instagram_profiles|Quickly read structured Instagram profile data. Requires a valid Instagram URL. This can be a cache lookup, so it can be more reliable than scraping| 13 | |web_data_instagram_posts|Quickly read structured Instagram post data. Requires a valid Instagram URL. This can be a cache lookup, so it can be more reliable than scraping| 14 | |web_data_instagram_reels|Quickly read structured Instagram reel data. Requires a valid Instagram URL. This can be a cache lookup, so it can be more reliable than scraping| 15 | |web_data_instagram_comments|Quickly read structured Instagram comments data. Requires a valid Instagram URL. This can be a cache lookup, so it can be more reliable than scraping| 16 | |web_data_facebook_posts|Quickly read structured Facebook post data. Requires a valid Facebook post URL. This can be a cache lookup, so it can be more reliable than scraping| 17 | |web_data_facebook_marketplace_listings|Quickly read structured Facebook marketplace listing data. Requires a valid Facebook marketplace listing URL. This can be a cache lookup, so it can be more reliable than scraping| 18 | |web_data_facebook_company_reviews|Quickly read structured Facebook company reviews data. Requires a valid Facebook company URL and number of reviews. This can be a cache lookup, so it can be more reliable than scraping| 19 | |web_data_x_posts|Quickly read structured X post data. Requires a valid X post URL. This can be a cache lookup, so it can be more reliable than scraping| 20 | |web_data_zillow_properties_listing|Quickly read structured zillow properties listing data. Requires a valid zillow properties listing URL. This can be a cache lookup, so it can be more reliable than scraping| 21 | |web_data_booking_hotel_listings|Quickly read structured booking hotel listings data. Requires a valid booking hotel listing URL. This can be a cache lookup, so it can be more reliable than scraping| 22 | |web_data_youtube_videos|Quickly read structured YouTube videos data. Requires a valid YouTube video URL. This can be a cache lookup, so it can be more reliable than scraping| 23 | |scraping_browser_navigate|Navigate a scraping browser session to a new URL| 24 | |scraping_browser_go_back|Go back to the previous page| 25 | |scraping_browser_go_forward|Go forward to the next page| 26 | |scraping_browser_click|Click on an element. Avoid calling this unless you know the element selector (you can use other tools to find those)| 27 | |scraping_browser_links|Get all links on the current page, text and selectors. It's strongly recommended that you call the links tool to check that your click target is valid| 28 | |scraping_browser_type|Type text into an element| 29 | |scraping_browser_wait_for|Wait for an element to be visible on the page| 30 | |scraping_browser_screenshot|Take a screenshot of the current page| 31 | |scraping_browser_get_html|Get the HTML content of the current page. Avoid using the full_page option unless it is important to see things like script tags since this can be large| 32 | |scraping_browser_get_text|Get the text content of the current page| 33 | |web_data_amazon_product_search|Quickly read structured amazon product search data. Requires a valid search keyword and amazon domain URL. This can be a cache lookup, so it can be more reliable than scraping| 34 | |web_data_walmart_product|Quickly read structured walmart product data. Requires a valid product URL with /ip/ in it. This can be a cache lookup, so it can be more reliable than scraping| 35 | |web_data_walmart_seller|Quickly read structured walmart seller data. Requires a valid walmart seller URL. This can be a cache lookup, so it can be more reliable than scraping| 36 | |web_data_ebay_product|Quickly read structured ebay product data. Requires a valid ebay product URL. This can be a cache lookup, so it can be more reliable than scraping| 37 | |web_data_homedepot_products|Quickly read structured homedepot product data. Requires a valid homedepot product URL. This can be a cache lookup, so it can be more reliable than scraping| 38 | |web_data_zara_products|Quickly read structured zara product data. Requires a valid zara product URL. This can be a cache lookup, so it can be more reliable than scraping| 39 | |web_data_etsy_products|Quickly read structured etsy product data. Requires a valid etsy product URL. This can be a cache lookup, so it can be more reliable than scraping| 40 | |web_data_bestbuy_products|Quickly read structured bestbuy product data. Requires a valid bestbuy product URL. This can be a cache lookup, so it can be more reliable than scraping| 41 | |web_data_linkedin_job_listings|Quickly read structured linkedin job listings data. This can be a cache lookup, so it can be more reliable than scraping| 42 | |web_data_linkedin_posts|Quickly read structured linkedin posts data. This can be a cache lookup, so it can be more reliable than scraping| 43 | |web_data_linkedin_people_search|Quickly read structured linkedin people search data. This can be a cache lookup, so it can be more reliable than scraping| 44 | |web_data_crunchbase_company|Quickly read structured crunchbase company data. This can be a cache lookup, so it can be more reliable than scraping| 45 | |web_data_facebook_events|Quickly read structured Facebook events data. Requires a valid Facebook event URL. This can be a cache lookup, so it can be more reliable than scraping| 46 | |web_data_tiktok_profiles|Quickly read structured Tiktok profiles data. Requires a valid Tiktok profile URL. This can be a cache lookup, so it can be more reliable than scraping| 47 | |web_data_tiktok_posts|Quickly read structured Tiktok post data. Requires a valid Tiktok post URL. This can be a cache lookup, so it can be more reliable than scraping| 48 | |web_data_tiktok_shop|Quickly read structured Tiktok shop data. Requires a valid Tiktok shop product URL. This can be a cache lookup, so it can be more reliable than scraping| 49 | |web_data_tiktok_comments|Quickly read structured Tiktok comments data. Requires a valid Tiktok video URL. This can be a cache lookup, so it can be more reliable than scraping| 50 | |web_data_google_maps_reviews|Quickly read structured Google maps reviews data. Requires a valid Google maps URL. This can be a cache lookup, so it can be more reliable than scraping| 51 | |web_data_google_shopping|Quickly read structured Google shopping data. Requires a valid Google shopping product URL. This can be a cache lookup, so it can be more reliable than scraping| 52 | |web_data_google_play_store|Quickly read structured Google play store data. Requires a valid Google play store app URL. This can be a cache lookup, so it can be more reliable than scraping| 53 | |web_data_apple_app_store|Quickly read structured apple app store data. Requires a valid apple app store app URL. This can be a cache lookup, so it can be more reliable than scraping| 54 | |web_data_reuter_news|Quickly read structured reuter news data. Requires a valid reuter news report URL. This can be a cache lookup, so it can be more reliable than scraping| 55 | |web_data_github_repository_file|Quickly read structured github repository data. Requires a valid github repository file URL. This can be a cache lookup, so it can be more reliable than scraping| 56 | |web_data_yahoo_finance_business|Quickly read structured yahoo finance business data. Requires a valid yahoo finance business URL. This can be a cache lookup, so it can be more reliable than scraping| 57 | |web_data_youtube_profiles|Quickly read structured youtube profiles data. Requires a valid youtube profile URL. This can be a cache lookup, so it can be more reliable than scraping| 58 | |web_data_youtube_comments|Quickly read structured youtube comments data. Requires a valid youtube video URL. This can be a cache lookup, so it can be more reliable than scraping| 59 | |web_data_reddit_posts|Quickly read structured reddit posts data. Requires a valid reddit post URL. This can be a cache lookup, so it can be more reliable than scraping| 60 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brightdata-com/brightdata-mcp/f6a59f23a1529e304f1f6c47bfbfa1a912283df4/assets/logo.png -------------------------------------------------------------------------------- /browser_session.js: -------------------------------------------------------------------------------- 1 | 'use strict'; /*jslint node:true es9:true*/ 2 | import * as playwright from 'playwright'; 3 | 4 | export class Browser_session { 5 | constructor({cdp_endpoint}){ 6 | this.cdp_endpoint = cdp_endpoint; 7 | this._domainSessions = new Map(); 8 | this._currentDomain = 'default'; 9 | } 10 | 11 | _getDomain(url) { 12 | try { 13 | const urlObj = new URL(url); 14 | return urlObj.hostname; 15 | } catch(e) { 16 | console.error(`Error extracting domain from ${url}:`, e); 17 | return 'default'; 18 | } 19 | } 20 | 21 | async _getDomainSession(domain, {log}={}) { 22 | if (!this._domainSessions.has(domain)) 23 | { 24 | this._domainSessions.set(domain, { 25 | browser: null, 26 | page: null, 27 | browserClosed: true 28 | }); 29 | } 30 | return this._domainSessions.get(domain); 31 | } 32 | 33 | async get_browser({log, domain='default'}={}){ 34 | try { 35 | const session = await this._getDomainSession(domain, {log}); 36 | if (session.browser) 37 | { 38 | try { await session.browser.contexts(); } 39 | catch(e){ 40 | log?.(`Browser connection lost for domain ${domain} (${e.message}), ` 41 | +`reconnecting...`); 42 | session.browser = null; 43 | session.page = null; 44 | session.browserClosed = true; 45 | } 46 | } 47 | if (!session.browser) 48 | { 49 | log?.(`Connecting to Bright Data Scraping Browser for domain ${domain}.`); 50 | session.browser = await playwright.chromium.connectOverCDP( 51 | this.cdp_endpoint); 52 | session.browserClosed = false; 53 | session.browser.on('disconnected', () => { 54 | log?.(`Browser disconnected for domain ${domain}`); 55 | session.browser = null; 56 | session.page = null; 57 | session.browserClosed = true; 58 | }); 59 | log?.(`Connected to Bright Data Scraping Browser for domain ${domain}`); 60 | } 61 | return session.browser; 62 | } catch(e){ 63 | console.error(`Error connecting to browser for domain ${domain}:`, e); 64 | const session = this._domainSessions.get(domain); 65 | if (session) 66 | { 67 | session.browser = null; 68 | session.page = null; 69 | session.browserClosed = true; 70 | } 71 | throw e; 72 | } 73 | } 74 | 75 | async get_page({url=null}={}){ 76 | if (url) 77 | { 78 | this._currentDomain = this._getDomain(url); 79 | } 80 | const domain = this._currentDomain; 81 | try { 82 | const session = await this._getDomainSession(domain); 83 | if (session.browserClosed || !session.page) 84 | { 85 | const browser = await this.get_browser({domain}); 86 | const existingContexts = browser.contexts(); 87 | if (existingContexts.length === 0) 88 | { 89 | const context = await browser.newContext(); 90 | session.page = await context.newPage(); 91 | } 92 | else 93 | { 94 | const existingPages = existingContexts[0]?.pages(); 95 | if (existingPages && existingPages.length > 0) 96 | session.page = existingPages[0]; 97 | else 98 | session.page = await existingContexts[0].newPage(); 99 | } 100 | session.browserClosed = false; 101 | session.page.once('close', ()=>{ 102 | session.page = null; 103 | }); 104 | } 105 | return session.page; 106 | } catch(e){ 107 | console.error(`Error getting page for domain ${domain}:`, e); 108 | const session = this._domainSessions.get(domain); 109 | if (session) 110 | { 111 | session.browser = null; 112 | session.page = null; 113 | session.browserClosed = true; 114 | } 115 | throw e; 116 | } 117 | } 118 | 119 | async close(domain=null){ 120 | if (domain) { 121 | const session = this._domainSessions.get(domain); 122 | if (session && session.browser) 123 | { 124 | try { await session.browser.close(); } 125 | catch(e){ console.error(`Error closing browser for domain ${domain}:`, e); } 126 | session.browser = null; 127 | session.page = null; 128 | session.browserClosed = true; 129 | this._domainSessions.delete(domain); 130 | } 131 | } else { 132 | for (const [domain, session] of this._domainSessions.entries()) { 133 | if (session.browser) 134 | { 135 | try { await session.browser.close(); } 136 | catch(e){ console.error(`Error closing browser for domain ${domain}:`, e); } 137 | session.browser = null; 138 | session.page = null; 139 | session.browserClosed = true; 140 | } 141 | } 142 | this._domainSessions.clear(); 143 | } 144 | if (!domain) 145 | { 146 | this._currentDomain = 'default'; 147 | } 148 | } 149 | } 150 | 151 | -------------------------------------------------------------------------------- /browser_tools.js: -------------------------------------------------------------------------------- 1 | 'use strict'; /*jslint node:true es9:true*/ 2 | import {UserError, imageContent as image_content} from 'fastmcp'; 3 | import {z} from 'zod'; 4 | import axios from 'axios'; 5 | import {Browser_session} from './browser_session.js'; 6 | let browser_zone = process.env.BROWSER_ZONE || 'mcp_browser'; 7 | 8 | let open_session; 9 | const require_browser = async()=>{ 10 | if (!open_session) 11 | { 12 | open_session = new Browser_session({ 13 | cdp_endpoint: await calculate_cdp_endpoint(), 14 | }); 15 | } 16 | return open_session; 17 | }; 18 | 19 | const calculate_cdp_endpoint = async()=>{ 20 | try { 21 | const status_response = await axios({ 22 | url: 'https://api.brightdata.com/status', 23 | method: 'GET', 24 | headers: {authorization: `Bearer ${process.env.API_TOKEN}`}, 25 | }); 26 | const customer = status_response.data.customer; 27 | const password_response = await axios({ 28 | url: `https://api.brightdata.com/zone/passwords?zone=${browser_zone}`, 29 | method: 'GET', 30 | headers: {authorization: `Bearer ${process.env.API_TOKEN}`}, 31 | }); 32 | const password = password_response.data.passwords[0]; 33 | 34 | return `wss://brd-customer-${customer}-zone-${browser_zone}:` 35 | +`${password}@brd.superproxy.io:9222`; 36 | } catch(e){ 37 | if (e.response?.status===422) 38 | throw new Error(`Browser zone '${browser_zone}' does not exist`); 39 | throw new Error(`Error retrieving browser credentials: ${e.message}`); 40 | } 41 | }; 42 | 43 | let scraping_browser_navigate = { 44 | name: 'scraping_browser_navigate', 45 | description: 'Navigate a scraping browser session to a new URL', 46 | parameters: z.object({ 47 | url: z.string().describe('The URL to navigate to'), 48 | }), 49 | execute: async({url})=>{ 50 | const page = await (await require_browser()).get_page({url}); 51 | try { 52 | await page.goto(url, { 53 | timeout: 120000, 54 | waitUntil: 'domcontentloaded', 55 | }); 56 | return [ 57 | `Successfully navigated to ${url}`, 58 | `Title: ${await page.title()}`, 59 | `URL: ${page.url()}`, 60 | ].join('\n'); 61 | } catch(e){ 62 | throw new UserError(`Error navigating to ${url}: ${e}`); 63 | } 64 | }, 65 | }; 66 | 67 | let scraping_browser_go_back = { 68 | name: 'scraping_browser_go_back', 69 | description: 'Go back to the previous page', 70 | parameters: z.object({}), 71 | execute: async()=>{ 72 | const page = await (await require_browser()).get_page(); 73 | try { 74 | await page.goBack(); 75 | return [ 76 | 'Successfully navigated back', 77 | `Title: ${await page.title()}`, 78 | `URL: ${page.url()}`, 79 | ].join('\n'); 80 | } catch(e){ 81 | throw new UserError(`Error navigating back: ${e}`); 82 | } 83 | }, 84 | }; 85 | 86 | const scraping_browser_go_forward = { 87 | name: 'scraping_browser_go_forward', 88 | description: 'Go forward to the next page', 89 | parameters: z.object({}), 90 | execute: async()=>{ 91 | const page = await (await require_browser()).get_page(); 92 | try { 93 | await page.goForward(); 94 | return [ 95 | 'Successfully navigated forward', 96 | `Title: ${await page.title()}`, 97 | `URL: ${page.url()}`, 98 | ].join('\n'); 99 | } catch(e){ 100 | throw new UserError(`Error navigating forward: ${e}`); 101 | } 102 | }, 103 | }; 104 | 105 | let scraping_browser_click = { 106 | name: 'scraping_browser_click', 107 | description: [ 108 | 'Click on an element.', 109 | 'Avoid calling this unless you know the element selector (you can use ' 110 | +'other tools to find those)', 111 | ].join('\n'), 112 | parameters: z.object({ 113 | selector: z.string().describe('CSS selector for the element to click'), 114 | }), 115 | execute: async({selector})=>{ 116 | const page = await (await require_browser()).get_page(); 117 | try { 118 | await page.click(selector, {timeout: 5000}); 119 | return `Successfully clicked element: ${selector}`; 120 | } catch(e){ 121 | throw new UserError(`Error clicking element ${selector}: ${e}`); 122 | } 123 | }, 124 | }; 125 | 126 | let scraping_browser_links = { 127 | name: 'scraping_browser_links', 128 | description: [ 129 | 'Get all links on the current page, text and selectors', 130 | "It's strongly recommended that you call the links tool to check that " 131 | +'your click target is valid', 132 | ].join('\n'), 133 | parameters: z.object({}), 134 | execute: async()=>{ 135 | const page = await (await require_browser()).get_page(); 136 | try { 137 | const links = await page.$$eval('a', elements=>{ 138 | return elements.map(el=>{ 139 | return { 140 | text: el.innerText, 141 | href: el.href, 142 | selector: el.outerHTML, 143 | }; 144 | }); 145 | }); 146 | return JSON.stringify(links, null, 2); 147 | } catch(e){ 148 | throw new UserError(`Error getting links: ${e}`); 149 | } 150 | }, 151 | }; 152 | 153 | let scraping_browser_type = { 154 | name: 'scraping_browser_type', 155 | description: 'Type text into an element', 156 | parameters: z.object({ 157 | selector: z.string() 158 | .describe('CSS selector for the element to type into'), 159 | text: z.string().describe('Text to type'), 160 | submit: z.boolean().optional() 161 | .describe('Whether to submit the form after typing (press Enter)'), 162 | }), 163 | execute: async({selector, text, submit})=>{ 164 | const page = await (await require_browser()).get_page(); 165 | try { 166 | await page.fill(selector, text); 167 | if (submit) 168 | await page.press(selector, 'Enter'); 169 | return `Successfully typed "${text}" into element: ` 170 | +`${selector}${submit ? ' and submitted the form' : ''}`; 171 | } catch(e){ 172 | throw new UserError(`Error typing into element ${selector}: ${e}`); 173 | } 174 | }, 175 | }; 176 | 177 | let scraping_browser_wait_for = { 178 | name: 'scraping_browser_wait_for', 179 | description: 'Wait for an element to be visible on the page', 180 | parameters: z.object({ 181 | selector: z.string().describe('CSS selector to wait for'), 182 | timeout: z.number().optional() 183 | .describe('Maximum time to wait in milliseconds (default: 30000)'), 184 | }), 185 | execute: async({selector, timeout})=>{ 186 | const page = await (await require_browser()).get_page(); 187 | try { 188 | await page.waitForSelector(selector, {timeout: timeout||30000}); 189 | return `Successfully waited for element: ${selector}`; 190 | } catch(e){ 191 | throw new UserError(`Error waiting for element ${selector}: ${e}`); 192 | } 193 | }, 194 | }; 195 | 196 | let scraping_browser_screenshot = { 197 | name: 'scraping_browser_screenshot', 198 | description: 'Take a screenshot of the current page', 199 | parameters: z.object({ 200 | full_page: z.boolean().optional().describe([ 201 | 'Whether to screenshot the full page (default: false)', 202 | 'You should avoid fullscreen if it\'s not important, since the ' 203 | +'images can be quite large', 204 | ].join('\n')), 205 | }), 206 | execute: async({full_page = false})=>{ 207 | const page = await (await require_browser()).get_page(); 208 | try { 209 | const buffer = await page.screenshot({fullPage: full_page}); 210 | return image_content({buffer}); 211 | } catch(e){ 212 | throw new UserError(`Error taking screenshot: ${e}`); 213 | } 214 | }, 215 | }; 216 | 217 | let scraping_browser_get_html = { 218 | name: 'scraping_browser_get_html', 219 | description: 'Get the HTML content of the current page. Avoid using the ' 220 | +'full_page option unless it is important to see things like script tags ' 221 | +'since this can be large', 222 | parameters: z.object({ 223 | full_page: z.boolean().optional().describe([ 224 | 'Whether to get the full page HTML including head and script tags', 225 | 'Avoid this if you only need the extra HTML, since it can be ' 226 | +'quite large', 227 | ].join('\n')), 228 | }), 229 | execute: async({full_page = false})=>{ 230 | const page = await (await require_browser()).get_page(); 231 | try { 232 | if (!full_page) 233 | return await page.$eval('body', body=>body.innerHTML); 234 | const html = await page.content(); 235 | if (!full_page && html) 236 | return html.split('')[1].split('')[0]; 237 | return html; 238 | } catch(e){ 239 | throw new UserError(`Error getting HTML content: ${e}`); 240 | } 241 | }, 242 | }; 243 | 244 | let scraping_browser_get_text = { 245 | name: 'scraping_browser_get_text', 246 | description: 'Get the text content of the current page', 247 | parameters: z.object({}), 248 | execute: async()=>{ 249 | const page = await (await require_browser()).get_page(); 250 | try { return await page.$eval('body', body=>body.innerText); } 251 | catch(e){ throw new UserError(`Error getting text content: ${e}`); } 252 | }, 253 | }; 254 | 255 | let scraping_browser_activation_instructions = { 256 | name: 'scraping_browser_activation_instructions', 257 | description: 'Instructions for activating the scraping browser', 258 | parameters: z.object({}), 259 | execute: async()=>{ 260 | return 'You need to run this MCP server with the BROWSER_AUTH ' 261 | +'environment varialbe before the browser tools will become ' 262 | +'available'; 263 | }, 264 | }; 265 | 266 | let browser_credentials; 267 | try { 268 | browser_credentials = process.env.API_TOKEN ? 269 | await calculate_cdp_endpoint() : null; 270 | } catch(e){ 271 | browser_credentials = null; 272 | } 273 | export const tools = browser_credentials ? [ 274 | scraping_browser_navigate, 275 | scraping_browser_go_back, 276 | scraping_browser_go_forward, 277 | scraping_browser_links, 278 | scraping_browser_click, 279 | scraping_browser_type, 280 | scraping_browser_wait_for, 281 | scraping_browser_screenshot, 282 | scraping_browser_get_text, 283 | scraping_browser_get_html, 284 | ] : [scraping_browser_activation_instructions]; 285 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # MCP Usage Examples 2 | 3 | A curated list of community demos using Bright Data's MCP server. 4 | 5 | ## 🧠 Notable Examples 6 | 7 | - **AI voice agent that closed 4 deals & made $596 overnight 🤑** 8 | [📹 YouTube Demo](https://www.youtube.com/watch?v=YGzT3sVdwdY) 9 | 10 | [💻 GitHub Repo](https://github.com/llSourcell/my_ai_intern) 11 | 12 | - **Langgraph with mcp-adapters demo** 13 | 14 | [📹 YouTube Demo](https://www.youtube.com/watch?v=6DXuadyaJ4g) 15 | 16 | [💻 Source Code](https://github.com/techwithtim/BrightDataMCPServerAgent) 17 | 18 | - **Researcher Agent built with Google ADK that is connected to Bright Data's MCP to fetch real-time data** 19 | 20 | [📹 YouTube Demo](https://www.youtube.com/watch?v=r7WG6dXWdUI) 21 | 22 | [💻Source Code](https://github.com/MeirKaD/MCP_ADK) 23 | 24 | - **Replacing 3 MCP servers with our MCP server to avoid getting blocked 🤯** 25 | 26 | [📹 YouTube Demo](https://www.youtube.com/watch?v=0xmE0OJrNmg) 27 | 28 | - **Scrape ANY Website In Realtime With This Powerful AI MCP Server** 29 | 30 | [📹 YouTube Demo](https://www.youtube.com/watch?v=bL5JIeGL3J0) 31 | 32 | --- 33 | 34 | Got a cool example? Open a PR or contact us! 35 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@brightdata/mcp", 3 | "version": "2.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@brightdata/mcp", 9 | "version": "2.0.0", 10 | "license": "MIT", 11 | "dependencies": { 12 | "axios": "^1.8.4", 13 | "fastmcp": "^1.27.6", 14 | "playwright": "^1.51.1", 15 | "zod": "^3.24.2" 16 | }, 17 | "bin": { 18 | "mcp": "server.js" 19 | } 20 | }, 21 | "node_modules/@modelcontextprotocol/sdk": { 22 | "version": "1.11.2", 23 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.2.tgz", 24 | "integrity": "sha512-H9vwztj5OAqHg9GockCQC06k1natgcxWQSRpQcPJf6i5+MWBzfKkRtxGbjQf0X2ihii0ffLZCRGbYV2f2bjNCQ==", 25 | "dependencies": { 26 | "content-type": "^1.0.5", 27 | "cors": "^2.8.5", 28 | "cross-spawn": "^7.0.3", 29 | "eventsource": "^3.0.2", 30 | "express": "^5.0.1", 31 | "express-rate-limit": "^7.5.0", 32 | "pkce-challenge": "^5.0.0", 33 | "raw-body": "^3.0.0", 34 | "zod": "^3.23.8", 35 | "zod-to-json-schema": "^3.24.1" 36 | }, 37 | "engines": { 38 | "node": ">=18" 39 | } 40 | }, 41 | "node_modules/@sec-ant/readable-stream": { 42 | "version": "0.4.1", 43 | "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", 44 | "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==" 45 | }, 46 | "node_modules/@sindresorhus/merge-streams": { 47 | "version": "4.0.0", 48 | "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", 49 | "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", 50 | "engines": { 51 | "node": ">=18" 52 | }, 53 | "funding": { 54 | "url": "https://github.com/sponsors/sindresorhus" 55 | } 56 | }, 57 | "node_modules/@standard-schema/spec": { 58 | "version": "1.0.0", 59 | "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", 60 | "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==" 61 | }, 62 | "node_modules/@tokenizer/inflate": { 63 | "version": "0.2.7", 64 | "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", 65 | "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", 66 | "dependencies": { 67 | "debug": "^4.4.0", 68 | "fflate": "^0.8.2", 69 | "token-types": "^6.0.0" 70 | }, 71 | "engines": { 72 | "node": ">=18" 73 | }, 74 | "funding": { 75 | "type": "github", 76 | "url": "https://github.com/sponsors/Borewit" 77 | } 78 | }, 79 | "node_modules/@tokenizer/token": { 80 | "version": "0.3.0", 81 | "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", 82 | "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" 83 | }, 84 | "node_modules/accepts": { 85 | "version": "2.0.0", 86 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", 87 | "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", 88 | "dependencies": { 89 | "mime-types": "^3.0.0", 90 | "negotiator": "^1.0.0" 91 | }, 92 | "engines": { 93 | "node": ">= 0.6" 94 | } 95 | }, 96 | "node_modules/ansi-regex": { 97 | "version": "5.0.1", 98 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 99 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 100 | "engines": { 101 | "node": ">=8" 102 | } 103 | }, 104 | "node_modules/ansi-styles": { 105 | "version": "4.3.0", 106 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 107 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 108 | "dependencies": { 109 | "color-convert": "^2.0.1" 110 | }, 111 | "engines": { 112 | "node": ">=8" 113 | }, 114 | "funding": { 115 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 116 | } 117 | }, 118 | "node_modules/asynckit": { 119 | "version": "0.4.0", 120 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 121 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 122 | }, 123 | "node_modules/axios": { 124 | "version": "1.8.4", 125 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", 126 | "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", 127 | "dependencies": { 128 | "follow-redirects": "^1.15.6", 129 | "form-data": "^4.0.0", 130 | "proxy-from-env": "^1.1.0" 131 | } 132 | }, 133 | "node_modules/body-parser": { 134 | "version": "2.2.0", 135 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", 136 | "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", 137 | "dependencies": { 138 | "bytes": "^3.1.2", 139 | "content-type": "^1.0.5", 140 | "debug": "^4.4.0", 141 | "http-errors": "^2.0.0", 142 | "iconv-lite": "^0.6.3", 143 | "on-finished": "^2.4.1", 144 | "qs": "^6.14.0", 145 | "raw-body": "^3.0.0", 146 | "type-is": "^2.0.0" 147 | }, 148 | "engines": { 149 | "node": ">=18" 150 | } 151 | }, 152 | "node_modules/bytes": { 153 | "version": "3.1.2", 154 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 155 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 156 | "engines": { 157 | "node": ">= 0.8" 158 | } 159 | }, 160 | "node_modules/call-bind-apply-helpers": { 161 | "version": "1.0.2", 162 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 163 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 164 | "dependencies": { 165 | "es-errors": "^1.3.0", 166 | "function-bind": "^1.1.2" 167 | }, 168 | "engines": { 169 | "node": ">= 0.4" 170 | } 171 | }, 172 | "node_modules/call-bound": { 173 | "version": "1.0.4", 174 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", 175 | "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", 176 | "dependencies": { 177 | "call-bind-apply-helpers": "^1.0.2", 178 | "get-intrinsic": "^1.3.0" 179 | }, 180 | "engines": { 181 | "node": ">= 0.4" 182 | }, 183 | "funding": { 184 | "url": "https://github.com/sponsors/ljharb" 185 | } 186 | }, 187 | "node_modules/cliui": { 188 | "version": "8.0.1", 189 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 190 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 191 | "dependencies": { 192 | "string-width": "^4.2.0", 193 | "strip-ansi": "^6.0.1", 194 | "wrap-ansi": "^7.0.0" 195 | }, 196 | "engines": { 197 | "node": ">=12" 198 | } 199 | }, 200 | "node_modules/color-convert": { 201 | "version": "2.0.1", 202 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 203 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 204 | "dependencies": { 205 | "color-name": "~1.1.4" 206 | }, 207 | "engines": { 208 | "node": ">=7.0.0" 209 | } 210 | }, 211 | "node_modules/color-name": { 212 | "version": "1.1.4", 213 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 214 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" 215 | }, 216 | "node_modules/combined-stream": { 217 | "version": "1.0.8", 218 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 219 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 220 | "dependencies": { 221 | "delayed-stream": "~1.0.0" 222 | }, 223 | "engines": { 224 | "node": ">= 0.8" 225 | } 226 | }, 227 | "node_modules/content-disposition": { 228 | "version": "1.0.0", 229 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", 230 | "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", 231 | "dependencies": { 232 | "safe-buffer": "5.2.1" 233 | }, 234 | "engines": { 235 | "node": ">= 0.6" 236 | } 237 | }, 238 | "node_modules/content-type": { 239 | "version": "1.0.5", 240 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 241 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 242 | "engines": { 243 | "node": ">= 0.6" 244 | } 245 | }, 246 | "node_modules/cookie": { 247 | "version": "0.7.2", 248 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 249 | "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 250 | "engines": { 251 | "node": ">= 0.6" 252 | } 253 | }, 254 | "node_modules/cookie-signature": { 255 | "version": "1.2.2", 256 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", 257 | "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", 258 | "engines": { 259 | "node": ">=6.6.0" 260 | } 261 | }, 262 | "node_modules/cors": { 263 | "version": "2.8.5", 264 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 265 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 266 | "dependencies": { 267 | "object-assign": "^4", 268 | "vary": "^1" 269 | }, 270 | "engines": { 271 | "node": ">= 0.10" 272 | } 273 | }, 274 | "node_modules/cross-spawn": { 275 | "version": "7.0.6", 276 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 277 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 278 | "dependencies": { 279 | "path-key": "^3.1.0", 280 | "shebang-command": "^2.0.0", 281 | "which": "^2.0.1" 282 | }, 283 | "engines": { 284 | "node": ">= 8" 285 | } 286 | }, 287 | "node_modules/debug": { 288 | "version": "4.4.0", 289 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 290 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 291 | "dependencies": { 292 | "ms": "^2.1.3" 293 | }, 294 | "engines": { 295 | "node": ">=6.0" 296 | }, 297 | "peerDependenciesMeta": { 298 | "supports-color": { 299 | "optional": true 300 | } 301 | } 302 | }, 303 | "node_modules/delayed-stream": { 304 | "version": "1.0.0", 305 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 306 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 307 | "engines": { 308 | "node": ">=0.4.0" 309 | } 310 | }, 311 | "node_modules/depd": { 312 | "version": "2.0.0", 313 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 314 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 315 | "engines": { 316 | "node": ">= 0.8" 317 | } 318 | }, 319 | "node_modules/dunder-proto": { 320 | "version": "1.0.1", 321 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 322 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 323 | "dependencies": { 324 | "call-bind-apply-helpers": "^1.0.1", 325 | "es-errors": "^1.3.0", 326 | "gopd": "^1.2.0" 327 | }, 328 | "engines": { 329 | "node": ">= 0.4" 330 | } 331 | }, 332 | "node_modules/ee-first": { 333 | "version": "1.1.1", 334 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 335 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 336 | }, 337 | "node_modules/emoji-regex": { 338 | "version": "8.0.0", 339 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 340 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" 341 | }, 342 | "node_modules/encodeurl": { 343 | "version": "2.0.0", 344 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 345 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 346 | "engines": { 347 | "node": ">= 0.8" 348 | } 349 | }, 350 | "node_modules/es-define-property": { 351 | "version": "1.0.1", 352 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 353 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 354 | "engines": { 355 | "node": ">= 0.4" 356 | } 357 | }, 358 | "node_modules/es-errors": { 359 | "version": "1.3.0", 360 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 361 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 362 | "engines": { 363 | "node": ">= 0.4" 364 | } 365 | }, 366 | "node_modules/es-object-atoms": { 367 | "version": "1.1.1", 368 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 369 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 370 | "dependencies": { 371 | "es-errors": "^1.3.0" 372 | }, 373 | "engines": { 374 | "node": ">= 0.4" 375 | } 376 | }, 377 | "node_modules/es-set-tostringtag": { 378 | "version": "2.1.0", 379 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", 380 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", 381 | "dependencies": { 382 | "es-errors": "^1.3.0", 383 | "get-intrinsic": "^1.2.6", 384 | "has-tostringtag": "^1.0.2", 385 | "hasown": "^2.0.2" 386 | }, 387 | "engines": { 388 | "node": ">= 0.4" 389 | } 390 | }, 391 | "node_modules/escalade": { 392 | "version": "3.2.0", 393 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", 394 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", 395 | "engines": { 396 | "node": ">=6" 397 | } 398 | }, 399 | "node_modules/escape-html": { 400 | "version": "1.0.3", 401 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 402 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 403 | }, 404 | "node_modules/etag": { 405 | "version": "1.8.1", 406 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 407 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 408 | "engines": { 409 | "node": ">= 0.6" 410 | } 411 | }, 412 | "node_modules/eventsource": { 413 | "version": "3.0.7", 414 | "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", 415 | "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", 416 | "dependencies": { 417 | "eventsource-parser": "^3.0.1" 418 | }, 419 | "engines": { 420 | "node": ">=18.0.0" 421 | } 422 | }, 423 | "node_modules/eventsource-parser": { 424 | "version": "3.0.1", 425 | "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz", 426 | "integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==", 427 | "engines": { 428 | "node": ">=18.0.0" 429 | } 430 | }, 431 | "node_modules/execa": { 432 | "version": "9.5.2", 433 | "resolved": "https://registry.npmjs.org/execa/-/execa-9.5.2.tgz", 434 | "integrity": "sha512-EHlpxMCpHWSAh1dgS6bVeoLAXGnJNdR93aabr4QCGbzOM73o5XmRfM/e5FUqsw3aagP8S8XEWUWFAxnRBnAF0Q==", 435 | "dependencies": { 436 | "@sindresorhus/merge-streams": "^4.0.0", 437 | "cross-spawn": "^7.0.3", 438 | "figures": "^6.1.0", 439 | "get-stream": "^9.0.0", 440 | "human-signals": "^8.0.0", 441 | "is-plain-obj": "^4.1.0", 442 | "is-stream": "^4.0.1", 443 | "npm-run-path": "^6.0.0", 444 | "pretty-ms": "^9.0.0", 445 | "signal-exit": "^4.1.0", 446 | "strip-final-newline": "^4.0.0", 447 | "yoctocolors": "^2.0.0" 448 | }, 449 | "engines": { 450 | "node": "^18.19.0 || >=20.5.0" 451 | }, 452 | "funding": { 453 | "url": "https://github.com/sindresorhus/execa?sponsor=1" 454 | } 455 | }, 456 | "node_modules/express": { 457 | "version": "5.1.0", 458 | "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", 459 | "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", 460 | "dependencies": { 461 | "accepts": "^2.0.0", 462 | "body-parser": "^2.2.0", 463 | "content-disposition": "^1.0.0", 464 | "content-type": "^1.0.5", 465 | "cookie": "^0.7.1", 466 | "cookie-signature": "^1.2.1", 467 | "debug": "^4.4.0", 468 | "encodeurl": "^2.0.0", 469 | "escape-html": "^1.0.3", 470 | "etag": "^1.8.1", 471 | "finalhandler": "^2.1.0", 472 | "fresh": "^2.0.0", 473 | "http-errors": "^2.0.0", 474 | "merge-descriptors": "^2.0.0", 475 | "mime-types": "^3.0.0", 476 | "on-finished": "^2.4.1", 477 | "once": "^1.4.0", 478 | "parseurl": "^1.3.3", 479 | "proxy-addr": "^2.0.7", 480 | "qs": "^6.14.0", 481 | "range-parser": "^1.2.1", 482 | "router": "^2.2.0", 483 | "send": "^1.1.0", 484 | "serve-static": "^2.2.0", 485 | "statuses": "^2.0.1", 486 | "type-is": "^2.0.1", 487 | "vary": "^1.1.2" 488 | }, 489 | "engines": { 490 | "node": ">= 18" 491 | }, 492 | "funding": { 493 | "type": "opencollective", 494 | "url": "https://opencollective.com/express" 495 | } 496 | }, 497 | "node_modules/express-rate-limit": { 498 | "version": "7.5.0", 499 | "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", 500 | "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", 501 | "engines": { 502 | "node": ">= 16" 503 | }, 504 | "funding": { 505 | "url": "https://github.com/sponsors/express-rate-limit" 506 | }, 507 | "peerDependencies": { 508 | "express": "^4.11 || 5 || ^5.0.0-beta.1" 509 | } 510 | }, 511 | "node_modules/fastmcp": { 512 | "version": "1.27.6", 513 | "resolved": "https://registry.npmjs.org/fastmcp/-/fastmcp-1.27.6.tgz", 514 | "integrity": "sha512-LLwp6SvHUKj6MLW+CjJjKU1+ioYA1vIrfBSoY5tu/qLG8dt1KzjkmU+EnxQYRdF1eeqtSsgp+35ZoVIxA7ysjQ==", 515 | "dependencies": { 516 | "@modelcontextprotocol/sdk": "^1.10.2", 517 | "@standard-schema/spec": "^1.0.0", 518 | "execa": "^9.5.2", 519 | "file-type": "^20.4.1", 520 | "fuse.js": "^7.1.0", 521 | "mcp-proxy": "^2.13.1", 522 | "strict-event-emitter-types": "^2.0.0", 523 | "undici": "^7.8.0", 524 | "uri-templates": "^0.2.0", 525 | "xsschema": "0.2.0-beta.3", 526 | "yargs": "^17.7.2", 527 | "zod": "^3.24.3", 528 | "zod-to-json-schema": "^3.24.5" 529 | }, 530 | "bin": { 531 | "fastmcp": "dist/bin/fastmcp.js" 532 | } 533 | }, 534 | "node_modules/fflate": { 535 | "version": "0.8.2", 536 | "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", 537 | "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" 538 | }, 539 | "node_modules/figures": { 540 | "version": "6.1.0", 541 | "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", 542 | "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", 543 | "dependencies": { 544 | "is-unicode-supported": "^2.0.0" 545 | }, 546 | "engines": { 547 | "node": ">=18" 548 | }, 549 | "funding": { 550 | "url": "https://github.com/sponsors/sindresorhus" 551 | } 552 | }, 553 | "node_modules/file-type": { 554 | "version": "20.4.1", 555 | "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", 556 | "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", 557 | "dependencies": { 558 | "@tokenizer/inflate": "^0.2.6", 559 | "strtok3": "^10.2.0", 560 | "token-types": "^6.0.0", 561 | "uint8array-extras": "^1.4.0" 562 | }, 563 | "engines": { 564 | "node": ">=18" 565 | }, 566 | "funding": { 567 | "url": "https://github.com/sindresorhus/file-type?sponsor=1" 568 | } 569 | }, 570 | "node_modules/finalhandler": { 571 | "version": "2.1.0", 572 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", 573 | "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", 574 | "dependencies": { 575 | "debug": "^4.4.0", 576 | "encodeurl": "^2.0.0", 577 | "escape-html": "^1.0.3", 578 | "on-finished": "^2.4.1", 579 | "parseurl": "^1.3.3", 580 | "statuses": "^2.0.1" 581 | }, 582 | "engines": { 583 | "node": ">= 0.8" 584 | } 585 | }, 586 | "node_modules/follow-redirects": { 587 | "version": "1.15.9", 588 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 589 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 590 | "funding": [ 591 | { 592 | "type": "individual", 593 | "url": "https://github.com/sponsors/RubenVerborgh" 594 | } 595 | ], 596 | "engines": { 597 | "node": ">=4.0" 598 | }, 599 | "peerDependenciesMeta": { 600 | "debug": { 601 | "optional": true 602 | } 603 | } 604 | }, 605 | "node_modules/form-data": { 606 | "version": "4.0.2", 607 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", 608 | "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", 609 | "dependencies": { 610 | "asynckit": "^0.4.0", 611 | "combined-stream": "^1.0.8", 612 | "es-set-tostringtag": "^2.1.0", 613 | "mime-types": "^2.1.12" 614 | }, 615 | "engines": { 616 | "node": ">= 6" 617 | } 618 | }, 619 | "node_modules/form-data/node_modules/mime-db": { 620 | "version": "1.52.0", 621 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 622 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 623 | "engines": { 624 | "node": ">= 0.6" 625 | } 626 | }, 627 | "node_modules/form-data/node_modules/mime-types": { 628 | "version": "2.1.35", 629 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 630 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 631 | "dependencies": { 632 | "mime-db": "1.52.0" 633 | }, 634 | "engines": { 635 | "node": ">= 0.6" 636 | } 637 | }, 638 | "node_modules/forwarded": { 639 | "version": "0.2.0", 640 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 641 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 642 | "engines": { 643 | "node": ">= 0.6" 644 | } 645 | }, 646 | "node_modules/fresh": { 647 | "version": "2.0.0", 648 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", 649 | "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", 650 | "engines": { 651 | "node": ">= 0.8" 652 | } 653 | }, 654 | "node_modules/fsevents": { 655 | "version": "2.3.2", 656 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", 657 | "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", 658 | "hasInstallScript": true, 659 | "optional": true, 660 | "os": [ 661 | "darwin" 662 | ], 663 | "engines": { 664 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 665 | } 666 | }, 667 | "node_modules/function-bind": { 668 | "version": "1.1.2", 669 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 670 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 671 | "funding": { 672 | "url": "https://github.com/sponsors/ljharb" 673 | } 674 | }, 675 | "node_modules/fuse.js": { 676 | "version": "7.1.0", 677 | "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz", 678 | "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==", 679 | "engines": { 680 | "node": ">=10" 681 | } 682 | }, 683 | "node_modules/get-caller-file": { 684 | "version": "2.0.5", 685 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 686 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 687 | "engines": { 688 | "node": "6.* || 8.* || >= 10.*" 689 | } 690 | }, 691 | "node_modules/get-intrinsic": { 692 | "version": "1.3.0", 693 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 694 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 695 | "dependencies": { 696 | "call-bind-apply-helpers": "^1.0.2", 697 | "es-define-property": "^1.0.1", 698 | "es-errors": "^1.3.0", 699 | "es-object-atoms": "^1.1.1", 700 | "function-bind": "^1.1.2", 701 | "get-proto": "^1.0.1", 702 | "gopd": "^1.2.0", 703 | "has-symbols": "^1.1.0", 704 | "hasown": "^2.0.2", 705 | "math-intrinsics": "^1.1.0" 706 | }, 707 | "engines": { 708 | "node": ">= 0.4" 709 | }, 710 | "funding": { 711 | "url": "https://github.com/sponsors/ljharb" 712 | } 713 | }, 714 | "node_modules/get-proto": { 715 | "version": "1.0.1", 716 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 717 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 718 | "dependencies": { 719 | "dunder-proto": "^1.0.1", 720 | "es-object-atoms": "^1.0.0" 721 | }, 722 | "engines": { 723 | "node": ">= 0.4" 724 | } 725 | }, 726 | "node_modules/get-stream": { 727 | "version": "9.0.1", 728 | "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", 729 | "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", 730 | "dependencies": { 731 | "@sec-ant/readable-stream": "^0.4.1", 732 | "is-stream": "^4.0.1" 733 | }, 734 | "engines": { 735 | "node": ">=18" 736 | }, 737 | "funding": { 738 | "url": "https://github.com/sponsors/sindresorhus" 739 | } 740 | }, 741 | "node_modules/gopd": { 742 | "version": "1.2.0", 743 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 744 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 745 | "engines": { 746 | "node": ">= 0.4" 747 | }, 748 | "funding": { 749 | "url": "https://github.com/sponsors/ljharb" 750 | } 751 | }, 752 | "node_modules/has-symbols": { 753 | "version": "1.1.0", 754 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 755 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 756 | "engines": { 757 | "node": ">= 0.4" 758 | }, 759 | "funding": { 760 | "url": "https://github.com/sponsors/ljharb" 761 | } 762 | }, 763 | "node_modules/has-tostringtag": { 764 | "version": "1.0.2", 765 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", 766 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", 767 | "dependencies": { 768 | "has-symbols": "^1.0.3" 769 | }, 770 | "engines": { 771 | "node": ">= 0.4" 772 | }, 773 | "funding": { 774 | "url": "https://github.com/sponsors/ljharb" 775 | } 776 | }, 777 | "node_modules/hasown": { 778 | "version": "2.0.2", 779 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 780 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 781 | "dependencies": { 782 | "function-bind": "^1.1.2" 783 | }, 784 | "engines": { 785 | "node": ">= 0.4" 786 | } 787 | }, 788 | "node_modules/http-errors": { 789 | "version": "2.0.0", 790 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 791 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 792 | "dependencies": { 793 | "depd": "2.0.0", 794 | "inherits": "2.0.4", 795 | "setprototypeof": "1.2.0", 796 | "statuses": "2.0.1", 797 | "toidentifier": "1.0.1" 798 | }, 799 | "engines": { 800 | "node": ">= 0.8" 801 | } 802 | }, 803 | "node_modules/human-signals": { 804 | "version": "8.0.1", 805 | "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", 806 | "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", 807 | "engines": { 808 | "node": ">=18.18.0" 809 | } 810 | }, 811 | "node_modules/iconv-lite": { 812 | "version": "0.6.3", 813 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 814 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 815 | "dependencies": { 816 | "safer-buffer": ">= 2.1.2 < 3.0.0" 817 | }, 818 | "engines": { 819 | "node": ">=0.10.0" 820 | } 821 | }, 822 | "node_modules/ieee754": { 823 | "version": "1.2.1", 824 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 825 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 826 | "funding": [ 827 | { 828 | "type": "github", 829 | "url": "https://github.com/sponsors/feross" 830 | }, 831 | { 832 | "type": "patreon", 833 | "url": "https://www.patreon.com/feross" 834 | }, 835 | { 836 | "type": "consulting", 837 | "url": "https://feross.org/support" 838 | } 839 | ] 840 | }, 841 | "node_modules/inherits": { 842 | "version": "2.0.4", 843 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 844 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 845 | }, 846 | "node_modules/ipaddr.js": { 847 | "version": "1.9.1", 848 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 849 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 850 | "engines": { 851 | "node": ">= 0.10" 852 | } 853 | }, 854 | "node_modules/is-fullwidth-code-point": { 855 | "version": "3.0.0", 856 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 857 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 858 | "engines": { 859 | "node": ">=8" 860 | } 861 | }, 862 | "node_modules/is-plain-obj": { 863 | "version": "4.1.0", 864 | "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", 865 | "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", 866 | "engines": { 867 | "node": ">=12" 868 | }, 869 | "funding": { 870 | "url": "https://github.com/sponsors/sindresorhus" 871 | } 872 | }, 873 | "node_modules/is-promise": { 874 | "version": "4.0.0", 875 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", 876 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" 877 | }, 878 | "node_modules/is-stream": { 879 | "version": "4.0.1", 880 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", 881 | "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", 882 | "engines": { 883 | "node": ">=18" 884 | }, 885 | "funding": { 886 | "url": "https://github.com/sponsors/sindresorhus" 887 | } 888 | }, 889 | "node_modules/is-unicode-supported": { 890 | "version": "2.1.0", 891 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", 892 | "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", 893 | "engines": { 894 | "node": ">=18" 895 | }, 896 | "funding": { 897 | "url": "https://github.com/sponsors/sindresorhus" 898 | } 899 | }, 900 | "node_modules/isexe": { 901 | "version": "2.0.0", 902 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 903 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" 904 | }, 905 | "node_modules/math-intrinsics": { 906 | "version": "1.1.0", 907 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 908 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 909 | "engines": { 910 | "node": ">= 0.4" 911 | } 912 | }, 913 | "node_modules/mcp-proxy": { 914 | "version": "2.14.2", 915 | "resolved": "https://registry.npmjs.org/mcp-proxy/-/mcp-proxy-2.14.2.tgz", 916 | "integrity": "sha512-uLG3fVGF/yf2895Bj0Lf1FOmz8RW6NzNOIWIBUJbIR5C3IkX3Q/md+Dm8WnDkMWo2T4BOlqzZmtfGF6muR0oyw==", 917 | "dependencies": { 918 | "@modelcontextprotocol/sdk": "^1.10.1", 919 | "eventsource": "^3.0.6", 920 | "yargs": "^17.7.2" 921 | }, 922 | "bin": { 923 | "mcp-proxy": "dist/bin/mcp-proxy.js" 924 | } 925 | }, 926 | "node_modules/media-typer": { 927 | "version": "1.1.0", 928 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", 929 | "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", 930 | "engines": { 931 | "node": ">= 0.8" 932 | } 933 | }, 934 | "node_modules/merge-descriptors": { 935 | "version": "2.0.0", 936 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", 937 | "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", 938 | "engines": { 939 | "node": ">=18" 940 | }, 941 | "funding": { 942 | "url": "https://github.com/sponsors/sindresorhus" 943 | } 944 | }, 945 | "node_modules/mime-db": { 946 | "version": "1.54.0", 947 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 948 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 949 | "engines": { 950 | "node": ">= 0.6" 951 | } 952 | }, 953 | "node_modules/mime-types": { 954 | "version": "3.0.1", 955 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", 956 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", 957 | "dependencies": { 958 | "mime-db": "^1.54.0" 959 | }, 960 | "engines": { 961 | "node": ">= 0.6" 962 | } 963 | }, 964 | "node_modules/ms": { 965 | "version": "2.1.3", 966 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 967 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 968 | }, 969 | "node_modules/negotiator": { 970 | "version": "1.0.0", 971 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", 972 | "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", 973 | "engines": { 974 | "node": ">= 0.6" 975 | } 976 | }, 977 | "node_modules/npm-run-path": { 978 | "version": "6.0.0", 979 | "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", 980 | "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", 981 | "dependencies": { 982 | "path-key": "^4.0.0", 983 | "unicorn-magic": "^0.3.0" 984 | }, 985 | "engines": { 986 | "node": ">=18" 987 | }, 988 | "funding": { 989 | "url": "https://github.com/sponsors/sindresorhus" 990 | } 991 | }, 992 | "node_modules/npm-run-path/node_modules/path-key": { 993 | "version": "4.0.0", 994 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", 995 | "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", 996 | "engines": { 997 | "node": ">=12" 998 | }, 999 | "funding": { 1000 | "url": "https://github.com/sponsors/sindresorhus" 1001 | } 1002 | }, 1003 | "node_modules/object-assign": { 1004 | "version": "4.1.1", 1005 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1006 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1007 | "engines": { 1008 | "node": ">=0.10.0" 1009 | } 1010 | }, 1011 | "node_modules/object-inspect": { 1012 | "version": "1.13.4", 1013 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 1014 | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 1015 | "engines": { 1016 | "node": ">= 0.4" 1017 | }, 1018 | "funding": { 1019 | "url": "https://github.com/sponsors/ljharb" 1020 | } 1021 | }, 1022 | "node_modules/on-finished": { 1023 | "version": "2.4.1", 1024 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1025 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1026 | "dependencies": { 1027 | "ee-first": "1.1.1" 1028 | }, 1029 | "engines": { 1030 | "node": ">= 0.8" 1031 | } 1032 | }, 1033 | "node_modules/once": { 1034 | "version": "1.4.0", 1035 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1036 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1037 | "dependencies": { 1038 | "wrappy": "1" 1039 | } 1040 | }, 1041 | "node_modules/parse-ms": { 1042 | "version": "4.0.0", 1043 | "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", 1044 | "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", 1045 | "engines": { 1046 | "node": ">=18" 1047 | }, 1048 | "funding": { 1049 | "url": "https://github.com/sponsors/sindresorhus" 1050 | } 1051 | }, 1052 | "node_modules/parseurl": { 1053 | "version": "1.3.3", 1054 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1055 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1056 | "engines": { 1057 | "node": ">= 0.8" 1058 | } 1059 | }, 1060 | "node_modules/path-key": { 1061 | "version": "3.1.1", 1062 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1063 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1064 | "engines": { 1065 | "node": ">=8" 1066 | } 1067 | }, 1068 | "node_modules/path-to-regexp": { 1069 | "version": "8.2.0", 1070 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", 1071 | "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", 1072 | "engines": { 1073 | "node": ">=16" 1074 | } 1075 | }, 1076 | "node_modules/peek-readable": { 1077 | "version": "7.0.0", 1078 | "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", 1079 | "integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==", 1080 | "engines": { 1081 | "node": ">=18" 1082 | }, 1083 | "funding": { 1084 | "type": "github", 1085 | "url": "https://github.com/sponsors/Borewit" 1086 | } 1087 | }, 1088 | "node_modules/pkce-challenge": { 1089 | "version": "5.0.0", 1090 | "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", 1091 | "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", 1092 | "engines": { 1093 | "node": ">=16.20.0" 1094 | } 1095 | }, 1096 | "node_modules/playwright": { 1097 | "version": "1.51.1", 1098 | "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz", 1099 | "integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==", 1100 | "dependencies": { 1101 | "playwright-core": "1.51.1" 1102 | }, 1103 | "bin": { 1104 | "playwright": "cli.js" 1105 | }, 1106 | "engines": { 1107 | "node": ">=18" 1108 | }, 1109 | "optionalDependencies": { 1110 | "fsevents": "2.3.2" 1111 | } 1112 | }, 1113 | "node_modules/playwright-core": { 1114 | "version": "1.51.1", 1115 | "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz", 1116 | "integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==", 1117 | "bin": { 1118 | "playwright-core": "cli.js" 1119 | }, 1120 | "engines": { 1121 | "node": ">=18" 1122 | } 1123 | }, 1124 | "node_modules/pretty-ms": { 1125 | "version": "9.2.0", 1126 | "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", 1127 | "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", 1128 | "dependencies": { 1129 | "parse-ms": "^4.0.0" 1130 | }, 1131 | "engines": { 1132 | "node": ">=18" 1133 | }, 1134 | "funding": { 1135 | "url": "https://github.com/sponsors/sindresorhus" 1136 | } 1137 | }, 1138 | "node_modules/proxy-addr": { 1139 | "version": "2.0.7", 1140 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1141 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1142 | "dependencies": { 1143 | "forwarded": "0.2.0", 1144 | "ipaddr.js": "1.9.1" 1145 | }, 1146 | "engines": { 1147 | "node": ">= 0.10" 1148 | } 1149 | }, 1150 | "node_modules/proxy-from-env": { 1151 | "version": "1.1.0", 1152 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 1153 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 1154 | }, 1155 | "node_modules/qs": { 1156 | "version": "6.14.0", 1157 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", 1158 | "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", 1159 | "dependencies": { 1160 | "side-channel": "^1.1.0" 1161 | }, 1162 | "engines": { 1163 | "node": ">=0.6" 1164 | }, 1165 | "funding": { 1166 | "url": "https://github.com/sponsors/ljharb" 1167 | } 1168 | }, 1169 | "node_modules/range-parser": { 1170 | "version": "1.2.1", 1171 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1172 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1173 | "engines": { 1174 | "node": ">= 0.6" 1175 | } 1176 | }, 1177 | "node_modules/raw-body": { 1178 | "version": "3.0.0", 1179 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", 1180 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", 1181 | "dependencies": { 1182 | "bytes": "3.1.2", 1183 | "http-errors": "2.0.0", 1184 | "iconv-lite": "0.6.3", 1185 | "unpipe": "1.0.0" 1186 | }, 1187 | "engines": { 1188 | "node": ">= 0.8" 1189 | } 1190 | }, 1191 | "node_modules/require-directory": { 1192 | "version": "2.1.1", 1193 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 1194 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 1195 | "engines": { 1196 | "node": ">=0.10.0" 1197 | } 1198 | }, 1199 | "node_modules/router": { 1200 | "version": "2.2.0", 1201 | "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", 1202 | "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", 1203 | "dependencies": { 1204 | "debug": "^4.4.0", 1205 | "depd": "^2.0.0", 1206 | "is-promise": "^4.0.0", 1207 | "parseurl": "^1.3.3", 1208 | "path-to-regexp": "^8.0.0" 1209 | }, 1210 | "engines": { 1211 | "node": ">= 18" 1212 | } 1213 | }, 1214 | "node_modules/safe-buffer": { 1215 | "version": "5.2.1", 1216 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1217 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1218 | "funding": [ 1219 | { 1220 | "type": "github", 1221 | "url": "https://github.com/sponsors/feross" 1222 | }, 1223 | { 1224 | "type": "patreon", 1225 | "url": "https://www.patreon.com/feross" 1226 | }, 1227 | { 1228 | "type": "consulting", 1229 | "url": "https://feross.org/support" 1230 | } 1231 | ] 1232 | }, 1233 | "node_modules/safer-buffer": { 1234 | "version": "2.1.2", 1235 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1236 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1237 | }, 1238 | "node_modules/send": { 1239 | "version": "1.2.0", 1240 | "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", 1241 | "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", 1242 | "dependencies": { 1243 | "debug": "^4.3.5", 1244 | "encodeurl": "^2.0.0", 1245 | "escape-html": "^1.0.3", 1246 | "etag": "^1.8.1", 1247 | "fresh": "^2.0.0", 1248 | "http-errors": "^2.0.0", 1249 | "mime-types": "^3.0.1", 1250 | "ms": "^2.1.3", 1251 | "on-finished": "^2.4.1", 1252 | "range-parser": "^1.2.1", 1253 | "statuses": "^2.0.1" 1254 | }, 1255 | "engines": { 1256 | "node": ">= 18" 1257 | } 1258 | }, 1259 | "node_modules/serve-static": { 1260 | "version": "2.2.0", 1261 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", 1262 | "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", 1263 | "dependencies": { 1264 | "encodeurl": "^2.0.0", 1265 | "escape-html": "^1.0.3", 1266 | "parseurl": "^1.3.3", 1267 | "send": "^1.2.0" 1268 | }, 1269 | "engines": { 1270 | "node": ">= 18" 1271 | } 1272 | }, 1273 | "node_modules/setprototypeof": { 1274 | "version": "1.2.0", 1275 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1276 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1277 | }, 1278 | "node_modules/shebang-command": { 1279 | "version": "2.0.0", 1280 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 1281 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 1282 | "dependencies": { 1283 | "shebang-regex": "^3.0.0" 1284 | }, 1285 | "engines": { 1286 | "node": ">=8" 1287 | } 1288 | }, 1289 | "node_modules/shebang-regex": { 1290 | "version": "3.0.0", 1291 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 1292 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 1293 | "engines": { 1294 | "node": ">=8" 1295 | } 1296 | }, 1297 | "node_modules/side-channel": { 1298 | "version": "1.1.0", 1299 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 1300 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 1301 | "dependencies": { 1302 | "es-errors": "^1.3.0", 1303 | "object-inspect": "^1.13.3", 1304 | "side-channel-list": "^1.0.0", 1305 | "side-channel-map": "^1.0.1", 1306 | "side-channel-weakmap": "^1.0.2" 1307 | }, 1308 | "engines": { 1309 | "node": ">= 0.4" 1310 | }, 1311 | "funding": { 1312 | "url": "https://github.com/sponsors/ljharb" 1313 | } 1314 | }, 1315 | "node_modules/side-channel-list": { 1316 | "version": "1.0.0", 1317 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 1318 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 1319 | "dependencies": { 1320 | "es-errors": "^1.3.0", 1321 | "object-inspect": "^1.13.3" 1322 | }, 1323 | "engines": { 1324 | "node": ">= 0.4" 1325 | }, 1326 | "funding": { 1327 | "url": "https://github.com/sponsors/ljharb" 1328 | } 1329 | }, 1330 | "node_modules/side-channel-map": { 1331 | "version": "1.0.1", 1332 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 1333 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 1334 | "dependencies": { 1335 | "call-bound": "^1.0.2", 1336 | "es-errors": "^1.3.0", 1337 | "get-intrinsic": "^1.2.5", 1338 | "object-inspect": "^1.13.3" 1339 | }, 1340 | "engines": { 1341 | "node": ">= 0.4" 1342 | }, 1343 | "funding": { 1344 | "url": "https://github.com/sponsors/ljharb" 1345 | } 1346 | }, 1347 | "node_modules/side-channel-weakmap": { 1348 | "version": "1.0.2", 1349 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 1350 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 1351 | "dependencies": { 1352 | "call-bound": "^1.0.2", 1353 | "es-errors": "^1.3.0", 1354 | "get-intrinsic": "^1.2.5", 1355 | "object-inspect": "^1.13.3", 1356 | "side-channel-map": "^1.0.1" 1357 | }, 1358 | "engines": { 1359 | "node": ">= 0.4" 1360 | }, 1361 | "funding": { 1362 | "url": "https://github.com/sponsors/ljharb" 1363 | } 1364 | }, 1365 | "node_modules/signal-exit": { 1366 | "version": "4.1.0", 1367 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", 1368 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", 1369 | "engines": { 1370 | "node": ">=14" 1371 | }, 1372 | "funding": { 1373 | "url": "https://github.com/sponsors/isaacs" 1374 | } 1375 | }, 1376 | "node_modules/statuses": { 1377 | "version": "2.0.1", 1378 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1379 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1380 | "engines": { 1381 | "node": ">= 0.8" 1382 | } 1383 | }, 1384 | "node_modules/strict-event-emitter-types": { 1385 | "version": "2.0.0", 1386 | "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz", 1387 | "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==" 1388 | }, 1389 | "node_modules/string-width": { 1390 | "version": "4.2.3", 1391 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 1392 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 1393 | "dependencies": { 1394 | "emoji-regex": "^8.0.0", 1395 | "is-fullwidth-code-point": "^3.0.0", 1396 | "strip-ansi": "^6.0.1" 1397 | }, 1398 | "engines": { 1399 | "node": ">=8" 1400 | } 1401 | }, 1402 | "node_modules/strip-ansi": { 1403 | "version": "6.0.1", 1404 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 1405 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 1406 | "dependencies": { 1407 | "ansi-regex": "^5.0.1" 1408 | }, 1409 | "engines": { 1410 | "node": ">=8" 1411 | } 1412 | }, 1413 | "node_modules/strip-final-newline": { 1414 | "version": "4.0.0", 1415 | "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", 1416 | "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", 1417 | "engines": { 1418 | "node": ">=18" 1419 | }, 1420 | "funding": { 1421 | "url": "https://github.com/sponsors/sindresorhus" 1422 | } 1423 | }, 1424 | "node_modules/strtok3": { 1425 | "version": "10.2.2", 1426 | "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz", 1427 | "integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==", 1428 | "dependencies": { 1429 | "@tokenizer/token": "^0.3.0", 1430 | "peek-readable": "^7.0.0" 1431 | }, 1432 | "engines": { 1433 | "node": ">=18" 1434 | }, 1435 | "funding": { 1436 | "type": "github", 1437 | "url": "https://github.com/sponsors/Borewit" 1438 | } 1439 | }, 1440 | "node_modules/toidentifier": { 1441 | "version": "1.0.1", 1442 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1443 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1444 | "engines": { 1445 | "node": ">=0.6" 1446 | } 1447 | }, 1448 | "node_modules/token-types": { 1449 | "version": "6.0.0", 1450 | "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", 1451 | "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", 1452 | "dependencies": { 1453 | "@tokenizer/token": "^0.3.0", 1454 | "ieee754": "^1.2.1" 1455 | }, 1456 | "engines": { 1457 | "node": ">=14.16" 1458 | }, 1459 | "funding": { 1460 | "type": "github", 1461 | "url": "https://github.com/sponsors/Borewit" 1462 | } 1463 | }, 1464 | "node_modules/type-is": { 1465 | "version": "2.0.1", 1466 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", 1467 | "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", 1468 | "dependencies": { 1469 | "content-type": "^1.0.5", 1470 | "media-typer": "^1.1.0", 1471 | "mime-types": "^3.0.0" 1472 | }, 1473 | "engines": { 1474 | "node": ">= 0.6" 1475 | } 1476 | }, 1477 | "node_modules/uint8array-extras": { 1478 | "version": "1.4.0", 1479 | "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", 1480 | "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", 1481 | "engines": { 1482 | "node": ">=18" 1483 | }, 1484 | "funding": { 1485 | "url": "https://github.com/sponsors/sindresorhus" 1486 | } 1487 | }, 1488 | "node_modules/undici": { 1489 | "version": "7.9.0", 1490 | "resolved": "https://registry.npmjs.org/undici/-/undici-7.9.0.tgz", 1491 | "integrity": "sha512-e696y354tf5cFZPXsF26Yg+5M63+5H3oE6Vtkh2oqbvsE2Oe7s2nIbcQh5lmG7Lp/eS29vJtTpw9+p6PX0qNSg==", 1492 | "engines": { 1493 | "node": ">=20.18.1" 1494 | } 1495 | }, 1496 | "node_modules/unicorn-magic": { 1497 | "version": "0.3.0", 1498 | "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", 1499 | "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", 1500 | "engines": { 1501 | "node": ">=18" 1502 | }, 1503 | "funding": { 1504 | "url": "https://github.com/sponsors/sindresorhus" 1505 | } 1506 | }, 1507 | "node_modules/unpipe": { 1508 | "version": "1.0.0", 1509 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1510 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1511 | "engines": { 1512 | "node": ">= 0.8" 1513 | } 1514 | }, 1515 | "node_modules/uri-templates": { 1516 | "version": "0.2.0", 1517 | "resolved": "https://registry.npmjs.org/uri-templates/-/uri-templates-0.2.0.tgz", 1518 | "integrity": "sha512-EWkjYEN0L6KOfEoOH6Wj4ghQqU7eBZMJqRHQnxQAq+dSEzRPClkWjf8557HkWQXF6BrAUoLSAyy9i3RVTliaNg==" 1519 | }, 1520 | "node_modules/vary": { 1521 | "version": "1.1.2", 1522 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1523 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1524 | "engines": { 1525 | "node": ">= 0.8" 1526 | } 1527 | }, 1528 | "node_modules/which": { 1529 | "version": "2.0.2", 1530 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 1531 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 1532 | "dependencies": { 1533 | "isexe": "^2.0.0" 1534 | }, 1535 | "bin": { 1536 | "node-which": "bin/node-which" 1537 | }, 1538 | "engines": { 1539 | "node": ">= 8" 1540 | } 1541 | }, 1542 | "node_modules/wrap-ansi": { 1543 | "version": "7.0.0", 1544 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 1545 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 1546 | "dependencies": { 1547 | "ansi-styles": "^4.0.0", 1548 | "string-width": "^4.1.0", 1549 | "strip-ansi": "^6.0.0" 1550 | }, 1551 | "engines": { 1552 | "node": ">=10" 1553 | }, 1554 | "funding": { 1555 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 1556 | } 1557 | }, 1558 | "node_modules/wrappy": { 1559 | "version": "1.0.2", 1560 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1561 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" 1562 | }, 1563 | "node_modules/xsschema": { 1564 | "version": "0.2.0-beta.3", 1565 | "resolved": "https://registry.npmjs.org/xsschema/-/xsschema-0.2.0-beta.3.tgz", 1566 | "integrity": "sha512-ViOZ1a1kAPHvFjDJp4ITeutdlbnEs56lx/NotlvcitwN04eHnU3DhR+P5GvXT7A+69qKrXAx0YrccvmfwGnUGw==", 1567 | "peerDependencies": { 1568 | "@valibot/to-json-schema": "^1.0.0", 1569 | "arktype": "^2.1.16", 1570 | "effect": "^3.14.5", 1571 | "zod-to-json-schema": "^3.24.5" 1572 | }, 1573 | "peerDependenciesMeta": { 1574 | "@valibot/to-json-schema": { 1575 | "optional": true 1576 | }, 1577 | "arktype": { 1578 | "optional": true 1579 | }, 1580 | "effect": { 1581 | "optional": true 1582 | }, 1583 | "zod-to-json-schema": { 1584 | "optional": true 1585 | } 1586 | } 1587 | }, 1588 | "node_modules/y18n": { 1589 | "version": "5.0.8", 1590 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 1591 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 1592 | "engines": { 1593 | "node": ">=10" 1594 | } 1595 | }, 1596 | "node_modules/yargs": { 1597 | "version": "17.7.2", 1598 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 1599 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 1600 | "dependencies": { 1601 | "cliui": "^8.0.1", 1602 | "escalade": "^3.1.1", 1603 | "get-caller-file": "^2.0.5", 1604 | "require-directory": "^2.1.1", 1605 | "string-width": "^4.2.3", 1606 | "y18n": "^5.0.5", 1607 | "yargs-parser": "^21.1.1" 1608 | }, 1609 | "engines": { 1610 | "node": ">=12" 1611 | } 1612 | }, 1613 | "node_modules/yargs-parser": { 1614 | "version": "21.1.1", 1615 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 1616 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 1617 | "engines": { 1618 | "node": ">=12" 1619 | } 1620 | }, 1621 | "node_modules/yoctocolors": { 1622 | "version": "2.1.1", 1623 | "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", 1624 | "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", 1625 | "engines": { 1626 | "node": ">=18" 1627 | }, 1628 | "funding": { 1629 | "url": "https://github.com/sponsors/sindresorhus" 1630 | } 1631 | }, 1632 | "node_modules/zod": { 1633 | "version": "3.24.4", 1634 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", 1635 | "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", 1636 | "funding": { 1637 | "url": "https://github.com/sponsors/colinhacks" 1638 | } 1639 | }, 1640 | "node_modules/zod-to-json-schema": { 1641 | "version": "3.24.5", 1642 | "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", 1643 | "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", 1644 | "peerDependencies": { 1645 | "zod": "^3.24.1" 1646 | } 1647 | } 1648 | } 1649 | } 1650 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@brightdata/mcp", 3 | "version": "2.0.0", 4 | "description": "An MCP interface into the Bright Data toolset", 5 | "type": "module", 6 | "bin": { 7 | "@brightdata/mcp": "./server.js" 8 | }, 9 | "keywords": [ 10 | "mcp", 11 | "brightdata" 12 | ], 13 | "author": "Bright Data", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/brightdata-com/brightdata-mcp.git" 17 | }, 18 | "license": "MIT", 19 | "dependencies": { 20 | "axios": "^1.8.4", 21 | "fastmcp": "^1.27.6", 22 | "playwright": "^1.51.1", 23 | "zod": "^3.24.2" 24 | }, 25 | "publishConfig": { 26 | "access": "public" 27 | }, 28 | "files": [ 29 | "server.js", 30 | "browser_tools.js", 31 | "browser_session.js" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; /*jslint node:true es9:true*/ 3 | import {FastMCP} from 'fastmcp'; 4 | import {z} from 'zod'; 5 | import axios from 'axios'; 6 | import {tools as browser_tools} from './browser_tools.js'; 7 | import {createRequire} from 'node:module'; 8 | const require = createRequire(import.meta.url); 9 | const package_json = require('./package.json'); 10 | const api_token = process.env.API_TOKEN; 11 | const unlocker_zone = process.env.WEB_UNLOCKER_ZONE || 'mcp_unlocker'; 12 | 13 | if (!api_token) 14 | throw new Error('Cannot run MCP server without API_TOKEN env'); 15 | 16 | const api_headers = ()=>({ 17 | 'user-agent': `${package_json.name}/${package_json.version}`, 18 | authorization: `Bearer ${api_token}`, 19 | }); 20 | 21 | async function ensure_required_zones(){ 22 | try { 23 | console.error('Checking for required zones...'); 24 | let response = await axios({ 25 | url: 'https://api.brightdata.com/zone/get_active_zones', 26 | method: 'GET', 27 | headers: api_headers(), 28 | }); 29 | let zones = response.data || []; 30 | let has_unlocker_zone = zones.some(zone=>zone.name==unlocker_zone); 31 | if (!has_unlocker_zone) 32 | { 33 | console.error(`Required zone "${unlocker_zone}" not found, ` 34 | +`creating it...`); 35 | await axios({ 36 | url: 'https://api.brightdata.com/zone', 37 | method: 'POST', 38 | headers: { 39 | ...api_headers(), 40 | 'Content-Type': 'application/json', 41 | }, 42 | data: { 43 | zone: {name: unlocker_zone, type: 'unblocker'}, 44 | plan: {type: 'unblocker'}, 45 | }, 46 | }); 47 | console.error(`Zone "${unlocker_zone}" created successfully`); 48 | } 49 | else 50 | console.error(`Required zone "${unlocker_zone}" already exists`); 51 | } catch(e){ 52 | console.error('Error checking/creating zones:', 53 | e.response?.data||e.message); 54 | } 55 | } 56 | 57 | await ensure_required_zones(); 58 | 59 | let server = new FastMCP({ 60 | name: 'Bright Data', 61 | version: package_json.version, 62 | }); 63 | let debug_stats = {tool_calls: {}}; 64 | 65 | server.addTool({ 66 | name: 'search_engine', 67 | description: 'Scrape search results from Google, Bing or Yandex. Returns ' 68 | +'SERP results in markdown (URL, title, description)', 69 | parameters: z.object({ 70 | query: z.string(), 71 | engine: z.enum([ 72 | 'google', 73 | 'bing', 74 | 'yandex', 75 | ]).optional().default('google'), 76 | }), 77 | execute: tool_fn('search_engine', async({query, engine})=>{ 78 | let response = await axios({ 79 | url: 'https://api.brightdata.com/request', 80 | method: 'POST', 81 | data: { 82 | url: search_url(engine, query), 83 | zone: unlocker_zone, 84 | format: 'raw', 85 | data_format: 'markdown', 86 | }, 87 | headers: api_headers(), 88 | responseType: 'text', 89 | }); 90 | return response.data; 91 | }), 92 | }); 93 | 94 | server.addTool({ 95 | name: 'scrape_as_markdown', 96 | description: 'Scrape a single webpage URL with advanced options for ' 97 | +'content extraction and get back the results in MarkDown language. ' 98 | +'This tool can unlock any webpage even if it uses bot detection or ' 99 | +'CAPTCHA.', 100 | parameters: z.object({url: z.string().url()}), 101 | execute: tool_fn('scrape_as_markdown', async({url})=>{ 102 | let response = await axios({ 103 | url: 'https://api.brightdata.com/request', 104 | method: 'POST', 105 | data: { 106 | url, 107 | zone: unlocker_zone, 108 | format: 'raw', 109 | data_format: 'markdown', 110 | }, 111 | headers: api_headers(), 112 | responseType: 'text', 113 | }); 114 | return response.data; 115 | }), 116 | }); 117 | server.addTool({ 118 | name: 'scrape_as_html', 119 | description: 'Scrape a single webpage URL with advanced options for ' 120 | +'content extraction and get back the results in HTML. ' 121 | +'This tool can unlock any webpage even if it uses bot detection or ' 122 | +'CAPTCHA.', 123 | parameters: z.object({url: z.string().url()}), 124 | execute: tool_fn('scrape_as_html', async({url})=>{ 125 | let response = await axios({ 126 | url: 'https://api.brightdata.com/request', 127 | method: 'POST', 128 | data: { 129 | url, 130 | zone: unlocker_zone, 131 | format: 'raw', 132 | }, 133 | headers: api_headers(), 134 | responseType: 'text', 135 | }); 136 | return response.data; 137 | }), 138 | }); 139 | 140 | server.addTool({ 141 | name: 'session_stats', 142 | description: 'Tell the user about the tool usage during this session', 143 | parameters: z.object({}), 144 | execute: tool_fn('session_stats', async()=>{ 145 | let used_tools = Object.entries(debug_stats.tool_calls); 146 | let lines = ['Tool calls this session:']; 147 | for (let [name, calls] of used_tools) 148 | lines.push(`- ${name} tool: called ${calls} times`); 149 | return lines.join('\n'); 150 | }), 151 | }); 152 | 153 | const datasets = [{ 154 | id: 'amazon_product', 155 | dataset_id: 'gd_l7q7dkf244hwjntr0', 156 | description: [ 157 | 'Quickly read structured amazon product data.', 158 | 'Requires a valid product URL with /dp/ in it.', 159 | 'This can be a cache lookup, so it can be more reliable than scraping', 160 | ].join('\n'), 161 | inputs: ['url'], 162 | }, { 163 | id: 'amazon_product_reviews', 164 | dataset_id: 'gd_le8e811kzy4ggddlq', 165 | description: [ 166 | 'Quickly read structured amazon product review data.', 167 | 'Requires a valid product URL with /dp/ in it.', 168 | 'This can be a cache lookup, so it can be more reliable than scraping', 169 | ].join('\n'), 170 | inputs: ['url'], 171 | }, { 172 | id: 'amazon_product_search', 173 | dataset_id: 'gd_lwdb4vjm1ehb499uxs', 174 | description: [ 175 | 'Quickly read structured amazon product search data.', 176 | 'Requires a valid search keyword and amazon domain URL.', 177 | 'This can be a cache lookup, so it can be more reliable than scraping', 178 | ].join('\n'), 179 | inputs: ['keyword', 'url', 'pages_to_search'], 180 | defaults: {pages_to_search: '1'}, 181 | }, { 182 | id: 'walmart_product', 183 | dataset_id: 'gd_l95fol7l1ru6rlo116', 184 | description: [ 185 | 'Quickly read structured walmart product data.', 186 | 'Requires a valid product URL with /ip/ in it.', 187 | 'This can be a cache lookup, so it can be more reliable than scraping', 188 | ].join('\n'), 189 | inputs: ['url'], 190 | }, { 191 | id: 'walmart_seller', 192 | dataset_id: 'gd_m7ke48w81ocyu4hhz0', 193 | description: [ 194 | 'Quickly read structured walmart seller data.', 195 | 'Requires a valid walmart seller URL.', 196 | 'This can be a cache lookup, so it can be more reliable than scraping', 197 | ].join('\n'), 198 | inputs: ['url'], 199 | }, { 200 | id: 'ebay_product', 201 | dataset_id: 'gd_ltr9mjt81n0zzdk1fb', 202 | description: [ 203 | 'Quickly read structured ebay product data.', 204 | 'Requires a valid ebay product URL.', 205 | 'This can be a cache lookup, so it can be more reliable than scraping', 206 | ].join('\n'), 207 | inputs: ['url'], 208 | }, { 209 | id: 'homedepot_products', 210 | dataset_id: 'gd_lmusivh019i7g97q2n', 211 | description: [ 212 | 'Quickly read structured homedepot product data.', 213 | 'Requires a valid homedepot product URL.', 214 | 'This can be a cache lookup, so it can be more reliable than scraping', 215 | ].join('\n'), 216 | inputs: ['url'], 217 | }, { 218 | id: 'zara_products', 219 | dataset_id: 'gd_lct4vafw1tgx27d4o0', 220 | description: [ 221 | 'Quickly read structured zara product data.', 222 | 'Requires a valid zara product URL.', 223 | 'This can be a cache lookup, so it can be more reliable than scraping', 224 | ].join('\n'), 225 | inputs: ['url'], 226 | }, { 227 | id: 'etsy_products', 228 | dataset_id: 'gd_ltppk0jdv1jqz25mz', 229 | description: [ 230 | 'Quickly read structured etsy product data.', 231 | 'Requires a valid etsy product URL.', 232 | 'This can be a cache lookup, so it can be more reliable than scraping', 233 | ].join('\n'), 234 | inputs: ['url'], 235 | }, { 236 | id: 'bestbuy_products', 237 | dataset_id: 'gd_ltre1jqe1jfr7cccf', 238 | description: [ 239 | 'Quickly read structured bestbuy product data.', 240 | 'Requires a valid bestbuy product URL.', 241 | 'This can be a cache lookup, so it can be more reliable than scraping', 242 | ].join('\n'), 243 | inputs: ['url'], 244 | }, { 245 | id: 'linkedin_person_profile', 246 | dataset_id: 'gd_l1viktl72bvl7bjuj0', 247 | description: [ 248 | 'Quickly read structured linkedin people profile data.', 249 | 'This can be a cache lookup, so it can be more reliable than scraping', 250 | ].join('\n'), 251 | inputs: ['url'], 252 | }, { 253 | id: 'linkedin_company_profile', 254 | dataset_id: 'gd_l1vikfnt1wgvvqz95w', 255 | description: [ 256 | 'Quickly read structured linkedin company profile data', 257 | 'This can be a cache lookup, so it can be more reliable than scraping', 258 | ].join('\n'), 259 | inputs: ['url'], 260 | }, { 261 | id: 'linkedin_job_listings', 262 | dataset_id: 'gd_lpfll7v5hcqtkxl6l', 263 | description: [ 264 | 'Quickly read structured linkedin job listings data', 265 | 'This can be a cache lookup, so it can be more reliable than scraping', 266 | ].join('\n'), 267 | inputs: ['url'], 268 | }, { 269 | id: 'linkedin_posts', 270 | dataset_id: 'gd_lyy3tktm25m4avu764', 271 | description: [ 272 | 'Quickly read structured linkedin posts data', 273 | 'This can be a cache lookup, so it can be more reliable than scraping', 274 | ].join('\n'), 275 | inputs: ['url'], 276 | }, { 277 | id: 'linkedin_people_search', 278 | dataset_id: 'gd_m8d03he47z8nwb5xc', 279 | description: [ 280 | 'Quickly read structured linkedin people search data', 281 | 'This can be a cache lookup, so it can be more reliable than scraping', 282 | ].join('\n'), 283 | inputs: ['url', 'first_name', 'last_name'], 284 | }, { 285 | id: 'crunchbase_company', 286 | dataset_id: 'gd_l1vijqt9jfj7olije', 287 | description: [ 288 | 'Quickly read structured crunchbase company data', 289 | 'This can be a cache lookup, so it can be more reliable than scraping', 290 | ].join('\n'), 291 | inputs: ['url'], 292 | }, 293 | { 294 | id: 'zoominfo_company_profile', 295 | dataset_id: 'gd_m0ci4a4ivx3j5l6nx', 296 | description: [ 297 | 'Quickly read structured ZoomInfo company profile data.', 298 | 'Requires a valid ZoomInfo company URL.', 299 | 'This can be a cache lookup, so it can be more reliable than scraping', 300 | ].join('\n'), 301 | inputs: ['url'], 302 | }, 303 | { 304 | id: 'instagram_profiles', 305 | dataset_id: 'gd_l1vikfch901nx3by4', 306 | description: [ 307 | 'Quickly read structured Instagram profile data.', 308 | 'Requires a valid Instagram URL.', 309 | 'This can be a cache lookup, so it can be more reliable than scraping', 310 | ].join('\n'), 311 | inputs: ['url'], 312 | }, 313 | { 314 | id: 'instagram_posts', 315 | dataset_id: 'gd_lk5ns7kz21pck8jpis', 316 | description: [ 317 | 'Quickly read structured Instagram post data.', 318 | 'Requires a valid Instagram URL.', 319 | 'This can be a cache lookup, so it can be more reliable than scraping', 320 | ].join('\n'), 321 | inputs: ['url'], 322 | }, 323 | { 324 | id: 'instagram_reels', 325 | dataset_id: 'gd_lyclm20il4r5helnj', 326 | description: [ 327 | 'Quickly read structured Instagram reel data.', 328 | 'Requires a valid Instagram URL.', 329 | 'This can be a cache lookup, so it can be more reliable than scraping', 330 | ].join('\n'), 331 | inputs: ['url'], 332 | }, 333 | { 334 | id: 'instagram_comments', 335 | dataset_id: 'gd_ltppn085pokosxh13', 336 | description: [ 337 | 'Quickly read structured Instagram comments data.', 338 | 'Requires a valid Instagram URL.', 339 | 'This can be a cache lookup, so it can be more reliable than scraping', 340 | ].join('\n'), 341 | inputs: ['url'], 342 | }, 343 | { 344 | id: 'facebook_posts', 345 | dataset_id: 'gd_lyclm1571iy3mv57zw', 346 | description: [ 347 | 'Quickly read structured Facebook post data.', 348 | 'Requires a valid Facebook post URL.', 349 | 'This can be a cache lookup, so it can be more reliable than scraping', 350 | ].join('\n'), 351 | inputs: ['url'], 352 | }, 353 | { 354 | id: 'facebook_marketplace_listings', 355 | dataset_id: 'gd_lvt9iwuh6fbcwmx1a', 356 | description: [ 357 | 'Quickly read structured Facebook marketplace listing data.', 358 | 'Requires a valid Facebook marketplace listing URL.', 359 | 'This can be a cache lookup, so it can be more reliable than scraping', 360 | ].join('\n'), 361 | inputs: ['url'], 362 | }, 363 | { 364 | id: 'facebook_company_reviews', 365 | dataset_id: 'gd_m0dtqpiu1mbcyc2g86', 366 | description: [ 367 | 'Quickly read structured Facebook company reviews data.', 368 | 'Requires a valid Facebook company URL and number of reviews.', 369 | 'This can be a cache lookup, so it can be more reliable than scraping', 370 | ].join('\n'), 371 | inputs: ['url', 'num_of_reviews'], 372 | }, { 373 | id: 'facebook_events', 374 | dataset_id: 'gd_m14sd0to1jz48ppm51', 375 | description: [ 376 | 'Quickly read structured Facebook events data.', 377 | 'Requires a valid Facebook event URL.', 378 | 'This can be a cache lookup, so it can be more reliable than scraping', 379 | ].join('\n'), 380 | inputs: ['url'], 381 | }, { 382 | id: 'tiktok_profiles', 383 | dataset_id: 'gd_l1villgoiiidt09ci', 384 | description: [ 385 | 'Quickly read structured Tiktok profiles data.', 386 | 'Requires a valid Tiktok profile URL.', 387 | 'This can be a cache lookup, so it can be more reliable than scraping', 388 | ].join('\n'), 389 | inputs: ['url'], 390 | }, { 391 | id: 'tiktok_posts', 392 | dataset_id: 'gd_lu702nij2f790tmv9h', 393 | description: [ 394 | 'Quickly read structured Tiktok post data.', 395 | 'Requires a valid Tiktok post URL.', 396 | 'This can be a cache lookup, so it can be more reliable than scraping', 397 | ].join('\n'), 398 | inputs: ['url'], 399 | }, { 400 | id: 'tiktok_shop', 401 | dataset_id: 'gd_m45m1u911dsa4274pi', 402 | description: [ 403 | 'Quickly read structured Tiktok shop data.', 404 | 'Requires a valid Tiktok shop product URL.', 405 | 'This can be a cache lookup, so it can be more reliable than scraping', 406 | ].join('\n'), 407 | inputs: ['url'], 408 | }, { 409 | id: 'tiktok_comments', 410 | dataset_id: 'gd_lkf2st302ap89utw5k', 411 | description: [ 412 | 'Quickly read structured Tiktok comments data.', 413 | 'Requires a valid Tiktok video URL.', 414 | 'This can be a cache lookup, so it can be more reliable than scraping', 415 | ].join('\n'), 416 | inputs: ['url'], 417 | }, { 418 | id: 'google_maps_reviews', 419 | dataset_id: 'gd_luzfs1dn2oa0teb81', 420 | description: [ 421 | 'Quickly read structured Google maps reviews data.', 422 | 'Requires a valid Google maps URL.', 423 | 'This can be a cache lookup, so it can be more reliable than scraping', 424 | ].join('\n'), 425 | inputs: ['url', 'days_limit'], 426 | defaults: {days_limit: '3'}, 427 | }, { 428 | id: 'google_shopping', 429 | dataset_id: 'gd_ltppk50q18kdw67omz', 430 | description: [ 431 | 'Quickly read structured Google shopping data.', 432 | 'Requires a valid Google shopping product URL.', 433 | 'This can be a cache lookup, so it can be more reliable than scraping', 434 | ].join('\n'), 435 | inputs: ['url'], 436 | }, { 437 | id: 'google_play_store', 438 | dataset_id: 'gd_lsk382l8xei8vzm4u', 439 | description: [ 440 | 'Quickly read structured Google play store data.', 441 | 'Requires a valid Google play store app URL.', 442 | 'This can be a cache lookup, so it can be more reliable than scraping', 443 | ].join('\n'), 444 | inputs: ['url'], 445 | }, { 446 | id: 'apple_app_store', 447 | dataset_id: 'gd_lsk9ki3u2iishmwrui', 448 | description: [ 449 | 'Quickly read structured apple app store data.', 450 | 'Requires a valid apple app store app URL.', 451 | 'This can be a cache lookup, so it can be more reliable than scraping', 452 | ].join('\n'), 453 | inputs: ['url'], 454 | }, { 455 | id: 'reuter_news', 456 | dataset_id: 'gd_lyptx9h74wtlvpnfu', 457 | description: [ 458 | 'Quickly read structured reuter news data.', 459 | 'Requires a valid reuter news report URL.', 460 | 'This can be a cache lookup, so it can be more reliable than scraping', 461 | ].join('\n'), 462 | inputs: ['url'], 463 | }, { 464 | id: 'github_repository_file', 465 | dataset_id: 'gd_lyrexgxc24b3d4imjt', 466 | description: [ 467 | 'Quickly read structured github repository data.', 468 | 'Requires a valid github repository file URL.', 469 | 'This can be a cache lookup, so it can be more reliable than scraping', 470 | ].join('\n'), 471 | inputs: ['url'], 472 | }, { 473 | id: 'yahoo_finance_business', 474 | dataset_id: 'gd_lmrpz3vxmz972ghd7', 475 | description: [ 476 | 'Quickly read structured yahoo finance business data.', 477 | 'Requires a valid yahoo finance business URL.', 478 | 'This can be a cache lookup, so it can be more reliable than scraping', 479 | ].join('\n'), 480 | inputs: ['url'], 481 | }, 482 | { 483 | id: 'x_posts', 484 | dataset_id: 'gd_lwxkxvnf1cynvib9co', 485 | description: [ 486 | 'Quickly read structured X post data.', 487 | 'Requires a valid X post URL.', 488 | 'This can be a cache lookup, so it can be more reliable than scraping', 489 | ].join('\n'), 490 | inputs: ['url'], 491 | }, 492 | { 493 | id: 'zillow_properties_listing', 494 | dataset_id: 'gd_lfqkr8wm13ixtbd8f5', 495 | description: [ 496 | 'Quickly read structured zillow properties listing data.', 497 | 'Requires a valid zillow properties listing URL.', 498 | 'This can be a cache lookup, so it can be more reliable than scraping', 499 | ].join('\n'), 500 | inputs: ['url'], 501 | }, 502 | { 503 | id: 'booking_hotel_listings', 504 | dataset_id: 'gd_m5mbdl081229ln6t4a', 505 | description: [ 506 | 'Quickly read structured booking hotel listings data.', 507 | 'Requires a valid booking hotel listing URL.', 508 | 'This can be a cache lookup, so it can be more reliable than scraping', 509 | ].join('\n'), 510 | inputs: ['url'], 511 | }, { 512 | id: 'youtube_profiles', 513 | dataset_id: 'gd_lk538t2k2p1k3oos71', 514 | description: [ 515 | 'Quickly read structured youtube profiles data.', 516 | 'Requires a valid youtube profile URL.', 517 | 'This can be a cache lookup, so it can be more reliable than scraping', 518 | ].join('\n'), 519 | inputs: ['url'], 520 | }, { 521 | id: 'youtube_comments', 522 | dataset_id: 'gd_lk9q0ew71spt1mxywf', 523 | description: [ 524 | 'Quickly read structured youtube comments data.', 525 | 'Requires a valid youtube video URL.', 526 | 'This can be a cache lookup, so it can be more reliable than scraping', 527 | ].join('\n'), 528 | inputs: ['url', 'num_of_comments'], 529 | defaults: {num_of_comments: '10'}, 530 | }, { 531 | id: 'reddit_posts', 532 | dataset_id: 'gd_lvz8ah06191smkebj4', 533 | description: [ 534 | 'Quickly read structured reddit posts data.', 535 | 'Requires a valid reddit post URL.', 536 | 'This can be a cache lookup, so it can be more reliable than scraping', 537 | ].join('\n'), 538 | inputs: ['url'], 539 | }, 540 | { 541 | id: 'youtube_videos', 542 | dataset_id: 'gd_m5mbdl081229ln6t4a', 543 | description: [ 544 | 'Quickly read structured YouTube videos data.', 545 | 'Requires a valid YouTube video URL.', 546 | 'This can be a cache lookup, so it can be more reliable than scraping', 547 | ].join('\n'), 548 | inputs: ['url'], 549 | }]; 550 | for (let {dataset_id, id, description, inputs, defaults = {}} of datasets) 551 | { 552 | let parameters = {}; 553 | for (let input of inputs) 554 | { 555 | let param_schema = input=='url' ? z.string().url() : z.string(); 556 | parameters[input] = defaults[input] !== undefined ? 557 | param_schema.default(defaults[input]) : param_schema; 558 | } 559 | server.addTool({ 560 | name: `web_data_${id}`, 561 | description, 562 | parameters: z.object(parameters), 563 | execute: tool_fn(`web_data_${id}`, async(data, ctx)=>{ 564 | let trigger_response = await axios({ 565 | url: 'https://api.brightdata.com/datasets/v3/trigger', 566 | params: {dataset_id, include_errors: true}, 567 | method: 'POST', 568 | data: [data], 569 | headers: api_headers(), 570 | }); 571 | if (!trigger_response.data?.snapshot_id) 572 | throw new Error('No snapshot ID returned from request'); 573 | let snapshot_id = trigger_response.data.snapshot_id; 574 | console.error(`[web_data_${id}] triggered collection with ` 575 | +`snapshot ID: ${snapshot_id}`); 576 | let max_attempts = 600; 577 | let attempts = 0; 578 | while (attempts < max_attempts) 579 | { 580 | try { 581 | if (ctx && ctx.reportProgress) 582 | { 583 | await ctx.reportProgress({ 584 | progress: attempts, 585 | total: max_attempts, 586 | message: `Polling for data (attempt ` 587 | +`${attempts + 1}/${max_attempts})`, 588 | }); 589 | } 590 | let snapshot_response = await axios({ 591 | url: `https://api.brightdata.com/datasets/v3` 592 | +`/snapshot/${snapshot_id}`, 593 | params: {format: 'json'}, 594 | method: 'GET', 595 | headers: api_headers(), 596 | }); 597 | if (snapshot_response.data?.status === 'running') 598 | { 599 | console.error(`[web_data_${id}] snapshot not ready, ` 600 | +`polling again (attempt ` 601 | +`${attempts + 1}/${max_attempts})`); 602 | attempts++; 603 | await new Promise(resolve=>setTimeout(resolve, 1000)); 604 | continue; 605 | } 606 | console.error(`[web_data_${id}] snapshot data received ` 607 | +`after ${attempts + 1} attempts`); 608 | let result_data = JSON.stringify(snapshot_response.data); 609 | return result_data; 610 | } catch(e){ 611 | console.error(`[web_data_${id}] polling error: ` 612 | +`${e.message}`); 613 | attempts++; 614 | await new Promise(resolve=>setTimeout(resolve, 1000)); 615 | } 616 | } 617 | throw new Error(`Timeout after ${max_attempts} seconds waiting ` 618 | +`for data`); 619 | }), 620 | }); 621 | } 622 | 623 | for (let tool of browser_tools) 624 | server.addTool(tool); 625 | 626 | console.error('Starting server...'); 627 | server.start({transportType: 'stdio'}); 628 | function tool_fn(name, fn){ 629 | return async(data, ctx)=>{ 630 | debug_stats.tool_calls[name] = debug_stats.tool_calls[name]||0; 631 | debug_stats.tool_calls[name]++; 632 | let ts = Date.now(); 633 | console.error(`[%s] executing %s`, name, JSON.stringify(data)); 634 | try { return await fn(data, ctx); } 635 | catch(e){ 636 | if (e.response) 637 | { 638 | console.error(`[%s] error %s %s: %s`, name, e.response.status, 639 | e.response.statusText, e.response.data); 640 | let message = e.response.data; 641 | if (message?.length) 642 | throw new Error(`HTTP ${e.response.status}: ${message}`); 643 | } 644 | else 645 | console.error(`[%s] error %s`, name, e.stack); 646 | throw e; 647 | } finally { 648 | let dur = Date.now()-ts; 649 | console.error(`[%s] tool finished in %sms`, name, dur); 650 | } 651 | }; 652 | } 653 | 654 | function search_url(engine, query){ 655 | let q = encodeURIComponent(query); 656 | if (engine=='yandex') 657 | return `https://yandex.com/search/?text=${q}`; 658 | if (engine=='bing') 659 | return `https://www.bing.com/search?q=${q}`; 660 | return `https://www.google.com/search?q=${q}`; 661 | } 662 | 663 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | startCommand: 2 | type: stdio 3 | configSchema: 4 | type: object 5 | required: 6 | - apiToken 7 | properties: 8 | apiToken: 9 | type: string 10 | description: "Bright Data API token, available in your Bright Data account settings" 11 | webUnlockerZone: 12 | type: string 13 | description: "Optional: The Web Unlocker zone name (defaults to 'mcp_unlocker')" 14 | browserZone: 15 | type: string 16 | description: "Optional: Zone name for the Browser API (enables browser control tools, deafults to 'mcp_browser')" 17 | commandFunction: |- 18 | config => ({ 19 | command: 'node', 20 | args: ['server.js'], 21 | env: { 22 | API_TOKEN: config.apiToken, 23 | WEB_UNLOCKER_ZONE: config.webUnlockerZone || 'mcp_unlocker', 24 | BROWSER_ZONE: config.browserZone || '' 25 | } 26 | }) 27 | --------------------------------------------------------------------------------