@mseep/mcp-omnisearch
Version:
MCP server for integrating Omnisearch with LLMs
131 lines (130 loc) • 6.82 kB
JavaScript
import { ErrorType, ProviderError, } from '../../../common/types.js';
import { handle_rate_limit, retry_with_backoff, sanitize_query, validate_api_key, } from '../../../common/utils.js';
import { config } from '../../../config/env.js';
export class KagiEnrichmentProvider {
constructor() {
this.name = 'kagi_enrichment';
this.description = 'Provides supplementary content from specialized indexes (Teclis for web, TinyGem for news). Ideal for discovering non-mainstream results and enriching content with specialized knowledge.';
}
async enhance_content(content) {
const api_key = validate_api_key(config.enhancement.kagi_enrichment.api_key, this.name);
const enrich_request = async () => {
try {
// Try both web and news endpoints
const results = await Promise.all([
fetch(`https://kagi.com/api/v0/enrich/web?${new URLSearchParams({
q: sanitize_query('artificial intelligence software development'),
limit: '5',
})}`, {
method: 'GET',
headers: {
Authorization: `Bot ${api_key}`,
Accept: 'application/json',
},
signal: AbortSignal.timeout(config.enhancement.kagi_enrichment.timeout),
}),
fetch(`https://kagi.com/api/v0/enrich/news?${new URLSearchParams({
q: sanitize_query('artificial intelligence code generation testing'),
limit: '5',
})}`, {
method: 'GET',
headers: {
Authorization: `Bot ${api_key}`,
Accept: 'application/json',
},
signal: AbortSignal.timeout(config.enhancement.kagi_enrichment.timeout),
}),
]);
const [webResponse, newsResponse] = results;
// Parse and validate responses
let webData;
let newsData;
try {
const webText = await webResponse.text();
webData = JSON.parse(webText);
}
catch (error) {
throw new ProviderError(ErrorType.API_ERROR, `Invalid JSON response from web endpoint: ${error instanceof Error ? error.message : 'Unknown error'}`, this.name);
}
try {
const newsText = await newsResponse.text();
newsData = JSON.parse(newsText);
}
catch (error) {
throw new ProviderError(ErrorType.API_ERROR, `Invalid JSON response from news endpoint: ${error instanceof Error ? error.message : 'Unknown error'}`, this.name);
}
if (!webResponse.ok || !webData.data) {
const error_message = webData.message || webResponse.statusText;
switch (webResponse.status) {
case 401:
throw new ProviderError(ErrorType.API_ERROR, 'Invalid API key', this.name);
case 429:
handle_rate_limit(this.name);
case 500:
throw new ProviderError(ErrorType.PROVIDER_ERROR, 'Kagi Enrichment API internal error', this.name);
default:
throw new ProviderError(ErrorType.API_ERROR, `Unexpected error from web endpoint: ${error_message}`, this.name);
}
}
if (!newsResponse.ok || !newsData.data) {
const error_message = newsData.message || newsResponse.statusText;
switch (newsResponse.status) {
case 401:
throw new ProviderError(ErrorType.API_ERROR, 'Invalid API key', this.name);
case 429:
handle_rate_limit(this.name);
case 500:
throw new ProviderError(ErrorType.PROVIDER_ERROR, 'Kagi Enrichment API internal error', this.name);
default:
throw new ProviderError(ErrorType.API_ERROR, `Unexpected error from news endpoint: ${error_message}`, this.name);
}
}
// Combine and filter results
const allData = [...webData.data, ...newsData.data]
.filter(result =>
// Filter for results about software/development/AI
result.snippet?.toLowerCase().includes('software') ||
result.snippet?.toLowerCase().includes('develop') ||
result.snippet?.toLowerCase().includes('programming') ||
result.snippet?.toLowerCase().includes('code') ||
result.snippet?.toLowerCase().includes('artificial intelligence') ||
result.snippet?.toLowerCase().includes('ai'));
// Clean and combine snippets
const enhanced_content = allData
.map((result) => result.snippet)
.filter(Boolean)
.map(snippet =>
// Fix HTML entities
snippet
.replace(/'/g, "'")
.replace(/"/g, '"')
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>'))
.join('\n\n');
return {
original_content: content,
enhanced_content,
enhancements: [
{
type: 'content_enrichment',
description: 'Added supplementary information from Teclis (web) and TinyGem (news) specialized indexes',
},
],
sources: allData.map((result) => ({
title: result.title,
url: result.url,
})),
source_provider: this.name,
};
}
catch (error) {
if (error instanceof ProviderError) {
throw error;
}
throw new ProviderError(ErrorType.API_ERROR, `Failed to fetch: ${error instanceof Error ? error.message : 'Unknown error'}`, this.name);
}
};
return retry_with_backoff(enrich_request);
}
}