githubSearchCode
Search GitHub repository code to find files by path or content patterns, enabling code discovery and pattern identification.
Instructions
Search file content or files by path
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| queries | Yes | Research queries for githubSearchCode (1-3 queries per call for optimal resource management). Review schema before use for optimal results |
Implementation Reference
- Core execution function for the 'githubSearchCode' tool. Handles bulk queries, invokes GitHub code search API, processes results including file filtering, pagination, and hint generation.
async function searchMultipleGitHubCode( queries: GitHubCodeSearchQuery[], authInfo?: AuthInfo, sessionId?: string ): Promise<CallToolResult> { return executeBulkOperation( queries, async (query: GitHubCodeSearchQuery, _index: number) => { try { const apiResult = await searchGitHubCodeAPI(query, authInfo, sessionId); const apiError = handleApiError(apiResult, query); if (apiError) return apiError; if (!('data' in apiResult)) { return handleCatchError( new Error('Invalid API response structure'), query ); } // Note: Files are already filtered by shouldIgnoreFile in codeSearch.ts API layer const files = apiResult.data.items.map(item => { const repoName = item.repository?.nameWithOwner; const baseFile = { path: item.path, ...(repoName && { repo: repoName }), ...(item.lastModifiedAt && { lastModifiedAt: item.lastModifiedAt, }), }; if (query.match === 'path') { return baseFile; } return { ...baseFile, text_matches: item.matches.map(match => match.context), }; }); const result: SearchResult = { files }; const repoContext = apiResult.data._researchContext?.repositoryContext; if (repoContext) { result.repositoryContext = repoContext; } // Add pagination info if available const pagination = apiResult.data.pagination; if (pagination) { result.pagination = pagination; } const hasContent = files.length > 0; // Build context for dynamic hints const hasOwnerRepo = !!(query.owner && query.repo); // Generate pagination hints const paginationHints: string[] = []; if (pagination) { const { currentPage, totalPages, totalMatches, perPage, hasMore } = pagination; const startItem = (currentPage - 1) * perPage + 1; const endItem = Math.min(currentPage * perPage, totalMatches); paginationHints.push( `Page ${currentPage}/${totalPages} (showing ${startItem}-${endItem} of ${totalMatches} matches)` ); if (hasMore) { paginationHints.push(`Next: page=${currentPage + 1}`); } if (currentPage > 1) { paginationHints.push(`Previous: page=${currentPage - 1}`); } if (!hasMore) { paginationHints.push('Final page'); } if (totalPages > 2) { paginationHints.push( `Jump to: page=1 (first) or page=${totalPages} (last)` ); } } // Use unified pattern: context for dynamic hints, extraHints for pagination return createSuccessResult( query, result as unknown as Record<string, unknown>, hasContent, TOOL_NAMES.GITHUB_SEARCH_CODE, { hintContext: { hasOwnerRepo, match: query.match }, extraHints: paginationHints, } ); } catch (error) { return handleCatchError(error, query); } }, { toolName: TOOL_NAMES.GITHUB_SEARCH_CODE, keysPriority: [ 'files', 'pagination', 'repositoryContext', 'error', ] satisfies Array<keyof SearchResult>, } ); } - Zod schemas defining the input structure and validation for the 'githubSearchCode' tool, including single query and bulk query schemas.
export const GitHubCodeSearchQuerySchema = BaseQuerySchema.extend({ keywordsToSearch: z .array(z.string()) .min(1) .max(5) .describe(GITHUB_SEARCH_CODE.search.keywordsToSearch), owner: z.string().optional().describe(GITHUB_SEARCH_CODE.scope.owner), repo: z.string().optional().describe(GITHUB_SEARCH_CODE.scope.repo), extension: z .string() .optional() .describe(GITHUB_SEARCH_CODE.filters.extension), filename: z.string().optional().describe(GITHUB_SEARCH_CODE.filters.filename), path: z.string().optional().describe(GITHUB_SEARCH_CODE.filters.path), match: z .enum(['file', 'path']) .optional() .describe(GITHUB_SEARCH_CODE.filters.match), limit: z .number() .int() .min(1) .max(100) .default(10) .optional() .describe(GITHUB_SEARCH_CODE.resultLimit.limit), page: z .number() .int() .min(1) .max(10) .default(1) .optional() .describe(GITHUB_SEARCH_CODE.pagination.page), }); export const GitHubCodeSearchBulkQuerySchema = createBulkQuerySchema( TOOL_NAMES.GITHUB_SEARCH_CODE, GitHubCodeSearchQuerySchema ); - packages/octocode-mcp/src/tools/github_search_code.ts:21-59 (registration)Registers the 'githubSearchCode' tool with the MCP server, providing schema, description, annotations, and the handler function.
export function registerGitHubSearchCodeTool( server: McpServer, callback?: ToolInvocationCallback ) { return server.registerTool( TOOL_NAMES.GITHUB_SEARCH_CODE, { description: DESCRIPTIONS[TOOL_NAMES.GITHUB_SEARCH_CODE], inputSchema: GitHubCodeSearchBulkQuerySchema, annotations: { title: 'GitHub Code Search', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true, }, }, withSecurityValidation( TOOL_NAMES.GITHUB_SEARCH_CODE, async ( args: { queries: GitHubCodeSearchQuery[]; }, authInfo, sessionId ): Promise<CallToolResult> => { const queries = args.queries || []; await invokeCallbackSafely( callback, TOOL_NAMES.GITHUB_SEARCH_CODE, queries ); return searchMultipleGitHubCode(queries, authInfo, sessionId); } ) ); } - Key helper function implementing the GitHub Code Search API call, caching, query building, error handling, and result optimization including minification and sanitization.
export async function searchGitHubCodeAPI( params: GitHubCodeSearchQuery, authInfo?: AuthInfo, sessionId?: string ): Promise<GitHubAPIResponse<OptimizedCodeSearchResult>> { // Cache key excludes context fields (mainResearchGoal, researchGoal, reasoning) // as they don't affect the API response const cacheKey = generateCacheKey( 'gh-api-code', { keywordsToSearch: params.keywordsToSearch, owner: params.owner, repo: params.repo, extension: params.extension, filename: params.filename, path: params.path, match: params.match, limit: params.limit, page: params.page, }, sessionId ); const result = await withDataCache< GitHubAPIResponse<OptimizedCodeSearchResult> >( cacheKey, async () => { return await searchGitHubCodeAPIInternal(params, authInfo); }, { shouldCache: (value: GitHubAPIResponse<OptimizedCodeSearchResult>) => 'data' in value && !(value as { error?: unknown }).error, } ); return result; } async function searchGitHubCodeAPIInternal( params: GitHubCodeSearchQuery, authInfo?: AuthInfo ): Promise<GitHubAPIResponse<OptimizedCodeSearchResult>> { try { const octokit = await getOctokit(authInfo); if (params.keywordsToSearch && params.keywordsToSearch.length > 0) { const validTerms = params.keywordsToSearch.filter( term => term && term.trim() ); if (validTerms.length === 0) { await logSessionError( TOOL_NAMES.GITHUB_SEARCH_CODE, SEARCH_ERRORS.QUERY_EMPTY.code ); return { error: SEARCH_ERRORS.QUERY_EMPTY.message, type: 'http', status: 400, }; } } const query = buildCodeSearchQuery(params); if (!query.trim()) { await logSessionError( TOOL_NAMES.GITHUB_SEARCH_CODE, SEARCH_ERRORS.QUERY_EMPTY.code ); return { error: SEARCH_ERRORS.QUERY_EMPTY.message, type: 'http', status: 400, }; } const perPage = Math.min( typeof params.limit === 'number' ? params.limit : 30, 100 ); const currentPage = params.page || 1; const searchParams: SearchCodeParameters = { q: query, per_page: perPage, page: currentPage, headers: { Accept: 'application/vnd.github.v3.text-match+json', }, }; const result = await octokit.rest.search.code(searchParams); const optimizedResult = await convertCodeSearchResult(result); // GitHub caps at 1000 total results const totalMatches = Math.min(optimizedResult.total_count, 1000); const totalPages = Math.min(Math.ceil(totalMatches / perPage), 10); const hasMore = currentPage < totalPages; return { data: { total_count: optimizedResult.total_count, items: optimizedResult.items, repository: optimizedResult.repository, matchLocations: optimizedResult.matchLocations, minified: optimizedResult.minified, minificationFailed: optimizedResult.minificationFailed, minificationTypes: optimizedResult.minificationTypes, _researchContext: optimizedResult._researchContext, pagination: { currentPage, totalPages, perPage, totalMatches, hasMore, }, }, status: 200, headers: result.headers, }; } catch (error: unknown) { const apiError = handleGitHubAPIError(error); return apiError; } } - packages/octocode-mcp/src/tools/toolConfig.ts:36-43 (registration)Tool configuration entry that includes the registration function for 'githubSearchCode', used by the central toolsManager to register all tools.
export const GITHUB_SEARCH_CODE: ToolConfig = { name: TOOL_NAMES.GITHUB_SEARCH_CODE, description: getDescription(TOOL_NAMES.GITHUB_SEARCH_CODE), isDefault: true, isLocal: false, type: 'search', fn: registerGitHubSearchCodeTool, };