Skip to main content
Chapter 5 Project — Full-Stack MCP Server for GitHub Automation

Designing the GitHub MCP Server Architecture

18 min read Lesson 17 / 40 Preview

Designing the GitHub MCP Server Architecture

In this chapter, we build a complete GitHub MCP server from scratch. This is the kind of project you would put on your resume or publish as an open-source package. By the end, you will have a server that lets AI manage repositories, issues, pull requests, and code — all through MCP.

What We Are Building

┌─────────────────────────────────────────────────┐
│           GitHub MCP Server                      │
│                                                   │
│  Tools:                                           │
│  ├── list-repos          List user repositories   │
│  ├── search-repos        Search GitHub repos      │
│  ├── create-issue        Create a new issue       │
│  ├── list-issues         List and filter issues   │
│  ├── create-pull-request Create a PR              │
│  ├── list-pull-requests  List and filter PRs      │
│  ├── search-code         Search code in repos     │
│  └── get-file-contents   Read file from repo      │
│                                                   │
│  Resources:                                       │
│  ├── github://user/profile    User profile        │
│  ├── github://repos/{owner}/{repo}/readme         │
│  └── github://repos/{owner}/{repo}/stats          │
│                                                   │
│  Prompts:                                         │
│  ├── repo-analysis       Analyze a repository     │
│  └── issue-triage        Triage open issues       │
└─────────────────────────────────────────────────┘

Project Setup

mkdir github-mcp-server && cd github-mcp-server
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node

GitHub API Client

// src/github.ts
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;

if (!GITHUB_TOKEN) {
  console.error("Error: GITHUB_TOKEN environment variable is required");
  process.exit(1);
}

const BASE_URL = "https://api.github.com";

export async function githubApi(path: string, options: RequestInit = {}) {
  const url = `${BASE_URL}${path}`;
  const response = await fetch(url, {
    ...options,
    headers: {
      Authorization: `Bearer ${GITHUB_TOKEN}`,
      Accept: "application/vnd.github.v3+json",
      "Content-Type": "application/json",
      ...options.headers,
    },
  });

  if (!response.ok) {
    const error = await response.json().catch(() => ({}));
    throw new Error(`GitHub API error ${response.status}: ${error.message || response.statusText}`);
  }

  return response.json();
}

// Rate limit helper
export async function checkRateLimit() {
  const data = await githubApi("/rate_limit");
  return {
    remaining: data.rate.remaining,
    limit: data.rate.limit,
    resetAt: new Date(data.rate.reset * 1000).toISOString(),
  };
}

Server Skeleton

// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";

const server = new McpServer({
  name: "github-mcp-server",
  version: "1.0.0",
});

// We will add tools, resources, and prompts in the next lessons

const transport = new StdioServerTransport();
await server.connect(transport);
console.error("GitHub MCP Server running");

Configuration for Claude Desktop

{
  "mcpServers": {
    "github": {
      "command": "node",
      "args": ["/path/to/github-mcp-server/build/index.js"],
      "env": {
        "GITHUB_TOKEN": "ghp_your_personal_access_token"
      }
    }
  }
}

Architecture Decisions

Why a Personal Access Token? For simplicity. In the enterprise chapter (Chapter 7), we will upgrade to OAuth 2.1 for multi-user scenarios.

Why not use Octokit? We use raw fetch to keep dependencies minimal and teach the underlying API. In production, you could swap in Octokit for convenience.

Error strategy: Every tool validates inputs, checks rate limits, and returns clear error messages. The AI should always know what went wrong and what to do about it.

Key Takeaway

A well-architected MCP server starts with a clear plan: what tools, resources, and prompts will it expose? How will it authenticate? How will it handle errors? This design-first approach ensures your server is coherent, maintainable, and ready for production.