@decodo/mcp-server 1.0.3 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +93 -34
  2. package/build/clients/scraper-api-client.js +78 -16
  3. package/build/clients/scraper-api-client.js.map +1 -1
  4. package/build/constants.js +35 -1
  5. package/build/constants.js.map +1 -1
  6. package/build/index.js +11 -11
  7. package/build/index.js.map +1 -1
  8. package/build/server/sapi-base-server.js +76 -0
  9. package/build/server/sapi-base-server.js.map +1 -0
  10. package/build/server/sapi-http-server.js +11 -0
  11. package/build/server/sapi-http-server.js.map +1 -0
  12. package/build/server/sapi-stdio-server.js +11 -0
  13. package/build/server/sapi-stdio-server.js.map +1 -0
  14. package/build/server.js +53 -0
  15. package/build/server.js.map +1 -0
  16. package/build/tools/amazon-bestsellers/amazon-bestsellers-tool.js +55 -0
  17. package/build/tools/amazon-bestsellers/amazon-bestsellers-tool.js.map +1 -0
  18. package/build/tools/amazon-bestsellers/index.js +18 -0
  19. package/build/tools/amazon-bestsellers/index.js.map +1 -0
  20. package/build/tools/amazon-pricing/amazon-pricing-tool.js +67 -0
  21. package/build/tools/amazon-pricing/amazon-pricing-tool.js.map +1 -0
  22. package/build/tools/amazon-pricing/index.js +18 -0
  23. package/build/tools/amazon-pricing/index.js.map +1 -0
  24. package/build/tools/amazon-product/amazon-product-tool.js +62 -0
  25. package/build/tools/amazon-product/amazon-product-tool.js.map +1 -0
  26. package/build/tools/amazon-product/index.js +18 -0
  27. package/build/tools/amazon-product/index.js.map +1 -0
  28. package/build/tools/amazon-search/amazon-search-tool.js +63 -0
  29. package/build/tools/amazon-search/amazon-search-tool.js.map +1 -0
  30. package/build/tools/amazon-search/index.js +18 -0
  31. package/build/tools/amazon-search/index.js.map +1 -0
  32. package/build/tools/amazon-sellers/amazon-sellers-tool.js +56 -0
  33. package/build/tools/amazon-sellers/amazon-sellers-tool.js.map +1 -0
  34. package/build/tools/amazon-sellers/index.js +18 -0
  35. package/build/tools/amazon-sellers/index.js.map +1 -0
  36. package/build/tools/bing-search/bing-search-tool.js +64 -0
  37. package/build/tools/bing-search/bing-search-tool.js.map +1 -0
  38. package/build/tools/bing-search/index.js +18 -0
  39. package/build/tools/bing-search/index.js.map +1 -0
  40. package/build/tools/chatgpt/chatgpt-tool.js +47 -0
  41. package/build/tools/chatgpt/chatgpt-tool.js.map +1 -0
  42. package/build/tools/chatgpt/index.js +18 -0
  43. package/build/tools/chatgpt/index.js.map +1 -0
  44. package/build/tools/google-ads/google-ads-tool.js +59 -0
  45. package/build/tools/google-ads/google-ads-tool.js.map +1 -0
  46. package/build/tools/google-ads/index.js +18 -0
  47. package/build/tools/google-ads/index.js.map +1 -0
  48. package/build/tools/google-ai-mode/google-ai-mode-tool.js +50 -0
  49. package/build/tools/google-ai-mode/google-ai-mode-tool.js.map +1 -0
  50. package/build/tools/google-ai-mode/index.js +18 -0
  51. package/build/tools/google-ai-mode/index.js.map +1 -0
  52. package/build/tools/google-lens/google-lens-tool.js +52 -0
  53. package/build/tools/google-lens/google-lens-tool.js.map +1 -0
  54. package/build/tools/google-lens/index.js +18 -0
  55. package/build/tools/google-lens/index.js.map +1 -0
  56. package/build/tools/google-search/google-search-tool.js +60 -0
  57. package/build/tools/google-search/google-search-tool.js.map +1 -0
  58. package/build/tools/google-search/index.js +18 -0
  59. package/build/tools/google-search/index.js.map +1 -0
  60. package/build/tools/google-travel-hotels/google-travel-hotels-tool.js +52 -0
  61. package/build/tools/google-travel-hotels/google-travel-hotels-tool.js.map +1 -0
  62. package/build/tools/google-travel-hotels/index.js +18 -0
  63. package/build/tools/google-travel-hotels/index.js.map +1 -0
  64. package/build/tools/index.js +30 -5
  65. package/build/tools/index.js.map +1 -1
  66. package/build/tools/perplexity/index.js +18 -0
  67. package/build/tools/perplexity/index.js.map +1 -0
  68. package/build/tools/perplexity/perplexity-tool.js +46 -0
  69. package/build/tools/perplexity/perplexity-tool.js.map +1 -0
  70. package/build/tools/reddit-post/index.js +18 -0
  71. package/build/tools/reddit-post/index.js.map +1 -0
  72. package/build/tools/reddit-post/reddit-post-tool.js +51 -0
  73. package/build/tools/reddit-post/reddit-post-tool.js.map +1 -0
  74. package/build/tools/reddit-subreddit/index.js +18 -0
  75. package/build/tools/reddit-subreddit/index.js.map +1 -0
  76. package/build/tools/reddit-subreddit/reddit-subreddit-tool.js +49 -0
  77. package/build/tools/reddit-subreddit/reddit-subreddit-tool.js.map +1 -0
  78. package/build/tools/reddit-user/index.js +18 -0
  79. package/build/tools/reddit-user/index.js.map +1 -0
  80. package/build/tools/reddit-user/reddit-user-tool.js +55 -0
  81. package/build/tools/reddit-user/reddit-user-tool.js.map +1 -0
  82. package/build/tools/scrape-as-markdown/index.js +18 -0
  83. package/build/tools/scrape-as-markdown/index.js.map +1 -0
  84. package/build/tools/scrape-as-markdown/scrape-as-markdown-tool.js +79 -0
  85. package/build/tools/scrape-as-markdown/scrape-as-markdown-tool.js.map +1 -0
  86. package/build/tools/screenshot/index.js +18 -0
  87. package/build/tools/screenshot/index.js.map +1 -0
  88. package/build/tools/screenshot/screenshot-tool.js +48 -0
  89. package/build/tools/screenshot/screenshot-tool.js.map +1 -0
  90. package/build/tools/target-product/index.js +18 -0
  91. package/build/tools/target-product/index.js.map +1 -0
  92. package/build/tools/target-product/target-product-tool.js +50 -0
  93. package/build/tools/target-product/target-product-tool.js.map +1 -0
  94. package/build/tools/target-search/index.js +18 -0
  95. package/build/tools/target-search/index.js.map +1 -0
  96. package/build/tools/target-search/target-search-tool.js +56 -0
  97. package/build/tools/target-search/target-search-tool.js.map +1 -0
  98. package/build/tools/tiktok-post/index.js +18 -0
  99. package/build/tools/tiktok-post/index.js.map +1 -0
  100. package/build/tools/tiktok-post/tiktok-post-tool.js +48 -0
  101. package/build/tools/tiktok-post/tiktok-post-tool.js.map +1 -0
  102. package/build/tools/tiktok-shop-product/index.js +18 -0
  103. package/build/tools/tiktok-shop-product/index.js.map +1 -0
  104. package/build/tools/tiktok-shop-product/tiktok-shop-product-tool.js +47 -0
  105. package/build/tools/tiktok-shop-product/tiktok-shop-product-tool.js.map +1 -0
  106. package/build/tools/tiktok-shop-search/index.js +18 -0
  107. package/build/tools/tiktok-shop-search/index.js.map +1 -0
  108. package/build/tools/tiktok-shop-search/tiktok-shop-search-tool.js +54 -0
  109. package/build/tools/tiktok-shop-search/tiktok-shop-search-tool.js.map +1 -0
  110. package/build/tools/tiktok-shop-url/index.js +18 -0
  111. package/build/tools/tiktok-shop-url/index.js.map +1 -0
  112. package/build/tools/tiktok-shop-url/tiktok-shop-url-tool.js +45 -0
  113. package/build/tools/tiktok-shop-url/tiktok-shop-url-tool.js.map +1 -0
  114. package/build/tools/tool.js +7 -0
  115. package/build/tools/tool.js.map +1 -0
  116. package/build/tools/walmart-product/index.js +18 -0
  117. package/build/tools/walmart-product/index.js.map +1 -0
  118. package/build/tools/walmart-product/walmart-product-tool.js +61 -0
  119. package/build/tools/walmart-product/walmart-product-tool.js.map +1 -0
  120. package/build/tools/walmart-search/index.js +18 -0
  121. package/build/tools/walmart-search/index.js.map +1 -0
  122. package/build/tools/walmart-search/walmart-search-tool.js +62 -0
  123. package/build/tools/walmart-search/walmart-search-tool.js.map +1 -0
  124. package/build/tools/youtube-channel/index.js +18 -0
  125. package/build/tools/youtube-channel/index.js.map +1 -0
  126. package/build/tools/youtube-channel/youtube-channel-tool.js +48 -0
  127. package/build/tools/youtube-channel/youtube-channel-tool.js.map +1 -0
  128. package/build/tools/youtube-metadata/index.js +18 -0
  129. package/build/tools/youtube-metadata/index.js.map +1 -0
  130. package/build/tools/youtube-metadata/youtube-metadata-tool.js +43 -0
  131. package/build/tools/youtube-metadata/youtube-metadata-tool.js.map +1 -0
  132. package/build/tools/youtube-search/index.js +18 -0
  133. package/build/tools/youtube-search/index.js.map +1 -0
  134. package/build/tools/youtube-search/youtube-search-tool.js +43 -0
  135. package/build/tools/youtube-search/youtube-search-tool.js.map +1 -0
  136. package/build/tools/youtube-subtitles/index.js +18 -0
  137. package/build/tools/youtube-subtitles/index.js.map +1 -0
  138. package/build/tools/youtube-subtitles/youtube-subtitles-tool.js +48 -0
  139. package/build/tools/youtube-subtitles/youtube-subtitles-tool.js.map +1 -0
  140. package/build/utils.js +14 -3
  141. package/build/utils.js.map +1 -1
  142. package/build/zod/zod-types.js +12 -4
  143. package/build/zod/zod-types.js.map +1 -1
  144. package/package.json +37 -12
  145. package/build/sapi-mcp-server.js +0 -40
  146. package/build/sapi-mcp-server.js.map +0 -1
  147. package/build/tools/amazon-search-parsed-tool.js +0 -46
  148. package/build/tools/amazon-search-parsed-tool.js.map +0 -1
  149. package/build/tools/google-search-parsed-tool.js +0 -54
  150. package/build/tools/google-search-parsed-tool.js.map +0 -1
  151. package/build/tools/reddit-post-tool.js +0 -34
  152. package/build/tools/reddit-post-tool.js.map +0 -1
  153. package/build/tools/reddit-subreddit-tool.js +0 -34
  154. package/build/tools/reddit-subreddit-tool.js.map +0 -1
  155. package/build/tools/scrape-as-markdown-tool.js +0 -70
  156. package/build/tools/scrape-as-markdown-tool.js.map +0 -1
  157. package/build/tools/scrape-tool.js +0 -70
  158. package/build/tools/scrape-tool.js.map +0 -1
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # Decodo MCP Server
2
2
 
3
+ [![](https://dcbadge.limes.pink/api/server/https://discord.gg/Ja8dqKgvbZ)](https://discord.gg/Ja8dqKgvbZ)
4
+ [![Install MCP Server](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=Decodo&config=eyJ1cmwiOiJodHRwczovL21jcC5kZWNvZG8uY29tL21jcCIsImhlYWRlcnMiOnsiQXV0aG9yaXphdGlvbiI6IkJhc2ljIDx3ZWJfYWR2YW5jZWRfdG9rZW4%2BIn19)
3
5
  [![smithery badge](https://smithery.ai/badge/@Decodo/decodo-mcp-server)](https://smithery.ai/server/@Decodo/decodo-mcp-server)
4
6
 
5
7
  <p align="center">
6
- <a href="https://dashboard.decodo.com/register?page=scrapers%2Fpricing&utm_source=github.com&utm_medium=social&utm_campaign=scraper_api_github"><img src="https://github.com/user-attachments/assets/13b08523-32b0-4c85-8e99-580d7c2a9055"></a>
7
-
8
- [![](https://dcbadge.vercel.app/api/server/Ja8dqKgvbZ)](https://discord.gg/Ja8dqKgvbZ)
8
+ <a href="https://dashboard.decodo.com/integrations?utm_source=github&utm_medium=social&utm_campaign=mcp_server"> <img src="https://github.com/user-attachments/assets/a1e52a9e-3da1-4081-b3c6-053aafb8f196"/></a>
9
9
 
10
10
  This repository provides a Model Context Protocol (MCP) server that connects LLMs and applications
11
11
  to Decodo's platform. The server facilitates integration between MCP-compatible clients and Decodo's
@@ -20,23 +20,30 @@ services, streamlining access to our tools and capabilities.
20
20
  - Simple integration. Seamless setup with popular MCP clients like Claude Desktop, Cursor, and
21
21
  Windsurf.
22
22
 
23
- ## Quick start via Smithery (Recommended)
23
+ ## Connecting to [Decodo's MCP server](https://mcp.decodo.com/mcp)
24
24
 
25
- Visit the `decodo-mcp-server` page on
26
- [Smithery](https://smithery.ai/server/@Decodo/decodo-mcp-server), select your preferred MCP client
27
- and generate installation instructions.
25
+ 1. Go to [decodo.com](https://decodo.com/scraping/web) and start a Web Scraping API plan (free plan
26
+ available).
28
27
 
29
- ![Smithery interface](img/smithery.png 'Smithery UI')
28
+ 2. Once your plan has started, obtain a Web Scraping API basic authentication token from the
29
+ [dashboard](https://dashboard.decodo.com/).
30
30
 
31
- ## Obtain Web Scraping API credentials
31
+ 3. Open your preferred MCP client and add the following configuration:
32
32
 
33
- You'll need the Decodo Advanced Web Scraping API credentials, which you can get by starting a free
34
- trial on the [dashboard](https://dashboard.decodo.com/). Once you have a plan activated, take a note
35
- of your generated username and password:
33
+ ```
34
+ {
35
+ "Decodo": {
36
+ "url": "https://mcp.decodo.com/mcp",
37
+ "headers": {
38
+ "Authorization": "Basic <basic_auth_token>"
39
+ }
40
+ }
41
+ }
42
+ ```
36
43
 
37
- ![Decodo dashboard](img/auth.png 'Decodo dashboard')
44
+ ## Running the MCP server locally
38
45
 
39
- ## Running the MCP server locally (manual)
46
+ <details>
40
47
 
41
48
  ### Prerequisites
42
49
 
@@ -76,11 +83,6 @@ this:
76
83
 
77
84
  4. Update your MCP client with the server information:
78
85
 
79
- ### Claude Desktop
80
-
81
- [Follow the guide here](https://modelcontextprotocol.io/quickstart/user) to find the setup file,
82
- then update `claude_desktop_config.json` to look like this:
83
-
84
86
  ```
85
87
  {
86
88
  "mcpServers": {
@@ -94,35 +96,92 @@ then update `claude_desktop_config.json` to look like this:
94
96
  }
95
97
  }
96
98
  }
99
+ ```
100
+
101
+ </details>
102
+
103
+ ## Toolsets
97
104
 
105
+ Tools are organized into toolsets. You can selectively enable specific toolsets by passing a
106
+ comma-separated list via the `toolsets` query parameter:
107
+
108
+ ```
109
+ "Decodo MCP Server": {
110
+ "url": "https://mcp.decodo.com/mcp?toolsets=web,ai",
111
+ "headers": {
112
+ "Authorization": "Basic <your_auth_token>"
113
+ }
114
+ }
98
115
  ```
99
116
 
100
- ### Cursor
117
+ When no toolsets are specified, all tools are registered.
101
118
 
102
- For installation instructions, see the
103
- [Cursor documentation](https://docs.cursor.com/context/model-context-protocol#installing-mcp-servers).
119
+ | Toolset | Tools |
120
+ | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
121
+ | `web` | `scrape_as_markdown`, `screenshot` |
122
+ | `search` | `google_search`, `google_ads`, `google_lens`, `google_travel_hotels`, `bing_search` |
123
+ | `ecommerce` | `amazon_search`, `amazon_product`, `amazon_pricing`, `amazon_sellers`, `amazon_bestsellers`, `walmart_search`, `walmart_product`, `target_search`, `target_product`, `tiktok_shop_search`, `tiktok_shop_product`, `tiktok_shop_url` |
124
+ | `social_media` | `reddit_post`, `reddit_subreddit`, `reddit_user`, `tiktok_post`, `youtube_metadata`, `youtube_channel`, `youtube_subtitles`, `youtube_search` |
125
+ | `ai` | `chatgpt`, `perplexity`, `google_ai_mode` |
104
126
 
105
127
  ## Tools
106
128
 
107
129
  The server exposes the following tools:
108
130
 
109
- | Tool | Description | Example prompt |
110
- | ---------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------- |
111
- | `scrape_as_markdown` | Scrapes any target URL, expects a URL to be given via prompt. Returns results in Markdown. | Scrape peacock.com from a US IP address and tell me the pricing. |
112
- | `google_search_parsed` | Scrapes Google Search for a given query, and returns parsed results. | Scrape Google Search for shoes and tell me the top position. |
113
- | `amazon_search_parsed` | Scrapes Amazon Search for a given query, and returns parsed results. | Scrape Amazon Search for toothbrushes |
131
+ | Tool | Description | Example prompt |
132
+ | ---------------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- |
133
+ | `scrape_as_markdown` | Scrapes any target URL, expects a URL to be given via prompt. Returns results in Markdown. | Scrape peacock.com from a US IP address and tell me the pricing. |
134
+ | `screenshot` | Captures a screenshot of any webpage and returns it as a PNG image. | Take a screenshot of github.com from a US IP address. |
135
+ | `google_search` | Scrapes Google Search for a given query, and returns parsed results. | Scrape Google Search for shoes and tell me the top position. |
136
+ | `google_ads` | Scrapes Google Ads search results with automatic parsing. | Scrape Google Ads for laptop and show me the top ads. |
137
+ | `google_lens` | Scrapes Google Lens image search results with automatic parsing. | Search Google Lens for this image: https://example.com/image.jpg |
138
+ | `google_ai_mode` | Scrapes Google AI Mode (Search with AI) results with automatic parsing. | Ask Google AI Mode: What are the top three dog breeds? |
139
+ | `google_travel_hotels` | Scrapes Google Travel Hotels search results. | Search Google Travel Hotels for hotels in Paris. |
140
+ | `amazon_search` | Scrapes Amazon Search for a given query, and returns parsed results. | Scrape Amazon Search for wireless keyboard. |
141
+ | `amazon_product` | Scrapes Amazon Product page with automatic parsing. | Scrape Amazon product B09H74FXNW and show me the details. |
142
+ | `amazon_pricing` | Scrapes Amazon Product pricing information with automatic parsing. | Get pricing for Amazon product B09H74FXNW. |
143
+ | `amazon_sellers` | Scrapes Amazon Seller information with automatic parsing. | Get information about Amazon seller A1R0Z7FJGTKESH. |
144
+ | `amazon_bestsellers` | Scrapes Amazon Bestsellers list with automatic parsing. | Show me Amazon bestsellers in electronics. |
145
+ | `walmart_search` | Scrapes Walmart Search for a given query, and returns parsed results. | Scrape Walmart Search for camping tent. |
146
+ | `walmart_product` | Scrapes Walmart Product page with automatic parsing. | Scrape Walmart product 15296401808. |
147
+ | `target_search` | Scrapes Target Search for a given query, and returns parsed results. | Scrape Target Search for kitchen appliances. |
148
+ | `target_product` | Scrapes Target Product page with automatic parsing. | Scrape Target product 92186007. |
149
+ | `tiktok_post` | Scrapes a TikTok post URL for structured data (e.g. engagement, caption, hashtags). | Scrape this TikTok post: https://www.tiktok.com/@nba/video/7393013274725403950 |
150
+ | `tiktok_shop_search` | Scrapes TikTok Shop Search for a given query, and returns parsed results. | Scrape TikTok Shop Search for phone cases. |
151
+ | `tiktok_shop_product` | Scrapes TikTok Shop Product page. | Scrape TikTok Shop product 1731541214379741272. |
152
+ | `tiktok_shop_url` | Scrapes TikTok Shop page by URL. | Scrape this TikTok Shop URL: https://www.tiktok.com/shop/s?q=HEADPHONES |
153
+ | `youtube_metadata` | Scrapes YouTube video metadata. | Get metadata for YouTube video dFu9aKJoqGg. |
154
+ | `youtube_channel` | Scrapes YouTube channel videos with automatic parsing. | Scrape YouTube channel @decodo_official. |
155
+ | `youtube_subtitles` | Scrapes YouTube video subtitles. | Get subtitles for YouTube video L8zSWbQN-v8. |
156
+ | `youtube_search` | Search YouTube videos. | Search YouTube for "How to care for chinchillas". |
157
+ | `reddit_post` | Scrapes a specific Reddit post. | Scrape the following Reddit post: https://www.reddit.com/r/horseracing/comments/1nsrn3/ |
158
+ | `reddit_subreddit` | Scrapes Reddit subreddit results with automatic parsing. | Scrape the top 5 posts on r/Python this week. |
159
+ | `reddit_user` | Scrapes a Reddit user profile and their posts/comments. | Scrape this Reddit user: https://www.reddit.com/user/IWasRightOnce/ |
160
+ | `bing_search` | Scrapes Bing Search results with automatic parsing. | Search Bing for laptop reviews. |
161
+ | `chatgpt` | Search and interact with ChatGPT for AI-powered responses and conversations. | Ask ChatGPT to explain quantum computing in simple terms. |
162
+ | `perplexity` | Search and interact with Perplexity for AI-powered responses and conversations. | Ask Perplexity what the latest trends in web development are. |
114
163
 
115
164
  ## Parameters
116
165
 
117
166
  The following parameters are inferred from user prompts:
118
167
 
119
- | Parameter | Description |
120
- | -------------- | ---------------------------------------------------------------------------------------------------- |
121
- | `jsRender` | Renders target URL in a headless browser. |
122
- | `geo` | Sets the country from which the request will originate. |
123
- | `locale` | Sets the locale of the request. |
124
- | `tokenLimit` | Truncates the response content up to this limit. Useful if the context window is small. |
125
- | `fullResponse` | Skips automatic truncation and returns full content. If context window is small, may throw warnings. |
168
+ | Parameter | Description |
169
+ | --------------- | ----------------------------------------------------------------------------------------------------- |
170
+ | `jsRender` | Renders target URL in a headless browser. |
171
+ | `geo` | Sets the country from which the request will originate. |
172
+ | `locale` | Sets the locale of the request. |
173
+ | `tokenLimit` | Truncates the response content up to this limit. Useful if the context window is small. |
174
+ | `prompt` | Prompt to send to AI tools (`chatgpt`, `perplexity`). |
175
+ | `search` | Activates ChatGPT's web search functionality (`chatgpt` only). |
176
+ | `xhr` | When true, includes XHR or fetch responses in the scrape result where supported (e.g. `tiktok_post`). |
177
+ | `deviceType` | Device type to emulate for the request (`desktop`, `mobile`, `tablet`). |
178
+ | `domain` | Domain to use for the request (e.g., `amazon.com`, `amazon.co.uk`, `bing.com`). |
179
+ | `pageFrom` | Starting page number for pagination. |
180
+ | `deliveryZip` | ZIP code for delivery location (Target, Walmart). |
181
+ | `storeId` | Store ID for local inventory (Target, Walmart). |
182
+ | `country` | Country for TikTok Shop requests. |
183
+ | `limit` | Maximum number of results to return (e.g., YouTube channel videos). |
184
+ | `language_code` | Language code for subtitles (e.g., `en`, `es`). |
126
185
 
127
186
  ## Examples
128
187
 
@@ -13,35 +13,97 @@ var __rest = (this && this.__rest) || function (s, e) {
13
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
+ var _a;
16
17
  Object.defineProperty(exports, "__esModule", { value: true });
17
18
  exports.ScraperApiClient = void 0;
18
19
  const axios_1 = __importDefault(require("axios"));
20
+ const MAX_RETRIES = Math.max(0, parseInt((_a = process.env.MAX_RETRIES) !== null && _a !== void 0 ? _a : '2', 10) || 2);
21
+ const RETRYABLE_STATUS_CODES = new Set([429, 502, 503, 504]);
22
+ const RETRYABLE_NETWORK_CODES = new Set(['ECONNRESET', 'ETIMEDOUT', 'ECONNABORTED', 'ENOTFOUND']);
23
+ const isRetryable = (error) => {
24
+ var _a;
25
+ if (error.response) {
26
+ return RETRYABLE_STATUS_CODES.has(error.response.status);
27
+ }
28
+ return RETRYABLE_NETWORK_CODES.has((_a = error.code) !== null && _a !== void 0 ? _a : '');
29
+ };
30
+ const getRetryDelay = (attempt, error) => {
31
+ var _a;
32
+ if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 429) {
33
+ const retryAfter = error.response.headers['retry-after'];
34
+ if (retryAfter) {
35
+ const seconds = Number(retryAfter);
36
+ if (!isNaN(seconds)) {
37
+ return seconds * 1000;
38
+ }
39
+ const date = Date.parse(retryAfter);
40
+ if (!isNaN(date)) {
41
+ return Math.max(0, date - Date.now());
42
+ }
43
+ }
44
+ }
45
+ const baseMs = 1000 * Math.pow(2, attempt);
46
+ const jitterMs = Math.random() * 500;
47
+ return baseMs + jitterMs;
48
+ };
49
+ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
19
50
  class ScraperApiClient {
20
- constructor({ auth }) {
51
+ constructor() {
21
52
  this.transformScrapingParams = ({ scrapingParams, }) => {
22
- const { jsRender } = scrapingParams, rest = __rest(scrapingParams, ["jsRender"]);
23
- const transformed = Object.assign(Object.assign({}, (jsRender && { headless: 'html' })), rest);
53
+ const { jsRender, headless } = scrapingParams, rest = __rest(scrapingParams, ["jsRender", "headless"]);
54
+ const transformed = Object.assign(Object.assign({}, (headless ? { headless } : jsRender && { headless: 'html' })), rest);
24
55
  return transformed;
25
56
  };
26
57
  this.transformResponse = ({ res }) => {
27
58
  const content = res.data.results[0].content;
28
59
  return Object.assign(Object.assign({}, res), { data: content });
29
60
  };
30
- this.scrape = async ({ scrapingParams }) => {
61
+ this.scrape = async ({ auth, scrapingParams, }) => {
62
+ var _a, _b, _c, _d, _e;
31
63
  const transformedParams = this.transformScrapingParams({ scrapingParams });
32
- const res = await axios_1.default.request({
33
- url: 'https://scraper-api.decodo.com/v2/scrape',
34
- method: 'POST',
35
- headers: {
36
- authorization: `Basic ${this.auth}`,
37
- 'x-integration': 'mcp',
38
- },
39
- data: Object.assign({}, transformedParams),
40
- });
41
- const response = this.transformResponse({ res });
42
- return response;
64
+ const url = process.env.DECODO_SAPI_HOST || 'https://scraper-api.decodo.com';
65
+ let lastError;
66
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
67
+ try {
68
+ const res = await axios_1.default.request({
69
+ url: `${url}/v2/scrape`,
70
+ method: 'POST',
71
+ headers: {
72
+ authorization: `Basic ${auth}`,
73
+ 'x-integration': 'mcp',
74
+ },
75
+ timeout: 180000,
76
+ data: Object.assign({}, transformedParams),
77
+ });
78
+ return this.transformResponse({ res });
79
+ }
80
+ catch (error) {
81
+ lastError = error;
82
+ if (attempt < MAX_RETRIES && axios_1.default.isAxiosError(error) && isRetryable(error)) {
83
+ const delayMs = getRetryDelay(attempt, error);
84
+ const reason = error.response
85
+ ? `status ${error.response.status}`
86
+ : `network error ${error.code}`;
87
+ console.error(`[scraper-api-client] Retry ${attempt + 1}/${MAX_RETRIES} after ${reason}, waiting ${Math.round(delayMs)}ms`);
88
+ await sleep(delayMs);
89
+ continue;
90
+ }
91
+ break;
92
+ }
93
+ }
94
+ if (axios_1.default.isAxiosError(lastError)) {
95
+ const status = (_a = lastError.response) === null || _a === void 0 ? void 0 : _a.status;
96
+ let errorMessage = (_d = (_c = (_b = lastError.response) === null || _b === void 0 ? void 0 : _b.data) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : lastError.message;
97
+ if (status === 401) {
98
+ errorMessage = 'Authentication failed.';
99
+ }
100
+ if (status === 429) {
101
+ errorMessage = JSON.stringify((_e = lastError.response) === null || _e === void 0 ? void 0 : _e.data);
102
+ }
103
+ throw new Error(`Scraper API request failed (${status}): ${errorMessage}`);
104
+ }
105
+ throw lastError;
43
106
  };
44
- this.auth = auth;
45
107
  }
46
108
  }
47
109
  exports.ScraperApiClient = ScraperApiClient;
@@ -1 +1 @@
1
- {"version":3,"file":"scraper-api-client.js","sourceRoot":"","sources":["../../src/clients/scraper-api-client.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,kDAA6C;AAI7C,MAAa,gBAAgB;IAG3B,YAAY,EAAE,IAAI,EAAoB;QAItC,4BAAuB,GAAG,CAAC,EACzB,cAAc,GAGf,EAAoB,EAAE;YACrB,MAAM,EAAE,QAAQ,KAAc,cAAc,EAAvB,IAAI,UAAK,cAAc,EAAtC,YAAqB,CAAiB,CAAC;YAC7C,MAAM,WAAW,mCAAQ,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAK,IAAI,CAAE,CAAC;YAEvE,OAAO,WAAW,CAAC;QACrB,CAAC,CAAC;QAEF,sBAAiB,GAAG,CAAI,EAAE,GAAG,EAAqD,EAAE,EAAE;YACpF,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAE5C,uCAAY,GAAG,KAAE,IAAI,EAAE,OAAO,IAAG;QACnC,CAAC,CAAC;QAEF,WAAM,GAAG,KAAK,EAAc,EAAE,cAAc,EAAyC,EAAE,EAAE;YACvF,MAAM,iBAAiB,GAAG,IAAI,CAAC,uBAAuB,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;YAE3E,MAAM,GAAG,GAAG,MAAM,eAAK,CAAC,OAAO,CAA4B;gBACzD,GAAG,EAAE,0CAA0C;gBAC/C,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,aAAa,EAAE,SAAS,IAAI,CAAC,IAAI,EAAE;oBACnC,eAAe,EAAE,KAAK;iBACvB;gBACD,IAAI,oBACC,iBAAiB,CACrB;aACF,CAAC,CAAC;YAEH,MAAM,QAAQ,GAAG,IAAI,CAAC,iBAAiB,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;YAEjD,OAAO,QAAQ,CAAC;QAClB,CAAC,CAAC;QAtCA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CAsCF;AA3CD,4CA2CC"}
1
+ {"version":3,"file":"scraper-api-client.js","sourceRoot":"","sources":["../../src/clients/scraper-api-client.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,45 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SCRAPER_API_TARGETS = void 0;
3
+ exports.SCRAPER_API_TARGETS = exports.TOOLSET = void 0;
4
+ var TOOLSET;
5
+ (function (TOOLSET) {
6
+ TOOLSET["WEB"] = "web";
7
+ TOOLSET["SEARCH"] = "search";
8
+ TOOLSET["ECOMMERCE"] = "ecommerce";
9
+ TOOLSET["SOCIAL_MEDIA"] = "social_media";
10
+ TOOLSET["AI"] = "ai";
11
+ })(TOOLSET || (exports.TOOLSET = TOOLSET = {}));
12
+ // todo: utils
4
13
  var SCRAPER_API_TARGETS;
5
14
  (function (SCRAPER_API_TARGETS) {
6
15
  SCRAPER_API_TARGETS["GOOGLE_SEARCH"] = "google_search";
16
+ SCRAPER_API_TARGETS["GOOGLE_TRAVEL_HOTELS"] = "google_travel_hotels";
17
+ SCRAPER_API_TARGETS["GOOGLE_ADS"] = "google_ads";
18
+ SCRAPER_API_TARGETS["GOOGLE_LENS"] = "google_lens";
19
+ SCRAPER_API_TARGETS["GOOGLE_AI_MODE"] = "google_ai_mode";
7
20
  SCRAPER_API_TARGETS["AMAZON_SEARCH"] = "amazon_search";
21
+ SCRAPER_API_TARGETS["AMAZON_PRODUCT"] = "amazon_product";
22
+ SCRAPER_API_TARGETS["AMAZON_PRICING"] = "amazon_pricing";
23
+ SCRAPER_API_TARGETS["AMAZON_SELLERS"] = "amazon_sellers";
24
+ SCRAPER_API_TARGETS["AMAZON_BESTSELLERS"] = "amazon_bestsellers";
25
+ SCRAPER_API_TARGETS["WALMART_SEARCH"] = "walmart_search";
26
+ SCRAPER_API_TARGETS["WALMART_PRODUCT"] = "walmart_product";
27
+ SCRAPER_API_TARGETS["TARGET_SEARCH"] = "target_search";
28
+ SCRAPER_API_TARGETS["TARGET_PRODUCT"] = "target_product";
29
+ SCRAPER_API_TARGETS["TIKTOK_POST"] = "tiktok_post";
30
+ SCRAPER_API_TARGETS["TIKTOK_SHOP_SEARCH"] = "tiktok_shop_search";
31
+ SCRAPER_API_TARGETS["TIKTOK_SHOP_PRODUCT"] = "tiktok_shop_product";
32
+ SCRAPER_API_TARGETS["TIKTOK_SHOP_URL"] = "tiktok";
33
+ SCRAPER_API_TARGETS["YOUTUBE_VIDEO"] = "youtube_video";
34
+ SCRAPER_API_TARGETS["YOUTUBE_METADATA"] = "youtube_metadata";
35
+ SCRAPER_API_TARGETS["YOUTUBE_CHANNEL"] = "youtube_channel";
36
+ SCRAPER_API_TARGETS["YOUTUBE_SUBTITLES"] = "youtube_subtitles";
37
+ SCRAPER_API_TARGETS["YOUTUBE_SEARCH"] = "youtube_search";
8
38
  SCRAPER_API_TARGETS["REDDIT_POST"] = "reddit_post";
9
39
  SCRAPER_API_TARGETS["REDDIT_SUBREDDIT"] = "reddit_subreddit";
40
+ SCRAPER_API_TARGETS["REDDIT_USER"] = "reddit_user";
41
+ SCRAPER_API_TARGETS["BING_SEARCH"] = "bing_search";
42
+ SCRAPER_API_TARGETS["CHATGPT"] = "chatgpt";
43
+ SCRAPER_API_TARGETS["PERPLEXITY"] = "perplexity";
10
44
  })(SCRAPER_API_TARGETS || (exports.SCRAPER_API_TARGETS = SCRAPER_API_TARGETS = {}));
11
45
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAA,IAAY,mBAMX;AAND,WAAY,mBAAmB;IAC7B,sDAA+B,CAAA;IAC/B,sDAA+B,CAAA;IAE/B,kDAA2B,CAAA;IAC3B,4DAAqC,CAAA;AACvC,CAAC,EANW,mBAAmB,mCAAnB,mBAAmB,QAM9B"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAA,IAAY,OAMX;AAND,WAAY,OAAO;IACjB,sBAAW,CAAA;IACX,4BAAiB,CAAA;IACjB,kCAAuB,CAAA;IACvB,wCAA6B,CAAA;IAC7B,oBAAS,CAAA;AACX,CAAC,EANW,OAAO,uBAAP,OAAO,QAMlB;AAED,cAAc;AACd,IAAY,mBAsCX;AAtCD,WAAY,mBAAmB;IAC7B,sDAA+B,CAAA;IAC/B,oEAA6C,CAAA;IAC7C,gDAAyB,CAAA;IACzB,kDAA2B,CAAA;IAC3B,wDAAiC,CAAA;IAEjC,sDAA+B,CAAA;IAC/B,wDAAiC,CAAA;IACjC,wDAAiC,CAAA;IACjC,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;IAEzC,wDAAiC,CAAA;IACjC,0DAAmC,CAAA;IAEnC,sDAA+B,CAAA;IAC/B,wDAAiC,CAAA;IAEjC,kDAA2B,CAAA;IAC3B,gEAAyC,CAAA;IACzC,kEAA2C,CAAA;IAC3C,iDAA0B,CAAA;IAE1B,sDAA+B,CAAA;IAC/B,4DAAqC,CAAA;IACrC,0DAAmC,CAAA;IACnC,8DAAuC,CAAA;IACvC,wDAAiC,CAAA;IAEjC,kDAA2B,CAAA;IAC3B,4DAAqC,CAAA;IACrC,kDAA2B,CAAA;IAE3B,kDAA2B,CAAA;IAE3B,0CAAmB,CAAA;IACnB,gDAAyB,CAAA;AAC3B,CAAC,EAtCW,mBAAmB,mCAAnB,mBAAmB,QAsC9B"}
package/build/index.js CHANGED
@@ -1,17 +1,16 @@
1
+ #!/usr/bin/env node
1
2
  "use strict";
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
4
  require("dotenv/config");
4
- const process_1 = require("process");
5
5
  const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
- const sapi_mcp_server_1 = require("./sapi-mcp-server");
7
- if (process.env.ENABLE_MCPS_LOGGER) {
8
- import('mcps-logger/console');
9
- }
6
+ const sapi_stdio_server_1 = require("./server/sapi-stdio-server");
7
+ const utils_1 = require("./utils");
10
8
  const parseEnvsOrExit = () => {
11
9
  const envs = ['SCRAPER_API_USERNAME', 'SCRAPER_API_PASSWORD'];
12
10
  for (const envKey of envs) {
13
11
  if (!process.env[envKey]) {
14
- (0, process_1.exit)(`env ${envKey} missing`);
12
+ console.error(`env ${envKey} missing`);
13
+ process.exit(1);
15
14
  }
16
15
  }
17
16
  return {
@@ -19,17 +18,18 @@ const parseEnvsOrExit = () => {
19
18
  sapiPassword: process.env['SCRAPER_API_PASSWORD'],
20
19
  };
21
20
  };
22
- async function main() {
21
+ const main = async () => {
23
22
  const transport = new stdio_js_1.StdioServerTransport();
24
23
  // if there are no envs, some MCP clients will fail silently
25
24
  const { sapiUsername, sapiPassword } = parseEnvsOrExit();
26
- const sapiMcpServer = new sapi_mcp_server_1.ScraperAPIMCPServer({
27
- sapiUsername,
28
- sapiPassword,
25
+ const auth = Buffer.from(`${sapiUsername}:${sapiPassword}`).toString('base64');
26
+ const sapiMcpServer = new sapi_stdio_server_1.ScraperAPIStdioServer({
27
+ auth,
28
+ toolsets: (0, utils_1.resolveToolsets)(process.env.TOOLSETS),
29
29
  });
30
30
  await sapiMcpServer.connect(transport);
31
31
  console.error('MCP Server running on stdio');
32
- }
32
+ };
33
33
  main().catch(error => {
34
34
  console.error('Fatal error in main():', error);
35
35
  process.exit(1);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAAA,yBAAuB;AAEvB,qCAA+B;AAC/B,wEAAiF;AACjF,uDAAwD;AAExD,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC;IACnC,MAAM,CAAC,qBAAqB,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,eAAe,GAAG,GAA2B,EAAE;IACnD,MAAM,IAAI,GAAG,CAAC,sBAAsB,EAAE,sBAAsB,CAAC,CAAC;IAE9D,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,IAAA,cAAI,EAAC,OAAO,MAAM,UAAU,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,OAAO;QACL,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAW;QAC3D,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAW;KAC5D,CAAC;AACJ,CAAC,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;IAE7C,4DAA4D;IAC5D,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,eAAe,EAAE,CAAC;IAEzD,MAAM,aAAa,GAAG,IAAI,qCAAmB,CAAC;QAC5C,YAAY;QACZ,YAAY;KACb,CAAC,CAAC;IACH,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvC,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;AAC/C,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AACA,yBAAuB;AAEvB,wEAAiF;AACjF,kEAAmE;AACnE,mCAA0C;AAE1C,MAAM,eAAe,GAAG,GAA2B,EAAE;IACnD,MAAM,IAAI,GAAG,CAAC,sBAAsB,EAAE,sBAAsB,CAAC,CAAC;IAE9D,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,OAAO,MAAM,UAAU,CAAC,CAAC;YACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,OAAO;QACL,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAW;QAC3D,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAW;KAC5D,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,IAAI,GAAG,KAAK,IAAI,EAAE;IACtB,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;IAE7C,4DAA4D;IAC5D,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,GAAG,eAAe,EAAE,CAAC;IAEzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,IAAI,YAAY,EAAE,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAE/E,MAAM,aAAa,GAAG,IAAI,yCAAqB,CAAC;QAC9C,IAAI;QACJ,QAAQ,EAAE,IAAA,uBAAe,EAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;KAChD,CAAC,CAAC;IACH,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvC,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;AAC/C,CAAC,CAAC;AAEF,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;IAC/C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScraperAPIBaseServer = void 0;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const scraper_api_client_1 = require("../clients/scraper-api-client");
6
+ const tools_1 = require("../tools");
7
+ class ScraperAPIBaseServer {
8
+ constructor({ auth, toolsets = [] }) {
9
+ this.auth = '';
10
+ this.server = new mcp_js_1.McpServer({
11
+ name: 'decodo',
12
+ version: '1.1.1',
13
+ });
14
+ this.sapiClient = new scraper_api_client_1.ScraperApiClient();
15
+ this.auth = auth;
16
+ this.registerTools({ toolsets });
17
+ this.registerResources();
18
+ }
19
+ connect(transport) {
20
+ this.server.connect(transport);
21
+ }
22
+ registerTools({ toolsets }) {
23
+ if (toolsets.length === 0) {
24
+ this.registerAllTools();
25
+ return;
26
+ }
27
+ for (const toolset of toolsets) {
28
+ const tools = ScraperAPIBaseServer.allTools.filter(tool => tool.toolset === toolset);
29
+ for (const tool of tools) {
30
+ tool.register({ server: this.server, sapiClient: this.sapiClient, auth: this.auth });
31
+ }
32
+ }
33
+ }
34
+ registerAllTools() {
35
+ for (const tool of ScraperAPIBaseServer.allTools) {
36
+ tool.register({ server: this.server, sapiClient: this.sapiClient, auth: this.auth });
37
+ }
38
+ }
39
+ registerResources() {
40
+ // todo: expose info about all targets available
41
+ }
42
+ }
43
+ exports.ScraperAPIBaseServer = ScraperAPIBaseServer;
44
+ ScraperAPIBaseServer.allTools = [
45
+ new tools_1.ScrapeAsMarkdownTool(),
46
+ new tools_1.ScreenshotTool(),
47
+ new tools_1.GoogleSearchTool(),
48
+ new tools_1.GoogleAdsTool(),
49
+ new tools_1.GoogleLensTool(),
50
+ new tools_1.GoogleAiModeTool(),
51
+ new tools_1.GoogleTravelHotelsTool(),
52
+ new tools_1.AmazonSearchTool(),
53
+ new tools_1.AmazonProductTool(),
54
+ new tools_1.AmazonPricingTool(),
55
+ new tools_1.AmazonSellersTool(),
56
+ new tools_1.AmazonBestsellersTool(),
57
+ new tools_1.WalmartSearchTool(),
58
+ new tools_1.WalmartProductTool(),
59
+ new tools_1.TargetSearchTool(),
60
+ new tools_1.TargetProductTool(),
61
+ new tools_1.TiktokPostTool(),
62
+ new tools_1.TiktokShopSearchTool(),
63
+ new tools_1.TiktokShopProductTool(),
64
+ new tools_1.TiktokShopUrlTool(),
65
+ new tools_1.YoutubeMetadataTool(),
66
+ new tools_1.YoutubeChannelTool(),
67
+ new tools_1.YoutubeSubtitlesTool(),
68
+ new tools_1.YoutubeSearchTool(),
69
+ new tools_1.RedditPostTool(),
70
+ new tools_1.RedditSubredditTool(),
71
+ new tools_1.RedditUserTool(),
72
+ new tools_1.BingSearchTool(),
73
+ new tools_1.ChatGPTTool(),
74
+ new tools_1.PerplexityTool(),
75
+ ];
76
+ //# sourceMappingURL=sapi-base-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sapi-base-server.js","sourceRoot":"","sources":["../../src/server/sapi-base-server.ts"],"names":[],"mappings":";;;AAAA,oEAAoE;AAEpE,sEAAiE;AACjE,oCA+BkB;AAKlB,MAAa,oBAAoB;IAO/B,YAAY,EAAE,IAAI,EAAE,QAAQ,GAAG,EAAE,EAAyC;QAF1E,SAAI,GAAW,EAAE,CAAC;QAGhB,IAAI,CAAC,MAAM,GAAG,IAAI,kBAAS,CAAC;YAC1B,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,OAAO;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,GAAG,IAAI,qCAAgB,EAAE,CAAC;QAEzC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAEjB,IAAI,CAAC,aAAa,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;QAEjC,IAAI,CAAC,iBAAiB,EAAE,CAAC;IAC3B,CAAC;IAED,OAAO,CAAC,SAA+D;QACrE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IAmCD,aAAa,CAAC,EAAE,QAAQ,EAA2B;QACjD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,OAAO;QACT,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,oBAAoB,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,KAAK,OAAO,CAAC,CAAC;YACrF,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACvF,CAAC;QACH,CAAC;IACH,CAAC;IAED,gBAAgB;QACd,KAAK,MAAM,IAAI,IAAI,oBAAoB,CAAC,QAAQ,EAAE,CAAC;YACjD,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACvF,CAAC;IACH,CAAC;IAED,iBAAiB;QACf,gDAAgD;IAClD,CAAC;;AAhFH,oDAiFC;AAxDQ,6BAAQ,GAAW;IACxB,IAAI,4BAAoB,EAAE;IAC1B,IAAI,sBAAc,EAAE;IACpB,IAAI,wBAAgB,EAAE;IACtB,IAAI,qBAAa,EAAE;IACnB,IAAI,sBAAc,EAAE;IACpB,IAAI,wBAAgB,EAAE;IACtB,IAAI,8BAAsB,EAAE;IAC5B,IAAI,wBAAgB,EAAE;IACtB,IAAI,yBAAiB,EAAE;IACvB,IAAI,yBAAiB,EAAE;IACvB,IAAI,yBAAiB,EAAE;IACvB,IAAI,6BAAqB,EAAE;IAC3B,IAAI,yBAAiB,EAAE;IACvB,IAAI,0BAAkB,EAAE;IACxB,IAAI,wBAAgB,EAAE;IACtB,IAAI,yBAAiB,EAAE;IACvB,IAAI,sBAAc,EAAE;IACpB,IAAI,4BAAoB,EAAE;IAC1B,IAAI,6BAAqB,EAAE;IAC3B,IAAI,yBAAiB,EAAE;IACvB,IAAI,2BAAmB,EAAE;IACzB,IAAI,0BAAkB,EAAE;IACxB,IAAI,4BAAoB,EAAE;IAC1B,IAAI,yBAAiB,EAAE;IACvB,IAAI,sBAAc,EAAE;IACpB,IAAI,2BAAmB,EAAE;IACzB,IAAI,sBAAc,EAAE;IACpB,IAAI,sBAAc,EAAE;IACpB,IAAI,mBAAW,EAAE;IACjB,IAAI,sBAAc,EAAE;CACrB,AA/Bc,CA+Bb"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScraperAPIHttpServer = void 0;
4
+ const sapi_base_server_1 = require("./sapi-base-server");
5
+ class ScraperAPIHttpServer extends sapi_base_server_1.ScraperAPIBaseServer {
6
+ constructor({ toolsets = [], auth }) {
7
+ super({ auth, toolsets });
8
+ }
9
+ }
10
+ exports.ScraperAPIHttpServer = ScraperAPIHttpServer;
11
+ //# sourceMappingURL=sapi-http-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sapi-http-server.js","sourceRoot":"","sources":["../../src/server/sapi-http-server.ts"],"names":[],"mappings":";;;AAEA,yDAA0D;AAG1D,MAAa,oBAAqB,SAAQ,uCAAoB;IAK5D,YAAY,EAAE,QAAQ,GAAG,EAAE,EAAE,IAAI,EAAyC;QACxE,KAAK,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5B,CAAC;CACF;AARD,oDAQC"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ScraperAPIStdioServer = void 0;
4
+ const sapi_base_server_1 = require("./sapi-base-server");
5
+ class ScraperAPIStdioServer extends sapi_base_server_1.ScraperAPIBaseServer {
6
+ constructor({ auth, toolsets = [] }) {
7
+ super({ auth, toolsets });
8
+ }
9
+ }
10
+ exports.ScraperAPIStdioServer = ScraperAPIStdioServer;
11
+ //# sourceMappingURL=sapi-stdio-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sapi-stdio-server.js","sourceRoot":"","sources":["../../src/server/sapi-stdio-server.ts"],"names":[],"mappings":";;;AAEA,yDAA0D;AAG1D,MAAa,qBAAsB,SAAQ,uCAAoB;IAK7D,YAAY,EAAE,IAAI,EAAE,QAAQ,GAAG,EAAE,EAAyC;QACxE,KAAK,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC5B,CAAC;CACF;AARD,sDAQC"}
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ require("dotenv/config");
7
+ const express_1 = __importDefault(require("express"));
8
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
9
+ const sapi_http_server_1 = require("./server/sapi-http-server");
10
+ const utils_1 = require("./utils");
11
+ const app = (0, express_1.default)();
12
+ app.use(express_1.default.json());
13
+ app.get('/mcp', (_req, res) => {
14
+ res.status(200).send('server up, use POST /mcp to see available tools');
15
+ });
16
+ app.post('/mcp', async (req, res) => {
17
+ const auth = req.headers.authorization;
18
+ if (!auth) {
19
+ res.status(401).send('Unauthorized');
20
+ return;
21
+ }
22
+ const parts = auth.split(' ');
23
+ if (parts.length !== 2 || parts[0] !== 'Basic') {
24
+ res.status(401).send("Valid 'Basic' authorization required");
25
+ return;
26
+ }
27
+ const token = parts[1];
28
+ const toolsets = (0, utils_1.resolveToolsets)(req.query.toolsets);
29
+ const server = new sapi_http_server_1.ScraperAPIHttpServer({ toolsets, auth: token });
30
+ const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({
31
+ sessionIdGenerator: undefined,
32
+ enableJsonResponse: true,
33
+ });
34
+ res.on('close', () => {
35
+ transport.close();
36
+ });
37
+ await server.connect(transport);
38
+ await transport.handleRequest(req, res, req.body);
39
+ });
40
+ app.get('/healthz', (_req, res) => {
41
+ res.status(200).send('ok');
42
+ });
43
+ const port = parseInt(process.env.PORT || '3000');
44
+ app
45
+ .listen(port, () => {
46
+ console.log(`Demo MCP Server running on http://localhost:${port}/mcp`);
47
+ console.log(`API host: ${process.env.DECODO_SAPI_HOST}`);
48
+ })
49
+ .on('error', (error) => {
50
+ console.error('Server error:', error);
51
+ process.exit(1);
52
+ });
53
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";;;;;AAAA,yBAAuB;AACvB,sDAA8B;AAC9B,0FAAmG;AACnG,gEAAiE;AACjE,mCAA0C;AAE1C,MAAM,GAAG,GAAG,IAAA,iBAAO,GAAE,CAAC;AAEtB,GAAG,CAAC,GAAG,CAAC,iBAAO,CAAC,IAAI,EAAE,CAAC,CAAC;AAExB,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAC5B,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;AAC1E,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAClC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC;IAEvC,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE9B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;QAC/C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAEvB,MAAM,QAAQ,GAAG,IAAA,uBAAe,EAAC,GAAG,CAAC,KAAK,CAAC,QAAkB,CAAC,CAAC;IAE/D,MAAM,MAAM,GAAG,IAAI,uCAAoB,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEnE,MAAM,SAAS,GAAG,IAAI,iDAA6B,CAAC;QAClD,kBAAkB,EAAE,SAAS;QAC7B,kBAAkB,EAAE,IAAI;KACzB,CAAC,CAAC;IAEH,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QACnB,SAAS,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,MAAM,SAAS,CAAC,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;AACpD,CAAC,CAAC,CAAC;AAEH,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;IAChC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;AAElD,GAAG;KACA,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACjB,OAAO,CAAC,GAAG,CAAC,+CAA+C,IAAI,MAAM,CAAC,CAAC;IACvE,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC;KACD,EAAE,CAAC,OAAO,EAAE,CAAC,KAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}