Skip to main content
Glama

mcp-omnisearch

index.ts6.88 kB
import { Octokit } from 'octokit'; import { BaseSearchParams, ErrorType, ProviderError, SearchProvider, SearchResult, } from '../../../common/types.js'; import { retry_with_backoff, validate_api_key, } from '../../../common/utils.js'; import { config } from '../../../config/env.js'; // Interface for individual code search result item from GitHub API interface GitHubCodeSearchResultItem { name: string; path: string; sha: string; url: string; git_url: string; html_url: string; score: number; repository: { full_name: string; html_url: string; }; text_matches?: { object_url: string; object_type: string; property: string; fragment: string; }[]; } // Interface for individual repository search result item from GitHub API interface GitHubRepositorySearchResultItem { full_name: string; html_url: string; description: string | null; stargazers_count: number; forks_count: number; open_issues_count: number; pushed_at: string; language: string | null; score: number; } export class GitHubSearchProvider implements SearchProvider { name = 'github'; description = 'Search for code on GitHub. This is ideal for finding code examples, tracking down function definitions, or locating files with specific names or paths. Supports advanced query syntax with qualifiers like `filename:`, `path:`, `repo:`, `user:`, `language:`, and `in:file`. For example, to find a file named `settings.json` in a `.claude` directory, you could use the query: `filename:settings.json path:.claude`'; // Main search method for code search (default behavior) async search(params: BaseSearchParams): Promise<SearchResult[]> { return this.search_code(params); } // Dedicated code search method with enhanced snippets async search_code( params: BaseSearchParams & { include_snippets?: boolean }, ): Promise<SearchResult[]> { const api_key = validate_api_key( config.search.github.api_key, this.name, ); const octokit = new Octokit({ auth: api_key }); const search_request = async () => { try { // Enable text matches to get better snippets const response = await octokit.rest.search.code({ q: params.query, per_page: params.limit ?? 10, // Request text matches for better snippets headers: { accept: 'application/vnd.github.v3.text-match+json', }, }); return response.data.items.map( (item: GitHubCodeSearchResultItem) => { // Extract better snippet from text matches let snippet = `No snippet available for ${item.path}`; if (item.text_matches && item.text_matches.length > 0) { // Combine multiple fragments for better context const fragments = item.text_matches .map((match) => match.fragment) .filter(Boolean); if (fragments.length > 0) { snippet = fragments.slice(0, 2).join(' ... '); } } return { title: `${item.repository.full_name}/${item.path}`, url: item.html_url, snippet, score: item.score, source_provider: this.name, // Add metadata for better context metadata: { repository: item.repository.full_name, file_path: item.path, file_name: item.name, search_type: 'code', }, }; }, ); } catch (error: any) { return this.handle_search_error(error); } }; return retry_with_backoff(search_request); } // Dedicated repository search method with enhanced metadata async search_repositories( params: BaseSearchParams & { sort?: 'stars' | 'forks' | 'updated'; }, ): Promise<SearchResult[]> { const api_key = validate_api_key( config.search.github.api_key, this.name, ); const octokit = new Octokit({ auth: api_key }); const search_request = async () => { try { const response = await octokit.rest.search.repos({ q: params.query, per_page: params.limit ?? 10, sort: params.sort, }); return response.data.items.map( (item: GitHubRepositorySearchResultItem) => { // Create richer description let snippet = item.description ?? 'No description available.'; if (item.language) { snippet += ` • Language: ${item.language}`; } snippet += ` • ⭐ ${item.stargazers_count} • 🍴 ${item.forks_count}`; return { title: item.full_name, url: item.html_url, snippet, score: item.score, source_provider: this.name, metadata: { repository: item.full_name, language: item.language, stars: item.stargazers_count, forks: item.forks_count, last_push: item.pushed_at, search_type: 'repository', }, }; }, ); } catch (error: any) { return this.handle_search_error(error); } }; return retry_with_backoff(search_request); } // Alias for backward compatibility async repository_search( params: BaseSearchParams & { sort?: 'stars' | 'forks' | 'updated'; }, ): Promise<SearchResult[]> { return this.search_repositories(params); } // User search method async search_users( params: BaseSearchParams, ): Promise<SearchResult[]> { const api_key = validate_api_key( config.search.github.api_key, this.name, ); const octokit = new Octokit({ auth: api_key }); const search_request = async () => { try { const response = await octokit.rest.search.users({ q: params.query, per_page: params.limit ?? 10, }); return response.data.items.map((user: any) => ({ title: user.login, url: user.html_url, snippet: user.bio ?? `GitHub user: ${user.login} • ${user.type}`, score: user.score, source_provider: this.name, metadata: { username: user.login, user_type: user.type, search_type: 'user', }, })); } catch (error: any) { return this.handle_search_error(error); } }; return retry_with_backoff(search_request); } // Centralized error handling private handle_search_error(error: any): never { const status = error.status || 500; const message = error.message || 'An unexpected error occurred.'; switch (status) { case 401: case 403: throw new ProviderError( ErrorType.API_ERROR, `Invalid or unauthorized GitHub API key: ${message}`, this.name, ); case 422: throw new ProviderError( ErrorType.INVALID_INPUT, `Invalid GitHub search query: ${message}`, this.name, ); case 429: throw new ProviderError( ErrorType.RATE_LIMIT, `GitHub API rate limit exceeded: ${message}`, this.name, ); default: throw new ProviderError( ErrorType.PROVIDER_ERROR, `GitHub API error: ${message}`, this.name, { status }, ); } } } // Export the provider instance export const github_search_provider = new GitHubSearchProvider();

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/spences10/mcp-omnisearch'

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