mcp-scraper 0.4.1 → 0.4.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.
- package/dist/bin/api-server.cjs +2976 -2023
- package/dist/bin/api-server.cjs.map +1 -1
- package/dist/bin/api-server.js +2 -2
- package/dist/bin/mcp-scraper-cli.cjs +2 -1
- package/dist/bin/mcp-scraper-cli.cjs.map +1 -1
- package/dist/bin/mcp-scraper-cli.js +3 -3
- package/dist/bin/mcp-scraper-install.cjs +1 -1
- package/dist/bin/mcp-scraper-install.cjs.map +1 -1
- package/dist/bin/mcp-scraper-install.js +1 -1
- package/dist/bin/mcp-stdio-server.cjs +158 -22
- package/dist/bin/mcp-stdio-server.cjs.map +1 -1
- package/dist/bin/mcp-stdio-server.js +3 -3
- package/dist/bin/paa-harvest.cjs +1 -0
- package/dist/bin/paa-harvest.cjs.map +1 -1
- package/dist/bin/paa-harvest.js +2 -2
- package/dist/{chunk-6TDUY7CA.js → chunk-4MFYMUIF.js} +25 -3
- package/dist/chunk-4MFYMUIF.js.map +1 -0
- package/dist/{chunk-QUOZ3LZG.js → chunk-BAMVP57P.js} +157 -22
- package/dist/chunk-BAMVP57P.js.map +1 -0
- package/dist/{chunk-NNEIXK5L.js → chunk-FUWZWKGO.js} +2 -2
- package/dist/{chunk-HBC4C75S.js → chunk-NJK5BTUK.js} +2 -2
- package/dist/{chunk-JOEZJL4I.js → chunk-NVUBFCJK.js} +2 -2
- package/dist/chunk-SRMGSCC3.js +7 -0
- package/dist/chunk-SRMGSCC3.js.map +1 -0
- package/dist/{chunk-BP27CZ5Q.js → chunk-XGIPATLV.js} +2 -1
- package/dist/chunk-XGIPATLV.js.map +1 -0
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/{server-U7V6DUSY.js → server-LBEYRKM2.js} +1134 -400
- package/dist/server-LBEYRKM2.js.map +1 -0
- package/dist/{site-extract-repository-2MQBAOX5.js → site-extract-repository-4NVO5KQA.js} +3 -3
- package/dist/{worker-RVZXIALX.js → worker-7XRZNLKB.js} +4 -4
- package/package.json +2 -1
- package/dist/chunk-2ULBZRQQ.js +0 -7
- package/dist/chunk-2ULBZRQQ.js.map +0 -1
- package/dist/chunk-6TDUY7CA.js.map +0 -1
- package/dist/chunk-BP27CZ5Q.js.map +0 -1
- package/dist/chunk-QUOZ3LZG.js.map +0 -1
- package/dist/server-U7V6DUSY.js.map +0 -1
- /package/dist/{chunk-NNEIXK5L.js.map → chunk-FUWZWKGO.js.map} +0 -0
- /package/dist/{chunk-HBC4C75S.js.map → chunk-NJK5BTUK.js.map} +0 -0
- /package/dist/{chunk-JOEZJL4I.js.map → chunk-NVUBFCJK.js.map} +0 -0
- /package/dist/{site-extract-repository-2MQBAOX5.js.map → site-extract-repository-4NVO5KQA.js.map} +0 -0
- /package/dist/{worker-RVZXIALX.js.map → worker-7XRZNLKB.js.map} +0 -0
package/dist/bin/paa-harvest.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
harvest
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-NJK5BTUK.js";
|
|
5
5
|
import {
|
|
6
6
|
browserServiceApiKey
|
|
7
7
|
} from "../chunk-HPV4VOQX.js";
|
|
8
8
|
import "../chunk-K443GQY5.js";
|
|
9
|
-
import "../chunk-
|
|
9
|
+
import "../chunk-XGIPATLV.js";
|
|
10
10
|
import "../chunk-M2S27J6Z.js";
|
|
11
11
|
import "../chunk-FUVQR6GK.js";
|
|
12
12
|
|
|
@@ -21,7 +21,9 @@ var MC_COSTS = {
|
|
|
21
21
|
instagram_transcribe: 50,
|
|
22
22
|
browser_minute: 12e3,
|
|
23
23
|
reddit_thread: 2e3,
|
|
24
|
-
video_analysis: 666667
|
|
24
|
+
video_analysis: 666667,
|
|
25
|
+
trustpilot_reviews: 500,
|
|
26
|
+
trustpilot_review: 100
|
|
25
27
|
};
|
|
26
28
|
var MC_PER_BROWSER_MS = MC_COSTS.browser_minute / 6e4;
|
|
27
29
|
function browserActiveCostMc(activeMs) {
|
|
@@ -195,6 +197,22 @@ var CREDIT_COST_CATALOG = [
|
|
|
195
197
|
credits: mcToCredits(MC_COSTS.video_analysis),
|
|
196
198
|
unit: "per 120 frames (max 480)",
|
|
197
199
|
notes: "Full multi-lens video breakdown: samples frames across a video (up to 30 minutes), analyzes each with vision AI, transcribes the audio, then produces summary, pacing/energy, words-per-minute, topic outline, key points, hook analysis, visual style, and a how-to-replicate recipe with a quality-control pass. $1 per 120 frames requested (max 480 = $4); billed down automatically if the video cannot use the requested frames, and refunded fully if the run fails."
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
key: "trustpilot_reviews",
|
|
203
|
+
label: "Trustpilot review harvest",
|
|
204
|
+
aliases: ["trustpilot_reviews", "trustpilot", "trustpilot reviews"],
|
|
205
|
+
credits: mcToCredits(MC_COSTS.trustpilot_reviews),
|
|
206
|
+
unit: "per call",
|
|
207
|
+
notes: "Base lookup. Reviews billed separately per card at trustpilot_review rate. Refunded if no reviews are found."
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
key: "trustpilot_review",
|
|
211
|
+
label: "Trustpilot review card",
|
|
212
|
+
aliases: ["trustpilot_review", "trustpilot card"],
|
|
213
|
+
credits: mcToCredits(MC_COSTS.trustpilot_review),
|
|
214
|
+
unit: "per review card",
|
|
215
|
+
notes: "Charged per review actually extracted, billed down automatically if the balance runs out mid-page."
|
|
198
216
|
}
|
|
199
217
|
];
|
|
200
218
|
var CONCURRENCY_PRICE_ID = "price_1Ta1NRS8aAcsk3TGwsRnYbix";
|
|
@@ -301,7 +319,11 @@ var LedgerOperation = {
|
|
|
301
319
|
VIDEO_ANALYSIS_REFUND: "video_analysis_refund",
|
|
302
320
|
VIDEO_ANALYSIS_ADJUST: "video_analysis_adjust",
|
|
303
321
|
MEMORY_AI: "memory_ai",
|
|
304
|
-
MEMORY_AI_REFUND: "memory_ai_refund"
|
|
322
|
+
MEMORY_AI_REFUND: "memory_ai_refund",
|
|
323
|
+
TRUSTPILOT_REVIEWS: "trustpilot_reviews",
|
|
324
|
+
TRUSTPILOT_REVIEWS_REFUND: "trustpilot_reviews_refund",
|
|
325
|
+
TRUSTPILOT_REVIEW: "trustpilot_review",
|
|
326
|
+
TRUSTPILOT_REVIEW_REFUND: "trustpilot_review_refund"
|
|
305
327
|
};
|
|
306
328
|
var MEMORY_AI_MARGIN_MULTIPLE = 3;
|
|
307
329
|
var MC_PER_USD = SUBSCRIPTION_TIERS["price_1TmiHRS8aAcsk3TGwmSNfNIa"].credits_mc / SUBSCRIPTION_TIERS["price_1TmiHRS8aAcsk3TGwmSNfNIa"].monthly_usd;
|
|
@@ -325,4 +347,4 @@ export {
|
|
|
325
347
|
MEMORY_AI_MARGIN_MULTIPLE,
|
|
326
348
|
MC_PER_USD
|
|
327
349
|
};
|
|
328
|
-
//# sourceMappingURL=chunk-
|
|
350
|
+
//# sourceMappingURL=chunk-4MFYMUIF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/rates.ts"],"sourcesContent":["export const MC_COSTS = {\n serp: 100,\n paa: 200,\n paa_base: 1_000,\n page_scrape: 100,\n url_map: 500,\n yt_channel: 200,\n yt_transcription: 200,\n fb_ad: 200,\n maps_search: 500,\n maps_place: 3_000,\n maps_review: 100,\n fb_search: 600,\n fb_transcribe: 200,\n google_ads_search: 600,\n google_ads_intel: 200,\n google_ads_transcribe: 200,\n instagram_profile: 400,\n instagram_media: 400,\n instagram_transcribe: 50,\n browser_minute: 12_000,\n reddit_thread: 2_000,\n video_analysis: 666_667,\n trustpilot_reviews: 500,\n trustpilot_review: 100,\n} as const\n\nexport type McCostKey = keyof typeof MC_COSTS\n\nexport const MC_PER_BROWSER_MS = MC_COSTS.browser_minute / 60_000\n\nexport function browserActiveCostMc(activeMs: number): number {\n return Math.round(activeMs * MC_PER_BROWSER_MS)\n}\n\nexport const BROWSER_OPEN_MIN_BALANCE_MC = 1_000\n\nexport const MC_PER_CREDIT = 1_000\n\nexport const CREDIT_COST_CATALOG: Array<{\n key: McCostKey\n label: string\n aliases: string[]\n credits: number\n unit: string\n notes?: string\n}> = [\n {\n key: 'serp',\n label: 'SERP search',\n aliases: ['search_serp', 'serp', 'google search', 'organic results'],\n credits: mcToCredits(MC_COSTS.serp),\n unit: 'per search',\n notes: 'Returns AI Overview, PAA snippet, videos, forums, and local pack.',\n },\n {\n key: 'paa',\n label: 'PAA harvest',\n aliases: ['harvest_paa', 'paa', 'people also ask', 'questions'],\n credits: mcToCredits(MC_COSTS.paa),\n unit: `per question (+${mcToCredits(MC_COSTS.paa_base)} credit base)`,\n notes: `Full SERP feature extraction. Billed ${mcToCredits(MC_COSTS.paa_base)} credit base + ${mcToCredits(MC_COSTS.paa)} per question actually returned (unused estimate refunded).`,\n },\n {\n key: 'page_scrape',\n label: 'Page crawl / extract',\n aliases: ['extract_url', 'extract_site', 'page scrape', 'url scrape', 'single page', 'site crawl'],\n credits: mcToCredits(MC_COSTS.page_scrape),\n unit: 'per page',\n notes: 'Applies to both single-URL extraction and per-page site crawls.',\n },\n {\n key: 'url_map',\n label: 'Site URL mapping',\n aliases: ['map_site_urls', 'url map', 'site map', 'crawl urls'],\n credits: mcToCredits(MC_COSTS.url_map),\n unit: 'per mapping operation',\n notes: 'Flat rate for the full /map-urls call regardless of URL count discovered.',\n },\n {\n key: 'yt_channel',\n label: 'YouTube search / channel harvest',\n aliases: ['youtube_harvest', 'youtube search', 'youtube channel', 'yt_channel'],\n credits: mcToCredits(MC_COSTS.yt_channel),\n unit: 'per call',\n },\n {\n key: 'yt_transcription',\n label: 'YouTube transcription',\n aliases: ['youtube_transcribe', 'youtube transcript', 'transcription', 'yt_transcription'],\n credits: mcToCredits(MC_COSTS.yt_transcription),\n unit: 'per minute',\n notes: 'A 5-minute hold is taken, then reconciled to actual video duration.',\n },\n {\n key: 'fb_ad',\n label: 'Facebook search / ad lookup',\n aliases: ['facebook_page_intel', 'facebook_ad_search', 'facebook_ad', 'facebook ads', 'fb ads'],\n credits: mcToCredits(MC_COSTS.fb_ad),\n unit: 'per call',\n },\n {\n key: 'maps_search',\n label: 'Maps business search',\n aliases: ['maps_search', 'google maps search', 'gmb search', 'gbp search', 'business profiles'],\n credits: mcToCredits(MC_COSTS.maps_search),\n unit: 'per search',\n notes: 'Returns up to 50 Google Maps business/profile candidates. Use maps_place_intel to hydrate selected businesses.',\n },\n {\n key: 'maps_place',\n label: 'Maps business lookup',\n aliases: ['maps_place_intel', 'google maps', 'maps place', 'place intel'],\n credits: mcToCredits(MC_COSTS.maps_place),\n unit: 'per business',\n notes: 'Base lookup. Reviews billed separately per card at maps_review rate.',\n },\n {\n key: 'maps_review',\n label: 'Maps review',\n aliases: ['maps_reviews', 'google reviews', 'review cards', 'reviews'],\n credits: mcToCredits(MC_COSTS.maps_review),\n unit: 'per review card',\n notes: 'Charged after extraction when includeReviews is true.',\n },\n {\n key: 'fb_search',\n label: 'Facebook ad library search',\n aliases: ['facebook_search', 'fb_search', 'fb ad search'],\n credits: mcToCredits(MC_COSTS.fb_search),\n unit: 'per search',\n notes: 'Browser automation to search Facebook Ads Library by keyword.',\n },\n {\n key: 'google_ads_search',\n label: 'Google Ads Transparency search',\n aliases: ['google_ads_search', 'google ads search', 'ads transparency search'],\n credits: mcToCredits(MC_COSTS.google_ads_search),\n unit: 'per search',\n notes: 'Browser automation to find advertisers in Google Ads Transparency Center by domain or name.',\n },\n {\n key: 'google_ads_intel',\n label: 'Google Ads Transparency advertiser intel',\n aliases: ['google_ads_page_intel', 'google ads intel', 'ads transparency intel'],\n credits: mcToCredits(MC_COSTS.google_ads_intel),\n unit: 'per call',\n notes: 'Lists and hydrates an advertiser\\'s creatives with image URLs and video references.',\n },\n {\n key: 'google_ads_transcribe',\n label: 'Google ad video transcription',\n aliases: ['google_ads_transcribe', 'google ad transcript'],\n credits: mcToCredits(MC_COSTS.google_ads_transcribe),\n unit: 'per minute',\n notes: 'A hold is taken, then reconciled to actual video duration.',\n },\n {\n key: 'fb_transcribe',\n label: 'Facebook video / ad transcription',\n aliases: ['facebook_transcribe', 'facebook_video_transcribe', 'fb_transcribe', 'fb ad transcript'],\n credits: mcToCredits(MC_COSTS.fb_transcribe),\n unit: 'per minute',\n notes: 'A hold is taken, then reconciled to actual video duration.',\n },\n {\n key: 'instagram_profile',\n label: 'Instagram profile content discovery',\n aliases: ['instagram_profile_content', 'instagram profile', 'instagram content list', 'ig profile'],\n credits: mcToCredits(MC_COSTS.instagram_profile),\n unit: 'per profile scan',\n notes: 'Browser extraction of public Instagram profile grid links. Complete history may require a logged-in profile.',\n },\n {\n key: 'instagram_media',\n label: 'Instagram media download',\n aliases: ['instagram_media_download', 'instagram reel download', 'instagram post download', 'ig media'],\n credits: mcToCredits(MC_COSTS.instagram_media),\n unit: 'per post or reel',\n notes: 'Extracts post text, image URLs, and reel audio/video tracks, with local downloads when the server can write files.',\n },\n {\n key: 'instagram_transcribe',\n label: 'Instagram media transcription',\n aliases: ['instagram transcript', 'instagram reel transcript', 'ig transcribe'],\n credits: mcToCredits(MC_COSTS.instagram_transcribe),\n unit: 'per call',\n notes: 'Whisper transcription of selected Instagram audio/video media via fal.ai.',\n },\n {\n key: 'browser_minute',\n label: 'Interactive browser session',\n aliases: ['browser_open', 'browser agent', 'browser_agent', 'live browser', 'browse', 'browser control', 'interactive browser'],\n credits: mcToCredits(MC_COSTS.browser_minute),\n unit: 'per minute of use',\n notes: 'Metered per minute of use for the whole time the browser session is open. Close the session to stop the meter; abandoned sessions are auto-closed after a short idle window.',\n },\n {\n key: 'reddit_thread',\n label: 'Reddit thread + comments',\n aliases: ['reddit', 'reddit_thread', 'reddit comments', 'subreddit', 'reddit post'],\n credits: mcToCredits(MC_COSTS.reddit_thread),\n unit: 'per thread',\n notes: 'Captures a Reddit post and its comment tree. Charged on success; refunded if the thread cannot be retrieved.',\n },\n {\n key: 'video_analysis',\n label: 'Video breakdown (frame-by-frame + transcript)',\n aliases: ['video_frame_analysis', 'video breakdown', 'video analysis', 'analyze video'],\n credits: mcToCredits(MC_COSTS.video_analysis),\n unit: 'per 120 frames (max 480)',\n notes: 'Full multi-lens video breakdown: samples frames across a video (up to 30 minutes), analyzes each with vision AI, transcribes the audio, then produces summary, pacing/energy, words-per-minute, topic outline, key points, hook analysis, visual style, and a how-to-replicate recipe with a quality-control pass. $1 per 120 frames requested (max 480 = $4); billed down automatically if the video cannot use the requested frames, and refunded fully if the run fails.',\n },\n {\n key: 'trustpilot_reviews',\n label: 'Trustpilot review harvest',\n aliases: ['trustpilot_reviews', 'trustpilot', 'trustpilot reviews'],\n credits: mcToCredits(MC_COSTS.trustpilot_reviews),\n unit: 'per call',\n notes: 'Base lookup. Reviews billed separately per card at trustpilot_review rate. Refunded if no reviews are found.',\n },\n {\n key: 'trustpilot_review',\n label: 'Trustpilot review card',\n aliases: ['trustpilot_review', 'trustpilot card'],\n credits: mcToCredits(MC_COSTS.trustpilot_review),\n unit: 'per review card',\n notes: 'Charged per review actually extracted, billed down automatically if the balance runs out mid-page.',\n },\n]\n\nexport const CONCURRENCY_PRICE_ID = 'price_1Ta1NRS8aAcsk3TGwsRnYbix'\n\nexport interface SubscriptionTier { tier: string; label: string; price_id: string; monthly_usd: number; credits_mc: number; concurrency: number; intro_coupon: string | null }\n\nexport const SUBSCRIPTION_TIERS: Record<string, SubscriptionTier> = {\n 'price_1TmiHRS8aAcsk3TGwmSNfNIa': { tier: 'starter', label: 'Starter', price_id: 'price_1TmiHRS8aAcsk3TGwmSNfNIa', monthly_usd: 12, credits_mc: 8_000_000, concurrency: 3, intro_coupon: 'mcp-starter-1dollar-intro-12' },\n 'price_1Tmg1nS8aAcsk3TGe8zcnGTM': { tier: 'growth', label: 'Growth', price_id: 'price_1Tmg1nS8aAcsk3TGe8zcnGTM', monthly_usd: 100, credits_mc: 80_000_000, concurrency: 10, intro_coupon: null },\n 'price_1Tmg1nS8aAcsk3TGsZw34iXS': { tier: 'scale', label: 'Scale', price_id: 'price_1Tmg1nS8aAcsk3TGsZw34iXS', monthly_usd: 250, credits_mc: 240_000_000, concurrency: 20, intro_coupon: null },\n}\n\nexport const SUBSCRIPTION_TIER_BY_KEY: Record<string, SubscriptionTier> = Object.fromEntries(\n Object.values(SUBSCRIPTION_TIERS).map(t => [t.tier, t]),\n)\nexport const CONCURRENCY_SLOT_PRICE_USD = 5\nexport const CONCURRENCY_SLOT_PRICE_CURRENCY = 'usd'\nexport const CONCURRENCY_SLOT_PRICE_INTERVAL = 'month'\nexport const CONCURRENCY_SLOT_PRICE_LABEL = '$5/month'\nexport const CONCURRENCY_SLOT_TERMINAL_COMMAND = 'npx -y -p mcp-scraper@latest mcp-scraper-cli billing concurrency checkout'\n\nexport function concurrencySlotBillingInfo() {\n const billingUrl = process.env.CONCURRENCY_BILLING_URL ?? 'https://mcpscraper.dev/billing'\n return {\n product: 'Extra concurrency slot',\n price_label: CONCURRENCY_SLOT_PRICE_LABEL,\n unit_amount_usd: CONCURRENCY_SLOT_PRICE_USD,\n currency: CONCURRENCY_SLOT_PRICE_CURRENCY,\n interval: CONCURRENCY_SLOT_PRICE_INTERVAL,\n billing_url: billingUrl,\n terminal_command: CONCURRENCY_SLOT_TERMINAL_COMMAND,\n terminal_command_with_api_key_env: `MCP_SCRAPER_API_KEY=sk_live_your_key ${CONCURRENCY_SLOT_TERMINAL_COMMAND}`,\n }\n}\n\nexport const FREE_SIGNUP_MC = 100_000\nexport const FREE_MONTHLY_REFRESH_MC = 250_000\n\nexport const BALANCE_PRICE_IDS: Record<string, number> = {\n 'price_1TZx6rS8aAcsk3TGNMc1Vgpo': 11_000_000,\n 'price_1TZx6sS8aAcsk3TGxgqB7khO': 27_500_000,\n 'price_1TZx6tS8aAcsk3TG8PnJqHlG': 60_500_000,\n 'price_1TZx6tS8aAcsk3TGNgRMpy0e': 121_000_000,\n}\n\nexport const BALANCE_PACK_LABELS: Record<string, string> = {\n 'price_1TZx6rS8aAcsk3TGNMc1Vgpo': '$10',\n 'price_1TZx6sS8aAcsk3TGxgqB7khO': '$25',\n 'price_1TZx6tS8aAcsk3TG8PnJqHlG': '$50',\n 'price_1TZx6tS8aAcsk3TGNgRMpy0e': '$100',\n}\n\nexport function mcToCredits(mc: number): number {\n return mc / MC_PER_CREDIT\n}\n\nexport function insufficientBalanceResponse(balanceMc: number, requiredMc: number) {\n const topupUrl = process.env.TOPUP_URL ?? 'https://mcpscraper.dev/billing'\n const balanceCredits = mcToCredits(balanceMc)\n const requiredCredits = mcToCredits(requiredMc)\n return {\n error: 'insufficient_balance',\n error_code: 'insufficient_balance' as const,\n message: `Insufficient credits. Balance: ${balanceCredits} credits. This call requires ${requiredCredits} credits. Top up at ${topupUrl}`,\n balance_credits: balanceCredits,\n required_credits: requiredCredits,\n topup_url: topupUrl,\n }\n}\n\nexport interface MemoryPlan {\n plan: 'pro' | 'team'\n label: string\n price_id: string\n interval: 'month' | 'year'\n monthly_usd: number\n quota_bytes: number\n}\n\nexport const MEMORY_FREE_QUOTA_BYTES = 0.01e9 * 1\n\nexport const MEMORY_PLANS: Record<string, MemoryPlan> = {\n 'price_1TnMSTS8aAcsk3TGBgiwuvqL': { plan: 'pro', label: 'Pro', price_id: 'price_1TnMSTS8aAcsk3TGBgiwuvqL', interval: 'month', monthly_usd: 19, quota_bytes: 5e9 },\n 'price_1TnMSTS8aAcsk3TGWBVU2agY': { plan: 'pro', label: 'Pro', price_id: 'price_1TnMSTS8aAcsk3TGWBVU2agY', interval: 'year', monthly_usd: 19, quota_bytes: 5e9 },\n}\n\nexport const MEMORY_PLAN_QUOTA: Record<string, number> = { free: MEMORY_FREE_QUOTA_BYTES, pro: 5e9, team: 50e9 }\n\nexport const MEMORY_MARGIN_MULTIPLE = 3\nexport const MEMORY_FREE_COST_CAP_USD = 1.0\nexport const MEMORY_PRO_COST_BUDGET_USD = 19 / MEMORY_MARGIN_MULTIPLE\n\nexport interface SchedulingPlan {\n price_id: string\n label: string\n interval: 'month'\n monthly_usd: number\n quota_per_period: number\n}\n\nexport const SCHEDULING_PLANS: Record<string, SchedulingPlan> = {\n 'price_1ToXcHS8aAcsk3TGf3cW7zHx': { price_id: 'price_1ToXcHS8aAcsk3TGf3cW7zHx', label: 'Scheduled Actions', interval: 'month', monthly_usd: 10, quota_per_period: 1000 },\n}\n\nexport const LedgerOperation = {\n TOPUP: 'topup',\n SUBSCRIPTION: 'subscription',\n SIGNUP_GRANT: 'signup_grant',\n MONTHLY_REFRESH: 'monthly_free_refresh',\n PAA: 'paa',\n PAA_REFUND: 'paa_refund',\n SERP: 'serp',\n REFUND: 'refund',\n TRANSCRIPTION: 'transcription',\n TRANSCRIPTION_HOLD: 'transcription_hold',\n TRANSCRIPTION_REFUND: 'transcription_refund',\n YT_CHANNEL: 'yt_channel',\n FB_AD: 'fb_ad',\n MAPS_SEARCH: 'maps_search',\n MAPS_PLACE: 'maps_place',\n MAPS_REVIEW: 'maps_review',\n MAPS_REVIEW_REFUND: 'maps_review_refund',\n EXTRACT_SITE: 'extract_site',\n EXTRACT_SITE_REFUND: 'extract_site_refund',\n EXTRACT_URL: 'page_scrape',\n URL_MAP: 'url_map',\n EXTRACT_SITE_HOLD: 'extract_site_hold',\n YT_CHANNEL_REFUND: 'yt_channel_refund',\n FB_AD_REFUND: 'fb_ad_refund',\n URL_MAP_REFUND: 'url_map_refund',\n FB_SEARCH: 'fb_search',\n FB_TRANSCRIBE: 'fb_transcribe',\n FB_SEARCH_REFUND: 'fb_search_refund',\n FB_TRANSCRIBE_REFUND: 'fb_transcribe_refund',\n GOOGLE_ADS_SEARCH: 'google_ads_search',\n GOOGLE_ADS_SEARCH_REFUND: 'google_ads_search_refund',\n GOOGLE_ADS_INTEL: 'google_ads_intel',\n GOOGLE_ADS_INTEL_REFUND: 'google_ads_intel_refund',\n GOOGLE_ADS_TRANSCRIBE: 'google_ads_transcribe',\n GOOGLE_ADS_TRANSCRIBE_REFUND: 'google_ads_transcribe_refund',\n INSTAGRAM_PROFILE: 'instagram_profile',\n INSTAGRAM_PROFILE_REFUND: 'instagram_profile_refund',\n INSTAGRAM_MEDIA: 'instagram_media',\n INSTAGRAM_MEDIA_REFUND:'instagram_media_refund',\n INSTAGRAM_TRANSCRIBE: 'instagram_transcribe',\n INSTAGRAM_TRANSCRIBE_REFUND: 'instagram_transcribe_refund',\n BROWSER_SESSION: 'browser_session',\n REDDIT_THREAD: 'reddit_thread',\n REDDIT_THREAD_REFUND: 'reddit_thread_refund',\n VIDEO_ANALYSIS: 'video_analysis',\n VIDEO_ANALYSIS_REFUND: 'video_analysis_refund',\n VIDEO_ANALYSIS_ADJUST: 'video_analysis_adjust',\n MEMORY_AI: 'memory_ai',\n MEMORY_AI_REFUND: 'memory_ai_refund',\n TRUSTPILOT_REVIEWS: 'trustpilot_reviews',\n TRUSTPILOT_REVIEWS_REFUND: 'trustpilot_reviews_refund',\n TRUSTPILOT_REVIEW: 'trustpilot_review',\n TRUSTPILOT_REVIEW_REFUND: 'trustpilot_review_refund',\n} as const\n\nexport type LedgerOperation = typeof LedgerOperation[keyof typeof LedgerOperation]\n\nexport const MEMORY_AI_MARGIN_MULTIPLE = 3\n\nexport const MC_PER_USD =\n SUBSCRIPTION_TIERS['price_1TmiHRS8aAcsk3TGwmSNfNIa'].credits_mc /\n SUBSCRIPTION_TIERS['price_1TmiHRS8aAcsk3TGwmSNfNIa'].monthly_usd\n"],"mappings":"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","names":[]}
|
|
@@ -14,10 +14,10 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
DEFAULT_MAPS_PROXY_MODE,
|
|
16
16
|
DEFAULT_PROXY_MODE
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-XGIPATLV.js";
|
|
18
18
|
import {
|
|
19
19
|
PACKAGE_VERSION
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-SRMGSCC3.js";
|
|
21
21
|
import {
|
|
22
22
|
sanitizeVendorName
|
|
23
23
|
} from "./chunk-M2S27J6Z.js";
|
|
@@ -316,6 +316,17 @@ var WORKFLOW_RECIPES = [
|
|
|
316
316
|
produces: ["content gaps", "page comparison CSV", "PAA questions CSV", "section map", "writer brief"],
|
|
317
317
|
runHint: "Use workflow_run with workflowId serp-comparison for competitor gaps or paa-expansion-brief when the user mainly wants a section map from PAA data."
|
|
318
318
|
},
|
|
319
|
+
{
|
|
320
|
+
id: "recurring_tracking_and_memory",
|
|
321
|
+
title: "Recurring Tracking & Memory",
|
|
322
|
+
description: "Run a check on a daily, weekly, or custom cadence with no external cron needed, and save each result somewhere searchable so you can compare against past runs later - rankings, prices, competitor changes, anything you would otherwise check by hand and forget to log.",
|
|
323
|
+
primaryWorkflowId: null,
|
|
324
|
+
recommendedTools: ["create-scheduled-action", "search_serp", "table-create", "table-insert-rows", "table-query", "memory-put", "memory-search", "list-scheduled-actions"],
|
|
325
|
+
requiredInputs: ["plain-language description of what to check and how often"],
|
|
326
|
+
optionalInputs: ["vault to save into", "timeOfDay", "timezone"],
|
|
327
|
+
produces: ["a live scheduled action that runs automatically on cadence", "a table row or note per run for structured history", "a searchable trend you can compare run-over-run"],
|
|
328
|
+
runHint: "Use create-scheduled-action with a plain description and cadence; the run itself checks list-vaults, uses table-insert-rows for repeating structured data (e.g. a keyword rank check) or memory-put for a one-off note, and reads prior runs with table-query or memory-search before writing the new one. For a one-time blueprint to build your own external cron/DB instead of a live scheduled action, use rank_tracker_workflow."
|
|
329
|
+
},
|
|
319
330
|
{
|
|
320
331
|
id: "ai_search_visibility_audit",
|
|
321
332
|
title: "AI Search Visibility Audit",
|
|
@@ -2026,6 +2037,51 @@ ${rows}`,
|
|
|
2026
2037
|
}
|
|
2027
2038
|
};
|
|
2028
2039
|
}
|
|
2040
|
+
function formatTrustpilotReviews(raw, input) {
|
|
2041
|
+
const parsed = parseData(raw);
|
|
2042
|
+
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
2043
|
+
const d = parsed.data;
|
|
2044
|
+
const domain = d.domain ?? input.domain;
|
|
2045
|
+
const reviewUrl = d.reviewUrl;
|
|
2046
|
+
const extractedAt = d.extractedAt;
|
|
2047
|
+
const requestedMaxPages = d.requestedMaxPages ?? input.maxPages ?? 5;
|
|
2048
|
+
const pagesFetched = d.pagesFetched;
|
|
2049
|
+
const reviews = d.reviews ?? [];
|
|
2050
|
+
const durationMs = d.durationMs;
|
|
2051
|
+
const rows = reviews.map((r) => {
|
|
2052
|
+
const stars = r.rating != null ? "\u2605".repeat(r.rating) + "\u2606".repeat(r.ratingScale - r.rating) : "\u2014";
|
|
2053
|
+
const answer = r.body[0]?.answer ?? "";
|
|
2054
|
+
return `| ${cell(r.reviewer.name ?? "Anonymous")} | ${stars} | ${cell(r.title)} | ${cell(r.date ? r.date.slice(0, 10) : null)} | ${r.flags.origin ?? "\u2014"} | ${r.flags.companyReplied ? "yes" : "no"} | ${cell(truncate(answer, 100))} |`;
|
|
2055
|
+
}).join("\n");
|
|
2056
|
+
const full = [
|
|
2057
|
+
`# Trustpilot Reviews: ${domain}`,
|
|
2058
|
+
`**Collected:** ${reviews.length} review${reviews.length === 1 ? "" : "s"} across ${pagesFetched ?? 0}/${requestedMaxPages} page${requestedMaxPages === 1 ? "" : "s"}`,
|
|
2059
|
+
reviewUrl ? `**Source:** ${reviewUrl}` : null,
|
|
2060
|
+
`
|
|
2061
|
+
## Reviews
|
|
2062
|
+
| Reviewer | Rating | Title | Date | Origin | Company Replied | Excerpt |
|
|
2063
|
+
|----------|--------|-------|------|--------|------------------|---------|
|
|
2064
|
+
${rows}`,
|
|
2065
|
+
`
|
|
2066
|
+
---
|
|
2067
|
+
\u{1F4A1} **Note:** sampling tool, default 5 pages (~100 reviews), max 50. For full-corpus export use Trustpilot's official Business API.`,
|
|
2068
|
+
durationMs != null ? `
|
|
2069
|
+
*Extracted in ${(durationMs / 1e3).toFixed(1)}s*` : null
|
|
2070
|
+
].filter(Boolean).join("\n");
|
|
2071
|
+
return {
|
|
2072
|
+
...oneBlock(full),
|
|
2073
|
+
structuredContent: {
|
|
2074
|
+
domain,
|
|
2075
|
+
reviewUrl: reviewUrl ?? null,
|
|
2076
|
+
extractedAt: extractedAt ?? null,
|
|
2077
|
+
requestedMaxPages,
|
|
2078
|
+
pagesFetched: pagesFetched ?? 0,
|
|
2079
|
+
reviewCount: reviews.length,
|
|
2080
|
+
reviews,
|
|
2081
|
+
durationMs: durationMs ?? 0
|
|
2082
|
+
}
|
|
2083
|
+
};
|
|
2084
|
+
}
|
|
2029
2085
|
async function formatDirectoryWorkflow(raw, input, ctx) {
|
|
2030
2086
|
const parsed = parseData(raw);
|
|
2031
2087
|
if ("error" in parsed) return { content: [{ type: "text", text: parsed.error }], isError: true };
|
|
@@ -2127,6 +2183,9 @@ function formatMapsPlaceIntel(raw, input) {
|
|
|
2127
2183
|
const about = d.aboutAttributes ?? [];
|
|
2128
2184
|
const reviews = d.reviews ?? [];
|
|
2129
2185
|
const reviewsStatus = d.reviewsStatus ?? "not_requested";
|
|
2186
|
+
const services = d.services ?? [];
|
|
2187
|
+
const areasServed = d.areasServed ?? [];
|
|
2188
|
+
const servicesStatus = d.servicesStatus ?? "not_requested";
|
|
2130
2189
|
const hoursTable = d.hoursTable ?? [];
|
|
2131
2190
|
const ratingLine = [rating, reviewCount ? `(${reviewCount} reviews)` : null].filter(Boolean).join(" ");
|
|
2132
2191
|
const basicLines = [
|
|
@@ -2181,6 +2240,20 @@ ${reviews.map((r, i) => {
|
|
|
2181
2240
|
${r.text ?? ""}`;
|
|
2182
2241
|
}).join("\n\n")}`;
|
|
2183
2242
|
})();
|
|
2243
|
+
const servicesSection = (() => {
|
|
2244
|
+
if (servicesStatus === "not_requested") return "";
|
|
2245
|
+
if (servicesStatus === "unavailable") return "\n## Services & Areas Served\n> Could not be retrieved this run \u2014 retry with `includeServices: true`.";
|
|
2246
|
+
if (servicesStatus === "none_exist") return "\n## Services & Areas Served\n*Not listed on this business's profile.*";
|
|
2247
|
+
const parts = [
|
|
2248
|
+
services.length ? `**Services**
|
|
2249
|
+
${services.map((s) => `- ${s}`).join("\n")}` : null,
|
|
2250
|
+
areasServed.length ? `**Areas Served**
|
|
2251
|
+
${areasServed.map((a) => `- ${a}`).join("\n")}` : null
|
|
2252
|
+
].filter(Boolean);
|
|
2253
|
+
return parts.length ? `
|
|
2254
|
+
## Services & Areas Served
|
|
2255
|
+
${parts.join("\n\n")}` : "";
|
|
2256
|
+
})();
|
|
2184
2257
|
const full = [
|
|
2185
2258
|
`# ${name}`,
|
|
2186
2259
|
category ? `*${category}*` : null,
|
|
@@ -2196,6 +2269,7 @@ ${basicLines}` : null,
|
|
|
2196
2269
|
## Entity IDs
|
|
2197
2270
|
${entitySection}` : null,
|
|
2198
2271
|
reviewsSection,
|
|
2272
|
+
servicesSection,
|
|
2199
2273
|
durationMs != null ? `
|
|
2200
2274
|
---
|
|
2201
2275
|
*Extracted in ${(durationMs / 1e3).toFixed(1)}s*` : null
|
|
@@ -2219,7 +2293,10 @@ ${entitySection}` : null,
|
|
|
2219
2293
|
lng: lng ?? null,
|
|
2220
2294
|
reviewsStatus,
|
|
2221
2295
|
reviewsCollected: reviews.length,
|
|
2222
|
-
reviewTopics: topics.map((t) => ({ label: String(t.label ?? ""), count: String(t.count ?? "") }))
|
|
2296
|
+
reviewTopics: topics.map((t) => ({ label: String(t.label ?? ""), count: String(t.count ?? "") })),
|
|
2297
|
+
services,
|
|
2298
|
+
areasServed,
|
|
2299
|
+
servicesStatus
|
|
2223
2300
|
}
|
|
2224
2301
|
};
|
|
2225
2302
|
}
|
|
@@ -2753,8 +2830,37 @@ var MapsPlaceIntelInputSchema = {
|
|
|
2753
2830
|
gl: z.string().length(2).default("us").describe("Google country code inferred from location."),
|
|
2754
2831
|
hl: z.string().length(2).default("en").describe("Language inferred from user request."),
|
|
2755
2832
|
includeReviews: z.boolean().default(false).describe("Fetch individual review cards \u2014 for reviews, customer pain, complaints, or praise themes."),
|
|
2756
|
-
maxReviews: z.number().int().min(1).max(500).default(50).describe("Max review cards when includeReviews is true. Default 50, maximum 500.")
|
|
2757
|
-
|
|
2833
|
+
maxReviews: z.number().int().min(1).max(500).default(50).describe("Max review cards when includeReviews is true. Default 50, maximum 500."),
|
|
2834
|
+
includeServices: z.boolean().default(false).describe("Fetch the business's configured services list and areas-served list, when the profile has them. Adds one extra page visit; not present for every business.")
|
|
2835
|
+
};
|
|
2836
|
+
var TrustpilotReviewsInputSchema = {
|
|
2837
|
+
domain: z.string().min(1).describe(`The business's domain as it appears in its Trustpilot URL, e.g. "www.bhphotovideo.com" (include the www. if the site uses it \u2014 pass the domain as-is, do not guess).`),
|
|
2838
|
+
maxPages: z.number().int().min(1).max(50).default(5).describe("Review pages to fetch (~20 reviews per page). Default 5 (~100 reviews). Maximum 50 \u2014 large companies can have 1,000+ pages; this tool is for sampling, not full-corpus export.")
|
|
2839
|
+
};
|
|
2840
|
+
var ReviewCardSchema = z.object({
|
|
2841
|
+
source: z.enum(["trustpilot", "g2"]),
|
|
2842
|
+
sourceReviewId: z.string().nullable(),
|
|
2843
|
+
reviewUrl: z.string().nullable(),
|
|
2844
|
+
reviewer: z.object({
|
|
2845
|
+
name: z.string().nullable(),
|
|
2846
|
+
profileId: z.string().nullable(),
|
|
2847
|
+
title: z.string().nullable(),
|
|
2848
|
+
companySegment: z.string().nullable()
|
|
2849
|
+
}),
|
|
2850
|
+
rating: z.number().nullable(),
|
|
2851
|
+
ratingScale: z.number(),
|
|
2852
|
+
title: z.string().nullable(),
|
|
2853
|
+
date: z.string().nullable(),
|
|
2854
|
+
body: z.array(z.object({ question: z.string().nullable(), answer: z.string() })),
|
|
2855
|
+
truncated: z.boolean(),
|
|
2856
|
+
flags: z.object({
|
|
2857
|
+
origin: z.string().nullable(),
|
|
2858
|
+
incentivized: z.boolean().nullable(),
|
|
2859
|
+
validated: z.boolean().nullable(),
|
|
2860
|
+
currentUser: z.boolean().nullable(),
|
|
2861
|
+
companyReplied: z.boolean().nullable()
|
|
2862
|
+
})
|
|
2863
|
+
});
|
|
2758
2864
|
var MapsSearchInputSchema = {
|
|
2759
2865
|
query: z.string().min(1).describe('Business category, niche, or search term, e.g. "roofers". Do not include location here \u2014 use location instead.'),
|
|
2760
2866
|
location: z.string().optional().describe('City, region, country, or service area, e.g. "Denver, CO".'),
|
|
@@ -3066,7 +3172,20 @@ var MapsPlaceIntelOutputSchema = {
|
|
|
3066
3172
|
reviewTopics: z.array(z.object({
|
|
3067
3173
|
label: z.string(),
|
|
3068
3174
|
count: z.string()
|
|
3069
|
-
}))
|
|
3175
|
+
})),
|
|
3176
|
+
services: z.array(z.string()),
|
|
3177
|
+
areasServed: z.array(z.string()),
|
|
3178
|
+
servicesStatus: z.string()
|
|
3179
|
+
};
|
|
3180
|
+
var TrustpilotReviewsOutputSchema = {
|
|
3181
|
+
domain: z.string(),
|
|
3182
|
+
reviewUrl: z.string(),
|
|
3183
|
+
extractedAt: z.string(),
|
|
3184
|
+
requestedMaxPages: z.number().int(),
|
|
3185
|
+
pagesFetched: z.number().int(),
|
|
3186
|
+
reviewCount: z.number().int(),
|
|
3187
|
+
reviews: z.array(ReviewCardSchema),
|
|
3188
|
+
durationMs: z.number()
|
|
3070
3189
|
};
|
|
3071
3190
|
var CreditsInfoOutputSchema = {
|
|
3072
3191
|
balanceCredits: z.number().nullable(),
|
|
@@ -4127,6 +4246,13 @@ function registerPaaExtractorMcpTools(server, executor, options = {}) {
|
|
|
4127
4246
|
outputSchema: recordOutputSchema("maps_search", MapsSearchOutputSchema),
|
|
4128
4247
|
annotations: liveWebToolAnnotations("Google Maps Business Search")
|
|
4129
4248
|
}, async (input) => formatMapsSearch(await executor.mapsSearch(input), input));
|
|
4249
|
+
server.registerTool("trustpilot_reviews", {
|
|
4250
|
+
title: "Trustpilot Review Harvest",
|
|
4251
|
+
description: "Extract customer reviews for a business from Trustpilot \u2014 reviewer, rating, title, body, date, invited/organic origin, company-reply flag. Sampling tool, not a full-corpus export: default 5 pages (~100 reviews), max 50 pages. For bulk/complete extraction across thousands of pages, use Trustpilot's official Business API instead.",
|
|
4252
|
+
inputSchema: TrustpilotReviewsInputSchema,
|
|
4253
|
+
outputSchema: recordOutputSchema("trustpilot_reviews", TrustpilotReviewsOutputSchema),
|
|
4254
|
+
annotations: liveWebToolAnnotations("Trustpilot Review Harvest")
|
|
4255
|
+
}, async (input) => formatTrustpilotReviews(await executor.trustpilotReviews(input), input));
|
|
4130
4256
|
server.registerTool("directory_workflow", {
|
|
4131
4257
|
title: "Directory Workflow: Markets + Maps",
|
|
4132
4258
|
description: `Build directory/prospecting datasets: selects US city markets from Census population data, optionally joins configured ZIP groups, then runs Google Maps business searches per city in parallel. Use for "all cities over 100k population in a state" or market+Maps workflows. ${fileBehavior("Saves a CSV of results per city.", "Large results are stored as a retrievable artifact \u2014 you get an inline summary plus an artifactId for report_artifact_read.")}`,
|
|
@@ -4458,6 +4584,9 @@ var HttpMcpToolExecutor = class {
|
|
|
4458
4584
|
mapsSearch(input) {
|
|
4459
4585
|
return this.call("/maps/search", input);
|
|
4460
4586
|
}
|
|
4587
|
+
trustpilotReviews(input) {
|
|
4588
|
+
return this.call("/trustpilot/reviews", input, this.httpTimeoutOverrideMs ?? 3e5);
|
|
4589
|
+
}
|
|
4461
4590
|
directoryWorkflow(input) {
|
|
4462
4591
|
const cityCount = typeof input.maxCities === "number" ? input.maxCities : 25;
|
|
4463
4592
|
const concurrency = typeof input.concurrency === "number" && input.concurrency > 0 ? input.concurrency : 5;
|
|
@@ -4854,7 +4983,7 @@ var BrowserProfileLogin = z2.object({
|
|
|
4854
4983
|
var BrowserProfileConnectOutputSchema = {
|
|
4855
4984
|
ok: z2.boolean(),
|
|
4856
4985
|
tool: z2.literal("browser_profile_connect"),
|
|
4857
|
-
session_id: NullableString2,
|
|
4986
|
+
session_id: NullableString2.describe("The underlying live browser session id backing this sign-in."),
|
|
4858
4987
|
auth_connection_id: z2.string(),
|
|
4859
4988
|
watch_url: z2.string().describe("mcpscraper.dev sign-in link to give the user so they can complete this login."),
|
|
4860
4989
|
live_view_url: NullableString2.describe("Deprecated; always null. Open watch_url to view the sign-in."),
|
|
@@ -4864,10 +4993,10 @@ var BrowserProfileConnectOutputSchema = {
|
|
|
4864
4993
|
account_email: NullableString2,
|
|
4865
4994
|
note: NullableString2,
|
|
4866
4995
|
status: z2.string(),
|
|
4867
|
-
flow_status: NullableString2,
|
|
4868
|
-
flow_step: NullableString2,
|
|
4869
|
-
flow_expires_at: NullableString2,
|
|
4870
|
-
post_login_url: NullableString2,
|
|
4996
|
+
flow_status: NullableString2.describe("Deprecated; always null."),
|
|
4997
|
+
flow_step: NullableString2.describe("Deprecated; always null."),
|
|
4998
|
+
flow_expires_at: NullableString2.describe("Deprecated; always null."),
|
|
4999
|
+
post_login_url: NullableString2.describe("Deprecated; always null."),
|
|
4871
5000
|
connected_logins: z2.array(BrowserProfileLogin).describe("Every login currently saved in this profile, so you can see what else is connected."),
|
|
4872
5001
|
next_steps: z2.array(z2.string()),
|
|
4873
5002
|
raw: BrowserRawObject.optional()
|
|
@@ -5292,6 +5421,11 @@ function slugPart(value) {
|
|
|
5292
5421
|
if (!trimmed) return null;
|
|
5293
5422
|
return trimmed.replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "").replace(/[^a-z0-9._-]+/g, "-").replace(/[._-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
5294
5423
|
}
|
|
5424
|
+
function normalizeDomain2(value) {
|
|
5425
|
+
const trimmed = value?.trim().toLowerCase();
|
|
5426
|
+
if (!trimmed) return null;
|
|
5427
|
+
return trimmed.replace(/^https?:\/\//, "").replace(/^www\./, "").replace(/\/.*$/, "").replace(/[^a-z0-9.-]+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
|
|
5428
|
+
}
|
|
5295
5429
|
function savedProfileNameFromEmail(email) {
|
|
5296
5430
|
const value = slugPart(email);
|
|
5297
5431
|
if (!value) return null;
|
|
@@ -5401,13 +5535,13 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
5401
5535
|
"browser_profile_connect",
|
|
5402
5536
|
{
|
|
5403
5537
|
title: "Save a Site Login to a Profile",
|
|
5404
|
-
description:
|
|
5538
|
+
description: 'Open a live hosted browser session so the user can sign into a site (ChatGPT, Claude, Reddit, any account-gated site) directly in their own browser tab, then save the login to a named profile. Returns a watch_url \u2014 give it to the user; they sign in fresh on the real site (existing browser cookies are NOT imported), then click "Done" on that page to save the session and close it. ONE profile holds MANY logins \u2014 call again with the same profile and a different domain to stack another account. NOT for one-off scraping (use extract_url) or driving the browser (use browser_open). Billed at the standard live-browser rate while the sign-in session stays open. After the user clicks Done, poll browser_profile_list until AUTHENTICATED, then browser_open with the profile.',
|
|
5405
5539
|
inputSchema: BrowserProfileConnectInputSchema,
|
|
5406
5540
|
outputSchema: recordOutputSchema("browser_profile_connect", BrowserProfileConnectOutputSchema),
|
|
5407
5541
|
annotations: annotations("Save a Site Login to a Profile")
|
|
5408
5542
|
},
|
|
5409
5543
|
async (input) => {
|
|
5410
|
-
const domain =
|
|
5544
|
+
const domain = normalizeDomain2(input.domain) || "chatgpt.com";
|
|
5411
5545
|
const setupUrl = input.login_url ?? input.url ?? (domain === "chatgpt.com" ? "https://chatgpt.com/" : `https://${domain}/`);
|
|
5412
5546
|
const profile = input.profile?.trim() || savedProfileNameFromEmail(input.email) || browserServiceProfileName();
|
|
5413
5547
|
if (!profile) {
|
|
@@ -5423,14 +5557,15 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
5423
5557
|
domain,
|
|
5424
5558
|
login_url: setupUrl,
|
|
5425
5559
|
...accountEmail ? { account_email: accountEmail } : {},
|
|
5426
|
-
...note ? { note } : {}
|
|
5560
|
+
...note ? { note } : {},
|
|
5561
|
+
...typeof input.timeout_seconds === "number" ? { timeout_seconds: input.timeout_seconds } : {}
|
|
5427
5562
|
});
|
|
5428
5563
|
if (!open.ok) return errorResult("browser_profile_connect", open.data);
|
|
5429
5564
|
const connectedLogins = await fetchConnectedLogins(profile).catch(() => []);
|
|
5430
5565
|
return structuredResult({
|
|
5431
5566
|
ok: true,
|
|
5432
5567
|
tool: "browser_profile_connect",
|
|
5433
|
-
session_id: null,
|
|
5568
|
+
session_id: open.data.browser_session_id ?? null,
|
|
5434
5569
|
auth_connection_id: open.data.connection_id,
|
|
5435
5570
|
watch_url: `${consoleBase}/console/auth/${open.data.connection_id}`,
|
|
5436
5571
|
live_view_url: null,
|
|
@@ -5440,13 +5575,13 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
5440
5575
|
account_email: accountEmail,
|
|
5441
5576
|
note,
|
|
5442
5577
|
status: open.data.status ?? null,
|
|
5443
|
-
flow_status:
|
|
5444
|
-
flow_step:
|
|
5445
|
-
flow_expires_at:
|
|
5446
|
-
post_login_url:
|
|
5578
|
+
flow_status: null,
|
|
5579
|
+
flow_step: null,
|
|
5580
|
+
flow_expires_at: null,
|
|
5581
|
+
post_login_url: null,
|
|
5447
5582
|
connected_logins: connectedLogins,
|
|
5448
5583
|
next_steps: [
|
|
5449
|
-
`Give the user watch_url so they can sign in to ${domain}.`,
|
|
5584
|
+
`Give the user watch_url so they can sign in to ${domain} directly in that page, then click "Done \u2014 I'm signed in".`,
|
|
5450
5585
|
"Poll browser_profile_list (with this profile) until the login reads AUTHENTICATED.",
|
|
5451
5586
|
"To add another account to this same profile, call browser_profile_connect again with the same profile and a different domain.",
|
|
5452
5587
|
"When authenticated, call browser_open with this profile to drive the logged-in session."
|
|
@@ -5471,7 +5606,7 @@ function registerBrowserAgentMcpTools(server, opts) {
|
|
|
5471
5606
|
error: "profile or email is required when BROWSER_AGENT_PROFILE_NAME is not available"
|
|
5472
5607
|
});
|
|
5473
5608
|
}
|
|
5474
|
-
const domain =
|
|
5609
|
+
const domain = normalizeDomain2(input.domain) || void 0;
|
|
5475
5610
|
const res = await req("POST", "/agent/profiles/list", {
|
|
5476
5611
|
profile,
|
|
5477
5612
|
...domain ? { domain } : {},
|
|
@@ -8054,4 +8189,4 @@ export {
|
|
|
8054
8189
|
registerMemoryMcpTools,
|
|
8055
8190
|
MemoryMcpToolExecutor
|
|
8056
8191
|
};
|
|
8057
|
-
//# sourceMappingURL=chunk-
|
|
8192
|
+
//# sourceMappingURL=chunk-BAMVP57P.js.map
|