UNPKG

mcp-omnisearch

Version:

MCP server for integrating Omnisearch with LLMs

363 lines (362 loc) 14.7 kB
import * as v from 'valibot'; import { create_error_response } from '../common/utils.js'; // Track available providers by category export const available_providers = { search: new Set(), ai_response: new Set(), processing: new Set(), enhancement: new Set(), }; class ToolRegistry { constructor() { this.processing_providers = new Map(); this.enhancement_providers = new Map(); } register_web_search_provider(provider) { this.web_search_provider = provider; available_providers.search.add(provider.name); } register_github_search_provider(provider) { this.github_search_provider = provider; available_providers.search.add(provider.name); } register_ai_search_provider(provider) { this.ai_search_provider = provider; available_providers.ai_response.add(provider.name); } register_firecrawl_process_provider(provider) { this.firecrawl_process_provider = provider; available_providers.processing.add(provider.name); } register_exa_process_provider(provider) { this.exa_process_provider = provider; available_providers.processing.add(provider.name); } register_processing_provider(provider) { this.processing_providers.set(provider.name, provider); available_providers.processing.add(provider.name); } register_enhancement_provider(provider) { this.enhancement_providers.set(provider.name, provider); available_providers.enhancement.add(provider.name); } setup_tool_handlers(server) { // Register web search tool if (this.web_search_provider) { server.tool({ name: 'web_search', description: this.web_search_provider.description, schema: v.object({ query: v.pipe(v.string(), v.description('Query')), provider: v.pipe(v.union([ v.literal('tavily'), v.literal('brave'), v.literal('kagi'), v.literal('exa'), ]), v.description('Search provider')), limit: v.optional(v.pipe(v.number(), v.description('Result limit'))), include_domains: v.optional(v.pipe(v.array(v.string()), v.description('Domains to include'))), exclude_domains: v.optional(v.pipe(v.array(v.string()), v.description('Domains to exclude'))), }), }, async ({ query, provider, limit, include_domains, exclude_domains, }) => { try { const results = await this.web_search_provider.search({ query, provider, limit, include_domains, exclude_domains, }); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2), }, ], }; } catch (error) { const error_response = create_error_response(error); return { content: [ { type: 'text', text: error_response.error, }, ], isError: true, }; } }); } // Register GitHub search tool if (this.github_search_provider) { server.tool({ name: 'github_search', description: this.github_search_provider.description, schema: v.object({ query: v.pipe(v.string(), v.description('Query')), search_type: v.optional(v.pipe(v.union([ v.literal('code'), v.literal('repositories'), v.literal('users'), ]), v.description('Search type (default: code)'))), limit: v.optional(v.pipe(v.number(), v.description('Result limit'))), sort: v.optional(v.pipe(v.union([ v.literal('stars'), v.literal('forks'), v.literal('updated'), ]), v.description('Sort order (repositories only)'))), }), }, async ({ query, search_type, limit, sort }) => { try { const results = await this.github_search_provider.search({ query, search_type, limit, sort, }); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2), }, ], }; } catch (error) { const error_response = create_error_response(error); return { content: [ { type: 'text', text: error_response.error, }, ], isError: true, }; } }); } // Register AI search tool if (this.ai_search_provider) { server.tool({ name: 'ai_search', description: this.ai_search_provider.description, schema: v.object({ query: v.pipe(v.string(), v.description('Query')), provider: v.pipe(v.union([ v.literal('perplexity'), v.literal('kagi_fastgpt'), v.literal('exa_answer'), ]), v.description('AI provider')), limit: v.optional(v.pipe(v.number(), v.description('Result limit'))), }), }, async ({ query, provider, limit }) => { try { const results = await this.ai_search_provider.search({ query, provider, limit, }); return { content: [ { type: 'text', text: JSON.stringify(results, null, 2), }, ], }; } catch (error) { const error_response = create_error_response(error); return { content: [ { type: 'text', text: error_response.error, }, ], isError: true, }; } }); } // Register Firecrawl process tool if (this.firecrawl_process_provider) { server.tool({ name: 'firecrawl_process', description: this.firecrawl_process_provider.description, schema: v.object({ url: v.pipe(v.union([v.string(), v.array(v.string())]), v.description('URL(s)')), mode: v.pipe(v.union([ v.literal('scrape'), v.literal('crawl'), v.literal('map'), v.literal('extract'), v.literal('actions'), ]), v.description('Processing mode')), extract_depth: v.optional(v.pipe(v.union([v.literal('basic'), v.literal('advanced')]), v.description('Extraction depth'))), }), }, async ({ url, mode, extract_depth }) => { try { const result = await this.firecrawl_process_provider.process_content(url, extract_depth, mode); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const error_response = create_error_response(error); return { content: [ { type: 'text', text: error_response.error, }, ], isError: true, }; } }); } // Register Exa process tool if (this.exa_process_provider) { server.tool({ name: 'exa_process', description: this.exa_process_provider.description, schema: v.object({ url: v.pipe(v.union([v.string(), v.array(v.string())]), v.description('URL(s)')), mode: v.pipe(v.union([v.literal('contents'), v.literal('similar')]), v.description('Processing mode')), extract_depth: v.optional(v.pipe(v.union([v.literal('basic'), v.literal('advanced')]), v.description('Extraction depth'))), }), }, async ({ url, mode, extract_depth }) => { try { const result = await this.exa_process_provider.process_content(url, extract_depth, mode); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const error_response = create_error_response(error); return { content: [ { type: 'text', text: error_response.error, }, ], isError: true, }; } }); } // Register remaining processing providers (kagi_summarizer, tavily_extract) this.processing_providers.forEach((provider) => { server.tool({ name: `${provider.name}_process`, description: provider.description, schema: v.object({ url: v.pipe(v.union([v.string(), v.array(v.string())]), v.description('URL(s)')), extract_depth: v.optional(v.pipe(v.union([v.literal('basic'), v.literal('advanced')]), v.description('Extraction depth'))), }), }, async ({ url, extract_depth }) => { try { const result = await provider.process_content(url, extract_depth); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const error_response = create_error_response(error); return { content: [ { type: 'text', text: error_response.error, }, ], isError: true, }; } }); }); // Register enhancement providers this.enhancement_providers.forEach((provider) => { server.tool({ name: `${provider.name}_enhance`, description: provider.description, schema: v.object({ content: v.pipe(v.string(), v.description('Content')), }), }, async ({ content }) => { try { const result = await provider.enhance_content(content); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { const error_response = create_error_response(error); return { content: [ { type: 'text', text: error_response.error, }, ], isError: true, }; } }); }); } } // Create singleton instance const registry = new ToolRegistry(); export const register_tools = (server) => { registry.setup_tool_handlers(server); }; // Export methods to register providers export const register_web_search_provider = (provider) => { registry.register_web_search_provider(provider); }; export const register_github_search_provider = (provider) => { registry.register_github_search_provider(provider); }; export const register_ai_search_provider = (provider) => { registry.register_ai_search_provider(provider); }; export const register_firecrawl_process_provider = (provider) => { registry.register_firecrawl_process_provider(provider); }; export const register_exa_process_provider = (provider) => { registry.register_exa_process_provider(provider); }; export const register_processing_provider = (provider) => { registry.register_processing_provider(provider); }; export const register_enhancement_provider = (provider) => { registry.register_enhancement_provider(provider); };