scorchcrawl-mcp 1.2.0 → 2.0.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/dist/copilot-client.js +39 -0
- package/dist/index.js +499 -576
- package/dist/response-utils.js +0 -146
- package/package.json +2 -4
- package/dist/bundle.js +0 -2777
- package/dist/copilot-agent.js +0 -619
- package/dist/rate-limiter.js +0 -361
- package/dist/scrape-cache.js +0 -34
package/dist/response-utils.js
CHANGED
|
@@ -26,19 +26,6 @@ const SUMMARIZE_TIMEOUT_MS = parseInt(process.env.SCORCHCRAWL_SUMMARIZE_TIMEOUT_
|
|
|
26
26
|
export function mapError(err) {
|
|
27
27
|
const raw = normalizeErrorString(err);
|
|
28
28
|
const lower = raw.toLowerCase();
|
|
29
|
-
// --- CAPTCHA (checked before ACCESS_DENIED since 'challenge' overlaps) ---
|
|
30
|
-
if (matches(lower, ['captcha', 'recaptcha', 'hcaptcha', 'turnstile'])) {
|
|
31
|
-
return {
|
|
32
|
-
code: 'CAPTCHA_REQUIRED',
|
|
33
|
-
message: 'Page requires CAPTCHA verification.',
|
|
34
|
-
suggestions: [
|
|
35
|
-
'Try proxy: "stealth" to avoid CAPTCHA triggers',
|
|
36
|
-
'Use scorch_agent which may navigate around CAPTCHAs',
|
|
37
|
-
'Try scorch_search to find the content from a cached source',
|
|
38
|
-
],
|
|
39
|
-
originalError: raw,
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
29
|
// --- HTTP status-based patterns ---
|
|
43
30
|
if (matches(lower, ['403', 'forbidden', 'cloudflare', 'cf-ray', 'challenge', 'access denied'])) {
|
|
44
31
|
return {
|
|
@@ -157,28 +144,6 @@ export function mapError(err) {
|
|
|
157
144
|
originalError: raw,
|
|
158
145
|
};
|
|
159
146
|
}
|
|
160
|
-
if (matches(lower, ['geo', 'geoblocked', 'not available in your', 'region', 'country restriction'])) {
|
|
161
|
-
return {
|
|
162
|
-
code: 'GEO_BLOCKED',
|
|
163
|
-
message: 'Content is geo-restricted.',
|
|
164
|
-
suggestions: [
|
|
165
|
-
'Use location parameter with a different country: location: { country: "US" }',
|
|
166
|
-
'Try proxy: "stealth" which may route through a different region',
|
|
167
|
-
],
|
|
168
|
-
originalError: raw,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
if (matches(lower, ['paywall', 'subscribe', 'premium content', 'members only'])) {
|
|
172
|
-
return {
|
|
173
|
-
code: 'PAYWALL',
|
|
174
|
-
message: 'Content is behind a paywall.',
|
|
175
|
-
suggestions: [
|
|
176
|
-
'Try scorch_search to find freely available alternatives',
|
|
177
|
-
'Use scorch_map to find non-paywalled pages on the same site',
|
|
178
|
-
],
|
|
179
|
-
originalError: raw,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
147
|
// --- Catch-all ---
|
|
183
148
|
return {
|
|
184
149
|
code: 'UNKNOWN_ERROR',
|
|
@@ -676,114 +641,3 @@ function injectMarkdown(data, newMarkdown, meta) {
|
|
|
676
641
|
// ---------------------------------------------------------------------------
|
|
677
642
|
export { MAX_CONTENT_CHARS, SUMMARIZE_AFTER_WORDS, SUMMARIZE_MODEL };
|
|
678
643
|
export { summaryCache, summarizationTimestamps };
|
|
679
|
-
// ---------------------------------------------------------------------------
|
|
680
|
-
// Feature 4: Response Quality Scoring
|
|
681
|
-
// ---------------------------------------------------------------------------
|
|
682
|
-
/** Known anti-bot / blocked-page signatures */
|
|
683
|
-
const ANTI_BOT_SIGNATURES = [
|
|
684
|
-
// Cloudflare
|
|
685
|
-
'checking your browser', 'cf-browser-verification', 'just a moment',
|
|
686
|
-
'cf-challenge-running', 'cf_chl_opt', 'ray id:',
|
|
687
|
-
// DataDome
|
|
688
|
-
'datadome', 'dd.js',
|
|
689
|
-
// PerimeterX
|
|
690
|
-
'perimeterx', 'px-captcha', 'human challenge',
|
|
691
|
-
// Incapsula / Imperva
|
|
692
|
-
'incapsula', 'imperva', '_incapsula_resource',
|
|
693
|
-
// Generic
|
|
694
|
-
'access denied', 'bot detected', 'automated access',
|
|
695
|
-
'please verify you are a human', 'captcha',
|
|
696
|
-
// Akamai
|
|
697
|
-
'akamai', 'ak_bmsc',
|
|
698
|
-
// Shape Security
|
|
699
|
-
'shape security',
|
|
700
|
-
];
|
|
701
|
-
/**
|
|
702
|
-
* Score the quality of a scrape response (0-100).
|
|
703
|
-
* Detects anti-bot pages, empty/minimal content, and common failure patterns.
|
|
704
|
-
*/
|
|
705
|
-
export function scoreResponseQuality(data) {
|
|
706
|
-
const issues = [];
|
|
707
|
-
let score = 100;
|
|
708
|
-
let antiBotDetected = false;
|
|
709
|
-
let antiBotSystem;
|
|
710
|
-
if (!data || typeof data !== 'object') {
|
|
711
|
-
return { score: 0, antiBotDetected: false, issues: ['No response data'] };
|
|
712
|
-
}
|
|
713
|
-
const obj = data;
|
|
714
|
-
const inner = (obj.data && typeof obj.data === 'object' ? obj.data : obj);
|
|
715
|
-
// Check for explicit failure
|
|
716
|
-
if (obj.success === false) {
|
|
717
|
-
score -= 50;
|
|
718
|
-
issues.push('Response indicates failure');
|
|
719
|
-
}
|
|
720
|
-
// Extract text content for analysis
|
|
721
|
-
const md = (typeof inner.markdown === 'string' ? inner.markdown : '');
|
|
722
|
-
const html = (typeof inner.html === 'string' ? inner.html : typeof inner.rawHtml === 'string' ? inner.rawHtml : '');
|
|
723
|
-
const content = md || html;
|
|
724
|
-
const lower = content.toLowerCase();
|
|
725
|
-
// --- Anti-bot detection ---
|
|
726
|
-
for (const sig of ANTI_BOT_SIGNATURES) {
|
|
727
|
-
if (lower.includes(sig)) {
|
|
728
|
-
antiBotDetected = true;
|
|
729
|
-
// Identify the specific system
|
|
730
|
-
if (sig.includes('cf-') || sig.includes('cloudflare') || sig === 'just a moment' || sig === 'checking your browser' || sig === 'ray id:') {
|
|
731
|
-
antiBotSystem = 'Cloudflare';
|
|
732
|
-
}
|
|
733
|
-
else if (sig.includes('datadome')) {
|
|
734
|
-
antiBotSystem = 'DataDome';
|
|
735
|
-
}
|
|
736
|
-
else if (sig.includes('perimeterx') || sig.includes('px-captcha')) {
|
|
737
|
-
antiBotSystem = 'PerimeterX';
|
|
738
|
-
}
|
|
739
|
-
else if (sig.includes('incapsula') || sig.includes('imperva')) {
|
|
740
|
-
antiBotSystem = 'Imperva/Incapsula';
|
|
741
|
-
}
|
|
742
|
-
else if (sig.includes('akamai') || sig.includes('ak_bmsc')) {
|
|
743
|
-
antiBotSystem = 'Akamai';
|
|
744
|
-
}
|
|
745
|
-
else if (sig.includes('captcha')) {
|
|
746
|
-
antiBotSystem = 'CAPTCHA';
|
|
747
|
-
}
|
|
748
|
-
else {
|
|
749
|
-
antiBotSystem = 'Unknown';
|
|
750
|
-
}
|
|
751
|
-
score -= 40;
|
|
752
|
-
issues.push(`Anti-bot protection detected: ${antiBotSystem}`);
|
|
753
|
-
break;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
// --- Content length analysis ---
|
|
757
|
-
if (content.length === 0) {
|
|
758
|
-
score -= 30;
|
|
759
|
-
issues.push('No content extracted');
|
|
760
|
-
}
|
|
761
|
-
else if (content.length < 100) {
|
|
762
|
-
score -= 20;
|
|
763
|
-
issues.push('Very minimal content (< 100 chars)');
|
|
764
|
-
}
|
|
765
|
-
else if (content.length < 500) {
|
|
766
|
-
score -= 10;
|
|
767
|
-
issues.push('Thin content (< 500 chars)');
|
|
768
|
-
}
|
|
769
|
-
// --- Navigation-only content detection (SPA shells) ---
|
|
770
|
-
const navPatterns = ['sign in', 'log in', 'menu', 'navigation', 'footer', 'header'];
|
|
771
|
-
const navCount = navPatterns.filter(p => lower.includes(p)).length;
|
|
772
|
-
const contentWords = content.split(/\s+/).length;
|
|
773
|
-
if (contentWords < 50 && navCount >= 3) {
|
|
774
|
-
score -= 20;
|
|
775
|
-
issues.push('Content appears to be navigation-only (possible SPA shell)');
|
|
776
|
-
}
|
|
777
|
-
// --- Error page detection ---
|
|
778
|
-
const errorPagePatterns = ['page not found', '404', 'error occurred', 'something went wrong', 'under maintenance'];
|
|
779
|
-
if (errorPagePatterns.some(p => lower.includes(p)) && contentWords < 200) {
|
|
780
|
-
score -= 15;
|
|
781
|
-
issues.push('Content appears to be an error page');
|
|
782
|
-
}
|
|
783
|
-
return {
|
|
784
|
-
score: Math.max(0, Math.min(100, score)),
|
|
785
|
-
antiBotDetected,
|
|
786
|
-
...(antiBotSystem && { antiBotSystem }),
|
|
787
|
-
issues,
|
|
788
|
-
};
|
|
789
|
-
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "scorchcrawl-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Open-source Copilot SDK-compliant MCP server for web scraping with stealth bot-detection bypass.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
15
15
|
"scripts": {
|
|
16
|
-
"build": "tsc
|
|
17
|
-
"build:tsc": "tsc",
|
|
16
|
+
"build": "tsc && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
|
|
18
17
|
"start": "node dist/index.js",
|
|
19
18
|
"start:server": "HTTP_STREAMABLE_SERVER=true node dist/index.js",
|
|
20
19
|
"dev": "tsc --watch",
|
|
@@ -63,7 +62,6 @@
|
|
|
63
62
|
"@types/node": "^22.0.0",
|
|
64
63
|
"@types/turndown": "^5.0.5",
|
|
65
64
|
"@types/uuid": "^10.0.0",
|
|
66
|
-
"esbuild": "^0.25.0",
|
|
67
65
|
"vitest": "^3.2.1"
|
|
68
66
|
}
|
|
69
67
|
}
|