one-search-mcp 1.1.3 β†’ 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # πŸš€ OneSearch MCP Server: Web Search & Crawl & Scraper & Extract
1
+ # πŸš€ OneSearch MCP Server: Web Search & Crawl & Scraper & Content Prep
2
2
 
3
3
  A Model Context Protocol (MCP) server implementation that integrates with multiple search providers for web search, local browser search, and scraping capabilities with agent-browser.
4
4
 
5
5
  ## Features
6
6
 
7
- - Web Search, scrape, crawl and extract content from websites.
7
+ - Web search, scrape, crawl and preprocess content from websites.
8
8
  - Support multiple search engines and web scrapers: **SearXNG**, **Tavily**, **DuckDuckGo**, **Bing**, **Google**, **Zhipu (ζ™Ίθ°±)**, **Exa**, **Bocha (博ζŸ₯)**, etc.
9
9
  - **Local web search** (browser search), support multiple search engines: **Bing**, **Google**, **Baidu**, **Sogou**, etc.
10
10
  - Use `agent-browser` for browser automation.
@@ -22,7 +22,7 @@ A Model Context Protocol (MCP) server implementation that integrates with multip
22
22
  **What Changed:**
23
23
 
24
24
  - `one_scrape` and `one_map` now use `agent-browser` instead of Firecrawl
25
- - `one_extract` tool is now fully implemented for structured data extraction from multiple URLs
25
+ - `one_extract` now preprocesses multi-URL page content for downstream analysis instead of performing built-in LLM extraction
26
26
  - All browser-based operations are now handled locally, providing better privacy and no API costs
27
27
 
28
28
  **Migration Steps:**
package/dist/index.cjs CHANGED
@@ -1,38 +1,29 @@
1
1
  #!/usr/bin/env node
2
- "use strict";var Re=Object.create;var F=Object.defineProperty;var ve=Object.getOwnPropertyDescriptor;var Te=Object.getOwnPropertyNames;var Ie=Object.getPrototypeOf,Ae=Object.prototype.hasOwnProperty;var z=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Te(e))!Ae.call(t,n)&&n!==r&&F(t,n,{get:()=>e[n],enumerable:!(s=ve(e,n))||s.enumerable});return t};var b=(t,e,r)=>(r=t!=null?Re(Ie(t)):{},z(e||!t||!t.__esModule?F(r,"default",{value:t,enumerable:!0}):r,t)),Oe=t=>z(F({},"__esModule",{value:!0}),t);var cr={};module.exports=Oe(cr);var we=require("@modelcontextprotocol/sdk/server/mcp.js"),ye=require("@modelcontextprotocol/sdk/server/stdio.js");var i=require("zod/v3"),W=i.z.object({query:i.z.string().describe("Search query string"),limit:i.z.number().optional().describe("Maximum number of results to return (default: 10)"),language:i.z.string().optional().describe("Language code for search results (default: auto)"),categories:i.z.enum(["general","news","images","videos","it","science","map","music","files","social_media"]).optional().describe("Categories to search for (default: general)"),timeRange:i.z.enum(["all","day","week","month","year"]).optional().describe("Time range for search results (default: all)")}),Z=i.z.object({url:i.z.string().describe("Starting URL for URL discovery"),search:i.z.string().optional().describe("Optional search term to filter URLs"),ignoreSitemap:i.z.boolean().optional().describe("Skip sitemap.xml discovery and only use HTML links"),sitemapOnly:i.z.boolean().optional().describe("Only use sitemap.xml for discovery, ignore HTML links"),includeSubdomains:i.z.boolean().optional().describe("Include URLs from subdomains in results"),limit:i.z.number().optional().describe("Maximum number of URLs to return")}),Pe=i.z.discriminatedUnion("type",[i.z.object({type:i.z.literal("wait"),milliseconds:i.z.number().describe("Time to wait in milliseconds")}),i.z.object({type:i.z.literal("click"),selector:i.z.string().describe("CSS selector for the target element")}),i.z.object({type:i.z.literal("screenshot"),fullPage:i.z.boolean().optional().describe("Take full page screenshot")}),i.z.object({type:i.z.literal("write"),selector:i.z.string().describe("CSS selector for the target element"),text:i.z.string().describe("Text to write")}),i.z.object({type:i.z.literal("press"),key:i.z.string().describe("Key to press")}),i.z.object({type:i.z.literal("scroll"),direction:i.z.enum(["up","down"]).describe("Scroll direction")}),i.z.object({type:i.z.literal("scrape")}),i.z.object({type:i.z.literal("executeJavascript"),script:i.z.string().describe("JavaScript code to execute")})]),V=i.z.object({url:i.z.string().describe("The URL to scrape"),formats:i.z.array(i.z.enum(["markdown","html","rawHtml","screenshot","links","screenshot@fullPage","extract"])).optional().describe("Content formats to extract (default: ['markdown'])"),onlyMainContent:i.z.boolean().optional().describe("Extract only the main content, filtering out navigation, footers, etc."),includeTags:i.z.array(i.z.string()).optional().describe("HTML tags to specifically include in extraction"),excludeTags:i.z.array(i.z.string()).optional().describe("HTML tags to exclude from extraction"),waitFor:i.z.number().optional().describe("Time in milliseconds to wait for dynamic content to load"),timeout:i.z.number().optional().describe("Maximum time in milliseconds to wait for the page to load"),actions:i.z.array(Pe).optional().describe("List of actions to perform before scraping"),extract:i.z.object({schema:i.z.record(i.z.any()).optional().describe("Schema for structured data extraction"),systemPrompt:i.z.string().optional().describe("System prompt for LLM extraction"),prompt:i.z.string().optional().describe("User prompt for LLM extraction")}).optional().describe("Configuration for structured data extraction"),mobile:i.z.boolean().optional().describe("Use mobile viewport"),skipTlsVerification:i.z.boolean().optional().describe("Skip TLS certificate verification"),removeBase64Images:i.z.boolean().optional().describe("Remove base64 encoded images from output"),location:i.z.object({country:i.z.string().optional().describe("Country code for geolocation"),languages:i.z.array(i.z.string()).optional().describe("Language codes for content")}).optional().describe("Location settings for scraping")}),Q=i.z.object({urls:i.z.array(i.z.string()).describe("List of URLs to extract information from"),prompt:i.z.string().optional().describe("Prompt for the LLM extraction"),systemPrompt:i.z.string().optional().describe("System prompt for LLM extraction"),schema:i.z.record(i.z.any()).optional().describe("JSON schema for structured data extraction"),allowExternalLinks:i.z.boolean().optional().describe("Allow extraction from external links"),enableWebSearch:i.z.boolean().optional().describe("Enable web search for additional context"),includeSubdomains:i.z.boolean().optional().describe("Include subdomains in extraction")});var A={name:"one_search",description:"Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",schema:W},O={name:"one_map",description:"Discover URLs from a starting point. Can use both sitemap.xml and HTML link discovery.",schema:Z},P={name:"one_scrape",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.",schema:V},C={name:"one_extract",description:"Extract structured information from web pages using LLM. Supports both cloud AI and self-hosted LLM extraction.",schema:Q};var X=require("agent-browser/dist/browser.js"),ee=b(require("turndown"),1),re=require("turndown-plugin-gfm"),_=b(require("cheerio"),1);var L=b(require("fs"),1),N=b(require("path"),1),J=b(require("os"),1);var Y=b(require("pino"),1),E=class{logger;constructor(e){this.logger=(0,Y.default)({name:e||"one-search-mcp",level:process.env.LOG_LEVEL||"info",transport:process.env.NODE_ENV==="development"?{target:"pino-pretty",options:{colorize:!0,translateTime:"SYS:standard",ignore:"pid,hostname"}}:void 0},process.stderr)}logWithLevel(e,r,...s){s.length>0?this.logger[e]({data:s},r):this.logger[e](r)}info(e,...r){this.logWithLevel("info",e,...r)}error(e,...r){this.logWithLevel("error",e,...r)}success(e,...r){r.length>0?this.logger.info({level:"success",data:r},e):this.logger.info({level:"success"},e)}warn(e,...r){this.logWithLevel("warn",e,...r)}log(e,...r){this.logWithLevel("info",e,...r)}},U=new E;var D=class{logger;constructor(e){this.logger=e??U}get browsers(){let e=J.homedir(),r=process.env.LOCALAPPDATA;return[{name:"Chromium",executable:{win32:"C:\\Program Files\\Chromium\\Application\\chrome.exe",darwin:"/Applications/Chromium.app/Contents/MacOS/Chromium",linux:"/usr/bin/chromium"},userDataDir:{win32:`${r}\\Chromium\\User Data`,darwin:`${e}/Library/Application Support/Chromium`,linux:`${e}/.config/chromium`}},{name:"Google Chrome",executable:{win32:"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",darwin:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",linux:"/usr/bin/google-chrome"},userDataDir:{win32:`${r}\\Google\\Chrome\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome`,linux:`${e}/.config/google-chrome`}},{name:"Google Chrome Canary",executable:{win32:"C:\\Program Files\\Google\\Chrome Canary\\Application\\chrome.exe",darwin:"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",linux:"/usr/bin/google-chrome-canary"},userDataDir:{win32:`${r}\\Google\\Chrome Canary\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome Canary`,linux:`${e}/.config/google-chrome-canary`}},{name:"Microsoft Edge",executable:{win32:"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",darwin:"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",linux:"/usr/bin/microsoft-edge"},userDataDir:{win32:`${r}\\Microsoft\\Edge\\User Data`,darwin:`${e}/Library/Application Support/Microsoft Edge`,linux:`${e}/.config/microsoft-edge`}}]}findBrowser(e){let r=process.platform;if(this.logger.info("Finding browser on platform:",r),r!=="darwin"&&r!=="win32"&&r!=="linux"){let o=new Error(`Unsupported platform: ${r}`);throw this.logger.error(o.message),o}let s=e?this.browsers.find(o=>o.name===e&&L.existsSync(o.executable[r])):this.browsers.find(o=>L.existsSync(o.executable[r]));if(this.logger.log("browser",s),!s){let o=e?new Error(`Cannot find browser: ${e}`):new Error("Cannot find a supported browser on your system. Please install Chrome, Chromium, Edge, or Brave.");throw this.logger.error(o.message),o}let n={executable:s.executable[r],userDataDir:s.userDataDir[r]};return this.logger.success(`Found browser: ${s.name}`),this.logger.info("Browser details:",n),n}getBrowserProfiles(e){let r=this.findBrowser(e);try{let n=JSON.parse(L.readFileSync(N.join(r.userDataDir,"Local State"),"utf8")).profile.info_cache;return Object.entries(n).map(([o,c])=>({displayName:c.name,path:N.join(r.userDataDir,o)}))}catch{return[]}}findChrome(){try{let{executable:e}=this.findBrowser("Google Chrome");return e}catch{return null}}};var q={DEFAULT_WAIT_MS:2e3,SEARCH_WAIT_MS:3e3,DEFAULT_TIMEOUT:3e4,DEFAULT_HEADLESS:!0},j=["bing","google","baidu","sogou"];var x=class{constructor(e={}){this.options=e;this.browser=new X.BrowserManager;try{let r=new D,{executable:s}=r.findBrowser();this.browserPath=s}catch{this.browserPath=void 0}this.turndown=new ee.default({headingStyle:"atx",codeBlockStyle:"fenced"}),this.turndown.use(re.gfm)}browser;turndown;browserPath;async ensureLaunched(){if(!this.browser.isLaunched())try{await this.browser.launch({id:`session-${Date.now()}`,action:"launch",headless:this.options.headless??!0,executablePath:this.browserPath})}catch(e){let r=e instanceof Error?e.message:String(e);throw r.includes("Executable")||r.includes("browser")?new Error(`Browser not found. Please install one of the following:
2
+ "use strict";var Te=Object.create;var F=Object.defineProperty;var Ie=Object.getOwnPropertyDescriptor;var Oe=Object.getOwnPropertyNames;var Ae=Object.getPrototypeOf,Pe=Object.prototype.hasOwnProperty;var z=(t,e,r,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of Oe(e))!Pe.call(t,n)&&n!==r&&F(t,n,{get:()=>e[n],enumerable:!(s=Ie(e,n))||s.enumerable});return t};var b=(t,e,r)=>(r=t!=null?Te(Ae(t)):{},z(e||!t||!t.__esModule?F(r,"default",{value:t,enumerable:!0}):r,t)),Ce=t=>z(F({},"__esModule",{value:!0}),t);var mr={};module.exports=Ce(mr);var be=require("@modelcontextprotocol/sdk/server/mcp.js"),xe=require("@modelcontextprotocol/sdk/server/stdio.js");var i=require("zod/v3"),W=i.z.object({query:i.z.string().describe("Search query string"),limit:i.z.number().optional().describe("Maximum number of results to return (default: 10)"),language:i.z.string().optional().describe("Language code for search results (default: auto)"),categories:i.z.enum(["general","news","images","videos","it","science","map","music","files","social_media"]).optional().describe("Categories to search for (default: general)"),timeRange:i.z.enum(["all","day","week","month","year"]).optional().describe("Time range for search results (default: all)")}),V=i.z.object({url:i.z.string().describe("Starting URL for URL discovery"),search:i.z.string().optional().describe("Optional search term to filter URLs"),ignoreSitemap:i.z.boolean().optional().describe("Skip sitemap.xml discovery and only use HTML links"),sitemapOnly:i.z.boolean().optional().describe("Only use sitemap.xml for discovery, ignore HTML links"),includeSubdomains:i.z.boolean().optional().describe("Include URLs from subdomains in results"),limit:i.z.number().optional().describe("Maximum number of URLs to return")}),_e=i.z.discriminatedUnion("type",[i.z.object({type:i.z.literal("wait"),milliseconds:i.z.number().describe("Time to wait in milliseconds")}),i.z.object({type:i.z.literal("click"),selector:i.z.string().describe("CSS selector for the target element")}),i.z.object({type:i.z.literal("screenshot"),fullPage:i.z.boolean().optional().describe("Take full page screenshot")}),i.z.object({type:i.z.literal("write"),selector:i.z.string().describe("CSS selector for the target element"),text:i.z.string().describe("Text to write")}),i.z.object({type:i.z.literal("press"),key:i.z.string().describe("Key to press")}),i.z.object({type:i.z.literal("scroll"),direction:i.z.enum(["up","down"]).describe("Scroll direction")}),i.z.object({type:i.z.literal("scrape")}),i.z.object({type:i.z.literal("executeJavascript"),script:i.z.string().describe("JavaScript code to execute")})]),Z=i.z.object({url:i.z.string().describe("The URL to scrape"),formats:i.z.array(i.z.enum(["markdown","html","rawHtml","screenshot","links","screenshot@fullPage","extract"])).optional().describe("Content formats to extract (default: ['markdown'])"),onlyMainContent:i.z.boolean().optional().describe("Extract only the main content, filtering out navigation, footers, etc."),includeTags:i.z.array(i.z.string()).optional().describe("HTML tags to specifically include in extraction"),excludeTags:i.z.array(i.z.string()).optional().describe("HTML tags to exclude from extraction"),waitFor:i.z.number().optional().describe("Time in milliseconds to wait for dynamic content to load"),timeout:i.z.number().optional().describe("Maximum time in milliseconds to wait for the page to load"),actions:i.z.array(_e).optional().describe("List of actions to perform before scraping"),extract:i.z.object({schema:i.z.record(i.z.any()).optional().describe("Schema for structured data extraction"),systemPrompt:i.z.string().optional().describe("System prompt for LLM extraction"),prompt:i.z.string().optional().describe("User prompt for LLM extraction")}).optional().describe("Configuration for structured data extraction"),mobile:i.z.boolean().optional().describe("Use mobile viewport"),skipTlsVerification:i.z.boolean().optional().describe("Skip TLS certificate verification"),removeBase64Images:i.z.boolean().optional().describe("Remove base64 encoded images from output"),location:i.z.object({country:i.z.string().optional().describe("Country code for geolocation"),languages:i.z.array(i.z.string()).optional().describe("Language codes for content")}).optional().describe("Location settings for scraping")}),Y=i.z.object({urls:i.z.array(i.z.string()).describe("List of URLs to extract information from")}).strict();var O={name:"one_search",description:"Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",schema:W},A={name:"one_map",description:"Discover URLs from a starting point. Can use both sitemap.xml and HTML link discovery.",schema:V},P={name:"one_scrape",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.",schema:Z},C={name:"one_extract",description:"Fetch and preprocess page content from one or more URLs. Returns cleaned text blocks that can be passed to downstream tools or models.",schema:Y};var X=require("agent-browser/dist/browser.js"),ee=b(require("turndown"),1),re=require("turndown-plugin-gfm"),k=b(require("cheerio"),1);var _=b(require("fs"),1),N=b(require("path"),1),J=b(require("os"),1);var Q=b(require("pino"),1),R=class{logger;constructor(e){this.logger=(0,Q.default)({name:e||"one-search-mcp",level:process.env.LOG_LEVEL||"info",transport:process.env.NODE_ENV==="development"?{target:"pino-pretty",options:{colorize:!0,translateTime:"SYS:standard",ignore:"pid,hostname"}}:void 0},process.stderr)}logWithLevel(e,r,...s){s.length>0?this.logger[e]({data:s},r):this.logger[e](r)}info(e,...r){this.logWithLevel("info",e,...r)}error(e,...r){this.logWithLevel("error",e,...r)}success(e,...r){r.length>0?this.logger.info({level:"success",data:r},e):this.logger.info({level:"success"},e)}warn(e,...r){this.logWithLevel("warn",e,...r)}log(e,...r){this.logWithLevel("info",e,...r)}},$=new R;var D=class{logger;constructor(e){this.logger=e??$}get browsers(){let e=J.homedir(),r=process.env.LOCALAPPDATA;return[{name:"Chromium",executable:{win32:"C:\\Program Files\\Chromium\\Application\\chrome.exe",darwin:"/Applications/Chromium.app/Contents/MacOS/Chromium",linux:"/usr/bin/chromium"},userDataDir:{win32:`${r}\\Chromium\\User Data`,darwin:`${e}/Library/Application Support/Chromium`,linux:`${e}/.config/chromium`}},{name:"Google Chrome",executable:{win32:"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",darwin:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",linux:"/usr/bin/google-chrome"},userDataDir:{win32:`${r}\\Google\\Chrome\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome`,linux:`${e}/.config/google-chrome`}},{name:"Google Chrome Canary",executable:{win32:"C:\\Program Files\\Google\\Chrome Canary\\Application\\chrome.exe",darwin:"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",linux:"/usr/bin/google-chrome-canary"},userDataDir:{win32:`${r}\\Google\\Chrome Canary\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome Canary`,linux:`${e}/.config/google-chrome-canary`}},{name:"Microsoft Edge",executable:{win32:"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",darwin:"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",linux:"/usr/bin/microsoft-edge"},userDataDir:{win32:`${r}\\Microsoft\\Edge\\User Data`,darwin:`${e}/Library/Application Support/Microsoft Edge`,linux:`${e}/.config/microsoft-edge`}}]}findBrowser(e){let r=process.platform;if(this.logger.info("Finding browser on platform:",r),r!=="darwin"&&r!=="win32"&&r!=="linux"){let o=new Error(`Unsupported platform: ${r}`);throw this.logger.error(o.message),o}let s=e?this.browsers.find(o=>o.name===e&&_.existsSync(o.executable[r])):this.browsers.find(o=>_.existsSync(o.executable[r]));if(this.logger.log("browser",s),!s){let o=e?new Error(`Cannot find browser: ${e}`):new Error("Cannot find a supported browser on your system. Please install Chrome, Chromium, Edge, or Brave.");throw this.logger.error(o.message),o}let n={executable:s.executable[r],userDataDir:s.userDataDir[r]};return this.logger.success(`Found browser: ${s.name}`),this.logger.info("Browser details:",n),n}getBrowserProfiles(e){let r=this.findBrowser(e);try{let n=JSON.parse(_.readFileSync(N.join(r.userDataDir,"Local State"),"utf8")).profile.info_cache;return Object.entries(n).map(([o,c])=>({displayName:c.name,path:N.join(r.userDataDir,o)}))}catch{return[]}}findChrome(){try{let{executable:e}=this.findBrowser("Google Chrome");return e}catch{return null}}};var q={DEFAULT_WAIT_MS:2e3,SEARCH_WAIT_MS:3e3,DEFAULT_TIMEOUT:3e4,DEFAULT_HEADLESS:!0},j=["bing","google","baidu","sogou"];var x=class{constructor(e={}){this.options=e;this.browser=new X.BrowserManager;try{let r=new D,{executable:s}=r.findBrowser();this.browserPath=s}catch{this.browserPath=void 0}this.turndown=new ee.default({headingStyle:"atx",codeBlockStyle:"fenced"}),this.turndown.use(re.gfm)}browser;turndown;browserPath;async ensureLaunched(){if(!this.browser.isLaunched())try{await this.browser.launch({id:`session-${Date.now()}`,action:"launch",headless:this.options.headless??!0,executablePath:this.browserPath})}catch(e){let r=e instanceof Error?e.message:String(e);throw r.includes("Executable")||r.includes("browser")?new Error(`Browser not found. Please install one of the following:
3
3
  - Google Chrome: https://www.google.com/chrome/
4
4
  - Microsoft Edge: https://www.microsoft.com/edge
5
5
  - Chromium: https://www.chromium.org/getting-involved/download-chromium/
6
6
  Or install via Playwright:
7
7
  npx playwright install chromium
8
- Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(q.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(q.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(c=>{try{return new URL(c).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(q.SEARCH_WAIT_MS);let c=await this.getHtml();return this.extractSearchResults(s,c).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return U.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=_.load(e),s=[];return r("div.g").each((n,o)=>{let c=r(o),l=c.find("a[href]").first(),u=c.find("h3").first(),a=c.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=_.load(e),s=[];return r("li.b_algo").each((n,o)=>{let c=r(o),l=c.find("h2 a").first(),u=c.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=_.load(e),s=[];return r("div.result").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=_.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let c=new URL(o[1],r).href;n.includes(c)||n.push(c)}catch{}return n}};function Ce(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var k=Ce();function Le(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function te(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function v({browser:t,signal:e,task:r}){let s=k.track(t),n=Le(t,s);if(e?.aborted)throw await n(),te(e);let o;try{let c=r();if(!e)return await c;let l=new Promise((u,a)=>{o=()=>{n(),a(te(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([c,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}var H=require("duck-duck-scrape");var d=new E("search");function f({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var _e=2e4;async function se(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:c="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=f({signal:e,timeoutMs:_e,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([y,I])=>{I!==void 0&&h.set(y,I.toString())});let m=await fetch(`${c}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(y=>({title:y.name,snippet:y.snippet,url:y.url,source:y.siteName,thumbnailUrl:y.thumbnailUrl,language:y.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}var B=b(require("duck-duck-scrape"),1),ne=b(require("async-retry"),1);async function oe(t,e){let{query:r,timeout:s=1e4,safeSearch:n=B.SafeSearchType.OFF,retry:o={retries:3},...c}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await(0,ne.default)(async a=>{try{return await B.search(r,{...c,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}var ae=b(require("url"),1);async function ie(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:c="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let G=f({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let R={q:r,pageno:s,categories:o,format:u,safesearch:l,language:a,engines:c,time_range:p},$=`${m}/search`,y=ae.default.format({query:R}),I={"Content-Type":"application/json"};h&&(I.Authorization=`Bearer ${h}`);let K=await(await fetch(`${$}${y}`,{method:"POST",headers:I,signal:G.signal})).json();return K.results?{results:K.results.slice(0,n).map(S=>{let xe=S.img_src?{thumbnail:S.thumbnail_src,src:S.img_src}:null,Ee=S.iframe_src?{thumbnail:S.thumbnail_src,src:S.iframe_src}:null;return{title:S.title,snippet:S.content,url:S.url,source:S.source,image:xe,video:Ee,engine:S.engine}}),success:!0}:{results:[],success:!1}}catch(R){let $=R instanceof Error?R.message:"Searxng search error.";throw d.error($),R}finally{G.cleanup()}}var ke="https://api.tavily.com/search",Me=2e4;async function ce(t,e){let{query:r,limit:s=10,categories:n="general",timeRange:o,apiKey:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!c)throw new Error("Tavily API key is required");let l=f({signal:e,timeoutMs:Me,timeoutMessage:"Tavily search timeout"});try{let u=await fetch(ke,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${c}`},body:JSON.stringify({query:r,topic:n,time_range:o,max_results:s}),signal:l.signal});if(!u.ok)throw new Error(`Tavily search failed: ${u.status} ${u.statusText}`);return{results:(await u.json()).results.map(g=>({title:g.title,url:g.url,snippet:g.content,engine:"tavily"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Tavily search error.";throw d.error(a),u}finally{l.cleanup()}}var T=new E("[LocalSearch]");function le(t){return j.includes(t)}function $e(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function ue(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let c=o.filter(le),l=o.filter(a=>!le(a));if(l.length>0&&T.warn(`Invalid search engines ignored: ${l.join(", ")}`),c.length===0)throw new Error(`No valid search engines provided. Valid engines: ${j.join(", ")}`);let u=new x({headless:!0,timeout:3e4});return await v({browser:u,signal:e,task:async()=>{let a=[];for(let p of c)try{T.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>$e(m,p));a.push(...h),T.info(`Found ${g.length} results from ${p}`);break}}catch(g){T.error(`Failed to search with ${p}:`,g)}return T.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw T.error(p,a),a})}var Ue="https://www.googleapis.com/customsearch/v1",De=2e4;async function pe(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});c&&c!=="auto"&&l.set("lr",`lang_${c}`);let u=f({signal:e,timeoutMs:De,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${Ue}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var qe="https://open.bigmodel.cn/api/paas/v4/web_search",Be=2e4;async function ge(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=f({signal:e,timeoutMs:Be,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(qe,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var He="https://api.exa.ai/search",Ge=2e4,Fe=1e4;async function he(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=f({signal:e,timeoutMs:Ge,timeoutMessage:"Exa search timeout"});try{let c=await fetch(He,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:Fe}}}),signal:o.signal});if(!c.ok)throw new Error(`Exa search failed: ${c.status} ${c.statusText}`);return{results:(await c.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(c){let l=c instanceof Error?c.message:"Exa search error.";throw d.error(l),c}finally{o.cleanup()}}var Ne="https://api.bocha.cn/v1/web-search",je=2e4;async function me(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let c="noLimit";o&&(c={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=f({signal:e,timeoutMs:je,timeoutMessage:"Bocha search timeout"});try{let u=await fetch(Ne,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:c}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var Ke={searxngSearch:async(t,e)=>await ie(t,e),tavilySearch:async(t,e)=>await ce(t,e),bingSearch:async(t,e)=>await se(t,e),duckDuckGoSearch:async(t,e)=>await oe(t,e),localSearch:async(t,e)=>await ue(t,e),googleSearch:async(t,e)=>await pe(t,e),zhipuSearch:async(t,e)=>await ge(t,e),exaSearch:async(t,e)=>await he(t,e),bochaSearch:async(t,e)=>await me(t,e)};async function de(t,e,r,s=Ke){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey},{categories:o,language:c}=e.defaultSearchOptions;return o&&(n.categories=o),c&&(n.language=c),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[H.SafeSearchType.STRICT,H.SafeSearchType.MODERATE,H.SafeSearchType.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}function M(t,e,r={}){return async(s,n)=>{let o=Date.now();try{r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request started for tool: [${t}]`});let c=await e(s,n);return r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request completed in ${Date.now()-o}ms`}),c}catch(c){return r.logMessage&&await r.logMessage({level:"error",data:`[${new Date().toISOString()}] Error in ${t}: ${c}`}),{content:[{type:"text",text:c instanceof Error?c.message:String(c)}],isError:!0}}}}function fe(t,e){let r,s=async()=>{r||(r=e()),await r},n=o=>{t.once(o,()=>{s().finally(()=>{typeof t.kill=="function"&&typeof t.pid=="number"&&t.kill(t.pid,o)})})};t.once("beforeExit",()=>{s()}),n("SIGINT"),n("SIGTERM")}function Se(t,e,r){let s,n=!1,o=u=>{n||(n=!0,r(u))},c=async()=>{s||(s=e()),await s},l=()=>{c().then(()=>{o(0)}).catch(()=>{o(1)})};t.once("end",l),t.once("close",l)}var be=b(require("@dotenvx/dotenvx"),1);be.default.config({quiet:!0});var ze=process.env.SEARCH_API_URL,We=process.env.SEARCH_API_KEY,Ze=process.env.SEARCH_PROVIDER??"local",Ve=process.env.SAFE_SEARCH??0,Qe=process.env.LIMIT??10,Ye=process.env.CATEGORIES??"general",Je=process.env.ENGINES??"all",Xe=process.env.FORMAT??"json",er=process.env.LANGUAGE??"auto",rr=process.env.TIME_RANGE??"",tr=process.env.TIMEOUT??1e4,w=new we.McpServer({name:"one-search-mcp",version:"1.1.3"},{capabilities:{tools:{},logging:{}}}),sr={limit:Number(Qe),categories:Ye,format:Xe,safeSearch:Number(Ve),language:er,engines:Je,timeRange:rr,timeout:tr};fe(process,async()=>{await k.cleanup()});Se(process.stdin,async()=>{await k.cleanup(),await w.close()},t=>{process.exit(t)});w.registerTool(A.name,{description:A.description,inputSchema:A.schema},M(A.name,async(t,e)=>{let{results:r,success:s}=await de(t,{provider:Ze,apiKey:We??"",apiUrl:ze,defaultSearchOptions:sr},e.signal);if(!s)throw new Error("Failed to search");return{content:[{type:"text",text:r.map(o=>`Title: ${o.title}
9
- URL: ${o.url}
10
- Description: ${o.snippet}
11
- ${o.markdown?`Content: ${o.markdown}`:""}`).join(`
8
+ Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(q.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(q.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(c=>{try{return new URL(c).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(q.SEARCH_WAIT_MS);let c=await this.getHtml();return this.extractSearchResults(s,c).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return $.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=k.load(e),s=[];return r("div.g").each((n,o)=>{let c=r(o),l=c.find("a[href]").first(),u=c.find("h3").first(),a=c.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=k.load(e),s=[];return r("li.b_algo").each((n,o)=>{let c=r(o),l=c.find("h2 a").first(),u=c.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=k.load(e),s=[];return r("div.result").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=k.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let c=new URL(o[1],r).href;n.includes(c)||n.push(c)}catch{}return n}};function ke(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var L=ke();function Le(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function te(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function v({browser:t,signal:e,task:r}){let s=L.track(t),n=Le(t,s);if(e?.aborted)throw await n(),te(e);let o;try{let c=r();if(!e)return await c;let l=new Promise((u,a)=>{o=()=>{n(),a(te(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([c,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}var H=require("duck-duck-scrape");var d=new R("search");function f({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var Me=2e4;async function se(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:c="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=f({signal:e,timeoutMs:Me,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([y,I])=>{I!==void 0&&h.set(y,I.toString())});let m=await fetch(`${c}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(y=>({title:y.name,snippet:y.snippet,url:y.url,source:y.siteName,thumbnailUrl:y.thumbnailUrl,language:y.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}var B=b(require("duck-duck-scrape"),1),ne=b(require("async-retry"),1);async function oe(t,e){let{query:r,timeout:s=1e4,safeSearch:n=B.SafeSearchType.OFF,retry:o={retries:3},...c}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await(0,ne.default)(async a=>{try{return await B.search(r,{...c,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}var ae=b(require("url"),1);async function ie(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:c="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let G=f({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let E={q:r,pageno:s,categories:o,format:u,safesearch:l,language:a,engines:c,time_range:p},U=`${m}/search`,y=ae.default.format({query:E}),I={"Content-Type":"application/json"};h&&(I.Authorization=`Bearer ${h}`);let K=await(await fetch(`${U}${y}`,{method:"POST",headers:I,signal:G.signal})).json();return K.results?{results:K.results.slice(0,n).map(S=>{let Ee=S.img_src?{thumbnail:S.thumbnail_src,src:S.img_src}:null,ve=S.iframe_src?{thumbnail:S.thumbnail_src,src:S.iframe_src}:null;return{title:S.title,snippet:S.content,url:S.url,source:S.source,image:Ee,video:ve,engine:S.engine}}),success:!0}:{results:[],success:!1}}catch(E){let U=E instanceof Error?E.message:"Searxng search error.";throw d.error(U),E}finally{G.cleanup()}}var ce=require("@tavily/core");var Ue=2e4,$e=new Set(["general","news","finance"]),De=new Set(["day","week","month","year","d","w","m","y"]);function qe(t){if(!(!t||!$e.has(t)))return t}function Be(t){if(!(!t||!De.has(t)))return t}function He(t){let e=Number(t);return!Number.isFinite(e)||e<=0?Math.ceil(Ue/1e3):Math.ceil(e/1e3)}async function le(t,e){let{query:r,limit:s=10,categories:n,timeRange:o,apiKey:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!c)throw new Error("Tavily API key is required");if(e?.aborted)throw e.reason instanceof Error?e.reason:new Error("Tavily search aborted");let l=(0,ce.tavily)({apiKey:c}),u={maxResults:s,timeout:He(t.timeout)},a=qe(n);a&&(u.topic=a);let p=Be(o);p&&(u.timeRange=p);try{return{results:(await l.search(r,u)).results.map(m=>({title:m.title,url:m.url,snippet:m.content,engine:"tavily"})),success:!0}}catch(g){let h=g instanceof Error?g.message:"Tavily search error.";throw d.error(h),g}}var T=new R("[LocalSearch]");function ue(t){return j.includes(t)}function Ge(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function pe(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let c=o.filter(ue),l=o.filter(a=>!ue(a));if(l.length>0&&T.warn(`Invalid search engines ignored: ${l.join(", ")}`),c.length===0)throw new Error(`No valid search engines provided. Valid engines: ${j.join(", ")}`);let u=new x({headless:!0,timeout:3e4});return await v({browser:u,signal:e,task:async()=>{let a=[];for(let p of c)try{T.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>Ge(m,p));a.push(...h),T.info(`Found ${g.length} results from ${p}`);break}}catch(g){T.error(`Failed to search with ${p}:`,g)}return T.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw T.error(p,a),a})}var Fe="https://www.googleapis.com/customsearch/v1",Ne=2e4;async function he(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});c&&c!=="auto"&&l.set("lr",`lang_${c}`);let u=f({signal:e,timeoutMs:Ne,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${Fe}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var je="https://open.bigmodel.cn/api/paas/v4/web_search",Ke=2e4;async function ge(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=f({signal:e,timeoutMs:Ke,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(je,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var ze="https://api.exa.ai/search",We=2e4,Ve=1e4;async function me(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=f({signal:e,timeoutMs:We,timeoutMessage:"Exa search timeout"});try{let c=await fetch(ze,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:Ve}}}),signal:o.signal});if(!c.ok)throw new Error(`Exa search failed: ${c.status} ${c.statusText}`);return{results:(await c.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(c){let l=c instanceof Error?c.message:"Exa search error.";throw d.error(l),c}finally{o.cleanup()}}var Ze="https://api.bocha.cn/v1/web-search",Ye=2e4;async function de(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let c="noLimit";o&&(c={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=f({signal:e,timeoutMs:Ye,timeoutMessage:"Bocha search timeout"});try{let u=await fetch(Ze,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:c}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var Qe={searxngSearch:async(t,e)=>await ie(t,e),tavilySearch:async(t,e)=>await le(t,e),bingSearch:async(t,e)=>await se(t,e),duckDuckGoSearch:async(t,e)=>await oe(t,e),localSearch:async(t,e)=>await pe(t,e),googleSearch:async(t,e)=>await he(t,e),zhipuSearch:async(t,e)=>await ge(t,e),exaSearch:async(t,e)=>await me(t,e),bochaSearch:async(t,e)=>await de(t,e)};async function fe(t,e,r,s=Qe){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey},{categories:o,language:c}=e.defaultSearchOptions;return o&&(n.categories=o),c&&(n.language=c),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[H.SafeSearchType.STRICT,H.SafeSearchType.MODERATE,H.SafeSearchType.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}async function Se(t,e){let r=[];for(let s of t)try{let n=await e.scrapeUrl(s,{formats:["markdown"],onlyMainContent:!0});if(n.success&&n.markdown){r.push(`## Content from ${s}
12
9
 
13
- `)}]}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(P.name,{description:P.description,inputSchema:P.schema},M(P.name,async(t,e)=>{let{url:r,...s}=t,{content:n}=await nr(r,s,e.signal);return{content:n}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(O.name,{description:O.description,inputSchema:O.schema},M(O.name,async(t,e)=>{let{content:r}=await or(t.url,t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(C.name,{description:C.description,inputSchema:C.schema},M(C.name,async(t,e)=>{let{content:r}=await ar(t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));async function nr(t,e,r){let s=new x({headless:!0,timeout:3e4});return await v({browser:s,signal:r,task:async()=>{let n=await s.scrapeUrl(t,e);if(!n.success)throw new Error(`Failed to scrape: ${n.error}`);let o=[];return n.markdown&&o.push(n.markdown),n.rawHtml&&o.push(n.rawHtml),n.links&&o.push(n.links.join(`
14
- `)),n.screenshot&&o.push(n.screenshot),n.html&&o.push(n.html),{content:[{type:"text",text:o.join(`
10
+ ${n.markdown}`);continue}r.push(`## Failed to extract from ${s}
15
11
 
16
- `)||"No content found"}],result:n,success:!0}}})}async function or(t,e,r){let s=new x({headless:!0,timeout:3e4});return await v({browser:s,signal:r,task:async()=>{let n=await s.mapUrl(t,e);if(!n.success)throw new Error(`Failed to map: ${n.error}`);if(!n.links)throw new Error(`No links found from: ${t}`);return{content:[{type:"text",text:n.links.join(`
17
- `).trim()}],result:n.links,success:!0}}})}async function ar(t,e){let{urls:r,prompt:s,systemPrompt:n,schema:o}=t,c=new x({headless:!0,timeout:3e4});return await v({browser:c,signal:e,task:async()=>{let l=[];for(let a of r)try{let p=await c.scrapeUrl(a,{formats:["markdown"],onlyMainContent:!0});p.success&&p.markdown?l.push(`## Content from ${a}
12
+ Error: ${n.error||"Unknown error"}`)}catch(n){let o=n instanceof Error?n.message:String(n);r.push(`## Failed to extract from ${s}
18
13
 
19
- ${p.markdown}`):l.push(`## Failed to extract from ${a}
20
-
21
- Error: ${p.error||"Unknown error"}`)}catch(p){let g=p instanceof Error?p.message:String(p);l.push(`## Failed to extract from ${a}
22
-
23
- Error: ${g}`)}let u=l.join(`
14
+ Error: ${o}`)}return r.join(`
24
15
 
25
16
  ---
26
17
 
27
- `);if(s||n||o){let a=[];n&&a.push(`System Instructions: ${n}`),s&&a.push(`Extraction Task: ${s}`),o&&a.push(`Expected Schema:
28
- ${JSON.stringify(o,null,2)}`),u=`${a.join(`
29
-
30
- `)}
31
-
32
- ---
18
+ `)}function M(t,e,r={}){return async(s,n)=>{let o=Date.now();try{r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request started for tool: [${t}]`});let c=await e(s,n);return r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request completed in ${Date.now()-o}ms`}),c}catch(c){return r.logMessage&&await r.logMessage({level:"error",data:`[${new Date().toISOString()}] Error in ${t}: ${c}`}),{content:[{type:"text",text:c instanceof Error?c.message:String(c)}],isError:!0}}}}function we(t,e){let r,s=async()=>{r||(r=e()),await r},n=o=>{t.once(o,()=>{s().finally(()=>{typeof t.kill=="function"&&typeof t.pid=="number"&&t.kill(t.pid,o)})})};t.once("beforeExit",()=>{s()}),n("SIGINT"),n("SIGTERM")}function ye(t,e,r){let s,n=!1,o=u=>{n||(n=!0,r(u))},c=async()=>{s||(s=e()),await s},l=()=>{c().then(()=>{o(0)}).catch(()=>{o(1)})};t.once("end",l),t.once("close",l)}var Re=b(require("@dotenvx/dotenvx"),1);Re.default.config({quiet:!0});var Je=process.env.SEARCH_API_URL,Xe=process.env.SEARCH_API_KEY,er=process.env.SEARCH_PROVIDER??"local",rr=process.env.SAFE_SEARCH??0,tr=process.env.LIMIT??10,sr=process.env.CATEGORIES??"general",nr=process.env.ENGINES??"all",or=process.env.FORMAT??"json",ar=process.env.LANGUAGE??"auto",ir=process.env.TIME_RANGE??"",cr=process.env.TIMEOUT??1e4,w=new be.McpServer({name:"one-search-mcp",version:"1.2.0"},{capabilities:{tools:{},logging:{}}}),lr={limit:Number(tr),categories:sr,format:or,safeSearch:Number(rr),language:ar,engines:nr,timeRange:ir,timeout:cr};we(process,async()=>{await L.cleanup()});ye(process.stdin,async()=>{await L.cleanup(),await w.close()},t=>{process.exit(t)});w.registerTool(O.name,{description:O.description,inputSchema:O.schema},M(O.name,async(t,e)=>{let{results:r,success:s}=await fe(t,{provider:er,apiKey:Xe??"",apiUrl:Je,defaultSearchOptions:lr},e.signal);if(!s)throw new Error("Failed to search");return{content:[{type:"text",text:r.map(o=>`Title: ${o.title}
19
+ URL: ${o.url}
20
+ Description: ${o.snippet}
21
+ ${o.markdown?`Content: ${o.markdown}`:""}`).join(`
33
22
 
34
- Extracted Content:
23
+ `)}]}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(P.name,{description:P.description,inputSchema:P.schema},M(P.name,async(t,e)=>{let{url:r,...s}=t,{content:n}=await ur(r,s,e.signal);return{content:n}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(A.name,{description:A.description,inputSchema:A.schema},M(A.name,async(t,e)=>{let{content:r}=await pr(t.url,t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(C.name,{description:C.description,inputSchema:C.schema},M(C.name,async(t,e)=>{let{content:r}=await hr(t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));async function ur(t,e,r){let s=new x({headless:!0,timeout:3e4});return await v({browser:s,signal:r,task:async()=>{let n=await s.scrapeUrl(t,e);if(!n.success)throw new Error(`Failed to scrape: ${n.error}`);let o=[];return n.markdown&&o.push(n.markdown),n.rawHtml&&o.push(n.rawHtml),n.links&&o.push(n.links.join(`
24
+ `)),n.screenshot&&o.push(n.screenshot),n.html&&o.push(n.html),{content:[{type:"text",text:o.join(`
35
25
 
36
- ${u}`}return{content:[{type:"text",text:u}]}}})}async function ir(){try{let t=new ye.StdioServerTransport;await w.connect(t),await w.sendLoggingMessage({level:"info",data:"OneSearch MCP server started"})}catch(t){let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error starting server: ${e}
37
- `),process.exit(1)}}ir().catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error running server: ${e}
26
+ `)||"No content found"}],result:n,success:!0}}})}async function pr(t,e,r){let s=new x({headless:!0,timeout:3e4});return await v({browser:s,signal:r,task:async()=>{let n=await s.mapUrl(t,e);if(!n.success)throw new Error(`Failed to map: ${n.error}`);if(!n.links)throw new Error(`No links found from: ${t}`);return{content:[{type:"text",text:n.links.join(`
27
+ `).trim()}],result:n.links,success:!0}}})}async function hr(t,e){let{urls:r}=t,s=new x({headless:!0,timeout:3e4});return await v({browser:s,signal:e,task:async()=>({content:[{type:"text",text:await Se(r,s)}]})})}async function gr(){try{let t=new xe.StdioServerTransport;await w.connect(t),await w.sendLoggingMessage({level:"info",data:"OneSearch MCP server started"})}catch(t){let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error starting server: ${e}
28
+ `),process.exit(1)}}gr().catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error running server: ${e}
38
29
  `),process.exit(1)});
package/dist/index.js CHANGED
@@ -1,38 +1,29 @@
1
1
  #!/usr/bin/env node
2
- import{McpServer as Me}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as $e}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as i}from"zod/v3";var j=i.object({query:i.string().describe("Search query string"),limit:i.number().optional().describe("Maximum number of results to return (default: 10)"),language:i.string().optional().describe("Language code for search results (default: auto)"),categories:i.enum(["general","news","images","videos","it","science","map","music","files","social_media"]).optional().describe("Categories to search for (default: general)"),timeRange:i.enum(["all","day","week","month","year"]).optional().describe("Time range for search results (default: all)")}),K=i.object({url:i.string().describe("Starting URL for URL discovery"),search:i.string().optional().describe("Optional search term to filter URLs"),ignoreSitemap:i.boolean().optional().describe("Skip sitemap.xml discovery and only use HTML links"),sitemapOnly:i.boolean().optional().describe("Only use sitemap.xml for discovery, ignore HTML links"),includeSubdomains:i.boolean().optional().describe("Include URLs from subdomains in results"),limit:i.number().optional().describe("Maximum number of URLs to return")}),pe=i.discriminatedUnion("type",[i.object({type:i.literal("wait"),milliseconds:i.number().describe("Time to wait in milliseconds")}),i.object({type:i.literal("click"),selector:i.string().describe("CSS selector for the target element")}),i.object({type:i.literal("screenshot"),fullPage:i.boolean().optional().describe("Take full page screenshot")}),i.object({type:i.literal("write"),selector:i.string().describe("CSS selector for the target element"),text:i.string().describe("Text to write")}),i.object({type:i.literal("press"),key:i.string().describe("Key to press")}),i.object({type:i.literal("scroll"),direction:i.enum(["up","down"]).describe("Scroll direction")}),i.object({type:i.literal("scrape")}),i.object({type:i.literal("executeJavascript"),script:i.string().describe("JavaScript code to execute")})]),z=i.object({url:i.string().describe("The URL to scrape"),formats:i.array(i.enum(["markdown","html","rawHtml","screenshot","links","screenshot@fullPage","extract"])).optional().describe("Content formats to extract (default: ['markdown'])"),onlyMainContent:i.boolean().optional().describe("Extract only the main content, filtering out navigation, footers, etc."),includeTags:i.array(i.string()).optional().describe("HTML tags to specifically include in extraction"),excludeTags:i.array(i.string()).optional().describe("HTML tags to exclude from extraction"),waitFor:i.number().optional().describe("Time in milliseconds to wait for dynamic content to load"),timeout:i.number().optional().describe("Maximum time in milliseconds to wait for the page to load"),actions:i.array(pe).optional().describe("List of actions to perform before scraping"),extract:i.object({schema:i.record(i.any()).optional().describe("Schema for structured data extraction"),systemPrompt:i.string().optional().describe("System prompt for LLM extraction"),prompt:i.string().optional().describe("User prompt for LLM extraction")}).optional().describe("Configuration for structured data extraction"),mobile:i.boolean().optional().describe("Use mobile viewport"),skipTlsVerification:i.boolean().optional().describe("Skip TLS certificate verification"),removeBase64Images:i.boolean().optional().describe("Remove base64 encoded images from output"),location:i.object({country:i.string().optional().describe("Country code for geolocation"),languages:i.array(i.string()).optional().describe("Language codes for content")}).optional().describe("Location settings for scraping")}),W=i.object({urls:i.array(i.string()).describe("List of URLs to extract information from"),prompt:i.string().optional().describe("Prompt for the LLM extraction"),systemPrompt:i.string().optional().describe("System prompt for LLM extraction"),schema:i.record(i.any()).optional().describe("JSON schema for structured data extraction"),allowExternalLinks:i.boolean().optional().describe("Allow extraction from external links"),enableWebSearch:i.boolean().optional().describe("Enable web search for additional context"),includeSubdomains:i.boolean().optional().describe("Include subdomains in extraction")});var I={name:"one_search",description:"Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",schema:j},A={name:"one_map",description:"Discover URLs from a starting point. Can use both sitemap.xml and HTML link discovery.",schema:K},O={name:"one_scrape",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.",schema:z},P={name:"one_extract",description:"Extract structured information from web pages using LLM. Supports both cloud AI and self-hosted LLM extraction.",schema:W};import{BrowserManager as he}from"agent-browser/dist/browser.js";import me from"turndown";import{gfm as de}from"turndown-plugin-gfm";import*as L from"cheerio";import*as C from"fs";import*as H from"path";import*as Z from"os";import ge from"pino";var x=class{logger;constructor(e){this.logger=ge({name:e||"one-search-mcp",level:process.env.LOG_LEVEL||"info",transport:process.env.NODE_ENV==="development"?{target:"pino-pretty",options:{colorize:!0,translateTime:"SYS:standard",ignore:"pid,hostname"}}:void 0},process.stderr)}logWithLevel(e,r,...s){s.length>0?this.logger[e]({data:s},r):this.logger[e](r)}info(e,...r){this.logWithLevel("info",e,...r)}error(e,...r){this.logWithLevel("error",e,...r)}success(e,...r){r.length>0?this.logger.info({level:"success",data:r},e):this.logger.info({level:"success"},e)}warn(e,...r){this.logWithLevel("warn",e,...r)}log(e,...r){this.logWithLevel("info",e,...r)}},$=new x;var U=class{logger;constructor(e){this.logger=e??$}get browsers(){let e=Z.homedir(),r=process.env.LOCALAPPDATA;return[{name:"Chromium",executable:{win32:"C:\\Program Files\\Chromium\\Application\\chrome.exe",darwin:"/Applications/Chromium.app/Contents/MacOS/Chromium",linux:"/usr/bin/chromium"},userDataDir:{win32:`${r}\\Chromium\\User Data`,darwin:`${e}/Library/Application Support/Chromium`,linux:`${e}/.config/chromium`}},{name:"Google Chrome",executable:{win32:"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",darwin:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",linux:"/usr/bin/google-chrome"},userDataDir:{win32:`${r}\\Google\\Chrome\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome`,linux:`${e}/.config/google-chrome`}},{name:"Google Chrome Canary",executable:{win32:"C:\\Program Files\\Google\\Chrome Canary\\Application\\chrome.exe",darwin:"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",linux:"/usr/bin/google-chrome-canary"},userDataDir:{win32:`${r}\\Google\\Chrome Canary\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome Canary`,linux:`${e}/.config/google-chrome-canary`}},{name:"Microsoft Edge",executable:{win32:"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",darwin:"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",linux:"/usr/bin/microsoft-edge"},userDataDir:{win32:`${r}\\Microsoft\\Edge\\User Data`,darwin:`${e}/Library/Application Support/Microsoft Edge`,linux:`${e}/.config/microsoft-edge`}}]}findBrowser(e){let r=process.platform;if(this.logger.info("Finding browser on platform:",r),r!=="darwin"&&r!=="win32"&&r!=="linux"){let o=new Error(`Unsupported platform: ${r}`);throw this.logger.error(o.message),o}let s=e?this.browsers.find(o=>o.name===e&&C.existsSync(o.executable[r])):this.browsers.find(o=>C.existsSync(o.executable[r]));if(this.logger.log("browser",s),!s){let o=e?new Error(`Cannot find browser: ${e}`):new Error("Cannot find a supported browser on your system. Please install Chrome, Chromium, Edge, or Brave.");throw this.logger.error(o.message),o}let n={executable:s.executable[r],userDataDir:s.userDataDir[r]};return this.logger.success(`Found browser: ${s.name}`),this.logger.info("Browser details:",n),n}getBrowserProfiles(e){let r=this.findBrowser(e);try{let n=JSON.parse(C.readFileSync(H.join(r.userDataDir,"Local State"),"utf8")).profile.info_cache;return Object.entries(n).map(([o,c])=>({displayName:c.name,path:H.join(r.userDataDir,o)}))}catch{return[]}}findChrome(){try{let{executable:e}=this.findBrowser("Google Chrome");return e}catch{return null}}};var D={DEFAULT_WAIT_MS:2e3,SEARCH_WAIT_MS:3e3,DEFAULT_TIMEOUT:3e4,DEFAULT_HEADLESS:!0},G=["bing","google","baidu","sogou"];var b=class{constructor(e={}){this.options=e;this.browser=new he;try{let r=new U,{executable:s}=r.findBrowser();this.browserPath=s}catch{this.browserPath=void 0}this.turndown=new me({headingStyle:"atx",codeBlockStyle:"fenced"}),this.turndown.use(de)}browser;turndown;browserPath;async ensureLaunched(){if(!this.browser.isLaunched())try{await this.browser.launch({id:`session-${Date.now()}`,action:"launch",headless:this.options.headless??!0,executablePath:this.browserPath})}catch(e){let r=e instanceof Error?e.message:String(e);throw r.includes("Executable")||r.includes("browser")?new Error(`Browser not found. Please install one of the following:
2
+ import{McpServer as He}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as Ge}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as c}from"zod/v3";var j=c.object({query:c.string().describe("Search query string"),limit:c.number().optional().describe("Maximum number of results to return (default: 10)"),language:c.string().optional().describe("Language code for search results (default: auto)"),categories:c.enum(["general","news","images","videos","it","science","map","music","files","social_media"]).optional().describe("Categories to search for (default: general)"),timeRange:c.enum(["all","day","week","month","year"]).optional().describe("Time range for search results (default: all)")}),K=c.object({url:c.string().describe("Starting URL for URL discovery"),search:c.string().optional().describe("Optional search term to filter URLs"),ignoreSitemap:c.boolean().optional().describe("Skip sitemap.xml discovery and only use HTML links"),sitemapOnly:c.boolean().optional().describe("Only use sitemap.xml for discovery, ignore HTML links"),includeSubdomains:c.boolean().optional().describe("Include URLs from subdomains in results"),limit:c.number().optional().describe("Maximum number of URLs to return")}),he=c.discriminatedUnion("type",[c.object({type:c.literal("wait"),milliseconds:c.number().describe("Time to wait in milliseconds")}),c.object({type:c.literal("click"),selector:c.string().describe("CSS selector for the target element")}),c.object({type:c.literal("screenshot"),fullPage:c.boolean().optional().describe("Take full page screenshot")}),c.object({type:c.literal("write"),selector:c.string().describe("CSS selector for the target element"),text:c.string().describe("Text to write")}),c.object({type:c.literal("press"),key:c.string().describe("Key to press")}),c.object({type:c.literal("scroll"),direction:c.enum(["up","down"]).describe("Scroll direction")}),c.object({type:c.literal("scrape")}),c.object({type:c.literal("executeJavascript"),script:c.string().describe("JavaScript code to execute")})]),z=c.object({url:c.string().describe("The URL to scrape"),formats:c.array(c.enum(["markdown","html","rawHtml","screenshot","links","screenshot@fullPage","extract"])).optional().describe("Content formats to extract (default: ['markdown'])"),onlyMainContent:c.boolean().optional().describe("Extract only the main content, filtering out navigation, footers, etc."),includeTags:c.array(c.string()).optional().describe("HTML tags to specifically include in extraction"),excludeTags:c.array(c.string()).optional().describe("HTML tags to exclude from extraction"),waitFor:c.number().optional().describe("Time in milliseconds to wait for dynamic content to load"),timeout:c.number().optional().describe("Maximum time in milliseconds to wait for the page to load"),actions:c.array(he).optional().describe("List of actions to perform before scraping"),extract:c.object({schema:c.record(c.any()).optional().describe("Schema for structured data extraction"),systemPrompt:c.string().optional().describe("System prompt for LLM extraction"),prompt:c.string().optional().describe("User prompt for LLM extraction")}).optional().describe("Configuration for structured data extraction"),mobile:c.boolean().optional().describe("Use mobile viewport"),skipTlsVerification:c.boolean().optional().describe("Skip TLS certificate verification"),removeBase64Images:c.boolean().optional().describe("Remove base64 encoded images from output"),location:c.object({country:c.string().optional().describe("Country code for geolocation"),languages:c.array(c.string()).optional().describe("Language codes for content")}).optional().describe("Location settings for scraping")}),W=c.object({urls:c.array(c.string()).describe("List of URLs to extract information from")}).strict();var I={name:"one_search",description:"Search and retrieve content from web pages. Returns SERP results by default (url, title, description).",schema:j},O={name:"one_map",description:"Discover URLs from a starting point. Can use both sitemap.xml and HTML link discovery.",schema:K},A={name:"one_scrape",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.",schema:z},P={name:"one_extract",description:"Fetch and preprocess page content from one or more URLs. Returns cleaned text blocks that can be passed to downstream tools or models.",schema:W};import{BrowserManager as me}from"agent-browser/dist/browser.js";import de from"turndown";import{gfm as fe}from"turndown-plugin-gfm";import*as _ from"cheerio";import*as C from"fs";import*as H from"path";import*as V from"os";import ge from"pino";var x=class{logger;constructor(e){this.logger=ge({name:e||"one-search-mcp",level:process.env.LOG_LEVEL||"info",transport:process.env.NODE_ENV==="development"?{target:"pino-pretty",options:{colorize:!0,translateTime:"SYS:standard",ignore:"pid,hostname"}}:void 0},process.stderr)}logWithLevel(e,r,...s){s.length>0?this.logger[e]({data:s},r):this.logger[e](r)}info(e,...r){this.logWithLevel("info",e,...r)}error(e,...r){this.logWithLevel("error",e,...r)}success(e,...r){r.length>0?this.logger.info({level:"success",data:r},e):this.logger.info({level:"success"},e)}warn(e,...r){this.logWithLevel("warn",e,...r)}log(e,...r){this.logWithLevel("info",e,...r)}},U=new x;var $=class{logger;constructor(e){this.logger=e??U}get browsers(){let e=V.homedir(),r=process.env.LOCALAPPDATA;return[{name:"Chromium",executable:{win32:"C:\\Program Files\\Chromium\\Application\\chrome.exe",darwin:"/Applications/Chromium.app/Contents/MacOS/Chromium",linux:"/usr/bin/chromium"},userDataDir:{win32:`${r}\\Chromium\\User Data`,darwin:`${e}/Library/Application Support/Chromium`,linux:`${e}/.config/chromium`}},{name:"Google Chrome",executable:{win32:"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",darwin:"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",linux:"/usr/bin/google-chrome"},userDataDir:{win32:`${r}\\Google\\Chrome\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome`,linux:`${e}/.config/google-chrome`}},{name:"Google Chrome Canary",executable:{win32:"C:\\Program Files\\Google\\Chrome Canary\\Application\\chrome.exe",darwin:"/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary",linux:"/usr/bin/google-chrome-canary"},userDataDir:{win32:`${r}\\Google\\Chrome Canary\\User Data`,darwin:`${e}/Library/Application Support/Google/Chrome Canary`,linux:`${e}/.config/google-chrome-canary`}},{name:"Microsoft Edge",executable:{win32:"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",darwin:"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",linux:"/usr/bin/microsoft-edge"},userDataDir:{win32:`${r}\\Microsoft\\Edge\\User Data`,darwin:`${e}/Library/Application Support/Microsoft Edge`,linux:`${e}/.config/microsoft-edge`}}]}findBrowser(e){let r=process.platform;if(this.logger.info("Finding browser on platform:",r),r!=="darwin"&&r!=="win32"&&r!=="linux"){let o=new Error(`Unsupported platform: ${r}`);throw this.logger.error(o.message),o}let s=e?this.browsers.find(o=>o.name===e&&C.existsSync(o.executable[r])):this.browsers.find(o=>C.existsSync(o.executable[r]));if(this.logger.log("browser",s),!s){let o=e?new Error(`Cannot find browser: ${e}`):new Error("Cannot find a supported browser on your system. Please install Chrome, Chromium, Edge, or Brave.");throw this.logger.error(o.message),o}let n={executable:s.executable[r],userDataDir:s.userDataDir[r]};return this.logger.success(`Found browser: ${s.name}`),this.logger.info("Browser details:",n),n}getBrowserProfiles(e){let r=this.findBrowser(e);try{let n=JSON.parse(C.readFileSync(H.join(r.userDataDir,"Local State"),"utf8")).profile.info_cache;return Object.entries(n).map(([o,i])=>({displayName:i.name,path:H.join(r.userDataDir,o)}))}catch{return[]}}findChrome(){try{let{executable:e}=this.findBrowser("Google Chrome");return e}catch{return null}}};var D={DEFAULT_WAIT_MS:2e3,SEARCH_WAIT_MS:3e3,DEFAULT_TIMEOUT:3e4,DEFAULT_HEADLESS:!0},G=["bing","google","baidu","sogou"];var b=class{constructor(e={}){this.options=e;this.browser=new me;try{let r=new $,{executable:s}=r.findBrowser();this.browserPath=s}catch{this.browserPath=void 0}this.turndown=new de({headingStyle:"atx",codeBlockStyle:"fenced"}),this.turndown.use(fe)}browser;turndown;browserPath;async ensureLaunched(){if(!this.browser.isLaunched())try{await this.browser.launch({id:`session-${Date.now()}`,action:"launch",headless:this.options.headless??!0,executablePath:this.browserPath})}catch(e){let r=e instanceof Error?e.message:String(e);throw r.includes("Executable")||r.includes("browser")?new Error(`Browser not found. Please install one of the following:
3
3
  - Google Chrome: https://www.google.com/chrome/
4
4
  - Microsoft Edge: https://www.microsoft.com/edge
5
5
  - Chromium: https://www.chromium.org/getting-involved/download-chromium/
6
6
  Or install via Playwright:
7
7
  npx playwright install chromium
8
- Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(D.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(D.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(c=>{try{return new URL(c).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(D.SEARCH_WAIT_MS);let c=await this.getHtml();return this.extractSearchResults(s,c).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return $.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=L.load(e),s=[];return r("div.g").each((n,o)=>{let c=r(o),l=c.find("a[href]").first(),u=c.find("h3").first(),a=c.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=L.load(e),s=[];return r("li.b_algo").each((n,o)=>{let c=r(o),l=c.find("h2 a").first(),u=c.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=L.load(e),s=[];return r("div.result").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=L.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let c=r(o),l=c.find("h3 a").first(),u=c.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let c=new URL(o[1],r).href;n.includes(c)||n.push(c)}catch{}return n}};function fe(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var _=fe();function Se(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function V(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function R({browser:t,signal:e,task:r}){let s=_.track(t),n=Se(t,s);if(e?.aborted)throw await n(),V(e);let o;try{let c=r();if(!e)return await c;let l=new Promise((u,a)=>{o=()=>{n(),a(V(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([c,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}import{SafeSearchType as F}from"duck-duck-scrape";var d=new x("search");function f({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var we=2e4;async function Q(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:c="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=f({signal:e,timeoutMs:we,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([y,T])=>{T!==void 0&&h.set(y,T.toString())});let m=await fetch(`${c}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(y=>({title:y.name,snippet:y.snippet,url:y.url,source:y.siteName,thumbnailUrl:y.thumbnailUrl,language:y.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}import*as q from"duck-duck-scrape";import ye from"async-retry";async function Y(t,e){let{query:r,timeout:s=1e4,safeSearch:n=q.SafeSearchType.OFF,retry:o={retries:3},...c}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await ye(async a=>{try{return await q.search(r,{...c,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}import be from"url";async function J(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:c="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let B=f({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let E={q:r,pageno:s,categories:o,format:u,safesearch:l,language:a,engines:c,time_range:p},M=`${m}/search`,y=be.format({query:E}),T={"Content-Type":"application/json"};h&&(T.Authorization=`Bearer ${h}`);let N=await(await fetch(`${M}${y}`,{method:"POST",headers:T,signal:B.signal})).json();return N.results?{results:N.results.slice(0,n).map(S=>{let le=S.img_src?{thumbnail:S.thumbnail_src,src:S.img_src}:null,ue=S.iframe_src?{thumbnail:S.thumbnail_src,src:S.iframe_src}:null;return{title:S.title,snippet:S.content,url:S.url,source:S.source,image:le,video:ue,engine:S.engine}}),success:!0}:{results:[],success:!1}}catch(E){let M=E instanceof Error?E.message:"Searxng search error.";throw d.error(M),E}finally{B.cleanup()}}var xe="https://api.tavily.com/search",Ee=2e4;async function X(t,e){let{query:r,limit:s=10,categories:n="general",timeRange:o,apiKey:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!c)throw new Error("Tavily API key is required");let l=f({signal:e,timeoutMs:Ee,timeoutMessage:"Tavily search timeout"});try{let u=await fetch(xe,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${c}`},body:JSON.stringify({query:r,topic:n,time_range:o,max_results:s}),signal:l.signal});if(!u.ok)throw new Error(`Tavily search failed: ${u.status} ${u.statusText}`);return{results:(await u.json()).results.map(g=>({title:g.title,url:g.url,snippet:g.content,engine:"tavily"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Tavily search error.";throw d.error(a),u}finally{l.cleanup()}}var v=new x("[LocalSearch]");function ee(t){return G.includes(t)}function Re(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function re(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let c=o.filter(ee),l=o.filter(a=>!ee(a));if(l.length>0&&v.warn(`Invalid search engines ignored: ${l.join(", ")}`),c.length===0)throw new Error(`No valid search engines provided. Valid engines: ${G.join(", ")}`);let u=new b({headless:!0,timeout:3e4});return await R({browser:u,signal:e,task:async()=>{let a=[];for(let p of c)try{v.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>Re(m,p));a.push(...h),v.info(`Found ${g.length} results from ${p}`);break}}catch(g){v.error(`Failed to search with ${p}:`,g)}return v.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw v.error(p,a),a})}var ve="https://www.googleapis.com/customsearch/v1",Te=2e4;async function te(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:c}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});c&&c!=="auto"&&l.set("lr",`lang_${c}`);let u=f({signal:e,timeoutMs:Te,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${ve}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var Ie="https://open.bigmodel.cn/api/paas/v4/web_search",Ae=2e4;async function se(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=f({signal:e,timeoutMs:Ae,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(Ie,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var Oe="https://api.exa.ai/search",Pe=2e4,Ce=1e4;async function ne(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=f({signal:e,timeoutMs:Pe,timeoutMessage:"Exa search timeout"});try{let c=await fetch(Oe,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:Ce}}}),signal:o.signal});if(!c.ok)throw new Error(`Exa search failed: ${c.status} ${c.statusText}`);return{results:(await c.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(c){let l=c instanceof Error?c.message:"Exa search error.";throw d.error(l),c}finally{o.cleanup()}}var Le="https://api.bocha.cn/v1/web-search",_e=2e4;async function oe(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let c="noLimit";o&&(c={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=f({signal:e,timeoutMs:_e,timeoutMessage:"Bocha search timeout"});try{let u=await fetch(Le,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:c}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var ke={searxngSearch:async(t,e)=>await J(t,e),tavilySearch:async(t,e)=>await X(t,e),bingSearch:async(t,e)=>await Q(t,e),duckDuckGoSearch:async(t,e)=>await Y(t,e),localSearch:async(t,e)=>await re(t,e),googleSearch:async(t,e)=>await te(t,e),zhipuSearch:async(t,e)=>await se(t,e),exaSearch:async(t,e)=>await ne(t,e),bochaSearch:async(t,e)=>await oe(t,e)};async function ae(t,e,r,s=ke){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey},{categories:o,language:c}=e.defaultSearchOptions;return o&&(n.categories=o),c&&(n.language=c),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[F.STRICT,F.MODERATE,F.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}function k(t,e,r={}){return async(s,n)=>{let o=Date.now();try{r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request started for tool: [${t}]`});let c=await e(s,n);return r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request completed in ${Date.now()-o}ms`}),c}catch(c){return r.logMessage&&await r.logMessage({level:"error",data:`[${new Date().toISOString()}] Error in ${t}: ${c}`}),{content:[{type:"text",text:c instanceof Error?c.message:String(c)}],isError:!0}}}}function ie(t,e){let r,s=async()=>{r||(r=e()),await r},n=o=>{t.once(o,()=>{s().finally(()=>{typeof t.kill=="function"&&typeof t.pid=="number"&&t.kill(t.pid,o)})})};t.once("beforeExit",()=>{s()}),n("SIGINT"),n("SIGTERM")}function ce(t,e,r){let s,n=!1,o=u=>{n||(n=!0,r(u))},c=async()=>{s||(s=e()),await s},l=()=>{c().then(()=>{o(0)}).catch(()=>{o(1)})};t.once("end",l),t.once("close",l)}import Ue from"@dotenvx/dotenvx";Ue.config({quiet:!0});var De=process.env.SEARCH_API_URL,qe=process.env.SEARCH_API_KEY,Be=process.env.SEARCH_PROVIDER??"local",He=process.env.SAFE_SEARCH??0,Ge=process.env.LIMIT??10,Fe=process.env.CATEGORIES??"general",Ne=process.env.ENGINES??"all",je=process.env.FORMAT??"json",Ke=process.env.LANGUAGE??"auto",ze=process.env.TIME_RANGE??"",We=process.env.TIMEOUT??1e4,w=new Me({name:"one-search-mcp",version:"1.1.3"},{capabilities:{tools:{},logging:{}}}),Ze={limit:Number(Ge),categories:Fe,format:je,safeSearch:Number(He),language:Ke,engines:Ne,timeRange:ze,timeout:We};ie(process,async()=>{await _.cleanup()});ce(process.stdin,async()=>{await _.cleanup(),await w.close()},t=>{process.exit(t)});w.registerTool(I.name,{description:I.description,inputSchema:I.schema},k(I.name,async(t,e)=>{let{results:r,success:s}=await ae(t,{provider:Be,apiKey:qe??"",apiUrl:De,defaultSearchOptions:Ze},e.signal);if(!s)throw new Error("Failed to search");return{content:[{type:"text",text:r.map(o=>`Title: ${o.title}
9
- URL: ${o.url}
10
- Description: ${o.snippet}
11
- ${o.markdown?`Content: ${o.markdown}`:""}`).join(`
8
+ Original error: ${r}`):e}}async getPage(){return await this.ensureLaunched(),this.browser.getPage()}async navigate(e){await(await this.getPage()).goto(e,{waitUntil:"domcontentloaded",timeout:this.options.timeout})}async getHtml(){return await(await this.getPage()).content()}async getText(){return await(await this.getPage()).evaluate(()=>document.body.innerText)}async screenshot(){return`data:image/png;base64,${(await(await this.getPage()).screenshot({type:"png",fullPage:!1})).toString("base64")}`}async wait(e){await new Promise(r=>setTimeout(r,e))}async close(){this.browser.isLaunched()&&await this.browser.close()}async scrapeUrl(e,r={}){try{await this.navigate(e),r.waitFor?await this.wait(r.waitFor):await this.wait(D.DEFAULT_WAIT_MS);let s={success:!0},n=r.formats||["markdown"],o=await this.getHtml();return n.includes("markdown")&&(s.markdown=this.turndown.turndown(o)),(n.includes("html")||n.includes("rawHtml"))&&(s.html=o,s.rawHtml=o),n.includes("links")&&(s.links=this.extractLinks(o,e)),(n.includes("screenshot")||n.includes("screenshot@fullPage"))&&(s.screenshot=await this.screenshot()),s}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async mapUrl(e,r={}){try{await this.navigate(e),await this.wait(D.DEFAULT_WAIT_MS);let s=await this.getHtml(),n=this.extractLinks(s,e);if(r.search&&(n=n.filter(o=>o.toLowerCase().includes(r.search.toLowerCase()))),!r.includeSubdomains){let o=new URL(e).hostname;n=n.filter(i=>{try{return new URL(i).hostname===o}catch{return!1}})}return r.limit&&(n=n.slice(0,r.limit)),{success:!0,links:n}}catch(s){return{success:!1,error:s instanceof Error?s.message:String(s)}}}async search(e){let{query:r,engine:s,limit:n=10}=e,o=this.getSearchUrl(s,r);await this.navigate(o),await this.wait(D.SEARCH_WAIT_MS);let i=await this.getHtml();return this.extractSearchResults(s,i).slice(0,n)}getSearchUrl(e,r){let s=encodeURIComponent(r);switch(e){case"google":return`https://www.google.com/search?q=${s}`;case"bing":return`https://www.bing.com/search?q=${s}`;case"baidu":return`https://www.baidu.com/s?wd=${s}`;case"sogou":return`https://www.sogou.com/web?query=${s}`;default:throw new Error(`Unsupported search engine: ${e}`)}}extractSearchResults(e,r){try{switch(e){case"google":return this.extractGoogleResults(r);case"bing":return this.extractBingResults(r);case"baidu":return this.extractBaiduResults(r);case"sogou":return this.extractSogouResults(r);default:return[]}}catch(s){let n=s instanceof Error?s.message:String(s);return U.warn(`Failed to extract ${e} search results: ${n}`),[]}}extractGoogleResults(e){let r=_.load(e),s=[];return r("div.g").each((n,o)=>{let i=r(o),l=i.find("a[href]").first(),u=i.find("h3").first(),a=i.find("div.VwiC3b, div[data-sncf]").first(),p=l.attr("href"),g=u.text().trim(),h=a.text().trim();p&&g&&!p.startsWith("/search")&&s.push({title:g,url:p,snippet:h})}),s}extractBingResults(e){let r=_.load(e),s=[];return r("li.b_algo").each((n,o)=>{let i=r(o),l=i.find("h2 a").first(),u=i.find("p, div.b_caption p").first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractBaiduResults(e){let r=_.load(e),s=[];return r("div.result").each((n,o)=>{let i=r(o),l=i.find("h3 a").first(),u=i.find('div.c-abstract, div[class*="abstract"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractSogouResults(e){let r=_.load(e),s=[];return r("div.vrwrap").each((n,o)=>{let i=r(o),l=i.find("h3 a").first(),u=i.find('p.str-text, p[class*="text"]').first(),a=l.attr("href"),p=l.text().trim(),g=u.text().trim();a&&p&&s.push({title:p,url:a,snippet:g})}),s}extractLinks(e,r){let s=/<a[^>]+href=["']([^"']+)["']/gi,n=[],o;for(;(o=s.exec(e))!==null;)try{let i=new URL(o[1],r).href;n.includes(i)||n.push(i)}catch{}return n}};function Se(){let t=new Set,e;return{get size(){return t.size},track(r){return e?(r.close(),()=>{}):(t.add(r),()=>{t.delete(r)})},async cleanup(){e||(e=(async()=>{let r=[...t];t.clear(),await Promise.allSettled(r.map(async s=>{await s.close()}))})()),await e}}}var k=Se();function we(t,e){let r;return async()=>{r||(r=(async()=>{e(),await t.close()})()),await r}}function Z(t){return t.reason??new DOMException("The operation was aborted.","AbortError")}async function E({browser:t,signal:e,task:r}){let s=k.track(t),n=we(t,s);if(e?.aborted)throw await n(),Z(e);let o;try{let i=r();if(!e)return await i;let l=new Promise((u,a)=>{o=()=>{n(),a(Z(e))},e.addEventListener("abort",o,{once:!0})});return await Promise.race([i,l])}finally{o&&e?.removeEventListener("abort",o),await n()}}import{SafeSearchType as F}from"duck-duck-scrape";var d=new x("search");function f({signal:t,timeoutMs:e,timeoutMessage:r}){if(e===void 0)return{signal:t,cleanup:()=>{}};let s=new AbortController,n=setTimeout(()=>{s.abort(new Error(r))},e);return{signal:t?AbortSignal.any([t,s.signal]):s.signal,cleanup:()=>{clearTimeout(n)}}}var ye=2e4;async function Y(t,e){let{query:r,limit:s=10,safeSearch:n=0,page:o=1,apiUrl:i="https://api.bing.microsoft.com/v7.0/search",apiKey:l,language:u}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!l)throw new Error("Bing API key is required");let a=["Off","Moderate","Strict"],p={q:r,count:s,offset:(o-1)*s,mkt:u,safeSearch:a[n]},g=f({signal:e,timeoutMs:ye,timeoutMessage:"Bing search timeout"});try{let h=new URLSearchParams;Object.entries(p).forEach(([y,T])=>{T!==void 0&&h.set(y,T.toString())});let m=await fetch(`${i}?${h}`,{method:"GET",headers:{"Content-Type":"application/json","Ocp-Apim-Subscription-Key":l},signal:g.signal});if(!m.ok)throw new Error(`Bing search error: ${m.status} ${m.statusText}`);return{results:(await m.json()).webPages?.value?.map(y=>({title:y.name,snippet:y.snippet,url:y.url,source:y.siteName,thumbnailUrl:y.thumbnailUrl,language:y.language,image:null,video:null,engine:"bing"}))??[],success:!0}}catch(h){let m=h instanceof Error?h.message:"Bing search error.";throw d.error(m),h}finally{g.cleanup()}}import*as q from"duck-duck-scrape";import be from"async-retry";async function Q(t,e){let{query:r,timeout:s=1e4,safeSearch:n=q.SafeSearchType.OFF,retry:o={retries:3},...i}=t;if(!r?.trim())throw new Error("Query cannot be empty");try{let l=await be(async a=>{try{return await q.search(r,{...i,safeSearch:n},{response_timeout:s,signal:e})}catch(p){throw e?.aborted&&a(e.reason??p),p}},o);return{results:(l?{noResults:l.noResults,vqd:l.vqd,results:l.results}:{noResults:!0,vqd:"",results:[]}).results.map(a=>({title:a.title,snippet:a.description,url:a.url,source:a.hostname,image:null,video:null,engine:"duckduckgo"})),success:!0}}catch(l){let u=l instanceof Error?l.message:"DuckDuckGo search error.";throw d.error(u),l}}import xe from"url";async function J(t,e){let{query:r,page:s=1,limit:n=10,categories:o="general",engines:i="all",safeSearch:l=0,format:u="json",language:a="auto",timeRange:p="",timeout:g=1e4,apiKey:h,apiUrl:m}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!m)throw new Error("SearxNG API URL is required");let B=f({signal:e,timeoutMs:Number(g),timeoutMessage:"Searxng search timeout"});try{let R={q:r,pageno:s,categories:o,format:u,safesearch:l,language:a,engines:i,time_range:p},M=`${m}/search`,y=xe.format({query:R}),T={"Content-Type":"application/json"};h&&(T.Authorization=`Bearer ${h}`);let N=await(await fetch(`${M}${y}`,{method:"POST",headers:T,signal:B.signal})).json();return N.results?{results:N.results.slice(0,n).map(S=>{let ue=S.img_src?{thumbnail:S.thumbnail_src,src:S.img_src}:null,pe=S.iframe_src?{thumbnail:S.thumbnail_src,src:S.iframe_src}:null;return{title:S.title,snippet:S.content,url:S.url,source:S.source,image:ue,video:pe,engine:S.engine}}),success:!0}:{results:[],success:!1}}catch(R){let M=R instanceof Error?R.message:"Searxng search error.";throw d.error(M),R}finally{B.cleanup()}}import{tavily as Re}from"@tavily/core";var Ee=2e4,ve=new Set(["general","news","finance"]),Te=new Set(["day","week","month","year","d","w","m","y"]);function Ie(t){if(!(!t||!ve.has(t)))return t}function Oe(t){if(!(!t||!Te.has(t)))return t}function Ae(t){let e=Number(t);return!Number.isFinite(e)||e<=0?Math.ceil(Ee/1e3):Math.ceil(e/1e3)}async function X(t,e){let{query:r,limit:s=10,categories:n,timeRange:o,apiKey:i}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!i)throw new Error("Tavily API key is required");if(e?.aborted)throw e.reason instanceof Error?e.reason:new Error("Tavily search aborted");let l=Re({apiKey:i}),u={maxResults:s,timeout:Ae(t.timeout)},a=Ie(n);a&&(u.topic=a);let p=Oe(o);p&&(u.timeRange=p);try{return{results:(await l.search(r,u)).results.map(m=>({title:m.title,url:m.url,snippet:m.content,engine:"tavily"})),success:!0}}catch(g){let h=g instanceof Error?g.message:"Tavily search error.";throw d.error(h),g}}var v=new x("[LocalSearch]");function ee(t){return G.includes(t)}function Pe(t,e){return{title:t.title,snippet:t.snippet,url:t.url,markdown:t.content,engine:e}}async function re(t,e){let{query:r,limit:s=10}=t,{engines:n="all"}=t;n==="all"&&(n="bing,google,baidu,sogou");let o=n.split(",").map(a=>a.trim()).filter(Boolean);if(o.length===0)throw new Error("engines is required");let i=o.filter(ee),l=o.filter(a=>!ee(a));if(l.length>0&&v.warn(`Invalid search engines ignored: ${l.join(", ")}`),i.length===0)throw new Error(`No valid search engines provided. Valid engines: ${G.join(", ")}`);let u=new b({headless:!0,timeout:3e4});return await E({browser:u,signal:e,task:async()=>{let a=[];for(let p of i)try{v.info(`Searching with engine: ${p}`);let g=await u.search({query:r,engine:p,limit:s});if(g.length>0){let h=g.map(m=>Pe(m,p));a.push(...h),v.info(`Found ${g.length} results from ${p}`);break}}catch(g){v.error(`Failed to search with ${p}:`,g)}return v.info(`Total results found: ${a.length}`),{results:a,success:!0}}}).catch(a=>{let p=a instanceof Error?a.message:"Local search error.";throw v.error(p,a),a})}var Ce="https://www.googleapis.com/customsearch/v1",_e=2e4;async function te(t,e){let{query:r,apiKey:s,apiUrl:n,limit:o=10,language:i}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s||!n)throw new Error("Google search requires SEARCH_API_KEY and SEARCH_API_URL (Search Engine ID)");let l=new URLSearchParams({key:s,cx:n,q:r,num:String(Math.min(o,10))});i&&i!=="auto"&&l.set("lr",`lang_${i}`);let u=f({signal:e,timeoutMs:_e,timeoutMessage:"Google search timeout"});try{let a=await fetch(`${Ce}?${l}`,{method:"GET",signal:u.signal});if(!a.ok)throw new Error(`Google search failed: ${a.status} ${a.statusText}`);let p=await a.json();if(p.error)throw new Error(`Google API error: ${p.error.message}`);return{results:(p.items??[]).map(m=>({title:m.title,url:m.link,snippet:m.snippet||"",source:m.displayLink,thumbnailUrl:m.pagemap?.cse_thumbnail?.[0]?.src,engine:"google"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Google search error.";throw d.error(p),a}finally{u.cleanup()}}var ke="https://open.bigmodel.cn/api/paas/v4/web_search",Le=2e4;async function se(t,e){let{query:r,apiKey:s,limit:n=10,engines:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Zhipu search requires SEARCH_API_KEY");let l=["search_std","search_pro","search_pro_sogou","search_pro_quark","search_pro_jina","search_pro_bing"].includes(o)?o:"search_std",u=f({signal:e,timeoutMs:Le,timeoutMessage:"Zhipu search timeout"});try{let a=await fetch(ke,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({search_engine:l,search_query:r,search_intent:!1,count:n}),signal:u.signal});if(!a.ok)throw new Error(`Zhipu search failed: ${a.status} ${a.statusText}`);return{results:((await a.json()).search_result??[]).map(m=>({title:m.title,url:m.link,snippet:m.content,source:m.media,engine:"zhipu"})),success:!0}}catch(a){let p=a instanceof Error?a.message:"Zhipu search error.";throw d.error(p),a}finally{u.cleanup()}}var Me="https://api.exa.ai/search",Ue=2e4,$e=1e4;async function ne(t,e){let{query:r,apiKey:s,limit:n=10}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Exa search requires SEARCH_API_KEY");let o=f({signal:e,timeoutMs:Ue,timeoutMessage:"Exa search timeout"});try{let i=await fetch(Me,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":s},body:JSON.stringify({query:r,numResults:n,contents:{text:{maxCharacters:$e}}}),signal:o.signal});if(!i.ok)throw new Error(`Exa search failed: ${i.status} ${i.statusText}`);return{results:(await i.json()).results.map(a=>({title:a.title||"",url:a.url,snippet:a.text||"",engine:"exa"})),success:!0}}catch(i){let l=i instanceof Error?i.message:"Exa search error.";throw d.error(l),i}finally{o.cleanup()}}var De="https://api.bocha.cn/v1/web-search",qe=2e4;async function oe(t,e){let{query:r,apiKey:s,limit:n=10,timeRange:o}=t;if(!r?.trim())throw new Error("Query cannot be empty");if(!s)throw new Error("Bocha search requires SEARCH_API_KEY");let i="noLimit";o&&(i={day:"oneDay",week:"oneWeek",month:"oneMonth",year:"oneYear"}[o]||"noLimit");let l=f({signal:e,timeoutMs:qe,timeoutMessage:"Bocha search timeout"});try{let u=await fetch(De,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${s}`},body:JSON.stringify({query:r,count:n,summary:!0,freshness:i}),signal:l.signal});if(!u.ok)throw new Error(`Bocha search failed: ${u.status} ${u.statusText}`);let a=await u.json();return{results:(a?.data?.webPages?.value||a?.webPages?.value||a?.results||[]).map(h=>({title:h.name||h.title||"",url:h.url||h.link||"",snippet:h.snippet||h.summary||h.content||"",source:h.siteName||h.site_name,thumbnailUrl:h.urlToImage||h.icon,engine:"bocha"})),success:!0}}catch(u){let a=u instanceof Error?u.message:"Bocha search error.";throw d.error(a),u}finally{l.cleanup()}}var Be={searxngSearch:async(t,e)=>await J(t,e),tavilySearch:async(t,e)=>await X(t,e),bingSearch:async(t,e)=>await Y(t,e),duckDuckGoSearch:async(t,e)=>await Q(t,e),localSearch:async(t,e)=>await re(t,e),googleSearch:async(t,e)=>await te(t,e),zhipuSearch:async(t,e)=>await se(t,e),exaSearch:async(t,e)=>await ne(t,e),bochaSearch:async(t,e)=>await oe(t,e)};async function ae(t,e,r,s=Be){switch(e.provider){case"searxng":{let n={...e.defaultSearchOptions,...t,apiKey:e.apiKey},{categories:o,language:i}=e.defaultSearchOptions;return o&&(n.categories=o),i&&(n.language=i),await s.searxngSearch(n,r)}case"tavily":return await s.tavilySearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bing":return await s.bingSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"duckduckgo":{let n=t.safeSearch??0,o=[F.STRICT,F.MODERATE,F.OFF];return await s.duckDuckGoSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,safeSearch:o[n]},r)}case"local":return await s.localSearch({...e.defaultSearchOptions,...t},r);case"google":return await s.googleSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey,apiUrl:e.apiUrl},r);case"zhipu":return await s.zhipuSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"exa":return await s.exaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);case"bocha":return await s.bochaSearch({...e.defaultSearchOptions,...t,apiKey:e.apiKey},r);default:throw new Error(`Unsupported search provider: ${e.provider}`)}}async function ie(t,e){let r=[];for(let s of t)try{let n=await e.scrapeUrl(s,{formats:["markdown"],onlyMainContent:!0});if(n.success&&n.markdown){r.push(`## Content from ${s}
12
9
 
13
- `)}]}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(O.name,{description:O.description,inputSchema:O.schema},k(O.name,async(t,e)=>{let{url:r,...s}=t,{content:n}=await Ve(r,s,e.signal);return{content:n}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(A.name,{description:A.description,inputSchema:A.schema},k(A.name,async(t,e)=>{let{content:r}=await Qe(t.url,t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(P.name,{description:P.description,inputSchema:P.schema},k(P.name,async(t,e)=>{let{content:r}=await Ye(t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));async function Ve(t,e,r){let s=new b({headless:!0,timeout:3e4});return await R({browser:s,signal:r,task:async()=>{let n=await s.scrapeUrl(t,e);if(!n.success)throw new Error(`Failed to scrape: ${n.error}`);let o=[];return n.markdown&&o.push(n.markdown),n.rawHtml&&o.push(n.rawHtml),n.links&&o.push(n.links.join(`
14
- `)),n.screenshot&&o.push(n.screenshot),n.html&&o.push(n.html),{content:[{type:"text",text:o.join(`
10
+ ${n.markdown}`);continue}r.push(`## Failed to extract from ${s}
15
11
 
16
- `)||"No content found"}],result:n,success:!0}}})}async function Qe(t,e,r){let s=new b({headless:!0,timeout:3e4});return await R({browser:s,signal:r,task:async()=>{let n=await s.mapUrl(t,e);if(!n.success)throw new Error(`Failed to map: ${n.error}`);if(!n.links)throw new Error(`No links found from: ${t}`);return{content:[{type:"text",text:n.links.join(`
17
- `).trim()}],result:n.links,success:!0}}})}async function Ye(t,e){let{urls:r,prompt:s,systemPrompt:n,schema:o}=t,c=new b({headless:!0,timeout:3e4});return await R({browser:c,signal:e,task:async()=>{let l=[];for(let a of r)try{let p=await c.scrapeUrl(a,{formats:["markdown"],onlyMainContent:!0});p.success&&p.markdown?l.push(`## Content from ${a}
12
+ Error: ${n.error||"Unknown error"}`)}catch(n){let o=n instanceof Error?n.message:String(n);r.push(`## Failed to extract from ${s}
18
13
 
19
- ${p.markdown}`):l.push(`## Failed to extract from ${a}
20
-
21
- Error: ${p.error||"Unknown error"}`)}catch(p){let g=p instanceof Error?p.message:String(p);l.push(`## Failed to extract from ${a}
22
-
23
- Error: ${g}`)}let u=l.join(`
14
+ Error: ${o}`)}return r.join(`
24
15
 
25
16
  ---
26
17
 
27
- `);if(s||n||o){let a=[];n&&a.push(`System Instructions: ${n}`),s&&a.push(`Extraction Task: ${s}`),o&&a.push(`Expected Schema:
28
- ${JSON.stringify(o,null,2)}`),u=`${a.join(`
29
-
30
- `)}
31
-
32
- ---
18
+ `)}function L(t,e,r={}){return async(s,n)=>{let o=Date.now();try{r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request started for tool: [${t}]`});let i=await e(s,n);return r.logMessage&&await r.logMessage({level:"info",data:`[${new Date().toISOString()}] Request completed in ${Date.now()-o}ms`}),i}catch(i){return r.logMessage&&await r.logMessage({level:"error",data:`[${new Date().toISOString()}] Error in ${t}: ${i}`}),{content:[{type:"text",text:i instanceof Error?i.message:String(i)}],isError:!0}}}}function ce(t,e){let r,s=async()=>{r||(r=e()),await r},n=o=>{t.once(o,()=>{s().finally(()=>{typeof t.kill=="function"&&typeof t.pid=="number"&&t.kill(t.pid,o)})})};t.once("beforeExit",()=>{s()}),n("SIGINT"),n("SIGTERM")}function le(t,e,r){let s,n=!1,o=u=>{n||(n=!0,r(u))},i=async()=>{s||(s=e()),await s},l=()=>{i().then(()=>{o(0)}).catch(()=>{o(1)})};t.once("end",l),t.once("close",l)}import Fe from"@dotenvx/dotenvx";Fe.config({quiet:!0});var Ne=process.env.SEARCH_API_URL,je=process.env.SEARCH_API_KEY,Ke=process.env.SEARCH_PROVIDER??"local",ze=process.env.SAFE_SEARCH??0,We=process.env.LIMIT??10,Ve=process.env.CATEGORIES??"general",Ze=process.env.ENGINES??"all",Ye=process.env.FORMAT??"json",Qe=process.env.LANGUAGE??"auto",Je=process.env.TIME_RANGE??"",Xe=process.env.TIMEOUT??1e4,w=new He({name:"one-search-mcp",version:"1.2.0"},{capabilities:{tools:{},logging:{}}}),er={limit:Number(We),categories:Ve,format:Ye,safeSearch:Number(ze),language:Qe,engines:Ze,timeRange:Je,timeout:Xe};ce(process,async()=>{await k.cleanup()});le(process.stdin,async()=>{await k.cleanup(),await w.close()},t=>{process.exit(t)});w.registerTool(I.name,{description:I.description,inputSchema:I.schema},L(I.name,async(t,e)=>{let{results:r,success:s}=await ae(t,{provider:Ke,apiKey:je??"",apiUrl:Ne,defaultSearchOptions:er},e.signal);if(!s)throw new Error("Failed to search");return{content:[{type:"text",text:r.map(o=>`Title: ${o.title}
19
+ URL: ${o.url}
20
+ Description: ${o.snippet}
21
+ ${o.markdown?`Content: ${o.markdown}`:""}`).join(`
33
22
 
34
- Extracted Content:
23
+ `)}]}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(A.name,{description:A.description,inputSchema:A.schema},L(A.name,async(t,e)=>{let{url:r,...s}=t,{content:n}=await rr(r,s,e.signal);return{content:n}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(O.name,{description:O.description,inputSchema:O.schema},L(O.name,async(t,e)=>{let{content:r}=await tr(t.url,t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));w.registerTool(P.name,{description:P.description,inputSchema:P.schema},L(P.name,async(t,e)=>{let{content:r}=await sr(t,e.signal);return{content:r}},{logMessage:t=>w.sendLoggingMessage(t)}));async function rr(t,e,r){let s=new b({headless:!0,timeout:3e4});return await E({browser:s,signal:r,task:async()=>{let n=await s.scrapeUrl(t,e);if(!n.success)throw new Error(`Failed to scrape: ${n.error}`);let o=[];return n.markdown&&o.push(n.markdown),n.rawHtml&&o.push(n.rawHtml),n.links&&o.push(n.links.join(`
24
+ `)),n.screenshot&&o.push(n.screenshot),n.html&&o.push(n.html),{content:[{type:"text",text:o.join(`
35
25
 
36
- ${u}`}return{content:[{type:"text",text:u}]}}})}async function Je(){try{let t=new $e;await w.connect(t),await w.sendLoggingMessage({level:"info",data:"OneSearch MCP server started"})}catch(t){let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error starting server: ${e}
37
- `),process.exit(1)}}Je().catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error running server: ${e}
26
+ `)||"No content found"}],result:n,success:!0}}})}async function tr(t,e,r){let s=new b({headless:!0,timeout:3e4});return await E({browser:s,signal:r,task:async()=>{let n=await s.mapUrl(t,e);if(!n.success)throw new Error(`Failed to map: ${n.error}`);if(!n.links)throw new Error(`No links found from: ${t}`);return{content:[{type:"text",text:n.links.join(`
27
+ `).trim()}],result:n.links,success:!0}}})}async function sr(t,e){let{urls:r}=t,s=new b({headless:!0,timeout:3e4});return await E({browser:s,signal:e,task:async()=>({content:[{type:"text",text:await ie(r,s)}]})})}async function nr(){try{let t=new Ge;await w.connect(t),await w.sendLoggingMessage({level:"info",data:"OneSearch MCP server started"})}catch(t){let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error starting server: ${e}
28
+ `),process.exit(1)}}nr().catch(t=>{let e=t instanceof Error?t.message:String(t);process.stderr.write(`Error running server: ${e}
38
29
  `),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one-search-mcp",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "description": "One Search MCP Server, Web Search & Crawl & Scraper & Extract, support agent-browser, SearXNG, Tavily, DuckDuckGo, Bing, etc.",
5
5
  "mcpName": "io.github.yokingma/one-search-mcp",
6
6
  "private": false,