mcpscraper-cli 0.7.0 → 0.8.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.
- package/README.md +4 -2
- package/dist/bin/cli.js +167 -13
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Command-line interface for [mcpscraper.dev](https://mcpscraper.dev) and [memory.mcpscraper.dev](https://memory.mcpscraper.dev).
|
|
4
4
|
|
|
5
|
-
The CLI provides friendly shortcuts for common operations plus universal discovery and invocation for every one of the
|
|
5
|
+
The CLI provides friendly shortcuts for common operations plus universal discovery and invocation for every one of the 158 unified MCP tools (73 MCP Scraper plus 85 mirrored memory tools).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -32,7 +32,9 @@ mcpscraper tools call describe_service_connection_tool --args '{"connectionId":"
|
|
|
32
32
|
mcpscraper tools call renew_connected_data_download --args '{"artifactId":"artifact_123"}' --json
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
`tools list` contains exactly
|
|
35
|
+
`tools list` contains exactly 158 manifest-backed names. `tools call` accepts any one of them. Tools marked destructive require `--yes`.
|
|
36
|
+
|
|
37
|
+
When a tool returns inline MCP media, the CLI writes each block to a private local file and prints its path instead of dumping base64. Use `--media-dir <path>` to choose the destination; the default is `~/Downloads/mcp-scraper`.
|
|
36
38
|
|
|
37
39
|
### `mcpscraper search <query>`
|
|
38
40
|
|
package/dist/bin/cli.js
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
-
import { realpathSync } from "fs";
|
|
6
|
+
import { mkdirSync, realpathSync, writeFileSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
7
9
|
import { ScraperClient, ScraperApiError } from "mcpscraper-sdk";
|
|
8
10
|
|
|
9
11
|
// src/version.ts
|
|
10
|
-
var CLI_VERSION = "0.
|
|
12
|
+
var CLI_VERSION = "0.8.0";
|
|
11
13
|
|
|
12
14
|
// src/generated-tools.ts
|
|
13
15
|
var MCP_TOOL_CATALOG = [
|
|
@@ -510,7 +512,7 @@ var MCP_TOOL_CATALOG = [
|
|
|
510
512
|
"name": "facebook_page_intel",
|
|
511
513
|
"category": "facebook",
|
|
512
514
|
"title": "Facebook Advertiser Ad Intel",
|
|
513
|
-
"description": "Harvest
|
|
515
|
+
"description": "Harvest public Ad Library creatives. Prefer exact pageId/libraryId; query is broad keyword discovery and can mix unrelated advertisers, so inspect matchConfidence/warnings before analysis. Paused or inactive non-political ads may be absent from Ad Library\u2014use meta_ad_creative_media with the connected ad account for those. Direct Ad Library videoUrl values go to facebook_ad_transcribe.",
|
|
514
516
|
"inputSchema": {
|
|
515
517
|
"type": "object",
|
|
516
518
|
"properties": {
|
|
@@ -524,7 +526,7 @@ var MCP_TOOL_CATALOG = [
|
|
|
524
526
|
},
|
|
525
527
|
"query": {
|
|
526
528
|
"type": "string",
|
|
527
|
-
"description": "
|
|
529
|
+
"description": "Broad Ad Library keyword discovery when pageId/libraryId is not known. Results can mix unrelated advertisers; inspect matchConfidence, matchedAdvertisers, and warnings before analysis. One of pageId, libraryId, or query is required."
|
|
528
530
|
},
|
|
529
531
|
"maxAds": {
|
|
530
532
|
"type": "integer",
|
|
@@ -714,14 +716,14 @@ var MCP_TOOL_CATALOG = [
|
|
|
714
716
|
"name": "facebook_ad_transcribe",
|
|
715
717
|
"category": "facebook",
|
|
716
718
|
"title": "Facebook Ad Transcription",
|
|
717
|
-
"description": "Transcribe
|
|
719
|
+
"description": "Transcribe a direct Meta/Facebook CDN video source returned by facebook_page_intel or meta_ad_creative_media. CDN sources can expire, so use them immediately. Do not pass public post/reel/share URLs; use facebook_video_transcribe for those. For a paused/account-owned ad, start with meta_ad_creative_media: it selects the direct Graph source when available and otherwise returns the effective organic-post candidate.",
|
|
718
720
|
"inputSchema": {
|
|
719
721
|
"type": "object",
|
|
720
722
|
"properties": {
|
|
721
723
|
"videoUrl": {
|
|
722
724
|
"type": "string",
|
|
723
725
|
"format": "uri",
|
|
724
|
-
"description": "Direct Facebook CDN video URL from facebook_page_intel. Do not pass a public post/reel/share URL
|
|
726
|
+
"description": "Direct Meta/Facebook CDN video URL from facebook_page_intel or meta_ad_creative_media. Use transient sources immediately. Do not pass a public post/reel/share URL\u2014use facebook_video_transcribe for those."
|
|
725
727
|
}
|
|
726
728
|
},
|
|
727
729
|
"required": [
|
|
@@ -854,14 +856,14 @@ var MCP_TOOL_CATALOG = [
|
|
|
854
856
|
"name": "facebook_video_transcribe",
|
|
855
857
|
"category": "facebook",
|
|
856
858
|
"title": "Facebook Organic Video Transcription",
|
|
857
|
-
"description": "Transcribe audio from
|
|
859
|
+
"description": "Transcribe audio from a public Facebook reel/video/post/share URL (including fb.watch). Renders the public page, selects the best progressive MP4, and returns transcript plus resolved metadata and a low-speech signal. For a connected paused ad, use meta_ad_creative_media first; pass its public post/permalink candidate here only when no direct Graph source is available. Dark/unpublished ads may not have a public route.",
|
|
858
860
|
"inputSchema": {
|
|
859
861
|
"type": "object",
|
|
860
862
|
"properties": {
|
|
861
863
|
"url": {
|
|
862
864
|
"type": "string",
|
|
863
865
|
"format": "uri",
|
|
864
|
-
"description": "
|
|
866
|
+
"description": "Public Facebook reel/video/watch/post/share URL from facebook.com, m.facebook.com, or fb.watch. For connected account ads, get the correct public candidate from meta_ad_creative_media instead of guessing URL structure."
|
|
865
867
|
},
|
|
866
868
|
"quality": {
|
|
867
869
|
"type": "string",
|
|
@@ -7027,6 +7029,57 @@ var MCP_TOOL_CATALOG = [
|
|
|
7027
7029
|
"idempotentHint": true,
|
|
7028
7030
|
"openWorldHint": true
|
|
7029
7031
|
}
|
|
7032
|
+
},
|
|
7033
|
+
{
|
|
7034
|
+
"name": "meta_ad_creative_media",
|
|
7035
|
+
"category": "connections",
|
|
7036
|
+
"title": "View Meta Ad Creative Media",
|
|
7037
|
+
"description": "Preferred connected-account path for viewing a Meta ad creative, especially paused or dark ads that may be absent from Ad Library. Given a tenant-owned Meta connectionId and adId, resolves the ad, creative, effective story/post candidate, image assets, video assets, and transient Graph playback source. Bounded creative images are returned as actual MCP image content for vision-capable clients. For video, follow the returned exact nextActions with facebook_ad_transcribe when Graph returned a direct source, facebook_video_transcribe when only a public post/video candidate is available, or video_frame_analysis for visual breakdown. This tool is read-only and does not itself spend transcription/analysis credits.",
|
|
7038
|
+
"inputSchema": {
|
|
7039
|
+
"type": "object",
|
|
7040
|
+
"properties": {
|
|
7041
|
+
"connectionId": {
|
|
7042
|
+
"type": "string",
|
|
7043
|
+
"minLength": 1,
|
|
7044
|
+
"description": "Tenant-owned Meta Marketing connectionId from list_service_connections."
|
|
7045
|
+
},
|
|
7046
|
+
"adId": {
|
|
7047
|
+
"type": "string",
|
|
7048
|
+
"pattern": "^\\d{5,30}$",
|
|
7049
|
+
"description": "Meta ad ID from the connected ad account. This is not an Ad Library archive ID."
|
|
7050
|
+
},
|
|
7051
|
+
"imageMode": {
|
|
7052
|
+
"type": "string",
|
|
7053
|
+
"enum": [
|
|
7054
|
+
"inline_preview",
|
|
7055
|
+
"resource_only",
|
|
7056
|
+
"none"
|
|
7057
|
+
],
|
|
7058
|
+
"default": "inline_preview",
|
|
7059
|
+
"description": "inline_preview returns bounded MCP image content that a vision-capable client can inspect. resource_only returns descriptors/URLs only. none skips image delivery."
|
|
7060
|
+
},
|
|
7061
|
+
"maxInlineImages": {
|
|
7062
|
+
"type": "integer",
|
|
7063
|
+
"minimum": 1,
|
|
7064
|
+
"maximum": 4,
|
|
7065
|
+
"default": 2,
|
|
7066
|
+
"description": "Maximum creative image/thumbnail previews to attach as MCP image blocks. Default 2; maximum 4."
|
|
7067
|
+
}
|
|
7068
|
+
},
|
|
7069
|
+
"required": [
|
|
7070
|
+
"connectionId",
|
|
7071
|
+
"adId"
|
|
7072
|
+
],
|
|
7073
|
+
"additionalProperties": false,
|
|
7074
|
+
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
7075
|
+
},
|
|
7076
|
+
"annotations": {
|
|
7077
|
+
"title": "View Meta Ad Creative Media",
|
|
7078
|
+
"readOnlyHint": true,
|
|
7079
|
+
"destructiveHint": false,
|
|
7080
|
+
"idempotentHint": false,
|
|
7081
|
+
"openWorldHint": true
|
|
7082
|
+
}
|
|
7030
7083
|
}
|
|
7031
7084
|
];
|
|
7032
7085
|
var MCP_TOOL_NAMES = MCP_TOOL_CATALOG.map((tool) => tool.name);
|
|
@@ -7052,6 +7105,106 @@ function parseToolArgs(value) {
|
|
|
7052
7105
|
}
|
|
7053
7106
|
return parsed;
|
|
7054
7107
|
}
|
|
7108
|
+
function mediaExtension(mimeType) {
|
|
7109
|
+
switch (mimeType?.toLowerCase()) {
|
|
7110
|
+
case "image/png":
|
|
7111
|
+
return "png";
|
|
7112
|
+
case "image/jpeg":
|
|
7113
|
+
return "jpg";
|
|
7114
|
+
case "image/gif":
|
|
7115
|
+
return "gif";
|
|
7116
|
+
case "image/webp":
|
|
7117
|
+
return "webp";
|
|
7118
|
+
case "image/avif":
|
|
7119
|
+
return "avif";
|
|
7120
|
+
case "audio/mpeg":
|
|
7121
|
+
return "mp3";
|
|
7122
|
+
case "audio/mp4":
|
|
7123
|
+
return "m4a";
|
|
7124
|
+
case "audio/wav":
|
|
7125
|
+
case "audio/x-wav":
|
|
7126
|
+
return "wav";
|
|
7127
|
+
case "audio/ogg":
|
|
7128
|
+
return "ogg";
|
|
7129
|
+
case "audio/flac":
|
|
7130
|
+
return "flac";
|
|
7131
|
+
case "audio/aac":
|
|
7132
|
+
return "aac";
|
|
7133
|
+
case "application/pdf":
|
|
7134
|
+
return "pdf";
|
|
7135
|
+
default:
|
|
7136
|
+
return "bin";
|
|
7137
|
+
}
|
|
7138
|
+
}
|
|
7139
|
+
function safeToolName(name) {
|
|
7140
|
+
return name.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "tool";
|
|
7141
|
+
}
|
|
7142
|
+
function decodeBase64(data) {
|
|
7143
|
+
const normalized = data.replace(/\s+/g, "");
|
|
7144
|
+
if (normalized.length === 0 || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {
|
|
7145
|
+
throw new Error("Refusing to save malformed base64 content from an MCP tool result");
|
|
7146
|
+
}
|
|
7147
|
+
const unpadded = normalized.replace(/=+$/, "");
|
|
7148
|
+
const remainder = unpadded.length % 4;
|
|
7149
|
+
const expectedPadding = remainder === 0 ? 0 : 4 - remainder;
|
|
7150
|
+
const providedPadding = normalized.length - unpadded.length;
|
|
7151
|
+
if (remainder === 1 || providedPadding > 0 && providedPadding !== expectedPadding) {
|
|
7152
|
+
throw new Error("Refusing to save malformed base64 content from an MCP tool result");
|
|
7153
|
+
}
|
|
7154
|
+
return Buffer.from(unpadded.padEnd(unpadded.length + expectedPadding, "="), "base64");
|
|
7155
|
+
}
|
|
7156
|
+
function saveToolMedia(name, result, mediaDir) {
|
|
7157
|
+
const outputDirectory = mediaDir ?? process.env.MCP_SCRAPER_OUTPUT_DIR ?? join(homedir(), "Downloads", "mcp-scraper");
|
|
7158
|
+
let mediaNumber = 0;
|
|
7159
|
+
const saveBinary = (data, mimeType, kind) => {
|
|
7160
|
+
const bytes = decodeBase64(data);
|
|
7161
|
+
if (bytes.byteLength > 10 * 1024 * 1024) {
|
|
7162
|
+
throw new Error(`Refusing to save inline MCP ${kind} larger than 10 MiB (${bytes.byteLength} bytes)`);
|
|
7163
|
+
}
|
|
7164
|
+
mkdirSync(outputDirectory, { recursive: true });
|
|
7165
|
+
mediaNumber += 1;
|
|
7166
|
+
const filename = `${safeToolName(name)}-${Date.now()}-${mediaNumber}.${mediaExtension(mimeType)}`;
|
|
7167
|
+
const savedPath = join(outputDirectory, filename);
|
|
7168
|
+
writeFileSync(savedPath, bytes, { mode: 384 });
|
|
7169
|
+
return { bytes: bytes.byteLength, savedPath };
|
|
7170
|
+
};
|
|
7171
|
+
const content = (result.content ?? []).map((block) => {
|
|
7172
|
+
if ((block.type === "image" || block.type === "audio") && typeof block.data === "string") {
|
|
7173
|
+
return {
|
|
7174
|
+
...block,
|
|
7175
|
+
data: void 0,
|
|
7176
|
+
...saveBinary(block.data, block.mimeType, block.type)
|
|
7177
|
+
};
|
|
7178
|
+
}
|
|
7179
|
+
if (block.type === "resource" && block.resource && typeof block.resource === "object") {
|
|
7180
|
+
const resource = block.resource;
|
|
7181
|
+
if (typeof resource.blob === "string") {
|
|
7182
|
+
return {
|
|
7183
|
+
...block,
|
|
7184
|
+
resource: {
|
|
7185
|
+
...resource,
|
|
7186
|
+
blob: void 0,
|
|
7187
|
+
...saveBinary(resource.blob, typeof resource.mimeType === "string" ? resource.mimeType : void 0, "resource")
|
|
7188
|
+
}
|
|
7189
|
+
};
|
|
7190
|
+
}
|
|
7191
|
+
}
|
|
7192
|
+
return block;
|
|
7193
|
+
});
|
|
7194
|
+
return { ...result, content };
|
|
7195
|
+
}
|
|
7196
|
+
function printableToolResult(result) {
|
|
7197
|
+
const hasNonTextContent = result.content?.some((block) => block.type !== "text") ?? false;
|
|
7198
|
+
if (hasNonTextContent) return result;
|
|
7199
|
+
if (result.structuredContent !== void 0) return result.structuredContent;
|
|
7200
|
+
const text = result.content?.find((block) => block.type === "text")?.text;
|
|
7201
|
+
if (typeof text !== "string") return result;
|
|
7202
|
+
try {
|
|
7203
|
+
return JSON.parse(text);
|
|
7204
|
+
} catch {
|
|
7205
|
+
return text;
|
|
7206
|
+
}
|
|
7207
|
+
}
|
|
7055
7208
|
async function run(fn) {
|
|
7056
7209
|
try {
|
|
7057
7210
|
await fn();
|
|
@@ -7079,7 +7232,7 @@ function createProgram(fetchImpl = globalThis.fetch) {
|
|
|
7079
7232
|
}
|
|
7080
7233
|
const program = new Command();
|
|
7081
7234
|
program.name("mcpscraper").description(`CLI for all ${MCP_TOOL_COUNT} mcpscraper.dev MCP tools plus ergonomic shortcuts`).version(CLI_VERSION).option("--api-key <key>", "mcpscraper.dev API key (or set MCPSCRAPER_API_KEY)");
|
|
7082
|
-
program.command("search <query>").description("Run a Google SERP search").option("--location <location>", "geographic location to search from").option("--json", "print
|
|
7235
|
+
program.command("search <query>").description("Run a Google SERP search").option("--location <location>", "geographic location to search from").option("--json", "print the complete JSON result (inline binary data is saved and replaced with file metadata)").action(async (query, opts, cmd) => {
|
|
7083
7236
|
await run(async () => {
|
|
7084
7237
|
const result = await client(cmd).searchSerp({
|
|
7085
7238
|
query,
|
|
@@ -7175,7 +7328,7 @@ Vault deposit: ${page.memory.deposited ? `saved to ${page.memory.vault}` : "fail
|
|
|
7175
7328
|
});
|
|
7176
7329
|
});
|
|
7177
7330
|
});
|
|
7178
|
-
const memory = program.command("memory").description(
|
|
7331
|
+
const memory = program.command("memory").description(`Ergonomic memory shortcuts; use tools list/call for the complete ${MCP_TOOL_COUNT}-tool surface`);
|
|
7179
7332
|
memory.command("search <query>").description("Semantic search across your mcp-memory vaults").option("--json", "print raw JSON").action(async (query, opts, cmd) => {
|
|
7180
7333
|
await run(async () => {
|
|
7181
7334
|
const result = await client(cmd).memoryTools.memory.search({ query });
|
|
@@ -7198,7 +7351,7 @@ Vault deposit: ${page.memory.deposited ? `saved to ${page.memory.vault}` : "fail
|
|
|
7198
7351
|
});
|
|
7199
7352
|
});
|
|
7200
7353
|
const tools = program.command("tools").description(`Discover, inspect, and call every unified MCP tool (${MCP_TOOL_COUNT} total)`);
|
|
7201
|
-
tools.command("list").description(
|
|
7354
|
+
tools.command("list").description(`List the complete local ${MCP_TOOL_COUNT}-tool catalog`).option("--category <name>", "filter by SDK category").option("--json", "print the complete catalog as JSON").action((opts) => {
|
|
7202
7355
|
const catalog = opts.category ? MCP_TOOL_CATALOG.filter((tool) => tool.category === opts.category) : MCP_TOOL_CATALOG;
|
|
7203
7356
|
if (opts.json) {
|
|
7204
7357
|
console.log(JSON.stringify(catalog, null, 2));
|
|
@@ -7213,7 +7366,7 @@ ${catalog.length}/${MCP_TOOL_COUNT} tools`);
|
|
|
7213
7366
|
if (!tool) throw new Error(`Unknown tool "${name}". Run "mcpscraper tools list" for all ${MCP_TOOL_COUNT} names.`);
|
|
7214
7367
|
console.log(JSON.stringify(tool, null, 2));
|
|
7215
7368
|
});
|
|
7216
|
-
tools.command("call <name>").description(
|
|
7369
|
+
tools.command("call <name>").description(`Call any of the ${MCP_TOOL_COUNT} MCP tools by exact wire name`).option("--args <json>", "tool arguments as a JSON object", "{}").option("--yes", "confirm a tool marked destructive").option("--media-dir <path>", "directory for inline MCP images (defaults to ~/Downloads/mcp-scraper)").option("--json", "print raw JSON").action(async (name, opts, cmd) => {
|
|
7217
7370
|
await run(async () => {
|
|
7218
7371
|
const tool = MCP_TOOL_CATALOG.find((entry) => entry.name === name);
|
|
7219
7372
|
if (!tool) throw new Error(`Unknown tool "${name}". Run "mcpscraper tools list" for all ${MCP_TOOL_COUNT} names.`);
|
|
@@ -7221,7 +7374,8 @@ ${catalog.length}/${MCP_TOOL_COUNT} tools`);
|
|
|
7221
7374
|
if (annotations.destructiveHint && !opts.yes) {
|
|
7222
7375
|
throw new Error(`Tool "${name}" is marked destructive. Re-run with --yes to confirm.`);
|
|
7223
7376
|
}
|
|
7224
|
-
const
|
|
7377
|
+
const rawResult = await client(cmd).tools.callToolResult(name, parseToolArgs(opts.args));
|
|
7378
|
+
const result = printableToolResult(saveToolMedia(name, rawResult, opts.mediaDir));
|
|
7225
7379
|
if (opts.json || typeof result !== "string") console.log(JSON.stringify(result, null, 2));
|
|
7226
7380
|
else console.log(result);
|
|
7227
7381
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcpscraper-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Command-line interface for mcpscraper.dev and memory.mcpscraper.dev",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"test": "tsx --test test/*.test.ts"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"
|
|
21
|
-
"
|
|
20
|
+
"commander": "^12.0.0",
|
|
21
|
+
"mcpscraper-sdk": "^0.11.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^22.0.0"
|