Skip to main content
Glama
AhmetSOGUT

GitHub Insights MCP Server

by AhmetSOGUT
README.md
# šŸ” GitHub Insights MCP Server

[![CI](https://github.com/AhmetSOGUT/github-insights-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/AhmetSOGUT/github-insights-mcp/actions/workflows/ci.yml)

An MCP (Model Context Protocol) server that lets Claude understand any public
GitHub repository — its purpose, structure, tech stack, and architecture —
by exposing typed, focused tools instead of requiring manual browsing.

Built to explore the MCP protocol: tool design, structured data contracts,
and the division of labor between a tool-providing server and the LLM
client that consumes it.

## Table of Contents

- [How it Works](#how-it-works)
- [Architecture](#architecture)
- [Tech Stack](#tech-stack)
- [Project Structure](#project-structure)
- [Getting Started](#getting-started)
- [Connecting to Claude Desktop](#connecting-to-claude-desktop)
- [Running Tests](#running-tests)
- [Design Decisions](#design-decisions)
- [Known Limitations](#known-limitations)
- [Roadmap](#roadmap)

## How it Works

Ask Claude Desktop about any public GitHub repo, and it can call one of five tools:

- **`get_repo_overview_tool`** — metadata (stars, language, last update) + README content
- **`get_repo_structure_tool`** — depth-limited file/folder tree
- **`get_tech_stack_tool`** — detected languages and dependency manifest files
- **`generate_architecture_diagram_tool`** — a Mermaid diagram of the top-level structure
- **`get_file_content_tool`** — full content of a specific file, for deeper analysis

Claude decides which tools to call and in what order based on the question —
e.g. reading `get_repo_structure_tool`'s output to identify an entry point
file, then calling `get_file_content_tool` to actually read it.

## Architecture

```
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”     stdio (MCP/JSON-RPC)    ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  Claude Desktop   │ ──────────────────────────▶ │  GitHub Insights MCP  │
│  (MCP client)      │ ◀────────────────────────── │       Server           │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜                              ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
                                                               │ HTTPS (REST)
                                                               ā–¼
                                                    ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
                                                    │   GitHub API       │
                                                    ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
```

The server runs locally as a subprocess launched by Claude Desktop, communicating
over stdio using the MCP protocol. It never talks to an LLM itself — it fetches
and structures data from the GitHub REST API, and lets the calling LLM (Claude)
handle summarization and reasoning. See [Design Decisions](#design-decisions)
for why this split matters.

## Tech Stack

| Layer | Choice | Why |
|---|---|---|
| Protocol | Official `mcp` Python SDK (v2, `MCPServer`) | Standard, actively maintained |
| HTTP Client | `httpx` (async) | Async-native, pairs cleanly with MCP's async tool handlers |
| Data Validation | `pydantic` | Typed tool schemas, response validation |
| Configuration | `pydantic-settings` | Validated config, resolved to an absolute `.env` path (see below) |
| Resilience | `tenacity` | Retries on transient network errors, not on definitive API errors |
| Testing | `pytest`, `pytest-asyncio`, `respx` | Fully mocked HTTP — no real GitHub calls during test runs |

## Project Structure

```
github-insights-mcp/
ā”œā”€ā”€ server.py                  # MCP entrypoint - registers tools, starts stdio transport
ā”œā”€ā”€ github_client.py            # Sole GitHub REST API access layer (auth, retries, error mapping)
ā”œā”€ā”€ analyzers/
│   ā”œā”€ā”€ overview.py               # Repo metadata + README content
│   ā”œā”€ā”€ structure.py               # Depth-limited file/folder tree
│   ā”œā”€ā”€ tech_stack.py               # Language + manifest file detection
│   ā”œā”€ā”€ diagram.py                   # Mermaid diagram generation (pure function)
│   └── file_content.py               # Single-file content retrieval
ā”œā”€ā”€ tests/
│   ā”œā”€ā”€ conftest.py                 # Shared fixtures
│   ā”œā”€ā”€ test_github_client.py        # URL parsing, HTTP error mapping (mocked)
│   ā”œā”€ā”€ test_tech_stack.py            # Detection logic (mocked)
│   └── test_diagram.py                # Diagram generation (pure, unmocked)
ā”œā”€ā”€ models.py                    # Pydantic models for every tool's input/output
ā”œā”€ā”€ config.py                     # Validated settings, absolute .env resolution
ā”œā”€ā”€ logging_config.py              # Logging setup (stderr, not stdout — see below)
ā”œā”€ā”€ exceptions.py                   # Domain-specific exception types
ā”œā”€ā”€ docs/
│   ā”œā”€ā”€ PRD.md                        # Product requirements
│   └── ARCHITECTURE.md                # Technical design
ā”œā”€ā”€ requirements.txt
ā”œā”€ā”€ pytest.ini
└── .env.example
```

## Getting Started

### Prerequisites
- Python 3.11+
- A GitHub Personal Access Token with **`public_repo`** scope only
  ([create one here](https://github.com/settings/tokens))
- Claude Desktop (free to install; no paid plan required for local MCP servers)

### Setup

```bash
git clone https://github.com/AhmetSOGUT/github-insights-mcp.git
cd github-insights-mcp

conda create -n github-insights-mcp python=3.11 -y
conda activate github-insights-mcp

pip install -r requirements.txt

cp .env.example .env
# edit .env and set GITHUB_TOKEN=your-token-here
```

## Connecting to Claude Desktop

1. Open Claude Desktop → **Settings → Developer → Edit Config**
2. Add this server under `mcpServers` (create the key if it doesn't exist):

```json
{
  "mcpServers": {
    "github-insights": {
      "command": "/absolute/path/to/your/python",
      "args": ["/absolute/path/to/github-insights-mcp/server.py"]
    }
  }
}
```

Use the **absolute path** to the Python interpreter inside your conda
environment (`where python` on Windows / `which python` on macOS/Linux) —
Claude Desktop does not inherit your shell's activated environment.

3. Restart Claude Desktop completely (quit from the system tray, not just
   close the window).
4. Check **Settings → Connectors** — `github-insights` should show as connected.
5. Ask Claude something like: *"Summarize what this repo does:
   https://github.com/owner/repo"*

## Running Tests

All GitHub API calls are mocked via `respx` — no token or network access
needed to run the suite:

```bash
pytest -v
```

## Design Decisions

- **Why does the server not summarize anything itself?**
  It returns structured, factual data (README text, file contents, metadata)
  and leaves synthesis to the calling LLM. Calling a second LLM from inside
  the server would mean a second API key, doubled latency, and duplicated
  work the client is already positioned to do. This keeps the server a pure
  tool provider, in line with what MCP is designed for.

- **Why log to `stderr` instead of `stdout`?**
  MCP communicates with its client over `stdout`. Anything else written to
  `stdout` — including log output — would corrupt the protocol stream and
  break the connection. All logging in this project is explicitly routed
  to `stderr`.

- **Why resolve `.env` via an absolute path in `config.py`?**
  Claude Desktop launches this server from an unpredictable working
  directory (not necessarily the project root). A relative `.env` path
  works when run manually from the project folder but fails silently
  under Claude Desktop, causing confusing startup failures. Resolving the
  path via `Path(__file__).parent` makes config loading independent of how
  the process was launched.

- **Why a single `GitHubClient` instead of calling `httpx` directly from
  each analyzer?**
  Authentication, retry behavior, and GitHub-specific error translation
  (404 → `RepoNotFoundError`, rate-limited 403 → `RateLimitError`) live in
  exactly one place. Analyzers stay focused on interpreting data, not on
  HTTP or auth concerns.

## Known Limitations

- **README and file content are truncated** (README to ~3,000 characters,
  individual files to 50,000 characters) to keep responses fast and
  token-efficient. For very large files or sparse READMEs, this can
  produce a less complete picture than Claude's own web browsing would,
  which has no such caps — this is a deliberate v1 trade-off, not an
  oversight (see `docs/PRD.md`, Section 4).
- **The architecture diagram only reflects top-level folders**, not actual
  import relationships between files. A more accurate diagram would
  require parsing source files, which is out of scope for v1.
- **Single-user, PAT-based auth only.** Anyone using this server needs
  their own GitHub token; there's no OAuth flow for a shared/hosted deployment.

## Roadmap

- [ ] Deeper architecture diagrams based on actual import/dependency parsing
- [ ] OAuth-based auth for a shared, multi-user deployment
- [ ] Commit/PR/issue activity summaries
- [ ] Smarter file selection (auto-detect entry points to read, rather than
      requiring the LLM to guess a path)
- [ ] A remote-hosted version of the server

## License

MIT