crawlforge-mcp-server 5.6.5 → 5.6.6
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crawlforge-mcp-server",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.6",
|
|
4
4
|
"mcpName": "io.github.mysleekdesigns/crawlforge-mcp-server",
|
|
5
5
|
"description": "CrawlForge MCP Server - Professional Model Context Protocol server with 29 web scraping, crawling, deep-research, and autonomous-extraction tools. Returns clean Markdown and structured JSON for Claude, Cursor, and any MCP client. Defaults to local Ollama for LLM extraction (no API key needed); OpenAI/Anthropic available as opt-in. Includes a unified multi-format scrape tool, an autonomous agent, pre-built site templates, and Camoufox stealth browsing.",
|
|
6
6
|
"main": "server.js",
|
package/server.js
CHANGED
|
@@ -106,7 +106,7 @@ const taskStore = createTaskStore({ logger });
|
|
|
106
106
|
// Create the server
|
|
107
107
|
const server = new McpServer({
|
|
108
108
|
name: "crawlforge",
|
|
109
|
-
version: "5.6.
|
|
109
|
+
version: "5.6.6",
|
|
110
110
|
description: "Production-ready MCP server with 29 web scraping, crawling, and content processing tools. Features MCP Resources (crawlforge://), Prompts, Sampling fallback, Elicitation, stealth browsing, deep research, structured extraction, embedded JavaScript state extraction, real Google SERP rank tracking, Reddit search via community archives, change tracking, local-LLM extraction via Ollama, unified multi-format scrape, and autonomous agent tool.",
|
|
111
111
|
homepage: "https://www.crawlforge.dev",
|
|
112
112
|
icon: "https://www.crawlforge.dev/icon.png",
|
|
@@ -11,6 +11,7 @@ import { ssrfGuard, isSsrfError } from '../../../utils/ssrfGuard.js';
|
|
|
11
11
|
import { noteRetryAfter } from '../../../utils/hostRateLimiter.js';
|
|
12
12
|
import { preflightFetch } from '../../../utils/robotsGate.js';
|
|
13
13
|
import { htmlToMarkdown } from '../../../utils/htmlToMarkdown.js';
|
|
14
|
+
import { elementText } from '../../../utils/elementText.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Fetch a URL with AbortController timeout (SSRF-guarded + per-host throttled).
|
|
@@ -111,8 +112,8 @@ function extractStructuredData($, selectors) {
|
|
|
111
112
|
try {
|
|
112
113
|
const elements = $(selector);
|
|
113
114
|
if (elements.length === 0) extracted[key] = null;
|
|
114
|
-
else if (elements.length === 1) extracted[key] = elements.
|
|
115
|
-
else extracted[key] = elements.map((_, el) =>
|
|
115
|
+
else if (elements.length === 1) extracted[key] = elementText($, elements.get(0));
|
|
116
|
+
else extracted[key] = elements.map((_, el) => elementText($, el)).get();
|
|
116
117
|
} catch {
|
|
117
118
|
extracted[key] = { error: `Invalid selector: ${selector}` };
|
|
118
119
|
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { load } from 'cheerio';
|
|
11
11
|
import { fetchWithTimeout } from './_fetch.js';
|
|
12
|
+
import { elementText } from '../../utils/elementText.js';
|
|
12
13
|
|
|
13
14
|
// Matches a trailing "@attr" suffix (e.g. "@href", "@data-id") — the attribute
|
|
14
15
|
// name must look like a real attribute, not the "@" of a CSS attribute-value
|
|
@@ -73,7 +74,7 @@ function extractRows($, { row_selector, selectors, max_results }) {
|
|
|
73
74
|
record[field] = null;
|
|
74
75
|
continue;
|
|
75
76
|
}
|
|
76
|
-
record[field] = attribute ? (el.attr(attribute) ?? null) : el.
|
|
77
|
+
record[field] = attribute ? (el.attr(attribute) ?? null) : elementText($, el.get(0));
|
|
77
78
|
matchCounts[field] += 1;
|
|
78
79
|
} catch (selectorError) {
|
|
79
80
|
record[field] = {
|
|
@@ -145,7 +146,7 @@ export async function scrapeStructuredHandler({ url, selectors, row_selector, ma
|
|
|
145
146
|
if (attribute) {
|
|
146
147
|
return $(el).attr(attribute) ?? null;
|
|
147
148
|
}
|
|
148
|
-
return
|
|
149
|
+
return elementText($, el);
|
|
149
150
|
};
|
|
150
151
|
|
|
151
152
|
if (elements.length === 1) {
|
package/src/utils/elementText.js
CHANGED
|
@@ -10,9 +10,8 @@
|
|
|
10
10
|
* of a recovered table), an element that wraps a table renders each table that
|
|
11
11
|
* way in place, and anything else is .text().trim(), unchanged.
|
|
12
12
|
*
|
|
13
|
-
* batchScrape/worker.js
|
|
14
|
-
* matched elements
|
|
15
|
-
* do not use this yet.
|
|
13
|
+
* batch_scrape's selector extraction (batchScrape/worker.js) and
|
|
14
|
+
* scrape_structured read matched elements through this helper too (5.6.6).
|
|
16
15
|
*/
|
|
17
16
|
|
|
18
17
|
const ROW_GROUPS = new Set(['table', 'thead', 'tbody', 'tfoot']);
|