@ignidor/web-search-mcp 1.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/README.md +267 -0
- package/bin/web-search-mcp.js +13 -0
- package/dist/crawl4ai-client.d.ts +238 -0
- package/dist/crawl4ai-client.d.ts.map +1 -0
- package/dist/crawl4ai-client.js +608 -0
- package/dist/crawl4ai-client.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +561 -0
- package/dist/index.js.map +1 -0
- package/dist/playwright-crawler.d.ts +92 -0
- package/dist/playwright-crawler.d.ts.map +1 -0
- package/dist/playwright-crawler.js +454 -0
- package/dist/playwright-crawler.js.map +1 -0
- package/dist/ranking.d.ts +58 -0
- package/dist/ranking.d.ts.map +1 -0
- package/dist/ranking.js +218 -0
- package/dist/ranking.js.map +1 -0
- package/dist/search.d.ts +15 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +187 -0
- package/dist/search.js.map +1 -0
- package/dist/types/index.d.ts +131 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/concurrency.d.ts +24 -0
- package/dist/utils/concurrency.d.ts.map +1 -0
- package/dist/utils/concurrency.js +53 -0
- package/dist/utils/concurrency.js.map +1 -0
- package/dist/utils/validators.d.ts +21 -0
- package/dist/utils/validators.d.ts.map +1 -0
- package/dist/utils/validators.js +75 -0
- package/dist/utils/validators.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Concurrency Control Utilities
|
|
2
|
+
import pLimit from 'p-limit';
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Concurrency Limiter
|
|
5
|
+
// ============================================================================
|
|
6
|
+
/**
|
|
7
|
+
* Creates a concurrency limiter for controlling parallel operations.
|
|
8
|
+
* @param concurrency - Maximum number of concurrent operations (default: 5)
|
|
9
|
+
* @returns A limit function that wraps async operations
|
|
10
|
+
*/
|
|
11
|
+
export function createConcurrencyLimiter(concurrency = 5) {
|
|
12
|
+
return pLimit(concurrency);
|
|
13
|
+
}
|
|
14
|
+
// ============================================================================
|
|
15
|
+
// Batch Processing
|
|
16
|
+
// ============================================================================
|
|
17
|
+
/**
|
|
18
|
+
* Processes items in batches with concurrency control.
|
|
19
|
+
* @param items - Array of items to process
|
|
20
|
+
* @param processor - Async function to process each item
|
|
21
|
+
* @param concurrency - Maximum concurrent operations
|
|
22
|
+
* @returns Array of results in the same order as input
|
|
23
|
+
*/
|
|
24
|
+
export async function processBatch(items, processor, concurrency = 5) {
|
|
25
|
+
const limit = createConcurrencyLimiter(concurrency);
|
|
26
|
+
const tasks = items.map((item, index) => limit(() => processor(item, index)));
|
|
27
|
+
return Promise.all(tasks);
|
|
28
|
+
}
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// Parallel Processing with Error Handling
|
|
31
|
+
// ============================================================================
|
|
32
|
+
/**
|
|
33
|
+
* Processes items with graceful error handling.
|
|
34
|
+
* Failed items return undefined instead of rejecting the entire batch.
|
|
35
|
+
* @param items - Array of items to process
|
|
36
|
+
* @param processor - Async function to process each item
|
|
37
|
+
* @param concurrency - Maximum concurrent operations
|
|
38
|
+
* @returns Array of successful results or undefined for failed items
|
|
39
|
+
*/
|
|
40
|
+
export async function processBatchWithErrorHandling(items, processor, concurrency = 5) {
|
|
41
|
+
const limit = createConcurrencyLimiter(concurrency);
|
|
42
|
+
const tasks = items.map((item, index) => limit(async () => {
|
|
43
|
+
try {
|
|
44
|
+
return await processor(item, index);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.error(`[Concurrency] Item ${index} failed:`, error instanceof Error ? error.message : error);
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}));
|
|
51
|
+
return Promise.all(tasks);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=concurrency.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../../src/utils/concurrency.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAEhC,OAAO,MAAM,MAAM,SAAS,CAAC;AAE7B,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,cAAsB,CAAC;IAC9D,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;AAC7B,CAAC;AAED,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,KAAU,EACV,SAAiD,EACjD,cAAsB,CAAC;IAEvB,MAAM,KAAK,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;IAEpD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACtC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CACpC,CAAC;IAEF,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC;AAED,+EAA+E;AAC/E,0CAA0C;AAC1C,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,KAAU,EACV,SAAiD,EACjD,cAAsB,CAAC;IAEvB,MAAM,KAAK,GAAG,wBAAwB,CAAC,WAAW,CAAC,CAAC;IAEpD,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACtC,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,KAAK,UAAU,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACrG,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CACH,CAAC;IAEF,OAAO,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that a URL is safe and properly formatted.
|
|
3
|
+
* Blocks localhost, private IPs, and non-HTTP protocols to prevent SSRF attacks.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isValidUrl(url: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Validates search query for security and length limits.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isValidQuery(query: string): {
|
|
10
|
+
valid: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Clamps a number between min and max values.
|
|
15
|
+
*/
|
|
16
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
17
|
+
/**
|
|
18
|
+
* Validates an array of URLs, returning only valid URLs.
|
|
19
|
+
*/
|
|
20
|
+
export declare function validateUrls(urls: string[]): string[];
|
|
21
|
+
//# sourceMappingURL=validators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../src/utils/validators.ts"],"names":[],"mappings":"AAMA;;;GAGG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CA4B/C;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAgB9E;AAMD;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE;AAMD;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAIrD"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// Validators and Sanitizers
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// URL Validation (SSRF Protection)
|
|
4
|
+
// ============================================================================
|
|
5
|
+
/**
|
|
6
|
+
* Validates that a URL is safe and properly formatted.
|
|
7
|
+
* Blocks localhost, private IPs, and non-HTTP protocols to prevent SSRF attacks.
|
|
8
|
+
*/
|
|
9
|
+
export function isValidUrl(url) {
|
|
10
|
+
if (!url || typeof url !== 'string')
|
|
11
|
+
return false;
|
|
12
|
+
try {
|
|
13
|
+
const parsed = new URL(url);
|
|
14
|
+
const hostname = parsed.hostname.toLowerCase();
|
|
15
|
+
// Block localhost variants
|
|
16
|
+
if (hostname === 'localhost' ||
|
|
17
|
+
hostname === '127.0.0.1' ||
|
|
18
|
+
hostname === '0.0.0.0' ||
|
|
19
|
+
hostname === '[::1]' ||
|
|
20
|
+
hostname.startsWith('127.')) {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
// Block private IP ranges
|
|
24
|
+
if (hostname.startsWith('192.168.') ||
|
|
25
|
+
hostname.startsWith('10.') ||
|
|
26
|
+
hostname.startsWith('172.16.')) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
// Only allow http/https
|
|
30
|
+
return url.startsWith('http://') || url.startsWith('https://');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Query Validation
|
|
38
|
+
// ============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Validates search query for security and length limits.
|
|
41
|
+
*/
|
|
42
|
+
export function isValidQuery(query) {
|
|
43
|
+
if (!query || typeof query !== 'string') {
|
|
44
|
+
return { valid: false, error: 'Query must be a non-empty string' };
|
|
45
|
+
}
|
|
46
|
+
const trimmed = query.trim();
|
|
47
|
+
if (trimmed.length === 0) {
|
|
48
|
+
return { valid: false, error: 'Query cannot be empty' };
|
|
49
|
+
}
|
|
50
|
+
if (trimmed.length > 500) {
|
|
51
|
+
return { valid: false, error: 'Query too long (maximum 500 characters)' };
|
|
52
|
+
}
|
|
53
|
+
return { valid: true };
|
|
54
|
+
}
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Number Range Validation
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* Clamps a number between min and max values.
|
|
60
|
+
*/
|
|
61
|
+
export function clamp(value, min, max) {
|
|
62
|
+
return Math.min(Math.max(value, min), max);
|
|
63
|
+
}
|
|
64
|
+
// ============================================================================
|
|
65
|
+
// URL List Validation
|
|
66
|
+
// ============================================================================
|
|
67
|
+
/**
|
|
68
|
+
* Validates an array of URLs, returning only valid URLs.
|
|
69
|
+
*/
|
|
70
|
+
export function validateUrls(urls) {
|
|
71
|
+
if (!Array.isArray(urls))
|
|
72
|
+
return [];
|
|
73
|
+
return urls.filter(url => isValidUrl(url));
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=validators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validators.js","sourceRoot":"","sources":["../../src/utils/validators.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAE5B,+EAA+E;AAC/E,mCAAmC;AACnC,+EAA+E;AAE/E;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAElD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;QAE/C,2BAA2B;QAC3B,IAAI,QAAQ,KAAK,WAAW;YACxB,QAAQ,KAAK,WAAW;YACxB,QAAQ,KAAK,SAAS;YACtB,QAAQ,KAAK,OAAO;YACpB,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,0BAA0B;QAC1B,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC;YAC/B,QAAQ,CAAC,UAAU,CAAC,KAAK,CAAC;YAC1B,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,wBAAwB;QACxB,OAAO,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC;IACrE,CAAC;IAED,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE7B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC;IAC1D,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC;IAC5E,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AACzB,CAAC;AAED,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,KAAK,CAAC,KAAa,EAAE,GAAW,EAAE,GAAW;IAC3D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;AAC7C,CAAC;AAED,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAEpC,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7C,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ignidor/web-search-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Local, unlimited web-search MCP server with BM25 ranking and Playwright crawling. No Docker, no API keys, no rate limits.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ignidor-web-search-mcp": "./bin/web-search-mcp.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"bin",
|
|
14
|
+
"README.md"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc",
|
|
18
|
+
"dev": "tsc --watch",
|
|
19
|
+
"start": "node dist/index.js",
|
|
20
|
+
"clean": "rm -rf dist",
|
|
21
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
22
|
+
"postpack": "echo 'Package created successfully. Install with: npx @ignidor/web-search-mcp'",
|
|
23
|
+
"test": "echo \"Tests not yet implemented\" && exit 0",
|
|
24
|
+
"lint": "echo \"Linting not yet implemented\" && exit 0"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18.0.0"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"mcp",
|
|
31
|
+
"web-search",
|
|
32
|
+
"playwright",
|
|
33
|
+
"bm25",
|
|
34
|
+
"search-engine",
|
|
35
|
+
"claude",
|
|
36
|
+
"ai",
|
|
37
|
+
"content-extraction",
|
|
38
|
+
"crawler",
|
|
39
|
+
"duckduckgo",
|
|
40
|
+
"unlimited",
|
|
41
|
+
"free",
|
|
42
|
+
"no-api-key"
|
|
43
|
+
],
|
|
44
|
+
"author": "Ignidor Team",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"repository": {
|
|
47
|
+
"type": "git",
|
|
48
|
+
"url": "git+https://github.com/JayaBigDataIsCool/ignidor-web-search-mcp.git"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/JayaBigDataIsCool/ignidor-web-search-mcp/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/JayaBigDataIsCool/ignidor-web-search-mcp#readme",
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@modelcontextprotocol/sdk": "^1.18.1",
|
|
56
|
+
"@types/cheerio": "^0.22.35",
|
|
57
|
+
"axios": "^1.7.9",
|
|
58
|
+
"cheerio": "^1.0.0",
|
|
59
|
+
"fast-bm25": "^0.0.5",
|
|
60
|
+
"p-limit": "^5.0.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^20.0.0",
|
|
64
|
+
"typescript": "^5.0.0"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"playwright": "^1.40.0"
|
|
68
|
+
},
|
|
69
|
+
"peerDependenciesMeta": {
|
|
70
|
+
"playwright": {
|
|
71
|
+
"optional": true
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"optionalDependencies": {
|
|
75
|
+
"playwright": "^1.57.0"
|
|
76
|
+
}
|
|
77
|
+
}
|