scrapeunblocker-mcp 0.3.1 → 0.3.3

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 (2) hide show
  1. package/dist/index.js +10 -229
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { APIError, ScrapeUnblockerClient } from "scrapeunblocker";
10
10
  import { z } from "zod";
11
11
 
12
12
  // src/plugin-catalog.ts
13
- var CATEGORIES = ["Travel", "Google", "Search engines", "E-commerce", "Classifieds & Resale", "Groceries", "Real estate", "Vehicles", "Jobs", "Reviews & Companies", "Local & Maps", "Apps", "Video, Music & Communities", "SEO & Ads"];
13
+ var CATEGORIES = ["Travel", "Google", "Search engines", "E-commerce", "Classifieds & Resale", "Groceries", "Real estate", "Vehicles", "Jobs", "Reviews & Companies", "Local & Maps", "Apps", "Video & Music", "SEO & Ads"];
14
14
  var PLUGINS = [
15
15
  {
16
16
  "name": "skyscanner_flights",
@@ -1755,52 +1755,6 @@ var PLUGINS = [
1755
1755
  }
1756
1756
  ]
1757
1757
  },
1758
- {
1759
- "name": "temu_search",
1760
- "title": "Search Temu by keyword",
1761
- "category": "E-commerce",
1762
- "path": "/goods/temu-search",
1763
- "fixed": {},
1764
- "description": "Searches Temu for `keyword` and returns the matching product urls (~40 per page). This is discovery only - fetch each url's data (name, price, rating, reviews, images) through `POST /goods/temu-product`. Use `sort` to pick Temu's own result order (relevance, best_selling, recent, price_asc, price_desc). Response time is typically ~2s.",
1765
- "params": [
1766
- {
1767
- "name": "keyword",
1768
- "type": "string",
1769
- "required": true,
1770
- "description": "Search phrase, e.g. 'kids toys' or 'phone case'."
1771
- },
1772
- {
1773
- "name": "limit",
1774
- "type": "integer",
1775
- "required": false,
1776
- "description": "Max product urls to return (one search page carries ~40).",
1777
- "default": 40
1778
- },
1779
- {
1780
- "name": "sort",
1781
- "type": "string",
1782
- "required": false,
1783
- "description": "Result order, using Temu's own sort: `relevance` (default), `best_selling` (top sales), `recent` (most recent), `price_asc` (price low to high) or `price_desc` (price high to low).",
1784
- "default": "relevance"
1785
- }
1786
- ]
1787
- },
1788
- {
1789
- "name": "temu_product",
1790
- "title": "Scrape a Temu product page",
1791
- "category": "E-commerce",
1792
- "path": "/goods/temu-product",
1793
- "fixed": {},
1794
- "description": "Fetches a single Temu product and returns it as JSON: `name`, `description`, `brand`, `sku`, `price`, `priceCurrency`, `availability`, `itemCondition`, `rating`, `reviewCount`, `variantGroupId`, `images`, `reviews` (per-review rating and date; Temu masks the review text and author name in this document) and `video` (a `{contentUrl, thumbnailUrl, uploadDate}` object when the product ships one, else null). Temu serves product data only to search-engine crawlers, so this reads the ld+json SEO document rather than a browser render. The url must be a product page (path ends in `-g-<id>.html`).",
1795
- "params": [
1796
- {
1797
- "name": "url",
1798
- "type": "string",
1799
- "required": true,
1800
- "description": "Full Temu product url, e.g. 'https://www.temu.com/xyz-g-601099512345678.html'. Must be a product page (its path ends in `-g-<id>.html`); search/category urls have no product data and are rejected."
1801
- }
1802
- ]
1803
- },
1804
1758
  {
1805
1759
  "name": "shein_search",
1806
1760
  "title": "Scrape SHEIN product search",
@@ -5099,7 +5053,7 @@ var PLUGINS = [
5099
5053
  {
5100
5054
  "name": "youtube_channel",
5101
5055
  "title": "Scrape a YouTube channel's videos",
5102
- "category": "Video, Music & Communities",
5056
+ "category": "Video & Music",
5103
5057
  "path": "/content/youtube-channel",
5104
5058
  "fixed": {},
5105
5059
  "description": "Returns a YouTube channel's recent videos as JSON: `videoId`, `title`, `views`, `published`, `thumbnail` and a watch `url`, plus a `channel` summary (name, id). Pass a handle (`@mkbhd`), a channel id (`UC...`) or a channel URL. Returns the channel's first page of videos (about 30).",
@@ -5135,7 +5089,7 @@ var PLUGINS = [
5135
5089
  {
5136
5090
  "name": "youtube_search",
5137
5091
  "title": "Search YouTube",
5138
- "category": "Video, Music & Communities",
5092
+ "category": "Video & Music",
5139
5093
  "path": "/content/youtube-search",
5140
5094
  "fixed": {},
5141
5095
  "description": "Searches YouTube for `query` and returns the results as JSON. For each video: `videoId`, `title`, `url`, `channel` (name/id/url), `publishedText`, `viewCount` (+ text), `lengthSeconds` (+ text), `thumbnails`, `descriptionSnippet`, `badges`, `verified` and `isLive`. Filter with `type` (video / channel / playlist / all) and `sort` (relevance / date / views / rating), and collect up to `max_results` across pages.",
@@ -5175,46 +5129,10 @@ var PLUGINS = [
5175
5129
  }
5176
5130
  ]
5177
5131
  },
5178
- {
5179
- "name": "youtube_comments",
5180
- "title": "Scrape a YouTube video's comments",
5181
- "category": "Video, Music & Communities",
5182
- "path": "/content/youtube-comments",
5183
- "fixed": {},
5184
- "description": "Returns a YouTube video's comments as JSON. For each comment: `commentId`, `author`, `authorChannelId`, `authorThumbnail`, `text`, `likeCount`, `publishedTimeText`, `replyCount`, and the `isPinned`, `isHearted`, `isAuthorVerified` and `isUploader` flags, plus the video `title` and total `commentCount`. Sort by `top` or `newest` and collect up to `max_results` across pages.",
5185
- "params": [
5186
- {
5187
- "name": "video",
5188
- "type": "string",
5189
- "required": true,
5190
- "description": "Video id (dQw4w9WgXcQ) or any YouTube video URL."
5191
- },
5192
- {
5193
- "name": "sort",
5194
- "type": "string",
5195
- "required": false,
5196
- "description": "Comment order: top, newest.",
5197
- "default": "top"
5198
- },
5199
- {
5200
- "name": "max_results",
5201
- "type": "integer",
5202
- "required": false,
5203
- "description": "How many comments to collect across pages (~20 per page).",
5204
- "default": 20
5205
- },
5206
- {
5207
- "name": "proxy_country",
5208
- "type": "string",
5209
- "required": false,
5210
- "description": "Proxy country the request is sent from (ISO-2, e.g. 'US', 'DE') for the watch page. Defaults to US."
5211
- }
5212
- ]
5213
- },
5214
5132
  {
5215
5133
  "name": "youtube_transcript",
5216
5134
  "title": "Get a YouTube video transcript",
5217
- "category": "Video, Music & Communities",
5135
+ "category": "Video & Music",
5218
5136
  "path": "/content/youtube-transcript",
5219
5137
  "fixed": {},
5220
5138
  "description": "Returns a YouTube video's captions as JSON: timestamped `segments` (`start`, `dur`, `text`) and a joined `text`, plus `availableLanguages`, the picked `language`, `isAutoGenerated`, and the video's `title`, `author` and `lengthSeconds`. Pass a video id or any YouTube video URL; choose a caption language with `lang`. Ideal for LLM/RAG pipelines, subtitles and content analysis.",
@@ -5249,7 +5167,7 @@ var PLUGINS = [
5249
5167
  {
5250
5168
  "name": "spotify_search",
5251
5169
  "title": "Scrape Spotify artists, albums, tracks, playlists and search (type=search)",
5252
- "category": "Video, Music & Communities",
5170
+ "category": "Video & Music",
5253
5171
  "path": "/music/spotify",
5254
5172
  "fixed": {
5255
5173
  "type": "search"
@@ -5293,7 +5211,7 @@ var PLUGINS = [
5293
5211
  {
5294
5212
  "name": "spotify_artist",
5295
5213
  "title": "Scrape Spotify artists, albums, tracks, playlists and search (type=artist)",
5296
- "category": "Video, Music & Communities",
5214
+ "category": "Video & Music",
5297
5215
  "path": "/music/spotify",
5298
5216
  "fixed": {
5299
5217
  "type": "artist"
@@ -5324,7 +5242,7 @@ var PLUGINS = [
5324
5242
  {
5325
5243
  "name": "spotify_album",
5326
5244
  "title": "Scrape Spotify artists, albums, tracks, playlists and search (type=album)",
5327
- "category": "Video, Music & Communities",
5245
+ "category": "Video & Music",
5328
5246
  "path": "/music/spotify",
5329
5247
  "fixed": {
5330
5248
  "type": "album"
@@ -5361,7 +5279,7 @@ var PLUGINS = [
5361
5279
  {
5362
5280
  "name": "spotify_track",
5363
5281
  "title": "Scrape Spotify artists, albums, tracks, playlists and search (type=track)",
5364
- "category": "Video, Music & Communities",
5282
+ "category": "Video & Music",
5365
5283
  "path": "/music/spotify",
5366
5284
  "fixed": {
5367
5285
  "type": "track"
@@ -5385,7 +5303,7 @@ var PLUGINS = [
5385
5303
  {
5386
5304
  "name": "spotify_playlist",
5387
5305
  "title": "Scrape Spotify artists, albums, tracks, playlists and search (type=playlist)",
5388
- "category": "Video, Music & Communities",
5306
+ "category": "Video & Music",
5389
5307
  "path": "/music/spotify",
5390
5308
  "fixed": {
5391
5309
  "type": "playlist"
@@ -5419,143 +5337,6 @@ var PLUGINS = [
5419
5337
  }
5420
5338
  ]
5421
5339
  },
5422
- {
5423
- "name": "telegram_channel",
5424
- "title": "Scrape a public Telegram channel: info and posts",
5425
- "category": "Video, Music & Communities",
5426
- "path": "/social/telegram-channel",
5427
- "fixed": {},
5428
- "description": "Reads a public Telegram channel's web preview. Returns `channel` (`username`, `title`, `description`, `avatar`, `verified`, `subscribers` and the other `counters`) and `posts`, newest first: `id`, `url`, `date` (ISO-8601 UTC), `text` (plain), `textHtml`, `views`, `author` (signature), `edited`, `forwardedFrom`, `replyTo`, `media` (photo / video / animation / round_video / voice / audio / document / sticker, with CDN `url`, `thumbnail`, `duration`), `poll`, `linkPreview`, `reactions` (a custom-emoji reaction has its `customEmojiId`, plus the standard `emoji` and `image` Telegram falls back to when the page shows that emoji anywhere), `buttons`, `links`, `hashtags` (the tags Telegram links; a bare '#7' is text) and `mentions`. One page (about 20 posts) by default; `max_posts` (up to 500) pages back through history; `before`, `after` and `since` bound the range, `q` searches inside the channel and `post_id` returns one post. `nextBefore` continues a capped answer. Only channels have a public preview: users, bots, groups and private channels answer 404. Billed one request per page fetched (`pagesFetched`).",
5429
- "params": [
5430
- {
5431
- "name": "channel",
5432
- "type": "string",
5433
- "required": true,
5434
- "description": "Public channel: username ('durov'), '@durov', or a t.me link ('https://t.me/durov', 'https://t.me/s/durov'). A post link ('https://t.me/durov/400') returns that one post."
5435
- },
5436
- {
5437
- "name": "max_posts",
5438
- "type": "integer",
5439
- "required": false,
5440
- "description": "Posts to collect, newest first (1-500), paging back through the channel's history; each page fetched is billed as one request. Omit it for one page, billed once: the newest posts (a page has 20 message slots and an album fills one per photo, so it can hold fewer posts)."
5441
- },
5442
- {
5443
- "name": "before",
5444
- "type": "integer",
5445
- "required": false,
5446
- "description": "Only posts with an id below this one. Pass `nextBefore` of a previous response to continue where it stopped."
5447
- },
5448
- {
5449
- "name": "after",
5450
- "type": "integer",
5451
- "required": false,
5452
- "description": "Stop at this post id: only posts with a higher id are returned."
5453
- },
5454
- {
5455
- "name": "since",
5456
- "type": "string",
5457
- "required": false,
5458
- "description": "Stop at this date: only posts published on or after it (YYYY-MM-DD or an ISO-8601 datetime, UTC when no offset is given)."
5459
- },
5460
- {
5461
- "name": "q",
5462
- "type": "string",
5463
- "required": false,
5464
- "description": "Search inside the channel: only posts matching this text."
5465
- },
5466
- {
5467
- "name": "post_id",
5468
- "type": "integer",
5469
- "required": false,
5470
- "description": "Return only this post (a post id of the channel)."
5471
- },
5472
- {
5473
- "name": "proxy_country",
5474
- "type": "string",
5475
- "required": false,
5476
- "description": "Proxy country the request is sent from (ISO-2, e.g. 'US', 'DE'). Leave blank to let the API pick."
5477
- }
5478
- ]
5479
- },
5480
- {
5481
- "name": "quora_question",
5482
- "title": "Scrape Quora questions, answers and profiles (mode=question)",
5483
- "category": "Video, Music & Communities",
5484
- "path": "/social/quora",
5485
- "fixed": {
5486
- "mode": "question"
5487
- },
5488
- "description": "Given a Quora `question` (URL or slug), returns the `question` (`qid`, `title`, `url`, `answerCount`, `allRelatedCount`, `followerCount`, `commentCount`, `topics`) and its `answers`: `id`, `url`, `relation` (`answer`, `merged` for an answer to a question Quora merged into this one, `related` with `include_related=true`), the answered `question`, `author` (`name`, `uid`, `profileUrl`, `imageUrl`, `credential`, `isVerified`, `isAnonymous`), the full `text` with `images` and `links`, `upvotes`, `views`, `shares`, `comments`, `createdAt`, `updatedAt` and `isAiGenerated`; plus `relatedQuestions`. Answers are in Quora's own order, up to `max_answers` (10 per page after the first few), unique within a call. `nextCursor` as `cursor` continues, best-effort: Quora re-ranks the list on every visit, so a continuation can repeat or skip answers of earlier calls (dedupe by `id`); for a complete list ask for a larger `max_answers` in one call (up to 300). `mode=profile&profile=` returns a public `profile` (`name`, `headline`, `description`, follower / following / answer / question / post counts, `contentViews`, `credentials`, `knowsAbout`, `spaces`, `topWriterYears`, `joinedAt`) with its newest `answers`. No login: only what Quora shows an anonymous visitor - Quora's search is not available that way, and Quora lists only a profile's newest few dozen answers to logged-out visitors (`pagingStopped`: `quora_logged_out_limit`). `pagingStopped` otherwise names why paging ended early (`blocked`, `time`, `unavailable`). Billed one unit per page fetched (`pagesFetched`); a `cursor` continuation bills only its GraphQL pages.",
5489
- "params": [
5490
- {
5491
- "name": "question",
5492
- "type": "string",
5493
- "required": true,
5494
- "description": "Quora question URL (https://www.quora.com/What-is-the-best-way-to-learn-programming), an answer URL on it, or the question slug (What-is-the-best-way-to-learn-programming). Language editions such as es.quora.com are accepted as URLs. Required with mode=question."
5495
- },
5496
- {
5497
- "name": "max_answers",
5498
- "type": "integer",
5499
- "required": false,
5500
- "description": "Answers to return: 1-300 with mode=question (default 20), 0-200 with mode=profile (default 10; 0 = profile only). Answers come 10 per page after the first few; each page is one billed unit."
5501
- },
5502
- {
5503
- "name": "include_related",
5504
- "type": "boolean",
5505
- "required": false,
5506
- "description": "mode=question: also return the answers Quora shows from related questions (its 'All related' view, `relation`='related'). By default only answers to this question and to questions merged into it.",
5507
- "default": false
5508
- },
5509
- {
5510
- "name": "cursor",
5511
- "type": "string",
5512
- "required": false,
5513
- "description": "Continue after a previous call: pass its `nextCursor`. Best-effort: each call is a new Quora visit and Quora re-ranks the list per visit, so a continuation can repeat or skip answers of earlier calls - dedupe by answer `id`. For a complete list ask for a larger `max_answers` in one call (up to 300)."
5514
- },
5515
- {
5516
- "name": "proxy_country",
5517
- "type": "string",
5518
- "required": false,
5519
- "description": "Proxy country the request is sent from (ISO-2, e.g. 'US', 'DE'). Leave blank for the countries chosen for the site."
5520
- }
5521
- ]
5522
- },
5523
- {
5524
- "name": "quora_profile",
5525
- "title": "Scrape Quora questions, answers and profiles (mode=profile)",
5526
- "category": "Video, Music & Communities",
5527
- "path": "/social/quora",
5528
- "fixed": {
5529
- "mode": "profile"
5530
- },
5531
- "description": "Given a Quora `question` (URL or slug), returns the `question` (`qid`, `title`, `url`, `answerCount`, `allRelatedCount`, `followerCount`, `commentCount`, `topics`) and its `answers`: `id`, `url`, `relation` (`answer`, `merged` for an answer to a question Quora merged into this one, `related` with `include_related=true`), the answered `question`, `author` (`name`, `uid`, `profileUrl`, `imageUrl`, `credential`, `isVerified`, `isAnonymous`), the full `text` with `images` and `links`, `upvotes`, `views`, `shares`, `comments`, `createdAt`, `updatedAt` and `isAiGenerated`; plus `relatedQuestions`. Answers are in Quora's own order, up to `max_answers` (10 per page after the first few), unique within a call. `nextCursor` as `cursor` continues, best-effort: Quora re-ranks the list on every visit, so a continuation can repeat or skip answers of earlier calls (dedupe by `id`); for a complete list ask for a larger `max_answers` in one call (up to 300). `mode=profile&profile=` returns a public `profile` (`name`, `headline`, `description`, follower / following / answer / question / post counts, `contentViews`, `credentials`, `knowsAbout`, `spaces`, `topWriterYears`, `joinedAt`) with its newest `answers`. No login: only what Quora shows an anonymous visitor - Quora's search is not available that way, and Quora lists only a profile's newest few dozen answers to logged-out visitors (`pagingStopped`: `quora_logged_out_limit`). `pagingStopped` otherwise names why paging ended early (`blocked`, `time`, `unavailable`). Billed one unit per page fetched (`pagesFetched`); a `cursor` continuation bills only its GraphQL pages.",
5532
- "params": [
5533
- {
5534
- "name": "profile",
5535
- "type": "string",
5536
- "required": true,
5537
- "description": "Quora profile URL (https://www.quora.com/profile/Adam-DAngelo) or profile slug (Adam-DAngelo). Required with mode=profile."
5538
- },
5539
- {
5540
- "name": "max_answers",
5541
- "type": "integer",
5542
- "required": false,
5543
- "description": "Answers to return: 1-300 with mode=question (default 20), 0-200 with mode=profile (default 10; 0 = profile only). Answers come 10 per page after the first few; each page is one billed unit."
5544
- },
5545
- {
5546
- "name": "cursor",
5547
- "type": "string",
5548
- "required": false,
5549
- "description": "Continue after a previous call: pass its `nextCursor`. Best-effort: each call is a new Quora visit and Quora re-ranks the list per visit, so a continuation can repeat or skip answers of earlier calls - dedupe by answer `id`. For a complete list ask for a larger `max_answers` in one call (up to 300)."
5550
- },
5551
- {
5552
- "name": "proxy_country",
5553
- "type": "string",
5554
- "required": false,
5555
- "description": "Proxy country the request is sent from (ISO-2, e.g. 'US', 'DE'). Leave blank for the countries chosen for the site."
5556
- }
5557
- ]
5558
- },
5559
5340
  {
5560
5341
  "name": "similarweb_overview",
5561
5342
  "title": "Get a website's Similarweb traffic overview",
@@ -5830,7 +5611,7 @@ ${details(plugin)}`, true);
5830
5611
  }
5831
5612
 
5832
5613
  // src/index.ts
5833
- var VERSION = "0.3.1";
5614
+ var VERSION = "0.3.3";
5834
5615
  var DEFAULT_BASE_URL = "https://api.scrapeunblocker.com";
5835
5616
  var API_KEY_HEADER = "x-scrapeunblocker-key";
5836
5617
  function resolveApiKey() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scrapeunblocker-mcp",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "mcpName": "com.scrapeunblocker/scrapeunblocker-mcp",
5
5
  "description": "Model Context Protocol (MCP) server for ScrapeUnblocker - lets Claude fetch any web page's HTML (or AI-parsed JSON) and run 100+ site plugins that return structured JSON, through the ScrapeUnblocker anti-bot API, using your own API key.",
6
6
  "keywords": [