UNPKG

mcp-deep-research

Version:

[![smithery badge](https://smithery.ai/badge/@baranwang/mcp-deep-research)](https://smithery.ai/server/@baranwang/mcp-deep-research) [![NPM Version](https://img.shields.io/npm/v/mcp-deep-research)](https://www.npmjs.com/package/mcp-deep-research) ![NPM Li

160 lines (149 loc) 6.79 kB
#!/usr/bin/env node "use strict"; var __webpack_exports__ = {}; const mcp_js_namespaceObject = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_namespaceObject = require("@modelcontextprotocol/sdk/server/stdio.js"); const types_js_namespaceObject = require("@modelcontextprotocol/sdk/types.js"); const core_namespaceObject = require("@tavily/core"); const external_zod_namespaceObject = require("zod"); const external_zod_to_json_schema_namespaceObject = require("zod-to-json-schema"); function safeParseInt(value, defaultValue) { const parsed = Number.parseInt(value ?? ''); return Number.isNaN(parsed) || parsed < 1 ? defaultValue : parsed; } const CONFIG = { MAX_SEARCH_KEYWORDS: safeParseInt(process.env.MAX_SEARCH_KEYWORDS, 5), MAX_PLANNING_ROUNDS: safeParseInt(process.env.MAX_PLANNING_ROUNDS, 5), TAVILY_API_URL: 'https://api.tavily.com/search', TAVILY_API_KEY: process.env.TAVILY_API_KEY, PACKAGE_VERSION: "1.1.0" }; async function searchTavily(keyword, options) { const tvly = (0, core_namespaceObject.tavily)({ apiKey: CONFIG.TAVILY_API_KEY }); const response = await tvly.search(keyword, options); let result = `## Search Results for \`${response.query}\`\n`; response.results.forEach((searchResult, index)=>{ result += `### Reference ${index + 1}:\n`; result += `Title: ${searchResult.title}\n`; result += `Content: ${searchResult.content}\n`; }); return result; } function generatePrompt(question, searchResults, rounds) { return `# Role Definition You are a professional information search and analysis expert, specializing in: - Analyzing core information needs from user queries - Designing precise search strategies - Synthesizing and distilling information - Providing accurate and comprehensive answers # User Question ${question} # Current Environment Current Search Round: ${rounds} Maximum Search Rounds: ${CONFIG.MAX_PLANNING_ROUNDS} Current Time: ${new Date().toLocaleString()} # Known Information ${searchResults ?? 'No reference information available'} # Analysis Steps 1. Resource Sufficiency Assessment - Carefully analyze if existing information is sufficient to answer the user's question - Identify information gaps or areas requiring additional verification 2. Search Strategy (if needed) - Identify specific information points that need supplementation - Design precise search keywords (each keyword must meet these requirements): * Include complete subject and predicate * Avoid pronouns and references * Have independent search value * Avoid logical overlap between keywords - Keyword quantity limit: 1~${CONFIG.MAX_SEARCH_KEYWORDS} 3. Continue Search (if needed) - Use current "Known Information" as reference parameter - Set rounds parameter to ${rounds + 1} - Output only search keywords, without any other content 4. Direct Answer (if information is sufficient) - Provide complete answer based on available information - Ensure the answer is accurate, comprehensive, and logically clear # Output Requirements ## If Further Search is Needed - Output only the list of search keywords - One keyword per line - Do not include any other explanations or notes ## If Direct Answer is Possible - Provide a clear, structured answer - Reference relevant sources when necessary - Clearly indicate any uncertain information # Constraints - Current round has reached ${rounds}/${CONFIG.MAX_PLANNING_ROUNDS}, must provide direct answer if maximum rounds exceeded - Search keywords must be independent and precise, no ambiguity allowed - Prohibited to output content unrelated to search strategy`; } const DeepResearchToolSchema = external_zod_namespaceObject.z.object({ question: external_zod_namespaceObject.z.string().describe('User question'), reference: external_zod_namespaceObject.z.string().optional().describe('Reference materials'), keywords: external_zod_namespaceObject.z.array(external_zod_namespaceObject.z.string()).min(0).max(CONFIG.MAX_SEARCH_KEYWORDS).optional().describe(`Search keywords, please provide 1~${CONFIG.MAX_SEARCH_KEYWORDS} keywords. Each keyword must: include complete subject and predicate, avoid pronouns and references, have independent search value, avoid logical overlap between keywords, and be directly relevant to the question`), topic: external_zod_namespaceObject.z["enum"]([ 'general', 'news', 'finance' ]).default('general').describe('Search topic'), rounds: external_zod_namespaceObject.z.number().default(1).describe('Current search round, defaults to 1') }); async function deepResearch(args) { if (!CONFIG.TAVILY_API_KEY) return { isError: true, content: [ { type: 'text', text: 'Please configure the `TAVILY_API_KEY` environment variable, get it from https://tavily.com/' } ] }; const { question, reference = '', keywords = [], topic, rounds } = DeepResearchToolSchema.parse(args); let searchResults = reference; if (keywords.length) { const results = await Promise.allSettled(keywords.map((keyword)=>searchTavily(keyword, { topic }))); searchResults += results.filter((result)=>'fulfilled' === result.status).map((result)=>result.value).join('\n'); } return { content: [ { type: 'text', text: generatePrompt(question, searchResults, rounds) } ] }; } async function main() { const mcpServer = new mcp_js_namespaceObject.McpServer({ name: 'DeepResearch', version: CONFIG.PACKAGE_VERSION }, { capabilities: { tools: {} } }); mcpServer.server.setRequestHandler(types_js_namespaceObject.ListToolsRequestSchema, ()=>({ tools: [ { name: 'deep-research', description: 'Deep web information search tool that can conduct multi-round in-depth research based on keywords and topics', inputSchema: (0, external_zod_to_json_schema_namespaceObject.zodToJsonSchema)(DeepResearchToolSchema) } ] })); mcpServer.server.setRequestHandler(types_js_namespaceObject.CallToolRequestSchema, (request)=>deepResearch(request.params.arguments)); const transport = new stdio_js_namespaceObject.StdioServerTransport(); await mcpServer.connect(transport); } main().catch((error)=>{ console.error(error); process.exit(1); }); for(var __webpack_i__ in __webpack_exports__)exports[__webpack_i__] = __webpack_exports__[__webpack_i__]; Object.defineProperty(exports, '__esModule', { value: true });