UNPKG

mcp-search-tools

Version:

MCP server and client for web search and page viewing tools - DuckDuckGo search and web scraping

267 lines (264 loc) 10.2 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import { z } from 'zod'; import { webPageTool, webPageSchema } from '../tools/web-page.js'; import { webSearchTool, webSearchSchema } from '../tools/web-search.js'; import { downloadFilesTool, downloadFilesSchema, } from '../tools/download-files.js'; // Default configuration constants const DEFAULT_MAX_RETRIES = 3; const DEFAULT_RETRY_DELAY = 1000; const mcpConfig = { defaults: { maxRetries: DEFAULT_MAX_RETRIES, retryDelay: DEFAULT_RETRY_DELAY, }, }; const server = new Server({ name: 'mcp-web-tools', version: '1.0.0', description: 'MCP server providing web search and page access tools', categories: ['internet', 'web'], }, { capabilities: { tools: {}, }, }); /** * List available tools with LLM-friendly descriptions */ server.setRequestHandler(ListToolsRequestSchema, () => { return { tools: [ { name: 'web_search', description: ` Search the web using DuckDuckGo for any query. **Best for:** Finding information across the web, researching topics, getting current information. **Not recommended for:** When you already know the exact URL you need (use web_page instead). **Common mistakes:** Using web_search when you have a specific URL to scrape. **Prompt Example:** "Search for latest TypeScript features released in 2024" **Usage Example:** \`\`\`json { "name": "web_search", "arguments": { "query": "latest TypeScript features 2024", "maxResults": 5, "time": "m" } } \`\`\` **Returns:** Search results with titles, URLs, and descriptions. `, inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query to execute' }, maxResults: { type: 'number', description: 'Maximum number of results to return', default: 10, }, region: { type: 'string', description: 'Region for search results', default: 'wt-wt', }, time: { type: 'string', description: 'Time filter for search results (d, w, m, y)', }, maxRetries: { type: 'number', description: 'Maximum retry attempts', default: mcpConfig.defaults.maxRetries, }, retryDelay: { type: 'number', description: 'Base delay in milliseconds between retry attempts', default: mcpConfig.defaults.retryDelay, }, }, required: ['query'], }, }, { name: 'web_page', description: ` Fetch and extract content from a specific web page URL. **Best for:** Getting full content from a known URL, extracting article text, documentation, or specific page content. **Not recommended for:** When you don't know the exact URL (use web_search first). **Common mistakes:** Using web_page for general web searches instead of specific URLs. **Prompt Example:** "Get the content from https://docs.python.org/3/library/asyncio.html" **Usage Example:** \`\`\`json { "name": "web_page", "arguments": { "urls": ["https://docs.python.org/3/library/asyncio.html", "https://example.com"], "maxLength": 3000, "includeLinks": true } } \`\`\` **Returns:** Page content in markdown format with optional links and metadata. `, inputSchema: { type: 'object', properties: { urls: { type: 'array', items: { type: 'string' }, description: 'URLs of the web pages to fetch', }, maxLength: { type: 'number', description: 'Maximum length of content to return', default: 5000, }, includeImages: { type: 'boolean', description: 'Include images in the response', default: false, }, includeLinks: { type: 'boolean', description: 'Include links in the response', default: true, }, maxRetries: { type: 'number', description: 'Maximum retry attempts', default: mcpConfig.defaults.maxRetries, }, retryDelay: { type: 'number', description: 'Base delay in milliseconds between retry attempts', default: mcpConfig.defaults.retryDelay, }, }, required: ['urls'], }, }, { name: 'download_files', description: ` Download one or more files from URLs to a specified directory. **Best for:** Downloading files from URLs to local storage with security and error handling. **Not recommended for:** When you don't have permission to write to the target directory. **Common mistakes:** Not specifying a valid directory path or providing invalid URLs. **Prompt Example:** "Download these files to /tmp/downloads" **Usage Example:** \`\`\`json { "name": "download_files", "arguments": { "urls": ["https://example.com/file1.txt", "https://example.com/file2.pdf"], "directory": "/tmp/downloads", "filenames": ["custom1.txt", "custom2.pdf"], "maxRetries": 3, "retryDelay": 1000, "timeout": 30000 } } \`\`\` **Returns:** Download results with file paths, sizes, and success status. `, inputSchema: { type: 'object', properties: { urls: { type: 'array', items: { type: 'string' }, description: 'Array of URLs to download', }, directory: { type: 'string', description: 'Target directory path for downloads', }, filenames: { type: 'array', items: { type: 'string' }, description: 'Optional array of custom filenames (same length as urls)', }, maxRetries: { type: 'number', description: 'Maximum number of retry attempts for failed downloads', default: mcpConfig.defaults.maxRetries, }, retryDelay: { type: 'number', description: 'Base delay in milliseconds between retry attempts', default: mcpConfig.defaults.retryDelay, }, timeout: { type: 'number', description: 'Request timeout in milliseconds', default: 30000, }, }, required: ['urls', 'directory'], }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { case 'web_search': { const searchInput = webSearchSchema.parse(args); return await webSearchTool(searchInput); } case 'web_page': { const pageInput = webPageSchema.parse(args); return await webPageTool(pageInput); } case 'download_files': { const downloadInput = downloadFilesSchema.parse(args); return await downloadFilesTool(downloadInput); } default: throw new Error(`Unknown tool: ${name}`); } } catch (error) { if (error instanceof z.ZodError) { return { content: [ { type: 'text', text: `Invalid input: ${error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join(', ')}`, }, ], isError: true, }; } return { content: [ { type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error('MCP Web Tools server started'); } // Always run the server when executed directly main().catch((error) => { console.error('Server error:', error); process.exit(1); }); export { server }; //# sourceMappingURL=index.js.map