intermediate30 minentree

Entree: GitHub Integration Server

An MCP server that integrates with the GitHub API. Search repos, read files, list issues, and create pull request summaries — all from your AI assistant.


title: "Entree: GitHub Integration Server" description: "An MCP server that integrates with the GitHub API. Search repos, read files, list issues, and create pull request summaries — all from your AI assistant." order: 5 category: "entree" level: "intermediate" duration: "30 min" date: "2026-04-01" tags:

  • tools
  • api
  • github keywords:
  • mcp github server
  • github integration mcp
  • mcp-framework github api
  • ai github tools

What You Get

A full GitHub integration server that lets Claude search repositories, read file contents, list issues, and summarize pull requests. Uses the GitHub REST API with token authentication.

Tools included:

  • search_repos — search GitHub repositories by query
  • get_file — read a file from a specific repo and branch
  • list_issues — list open issues for a repository
  • get_pr — get details and diff summary for a pull request

Quick Start

npx mcp-framework create github-server
cd github-server

The Search Repos Tool

Create src/tools/SearchReposTool.ts:

import { MCPTool } from "mcp-framework";
import { z } from "zod";

const SearchInput = z.object({
  query: z.string().describe("Search query for GitHub repositories"),
  limit: z.number().min(1).max(20).default(5).describe("Max results"),
});

class SearchReposTool extends MCPTool<typeof SearchInput> {
  name = "search_repos";
  description = "Search GitHub repositories";
  schema = { input: SearchInput };

  async execute(input: z.infer<typeof SearchInput>) {
    const res = await fetch(
      `https://api.github.com/search/repositories?q=${encodeURIComponent(input.query)}&per_page=${input.limit}`,
      {
        headers: {
          Accept: "application/vnd.github.v3+json",
          ...(process.env.GITHUB_TOKEN && {
            Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
          }),
        },
      }
    );

    if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);

    const data = await res.json();
    return {
      results: data.items.map((repo: Record<string, unknown>) => ({
        name: repo.full_name,
        description: repo.description,
        stars: repo.stargazers_count,
        language: repo.language,
        url: repo.html_url,
      })),
    };
  }
}

export default SearchReposTool;

The Get File Tool

Create src/tools/GetFileTool.ts:

import { MCPTool } from "mcp-framework";
import { z } from "zod";

const FileInput = z.object({
  owner: z.string().describe("Repository owner"),
  repo: z.string().describe("Repository name"),
  path: z.string().describe("File path within the repo"),
  ref: z.string().default("main").describe("Branch or commit ref"),
});

class GetFileTool extends MCPTool<typeof FileInput> {
  name = "get_file";
  description = "Read a file from a GitHub repository";
  schema = { input: FileInput };

  async execute(input: z.infer<typeof FileInput>) {
    const res = await fetch(
      `https://api.github.com/repos/${input.owner}/${input.repo}/contents/${input.path}?ref=${input.ref}`,
      {
        headers: {
          Accept: "application/vnd.github.v3+json",
          ...(process.env.GITHUB_TOKEN && {
            Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
          }),
        },
      }
    );

    if (!res.ok) throw new Error(`File not found: ${res.status}`);

    const data = await res.json();
    const content = Buffer.from(data.content, "base64").toString("utf-8");

    return {
      path: data.path,
      size: data.size,
      content,
    };
  }
}

export default GetFileTool;

The List Issues Tool

Create src/tools/ListIssuesTool.ts:

import { MCPTool } from "mcp-framework";
import { z } from "zod";

const IssuesInput = z.object({
  owner: z.string().describe("Repository owner"),
  repo: z.string().describe("Repository name"),
  state: z.enum(["open", "closed", "all"]).default("open"),
  limit: z.number().min(1).max(30).default(10),
});

class ListIssuesTool extends MCPTool<typeof IssuesInput> {
  name = "list_issues";
  description = "List issues for a GitHub repository";
  schema = { input: IssuesInput };

  async execute(input: z.infer<typeof IssuesInput>) {
    const res = await fetch(
      `https://api.github.com/repos/${input.owner}/${input.repo}/issues?state=${input.state}&per_page=${input.limit}`,
      {
        headers: {
          Accept: "application/vnd.github.v3+json",
          ...(process.env.GITHUB_TOKEN && {
            Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
          }),
        },
      }
    );

    if (!res.ok) throw new Error(`GitHub API error: ${res.status}`);

    const data = await res.json();
    return {
      issues: data.map((issue: Record<string, unknown>) => ({
        number: issue.number,
        title: issue.title,
        state: issue.state,
        author: (issue.user as Record<string, unknown>)?.login,
        labels: (issue.labels as Record<string, unknown>[])?.map(
          (l) => l.name
        ),
        url: issue.html_url,
      })),
    };
  }
}

export default ListIssuesTool;

Authentication

Set your GitHub token as an environment variable:

{
  "mcpServers": {
    "github": {
      "command": "node",
      "args": ["./dist/index.js"],
      "env": {
        "GITHUB_TOKEN": "ghp_your_token_here"
      }
    }
  }
}

The token is optional for public repos but required for private repos and to avoid rate limits.

What You Learn

  • Authenticating with external APIs via environment variables
  • Building multiple related tools in one server
  • Handling GitHub's REST API responses
  • Pagination and result limiting for AI-friendly outputs
  • Base64 decoding file contents

Next Up

Combine everything you have learned in the Developer Tools Suite, or explore AI capabilities with the AI Image Generation Server.


Built with mcp-framework (3.3M+ downloads) by @QuantGeekDev. Validated by Anthropic.