githubSearchCode
Locate code patterns and file names across GitHub repositories using keyword, path, extension, and filename filters.
Instructions
Search GitHub code [EXTERNAL: GitHub API]
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 | |
| responseCharOffset | No | Character offset for top-level bulk response pagination across results[]. Use when a multi-query response was auto-paginated. | |
| responseCharLength | No | Character budget for top-level bulk response pagination across results[]. Overrides the shared default for this call. |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| results | Yes | Array of results, one per input query, discriminated by status | |
| responsePagination | No | Pagination metadata for top-level bulk response pagination across results[] |
Implementation Reference
- Main execution function for githubSearchCode tool. Accepts ToolExecutionArgs<GitHubCodeSearchQuery>, performs bulk code search via GitHub API, maps results using mapCodeSearchProviderResult, and returns structured results with hints and pagination.
export async function searchMultipleGitHubCode( args: ToolExecutionArgs<GitHubCodeSearchQuery> ): Promise<CallToolResult> { const { queries, authInfo, responseCharOffset, responseCharLength } = args; const getProviderContext = createLazyProviderContext(authInfo); return executeBulkOperation( queries, async (query: GitHubCodeSearchQuery, _index: number) => { try { const currentProviderContext = getProviderContext(); const providerResult = await executeProviderOperation(query, () => currentProviderContext.provider.searchCode( mapCodeSearchToolQuery(query) ) ); if (providerResult.ok === false) { return providerResult.result; } const result: GitHubSearchCodeData = mapCodeSearchProviderResult( providerResult.response.data, query ); const hasContent = (result.files?.length || 0) > 0; const hasOwnerRepo = !!(query.owner && query.repo); const paginationHints = result.pagination ? buildPaginationHints(result.pagination, 'matches') : []; 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'], responseCharOffset, responseCharLength, } ); } - Maps GitHubCodeSearchQuery to provider query format (mapCodeSearchToolQuery) and maps raw search results to GitHubSearchCodeData (mapCodeSearchProviderResult), including files, text_matches, repositoryContext, and pagination.
export function mapCodeSearchToolQuery(query: GitHubCodeSearchQuery) { return { keywords: query.keywordsToSearch, projectId: toProviderProjectId(query.owner, query.repo), owner: query.owner, path: query.path, filename: query.filename, extension: query.extension, match: query.match, limit: query.limit, page: query.page, mainResearchGoal: query.mainResearchGoal, researchGoal: query.researchGoal, reasoning: query.reasoning, }; } export function mapCodeSearchProviderResult( data: CodeSearchResult, query: GitHubCodeSearchQuery ): GitHubSearchCodeData { const splitRepositoryPath = (repositoryPath: string) => { const slashIdx = repositoryPath.lastIndexOf('/'); if (slashIdx <= 0) { return { owner: '', repo: repositoryPath, }; } return { owner: repositoryPath.substring(0, slashIdx), repo: repositoryPath.substring(slashIdx + 1), }; }; const files = data.items.map(item => { const repoFullName = item.repository.name || ''; const { owner, repo: repoName } = splitRepositoryPath(repoFullName); const baseFile = { path: item.path, owner, repo: repoName, ...(item.lastModifiedAt && { lastModifiedAt: item.lastModifiedAt }), }; if (query.match === 'path') { return baseFile; } return { ...baseFile, text_matches: item.matches.map(match => ({ value: match.context, ...(match.positions?.length && { matchIndices: match.positions.map(([start, end]) => ({ start, end, })), }), })), }; }); const result: GitHubSearchCodeData = { files }; if (data.repositoryContext?.branch) { result.repositoryContext = { branch: data.repositoryContext.branch, }; } if (data.pagination) { result.pagination = { currentPage: data.pagination.currentPage, totalPages: data.pagination.totalPages, perPage: data.pagination.entriesPerPage || 10, totalMatches: data.pagination.totalMatches || 0, hasMore: data.pagination.hasMore, }; } return result; } - Registration configuration for githubSearchCode tool using createRemoteToolRegistration, binding the name (TOOL_NAMES.GITHUB_SEARCH_CODE), input/output schemas, and the searchMultipleGitHubCode execution function.
export const registerGitHubSearchCodeTool = createRemoteToolRegistration<GitHubCodeSearchQuery>({ name: TOOL_NAMES.GITHUB_SEARCH_CODE, title: 'GitHub Code Search', inputSchema: GitHubCodeSearchBulkQuerySchema, outputSchema: GitHubSearchCodeOutputSchema, executionFn: searchMultipleGitHubCode, }); - packages/octocode-mcp/src/tools/registerRemoteTool.ts:39-89 (registration)Generic remote tool registration factory that wraps tool registration with MCP server, security validation, and callback invocation, delegating to the execution function.
export function createRemoteToolRegistration<TQuery>( config: RemoteToolConfig<TQuery> ): ( server: McpServer, callback?: ToolInvocationCallback ) => ReturnType<McpServer['registerTool']> { const { name, title, inputSchema, outputSchema, executionFn, annotations } = config; return (server: McpServer, callback?: ToolInvocationCallback) => { return server.registerTool( name, { description: DESCRIPTIONS[name], inputSchema: toMCPSchema(inputSchema), outputSchema: toMCPSchema(outputSchema), annotations: { title, readOnlyHint: annotations?.readOnlyHint ?? true, destructiveHint: annotations?.destructiveHint ?? false, idempotentHint: annotations?.idempotentHint ?? true, openWorldHint: annotations?.openWorldHint ?? true, }, }, withSecurityValidation( name, async ( args: { queries: TQuery[]; responseCharOffset?: number; responseCharLength?: number; }, authInfo, sessionId ): Promise<CallToolResult> => { const queries = args.queries || []; await invokeCallbackSafely(callback, name, queries); return executionFn({ queries, responseCharOffset: args.responseCharOffset, responseCharLength: args.responseCharLength, authInfo, sessionId, }); } ) ); }; } - Low-level GitHub code search API function (searchGitHubCodeAPI) that builds search queries, executes GitHub API search, sanitizes content, minifies results, and returns OptimizedCodeSearchResult with pagination.
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 clampedPage = Math.min(currentPage, Math.max(1, totalPages)); const hasMore = clampedPage < 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: clampedPage, totalPages, perPage, totalMatches, hasMore, }, }, status: 200, headers: result.headers, }; } catch (error: unknown) { const apiError = handleGitHubAPIError(error); return apiError; } }