mcp-omnisearch
Version:
MCP server for integrating Omnisearch with LLMs
363 lines (362 loc) • 14.7 kB
JavaScript
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);
};