Skip to main content
Glama

githubSearchCode

Read-onlyIdempotent

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

TableJSON Schema
NameRequiredDescriptionDefault
queriesYesResearch 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
    );
  • 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;
      }
    }
  • 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,
    };
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds substantial behavioral context beyond annotations, including detailed gotchas about filter combinations ('3+ filters risky'), path matching behavior ('Strict prefix matching'), cost considerations ('Prefer specifying owner & repo for precision and cost'), and search strategy guidance ('Start lean: single filter → verify results').

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (<when>, <fromTool>, <gotchas>, <examples>) that make information easy to find. Every sentence provides actionable guidance with zero waste, and the information is appropriately front-loaded with the core purpose first.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a complex search tool with rich annotations but no output schema, the description provides comprehensive context including usage scenarios, sibling relationships, behavioral gotchas, and practical examples. It addresses the tool's complexity adequately despite the absence of output schema documentation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

While schema description coverage is 100%, the description adds valuable semantic context through examples that show how parameters work together, explanations of match types ('Find File (`match="path"`), Find Pattern (`match="file"`)'), and practical guidance on parameter combinations in the gotchas section.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool searches 'file content or files by path' with specific use cases like 'Find code patterns | Locate files | Discovery'. It distinguishes from siblings by explicitly mentioning when to use alternatives like githubSearchRepositories for finding repos or packageSearch for checking packages.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit guidance in the <when> section about when to use this tool, and in the <fromTool> section it details relationships with sibling tools. It also includes specific exclusions like 'AVOID: Broad terms | Finding package repos' with clear alternatives named.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

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/bgauryy/octocode-mcp'

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