one-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/LICENSE +21 -0
- package/README.md +28 -0
- package/dist/index.cjs +518 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +491 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 zac_ma.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 🚀 OneSearch MCP Server: Web Search & Crawl & Scraper & Extract
|
|
2
|
+
|
|
3
|
+
A Model Context Protocol (MCP) server implementation that integrates with Searxng/Firecrawl/Tavily for web search and scraping capabilities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Web Search, scrape, crawl and extract content from websites.
|
|
8
|
+
- Support multiple search engines and web scrapers: SearXNG, Firecrawl, Tavily, etc.
|
|
9
|
+
- Support for self-hosted: SearXNG, Firecrawl, etc. (see [Deploy](./deploy/README.md))
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```shell
|
|
14
|
+
npm install -g one-search-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```shell
|
|
18
|
+
# Running with npx
|
|
19
|
+
env SEARCH_API_KEY=YOUR_API_KEY SEARCH_API_URL=YOUR_API_URL npx -y one-search-mcp
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Self-host
|
|
23
|
+
|
|
24
|
+
Local deployment of Searxng and Firecrawl, please refer to [Deploy](./deploy/README.md)
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
MIT License - see [LICENSE](./LICENSE) file for details.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,518 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
25
|
+
|
|
26
|
+
// src/index.ts
|
|
27
|
+
var index_exports = {};
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
30
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
31
|
+
|
|
32
|
+
// src/search.ts
|
|
33
|
+
var import_node_url = __toESM(require("url"), 1);
|
|
34
|
+
var import_core = require("@tavily/core");
|
|
35
|
+
async function searxngSearch(apiUrl, params) {
|
|
36
|
+
try {
|
|
37
|
+
const {
|
|
38
|
+
query,
|
|
39
|
+
limit = 10,
|
|
40
|
+
categories = "general",
|
|
41
|
+
engines = "all",
|
|
42
|
+
safeSearch = 0,
|
|
43
|
+
format = "json",
|
|
44
|
+
language = "auto",
|
|
45
|
+
timeRange = "",
|
|
46
|
+
timeout = 1e4,
|
|
47
|
+
apiKey = ""
|
|
48
|
+
} = params;
|
|
49
|
+
const controller = new AbortController();
|
|
50
|
+
const timeoutId = setTimeout(() => controller.abort(), Number(timeout));
|
|
51
|
+
const config2 = {
|
|
52
|
+
q: query,
|
|
53
|
+
pageno: limit,
|
|
54
|
+
categories,
|
|
55
|
+
format,
|
|
56
|
+
safesearch: safeSearch,
|
|
57
|
+
language,
|
|
58
|
+
engines,
|
|
59
|
+
time_range: timeRange
|
|
60
|
+
};
|
|
61
|
+
const endpoint = `${apiUrl}/search`;
|
|
62
|
+
const queryParams = import_node_url.default.format({ query: config2 });
|
|
63
|
+
const headers = {
|
|
64
|
+
"Content-Type": "application/json"
|
|
65
|
+
};
|
|
66
|
+
if (apiKey) {
|
|
67
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
68
|
+
}
|
|
69
|
+
const res = await fetch(`${endpoint}${queryParams}`, {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers,
|
|
72
|
+
signal: controller.signal
|
|
73
|
+
});
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
const result = await res.json();
|
|
76
|
+
if (result.results) {
|
|
77
|
+
const results = result.results.map((item) => {
|
|
78
|
+
const image = item.img_src ? {
|
|
79
|
+
thumbnail: item.thumbnail_src,
|
|
80
|
+
src: item.img_src
|
|
81
|
+
} : null;
|
|
82
|
+
const video = item.iframe_src ? {
|
|
83
|
+
thumbnail: item.thumbnail_src,
|
|
84
|
+
src: item.iframe_src
|
|
85
|
+
} : null;
|
|
86
|
+
return {
|
|
87
|
+
title: item.title,
|
|
88
|
+
snippet: item.content,
|
|
89
|
+
url: item.url,
|
|
90
|
+
source: item.source,
|
|
91
|
+
image,
|
|
92
|
+
video,
|
|
93
|
+
engine: item.engine
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
return {
|
|
97
|
+
results,
|
|
98
|
+
success: true
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
results: [],
|
|
103
|
+
success: false
|
|
104
|
+
};
|
|
105
|
+
} catch (err) {
|
|
106
|
+
process.stdout.write(err?.message ?? "Searxng search error.");
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
var tvly = null;
|
|
111
|
+
async function tavilySearch(query, options) {
|
|
112
|
+
const {
|
|
113
|
+
limit = 10,
|
|
114
|
+
categories = "general",
|
|
115
|
+
timeRange,
|
|
116
|
+
apiKey
|
|
117
|
+
} = options;
|
|
118
|
+
if (!apiKey) {
|
|
119
|
+
throw new Error("Tavily API key is required");
|
|
120
|
+
}
|
|
121
|
+
if (!tvly) {
|
|
122
|
+
tvly = (0, import_core.tavily)({
|
|
123
|
+
apiKey
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
const params = {
|
|
127
|
+
topic: categories,
|
|
128
|
+
timeRange,
|
|
129
|
+
maxResults: limit
|
|
130
|
+
};
|
|
131
|
+
const res = await tvly.search(query, params);
|
|
132
|
+
const results = res.results.map((item) => ({
|
|
133
|
+
title: item.title,
|
|
134
|
+
url: item.url,
|
|
135
|
+
snippet: item.content
|
|
136
|
+
}));
|
|
137
|
+
return {
|
|
138
|
+
results,
|
|
139
|
+
success: true
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// src/index.ts
|
|
144
|
+
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
145
|
+
var SEARCH_TOOL = {
|
|
146
|
+
name: "one_search",
|
|
147
|
+
description: "Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",
|
|
148
|
+
inputSchema: {
|
|
149
|
+
type: "object",
|
|
150
|
+
properties: {
|
|
151
|
+
query: {
|
|
152
|
+
type: "string",
|
|
153
|
+
description: "Search query string"
|
|
154
|
+
},
|
|
155
|
+
limit: {
|
|
156
|
+
type: "number",
|
|
157
|
+
description: "Maximum number of results to return (default: 5)"
|
|
158
|
+
},
|
|
159
|
+
language: {
|
|
160
|
+
type: "string",
|
|
161
|
+
description: "Language code for search results (default: en)"
|
|
162
|
+
},
|
|
163
|
+
categories: {
|
|
164
|
+
type: "string",
|
|
165
|
+
description: "Categories to search for (default: general)"
|
|
166
|
+
},
|
|
167
|
+
timeRange: {
|
|
168
|
+
type: "string",
|
|
169
|
+
description: "Time range for search results (default: all)"
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
required: ["query"]
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
var SCRAPE_TOOL = {
|
|
176
|
+
name: "one_scrape",
|
|
177
|
+
description: "Scrape a single webpage with advanced options for content extraction. Supports various formats including markdown, HTML, and screenshots. Can execute custom actions like clicking or scrolling before scraping.",
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
url: {
|
|
182
|
+
type: "string",
|
|
183
|
+
description: "The URL to scrape"
|
|
184
|
+
},
|
|
185
|
+
formats: {
|
|
186
|
+
type: "array",
|
|
187
|
+
items: {
|
|
188
|
+
type: "string",
|
|
189
|
+
enum: [
|
|
190
|
+
"markdown",
|
|
191
|
+
"html",
|
|
192
|
+
"rawHtml",
|
|
193
|
+
"screenshot",
|
|
194
|
+
"links",
|
|
195
|
+
"screenshot@fullPage",
|
|
196
|
+
"extract"
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
description: "Content formats to extract (default: ['markdown'])"
|
|
200
|
+
},
|
|
201
|
+
onlyMainContent: {
|
|
202
|
+
type: "boolean",
|
|
203
|
+
description: "Extract only the main content, filtering out navigation, footers, etc."
|
|
204
|
+
},
|
|
205
|
+
includeTags: {
|
|
206
|
+
type: "array",
|
|
207
|
+
items: { type: "string" },
|
|
208
|
+
description: "HTML tags to specifically include in extraction"
|
|
209
|
+
},
|
|
210
|
+
excludeTags: {
|
|
211
|
+
type: "array",
|
|
212
|
+
items: { type: "string" },
|
|
213
|
+
description: "HTML tags to exclude from extraction"
|
|
214
|
+
},
|
|
215
|
+
waitFor: {
|
|
216
|
+
type: "number",
|
|
217
|
+
description: "Time in milliseconds to wait for dynamic content to load"
|
|
218
|
+
},
|
|
219
|
+
timeout: {
|
|
220
|
+
type: "number",
|
|
221
|
+
description: "Maximum time in milliseconds to wait for the page to load"
|
|
222
|
+
},
|
|
223
|
+
actions: {
|
|
224
|
+
type: "array",
|
|
225
|
+
items: {
|
|
226
|
+
type: "object",
|
|
227
|
+
properties: {
|
|
228
|
+
type: {
|
|
229
|
+
type: "string",
|
|
230
|
+
enum: [
|
|
231
|
+
"wait",
|
|
232
|
+
"click",
|
|
233
|
+
"screenshot",
|
|
234
|
+
"write",
|
|
235
|
+
"press",
|
|
236
|
+
"scroll",
|
|
237
|
+
"scrape",
|
|
238
|
+
"executeJavascript"
|
|
239
|
+
],
|
|
240
|
+
description: "Type of action to perform"
|
|
241
|
+
},
|
|
242
|
+
selector: {
|
|
243
|
+
type: "string",
|
|
244
|
+
description: "CSS selector for the target element"
|
|
245
|
+
},
|
|
246
|
+
milliseconds: {
|
|
247
|
+
type: "number",
|
|
248
|
+
description: "Time to wait in milliseconds (for wait action)"
|
|
249
|
+
},
|
|
250
|
+
text: {
|
|
251
|
+
type: "string",
|
|
252
|
+
description: "Text to write (for write action)"
|
|
253
|
+
},
|
|
254
|
+
key: {
|
|
255
|
+
type: "string",
|
|
256
|
+
description: "Key to press (for press action)"
|
|
257
|
+
},
|
|
258
|
+
direction: {
|
|
259
|
+
type: "string",
|
|
260
|
+
enum: ["up", "down"],
|
|
261
|
+
description: "Scroll direction"
|
|
262
|
+
},
|
|
263
|
+
script: {
|
|
264
|
+
type: "string",
|
|
265
|
+
description: "JavaScript code to execute"
|
|
266
|
+
},
|
|
267
|
+
fullPage: {
|
|
268
|
+
type: "boolean",
|
|
269
|
+
description: "Take full page screenshot"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
required: ["type"]
|
|
273
|
+
},
|
|
274
|
+
description: "List of actions to perform before scraping"
|
|
275
|
+
},
|
|
276
|
+
extract: {
|
|
277
|
+
type: "object",
|
|
278
|
+
properties: {
|
|
279
|
+
schema: {
|
|
280
|
+
type: "object",
|
|
281
|
+
description: "Schema for structured data extraction"
|
|
282
|
+
},
|
|
283
|
+
systemPrompt: {
|
|
284
|
+
type: "string",
|
|
285
|
+
description: "System prompt for LLM extraction"
|
|
286
|
+
},
|
|
287
|
+
prompt: {
|
|
288
|
+
type: "string",
|
|
289
|
+
description: "User prompt for LLM extraction"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
description: "Configuration for structured data extraction"
|
|
293
|
+
},
|
|
294
|
+
mobile: {
|
|
295
|
+
type: "boolean",
|
|
296
|
+
description: "Use mobile viewport"
|
|
297
|
+
},
|
|
298
|
+
skipTlsVerification: {
|
|
299
|
+
type: "boolean",
|
|
300
|
+
description: "Skip TLS certificate verification"
|
|
301
|
+
},
|
|
302
|
+
removeBase64Images: {
|
|
303
|
+
type: "boolean",
|
|
304
|
+
description: "Remove base64 encoded images from output"
|
|
305
|
+
},
|
|
306
|
+
location: {
|
|
307
|
+
type: "object",
|
|
308
|
+
properties: {
|
|
309
|
+
country: {
|
|
310
|
+
type: "string",
|
|
311
|
+
description: "Country code for geolocation"
|
|
312
|
+
},
|
|
313
|
+
languages: {
|
|
314
|
+
type: "array",
|
|
315
|
+
items: { type: "string" },
|
|
316
|
+
description: "Language codes for content"
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
description: "Location settings for scraping"
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
required: ["url"]
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
var EXTRACT_TOOL = {
|
|
326
|
+
name: "one_extract",
|
|
327
|
+
description: "Extract structured information from web pages using LLM. Supports both cloud AI and self-hosted LLM extraction.",
|
|
328
|
+
inputSchema: {
|
|
329
|
+
type: "object",
|
|
330
|
+
properties: {
|
|
331
|
+
urls: {
|
|
332
|
+
type: "array",
|
|
333
|
+
items: { type: "string" },
|
|
334
|
+
description: "List of URLs to extract information from"
|
|
335
|
+
},
|
|
336
|
+
prompt: {
|
|
337
|
+
type: "string",
|
|
338
|
+
description: "Prompt for the LLM extraction"
|
|
339
|
+
},
|
|
340
|
+
systemPrompt: {
|
|
341
|
+
type: "string",
|
|
342
|
+
description: "System prompt for LLM extraction"
|
|
343
|
+
},
|
|
344
|
+
schema: {
|
|
345
|
+
type: "object",
|
|
346
|
+
description: "JSON schema for structured data extraction"
|
|
347
|
+
},
|
|
348
|
+
allowExternalLinks: {
|
|
349
|
+
type: "boolean",
|
|
350
|
+
description: "Allow extraction from external links"
|
|
351
|
+
},
|
|
352
|
+
enableWebSearch: {
|
|
353
|
+
type: "boolean",
|
|
354
|
+
description: "Enable web search for additional context"
|
|
355
|
+
},
|
|
356
|
+
includeSubdomains: {
|
|
357
|
+
type: "boolean",
|
|
358
|
+
description: "Include subdomains in extraction"
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
required: ["urls"]
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
var server = new import_server.Server(
|
|
365
|
+
{
|
|
366
|
+
name: "one-search-mcp",
|
|
367
|
+
version: "0.0.1"
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
capabilities: {
|
|
371
|
+
tools: {},
|
|
372
|
+
logging: {}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
var SEARCH_API_URL = process.env.SEARCH_API_URL;
|
|
377
|
+
var SEARCH_API_KEY = process.env.SEARCH_API_KEY;
|
|
378
|
+
var SEARCH_PROVIDER = process.env.SEARCH_PROVIDER ?? "searxng";
|
|
379
|
+
if (!SEARCH_API_URL) {
|
|
380
|
+
process.stderr.write("SEARCH_API_URL must be set");
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
var SAFE_SEARCH = process.env.SAFE_SEARCH ?? 0;
|
|
384
|
+
var LIMIT = process.env.LIMIT ?? 10;
|
|
385
|
+
var CATEGORIES = process.env.CATEGORIES ?? "general";
|
|
386
|
+
var ENGINES = process.env.ENGINES ?? "all";
|
|
387
|
+
var FORMAT = process.env.FORMAT ?? "json";
|
|
388
|
+
var LANGUAGE = process.env.LANGUAGE ?? "auto";
|
|
389
|
+
var TIME_RANGE = process.env.TIME_RANGE ?? "";
|
|
390
|
+
var DEFAULT_TIMEOUT = process.env.TIMEOUT ?? 1e4;
|
|
391
|
+
var config = {
|
|
392
|
+
pageno: LIMIT,
|
|
393
|
+
categories: CATEGORIES,
|
|
394
|
+
format: FORMAT,
|
|
395
|
+
safesearch: SAFE_SEARCH,
|
|
396
|
+
language: LANGUAGE,
|
|
397
|
+
engines: ENGINES,
|
|
398
|
+
time_range: TIME_RANGE,
|
|
399
|
+
timeout: DEFAULT_TIMEOUT
|
|
400
|
+
};
|
|
401
|
+
server.setRequestHandler(import_types.ListToolsRequestSchema, async () => ({
|
|
402
|
+
tools: [
|
|
403
|
+
SEARCH_TOOL,
|
|
404
|
+
EXTRACT_TOOL,
|
|
405
|
+
SCRAPE_TOOL
|
|
406
|
+
]
|
|
407
|
+
}));
|
|
408
|
+
server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
409
|
+
const startTime = Date.now();
|
|
410
|
+
try {
|
|
411
|
+
const { name, arguments: args } = request.params;
|
|
412
|
+
if (!args) {
|
|
413
|
+
throw new Error("No arguments provided");
|
|
414
|
+
}
|
|
415
|
+
server.sendLoggingMessage({
|
|
416
|
+
level: "info",
|
|
417
|
+
data: `[${(/* @__PURE__ */ new Date()).toISOString()}] Received request for tool: [${name}]`
|
|
418
|
+
});
|
|
419
|
+
switch (name) {
|
|
420
|
+
case "one_search": {
|
|
421
|
+
if (!checkSearchArgs(args)) {
|
|
422
|
+
throw new Error(`Invalid arguments for tool: [${name}]`);
|
|
423
|
+
}
|
|
424
|
+
try {
|
|
425
|
+
const { results, success } = await processSearch(SEARCH_API_URL, {
|
|
426
|
+
...config,
|
|
427
|
+
...args,
|
|
428
|
+
apiKey: SEARCH_API_KEY ?? ""
|
|
429
|
+
});
|
|
430
|
+
if (!success) {
|
|
431
|
+
throw new Error("Failed to search");
|
|
432
|
+
}
|
|
433
|
+
return {
|
|
434
|
+
results,
|
|
435
|
+
success
|
|
436
|
+
};
|
|
437
|
+
} catch (error) {
|
|
438
|
+
server.sendLoggingMessage({
|
|
439
|
+
level: "error",
|
|
440
|
+
data: `[${(/* @__PURE__ */ new Date()).toISOString()}] Error searching: ${error}`
|
|
441
|
+
});
|
|
442
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
443
|
+
return {
|
|
444
|
+
success: false,
|
|
445
|
+
error: msg
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
default:
|
|
450
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
451
|
+
}
|
|
452
|
+
} catch (error) {
|
|
453
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
454
|
+
server.sendLoggingMessage({
|
|
455
|
+
level: "error",
|
|
456
|
+
data: {
|
|
457
|
+
message: `[${(/* @__PURE__ */ new Date()).toISOString()}] Error processing request: ${msg}`,
|
|
458
|
+
tool: request.params.name,
|
|
459
|
+
arguments: request.params.arguments,
|
|
460
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
461
|
+
duration: Date.now() - startTime
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
return {
|
|
465
|
+
success: false,
|
|
466
|
+
error: error instanceof Error ? error.message : msg
|
|
467
|
+
};
|
|
468
|
+
} finally {
|
|
469
|
+
server.sendLoggingMessage({
|
|
470
|
+
level: "info",
|
|
471
|
+
data: `[${(/* @__PURE__ */ new Date()).toISOString()}] Request completed in ${Date.now() - startTime}ms`
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
async function processSearch(apiUrl, args) {
|
|
476
|
+
switch (SEARCH_PROVIDER) {
|
|
477
|
+
case "searxng":
|
|
478
|
+
return await searxngSearch(apiUrl, {
|
|
479
|
+
...config,
|
|
480
|
+
...args,
|
|
481
|
+
apiKey: SEARCH_API_KEY
|
|
482
|
+
});
|
|
483
|
+
case "tavily":
|
|
484
|
+
return await tavilySearch(apiUrl, {
|
|
485
|
+
...config,
|
|
486
|
+
...args,
|
|
487
|
+
apiKey: SEARCH_API_KEY
|
|
488
|
+
});
|
|
489
|
+
default:
|
|
490
|
+
throw new Error(`Unsupported search provider: ${SEARCH_PROVIDER}`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function checkSearchArgs(args) {
|
|
494
|
+
return typeof args === "object" && args !== null && "query" in args && typeof args.query === "string";
|
|
495
|
+
}
|
|
496
|
+
async function runServer() {
|
|
497
|
+
try {
|
|
498
|
+
process.stdout.write("Starting OneSearch MCP server...\n");
|
|
499
|
+
const transport = new import_stdio.StdioServerTransport();
|
|
500
|
+
await server.connect(transport);
|
|
501
|
+
server.sendLoggingMessage({
|
|
502
|
+
level: "info",
|
|
503
|
+
data: "OneSearch MCP server started"
|
|
504
|
+
});
|
|
505
|
+
} catch (error) {
|
|
506
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
507
|
+
process.stderr.write(`Error starting server: ${msg}
|
|
508
|
+
`);
|
|
509
|
+
process.exit(1);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
runServer().catch((error) => {
|
|
513
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
514
|
+
process.stderr.write(`Error running server: ${msg}
|
|
515
|
+
`);
|
|
516
|
+
process.exit(1);
|
|
517
|
+
});
|
|
518
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/search.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js';\nimport { ISearchRequestOptions, ISearchResponse, Provider } from './interface.js';\nimport { searxngSearch, tavilySearch } from './search.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n\n// export interface\nexport * from './interface.js';\n\n// tools definition\nconst SEARCH_TOOL: Tool = {\n name: 'one_search',\n description:\n 'Search and retrieve content from web pages. ' +\n 'Returns SERP results by default (url, title, description).',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query string',\n },\n limit: {\n type: 'number',\n description: 'Maximum number of results to return (default: 5)',\n },\n language: {\n type: 'string',\n description: 'Language code for search results (default: en)',\n },\n categories: {\n type: 'string',\n description: 'Categories to search for (default: general)',\n },\n timeRange: {\n type: 'string',\n description: 'Time range for search results (default: all)',\n },\n },\n required: ['query'],\n },\n};\n\nconst SCRAPE_TOOL: Tool = {\n name: 'one_scrape',\n description:\n 'Scrape a single webpage with advanced options for content extraction. ' +\n 'Supports various formats including markdown, HTML, and screenshots. ' +\n 'Can execute custom actions like clicking or scrolling before scraping.',\n inputSchema: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'The URL to scrape',\n },\n formats: {\n type: 'array',\n items: {\n type: 'string',\n enum: [\n 'markdown',\n 'html',\n 'rawHtml',\n 'screenshot',\n 'links',\n 'screenshot@fullPage',\n 'extract',\n ],\n },\n description: \"Content formats to extract (default: ['markdown'])\",\n },\n onlyMainContent: {\n type: 'boolean',\n description:\n 'Extract only the main content, filtering out navigation, footers, etc.',\n },\n includeTags: {\n type: 'array',\n items: { type: 'string' },\n description: 'HTML tags to specifically include in extraction',\n },\n excludeTags: {\n type: 'array',\n items: { type: 'string' },\n description: 'HTML tags to exclude from extraction',\n },\n waitFor: {\n type: 'number',\n description: 'Time in milliseconds to wait for dynamic content to load',\n },\n timeout: {\n type: 'number',\n description:\n 'Maximum time in milliseconds to wait for the page to load',\n },\n actions: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: [\n 'wait',\n 'click',\n 'screenshot',\n 'write',\n 'press',\n 'scroll',\n 'scrape',\n 'executeJavascript',\n ],\n description: 'Type of action to perform',\n },\n selector: {\n type: 'string',\n description: 'CSS selector for the target element',\n },\n milliseconds: {\n type: 'number',\n description: 'Time to wait in milliseconds (for wait action)',\n },\n text: {\n type: 'string',\n description: 'Text to write (for write action)',\n },\n key: {\n type: 'string',\n description: 'Key to press (for press action)',\n },\n direction: {\n type: 'string',\n enum: ['up', 'down'],\n description: 'Scroll direction',\n },\n script: {\n type: 'string',\n description: 'JavaScript code to execute',\n },\n fullPage: {\n type: 'boolean',\n description: 'Take full page screenshot',\n },\n },\n required: ['type'],\n },\n description: 'List of actions to perform before scraping',\n },\n extract: {\n type: 'object',\n properties: {\n schema: {\n type: 'object',\n description: 'Schema for structured data extraction',\n },\n systemPrompt: {\n type: 'string',\n description: 'System prompt for LLM extraction',\n },\n prompt: {\n type: 'string',\n description: 'User prompt for LLM extraction',\n },\n },\n description: 'Configuration for structured data extraction',\n },\n mobile: {\n type: 'boolean',\n description: 'Use mobile viewport',\n },\n skipTlsVerification: {\n type: 'boolean',\n description: 'Skip TLS certificate verification',\n },\n removeBase64Images: {\n type: 'boolean',\n description: 'Remove base64 encoded images from output',\n },\n location: {\n type: 'object',\n properties: {\n country: {\n type: 'string',\n description: 'Country code for geolocation',\n },\n languages: {\n type: 'array',\n items: { type: 'string' },\n description: 'Language codes for content',\n },\n },\n description: 'Location settings for scraping',\n },\n },\n required: ['url'],\n },\n};\n\nconst EXTRACT_TOOL: Tool = {\n name: 'one_extract',\n description:\n 'Extract structured information from web pages using LLM. ' +\n 'Supports both cloud AI and self-hosted LLM extraction.',\n inputSchema: {\n type: 'object',\n properties: {\n urls: {\n type: 'array',\n items: { type: 'string' },\n description: 'List of URLs to extract information from',\n },\n prompt: {\n type: 'string',\n description: 'Prompt for the LLM extraction',\n },\n systemPrompt: {\n type: 'string',\n description: 'System prompt for LLM extraction',\n },\n schema: {\n type: 'object',\n description: 'JSON schema for structured data extraction',\n },\n allowExternalLinks: {\n type: 'boolean',\n description: 'Allow extraction from external links',\n },\n enableWebSearch: {\n type: 'boolean',\n description: 'Enable web search for additional context',\n },\n includeSubdomains: {\n type: 'boolean',\n description: 'Include subdomains in extraction',\n },\n },\n required: ['urls'],\n },\n};\n\n// Server implementation\nconst server = new Server(\n {\n name: 'one-search-mcp',\n version: '0.0.1',\n },\n {\n capabilities: {\n tools: {},\n logging: {},\n },\n },\n);\n\n// searxng api\nconst SEARCH_API_URL = process.env.SEARCH_API_URL;\nconst SEARCH_API_KEY = process.env.SEARCH_API_KEY;\nconst SEARCH_PROVIDER: Provider = process.env.SEARCH_PROVIDER as Provider ?? 'searxng';\n\nif (!SEARCH_API_URL) {\n process.stderr.write('SEARCH_API_URL must be set');\n process.exit(1);\n}\n\n// query params\nconst SAFE_SEARCH = process.env.SAFE_SEARCH ?? 0;\nconst LIMIT = process.env.LIMIT ?? 10;\nconst CATEGORIES = process.env.CATEGORIES ?? 'general';\nconst ENGINES = process.env.ENGINES ?? 'all';\nconst FORMAT = process.env.FORMAT ?? 'json';\nconst LANGUAGE = process.env.LANGUAGE ?? 'auto';\nconst TIME_RANGE = process.env.TIME_RANGE ?? '';\nconst DEFAULT_TIMEOUT = process.env.TIMEOUT ?? 10000;\n\nconst config = {\n pageno: LIMIT,\n categories: CATEGORIES,\n format: FORMAT,\n safesearch: SAFE_SEARCH,\n language: LANGUAGE,\n engines: ENGINES,\n time_range: TIME_RANGE,\n timeout: DEFAULT_TIMEOUT,\n};\n\n// Tool handlers\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n SEARCH_TOOL,\n EXTRACT_TOOL,\n SCRAPE_TOOL,\n ],\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const startTime = Date.now();\n\n try {\n const { name, arguments: args } = request.params;\n\n if (!args) {\n throw new Error('No arguments provided');\n }\n \n server.sendLoggingMessage({\n level: 'info',\n data: `[${new Date().toISOString()}] Received request for tool: [${name}]`,\n });\n \n switch (name) {\n case 'one_search': {\n // check args.\n if (!checkSearchArgs(args)) {\n throw new Error(`Invalid arguments for tool: [${name}]`);\n }\n try {\n const { results, success } = await processSearch(SEARCH_API_URL, {\n ...config,\n ...args,\n apiKey: SEARCH_API_KEY ?? '',\n });\n if (!success) {\n throw new Error('Failed to search');\n }\n return {\n results,\n success,\n };\n } catch (error) {\n server.sendLoggingMessage({\n level: 'error',\n data: `[${new Date().toISOString()}] Error searching: ${error}`,\n });\n const msg = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n error: msg,\n };\n }\n }\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n } catch(error) {\n const msg = error instanceof Error ? error.message : String(error);\n server.sendLoggingMessage({\n level: 'error',\n data: {\n message: `[${new Date().toISOString()}] Error processing request: ${msg}`,\n tool: request.params.name,\n arguments: request.params.arguments,\n timestamp: new Date().toISOString(),\n duration: Date.now() - startTime,\n },\n });\n return {\n success: false,\n error: error instanceof Error ? error.message : msg,\n };\n } finally {\n server.sendLoggingMessage({\n level: 'info',\n data: `[${new Date().toISOString()}] Request completed in ${Date.now() - startTime}ms`,\n });\n }\n});\n\nasync function processSearch(apiUrl: string, args: ISearchRequestOptions): Promise<ISearchResponse> {\n switch (SEARCH_PROVIDER) {\n case 'searxng':\n return await searxngSearch(apiUrl, {\n ...config,\n ...args,\n apiKey: SEARCH_API_KEY,\n });\n case 'tavily':\n return await tavilySearch(apiUrl, {\n ...config,\n ...args,\n apiKey: SEARCH_API_KEY,\n });\n default:\n throw new Error(`Unsupported search provider: ${SEARCH_PROVIDER}`);\n }\n}\n\nfunction checkSearchArgs(args: unknown): args is ISearchRequestOptions {\n return (\n typeof args === 'object' &&\n args !== null &&\n 'query' in args &&\n typeof args.query === 'string'\n );\n}\n\nasync function runServer() {\n try {\n process.stdout.write('Starting OneSearch MCP server...\\n');\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n server.sendLoggingMessage({\n level: 'info',\n data: 'OneSearch MCP server started',\n });\n\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n process.stderr.write(`Error starting server: ${msg}\\n`);\n process.exit(1);\n }\n}\n\n// run server\nrunServer().catch((error) => {\n const msg = error instanceof Error ? error.message : String(error);\n process.stderr.write(`Error running server: ${msg}\\n`);\n process.exit(1);\n});\n","import url from 'node:url';\nimport { tavily, TavilyClient, TavilySearchOptions } from '@tavily/core';\nimport { ISearchRequestOptions, ISearchResponse, ISearchResponseResult } from './interface.js';\n\n/**\n * SearxNG Search API\n * - https://docs.searxng.org/dev/search_api.html\n */\nexport async function searxngSearch(apiUrl: string, params: ISearchRequestOptions): Promise<ISearchResponse> {\n try {\n const {\n query,\n limit = 10,\n categories = 'general',\n engines = 'all',\n safeSearch = 0,\n format = 'json',\n language = 'auto',\n timeRange = '',\n timeout = 10000,\n apiKey = '',\n } = params;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), Number(timeout));\n\n const config = {\n q: query,\n pageno: limit,\n categories,\n format,\n safesearch: safeSearch,\n language,\n engines,\n time_range: timeRange,\n };\n\n const endpoint = `${apiUrl}/search`;\n\n const queryParams = url.format({ query: config });\n\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n };\n\n if (apiKey) {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n const res = await fetch(`${endpoint}${queryParams}`, {\n method: 'POST',\n headers,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n const result = await res.json();\n if (result.results) {\n const results: ISearchResponseResult[] = result.results.map((item: Record<string, unknown>) => {\n const image = item.img_src ? {\n thumbnail: item.thumbnail_src,\n src: item.img_src,\n } : null;\n const video = item.iframe_src ? {\n thumbnail: item.thumbnail_src,\n src: item.iframe_src,\n } : null;\n return {\n title: item.title,\n snippet: item.content,\n url: item.url,\n source: item.source,\n image,\n video,\n engine: item.engine,\n };\n });\n return {\n results,\n success: true,\n };\n }\n return {\n results: [],\n success: false,\n };\n } catch (err: any) {\n process.stdout.write(err?.message ?? 'Searxng search error.');\n throw err;\n }\n}\n\n\nlet tvly: TavilyClient | null = null;\nexport async function tavilySearch(query: string, options: ISearchRequestOptions): Promise<ISearchResponse> {\n const {\n limit = 10,\n categories = 'general',\n timeRange,\n apiKey,\n } = options;\n\n if (!apiKey) {\n throw new Error('Tavily API key is required');\n }\n\n if (!tvly) {\n tvly = tavily({\n apiKey,\n });\n }\n\n const params: TavilySearchOptions = {\n topic: categories as TavilySearchOptions['topic'],\n timeRange: timeRange as TavilySearchOptions['timeRange'],\n maxResults: limit,\n };\n\n const res = await tvly.search(query, params);\n const results = res.results.map(item => ({\n title: item.title,\n url: item.url,\n snippet: item.content,\n }));\n\n return {\n results,\n success: true,\n };\n}\n"],"mappings":"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","names":["config","url"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface IMediaItem {
|
|
2
|
+
thumbnail?: string;
|
|
3
|
+
src?: string;
|
|
4
|
+
}
|
|
5
|
+
interface ISearchRequestOptions {
|
|
6
|
+
query: string;
|
|
7
|
+
limit?: number;
|
|
8
|
+
categories?: string;
|
|
9
|
+
format?: string;
|
|
10
|
+
language?: string;
|
|
11
|
+
engines?: string;
|
|
12
|
+
safeSearch?: number;
|
|
13
|
+
timeRange?: string;
|
|
14
|
+
timeout?: number | string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
}
|
|
17
|
+
interface ISearchResponseResult {
|
|
18
|
+
title: string;
|
|
19
|
+
snippet: string;
|
|
20
|
+
url: string;
|
|
21
|
+
source?: string;
|
|
22
|
+
engine?: string;
|
|
23
|
+
image?: IMediaItem | null;
|
|
24
|
+
video?: IMediaItem | null;
|
|
25
|
+
}
|
|
26
|
+
interface ISearchResponse {
|
|
27
|
+
results: ISearchResponseResult[];
|
|
28
|
+
success: boolean;
|
|
29
|
+
}
|
|
30
|
+
type Provider = 'searxng' | 'google' | 'bing' | 'tavily';
|
|
31
|
+
type SearchTimeRange = 'year' | 'month' | 'week' | 'day';
|
|
32
|
+
|
|
33
|
+
export type { IMediaItem, ISearchRequestOptions, ISearchResponse, ISearchResponseResult, Provider, SearchTimeRange };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
interface IMediaItem {
|
|
2
|
+
thumbnail?: string;
|
|
3
|
+
src?: string;
|
|
4
|
+
}
|
|
5
|
+
interface ISearchRequestOptions {
|
|
6
|
+
query: string;
|
|
7
|
+
limit?: number;
|
|
8
|
+
categories?: string;
|
|
9
|
+
format?: string;
|
|
10
|
+
language?: string;
|
|
11
|
+
engines?: string;
|
|
12
|
+
safeSearch?: number;
|
|
13
|
+
timeRange?: string;
|
|
14
|
+
timeout?: number | string;
|
|
15
|
+
apiKey?: string;
|
|
16
|
+
}
|
|
17
|
+
interface ISearchResponseResult {
|
|
18
|
+
title: string;
|
|
19
|
+
snippet: string;
|
|
20
|
+
url: string;
|
|
21
|
+
source?: string;
|
|
22
|
+
engine?: string;
|
|
23
|
+
image?: IMediaItem | null;
|
|
24
|
+
video?: IMediaItem | null;
|
|
25
|
+
}
|
|
26
|
+
interface ISearchResponse {
|
|
27
|
+
results: ISearchResponseResult[];
|
|
28
|
+
success: boolean;
|
|
29
|
+
}
|
|
30
|
+
type Provider = 'searxng' | 'google' | 'bing' | 'tavily';
|
|
31
|
+
type SearchTimeRange = 'year' | 'month' | 'week' | 'day';
|
|
32
|
+
|
|
33
|
+
export type { IMediaItem, ISearchRequestOptions, ISearchResponse, ISearchResponseResult, Provider, SearchTimeRange };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,491 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
|
|
5
|
+
// src/search.ts
|
|
6
|
+
import url from "node:url";
|
|
7
|
+
import { tavily } from "@tavily/core";
|
|
8
|
+
async function searxngSearch(apiUrl, params) {
|
|
9
|
+
try {
|
|
10
|
+
const {
|
|
11
|
+
query,
|
|
12
|
+
limit = 10,
|
|
13
|
+
categories = "general",
|
|
14
|
+
engines = "all",
|
|
15
|
+
safeSearch = 0,
|
|
16
|
+
format = "json",
|
|
17
|
+
language = "auto",
|
|
18
|
+
timeRange = "",
|
|
19
|
+
timeout = 1e4,
|
|
20
|
+
apiKey = ""
|
|
21
|
+
} = params;
|
|
22
|
+
const controller = new AbortController();
|
|
23
|
+
const timeoutId = setTimeout(() => controller.abort(), Number(timeout));
|
|
24
|
+
const config2 = {
|
|
25
|
+
q: query,
|
|
26
|
+
pageno: limit,
|
|
27
|
+
categories,
|
|
28
|
+
format,
|
|
29
|
+
safesearch: safeSearch,
|
|
30
|
+
language,
|
|
31
|
+
engines,
|
|
32
|
+
time_range: timeRange
|
|
33
|
+
};
|
|
34
|
+
const endpoint = `${apiUrl}/search`;
|
|
35
|
+
const queryParams = url.format({ query: config2 });
|
|
36
|
+
const headers = {
|
|
37
|
+
"Content-Type": "application/json"
|
|
38
|
+
};
|
|
39
|
+
if (apiKey) {
|
|
40
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
41
|
+
}
|
|
42
|
+
const res = await fetch(`${endpoint}${queryParams}`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers,
|
|
45
|
+
signal: controller.signal
|
|
46
|
+
});
|
|
47
|
+
clearTimeout(timeoutId);
|
|
48
|
+
const result = await res.json();
|
|
49
|
+
if (result.results) {
|
|
50
|
+
const results = result.results.map((item) => {
|
|
51
|
+
const image = item.img_src ? {
|
|
52
|
+
thumbnail: item.thumbnail_src,
|
|
53
|
+
src: item.img_src
|
|
54
|
+
} : null;
|
|
55
|
+
const video = item.iframe_src ? {
|
|
56
|
+
thumbnail: item.thumbnail_src,
|
|
57
|
+
src: item.iframe_src
|
|
58
|
+
} : null;
|
|
59
|
+
return {
|
|
60
|
+
title: item.title,
|
|
61
|
+
snippet: item.content,
|
|
62
|
+
url: item.url,
|
|
63
|
+
source: item.source,
|
|
64
|
+
image,
|
|
65
|
+
video,
|
|
66
|
+
engine: item.engine
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
results,
|
|
71
|
+
success: true
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
results: [],
|
|
76
|
+
success: false
|
|
77
|
+
};
|
|
78
|
+
} catch (err) {
|
|
79
|
+
process.stdout.write(err?.message ?? "Searxng search error.");
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
var tvly = null;
|
|
84
|
+
async function tavilySearch(query, options) {
|
|
85
|
+
const {
|
|
86
|
+
limit = 10,
|
|
87
|
+
categories = "general",
|
|
88
|
+
timeRange,
|
|
89
|
+
apiKey
|
|
90
|
+
} = options;
|
|
91
|
+
if (!apiKey) {
|
|
92
|
+
throw new Error("Tavily API key is required");
|
|
93
|
+
}
|
|
94
|
+
if (!tvly) {
|
|
95
|
+
tvly = tavily({
|
|
96
|
+
apiKey
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const params = {
|
|
100
|
+
topic: categories,
|
|
101
|
+
timeRange,
|
|
102
|
+
maxResults: limit
|
|
103
|
+
};
|
|
104
|
+
const res = await tvly.search(query, params);
|
|
105
|
+
const results = res.results.map((item) => ({
|
|
106
|
+
title: item.title,
|
|
107
|
+
url: item.url,
|
|
108
|
+
snippet: item.content
|
|
109
|
+
}));
|
|
110
|
+
return {
|
|
111
|
+
results,
|
|
112
|
+
success: true
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/index.ts
|
|
117
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
118
|
+
var SEARCH_TOOL = {
|
|
119
|
+
name: "one_search",
|
|
120
|
+
description: "Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {
|
|
124
|
+
query: {
|
|
125
|
+
type: "string",
|
|
126
|
+
description: "Search query string"
|
|
127
|
+
},
|
|
128
|
+
limit: {
|
|
129
|
+
type: "number",
|
|
130
|
+
description: "Maximum number of results to return (default: 5)"
|
|
131
|
+
},
|
|
132
|
+
language: {
|
|
133
|
+
type: "string",
|
|
134
|
+
description: "Language code for search results (default: en)"
|
|
135
|
+
},
|
|
136
|
+
categories: {
|
|
137
|
+
type: "string",
|
|
138
|
+
description: "Categories to search for (default: general)"
|
|
139
|
+
},
|
|
140
|
+
timeRange: {
|
|
141
|
+
type: "string",
|
|
142
|
+
description: "Time range for search results (default: all)"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
required: ["query"]
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
var SCRAPE_TOOL = {
|
|
149
|
+
name: "one_scrape",
|
|
150
|
+
description: "Scrape a single webpage with advanced options for content extraction. Supports various formats including markdown, HTML, and screenshots. Can execute custom actions like clicking or scrolling before scraping.",
|
|
151
|
+
inputSchema: {
|
|
152
|
+
type: "object",
|
|
153
|
+
properties: {
|
|
154
|
+
url: {
|
|
155
|
+
type: "string",
|
|
156
|
+
description: "The URL to scrape"
|
|
157
|
+
},
|
|
158
|
+
formats: {
|
|
159
|
+
type: "array",
|
|
160
|
+
items: {
|
|
161
|
+
type: "string",
|
|
162
|
+
enum: [
|
|
163
|
+
"markdown",
|
|
164
|
+
"html",
|
|
165
|
+
"rawHtml",
|
|
166
|
+
"screenshot",
|
|
167
|
+
"links",
|
|
168
|
+
"screenshot@fullPage",
|
|
169
|
+
"extract"
|
|
170
|
+
]
|
|
171
|
+
},
|
|
172
|
+
description: "Content formats to extract (default: ['markdown'])"
|
|
173
|
+
},
|
|
174
|
+
onlyMainContent: {
|
|
175
|
+
type: "boolean",
|
|
176
|
+
description: "Extract only the main content, filtering out navigation, footers, etc."
|
|
177
|
+
},
|
|
178
|
+
includeTags: {
|
|
179
|
+
type: "array",
|
|
180
|
+
items: { type: "string" },
|
|
181
|
+
description: "HTML tags to specifically include in extraction"
|
|
182
|
+
},
|
|
183
|
+
excludeTags: {
|
|
184
|
+
type: "array",
|
|
185
|
+
items: { type: "string" },
|
|
186
|
+
description: "HTML tags to exclude from extraction"
|
|
187
|
+
},
|
|
188
|
+
waitFor: {
|
|
189
|
+
type: "number",
|
|
190
|
+
description: "Time in milliseconds to wait for dynamic content to load"
|
|
191
|
+
},
|
|
192
|
+
timeout: {
|
|
193
|
+
type: "number",
|
|
194
|
+
description: "Maximum time in milliseconds to wait for the page to load"
|
|
195
|
+
},
|
|
196
|
+
actions: {
|
|
197
|
+
type: "array",
|
|
198
|
+
items: {
|
|
199
|
+
type: "object",
|
|
200
|
+
properties: {
|
|
201
|
+
type: {
|
|
202
|
+
type: "string",
|
|
203
|
+
enum: [
|
|
204
|
+
"wait",
|
|
205
|
+
"click",
|
|
206
|
+
"screenshot",
|
|
207
|
+
"write",
|
|
208
|
+
"press",
|
|
209
|
+
"scroll",
|
|
210
|
+
"scrape",
|
|
211
|
+
"executeJavascript"
|
|
212
|
+
],
|
|
213
|
+
description: "Type of action to perform"
|
|
214
|
+
},
|
|
215
|
+
selector: {
|
|
216
|
+
type: "string",
|
|
217
|
+
description: "CSS selector for the target element"
|
|
218
|
+
},
|
|
219
|
+
milliseconds: {
|
|
220
|
+
type: "number",
|
|
221
|
+
description: "Time to wait in milliseconds (for wait action)"
|
|
222
|
+
},
|
|
223
|
+
text: {
|
|
224
|
+
type: "string",
|
|
225
|
+
description: "Text to write (for write action)"
|
|
226
|
+
},
|
|
227
|
+
key: {
|
|
228
|
+
type: "string",
|
|
229
|
+
description: "Key to press (for press action)"
|
|
230
|
+
},
|
|
231
|
+
direction: {
|
|
232
|
+
type: "string",
|
|
233
|
+
enum: ["up", "down"],
|
|
234
|
+
description: "Scroll direction"
|
|
235
|
+
},
|
|
236
|
+
script: {
|
|
237
|
+
type: "string",
|
|
238
|
+
description: "JavaScript code to execute"
|
|
239
|
+
},
|
|
240
|
+
fullPage: {
|
|
241
|
+
type: "boolean",
|
|
242
|
+
description: "Take full page screenshot"
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
required: ["type"]
|
|
246
|
+
},
|
|
247
|
+
description: "List of actions to perform before scraping"
|
|
248
|
+
},
|
|
249
|
+
extract: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
schema: {
|
|
253
|
+
type: "object",
|
|
254
|
+
description: "Schema for structured data extraction"
|
|
255
|
+
},
|
|
256
|
+
systemPrompt: {
|
|
257
|
+
type: "string",
|
|
258
|
+
description: "System prompt for LLM extraction"
|
|
259
|
+
},
|
|
260
|
+
prompt: {
|
|
261
|
+
type: "string",
|
|
262
|
+
description: "User prompt for LLM extraction"
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
description: "Configuration for structured data extraction"
|
|
266
|
+
},
|
|
267
|
+
mobile: {
|
|
268
|
+
type: "boolean",
|
|
269
|
+
description: "Use mobile viewport"
|
|
270
|
+
},
|
|
271
|
+
skipTlsVerification: {
|
|
272
|
+
type: "boolean",
|
|
273
|
+
description: "Skip TLS certificate verification"
|
|
274
|
+
},
|
|
275
|
+
removeBase64Images: {
|
|
276
|
+
type: "boolean",
|
|
277
|
+
description: "Remove base64 encoded images from output"
|
|
278
|
+
},
|
|
279
|
+
location: {
|
|
280
|
+
type: "object",
|
|
281
|
+
properties: {
|
|
282
|
+
country: {
|
|
283
|
+
type: "string",
|
|
284
|
+
description: "Country code for geolocation"
|
|
285
|
+
},
|
|
286
|
+
languages: {
|
|
287
|
+
type: "array",
|
|
288
|
+
items: { type: "string" },
|
|
289
|
+
description: "Language codes for content"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
description: "Location settings for scraping"
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
required: ["url"]
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
var EXTRACT_TOOL = {
|
|
299
|
+
name: "one_extract",
|
|
300
|
+
description: "Extract structured information from web pages using LLM. Supports both cloud AI and self-hosted LLM extraction.",
|
|
301
|
+
inputSchema: {
|
|
302
|
+
type: "object",
|
|
303
|
+
properties: {
|
|
304
|
+
urls: {
|
|
305
|
+
type: "array",
|
|
306
|
+
items: { type: "string" },
|
|
307
|
+
description: "List of URLs to extract information from"
|
|
308
|
+
},
|
|
309
|
+
prompt: {
|
|
310
|
+
type: "string",
|
|
311
|
+
description: "Prompt for the LLM extraction"
|
|
312
|
+
},
|
|
313
|
+
systemPrompt: {
|
|
314
|
+
type: "string",
|
|
315
|
+
description: "System prompt for LLM extraction"
|
|
316
|
+
},
|
|
317
|
+
schema: {
|
|
318
|
+
type: "object",
|
|
319
|
+
description: "JSON schema for structured data extraction"
|
|
320
|
+
},
|
|
321
|
+
allowExternalLinks: {
|
|
322
|
+
type: "boolean",
|
|
323
|
+
description: "Allow extraction from external links"
|
|
324
|
+
},
|
|
325
|
+
enableWebSearch: {
|
|
326
|
+
type: "boolean",
|
|
327
|
+
description: "Enable web search for additional context"
|
|
328
|
+
},
|
|
329
|
+
includeSubdomains: {
|
|
330
|
+
type: "boolean",
|
|
331
|
+
description: "Include subdomains in extraction"
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
required: ["urls"]
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
var server = new Server(
|
|
338
|
+
{
|
|
339
|
+
name: "one-search-mcp",
|
|
340
|
+
version: "0.0.1"
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
capabilities: {
|
|
344
|
+
tools: {},
|
|
345
|
+
logging: {}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
);
|
|
349
|
+
var SEARCH_API_URL = process.env.SEARCH_API_URL;
|
|
350
|
+
var SEARCH_API_KEY = process.env.SEARCH_API_KEY;
|
|
351
|
+
var SEARCH_PROVIDER = process.env.SEARCH_PROVIDER ?? "searxng";
|
|
352
|
+
if (!SEARCH_API_URL) {
|
|
353
|
+
process.stderr.write("SEARCH_API_URL must be set");
|
|
354
|
+
process.exit(1);
|
|
355
|
+
}
|
|
356
|
+
var SAFE_SEARCH = process.env.SAFE_SEARCH ?? 0;
|
|
357
|
+
var LIMIT = process.env.LIMIT ?? 10;
|
|
358
|
+
var CATEGORIES = process.env.CATEGORIES ?? "general";
|
|
359
|
+
var ENGINES = process.env.ENGINES ?? "all";
|
|
360
|
+
var FORMAT = process.env.FORMAT ?? "json";
|
|
361
|
+
var LANGUAGE = process.env.LANGUAGE ?? "auto";
|
|
362
|
+
var TIME_RANGE = process.env.TIME_RANGE ?? "";
|
|
363
|
+
var DEFAULT_TIMEOUT = process.env.TIMEOUT ?? 1e4;
|
|
364
|
+
var config = {
|
|
365
|
+
pageno: LIMIT,
|
|
366
|
+
categories: CATEGORIES,
|
|
367
|
+
format: FORMAT,
|
|
368
|
+
safesearch: SAFE_SEARCH,
|
|
369
|
+
language: LANGUAGE,
|
|
370
|
+
engines: ENGINES,
|
|
371
|
+
time_range: TIME_RANGE,
|
|
372
|
+
timeout: DEFAULT_TIMEOUT
|
|
373
|
+
};
|
|
374
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
375
|
+
tools: [
|
|
376
|
+
SEARCH_TOOL,
|
|
377
|
+
EXTRACT_TOOL,
|
|
378
|
+
SCRAPE_TOOL
|
|
379
|
+
]
|
|
380
|
+
}));
|
|
381
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
382
|
+
const startTime = Date.now();
|
|
383
|
+
try {
|
|
384
|
+
const { name, arguments: args } = request.params;
|
|
385
|
+
if (!args) {
|
|
386
|
+
throw new Error("No arguments provided");
|
|
387
|
+
}
|
|
388
|
+
server.sendLoggingMessage({
|
|
389
|
+
level: "info",
|
|
390
|
+
data: `[${(/* @__PURE__ */ new Date()).toISOString()}] Received request for tool: [${name}]`
|
|
391
|
+
});
|
|
392
|
+
switch (name) {
|
|
393
|
+
case "one_search": {
|
|
394
|
+
if (!checkSearchArgs(args)) {
|
|
395
|
+
throw new Error(`Invalid arguments for tool: [${name}]`);
|
|
396
|
+
}
|
|
397
|
+
try {
|
|
398
|
+
const { results, success } = await processSearch(SEARCH_API_URL, {
|
|
399
|
+
...config,
|
|
400
|
+
...args,
|
|
401
|
+
apiKey: SEARCH_API_KEY ?? ""
|
|
402
|
+
});
|
|
403
|
+
if (!success) {
|
|
404
|
+
throw new Error("Failed to search");
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
results,
|
|
408
|
+
success
|
|
409
|
+
};
|
|
410
|
+
} catch (error) {
|
|
411
|
+
server.sendLoggingMessage({
|
|
412
|
+
level: "error",
|
|
413
|
+
data: `[${(/* @__PURE__ */ new Date()).toISOString()}] Error searching: ${error}`
|
|
414
|
+
});
|
|
415
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
416
|
+
return {
|
|
417
|
+
success: false,
|
|
418
|
+
error: msg
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
default:
|
|
423
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
424
|
+
}
|
|
425
|
+
} catch (error) {
|
|
426
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
427
|
+
server.sendLoggingMessage({
|
|
428
|
+
level: "error",
|
|
429
|
+
data: {
|
|
430
|
+
message: `[${(/* @__PURE__ */ new Date()).toISOString()}] Error processing request: ${msg}`,
|
|
431
|
+
tool: request.params.name,
|
|
432
|
+
arguments: request.params.arguments,
|
|
433
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
434
|
+
duration: Date.now() - startTime
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
return {
|
|
438
|
+
success: false,
|
|
439
|
+
error: error instanceof Error ? error.message : msg
|
|
440
|
+
};
|
|
441
|
+
} finally {
|
|
442
|
+
server.sendLoggingMessage({
|
|
443
|
+
level: "info",
|
|
444
|
+
data: `[${(/* @__PURE__ */ new Date()).toISOString()}] Request completed in ${Date.now() - startTime}ms`
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
async function processSearch(apiUrl, args) {
|
|
449
|
+
switch (SEARCH_PROVIDER) {
|
|
450
|
+
case "searxng":
|
|
451
|
+
return await searxngSearch(apiUrl, {
|
|
452
|
+
...config,
|
|
453
|
+
...args,
|
|
454
|
+
apiKey: SEARCH_API_KEY
|
|
455
|
+
});
|
|
456
|
+
case "tavily":
|
|
457
|
+
return await tavilySearch(apiUrl, {
|
|
458
|
+
...config,
|
|
459
|
+
...args,
|
|
460
|
+
apiKey: SEARCH_API_KEY
|
|
461
|
+
});
|
|
462
|
+
default:
|
|
463
|
+
throw new Error(`Unsupported search provider: ${SEARCH_PROVIDER}`);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function checkSearchArgs(args) {
|
|
467
|
+
return typeof args === "object" && args !== null && "query" in args && typeof args.query === "string";
|
|
468
|
+
}
|
|
469
|
+
async function runServer() {
|
|
470
|
+
try {
|
|
471
|
+
process.stdout.write("Starting OneSearch MCP server...\n");
|
|
472
|
+
const transport = new StdioServerTransport();
|
|
473
|
+
await server.connect(transport);
|
|
474
|
+
server.sendLoggingMessage({
|
|
475
|
+
level: "info",
|
|
476
|
+
data: "OneSearch MCP server started"
|
|
477
|
+
});
|
|
478
|
+
} catch (error) {
|
|
479
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
480
|
+
process.stderr.write(`Error starting server: ${msg}
|
|
481
|
+
`);
|
|
482
|
+
process.exit(1);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
runServer().catch((error) => {
|
|
486
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
487
|
+
process.stderr.write(`Error running server: ${msg}
|
|
488
|
+
`);
|
|
489
|
+
process.exit(1);
|
|
490
|
+
});
|
|
491
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/search.ts"],"sourcesContent":["import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { CallToolRequestSchema, ListToolsRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js';\nimport { ISearchRequestOptions, ISearchResponse, Provider } from './interface.js';\nimport { searxngSearch, tavilySearch } from './search.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\n\n// export interface\nexport * from './interface.js';\n\n// tools definition\nconst SEARCH_TOOL: Tool = {\n name: 'one_search',\n description:\n 'Search and retrieve content from web pages. ' +\n 'Returns SERP results by default (url, title, description).',\n inputSchema: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Search query string',\n },\n limit: {\n type: 'number',\n description: 'Maximum number of results to return (default: 5)',\n },\n language: {\n type: 'string',\n description: 'Language code for search results (default: en)',\n },\n categories: {\n type: 'string',\n description: 'Categories to search for (default: general)',\n },\n timeRange: {\n type: 'string',\n description: 'Time range for search results (default: all)',\n },\n },\n required: ['query'],\n },\n};\n\nconst SCRAPE_TOOL: Tool = {\n name: 'one_scrape',\n description:\n 'Scrape a single webpage with advanced options for content extraction. ' +\n 'Supports various formats including markdown, HTML, and screenshots. ' +\n 'Can execute custom actions like clicking or scrolling before scraping.',\n inputSchema: {\n type: 'object',\n properties: {\n url: {\n type: 'string',\n description: 'The URL to scrape',\n },\n formats: {\n type: 'array',\n items: {\n type: 'string',\n enum: [\n 'markdown',\n 'html',\n 'rawHtml',\n 'screenshot',\n 'links',\n 'screenshot@fullPage',\n 'extract',\n ],\n },\n description: \"Content formats to extract (default: ['markdown'])\",\n },\n onlyMainContent: {\n type: 'boolean',\n description:\n 'Extract only the main content, filtering out navigation, footers, etc.',\n },\n includeTags: {\n type: 'array',\n items: { type: 'string' },\n description: 'HTML tags to specifically include in extraction',\n },\n excludeTags: {\n type: 'array',\n items: { type: 'string' },\n description: 'HTML tags to exclude from extraction',\n },\n waitFor: {\n type: 'number',\n description: 'Time in milliseconds to wait for dynamic content to load',\n },\n timeout: {\n type: 'number',\n description:\n 'Maximum time in milliseconds to wait for the page to load',\n },\n actions: {\n type: 'array',\n items: {\n type: 'object',\n properties: {\n type: {\n type: 'string',\n enum: [\n 'wait',\n 'click',\n 'screenshot',\n 'write',\n 'press',\n 'scroll',\n 'scrape',\n 'executeJavascript',\n ],\n description: 'Type of action to perform',\n },\n selector: {\n type: 'string',\n description: 'CSS selector for the target element',\n },\n milliseconds: {\n type: 'number',\n description: 'Time to wait in milliseconds (for wait action)',\n },\n text: {\n type: 'string',\n description: 'Text to write (for write action)',\n },\n key: {\n type: 'string',\n description: 'Key to press (for press action)',\n },\n direction: {\n type: 'string',\n enum: ['up', 'down'],\n description: 'Scroll direction',\n },\n script: {\n type: 'string',\n description: 'JavaScript code to execute',\n },\n fullPage: {\n type: 'boolean',\n description: 'Take full page screenshot',\n },\n },\n required: ['type'],\n },\n description: 'List of actions to perform before scraping',\n },\n extract: {\n type: 'object',\n properties: {\n schema: {\n type: 'object',\n description: 'Schema for structured data extraction',\n },\n systemPrompt: {\n type: 'string',\n description: 'System prompt for LLM extraction',\n },\n prompt: {\n type: 'string',\n description: 'User prompt for LLM extraction',\n },\n },\n description: 'Configuration for structured data extraction',\n },\n mobile: {\n type: 'boolean',\n description: 'Use mobile viewport',\n },\n skipTlsVerification: {\n type: 'boolean',\n description: 'Skip TLS certificate verification',\n },\n removeBase64Images: {\n type: 'boolean',\n description: 'Remove base64 encoded images from output',\n },\n location: {\n type: 'object',\n properties: {\n country: {\n type: 'string',\n description: 'Country code for geolocation',\n },\n languages: {\n type: 'array',\n items: { type: 'string' },\n description: 'Language codes for content',\n },\n },\n description: 'Location settings for scraping',\n },\n },\n required: ['url'],\n },\n};\n\nconst EXTRACT_TOOL: Tool = {\n name: 'one_extract',\n description:\n 'Extract structured information from web pages using LLM. ' +\n 'Supports both cloud AI and self-hosted LLM extraction.',\n inputSchema: {\n type: 'object',\n properties: {\n urls: {\n type: 'array',\n items: { type: 'string' },\n description: 'List of URLs to extract information from',\n },\n prompt: {\n type: 'string',\n description: 'Prompt for the LLM extraction',\n },\n systemPrompt: {\n type: 'string',\n description: 'System prompt for LLM extraction',\n },\n schema: {\n type: 'object',\n description: 'JSON schema for structured data extraction',\n },\n allowExternalLinks: {\n type: 'boolean',\n description: 'Allow extraction from external links',\n },\n enableWebSearch: {\n type: 'boolean',\n description: 'Enable web search for additional context',\n },\n includeSubdomains: {\n type: 'boolean',\n description: 'Include subdomains in extraction',\n },\n },\n required: ['urls'],\n },\n};\n\n// Server implementation\nconst server = new Server(\n {\n name: 'one-search-mcp',\n version: '0.0.1',\n },\n {\n capabilities: {\n tools: {},\n logging: {},\n },\n },\n);\n\n// searxng api\nconst SEARCH_API_URL = process.env.SEARCH_API_URL;\nconst SEARCH_API_KEY = process.env.SEARCH_API_KEY;\nconst SEARCH_PROVIDER: Provider = process.env.SEARCH_PROVIDER as Provider ?? 'searxng';\n\nif (!SEARCH_API_URL) {\n process.stderr.write('SEARCH_API_URL must be set');\n process.exit(1);\n}\n\n// query params\nconst SAFE_SEARCH = process.env.SAFE_SEARCH ?? 0;\nconst LIMIT = process.env.LIMIT ?? 10;\nconst CATEGORIES = process.env.CATEGORIES ?? 'general';\nconst ENGINES = process.env.ENGINES ?? 'all';\nconst FORMAT = process.env.FORMAT ?? 'json';\nconst LANGUAGE = process.env.LANGUAGE ?? 'auto';\nconst TIME_RANGE = process.env.TIME_RANGE ?? '';\nconst DEFAULT_TIMEOUT = process.env.TIMEOUT ?? 10000;\n\nconst config = {\n pageno: LIMIT,\n categories: CATEGORIES,\n format: FORMAT,\n safesearch: SAFE_SEARCH,\n language: LANGUAGE,\n engines: ENGINES,\n time_range: TIME_RANGE,\n timeout: DEFAULT_TIMEOUT,\n};\n\n// Tool handlers\nserver.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: [\n SEARCH_TOOL,\n EXTRACT_TOOL,\n SCRAPE_TOOL,\n ],\n}));\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const startTime = Date.now();\n\n try {\n const { name, arguments: args } = request.params;\n\n if (!args) {\n throw new Error('No arguments provided');\n }\n \n server.sendLoggingMessage({\n level: 'info',\n data: `[${new Date().toISOString()}] Received request for tool: [${name}]`,\n });\n \n switch (name) {\n case 'one_search': {\n // check args.\n if (!checkSearchArgs(args)) {\n throw new Error(`Invalid arguments for tool: [${name}]`);\n }\n try {\n const { results, success } = await processSearch(SEARCH_API_URL, {\n ...config,\n ...args,\n apiKey: SEARCH_API_KEY ?? '',\n });\n if (!success) {\n throw new Error('Failed to search');\n }\n return {\n results,\n success,\n };\n } catch (error) {\n server.sendLoggingMessage({\n level: 'error',\n data: `[${new Date().toISOString()}] Error searching: ${error}`,\n });\n const msg = error instanceof Error ? error.message : 'Unknown error';\n return {\n success: false,\n error: msg,\n };\n }\n }\n default:\n throw new Error(`Unknown tool: ${name}`);\n }\n } catch(error) {\n const msg = error instanceof Error ? error.message : String(error);\n server.sendLoggingMessage({\n level: 'error',\n data: {\n message: `[${new Date().toISOString()}] Error processing request: ${msg}`,\n tool: request.params.name,\n arguments: request.params.arguments,\n timestamp: new Date().toISOString(),\n duration: Date.now() - startTime,\n },\n });\n return {\n success: false,\n error: error instanceof Error ? error.message : msg,\n };\n } finally {\n server.sendLoggingMessage({\n level: 'info',\n data: `[${new Date().toISOString()}] Request completed in ${Date.now() - startTime}ms`,\n });\n }\n});\n\nasync function processSearch(apiUrl: string, args: ISearchRequestOptions): Promise<ISearchResponse> {\n switch (SEARCH_PROVIDER) {\n case 'searxng':\n return await searxngSearch(apiUrl, {\n ...config,\n ...args,\n apiKey: SEARCH_API_KEY,\n });\n case 'tavily':\n return await tavilySearch(apiUrl, {\n ...config,\n ...args,\n apiKey: SEARCH_API_KEY,\n });\n default:\n throw new Error(`Unsupported search provider: ${SEARCH_PROVIDER}`);\n }\n}\n\nfunction checkSearchArgs(args: unknown): args is ISearchRequestOptions {\n return (\n typeof args === 'object' &&\n args !== null &&\n 'query' in args &&\n typeof args.query === 'string'\n );\n}\n\nasync function runServer() {\n try {\n process.stdout.write('Starting OneSearch MCP server...\\n');\n\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n server.sendLoggingMessage({\n level: 'info',\n data: 'OneSearch MCP server started',\n });\n\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n process.stderr.write(`Error starting server: ${msg}\\n`);\n process.exit(1);\n }\n}\n\n// run server\nrunServer().catch((error) => {\n const msg = error instanceof Error ? error.message : String(error);\n process.stderr.write(`Error running server: ${msg}\\n`);\n process.exit(1);\n});\n","import url from 'node:url';\nimport { tavily, TavilyClient, TavilySearchOptions } from '@tavily/core';\nimport { ISearchRequestOptions, ISearchResponse, ISearchResponseResult } from './interface.js';\n\n/**\n * SearxNG Search API\n * - https://docs.searxng.org/dev/search_api.html\n */\nexport async function searxngSearch(apiUrl: string, params: ISearchRequestOptions): Promise<ISearchResponse> {\n try {\n const {\n query,\n limit = 10,\n categories = 'general',\n engines = 'all',\n safeSearch = 0,\n format = 'json',\n language = 'auto',\n timeRange = '',\n timeout = 10000,\n apiKey = '',\n } = params;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), Number(timeout));\n\n const config = {\n q: query,\n pageno: limit,\n categories,\n format,\n safesearch: safeSearch,\n language,\n engines,\n time_range: timeRange,\n };\n\n const endpoint = `${apiUrl}/search`;\n\n const queryParams = url.format({ query: config });\n\n const headers: HeadersInit = {\n 'Content-Type': 'application/json',\n };\n\n if (apiKey) {\n headers['Authorization'] = `Bearer ${apiKey}`;\n }\n\n const res = await fetch(`${endpoint}${queryParams}`, {\n method: 'POST',\n headers,\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n const result = await res.json();\n if (result.results) {\n const results: ISearchResponseResult[] = result.results.map((item: Record<string, unknown>) => {\n const image = item.img_src ? {\n thumbnail: item.thumbnail_src,\n src: item.img_src,\n } : null;\n const video = item.iframe_src ? {\n thumbnail: item.thumbnail_src,\n src: item.iframe_src,\n } : null;\n return {\n title: item.title,\n snippet: item.content,\n url: item.url,\n source: item.source,\n image,\n video,\n engine: item.engine,\n };\n });\n return {\n results,\n success: true,\n };\n }\n return {\n results: [],\n success: false,\n };\n } catch (err: any) {\n process.stdout.write(err?.message ?? 'Searxng search error.');\n throw err;\n }\n}\n\n\nlet tvly: TavilyClient | null = null;\nexport async function tavilySearch(query: string, options: ISearchRequestOptions): Promise<ISearchResponse> {\n const {\n limit = 10,\n categories = 'general',\n timeRange,\n apiKey,\n } = options;\n\n if (!apiKey) {\n throw new Error('Tavily API key is required');\n }\n\n if (!tvly) {\n tvly = tavily({\n apiKey,\n });\n }\n\n const params: TavilySearchOptions = {\n topic: categories as TavilySearchOptions['topic'],\n timeRange: timeRange as TavilySearchOptions['timeRange'],\n maxResults: limit,\n };\n\n const res = await tvly.search(query, params);\n const results = res.results.map(item => ({\n title: item.title,\n url: item.url,\n snippet: item.content,\n }));\n\n return {\n results,\n success: true,\n };\n}\n"],"mappings":"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","names":["config"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "one-search-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "One Search MCP Server, Web Search & Crawl & Scraper & Extract, support SearXNG, Firecrawl, Tavily, etc.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"AI",
|
|
9
|
+
"LLM",
|
|
10
|
+
"MCP",
|
|
11
|
+
"ModelContextProtocol",
|
|
12
|
+
"SearXNG MCP Server",
|
|
13
|
+
"Firecrawl MCP Server",
|
|
14
|
+
"Search MCP Server",
|
|
15
|
+
"Web Search",
|
|
16
|
+
"LLM Tool",
|
|
17
|
+
"One Search"
|
|
18
|
+
],
|
|
19
|
+
"author": "zac.ma",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/yokingma/one-search-mcp.git"
|
|
24
|
+
},
|
|
25
|
+
"main": "./dist/index.cjs",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"files": [
|
|
29
|
+
"dist/**"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20.0.0"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"dev": "dotenvx run --env-file=.env -- cross-env NODE_ENV=development tsx src/index.ts",
|
|
36
|
+
"build": "tsup",
|
|
37
|
+
"lint": "eslint src",
|
|
38
|
+
"lint:fix": "eslint src --fix"
|
|
39
|
+
},
|
|
40
|
+
"tsup": {
|
|
41
|
+
"entry": [
|
|
42
|
+
"src/index.ts"
|
|
43
|
+
],
|
|
44
|
+
"outDir": "dist",
|
|
45
|
+
"format": [
|
|
46
|
+
"cjs",
|
|
47
|
+
"esm"
|
|
48
|
+
],
|
|
49
|
+
"splitting": false,
|
|
50
|
+
"dts": true,
|
|
51
|
+
"clean": true,
|
|
52
|
+
"sourcemap": true
|
|
53
|
+
},
|
|
54
|
+
"exports": {
|
|
55
|
+
".": {
|
|
56
|
+
"require": "./dist/index.cjs",
|
|
57
|
+
"import": "./dist/index.js"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@eslint/js": "^8.56.0",
|
|
62
|
+
"@types/node": "^22.13.10",
|
|
63
|
+
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
|
64
|
+
"@typescript-eslint/parser": "^7.0.0",
|
|
65
|
+
"cross-env": "^7.0.3",
|
|
66
|
+
"eslint": "^8.56.0",
|
|
67
|
+
"tsup": "^8.4.0",
|
|
68
|
+
"tsx": "^4.19.3",
|
|
69
|
+
"typescript": "^5.3.3",
|
|
70
|
+
"typescript-eslint": "^7.0.0"
|
|
71
|
+
},
|
|
72
|
+
"dependencies": {
|
|
73
|
+
"@dotenvx/dotenvx": "^1.38.5",
|
|
74
|
+
"@modelcontextprotocol/sdk": "^1.7.0",
|
|
75
|
+
"@tavily/core": "^0.3.1"
|
|
76
|
+
}
|
|
77
|
+
}
|