crawlforge-mcp-server 5.0.1 → 5.0.2
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/CLAUDE.md +1 -1
- package/package.json +1 -1
- package/server.js +1 -1
- package/src/core/LLMsTxtAnalyzer.js +3 -1
- package/src/core/analysis/ContentAnalyzer.js +3 -1
- package/src/tools/extract/extractStructured.js +2 -1
- package/src/tools/llmstxt/generateLLMsTxt.js +8 -6
- package/src/tools/templates/TemplateRegistry.js +6 -2
package/CLAUDE.md
CHANGED
|
@@ -62,7 +62,7 @@ These guidelines are working if: fewer unnecessary changes in diffs, fewer rewri
|
|
|
62
62
|
|
|
63
63
|
CrawlForge MCP Server - A professional MCP (Model Context Protocol) server providing 27 web scraping, crawling, and content processing tools (5 inline + 22 advanced).
|
|
64
64
|
|
|
65
|
-
**Current Version:** 5.0.
|
|
65
|
+
**Current Version:** 5.0.2
|
|
66
66
|
|
|
67
67
|
## Development Commands
|
|
68
68
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "crawlforge-mcp-server",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.2",
|
|
4
4
|
"mcpName": "io.github.mysleekdesigns/crawlforge-mcp-server",
|
|
5
5
|
"description": "CrawlForge MCP Server - Professional Model Context Protocol server with 27 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
|
@@ -99,7 +99,7 @@ const taskStore = createTaskStore({ logger });
|
|
|
99
99
|
// Create the server
|
|
100
100
|
const server = new McpServer({
|
|
101
101
|
name: "crawlforge",
|
|
102
|
-
version: "5.0.
|
|
102
|
+
version: "5.0.2",
|
|
103
103
|
description: "Production-ready MCP server with 27 web scraping, crawling, and content processing tools. Features MCP Resources (crawlforge://), Prompts, Sampling fallback, Elicitation, stealth browsing, deep research, structured extraction, real Google SERP rank tracking, change tracking, local-LLM extraction via Ollama, unified multi-format scrape, and autonomous agent tool.",
|
|
104
104
|
homepage: "https://www.crawlforge.dev",
|
|
105
105
|
icon: "https://www.crawlforge.dev/icon.png",
|
|
@@ -228,7 +228,9 @@ export class LLMsTxtAnalyzer {
|
|
|
228
228
|
$('a[href*="api"], a[href*="developer"], a[href*="docs"]').each((_, element) => {
|
|
229
229
|
const href = $(element).attr('href');
|
|
230
230
|
const text = $(element).text().toLowerCase();
|
|
231
|
-
|
|
231
|
+
// Word-boundary match: substring checks flagged "Sapiens"/"rapid"
|
|
232
|
+
// style words as API links.
|
|
233
|
+
if (href && /\b(api|developer)s?\b/.test(text)) {
|
|
232
234
|
apis.push({
|
|
233
235
|
url: new URL(href, baseUrl).toString(),
|
|
234
236
|
type: 'documentation',
|
|
@@ -546,7 +546,9 @@ export class ContentAnalyzer {
|
|
|
546
546
|
const people = doc.people().out('array');
|
|
547
547
|
const places = doc.places().out('array');
|
|
548
548
|
const organizations = doc.organizations().out('array');
|
|
549
|
-
|
|
549
|
+
// .dates() needs the compromise-dates plugin (not installed) and threw,
|
|
550
|
+
// aborting ALL entity extraction; #Date+ tag matching is core compromise.
|
|
551
|
+
const dates = doc.match('#Date+').out('array');
|
|
550
552
|
const money = doc.money().out('array');
|
|
551
553
|
let other = doc.topics().out('array').slice(0, 10);
|
|
552
554
|
|
|
@@ -28,7 +28,8 @@ const SEMANTIC_FIELD_SELECTORS = {
|
|
|
28
28
|
summary: ['article p', 'main p', 'p'],
|
|
29
29
|
author: ['[rel="author"]', '.author', '.byline'],
|
|
30
30
|
date: ['time', '.date'],
|
|
31
|
-
published: ['time', '.published', '.date']
|
|
31
|
+
published: ['time', '.published', '.date'],
|
|
32
|
+
price: ['[itemprop="price"]', '[class*="price"]']
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const ExtractStructuredSchema = z.object({
|
|
@@ -225,6 +225,14 @@ export class GenerateLLMsTxtTool {
|
|
|
225
225
|
emitSection('Tools', flatten('tools'));
|
|
226
226
|
emitSection('Navigation', flatten('navigation'));
|
|
227
227
|
|
|
228
|
+
// Fallback: if no categorized section produced output, list the raw
|
|
229
|
+
// sitemap so llms.txt always carries a URL inventory. Must run BEFORE the
|
|
230
|
+
// APIs section — an APIs entry alone used to set hasBody and suppress it.
|
|
231
|
+
const hasBody = lines.some((l) => l.startsWith('## '));
|
|
232
|
+
if (!hasBody) {
|
|
233
|
+
emitSection('Pages', analysis.structure?.sitemap || []);
|
|
234
|
+
}
|
|
235
|
+
|
|
228
236
|
// APIs as their own section.
|
|
229
237
|
if (Array.isArray(analysis.apis) && analysis.apis.length > 0) {
|
|
230
238
|
lines.push('## APIs');
|
|
@@ -236,12 +244,6 @@ export class GenerateLLMsTxtTool {
|
|
|
236
244
|
lines.push('');
|
|
237
245
|
}
|
|
238
246
|
|
|
239
|
-
// Fallback: if no categorized sections produced output, list the raw sitemap.
|
|
240
|
-
const hasBody = lines.some((l) => l.startsWith('## '));
|
|
241
|
-
if (!hasBody) {
|
|
242
|
-
emitSection('Pages', analysis.structure?.sitemap || []);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
247
|
return lines.join('\n').replace(/\n{3,}/g, '\n\n').trimEnd() + '\n';
|
|
246
248
|
}
|
|
247
249
|
|
|
@@ -87,9 +87,13 @@ const TEMPLATES = [
|
|
|
87
87
|
description: attr($, 'meta[property="og:description"]', 'content') || text($, 'p.f4.my-3'),
|
|
88
88
|
stars: text($, '#repo-stars-counter-star') || text($, '[aria-label*="stargazers"]'),
|
|
89
89
|
forks: text($, '#repo-network-counter') || text($, '[aria-label*="forks"]'),
|
|
90
|
-
|
|
90
|
+
// React (logged-out) layout has no watchers aria-label; the count is
|
|
91
|
+
// the <strong> right after the single octicon-eye. Language is a
|
|
92
|
+
// client-side skeleton on that layout — unrecoverable from static
|
|
93
|
+
// HTML, so it stays null there (itemprop still works on classic).
|
|
94
|
+
watchers: text($, '.octicon-eye + strong') || text($, '[aria-label*="watchers"]'),
|
|
91
95
|
language: text($, 'span[itemprop="programmingLanguage"]') || text($, '.d-inline-flex[class*="language"]'),
|
|
92
|
-
topics: list($, 'a.topic-tag'),
|
|
96
|
+
topics: list($, 'a.topic-tag, a[href^="/topics/"]'),
|
|
93
97
|
license: text($, 'a[href*="blob/"][href*="LICENSE"]') || text($, '.octicon-law ~ span'),
|
|
94
98
|
last_updated: attr($, 'relative-time', 'datetime'),
|
|
95
99
|
homepage: attr($, 'a[href][rel="noopener noreferrer"]', 'href'),
|