mcpscraper-cli 0.26.0 → 0.28.0

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 (3) hide show
  1. package/README.md +12 -3
  2. package/dist/bin/cli.js +372 -30
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -4,7 +4,7 @@ Command-line interface for [mcpscraper.dev](https://mcpscraper.dev) and [memory.
4
4
 
5
5
  [Release history](https://github.com/VilovietaSEO/mcpscraper-sdk/blob/main/CHANGELOG.md)
6
6
 
7
- The CLI provides friendly shortcuts for common operations plus universal discovery and invocation for every one of the 249 unified MCP tools (148 MCP Scraper plus 101 mirrored Memory tools).
7
+ The CLI provides friendly shortcuts for common operations plus universal discovery and invocation for every one of the 254 unified MCP tools (153 MCP Scraper plus 101 mirrored Memory tools).
8
8
 
9
9
  ## Install
10
10
 
@@ -37,7 +37,7 @@ mcpscraper tools call archive_read --args '{"url":"https://github.com/octocat/He
37
37
  mcpscraper tools call list_scheduled_runs --args '{"view":"inbox","limit":30}' --json
38
38
  ```
39
39
 
40
- `tools list` contains exactly 249 manifest-backed names. `tools call` accepts any one of them. Tools marked destructive require `--yes`.
40
+ `tools list` contains exactly 254 manifest-backed names. `tools call` accepts any one of them. Tools marked destructive require `--yes`.
41
41
 
42
42
  Google SERP searches cost 60 Credits. PAA harvests cost 400 Credits plus 10 Credits per returned question. For additional throughput, one optional $5/month concurrency pack adds two browser slots; pack quantity scales both slots and monthly price linearly.
43
43
 
@@ -71,7 +71,16 @@ Options: `--screenshot`, `--deposit-to-vault`, `--vault <name>`, `--json`.
71
71
 
72
72
  ### `mcpscraper crawl <url>`
73
73
 
74
- Crawls and extracts every page of a site. Options: `--max-pages <n>` (default 100), `--json`.
74
+ Starts a durable background export that retains complete JSON, acquired HTML, and Markdown for every successful page. Options: `--max-pages <n>` (default 100), `--preserve-media`, `--idempotency-key <key>`, `--json`.
75
+
76
+ Poll and read the export without downloading and unpacking the ZIP:
77
+
78
+ ```bash
79
+ mcpscraper export-status <job-id>
80
+ mcpscraper export-read <job-id> --representation manifest
81
+ mcpscraper export-read <job-id> --representation html --page-id <page-id> --output page.html
82
+ mcpscraper export-image <job-id> <image-id> --output image.jpg
83
+ ```
75
84
 
76
85
  ### `mcpscraper map <url>`
77
86
 
package/dist/bin/cli.js CHANGED
@@ -5,11 +5,12 @@ import { Command } from "commander";
5
5
  import { fileURLToPath } from "url";
6
6
  import { mkdirSync, realpathSync, writeFileSync } from "fs";
7
7
  import { homedir } from "os";
8
- import { join } from "path";
8
+ import { dirname, join } from "path";
9
+ import { randomUUID } from "crypto";
9
10
  import { ScraperClient, ScraperApiError } from "mcpscraper-sdk";
10
11
 
11
12
  // src/version.ts
12
- var CLI_VERSION = "0.26.0";
13
+ var CLI_VERSION = "0.28.0";
13
14
 
14
15
  // src/generated-tools.ts
15
16
  var MCP_TOOL_CATALOG = [
@@ -58,6 +59,12 @@ var MCP_TOOL_CATALOG = [
58
59
  "mobile"
59
60
  ]
60
61
  },
62
+ "idempotencyKey": {
63
+ "description": "Retry key: reuse after a timeout to avoid re-billing. New key per harvest.",
64
+ "type": "string",
65
+ "minLength": 8,
66
+ "maxLength": 200
67
+ },
61
68
  "serpIdentity": {
62
69
  "description": "Optional persistent SERP identity created with serp_identity_create. Reuses the same saved browser state and fixed network address across calls.",
63
70
  "type": "string",
@@ -145,6 +152,12 @@ var MCP_TOOL_CATALOG = [
145
152
  "mobile"
146
153
  ]
147
154
  },
155
+ "idempotencyKey": {
156
+ "description": "Retry key: reuse after a timeout to avoid re-billing. New key per search.",
157
+ "type": "string",
158
+ "minLength": 8,
159
+ "maxLength": 200
160
+ },
148
161
  "serpIdentity": {
149
162
  "description": "Optional persistent SERP identity created with serp_identity_create. Reuses the same saved browser state and fixed network address across calls.",
150
163
  "type": "string",
@@ -215,7 +228,7 @@ var MCP_TOOL_CATALOG = [
215
228
  "name": "extract_url",
216
229
  "category": "web",
217
230
  "title": "Single URL Extract",
218
- "description": "Extract structured data from one public URL: content, schema, headings, metadata, screenshots, branding, featured image, or media assets. Wayback replay URLs automatically return the archived page copy without playback chrome. Use delivery:auto for bounded inline results with automatic artifact offload, delivery:artifact for a durable owner-scoped report, or delivery:memory to save the full page into hosted MCP Memory. preserveMedia is the preferred media-retention flag; depositToVault and downloadMedia remain temporary compatibility aliases.",
231
+ "description": "Extract structured data from one public URL: content, schema, headings, metadata, screenshots, branding, featured image, or media assets. Tries a plain HTTP fetch first and only opens a stealth browser when that fetch is blocked, hits a bot check, or returns unusably thin content \u2014 most calls never need the browser, and a bot_check_unresolved error means the browser already tried and failed, not that a retry will help. Wayback replay URLs automatically return the archived page copy without playback chrome. Use delivery:auto for bounded inline results with automatic artifact offload, delivery:artifact for a durable owner-scoped report, or delivery:memory to save the full page into hosted MCP Memory. preserveMedia is the preferred media-retention flag; depositToVault and downloadMedia remain temporary compatibility aliases.",
219
232
  "inputSchema": {
220
233
  "type": "object",
221
234
  "properties": {
@@ -226,12 +239,12 @@ var MCP_TOOL_CATALOG = [
226
239
  },
227
240
  "screenshot": {
228
241
  "default": false,
229
- "description": "Capture a full-page screenshot and return it through the result contract. Large captures may be offloaded to an owned artifact.",
242
+ "description": "Capture a full-page screenshot. Large captures may be offloaded to an owned artifact.",
230
243
  "type": "boolean"
231
244
  },
232
245
  "screenshotDevice": {
233
246
  "default": "desktop",
234
- "description": "Viewport for screenshot. desktop = 1440\xD7900, mobile = 390\xD7844.",
247
+ "description": "Viewport: desktop = 1440\xD7900, mobile = 390\xD7844.",
235
248
  "type": "string",
236
249
  "enum": [
237
250
  "desktop",
@@ -240,16 +253,16 @@ var MCP_TOOL_CATALOG = [
240
253
  },
241
254
  "extractBranding": {
242
255
  "default": false,
243
- "description": "Extract brand colors, fonts, logo, and favicon via a rendered browser session.",
256
+ "description": "Extract brand colors, fonts, logo, and favicon via a rendered session.",
244
257
  "type": "boolean"
245
258
  },
246
259
  "includeFeaturedImage": {
247
260
  "default": false,
248
- "description": "Return the best featured image from Open Graph, Twitter, JSON-LD, or page content. For Wayback replay URLs, also returns the timestamp-matched archived image URL when available.",
261
+ "description": "Return the best featured image from Open Graph, Twitter, JSON-LD, or page content. Wayback URLs get the timestamp-matched archived image.",
249
262
  "type": "boolean"
250
263
  },
251
264
  "downloadMedia": {
252
- "description": "Deprecated alias for preserveMedia. Omit when using preserveMedia; when omitted, media preservation defaults to false.",
265
+ "description": "Deprecated alias for preserveMedia; omit when using preserveMedia.",
253
266
  "type": "boolean"
254
267
  },
255
268
  "mediaTypes": {
@@ -258,7 +271,7 @@ var MCP_TOOL_CATALOG = [
258
271
  "video",
259
272
  "audio"
260
273
  ],
261
- "description": "Which media types to download. Default all three.",
274
+ "description": "Media types to download. Default all three.",
262
275
  "type": "array",
263
276
  "items": {
264
277
  "type": "string",
@@ -271,21 +284,21 @@ var MCP_TOOL_CATALOG = [
271
284
  },
272
285
  "maxMediaAssets": {
273
286
  "default": 100,
274
- "description": "Maximum media records to retain and attempt to download after filtering and responsive-variant collapse.",
287
+ "description": "Maximum media records to retain after filtering and variant collapse.",
275
288
  "type": "integer",
276
289
  "minimum": 1,
277
290
  "maximum": 250
278
291
  },
279
292
  "maxInlineImages": {
280
293
  "default": 3,
281
- "description": "Maximum downloaded images to attach as AI-readable image content blocks. All successfully downloaded media remains available in the ZIP.",
294
+ "description": "Maximum images attached as AI-readable content blocks. All downloaded media stays available in the ZIP.",
282
295
  "type": "integer",
283
296
  "minimum": 0,
284
297
  "maximum": 5
285
298
  },
286
299
  "delivery": {
287
300
  "default": "auto",
288
- "description": "Where to deliver the result. auto keeps small results inline and offloads large ones; artifact always returns an owned artifact; memory stores the full page in hosted Memory; inline returns a bounded response.",
301
+ "description": "auto offloads large results; artifact always returns an artifact; memory stores in hosted Memory; inline is bounded.",
289
302
  "type": "string",
290
303
  "enum": [
291
304
  "auto",
@@ -296,19 +309,25 @@ var MCP_TOOL_CATALOG = [
296
309
  },
297
310
  "preserveMedia": {
298
311
  "default": false,
299
- "description": "Collect media from static source plus a rendered, lazy-loaded page; collapse responsive variants; return provenance and completeness; attach bounded image previews; and create an owner-scoped ZIP readable with archive_read.",
312
+ "description": "Collect media from static plus rendered page content, collapse responsive variants, attach bounded previews, and create an owner-scoped ZIP readable with archive_read.",
300
313
  "type": "boolean"
301
314
  },
302
315
  "depositToVault": {
303
316
  "default": false,
304
- "description": "Save the full page content into the user's MCP Memory vault server-side, embedded for semantic recall \u2014 the full body is NOT returned to chat.",
317
+ "description": "Save the full page content into the user's MCP Memory vault, embedded for semantic recall \u2014 the full body is NOT returned to chat.",
305
318
  "type": "boolean"
306
319
  },
307
320
  "vaultName": {
308
- "description": "Optional vault to deposit into. Defaults to the user's personal vault.",
321
+ "description": "Optional vault to deposit into; defaults to the personal vault.",
309
322
  "type": "string",
310
323
  "minLength": 1,
311
324
  "maxLength": 120
325
+ },
326
+ "idempotencyKey": {
327
+ "description": "Retry key: reuse after a timeout to avoid re-billing. New key per extraction.",
328
+ "type": "string",
329
+ "minLength": 8,
330
+ "maxLength": 200
312
331
  }
313
332
  },
314
333
  "required": [
@@ -495,7 +514,7 @@ var MCP_TOOL_CATALOG = [
495
514
  "name": "extract_site",
496
515
  "category": "web",
497
516
  "title": "Multi-Page Site Content Crawl",
498
- "description": "Crawl a public website and return page CONTENT (Markdown) across multiple pages. A Wayback replay URL produces one archived site snapshot. The optional wayback plan produces whole-site, single-page, or selected-page timelines across explicit months or a month range, all in one export with a capture matrix. Pass a new idempotencyKey for each intended crawl and reuse it only when retrying that call. Every MCP crawl starts a durable export; poll check_site_export for honest outcome counters and the saved ZIP. Content only \u2014 for a technical SEO audit use audit_site instead.",
517
+ "description": "Crawl a public website and durably retain complete per-page JSON, acquired HTML, and Markdown. Each page is fetched with a plain HTTP request first; a browser retries only pages that were blocked or hit a bot check. A Wayback replay URL produces one archived site snapshot; the optional wayback plan produces timelines across explicit months or a month range. Pass a new idempotencyKey for each intended crawl and reuse it only when retrying that call. Poll check_site_export, then use site_export_read for complete page representations and site_export_image for preserved images; a saved ZIP is also produced. For a technical SEO audit use audit_site instead.",
499
518
  "inputSchema": {
500
519
  "type": "object",
501
520
  "properties": {
@@ -570,12 +589,13 @@ var MCP_TOOL_CATALOG = [
570
589
  "maximum": 100
571
590
  },
572
591
  "formats": {
573
- "description": "Per-page output formats: markdown, links, json, images are captured cheaply from HTML; branding (site-level logo/colors/fonts) requires a browser and adds time. Defaults to markdown+links.",
592
+ "description": "Requested export views. Every successful page is durably retained as complete JSON, acquired HTML, and Markdown; links and image references are included in page JSON. branding adds a browser-based site summary.",
574
593
  "type": "array",
575
594
  "items": {
576
595
  "type": "string",
577
596
  "enum": [
578
597
  "markdown",
598
+ "html",
579
599
  "links",
580
600
  "json",
581
601
  "images",
@@ -585,7 +605,7 @@ var MCP_TOOL_CATALOG = [
585
605
  },
586
606
  "background": {
587
607
  "default": true,
588
- "description": "MCP multi-page crawls always run as durable background jobs. Poll check_site_export for progress, outcome counters, and the hosted ZIP.",
608
+ "description": "MCP multi-page crawls always run as durable background jobs. Poll check_site_export, then use site_export_read for complete JSON/HTML/Markdown and site_export_image for downloaded images.",
589
609
  "type": "boolean",
590
610
  "const": true
591
611
  },
@@ -700,7 +720,7 @@ var MCP_TOOL_CATALOG = [
700
720
  "name": "check_site_export",
701
721
  "category": "web",
702
722
  "title": "Check Site Export",
703
- "description": "Poll a background extract_site or audit_site job. Reports discovered, attempted, successful, failed, and remaining pages. Complete and partial jobs return a downloadable ZIP; partial bundles include successful content plus per-page failure reasons.",
723
+ "description": "Poll a background extract_site or audit_site job. Reports discovered, attempted, successful, failed, and remaining pages plus a stable public error envelope when terminal. Complete and partial jobs expose direct AI readback through site_export_read/site_export_image and also return a downloadable ZIP.",
704
724
  "inputSchema": {
705
725
  "type": "object",
706
726
  "properties": {
@@ -1760,10 +1780,19 @@ var MCP_TOOL_CATALOG = [
1760
1780
  "name": "commons_search_entities",
1761
1781
  "category": "commons",
1762
1782
  "title": "Transparent Commons Entity Search",
1763
- "description": "Search the Transparent Commons public wiki graph using the same fields the public /wiki/ frontend uses: title, description, tags, keywords, JSON-LD, article body, citations, media, source metadata, related entities, trails, and saved account filters. This reads published Commons entities only.",
1783
+ "description": "Search the Transparent Commons public wiki graph. Hybrid by default: meaning-based candidates fused with exact matching over title, description, tags, keywords, JSON-LD, body, citations, media, source metadata, related entities, trails, and saved filters, ranked title-first. Set mode:lexical for a quoted exact name or phrase. Reads published Commons entities only; never a personal Memory vault.",
1764
1784
  "inputSchema": {
1765
1785
  "type": "object",
1766
1786
  "properties": {
1787
+ "mode": {
1788
+ "description": "Retrieval mode. hybrid (default) fuses meaning-based candidates with exact term matching; lexical matches terms only, which is what you want when the caller quoted an exact name or phrase; semantic ignores term matching entirely.",
1789
+ "type": "string",
1790
+ "enum": [
1791
+ "lexical",
1792
+ "semantic",
1793
+ "hybrid"
1794
+ ]
1795
+ },
1767
1796
  "query": {
1768
1797
  "description": "Search text matched against title, description, tags, keywords, JSON-LD, source metadata, citations, media, and article body.",
1769
1798
  "type": "string",
@@ -3032,7 +3061,7 @@ var MCP_TOOL_CATALOG = [
3032
3061
  "name": "commons_submit_entity",
3033
3062
  "category": "commons",
3034
3063
  "title": "Transparent Commons Governed Entity Write",
3035
- "description": "Create or propose an edit to a Transparent Commons public wiki entity through the governed MCP Scraper write plane. A new entity costs 10 Credits; an edit to an existing entity costs 2 Credits. Preparation and validation are free, and idempotent retries are not charged twice. This never edits rendered HTML directly. Normal workflow is commons_prepare_entity, compose, commons_validate_entity, then submit. Choose a precise entityType and write a neutral encyclopedia projection with the matching entity structure, not a blog article or raw scrape dump. Use H2 sections and H3/H4/H5 subitems; every heading appears in the public page menu. Omit unsupported sections instead of publishing empty/filler headings. It writes a proposal, records a contribution ledger entry when accepted, and auto-publishes only safe non-conflicting changes with a featured image and source/body evidence. Existing entity edits require baseRevision to auto-publish. Requires idempotencyKey.",
3064
+ "description": "Create or propose an edit to a Transparent Commons public wiki entity through the governed MCP Scraper write plane. A new entity costs 10 Credits; an edit to an existing entity costs 2 Credits. Preparation and validation are free, and idempotent retries are not charged twice. This never edits rendered HTML directly. Normal workflow is commons_prepare_entity, compose, commons_validate_entity, then submit. commons_prepare_entity returns the entity profile contract and section guidance to compose against. Transparent Commons is an open wiki: any account may edit any entity, and every edit publishes and is recorded on the public contribution ledger. Publishing requires a featured image and body/source evidence. Pass baseRevision when editing so the ledger can show whether the edit was written on top of a newer revision. Requires idempotencyKey.",
3036
3065
  "inputSchema": {
3037
3066
  "type": "object",
3038
3067
  "properties": {
@@ -3914,6 +3943,15 @@ var MCP_TOOL_CATALOG = [
3914
3943
  "filter": {
3915
3944
  "type": "object",
3916
3945
  "properties": {
3946
+ "mode": {
3947
+ "description": "Retrieval mode. hybrid (default) fuses meaning-based candidates with exact term matching; lexical matches terms only, which is what you want when the caller quoted an exact name or phrase; semantic ignores term matching entirely.",
3948
+ "type": "string",
3949
+ "enum": [
3950
+ "lexical",
3951
+ "semantic",
3952
+ "hybrid"
3953
+ ]
3954
+ },
3917
3955
  "query": {
3918
3956
  "description": "Search text matched against title, description, tags, keywords, JSON-LD, source metadata, citations, media, and article body.",
3919
3957
  "type": "string",
@@ -4932,7 +4970,7 @@ var MCP_TOOL_CATALOG = [
4932
4970
  "name": "local_sourcebook_submission_status",
4933
4971
  "category": "directory",
4934
4972
  "title": "Local Sourcebook Submission Status",
4935
- "description": "Read the authenticated caller\u2019s listing draft, enrichment coverage, immutable revision number, publication state, and exact live LocalSourcebook.com profile and reviews URLs.",
4973
+ "description": "Read the caller\u2019s listing draft, enrichment coverage, revision number, publication state, and live LocalSourcebook.com profile and reviews URLs. When status is failed, acquisitionError says why \u2014 read it before retrying, since a refresh repeats the same acquisition. Coverage counters are written only on success, so zeroed counters on a failed listing do not mean the crawl never ran.",
4936
4974
  "inputSchema": {
4937
4975
  "type": "object",
4938
4976
  "properties": {
@@ -4959,7 +4997,7 @@ var MCP_TOOL_CATALOG = [
4959
4997
  "name": "local_sourcebook_refresh",
4960
4998
  "category": "directory",
4961
4999
  "title": "Refresh a Local Sourcebook Listing",
4962
- "description": "Queue a new broad crawl and review/media acquisition pass for a listing owned by the authenticated MCP Scraper account. A refresh costs 2 Credits total, including acquisition; idempotent retries are not charged twice. The last published revision remains public until the refreshed evidence revision completes and auto-publishes.",
5000
+ "description": "Queue a new broad crawl and review/media acquisition pass for a listing owned by the authenticated MCP Scraper account. A refresh costs 2 Credits total, including acquisition; idempotent retries are not charged twice, and a replayed retry still re-runs the acquisition, so a failed listing is always recoverable. Read local_sourcebook_submission_status first: a refresh repeats the same acquisition, so fix whatever acquisitionError reports before spending another pass. The last published revision remains public until the refreshed evidence revision completes and auto-publishes.",
4963
5001
  "inputSchema": {
4964
5002
  "type": "object",
4965
5003
  "properties": {
@@ -16698,10 +16736,14 @@ var MCP_TOOL_CATALOG = [
16698
16736
  "name": "commons_get_publication",
16699
16737
  "category": "commons",
16700
16738
  "title": "Get Transparent Commons Publication",
16701
- "description": "Read a Transparent Commons publication and its latest edition revisions. Omit subdomain to inspect the caller-owned publication; pass a name to inspect a public publication. Returns the permanent public and archive URLs needed for sharing or later edits.",
16739
+ "description": "Read a Transparent Commons publication and its latest edition revisions. Omit subdomain to inspect the caller-owned publication; pass a name to inspect a public publication. Returns the permanent public and archive URLs needed for sharing or later edits. Set includeArticles to also return every stored article\u2019s Markdown source and slug, which is what commons_update_editorial_article edits \u2014 the payload is large, so request it only when you intend to edit.",
16702
16740
  "inputSchema": {
16703
16741
  "type": "object",
16704
16742
  "properties": {
16743
+ "includeArticles": {
16744
+ "description": "Return every stored article\u2019s Markdown source alongside each edition. Large payload; request it only when you intend to edit an article. Owner-only: a publication read by name never returns sources.",
16745
+ "type": "boolean"
16746
+ },
16705
16747
  "subdomain": {
16706
16748
  "description": "Public publication name to inspect. Omit to return the publication owned by the authenticated account.",
16707
16749
  "type": "string",
@@ -17616,6 +17658,259 @@ var MCP_TOOL_CATALOG = [
17616
17658
  "idempotentHint": false,
17617
17659
  "openWorldHint": true
17618
17660
  }
17661
+ },
17662
+ {
17663
+ "name": "commons_get_proposal",
17664
+ "category": "commons",
17665
+ "title": "Transparent Commons Proposal Status",
17666
+ "description": "Read one commons_submit_entity proposal owned by the calling account. Use it when submit returned a proposal that was held for review instead of published, to see the review reason and whether it has since been applied.",
17667
+ "inputSchema": {
17668
+ "type": "object",
17669
+ "properties": {
17670
+ "proposalId": {
17671
+ "type": "string",
17672
+ "minLength": 1,
17673
+ "maxLength": 120,
17674
+ "description": "Proposal id returned by commons_submit_entity, in the form commons-proposal-<uuid>."
17675
+ }
17676
+ },
17677
+ "required": [
17678
+ "proposalId"
17679
+ ],
17680
+ "$schema": "https://json-schema.org/draft/2020-12/schema"
17681
+ },
17682
+ "annotations": {
17683
+ "title": "Transparent Commons Proposal Status",
17684
+ "readOnlyHint": true,
17685
+ "destructiveHint": false,
17686
+ "idempotentHint": true,
17687
+ "openWorldHint": false
17688
+ }
17689
+ },
17690
+ {
17691
+ "name": "commons_host_image",
17692
+ "category": "commons",
17693
+ "title": "Host a Transparent Commons Image",
17694
+ "description": "Host an image from a public https URL or base64 bytes, returning a permanent Commons URL. Commons never serves a third-party image URL \u2014 commons_submit_entity hosts external images automatically, so use this only to host one before or independently of a write. JPEG, PNG, GIF, WebP up to 10 MB; SVG rejected. Identical bytes are stored once.",
17695
+ "inputSchema": {
17696
+ "type": "object",
17697
+ "properties": {
17698
+ "sourceUrl": {
17699
+ "description": "Public https image URL to download and host. The wiki never serves a third-party URL directly, so pass the original source here rather than putting it on the entity.",
17700
+ "type": "string",
17701
+ "format": "uri"
17702
+ },
17703
+ "imageBase64": {
17704
+ "description": "Base64 image bytes for an image you already hold. Use for images under about 3 MB; larger files should be published from a URL.",
17705
+ "type": "string",
17706
+ "minLength": 16
17707
+ },
17708
+ "alt": {
17709
+ "description": "Alt text describing the image for readers who cannot see it.",
17710
+ "type": "string",
17711
+ "maxLength": 500
17712
+ },
17713
+ "license": {
17714
+ "description": "License or usage terms of the original image, preserved with the hosted copy.",
17715
+ "type": "string",
17716
+ "maxLength": 240
17717
+ },
17718
+ "attribution": {
17719
+ "description": "Credit line for the original photographer, publication, or archive.",
17720
+ "type": "string",
17721
+ "maxLength": 500
17722
+ }
17723
+ },
17724
+ "$schema": "https://json-schema.org/draft/2020-12/schema"
17725
+ },
17726
+ "annotations": {
17727
+ "title": "Host a Transparent Commons Image",
17728
+ "readOnlyHint": false,
17729
+ "destructiveHint": false,
17730
+ "idempotentHint": true,
17731
+ "openWorldHint": true
17732
+ }
17733
+ },
17734
+ {
17735
+ "name": "commons_update_editorial_article",
17736
+ "category": "commons",
17737
+ "title": "Edit One Transparent Commons Article",
17738
+ "description": "Replace the Markdown of one article in a published edition without resupplying the others: it reads the stored sources, swaps the article you name, and publishes the next revision with the rest byte-identical. Read commons_get_publication with includeArticles first for the current source and revision. commons_publish_editorial is the full-replace path. Requires baseRevision and idempotencyKey.",
17739
+ "inputSchema": {
17740
+ "type": "object",
17741
+ "properties": {
17742
+ "publicationSubdomain": {
17743
+ "type": "string",
17744
+ "minLength": 3,
17745
+ "maxLength": 50,
17746
+ "description": "Publication owned by the authenticated account."
17747
+ },
17748
+ "editionSlug": {
17749
+ "description": "Edition holding the article. Defaults to the publication\u2019s latest edition.",
17750
+ "type": "string",
17751
+ "maxLength": 80,
17752
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
17753
+ },
17754
+ "articleSlug": {
17755
+ "type": "string",
17756
+ "maxLength": 80,
17757
+ "pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
17758
+ "description": "Slug of the one article to replace, from commons_get_publication with includeArticles."
17759
+ },
17760
+ "idempotencyKey": {
17761
+ "type": "string",
17762
+ "minLength": 8,
17763
+ "maxLength": 200,
17764
+ "description": "Unique key for this intended edit. Reuse it only when retrying the same edit."
17765
+ },
17766
+ "baseRevision": {
17767
+ "type": "integer",
17768
+ "exclusiveMinimum": 0,
17769
+ "maximum": 9007199254740991,
17770
+ "description": "Current edition revision from commons_get_publication. The edit is refused if the edition moved on."
17771
+ },
17772
+ "markdown": {
17773
+ "description": "Replacement Markdown body for this article. Omit to change only the metadata fields below.",
17774
+ "type": "string",
17775
+ "minLength": 1,
17776
+ "maxLength": 4e5
17777
+ },
17778
+ "title": {
17779
+ "description": "Replacement article title.",
17780
+ "type": "string",
17781
+ "minLength": 1,
17782
+ "maxLength": 180
17783
+ },
17784
+ "summary": {
17785
+ "description": "Replacement one or two sentence summary.",
17786
+ "type": "string",
17787
+ "minLength": 1,
17788
+ "maxLength": 500
17789
+ },
17790
+ "category": {
17791
+ "description": "Replacement section label.",
17792
+ "type": "string",
17793
+ "minLength": 1,
17794
+ "maxLength": 80
17795
+ },
17796
+ "kicker": {
17797
+ "description": "Replacement framing line above the title.",
17798
+ "type": "string",
17799
+ "minLength": 1,
17800
+ "maxLength": 140
17801
+ },
17802
+ "order": {
17803
+ "description": "Replacement reading-order position.",
17804
+ "type": "integer",
17805
+ "minimum": 1,
17806
+ "maximum": 1e3
17807
+ }
17808
+ },
17809
+ "required": [
17810
+ "publicationSubdomain",
17811
+ "articleSlug",
17812
+ "idempotencyKey",
17813
+ "baseRevision"
17814
+ ],
17815
+ "$schema": "https://json-schema.org/draft/2020-12/schema"
17816
+ },
17817
+ "annotations": {
17818
+ "title": "Edit One Transparent Commons Article",
17819
+ "readOnlyHint": false,
17820
+ "destructiveHint": false,
17821
+ "idempotentHint": true,
17822
+ "openWorldHint": true
17823
+ }
17824
+ },
17825
+ {
17826
+ "name": "site_export_image",
17827
+ "category": "web",
17828
+ "title": "Read Site Export Image",
17829
+ "description": "Return one downloaded site-export image as a standard MCP image content block with source-page and source-URL provenance. Obtain imageId from site_export_read manifest.",
17830
+ "inputSchema": {
17831
+ "type": "object",
17832
+ "properties": {
17833
+ "jobId": {
17834
+ "type": "string",
17835
+ "minLength": 1,
17836
+ "description": "Site export job ID returned by extract_site or audit_site."
17837
+ },
17838
+ "imageId": {
17839
+ "type": "string",
17840
+ "pattern": "^[a-f0-9]{64}$",
17841
+ "description": "Downloaded image ID returned by a site_export_read manifest."
17842
+ }
17843
+ },
17844
+ "required": [
17845
+ "jobId",
17846
+ "imageId"
17847
+ ],
17848
+ "$schema": "https://json-schema.org/draft/2020-12/schema"
17849
+ },
17850
+ "annotations": {
17851
+ "title": "Read Site Export Image",
17852
+ "readOnlyHint": true,
17853
+ "destructiveHint": false,
17854
+ "idempotentHint": false,
17855
+ "openWorldHint": true
17856
+ }
17857
+ },
17858
+ {
17859
+ "name": "site_export_read",
17860
+ "category": "web",
17861
+ "title": "Read Site Export Page",
17862
+ "description": "List a site export manifest or read complete JSON, HTML, or Markdown for one page in resumable UTF-8 windows. Uses owner-scoped durable content directly and does not load the final ZIP.",
17863
+ "inputSchema": {
17864
+ "type": "object",
17865
+ "properties": {
17866
+ "jobId": {
17867
+ "type": "string",
17868
+ "minLength": 1,
17869
+ "description": "Site export job ID returned by extract_site or audit_site."
17870
+ },
17871
+ "pageId": {
17872
+ "description": "Page ID returned by a manifest read. Omit to list the export manifest.",
17873
+ "type": "string",
17874
+ "pattern": "^[a-f0-9]{64}$"
17875
+ },
17876
+ "format": {
17877
+ "default": "manifest",
17878
+ "description": "manifest lists page/image IDs; JSON, HTML, and Markdown read one page representation.",
17879
+ "type": "string",
17880
+ "enum": [
17881
+ "manifest",
17882
+ "json",
17883
+ "html",
17884
+ "markdown"
17885
+ ]
17886
+ },
17887
+ "offset": {
17888
+ "default": 0,
17889
+ "description": "UTF-8 byte offset. Continue from nextOffset until it is null.",
17890
+ "type": "integer",
17891
+ "minimum": 0,
17892
+ "maximum": 9007199254740991
17893
+ },
17894
+ "maxBytes": {
17895
+ "default": 64e3,
17896
+ "description": "Maximum UTF-8 bytes returned in this window.",
17897
+ "type": "integer",
17898
+ "minimum": 1,
17899
+ "maximum": 1e6
17900
+ }
17901
+ },
17902
+ "required": [
17903
+ "jobId"
17904
+ ],
17905
+ "$schema": "https://json-schema.org/draft/2020-12/schema"
17906
+ },
17907
+ "annotations": {
17908
+ "title": "Read Site Export Page",
17909
+ "readOnlyHint": true,
17910
+ "destructiveHint": false,
17911
+ "idempotentHint": false,
17912
+ "openWorldHint": true
17913
+ }
17619
17914
  }
17620
17915
  ];
17621
17916
  var MCP_TOOL_NAMES = MCP_TOOL_CATALOG.map((tool) => tool.name);
@@ -17812,21 +18107,68 @@ Vault deposit: ${page.memory.deposited ? `saved to ${page.memory.vault}` : "fail
17812
18107
  });
17813
18108
  });
17814
18109
  });
17815
- program.command("crawl <url>").description("Crawl and extract every page of a site").option("--max-pages <n>", "maximum pages to crawl", (v) => parseInt(v, 10)).option("--json", "print raw JSON").action(async (url, opts, cmd) => {
18110
+ program.command("crawl <url>").description("Start a durable site export with complete per-page JSON, acquired HTML, and Markdown").option("--max-pages <n>", "maximum pages to crawl", (v) => parseInt(v, 10)).option("--idempotency-key <key>", "stable retry key; defaults to a new UUID for this intended crawl").option("--preserve-media", "download supported images into the export").option("--json", "print raw JSON").action(async (url, opts, cmd) => {
17816
18111
  await run(async () => {
17817
18112
  const result = await client(cmd).extractSite({
17818
18113
  url,
17819
18114
  maxPages: opts.maxPages ?? 100,
17820
- background: false,
18115
+ background: true,
17821
18116
  downloadImages: false,
18117
+ preserveMedia: Boolean(opts.preserveMedia),
17822
18118
  rotateProxies: false,
17823
18119
  rotateProxyEvery: 30,
17824
- browserFallback: false
17825
- });
18120
+ browserFallback: false,
18121
+ formats: ["json", "html", "markdown", "links"]
18122
+ }, { idempotencyKey: opts.idempotencyKey ?? randomUUID() });
17826
18123
  printResult(result, Boolean(opts.json), (r) => {
17827
- const pages = r.pages ?? [];
17828
- console.log(`Crawled ${pages.length} page(s).`);
18124
+ const job = r;
18125
+ console.log(`Export ${job.status ?? "started"}: ${job.jobId ?? "(missing job id)"}`);
18126
+ console.log(`Poll: mcpscraper export-status ${job.jobId ?? "<job-id>"}`);
18127
+ });
18128
+ });
18129
+ });
18130
+ program.command("export-status <job-id>").description("Poll a durable site export with progress, public errors, and artifact metadata").option("--json", "print raw JSON").action(async (jobId, opts, cmd) => {
18131
+ await run(async () => {
18132
+ const result = await client(cmd).getExtractSiteStatus(jobId);
18133
+ printResult(result, Boolean(opts.json), (value) => {
18134
+ const status = value;
18135
+ console.log(`${status.status ?? "unknown"} \xB7 discovered ${status.discovered ?? 0} \xB7 attempted ${status.attempted ?? 0} \xB7 successful ${status.successful ?? 0} \xB7 failed ${status.failed ?? 0} \xB7 remaining ${status.remaining ?? 0}`);
18136
+ if (status.error) console.log(status.error);
18137
+ if (["complete", "partial", "failed"].includes(status.status ?? "")) console.log(`Manifest: mcpscraper export-read ${jobId}`);
18138
+ });
18139
+ });
18140
+ });
18141
+ program.command("export-read <job-id>").description("List an export manifest or read one complete JSON, HTML, or Markdown representation").option("--page-id <id>", "page ID returned by the manifest").option("--format <format>", "manifest, json, html, or markdown", "manifest").option("--offset <n>", "UTF-8 byte offset", (value) => parseInt(value, 10), 0).option("--max-bytes <n>", "maximum bytes in this window", (value) => parseInt(value, 10), 64e3).option("--output <path>", "write the returned text window to a file").option("--json", "print response metadata and text as JSON").action(async (jobId, opts, cmd) => {
18142
+ await run(async () => {
18143
+ if (!["manifest", "json", "html", "markdown"].includes(opts.format)) throw new Error("--format must be manifest, json, html, or markdown");
18144
+ const result = await client(cmd).readExtractSiteExport({
18145
+ jobId,
18146
+ pageId: opts.pageId,
18147
+ format: opts.format,
18148
+ offset: opts.offset,
18149
+ maxBytes: opts.maxBytes
17829
18150
  });
18151
+ if (opts.output) writeFileSync(opts.output, result.text, { mode: 384 });
18152
+ if (opts.json) console.log(JSON.stringify(result, null, 2));
18153
+ else {
18154
+ if (opts.output) console.log(`Saved ${Buffer.byteLength(result.text)} bytes to ${opts.output}`);
18155
+ else process.stdout.write(result.text);
18156
+ if (result.nextOffset != null) console.error(`
18157
+ Continue with --offset ${result.nextOffset}`);
18158
+ }
18159
+ });
18160
+ });
18161
+ program.command("export-image <job-id> <image-id>").description("Download one preserved site-export image by image ID").option("--output <path>", "output path; defaults under ~/Downloads/mcp-scraper").option("--json", "print image metadata after saving").action(async (jobId, imageId, opts, cmd) => {
18162
+ await run(async () => {
18163
+ const result = await client(cmd).readExtractSiteImage({ jobId, imageId });
18164
+ const target = opts.output ?? join(homedir(), "Downloads", "mcp-scraper", `${imageId}.${mediaExtension(result.mimeType)}`);
18165
+ mkdirSync(dirname(target), { recursive: true });
18166
+ const bytes = decodeBase64(result.dataBase64);
18167
+ writeFileSync(target, bytes, { mode: 384 });
18168
+ const metadata = { ...result, dataBase64: void 0, savedPath: target };
18169
+ if (opts.json) console.log(JSON.stringify(metadata, null, 2));
18170
+ else console.log(`Saved ${bytes.length} bytes to ${target}
18171
+ Source: ${result.sourceUrl ?? "unknown"}`);
17830
18172
  });
17831
18173
  });
17832
18174
  program.command("map <url>").description("Discover all URLs on a site").option("--json", "print raw JSON").action(async (url, opts, cmd) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcpscraper-cli",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Command-line interface for mcpscraper.dev and memory.mcpscraper.dev",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -19,7 +19,7 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "commander": "^12.0.0",
22
- "mcpscraper-sdk": "^0.27.0"
22
+ "mcpscraper-sdk": "^0.29.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/node": "^22.0.0"