├── 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 |
4 |
5 |
6 |
7 | Bright Data MCP
8 | Enhance AI Agents with Real-Time Web Data
9 |
10 |
11 |
12 |
13 |
15 |
16 |
17 |
18 |
20 |
21 |
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 | 
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 | 
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 | [](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 |
--------------------------------------------------------------------------------