mcp-searchable 1.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/.npx-install/README.md +188 -0
- package/.npx-install/bootstrap.js +341 -0
- package/LICENSE +21 -0
- package/README.md +312 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +185 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/storage.d.ts +17 -0
- package/dist/lib/storage.d.ts.map +1 -0
- package/dist/lib/storage.js +105 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/web-ask.d.ts +13 -0
- package/dist/lib/web-ask.d.ts.map +1 -0
- package/dist/lib/web-ask.js +73 -0
- package/dist/lib/web-ask.js.map +1 -0
- package/dist/lib/web-fetch.d.ts +13 -0
- package/dist/lib/web-fetch.d.ts.map +1 -0
- package/dist/lib/web-fetch.js +48 -0
- package/dist/lib/web-fetch.js.map +1 -0
- package/dist/lib/web-search.d.ts +23 -0
- package/dist/lib/web-search.d.ts.map +1 -0
- package/dist/lib/web-search.js +160 -0
- package/dist/lib/web-search.js.map +1 -0
- package/package.json +99 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
export class OllamaAskProvider {
|
|
3
|
+
storage;
|
|
4
|
+
ollamaUrl;
|
|
5
|
+
model;
|
|
6
|
+
constructor(storage, ollamaUrl = process.env.OLLAMA_URL || 'http://localhost:11434', model = process.env.OLLAMA_MODEL || 'llama3') {
|
|
7
|
+
this.storage = storage;
|
|
8
|
+
this.ollamaUrl = ollamaUrl;
|
|
9
|
+
this.model = model;
|
|
10
|
+
}
|
|
11
|
+
async ask(question, limit = 3) {
|
|
12
|
+
const safeQuery = question.replace(/["'^]/g, '').trim();
|
|
13
|
+
let grepResults = [];
|
|
14
|
+
try {
|
|
15
|
+
grepResults = this.storage.grep(safeQuery, limit);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
const words = safeQuery.split(/\s+/).filter((w) => w.length > 2);
|
|
19
|
+
if (words.length > 0) {
|
|
20
|
+
try {
|
|
21
|
+
grepResults = this.storage.grep(words.join(' OR '), limit);
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
console.warn('Fallback FTS5 query failed', e);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (grepResults.length === 0) {
|
|
29
|
+
return {
|
|
30
|
+
answer: "I don't have any relevant information in my web stash to answer that question.",
|
|
31
|
+
contextUrls: [],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const contextUrls = [];
|
|
35
|
+
let contextText = '';
|
|
36
|
+
for (const res of grepResults) {
|
|
37
|
+
const page = this.storage.getPage(res.url);
|
|
38
|
+
if (page) {
|
|
39
|
+
contextUrls.push(res.url);
|
|
40
|
+
const truncatedContent = page.content.length > 2000 ? page.content.substring(0, 2000) + '...' : page.content;
|
|
41
|
+
contextText += `Source: ${page.url}\nTitle: ${page.title}\nContent: \n${truncatedContent}\n\n`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const prompt = `
|
|
45
|
+
You are a helpful assistant. Use the following context to answer the question at the end.
|
|
46
|
+
If the answer is not contained in the context, say "I don't know based on the provided context."
|
|
47
|
+
|
|
48
|
+
Context:
|
|
49
|
+
${contextText}
|
|
50
|
+
|
|
51
|
+
Question: ${question}
|
|
52
|
+
|
|
53
|
+
Answer:`;
|
|
54
|
+
try {
|
|
55
|
+
const response = await axios.post(`${this.ollamaUrl}/api/generate`, {
|
|
56
|
+
model: this.model,
|
|
57
|
+
prompt: prompt,
|
|
58
|
+
stream: false,
|
|
59
|
+
}, {
|
|
60
|
+
timeout: 60000,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
answer: response.data.response,
|
|
64
|
+
contextUrls,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
console.error('Error calling Ollama:', error);
|
|
69
|
+
throw new Error(`Failed to get answer from Ollama: ${error instanceof Error ? error.message : String(error)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=web-ask.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-ask.js","sourceRoot":"","sources":["../../src/lib/web-ask.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface WebFetchResult {
|
|
2
|
+
title: string;
|
|
3
|
+
content: string;
|
|
4
|
+
url: string;
|
|
5
|
+
excerpt?: string;
|
|
6
|
+
siteName?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare class WebFetcher {
|
|
9
|
+
private turndownService;
|
|
10
|
+
constructor();
|
|
11
|
+
fetch(url: string): Promise<WebFetchResult>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=web-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-fetch.d.ts","sourceRoot":"","sources":["../../src/lib/web-fetch.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,eAAe,CAAkB;;IASnC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;CAyClD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Readability } from '@mozilla/readability';
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import { JSDOM, VirtualConsole } from 'jsdom';
|
|
4
|
+
import TurndownService from 'turndown';
|
|
5
|
+
export class WebFetcher {
|
|
6
|
+
turndownService;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.turndownService = new TurndownService({
|
|
9
|
+
headingStyle: 'atx',
|
|
10
|
+
codeBlockStyle: 'fenced',
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
async fetch(url) {
|
|
14
|
+
try {
|
|
15
|
+
const response = await axios.get(url, {
|
|
16
|
+
headers: {
|
|
17
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
|
18
|
+
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
|
|
19
|
+
},
|
|
20
|
+
timeout: 10000,
|
|
21
|
+
});
|
|
22
|
+
const contentType = response.headers['content-type']?.toString() || '';
|
|
23
|
+
if (!contentType.includes('text/html') && !contentType.includes('application/xhtml+xml')) {
|
|
24
|
+
throw new Error(`Unsupported content type: ${contentType}`);
|
|
25
|
+
}
|
|
26
|
+
const virtualConsole = new VirtualConsole();
|
|
27
|
+
const dom = new JSDOM(response.data, { url, virtualConsole });
|
|
28
|
+
const reader = new Readability(dom.window.document);
|
|
29
|
+
const article = reader.parse();
|
|
30
|
+
if (!article) {
|
|
31
|
+
throw new Error('Failed to extract article content using Readability');
|
|
32
|
+
}
|
|
33
|
+
const markdown = this.turndownService.turndown(article.content || '');
|
|
34
|
+
return {
|
|
35
|
+
title: article.title || '',
|
|
36
|
+
content: markdown,
|
|
37
|
+
url: url,
|
|
38
|
+
excerpt: article.excerpt || undefined,
|
|
39
|
+
siteName: article.siteName || undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error(`Error fetching or parsing URL ${url}:`, error);
|
|
44
|
+
throw new Error(`Failed to fetch web page: ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=web-fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-fetch.js","sourceRoot":"","sources":["../../src/lib/web-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,eAAe,MAAM,UAAU,CAAC;AAUvC,MAAM,OAAO,UAAU;IACb,eAAe,CAAkB;IAEzC;QACE,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,CAAC;YACzC,YAAY,EAAE,KAAK;YACnB,cAAc,EAAE,QAAQ;SACzB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAW;QACrB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE;gBACpC,OAAO,EAAE;oBACP,YAAY,EACV,qHAAqH;oBACvH,MAAM,EAAE,4EAA4E;iBACrF;gBACD,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;YAEH,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACvE,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBACzF,MAAM,IAAI,KAAK,CAAC,6BAA6B,WAAW,EAAE,CAAC,CAAC;YAC9D,CAAC;YAGD,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;YAC5C,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,cAAc,EAAE,CAAC,CAAC;YAE9D,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACpD,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAE,CAAC;YAE/B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;YACzE,CAAC;YAED,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAEtE,OAAO;gBACL,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,EAAE;gBAC1B,OAAO,EAAE,QAAQ;gBACjB,GAAG,EAAE,GAAG;gBACR,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,SAAS;gBACrC,QAAQ,EAAE,OAAO,CAAC,QAAQ,IAAI,SAAS;aACxC,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,GAAG,GAAG,EAAE,KAAK,CAAC,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzG,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface SearchResult {
|
|
2
|
+
title: string;
|
|
3
|
+
url: string;
|
|
4
|
+
snippet: string;
|
|
5
|
+
}
|
|
6
|
+
export interface WebSearchProvider {
|
|
7
|
+
search(query: string, limit?: number): Promise<SearchResult[]>;
|
|
8
|
+
}
|
|
9
|
+
export type SearchProviderName = 'duckduckgo' | 'google' | 'bing' | 'brave';
|
|
10
|
+
export declare class DuckDuckGoSearchProvider implements WebSearchProvider {
|
|
11
|
+
search(query: string, limit?: number): Promise<SearchResult[]>;
|
|
12
|
+
}
|
|
13
|
+
export declare class GoogleSearchProvider implements WebSearchProvider {
|
|
14
|
+
search(query: string, limit?: number): Promise<SearchResult[]>;
|
|
15
|
+
}
|
|
16
|
+
export declare class BingSearchProvider implements WebSearchProvider {
|
|
17
|
+
search(query: string, limit?: number): Promise<SearchResult[]>;
|
|
18
|
+
}
|
|
19
|
+
export declare class BraveSearchProvider implements WebSearchProvider {
|
|
20
|
+
search(query: string, limit?: number): Promise<SearchResult[]>;
|
|
21
|
+
}
|
|
22
|
+
export declare function createSearchProvider(provider?: SearchProviderName): WebSearchProvider;
|
|
23
|
+
//# sourceMappingURL=web-search.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-search.d.ts","sourceRoot":"","sources":["../../src/lib/web-search.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;CAChE;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AA6B5E,qBAAa,wBAAyB,YAAW,iBAAiB;IAC1D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA4CzE;AAED,qBAAa,oBAAqB,YAAW,iBAAiB;IACtD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA4BzE;AAED,qBAAa,kBAAmB,YAAW,iBAAiB;IACpD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA+BzE;AAED,qBAAa,mBAAoB,YAAW,iBAAiB;IACrD,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;CA8BzE;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,kBAAiC,GAAG,iBAAiB,CAanG"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { JSDOM } from 'jsdom';
|
|
3
|
+
const GOOGLE_API_ENDPOINT = 'https://www.googleapis.com/customsearch/v1';
|
|
4
|
+
const BING_API_ENDPOINT = 'https://api.bing.microsoft.com/v7.0/search';
|
|
5
|
+
const BRAVE_API_ENDPOINT = 'https://api.search.brave.com/res/v1/web/search';
|
|
6
|
+
function validatePositiveLimit(limit) {
|
|
7
|
+
if (!Number.isFinite(limit) || !Number.isInteger(limit) || limit <= 0) {
|
|
8
|
+
throw new Error('Search limit must be a positive integer');
|
|
9
|
+
}
|
|
10
|
+
return limit;
|
|
11
|
+
}
|
|
12
|
+
function requireEnvVar(name) {
|
|
13
|
+
const value = process.env[name];
|
|
14
|
+
if (!value) {
|
|
15
|
+
throw new Error(`Missing required environment variable: ${name}`);
|
|
16
|
+
}
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function mapSearchError(provider, error) {
|
|
20
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21
|
+
return new Error(`Failed to perform ${provider} web search: ${message}`);
|
|
22
|
+
}
|
|
23
|
+
export class DuckDuckGoSearchProvider {
|
|
24
|
+
async search(query, limit = 10) {
|
|
25
|
+
const normalizedLimit = validatePositiveLimit(limit);
|
|
26
|
+
try {
|
|
27
|
+
const response = await axios.get('https://html.duckduckgo.com/html/', {
|
|
28
|
+
params: { q: query },
|
|
29
|
+
headers: {
|
|
30
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
const dom = new JSDOM(response.data);
|
|
34
|
+
const document = dom.window.document;
|
|
35
|
+
const results = [];
|
|
36
|
+
const resultElements = document.querySelectorAll('.result');
|
|
37
|
+
for (let i = 0; i < resultElements.length && results.length < normalizedLimit; i++) {
|
|
38
|
+
const el = resultElements[i];
|
|
39
|
+
const titleEl = el.querySelector('.result__title a.result__a, .result__a, .result__title a.result__url');
|
|
40
|
+
const snippetEl = el.querySelector('.result__snippet');
|
|
41
|
+
if (titleEl && snippetEl) {
|
|
42
|
+
let url = titleEl.getAttribute('href') || '';
|
|
43
|
+
if (url.startsWith('//duckduckgo.com/l/?uddg=')) {
|
|
44
|
+
const urlParams = new URLSearchParams(url.split('?')[1]);
|
|
45
|
+
url = decodeURIComponent(urlParams.get('uddg') || url);
|
|
46
|
+
}
|
|
47
|
+
results.push({
|
|
48
|
+
title: titleEl.textContent?.trim() || '',
|
|
49
|
+
url: url,
|
|
50
|
+
snippet: snippetEl.textContent?.trim() || '',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return results;
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
throw mapSearchError('duckduckgo', error);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export class GoogleSearchProvider {
|
|
62
|
+
async search(query, limit = 10) {
|
|
63
|
+
const normalizedLimit = validatePositiveLimit(limit);
|
|
64
|
+
const apiKey = requireEnvVar('GOOGLE_API_KEY');
|
|
65
|
+
const cx = requireEnvVar('GOOGLE_CSE_ID');
|
|
66
|
+
try {
|
|
67
|
+
const response = await axios.get(GOOGLE_API_ENDPOINT, {
|
|
68
|
+
params: {
|
|
69
|
+
key: apiKey,
|
|
70
|
+
cx,
|
|
71
|
+
q: query,
|
|
72
|
+
num: Math.min(normalizedLimit, 10),
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
const items = Array.isArray(response.data?.items)
|
|
76
|
+
? response.data.items
|
|
77
|
+
: [];
|
|
78
|
+
return items.slice(0, normalizedLimit).map((item) => ({
|
|
79
|
+
title: item.title?.trim() || '',
|
|
80
|
+
url: item.link?.trim() || '',
|
|
81
|
+
snippet: item.snippet?.trim() || '',
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
throw mapSearchError('google', error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export class BingSearchProvider {
|
|
90
|
+
async search(query, limit = 10) {
|
|
91
|
+
const normalizedLimit = validatePositiveLimit(limit);
|
|
92
|
+
const apiKey = requireEnvVar('BING_API_KEY');
|
|
93
|
+
const endpoint = process.env.BING_API_ENDPOINT || BING_API_ENDPOINT;
|
|
94
|
+
try {
|
|
95
|
+
const response = await axios.get(endpoint, {
|
|
96
|
+
params: {
|
|
97
|
+
q: query,
|
|
98
|
+
count: Math.min(normalizedLimit, 50),
|
|
99
|
+
},
|
|
100
|
+
headers: {
|
|
101
|
+
'Ocp-Apim-Subscription-Key': apiKey,
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const values = Array.isArray(response.data?.webPages?.value)
|
|
105
|
+
? response.data.webPages.value
|
|
106
|
+
: [];
|
|
107
|
+
return values.slice(0, normalizedLimit).map((item) => ({
|
|
108
|
+
title: item.name?.trim() || '',
|
|
109
|
+
url: item.url?.trim() || '',
|
|
110
|
+
snippet: item.snippet?.trim() || '',
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
throw mapSearchError('bing', error);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
export class BraveSearchProvider {
|
|
119
|
+
async search(query, limit = 10) {
|
|
120
|
+
const normalizedLimit = validatePositiveLimit(limit);
|
|
121
|
+
const apiKey = requireEnvVar('BRAVE_API_KEY');
|
|
122
|
+
try {
|
|
123
|
+
const response = await axios.get(BRAVE_API_ENDPOINT, {
|
|
124
|
+
params: {
|
|
125
|
+
q: query,
|
|
126
|
+
count: Math.min(normalizedLimit, 20),
|
|
127
|
+
},
|
|
128
|
+
headers: {
|
|
129
|
+
'X-Subscription-Token': apiKey,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
const results = Array.isArray(response.data?.web?.results)
|
|
133
|
+
? response.data.web.results
|
|
134
|
+
: [];
|
|
135
|
+
return results.slice(0, normalizedLimit).map((item) => ({
|
|
136
|
+
title: item.title?.trim() || '',
|
|
137
|
+
url: item.url?.trim() || '',
|
|
138
|
+
snippet: item.description?.trim() || '',
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
throw mapSearchError('brave', error);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export function createSearchProvider(provider = 'duckduckgo') {
|
|
147
|
+
switch (provider) {
|
|
148
|
+
case 'duckduckgo':
|
|
149
|
+
return new DuckDuckGoSearchProvider();
|
|
150
|
+
case 'google':
|
|
151
|
+
return new GoogleSearchProvider();
|
|
152
|
+
case 'bing':
|
|
153
|
+
return new BingSearchProvider();
|
|
154
|
+
case 'brave':
|
|
155
|
+
return new BraveSearchProvider();
|
|
156
|
+
default:
|
|
157
|
+
throw new Error(`Unsupported search provider: ${provider}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=web-search.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"web-search.js","sourceRoot":"","sources":["../../src/lib/web-search.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"author": "Templ Project",
|
|
3
|
+
"bin": {
|
|
4
|
+
"mcp-searchable": "./dist/index.js"
|
|
5
|
+
},
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/templ-project/typescript/issues"
|
|
8
|
+
},
|
|
9
|
+
"description": "Smart Web Extraction MCP Server",
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@jscpd/badge-reporter": "^4.2.2",
|
|
12
|
+
"@templ-project/eslint": "^1.3.0",
|
|
13
|
+
"@templ-project/prettier": "^1.1.0",
|
|
14
|
+
"@templ-project/vitest": "^0.3.2",
|
|
15
|
+
"@types/axios": "^0.14.4",
|
|
16
|
+
"@types/better-sqlite3": "^7.6.13",
|
|
17
|
+
"@types/jsdom": "^28.0.3",
|
|
18
|
+
"@types/mozilla-readability": "^0.2.1",
|
|
19
|
+
"@types/node": "^25.8.0",
|
|
20
|
+
"@types/turndown": "^5.0.6",
|
|
21
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
22
|
+
"esbuild": "^0.28.0",
|
|
23
|
+
"husky": "^9.1.7",
|
|
24
|
+
"jscpd": "^4.2.2",
|
|
25
|
+
"licensee": "^12.0.1",
|
|
26
|
+
"lint-staged": "^17.0.5",
|
|
27
|
+
"npm-run-all2": "^8.0.4",
|
|
28
|
+
"rimraf": "^6.1.3",
|
|
29
|
+
"tsx": "^4.22.0",
|
|
30
|
+
"typedoc": "0.28.19",
|
|
31
|
+
"typedoc-plugin-markdown": "4.11.0",
|
|
32
|
+
"typescript": "^6.0.3"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.0.0",
|
|
36
|
+
"npm": ">=10.0.0"
|
|
37
|
+
},
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"import": "./dist/index.js",
|
|
41
|
+
"default": "./dist/index.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"files": [".npx-install", "dist"],
|
|
45
|
+
"homepage": "https://github.com/templ-project/typescript#readme",
|
|
46
|
+
"keywords": [
|
|
47
|
+
"bootstrap",
|
|
48
|
+
"browser",
|
|
49
|
+
"eslint",
|
|
50
|
+
"esm",
|
|
51
|
+
"import-maps",
|
|
52
|
+
"node",
|
|
53
|
+
"prettier",
|
|
54
|
+
"template",
|
|
55
|
+
"typescript",
|
|
56
|
+
"vitest"
|
|
57
|
+
],
|
|
58
|
+
"license": "MIT",
|
|
59
|
+
"main": "./dist/index.js",
|
|
60
|
+
"module": "./dist/index.js",
|
|
61
|
+
"name": "mcp-searchable",
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "https://github.com/templ-project/typescript.git"
|
|
65
|
+
},
|
|
66
|
+
"scripts": {
|
|
67
|
+
"build": "run-s clean build:esm",
|
|
68
|
+
"build:esm": "tsc -p tsconfig.json",
|
|
69
|
+
"clean": "rimraf .nyc_output coverage dist site",
|
|
70
|
+
"docs": "uv run mkdocs build --strict",
|
|
71
|
+
"duplicate-check": "jscpd src/",
|
|
72
|
+
"format": "prettier --write .",
|
|
73
|
+
"format:check": "prettier --check .",
|
|
74
|
+
"license-check": "licensee --errors-only",
|
|
75
|
+
"lint": "eslint --fix .",
|
|
76
|
+
"lint:check": "eslint .",
|
|
77
|
+
"prebuild": "run-s clean",
|
|
78
|
+
"pretest": "run-s clean build",
|
|
79
|
+
"start": "node dist/index.js",
|
|
80
|
+
"start:debug": "node --inspect-brk dist/index.js",
|
|
81
|
+
"test": "vitest run",
|
|
82
|
+
"test:coverage": "vitest run --coverage",
|
|
83
|
+
"test:watch": "vitest",
|
|
84
|
+
"typecheck": "tsc --noEmit",
|
|
85
|
+
"validate": "run-s format lint duplicate-check test",
|
|
86
|
+
"postbuild": "chmod +x dist/index.js"
|
|
87
|
+
},
|
|
88
|
+
"sideEffects": false,
|
|
89
|
+
"type": "module",
|
|
90
|
+
"version": "1.0.2",
|
|
91
|
+
"dependencies": {
|
|
92
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
93
|
+
"@mozilla/readability": "^0.6.0",
|
|
94
|
+
"axios": "^1.16.1",
|
|
95
|
+
"better-sqlite3": "^12.10.0",
|
|
96
|
+
"jsdom": "^29.1.1",
|
|
97
|
+
"turndown": "^7.2.4"
|
|
98
|
+
}
|
|
99
|
+
}
|