Skip to main content
Glama
shanksxz

GitHub Repository MCP Server

get-repo-context

Retrieve all files from a GitHub repository to provide context for AI models, with options to filter by file extensions and exclude specific paths.

Instructions

Get all files from a GitHub repository to use as context

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
ownerYesGitHub repository owner/organization name
repoYesGitHub repository name
maxFilesNoMaximum number of files to include (default: 50)
fileExtensionsNoFile extensions to include (e.g., ['js', 'ts', 'md'])
excludePathsNoPaths to exclude (e.g., ['node_modules', 'dist'])

Implementation Reference

  • The main execution handler for the 'get-repo-context' tool. It fetches all files recursively from the GitHub repository, applies filters based on maxFiles, fileExtensions, and excludePaths, retrieves content for selected files, and returns formatted markdown text blocks.
    async ({ owner, repo, maxFiles = 50, fileExtensions = [], excludePaths = ['node_modules', 'dist', 'build'] }) => {
      try {
        console.error(`Fetching files from ${owner}/${repo}...`);
    
        const allFiles = await getAllFiles(owner, repo);
        console.error(`Found ${allFiles.length} total files in the repository`);
    
        let filteredFiles = allFiles.filter(file => {
          if (excludePaths.some(excludePath => file.path.includes(excludePath))) {
            return false;
          }
    
          if (fileExtensions.length > 0) {
            const extension = file.path.split('.').pop() || '';
            return fileExtensions.includes(extension);
          }
    
          return true;
        });
    
        filteredFiles = filteredFiles.slice(0, maxFiles);
        console.error(`Selected ${filteredFiles.length} files after filtering`);
    
        const fileContents: { path: string; content: string | null }[] = [];
        for (const file of filteredFiles) {
          if (file.type === 'file') {
            const content = await getFileContent(owner, repo, file.path);
            fileContents.push({
              path: file.path,
              content
            });
          }
        }
    
        const formattedContent = fileContents
          .filter(file => file.content !== null)
          .map(file => `File: ${file.path}\n\n\`\`\`\n${file.content}\n\`\`\`\n\n`)
          .join('---\n\n');
    
        return {
          content: [
            {
              type: "text",
              text: `Repository Context for ${owner}/${repo}:\n\n${formattedContent}`,
            },
          ],
        };
      } catch (error) {
        console.error("Error fetching repository context:", error);
        return {
          content: [
            {
              type: "text",
              text: `Error fetching repository context: ${error instanceof Error ? error.message : String(error)}`,
            },
          ],
        };
      }
    },
  • Input schema defined using Zod for the tool parameters: owner (string), repo (string), maxFiles (number, optional), fileExtensions (array of strings, optional), excludePaths (array of strings, optional).
    {
      owner: z.string().describe("GitHub repository owner/organization name"),
      repo: z.string().describe("GitHub repository name"),
      maxFiles: z.number().optional().describe("Maximum number of files to include (default: 50)"),
      fileExtensions: z.array(z.string()).optional().describe("File extensions to include (e.g., ['js', 'ts', 'md'])"),
      excludePaths: z.array(z.string()).optional().describe("Paths to exclude (e.g., ['node_modules', 'dist'])"),
    },
  • src/index.ts:84-153 (registration)
    Registration of the 'get-repo-context' tool using server.tool(), including name, description, input schema, and handler function.
    server.tool(
      "get-repo-context",
      "Get all files from a GitHub repository to use as context",
      {
        owner: z.string().describe("GitHub repository owner/organization name"),
        repo: z.string().describe("GitHub repository name"),
        maxFiles: z.number().optional().describe("Maximum number of files to include (default: 50)"),
        fileExtensions: z.array(z.string()).optional().describe("File extensions to include (e.g., ['js', 'ts', 'md'])"),
        excludePaths: z.array(z.string()).optional().describe("Paths to exclude (e.g., ['node_modules', 'dist'])"),
      },
      async ({ owner, repo, maxFiles = 50, fileExtensions = [], excludePaths = ['node_modules', 'dist', 'build'] }) => {
        try {
          console.error(`Fetching files from ${owner}/${repo}...`);
    
          const allFiles = await getAllFiles(owner, repo);
          console.error(`Found ${allFiles.length} total files in the repository`);
    
          let filteredFiles = allFiles.filter(file => {
            if (excludePaths.some(excludePath => file.path.includes(excludePath))) {
              return false;
            }
    
            if (fileExtensions.length > 0) {
              const extension = file.path.split('.').pop() || '';
              return fileExtensions.includes(extension);
            }
    
            return true;
          });
    
          filteredFiles = filteredFiles.slice(0, maxFiles);
          console.error(`Selected ${filteredFiles.length} files after filtering`);
    
          const fileContents: { path: string; content: string | null }[] = [];
          for (const file of filteredFiles) {
            if (file.type === 'file') {
              const content = await getFileContent(owner, repo, file.path);
              fileContents.push({
                path: file.path,
                content
              });
            }
          }
    
          const formattedContent = fileContents
            .filter(file => file.content !== null)
            .map(file => `File: ${file.path}\n\n\`\`\`\n${file.content}\n\`\`\`\n\n`)
            .join('---\n\n');
    
          return {
            content: [
              {
                type: "text",
                text: `Repository Context for ${owner}/${repo}:\n\n${formattedContent}`,
              },
            ],
          };
        } catch (error) {
          console.error("Error fetching repository context:", error);
          return {
            content: [
              {
                type: "text",
                text: `Error fetching repository context: ${error instanceof Error ? error.message : String(error)}`,
              },
            ],
          };
        }
      },
    );
  • Helper function getAllFiles() that recursively lists all files in the repository by traversing directories using GitHub API.
    async function getAllFiles(owner: string, repo: string, path: string = ""): Promise<{ path: string, type: string }[]> {
      const contents = await getRepoContents(owner, repo, path);
      let allFiles: { path: string, type: string }[] = [];
    
      for (const item of contents) {
        if (item.type === 'file') {
          allFiles.push({
            path: item.path,
            type: 'file'
          });
        } else if (item.type === 'dir') {
          const subFiles = await getAllFiles(owner, repo, item.path);
          allFiles = [...allFiles, ...subFiles];
        }
      }
    
      return allFiles;
    }
  • Helper function getRepoContents() that fetches contents of a specific path in the repo using Octokit.
    async function getRepoContents(owner: string, repo: string, path: string = ""): Promise<any[]> {
      try {
        const response = await octokit.repos.getContent({
          owner,
          repo,
          path,
        });
    
        if (Array.isArray(response.data)) {
          return response.data;
        } else if (response.data && 'type' in response.data) {
          return [response.data];
        }
    
        return [];
      } catch (error) {
        console.error(`Error getting repo contents for ${owner}/${repo}/${path}:`, error);
        return [];
      }
    }
  • Helper function getFileContent() that retrieves the base64-decoded content of a specific file.
    async function getFileContent(owner: string, repo: string, path: string): Promise<string | null> {
      try {
        const response = await octokit.repos.getContent({
          owner,
          repo,
          path,
        });
    
        if ('content' in response.data && 'encoding' in response.data) {
          if (response.data.encoding === 'base64') {
            return Buffer.from(response.data.content, 'base64').toString('utf-8');
          }
        }
    
        return null;
      } catch (error) {
        console.error(`Error getting file content for ${owner}/${repo}/${path}:`, error);
        return null;
      }
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions that files are retrieved 'to use as context', implying a read-only operation for contextual purposes, but fails to disclose critical behavioral traits such as rate limits, authentication requirements, pagination, error handling, or what the output format looks like (e.g., structured data vs. raw files). For a tool with 5 parameters and no output schema, this is a significant gap in transparency.

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 a single, clear sentence that efficiently conveys the core purpose without unnecessary words. It's front-loaded with the main action and resource, making it easy for an agent to quickly understand the tool's intent. Every part of the sentence earns its place by specifying the scope ('all files') and purpose ('to use as context').

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

Completeness2/5

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

Given the complexity (5 parameters, no annotations, no output schema), the description is incomplete. It lacks details on behavioral aspects (e.g., how files are returned, error conditions), doesn't differentiate from sibling tools, and provides minimal usage guidance. While the input schema is well-documented, the description fails to add sufficient value beyond it, leaving gaps in understanding the tool's full context and operation.

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

Parameters3/5

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

Schema description coverage is 100%, meaning all parameters are well-documented in the input schema itself (e.g., owner, repo, maxFiles with default, fileExtensions, excludePaths). The description adds no additional parameter semantics beyond what the schema provides, such as explaining how fileExtensions or excludePaths interact or typical use cases. Given the high schema coverage, a baseline score of 3 is appropriate, as the description doesn't compensate but also doesn't detract.

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

Purpose4/5

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

The description clearly states the verb ('Get') and resource ('all files from a GitHub repository'), specifying the action and target. However, it doesn't explicitly differentiate from sibling tools like 'get-file-content' (which likely gets specific file contents) or 'get-repo-structure' (which might get directory structure), leaving room for ambiguity about what distinguishes this tool from those alternatives.

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

Usage Guidelines2/5

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

The description provides minimal guidance: it states the tool is for getting files 'to use as context', but offers no explicit advice on when to use this tool versus the sibling tools (get-file-content, get-repo-structure). There's no mention of prerequisites, limitations, or alternative scenarios, leaving the agent to infer usage from the tool name and parameters alone.

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/shanksxz/gh-mcp-server'

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