mcp-scraper 0.3.14 → 0.3.15
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/dist/bin/api-server.cjs +1991 -508
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +3 -3
- package/dist/bin/browser-agent-stdio-server.cjs +6 -6
- package/dist/bin/browser-agent-stdio-server.cjs.map +1 -1
- package/dist/bin/browser-agent-stdio-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +1 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs +425 -44
- package/dist/bin/mcp-scraper-combined-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-scraper-combined-stdio-server.js +6 -5
- package/dist/bin/mcp-scraper-combined-stdio-server.js.map +1 -1
- package/dist/bin/mcp-scraper-install.cjs +2 -2
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +2 -2
- package/dist/bin/mcp-stdio-server.cjs +419 -38
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +2 -2
- package/dist/chunk-2MX5WOII.js +7 -0
- package/dist/chunk-2MX5WOII.js.map +1 -0
- package/dist/{chunk-DBQDG7EH.js → chunk-5H22TOXQ.js} +28 -1
- package/dist/chunk-5H22TOXQ.js.map +1 -0
- package/dist/{chunk-KPXMPAJ3.js → chunk-7Y6JDBML.js} +2 -2
- package/dist/chunk-7Y6JDBML.js.map +1 -0
- package/dist/{chunk-AX7UBYLG.js → chunk-WJ2LWHPA.js} +7 -7
- package/dist/chunk-WJ2LWHPA.js.map +1 -0
- package/dist/{chunk-HM6FHV5U.js → chunk-WQAIOY2D.js} +424 -39
- package/dist/chunk-WQAIOY2D.js.map +1 -0
- package/dist/{chunk-AZ5PKU4F.js → chunk-ZAUMSBV3.js} +2 -2
- package/dist/{db-BE4JVB3V.js → db-P76GVIFN.js} +2 -2
- package/dist/{server-ILIVSPNY.js → server-UOQ2JIUQ.js} +1106 -93
- package/dist/server-UOQ2JIUQ.js.map +1 -0
- package/dist/{worker-FG7ZWEGA.js → worker-2WVKKCC7.js} +3 -3
- package/docs/final-tooling-spec.md +206 -0
- package/docs/mcp-tool-design-guide.md +225 -0
- package/docs/mcp-tool-manifest.generated.json +13 -13
- package/docs/seo-crawl-report-spec.md +287 -0
- package/docs/tool-catalog-spec.md +388 -0
- package/package.json +2 -1
- package/dist/chunk-3QHZPR4U.js +0 -7
- package/dist/chunk-3QHZPR4U.js.map +0 -1
- package/dist/chunk-AX7UBYLG.js.map +0 -1
- package/dist/chunk-DBQDG7EH.js.map +0 -1
- package/dist/chunk-HM6FHV5U.js.map +0 -1
- package/dist/chunk-KPXMPAJ3.js.map +0 -1
- package/dist/server-ILIVSPNY.js.map +0 -1
- /package/dist/{chunk-AZ5PKU4F.js.map → chunk-ZAUMSBV3.js.map} +0 -0
- /package/dist/{db-BE4JVB3V.js.map → db-P76GVIFN.js.map} +0 -0
- /package/dist/{worker-FG7ZWEGA.js.map → worker-2WVKKCC7.js.map} +0 -0
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
# Tool Catalog Spec — Firecrawl-style categorized tool surface
|
|
2
|
+
|
|
3
|
+
Engineering blueprint for a DB-backed **tool catalog** that drives the frontend's categorized
|
|
4
|
+
tool picker (Discover / Extract / Crawl / Special), the per-tool **source dropdowns**
|
|
5
|
+
(Search, Transcribe), **mode toggles** (Scrape scope, Browser Mode), and the dynamic **param forms**.
|
|
6
|
+
|
|
7
|
+
The catalog is the single source of truth the frontend reads via `GET /catalog`. It is seeded
|
|
8
|
+
idempotently from a canonical TS definition on startup, so the DB is the runtime store and code is
|
|
9
|
+
the authored source. Underlying execution still routes to the existing MCP tools / REST endpoints —
|
|
10
|
+
the catalog only describes *how to present and assemble a request*.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 0. Current-state anchors (verified this codebase)
|
|
15
|
+
|
|
16
|
+
- MCP tool metadata already lives in `src/mcp/mcp-tool-manifest.ts` (name, surfaces, sample prompts,
|
|
17
|
+
format examples). The catalog is a *presentation* layer on top; it references tool names but adds
|
|
18
|
+
grouping, sources, modes, and form fields the manifest doesn't model.
|
|
19
|
+
- Input schemas: `src/mcp/mcp-tool-schemas.ts` (Zod). Param definitions in the catalog should mirror
|
|
20
|
+
these (same keys, bounds, enums) so the form posts valid bodies.
|
|
21
|
+
- REST routes: `src/api/server.ts` (e.g. `POST /extract-site`, `POST /map-urls`, `POST /extract-url`,
|
|
22
|
+
`/youtube/*`, `/maps/*`, `/instagram/*`). Catalog rows store the route each tool/source hits.
|
|
23
|
+
- DB: Turso/libSQL via `src/api/db.ts` `migrate()` (CREATE TABLE IF NOT EXISTS, TEXT/INTEGER,
|
|
24
|
+
`datetime('now')`). Billing ops in `src/api/rates.ts` (`LedgerOperation`, `MC_COSTS`).
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 1. Information architecture
|
|
29
|
+
|
|
30
|
+
Three render primitives, chosen per tool by `catalog_tools.kind`:
|
|
31
|
+
|
|
32
|
+
| kind | Frontend control | Examples |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| `simple` | param form only | Map, Directory, Place Intel |
|
|
35
|
+
| `composite_source` | **source dropdown** (multi-select) + per-source params | Search, Transcribe |
|
|
36
|
+
| `modal` | **mode toggle groups** + param form | Scrape (scope + browser_mode) |
|
|
37
|
+
|
|
38
|
+
Groups (top nav, Firecrawl's DISCOVER/EXTRACT/CRAWL plus our SPECIAL):
|
|
39
|
+
|
|
40
|
+
| group key | label | tools |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| `discover` | Discover | search |
|
|
43
|
+
| `extract` | Extract | scrape, transcribe |
|
|
44
|
+
| `crawl` | Crawl | map, crawl |
|
|
45
|
+
| `special` | Special | directory, seo_analysis, deep_research, place_intel, page_intel, rank_tracker, ai_visibility |
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 2. DB schema — full DDL (add to `src/api/db.ts` `migrate()`)
|
|
50
|
+
|
|
51
|
+
All TEXT PKs are stable string keys (not autoincrement) so seed data is idempotent and referenceable.
|
|
52
|
+
|
|
53
|
+
```sql
|
|
54
|
+
CREATE TABLE IF NOT EXISTS catalog_groups (
|
|
55
|
+
key TEXT PRIMARY KEY, -- 'discover' | 'extract' | 'crawl' | 'special'
|
|
56
|
+
label TEXT NOT NULL,
|
|
57
|
+
icon TEXT, -- lucide icon name
|
|
58
|
+
display_order INTEGER NOT NULL DEFAULT 0
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
CREATE TABLE IF NOT EXISTS catalog_tools (
|
|
62
|
+
key TEXT PRIMARY KEY, -- 'search' | 'scrape' | 'crawl' | 'map' | 'transcribe' | ...
|
|
63
|
+
group_key TEXT NOT NULL REFERENCES catalog_groups(key),
|
|
64
|
+
label TEXT NOT NULL,
|
|
65
|
+
tagline TEXT, -- one-liner under the card
|
|
66
|
+
description TEXT, -- full help text
|
|
67
|
+
icon TEXT,
|
|
68
|
+
kind TEXT NOT NULL, -- 'simple' | 'composite_source' | 'modal'
|
|
69
|
+
is_special INTEGER NOT NULL DEFAULT 0,
|
|
70
|
+
is_workflow INTEGER NOT NULL DEFAULT 0, -- backed by a skill/workflow vs a direct tool/route
|
|
71
|
+
default_endpoint TEXT, -- REST route for simple/modal tools (null for composite_source — see sources)
|
|
72
|
+
default_mcp_name TEXT, -- MCP tool name for simple/modal tools
|
|
73
|
+
billing_op TEXT, -- LedgerOperation key (rates.ts) for cost display
|
|
74
|
+
cost_hint_mc INTEGER, -- per-unit MC estimate for the UI
|
|
75
|
+
enabled INTEGER NOT NULL DEFAULT 1,
|
|
76
|
+
display_order INTEGER NOT NULL DEFAULT 0,
|
|
77
|
+
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
|
78
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
CREATE TABLE IF NOT EXISTS catalog_sources (
|
|
82
|
+
id TEXT PRIMARY KEY, -- '<tool>.<source>' e.g. 'search.web'
|
|
83
|
+
tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
|
|
84
|
+
key TEXT NOT NULL, -- 'web' | 'maps' | 'fb_ads' | 'youtube' | 'fb_video' ...
|
|
85
|
+
label TEXT NOT NULL,
|
|
86
|
+
icon TEXT,
|
|
87
|
+
endpoint TEXT NOT NULL, -- REST route this source posts to
|
|
88
|
+
mcp_name TEXT NOT NULL, -- MCP tool name this source maps to
|
|
89
|
+
billing_op TEXT,
|
|
90
|
+
cost_hint_mc INTEGER,
|
|
91
|
+
default_selected INTEGER NOT NULL DEFAULT 0,
|
|
92
|
+
multi_select INTEGER NOT NULL DEFAULT 1, -- may be combined with other sources
|
|
93
|
+
display_order INTEGER NOT NULL DEFAULT 0,
|
|
94
|
+
UNIQUE (tool_key, key)
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
CREATE TABLE IF NOT EXISTS catalog_modes (
|
|
98
|
+
id TEXT PRIMARY KEY, -- '<tool>.<group>.<option>' e.g. 'scrape.scope.bulk'
|
|
99
|
+
tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
|
|
100
|
+
group_key TEXT NOT NULL, -- 'scope' | 'browser_mode'
|
|
101
|
+
group_label TEXT NOT NULL,
|
|
102
|
+
option_key TEXT NOT NULL, -- 'single' | 'bulk' | 'default' | 'chrome_profile'
|
|
103
|
+
option_label TEXT NOT NULL,
|
|
104
|
+
is_default INTEGER NOT NULL DEFAULT 0,
|
|
105
|
+
endpoint_override TEXT, -- if this option changes the target route
|
|
106
|
+
mcp_name_override TEXT,
|
|
107
|
+
param_overrides TEXT, -- JSON object merged into the request body when selected
|
|
108
|
+
display_order INTEGER NOT NULL DEFAULT 0,
|
|
109
|
+
UNIQUE (tool_key, group_key, option_key)
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
CREATE TABLE IF NOT EXISTS catalog_params (
|
|
113
|
+
id TEXT PRIMARY KEY, -- '<tool>.<source|*>.<param>'
|
|
114
|
+
tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
|
|
115
|
+
source_key TEXT, -- null = applies to all; else only when that source/mode active
|
|
116
|
+
key TEXT NOT NULL, -- the actual API body field name
|
|
117
|
+
label TEXT NOT NULL,
|
|
118
|
+
type TEXT NOT NULL, -- 'string'|'number'|'boolean'|'enum'|'multi_enum'|'url'
|
|
119
|
+
required INTEGER NOT NULL DEFAULT 0,
|
|
120
|
+
default_value TEXT, -- JSON-encoded
|
|
121
|
+
min_value REAL,
|
|
122
|
+
max_value REAL,
|
|
123
|
+
enum_values TEXT, -- JSON array of {value,label}
|
|
124
|
+
placeholder TEXT,
|
|
125
|
+
help TEXT,
|
|
126
|
+
depends_on TEXT, -- another param key
|
|
127
|
+
depends_value TEXT, -- show this param only when depends_on == depends_value
|
|
128
|
+
advanced INTEGER NOT NULL DEFAULT 0, -- behind an "Advanced" disclosure
|
|
129
|
+
display_order INTEGER NOT NULL DEFAULT 0,
|
|
130
|
+
UNIQUE (tool_key, source_key, key)
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
CREATE TABLE IF NOT EXISTS catalog_examples (
|
|
134
|
+
id TEXT PRIMARY KEY,
|
|
135
|
+
tool_key TEXT NOT NULL REFERENCES catalog_tools(key),
|
|
136
|
+
text TEXT NOT NULL,
|
|
137
|
+
display_order INTEGER NOT NULL DEFAULT 0
|
|
138
|
+
);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Indexes:
|
|
142
|
+
```sql
|
|
143
|
+
CREATE INDEX IF NOT EXISTS catalog_tools_group ON catalog_tools(group_key);
|
|
144
|
+
CREATE INDEX IF NOT EXISTS catalog_sources_tool ON catalog_sources(tool_key);
|
|
145
|
+
CREATE INDEX IF NOT EXISTS catalog_modes_tool ON catalog_modes(tool_key);
|
|
146
|
+
CREATE INDEX IF NOT EXISTS catalog_params_tool ON catalog_params(tool_key);
|
|
147
|
+
CREATE INDEX IF NOT EXISTS catalog_examples_tool ON catalog_examples(tool_key);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 3. Complete catalog mapping (the seed data — every tool, source, mode, param)
|
|
153
|
+
|
|
154
|
+
Authored in `src/api/catalog-seed.ts` as typed constants, written via `seedCatalog()` (Section 5).
|
|
155
|
+
|
|
156
|
+
### 3.1 Groups
|
|
157
|
+
```
|
|
158
|
+
discover "Discover" order 0
|
|
159
|
+
extract "Extract" order 1
|
|
160
|
+
crawl "Crawl" order 2
|
|
161
|
+
special "Special" order 3
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### 3.2 Tools
|
|
165
|
+
|
|
166
|
+
Endpoints and billing_op VERIFIED against `http-mcp-tool-executor.ts` and `rates.ts LedgerOperation`:
|
|
167
|
+
|
|
168
|
+
| key | group | label | kind | endpoint · mcp | special | workflow | billing_op |
|
|
169
|
+
|---|---|---|---|---|---|---|---|
|
|
170
|
+
| `search` | discover | Search | composite_source | (per source) | 0 | 0 | (per source) |
|
|
171
|
+
| `scrape` | extract | Scrape | modal | (per mode) | 0 | 0 | `EXTRACT_URL` / `EXTRACT_SITE` |
|
|
172
|
+
| `transcribe` | extract | Transcribe | composite_source | (per source) | 0 | 0 | (per source) |
|
|
173
|
+
| `map` | crawl | Map | simple | `/map-urls` · `map_site_urls` | 0 | 0 | `URL_MAP` ✅(was MAP_SITE) |
|
|
174
|
+
| `crawl` | crawl | Crawl | simple | `/extract-site` · `extract_site` | 0 | 0 | `EXTRACT_SITE` |
|
|
175
|
+
| `directory` | special | Directory | simple | `/directory/run` ✅(was /directory) · `directory_workflow` | 1 | 1 | ⚠️ no dedicated op — bills per-city via `MAPS_SEARCH`; verify in `/directory/run` handler |
|
|
176
|
+
| `seo_analysis` | special | SEO Technical Analysis | simple | `/extract-site` (background) · `extract_site` | 1 | 1 | `EXTRACT_SITE` |
|
|
177
|
+
| `deep_research` | special | Deep Research | simple | `/workflows/run` · `workflow_run` | 1 | 1 | ⚠️ no `WORKFLOW` op — confirm how `/workflows/run` bills |
|
|
178
|
+
| `place_intel` | special | Place Intel | simple | `/maps/place` ✅(was /maps/place-intel) · `maps_place_intel` | 1 | 0 | `MAPS_PLACE` |
|
|
179
|
+
| `page_intel` | special | Page Intel | simple | `/facebook/page-intel` · `facebook_page_intel` | 1 | 0 | ⚠️ no `FB_PAGE` op — verify which op `/facebook/page-intel` debits |
|
|
180
|
+
| `rank_tracker` | special | Rank Tracker | simple | ⚠️ NO REST route in executor · `rank_tracker_blueprint` | 1 | 1 | ⚠️ unknown — see §9 |
|
|
181
|
+
| `ai_visibility` | special | AI Visibility (Fan-out) | simple | ⚠️ no simple REST route — driven by `browser_*` agent · `browser_capture_fanout` | 1 | 0 | `BROWSER_SESSION` ✅(was BROWSER_AGENT) |
|
|
182
|
+
|
|
183
|
+
### 3.3 Sources
|
|
184
|
+
|
|
185
|
+
**search** (multi-select, default = web) — routes VERIFIED against `http-mcp-tool-executor.ts`:
|
|
186
|
+
| key | label | endpoint | mcp_name | billing_op | default |
|
|
187
|
+
|---|---|---|---|---|---|
|
|
188
|
+
| `web` | Web | `/harvest/sync` (body `serpOnly:true`) | `search_serp` | `SERP` | ✓ |
|
|
189
|
+
| `web_detailed` | Web — SERP + PAA detail | `/harvest/sync` | `harvest_paa` | `PAA` | |
|
|
190
|
+
| `maps` | Maps | `/maps/search` | `maps_search` | `MAPS_SEARCH` | |
|
|
191
|
+
| `fb_ads` | Facebook Ads | `/facebook/search` | `facebook_ad_search` | `FB_SEARCH` | |
|
|
192
|
+
| `youtube` | YouTube | `/youtube/harvest` | `youtube_harvest` | `YT_CHANNEL` | |
|
|
193
|
+
| `instagram` | Instagram | `/instagram/profile-content` | `instagram_profile_content` | `INSTAGRAM_PROFILE` | |
|
|
194
|
+
|
|
195
|
+
> Note: `search_serp` and `harvest_paa` BOTH hit `/harvest/sync` — `search_serp` adds `serpOnly:true`.
|
|
196
|
+
> The catalog source rows carry that override in `catalog_sources` (add a `body_overrides TEXT` column,
|
|
197
|
+
> see §9) rather than via separate endpoints.
|
|
198
|
+
|
|
199
|
+
**transcribe** (single-select, default = youtube) — routes VERIFIED:
|
|
200
|
+
| key | label | endpoint | mcp_name | billing_op | default |
|
|
201
|
+
|---|---|---|---|---|---|
|
|
202
|
+
| `youtube` | YouTube Video | `/youtube/transcribe` | `youtube_transcribe` | `TRANSCRIPTION` | ✓ |
|
|
203
|
+
| `fb_video` | Facebook Video | `/facebook/video-transcribe` | `facebook_video_transcribe` | `TRANSCRIPTION` | |
|
|
204
|
+
| `fb_ad` | Facebook Ad | `/facebook/transcribe` | `facebook_ad_transcribe` | `FB_TRANSCRIBE` | |
|
|
205
|
+
| `instagram` | Instagram Reel | `/instagram/media-download` | `instagram_media_download` | `INSTAGRAM_MEDIA` | |
|
|
206
|
+
|
|
207
|
+
### 3.4 Modes (scrape)
|
|
208
|
+
|
|
209
|
+
group `scope` (default single):
|
|
210
|
+
| option | label | endpoint_override | mcp_name_override | param_overrides |
|
|
211
|
+
|---|---|---|---|---|
|
|
212
|
+
| `single` | Headless Single | `/extract-url` | `extract_url` | `{}` |
|
|
213
|
+
| `bulk` | Headless Bulk | `/extract-site` | `extract_site` | `{ "rotateProxies": true }` |
|
|
214
|
+
|
|
215
|
+
group `browser_mode` (default default):
|
|
216
|
+
| option | label | param_overrides |
|
|
217
|
+
|---|---|---|
|
|
218
|
+
| `default` | Default | `{}` |
|
|
219
|
+
| `chrome_profile` | Use Chrome Profile | `{ "browserProfile": true }` |
|
|
220
|
+
|
|
221
|
+
> `browserProfile` is a new body flag the scrape route would honor to route through a saved hosted
|
|
222
|
+
> profile (the `browser_profile_*` system). Wire in a follow-up; the catalog can ship the toggle disabled.
|
|
223
|
+
|
|
224
|
+
### 3.5 Params (per tool/source) — keys MUST match the Zod schemas in `mcp-tool-schemas.ts`
|
|
225
|
+
|
|
226
|
+
**search / web** (`search_serp`): `query`(string,req), `location`(string), `gl`(string),
|
|
227
|
+
`maxResults`(number,1..100,default 10).
|
|
228
|
+
**search / web_detailed** (`harvest_paa`): `query`(string,req), `maxQuestions`(number,1..200,default 30), `location`(string).
|
|
229
|
+
**search / maps** (`maps_search`): `query`(string,req), `location`(string,req), `maxResults`(number,1..20,default 10), `includeReviews`(boolean).
|
|
230
|
+
**search / fb_ads** (`facebook_ad_search`): `query`(string,req), `country`(string,default US), `maxResults`(number,1..20,default 10).
|
|
231
|
+
**search / youtube** (`youtube_harvest`): mode `enum[search,channel]`, `query`(string), `channelHandle`(string), `maxVideos`(number,1..500,default 50).
|
|
232
|
+
**search / instagram** (`instagram_profile_content`): `handle|url`(string,req), `maxItems`(number,1..2000,default 50), `maxScrolls`(number,0..250,default 10).
|
|
233
|
+
|
|
234
|
+
**transcribe / ***: `url`(url,req) for all four. Add `includeTranscript`(boolean,default true) +
|
|
235
|
+
`mediaTypes`(multi_enum[image,video,audio]) for instagram.
|
|
236
|
+
|
|
237
|
+
**scrape (all)**: `url`(url,req), `formats`(multi_enum[markdown,links,json,images,branding]),
|
|
238
|
+
`screenshot`(boolean,advanced), `downloadMedia`(boolean,advanced).
|
|
239
|
+
**scrape / scope=bulk only** (`source_key='scope:bulk'`): `maxPages`(number,1..10000,default 100),
|
|
240
|
+
`rotateProxyEvery`(number,1..100,default 30,advanced), `background`(boolean).
|
|
241
|
+
|
|
242
|
+
**map** (`map_site_urls`): `url`(url,req), `maxUrls`(number,1..10000,default 100).
|
|
243
|
+
**crawl** (`extract_site`): `url`(url,req), `maxPages`(number,1..10000,default 100),
|
|
244
|
+
`formats`(multi_enum...), `rotateProxies`(boolean,default true), `background`(boolean,default true),
|
|
245
|
+
`rotateProxyEvery`(number,advanced).
|
|
246
|
+
|
|
247
|
+
**directory** (`directory_workflow`): `query`(string,req), `state`(string,req), `minPopulation`(number,default 100000),
|
|
248
|
+
`maxResultsPerCity`(number,1..50,default 50,advanced), `concurrency`(number,advanced).
|
|
249
|
+
**seo_analysis** (`extract_site` background): `url`(url,req), `maxPages`(number,default 1000),
|
|
250
|
+
`formats` preset `["markdown","links","json","images"]` (hidden/locked).
|
|
251
|
+
**deep_research**: `question`(string,req), `depth`(enum[quick,standard,deep],default standard).
|
|
252
|
+
**place_intel** (`maps_place_intel`): `businessName`(string,req), `location`(string,req), `includeReviews`(boolean), `maxReviews`(number,1..500,default 50).
|
|
253
|
+
**page_intel** (`facebook_page_intel`): `url|pageId`(string,req).
|
|
254
|
+
**rank_tracker** (`rank_tracker_blueprint`): `domain`(string,req), `keywords`(array<string>), `location`(string).
|
|
255
|
+
**ai_visibility** (`browser_capture_fanout`): `prompt`(string), `session_id`(string), `first_party_domain`(string), `export`(boolean).
|
|
256
|
+
|
|
257
|
+
### 3.6 Examples (sample prompts)
|
|
258
|
+
- search: "Top restaurants in San Francisco"
|
|
259
|
+
- scrape: "https://steenshoney.com/"
|
|
260
|
+
- crawl: "https://steenshoney.com/ (full site SEO crawl)"
|
|
261
|
+
- transcribe: a Facebook/YouTube video URL
|
|
262
|
+
- directory: "roofers in TN, minPopulation 100000"
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 4. Repository — `src/api/tool-catalog-repository.ts`
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
export interface CatalogParam { key: string; label: string; type: string; required: boolean;
|
|
270
|
+
default?: unknown; min?: number; max?: number; enumValues?: Array<{value:string;label:string}>;
|
|
271
|
+
placeholder?: string; help?: string; dependsOn?: string; dependsValue?: string; advanced: boolean; sourceKey: string | null }
|
|
272
|
+
export interface CatalogSource { key: string; label: string; icon?: string; endpoint: string; mcpName: string;
|
|
273
|
+
defaultSelected: boolean; multiSelect: boolean; costHintMc?: number }
|
|
274
|
+
export interface CatalogModeOption { groupKey: string; groupLabel: string; optionKey: string; optionLabel: string;
|
|
275
|
+
isDefault: boolean; endpointOverride?: string; mcpNameOverride?: string; paramOverrides?: Record<string,unknown> }
|
|
276
|
+
export interface CatalogTool { key: string; groupKey: string; label: string; tagline?: string; description?: string;
|
|
277
|
+
icon?: string; kind: 'simple'|'composite_source'|'modal'; isSpecial: boolean; isWorkflow: boolean;
|
|
278
|
+
defaultEndpoint?: string; defaultMcpName?: string; billingOp?: string; costHintMc?: number;
|
|
279
|
+
sources: CatalogSource[]; modes: CatalogModeOption[]; params: CatalogParam[]; examples: string[] }
|
|
280
|
+
export interface CatalogGroup { key: string; label: string; icon?: string; tools: CatalogTool[] }
|
|
281
|
+
|
|
282
|
+
export async function getCatalog(): Promise<CatalogGroup[]> // one assembled tree, ordered
|
|
283
|
+
export async function seedCatalog(): Promise<void> // idempotent upsert from catalog-seed.ts
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
`getCatalog()`: 5 SELECTs (groups, tools, sources, modes, params, examples) ordered by `display_order`,
|
|
287
|
+
assembled in memory into the nested tree. Single round-trip-ish; cache in-process for 60s (catalog is static).
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## 5. Seeding — `src/api/catalog-seed.ts` + wire into `migrate()`
|
|
292
|
+
|
|
293
|
+
- `catalog-seed.ts` exports `CATALOG_SEED` (typed literal mirroring Section 3).
|
|
294
|
+
- `seedCatalog()` performs `INSERT ... ON CONFLICT(key/id) DO UPDATE` (libSQL `INSERT OR REPLACE`)
|
|
295
|
+
for every row, so redeploys converge the DB to the authored definition. Rows removed from the seed
|
|
296
|
+
are pruned with `DELETE FROM <table> WHERE key NOT IN (...)`.
|
|
297
|
+
- Call order in `db.ts`: after `migrate()` creates the tables, call `await seedCatalog()` once at boot
|
|
298
|
+
(in `bin/api-server.ts` startup, after `migrate()`), behind `if (process.env.SEED_CATALOG !== 'false')`.
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## 6. REST endpoint — `src/api/server.ts`
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
GET /catalog -> 200 { groups: CatalogGroup[] } (public; no auth; cache-control 60s)
|
|
306
|
+
```
|
|
307
|
+
- Add `app.get('/catalog', async (c) => c.json({ groups: await getCatalog() }))`.
|
|
308
|
+
- No vendor leakage risk (static metadata) but still run labels/descriptions through the existing
|
|
309
|
+
output path conventions if any dynamic field is ever added.
|
|
310
|
+
|
|
311
|
+
Frontend contract (the exact JSON the picker consumes):
|
|
312
|
+
```jsonc
|
|
313
|
+
{ "groups": [
|
|
314
|
+
{ "key":"discover","label":"Discover","icon":"search","tools":[
|
|
315
|
+
{ "key":"search","label":"Search","kind":"composite_source","isSpecial":false,
|
|
316
|
+
"sources":[{"key":"web","label":"Web","endpoint":"/search-serp","mcpName":"search_serp","defaultSelected":true,"multiSelect":true}, ...],
|
|
317
|
+
"modes":[], "params":[{"key":"query","label":"Query","type":"string","required":true,"sourceKey":null}, ...],
|
|
318
|
+
"examples":["Top restaurants in San Francisco"] } ]},
|
|
319
|
+
... ] }
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Frontend behavior:
|
|
323
|
+
- Render groups as the top nav (Discover/Extract/Crawl/Special). `isSpecial` tools live under the
|
|
324
|
+
**Special** group already by `group_key='special'`; the flag is redundant insurance for filtering.
|
|
325
|
+
- `composite_source` → render the source dropdown (multi/single per `multiSelect`); union the params of
|
|
326
|
+
selected sources (params with matching `sourceKey`) plus shared params (`sourceKey=null`).
|
|
327
|
+
- `modal` → render mode toggle groups; on selection, merge `paramOverrides` into the body and switch
|
|
328
|
+
endpoint per `endpointOverride`.
|
|
329
|
+
- `simple` → render `params` form; POST to `defaultEndpoint`.
|
|
330
|
+
- "Get code" button: serialize the assembled request (endpoint + body) to a curl/SDK snippet.
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 7. Build order
|
|
335
|
+
1. **Schema** — add the 6 tables + indexes to `db.ts migrate()`. (P1)
|
|
336
|
+
2. **Seed authoring** — `catalog-seed.ts` with the full Section-3 mapping. (P1)
|
|
337
|
+
3. **Repository + seed routine** — `tool-catalog-repository.ts`; call `seedCatalog()` at boot. (P1)
|
|
338
|
+
4. **Endpoint** — `GET /catalog`. (P1)
|
|
339
|
+
5. **Billing ops** — add any missing `LedgerOperation` keys referenced by `billing_op`. (P1, gating)
|
|
340
|
+
6. **Frontend picker** — consume `/catalog`, render the three kinds. (P2)
|
|
341
|
+
7. **Mode wiring follow-ups** — `browserProfile` flag on the scrape route; `seo_analysis`/`deep_research`
|
|
342
|
+
workflow runners if not already endpoints. (P2/P3)
|
|
343
|
+
|
|
344
|
+
## 8. Non-goals (v1)
|
|
345
|
+
Auth-gated per-plan tool visibility, usage analytics on catalog, A/B of layouts, i18n of labels,
|
|
346
|
+
and the actual `browserProfile` / workflow-runner execution wiring (catalog ships them as
|
|
347
|
+
described-but-some-disabled until the routes exist).
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## 9. Verification notes (self-checked against the codebase)
|
|
352
|
+
|
|
353
|
+
Routes verified against `src/mcp/http-mcp-tool-executor.ts`; ledger ops against
|
|
354
|
+
`src/api/rates.ts LedgerOperation`. Corrections already applied above:
|
|
355
|
+
|
|
356
|
+
**Route fixes (my first draft was wrong):**
|
|
357
|
+
- `search_serp` → **`/harvest/sync` with body `serpOnly:true`** (not `/search-serp` — that route doesn't exist).
|
|
358
|
+
- `harvest_paa` → **`/harvest/sync`** (not `/harvest-paa`). Both search sources share this route.
|
|
359
|
+
- `facebook_ad_search` → **`/facebook/search`** (not `/facebook/ad-search`).
|
|
360
|
+
- `facebook_ad_transcribe` → **`/facebook/transcribe`** (not `/facebook/ad-transcribe`).
|
|
361
|
+
- `maps_place_intel` → **`/maps/place`** (not `/maps/place-intel`).
|
|
362
|
+
- `directory_workflow` → **`/directory/run`** (the executor route; there is also a separate top-level `/directory` page route — do not confuse).
|
|
363
|
+
|
|
364
|
+
**Billing-op fixes:**
|
|
365
|
+
- `map` → **`URL_MAP`** (not the invented `MAP_SITE`).
|
|
366
|
+
- `ai_visibility` → **`BROWSER_SESSION`** (not `BROWSER_AGENT`).
|
|
367
|
+
- Verified-correct as-is: `EXTRACT_URL`, `EXTRACT_SITE`, `MAPS_PLACE`, `MAPS_SEARCH`, `SERP`, `PAA`,
|
|
368
|
+
`FB_SEARCH`, `FB_TRANSCRIBE`, `YT_CHANNEL`, `TRANSCRIPTION`, `INSTAGRAM_PROFILE`, `INSTAGRAM_MEDIA`.
|
|
369
|
+
|
|
370
|
+
**Unresolved — needs a human/code check before seeding (do NOT guess):**
|
|
371
|
+
1. **`rank_tracker_blueprint` has NO route in `http-mcp-tool-executor.ts`.** It's a registered MCP tool
|
|
372
|
+
but isn't wired through the HTTP executor — it may be served by a different server (`paa-mcp-server`),
|
|
373
|
+
be skill-only, or unimplemented over REST. Confirm its execution path before adding it to the catalog;
|
|
374
|
+
otherwise ship it disabled.
|
|
375
|
+
2. **No dedicated ledger ops for `directory`, `deep_research`/workflows, `page_intel`.** Directory likely
|
|
376
|
+
bills per-city through `MAPS_SEARCH`; `/workflows/run` and `/facebook/page-intel` billing must be read
|
|
377
|
+
from their handlers. Set `billing_op` only after confirming, or leave null and compute cost server-side.
|
|
378
|
+
3. **`ai_visibility` / `browser_capture_fanout` is not a simple POST** — it requires an open browser
|
|
379
|
+
session (`session_id` from `browser_open`). It can't be a one-shot form like the others; model it as a
|
|
380
|
+
guided flow or omit from v1.
|
|
381
|
+
|
|
382
|
+
**Schema addendum from verification:** add `body_overrides TEXT` (JSON) to `catalog_sources` so the
|
|
383
|
+
`web` source can carry `{"serpOnly":true}` against the shared `/harvest/sync` route. (The §2 DDL should
|
|
384
|
+
include this column.)
|
|
385
|
+
|
|
386
|
+
**Confidence:** the four core verbs (Search, Scrape, Map, Crawl) + Transcribe + Place/Page Intel are
|
|
387
|
+
fully route- and billing-verified and safe to build. The three flagged Special tools
|
|
388
|
+
(rank_tracker, deep_research, ai_visibility) need route/billing confirmation first.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-scraper",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.15",
|
|
4
4
|
"description": "MCP server for MCP Scraper web intelligence tools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -58,6 +58,7 @@
|
|
|
58
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
59
59
|
"@onkernel/sdk": "^0.52.0",
|
|
60
60
|
"@resvg/resvg-js": "^2.6.2",
|
|
61
|
+
"@vercel/blob": "^2.4.1",
|
|
61
62
|
"commander": "^12.0.0",
|
|
62
63
|
"hono": "^4.12.18",
|
|
63
64
|
"inngest": "^4.4.0",
|
package/dist/chunk-3QHZPR4U.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/version.ts"],"sourcesContent":["export const PACKAGE_VERSION = '0.3.14'\n"],"mappings":";AAAO,IAAM,kBAAkB;","names":[]}
|