Skip to main content
Glama

Onyx Documentation MCP Server

search-engine.jsโ€ข9.12 kB
import fs from 'fs/promises'; import path from 'path'; export class SearchEngine { constructor(dataDir) { this.dataDir = dataDir; this.cache = { docs: null, githubFiles: null, githubPatterns: null, examplesByTopic: null }; } async loadData(type) { if (this.cache[type]) { return this.cache[type]; } const filePaths = { docs: 'onyx-docs.json', githubFiles: 'github/onyx-code.json', githubPatterns: 'github/code-patterns.json', examplesByTopic: 'github/examples-by-topic.json' }; try { const filePath = path.join(this.dataDir, filePaths[type]); const data = await fs.readFile(filePath, 'utf8'); this.cache[type] = JSON.parse(data); return this.cache[type]; } catch (error) { console.error(`Failed to load ${type}:`, error.message); return null; } } // Search official Onyx documentation async searchDocs(query, limit = 5) { const docs = await this.loadData('docs'); if (!docs) { return { error: 'Documentation not available. Run crawler first.' }; } const queryLower = query.toLowerCase(); const results = []; for (const doc of docs) { let score = 0; const titleMatch = doc.title.toLowerCase().includes(queryLower); const contentMatch = doc.content.toLowerCase().includes(queryLower); if (titleMatch) score += 10; if (contentMatch) score += 1; // Check headings for (const heading of doc.headings || []) { if (heading.text.toLowerCase().includes(queryLower)) { score += 5; } } if (score > 0) { results.push({ ...doc, score, snippet: this.getSnippet(doc.content, queryLower) }); } } results.sort((a, b) => b.score - a.score); return { query, source: 'documentation', totalFound: results.length, results: results.slice(0, limit).map(r => ({ title: r.title, url: r.url, snippet: r.snippet, headings: (r.headings || []).map(h => h.text).slice(0, 3), score: r.score })) }; } // Search GitHub code examples by topic async searchGitHubExamples(topic, limit = 5) { const examplesByTopic = await this.loadData('examplesByTopic'); if (!examplesByTopic) { return { error: 'GitHub examples not available. Run GitHub crawler first.' }; } const availableTopics = Object.keys(examplesByTopic); // Find matching topics (exact match or contains) const matchingTopics = availableTopics.filter(t => t.toLowerCase().includes(topic.toLowerCase()) || topic.toLowerCase().includes(t.toLowerCase()) ); if (matchingTopics.length === 0) { return { query: topic, availableTopics: availableTopics.sort(), examples: [], message: `No examples found for topic "${topic}". Try one of the available topics.` }; } const examples = []; for (const matchingTopic of matchingTopics) { const topicExamples = examplesByTopic[matchingTopic] || []; examples.push(...topicExamples.slice(0, Math.ceil(limit / matchingTopics.length))); } return { query: topic, matchingTopics, examples: examples.slice(0, limit).map(example => ({ file: example.path, repository: example.repository, url: example.url, code: example.code.length > 1000 ? example.code.substring(0, 1000) + '\n... (truncated)' : example.code, fullCodeLength: example.code.length })), totalAvailable: examples.length, availableTopics: availableTopics.sort() }; } // Get Onyx function examples from GitHub async getOnyxFunctionExamples(functionName = null, limit = 10) { const patterns = await this.loadData('githubPatterns'); if (!patterns) { return { error: 'GitHub patterns not available. Run GitHub crawler first.' }; } let functions = patterns.functions || []; if (functionName) { // Filter by function name (partial match) functions = functions.filter(func => func.definition.toLowerCase().includes(functionName.toLowerCase()) ); } return { query: functionName, totalFound: functions.length, examples: functions.slice(0, limit).map(func => ({ definition: func.definition, file: func.file, repository: func.repository, url: func.url })) }; } // Get Onyx struct examples from GitHub async getOnyxStructExamples(structName = null, limit = 10) { const patterns = await this.loadData('githubPatterns'); if (!patterns) { return { error: 'GitHub patterns not available. Run GitHub crawler first.' }; } let structs = patterns.structs || []; if (structName) { // Filter by struct name (partial match) structs = structs.filter(struct => struct.definition.toLowerCase().includes(structName.toLowerCase()) ); } return { query: structName, totalFound: structs.length, examples: structs.slice(0, limit).map(struct => ({ definition: struct.definition, file: struct.file, repository: struct.repository, url: struct.url })) }; } // Search across all sources async searchAll(query, sources = ['docs', 'github'], limit = 10) { const results = { query, sources: sources, totalResults: 0, resultsBySources: {} }; const perSourceLimit = Math.ceil(limit / sources.length); if (sources.includes('docs')) { results.resultsBySources.docs = await this.searchDocs(query, perSourceLimit); if (!results.resultsBySources.docs.error) { results.totalResults += results.resultsBySources.docs.totalFound || 0; } } if (sources.includes('github')) { // Search GitHub files directly const githubFiles = await this.loadData('githubFiles'); if (githubFiles) { const githubResults = this.searchGitHubFiles(githubFiles, query, perSourceLimit); results.resultsBySources.github = githubResults; results.totalResults += githubResults.totalFound || 0; } } // Combine and rank all results const allResults = []; Object.values(results.resultsBySources).forEach(sourceResults => { if (sourceResults.results && !sourceResults.error) { sourceResults.results.forEach(result => { allResults.push({ ...result, source: sourceResults.source }); }); } }); // Sort by score (higher is better) allResults.sort((a, b) => (b.score || 0) - (a.score || 0)); results.combinedResults = allResults.slice(0, limit); return results; } // Search GitHub files directly searchGitHubFiles(files, query, limit) { const queryLower = query.toLowerCase(); const results = []; for (const file of files) { let score = 0; const pathMatch = file.path.toLowerCase().includes(queryLower); const repoMatch = file.repository.toLowerCase().includes(queryLower); const codeMatch = file.code && file.code.toLowerCase().includes(queryLower); const contentMatch = file.content && file.content.toLowerCase().includes(queryLower); // Higher scores for different types of matches if (pathMatch) score += 5; if (repoMatch) score += 3; if (codeMatch || contentMatch) score += 1; // Boost score for important file types if (file.fileType === 'readme') score += 10; if (file.fileType === 'package-config') score += 8; if (file.fileType === 'project-config') score += 6; if (file.fileType === 'documentation') score += 7; if (file.fileType === 'web-content') score += 5; if (file.fileType === 'web-index') score += 6; if (file.fileType === 'example' && file.path.toLowerCase().endsWith('.html')) score += 4; if (score > 0) { const content = file.content || file.code || ''; results.push({ file: file.path, repository: file.repository, url: file.url, score: score, fileType: file.fileType || 'unknown', codeSnippet: this.getSnippet(content, queryLower, 300) }); } } results.sort((a, b) => b.score - a.score); return { source: 'github', totalFound: results.length, results: results.slice(0, limit) }; } // Get a snippet of text around the query getSnippet(content, query, contextLength = 150) { const index = content.toLowerCase().indexOf(query.toLowerCase()); if (index === -1) return content.substring(0, contextLength) + '...'; const start = Math.max(0, index - contextLength / 2); const end = Math.min(content.length, index + query.length + contextLength / 2); return (start > 0 ? '...' : '') + content.substring(start, end) + (end < content.length ? '...' : ''); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/elias-michaias/onyx_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server