@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.
@@ -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
+ }