Skip to main content
Glama
Productdeveloper5192

github-directory-mcp

README.md
# github-directory-mcp

An MCP (Model Context Protocol) server, written in TypeScript, that gives an AI assistant two tools
for querying GitHub: listing a user/org's repositories, and listing the directories inside a repo.

This README covers MCP from first principles through to this project's internals, running it, and
extending it.

---

## Table of contents

1. [What is MCP](#1-what-is-mcp)
2. [How the protocol actually works](#2-how-the-protocol-actually-works)
3. [Transports: stdio vs HTTP](#3-transports-stdio-vs-http)
4. [What this project does](#4-what-this-project-does)
5. [Project layout](#5-project-layout)
6. [Setup from scratch](#6-setup-from-scratch)
7. [Tool reference](#7-tool-reference)
8. [Accessing the server](#8-accessing-the-server)
9. [Troubleshooting](#9-troubleshooting)
10. [Advanced: extending this server](#10-advanced-extending-this-server)
11. [Security notes](#11-security-notes)

---

## Quick command reference

Every command actually needed to build, run, register, and debug this server, in one place. Run
these from a normal terminal (VS Code integrated terminal or system PowerShell) in the project
folder, `C:\Users\banda\OneDrive\Desktop\Mcp` — not a sandboxed/CI shell.

These are wired up as real `npm run` scripts in `package.json` — not just copy-paste text:

```powershell
# One-time setup
npm install
cp .env.example .env          # then paste your GITHUB_TOKEN into .env

# Build (re-run after any src/index.ts edit)
npm run build

# Register with Claude Code (one-time; restart the session afterwards)
npm run register

# Manual visual testing via MCP Inspector
npm run inspector

# Same, but skip the session-token requirement (fixes stale-tab proxy errors)
npm run inspector:noauth

# Raw run (server just listens on stdio; used for scripted JSON-RPC testing)
npm start

# Verify GITHUB_TOKEN is valid
npm run check-token

# Find + stop whatever is holding the Inspector's ports (6274 UI / 6277 proxy)
netstat -ano | findstr "6274 6277"
Stop-Process -Id <PID> -Force
```

| Script | What it runs |
|---|---|
| `npm run build` | `tsc` — compiles `src/index.ts` → `dist/index.js` |
| `npm start` | `node dist/index.js` — raw stdio server |
| `npm run dev` | build then start, in one step |
| `npm run inspector` | Launches MCP Inspector against the built server |
| `npm run inspector:noauth` | Same, with `DANGEROUSLY_OMIT_AUTH=true` (via `cross-env`, so it works on Windows too) |
| `npm run register` | `claude mcp add github-directory -- node dist/index.js` |
| `npm run check-token` | Hits `GET /user` with your `GITHUB_TOKEN` and prints the resolved account or the raw error |

---

## 1. What is MCP

**Model Context Protocol** is an open standard (created by Anthropic, now used across the industry)
that defines a common way for an AI application — a chat client, an IDE assistant, an agent — to
discover and call external capabilities, without every AI vendor and every tool vendor needing a
custom integration for each other.

Before MCP: if you wanted Claude to talk to GitHub, Slack, a database, and a filesystem, someone had
to write four bespoke integrations *inside* the AI application, each with its own auth handling,
its own API shape, its own way of describing what it can do.

With MCP: each of those four things (GitHub, Slack, DB, filesystem) is wrapped in a small standalone
program called an **MCP server**. Any MCP-compatible client (Claude Code, Claude Desktop, other
agent frameworks) can talk to any MCP server using the same protocol. The server describes its own
capabilities at connection time — the client doesn't need prior knowledge of what a "GitHub server"
looks like.

The analogy people reach for is **USB-C for AI**: one physical/logical connector, many devices
behind it, no per-device custom cable.

### The three primitives MCP servers expose

| Primitive | What it is | Example in this project |
|---|---|---|
| **Tools** | Functions the model can call, with typed inputs/outputs. The model decides when to call them. | `list_github_repositories`, `list_github_directories` |
| **Resources** | Read-only data the client can attach to context (files, query results). Not used in this project. | — |
| **Prompts** | Reusable prompt templates the server offers. Not used in this project. | — |

This project only implements **tools**, which is the most common case — "let the model call a
function and get a result back."

---

## 2. How the protocol actually works

Under the hood, MCP is **JSON-RPC 2.0** — a simple, transport-agnostic RPC format where every
message is a JSON object with a `method`, optional `params`, and an `id` to match requests to
responses.

A session looks like this:

```
Client                                   Server (this project)
  |--- initialize ------------------------->|   client says "here's my protocol version/capabilities"
  |<-- initialize result -------------------|   server replies with its name/version/capabilities
  |--- notifications/initialized ---------->|   client confirms handshake is done
  |
  |--- tools/list -------------------------->|   client asks "what tools do you have?"
  |<-- tools/list result -------------------|   server returns tool names + JSON Schema for each input
  |
  |--- tools/call (list_github_repositories)->|  client invokes a tool with arguments
  |<-- tools/call result --------------------|   server runs it, returns text/structured content
```

Every tool advertises an **input schema** (we use [Zod](https://zod.dev) to define it, which the SDK
converts to JSON Schema automatically). This is what lets the calling model know it must supply
`owner` and `repo` as strings for `list_github_directories`, for instance — the schema is enforced
before your tool code even runs (see `src/index.ts`'s `.min(1)` validators, which reject empty
strings with a clear error instead of letting a malformed request reach the GitHub API).

---

## 3. Transports: stdio vs HTTP

MCP defines the *message format* (JSON-RPC) separately from the *transport* (how bytes physically
move between client and server). Two transports matter in practice:

- **stdio** (what this project uses): the client spawns your server as a child process and talks to
  it over its stdin/stdout. Zero networking, zero ports, zero auth needed — the client owns the
  process. This is the right choice for a local tool a single user runs on their own machine, which
  is what `claude mcp add ... -- node dist/index.js` does.
- **Streamable HTTP**: the server runs independently (its own process, possibly remote), and clients
  connect to it over HTTP. Needed when the server is shared, long-running, or not co-located with
  the client. Requires you to think about auth, since now it's a network-reachable service.

This project only implements stdio (see `StdioServerTransport` in `src/index.ts`). If you ever need
to expose it to multiple remote clients, that's the piece you'd swap out — the tool logic itself
wouldn't change.

---

## 4. What this project does

Two tools, both backed directly by the [GitHub REST API](https://docs.github.com/en/rest) (no
GitHub SDK dependency — just `fetch`):

- **`list_github_repositories`** — calls `GET /users/{owner}/repos` (falling back to
  `GET /orgs/{owner}/repos` if that 404s), or `GET /user/repos` when `owner` is omitted and a token
  is set. Returns `full_name`, description, privacy/fork flags, and URL for each repo.
- **`list_github_directories`** — calls `GET /repos/{owner}/{repo}/contents/{path}` for a shallow
  listing, or `GET /repos/{owner}/{repo}/git/trees/{ref}?recursive=1` (filtered to `type: "tree"`)
  for a full recursive directory walk.

Both accept an optional `GITHUB_TOKEN` (read from `.env` via `dotenv`) which is sent as a
`Bearer` token — required for your own private repos / own-account listing, and generally useful to
avoid GitHub's 60 req/hour unauthenticated rate limit (jumps to 5000 req/hour authenticated).

---

## 5. Project layout

```
Mcp/
├── src/index.ts        Server source: tool definitions, GitHub API calls, stdio bootstrap
├── dist/index.js        Compiled output — this is what actually gets executed
├── .env                  Your real GITHUB_TOKEN (git-ignored, never commit)
├── .env.example          Template for .env (safe to commit)
├── .gitignore            Excludes node_modules/, dist/, .env, Repository_token.txt
├── repositories.txt      A saved snapshot of a list_github_repositories run
├── package.json          Scripts: build (tsc), start, dev
└── tsconfig.json         TypeScript compiler config (NodeNext modules, ES2022 target)
```

---

## 6. Setup from scratch

Requirements: Node.js 18+ (this was built and tested on Node 25).

```powershell
cd "C:\Users\banda\OneDrive\Desktop\Mcp"
npm install
cp .env.example .env
```

Edit `.env` and paste in a GitHub token:

```
GITHUB_TOKEN=github_pat_xxxxxxxxxxxxxxxxxxxx
```

(Generate one at github.com → Settings → Developer settings → Personal access tokens. Fine-grained,
read-only "Contents" + "Metadata" repo permissions are enough for these two tools.)

Build:

```powershell
npm run build
```

This compiles `src/index.ts` → `dist/index.js` via `tsc`. Re-run this any time you edit `src/index.ts`.

---

## 7. Tool reference

### `list_github_repositories`

| Param | Type | Required | Notes |
|---|---|---|---|
| `owner` | string | no | Username or org. Omit to list the authenticated user's own repos (needs `GITHUB_TOKEN`). |
| `perPage` | number 1-100 | no | Default 30. |
| `page` | number | no | Default 1. |

### `list_github_directories`

| Param | Type | Required | Notes |
|---|---|---|---|
| `owner` | string | **yes** | e.g. `"anthropics"`. Must be non-empty (validated). |
| `repo` | string | **yes** | e.g. `"claude-code"`. Must be non-empty (validated). |
| `path` | string | no | Default: repo root. |
| `ref` | string | no | Branch/tag/SHA. Default: repo's default branch. |
| `recursive` | boolean | no | Default false. `true` walks the entire tree via the Git Trees API. |

**Note:** both `owner` and `repo` must actually be provided — if you ask something vague like "list
my repo's directories" without naming a repo, the calling model may send empty strings, which now
fails fast with a validation error instead of silently hitting a malformed GitHub URL.

---

## 8. Accessing the server

There are two ways to actually *use* this server, depending on your goal.

### A. Through Claude Code (the real use case)

Register it once, from a normal terminal (not a sandboxed/CI one):

```powershell
claude mcp add github-directory -- node "C:\Users\banda\OneDrive\Desktop\Mcp\dist\index.js"
```

Restart your Claude Code session (MCP servers load at session start), then just ask, naming the repo
explicitly:

> "List directories in Productdeveloper5192/MCP"
> "List my github repositories"

Claude reads the tool schemas via `tools/list`, decides when a request warrants calling one, and
calls it via `tools/call` — all of that JSON-RPC exchange happens invisibly.

### B. MCP Inspector (manual/visual testing, no client needed)

```powershell
npx @modelcontextprotocol/inspector node dist/index.js
```

This starts two things: a **proxy** on port 6277 (bridges the browser to your stdio process) and a
**UI** on port 6274. It prints a URL with a session token baked in — open that exact URL.

1. Click **Connect**.
2. **Tools** tab → **List Tools**.
3. Pick a tool, fill the form, **Run Tool**.

If you keep hitting "Error Connecting to MCP Inspector Proxy" from stale browser tabs/tokens, skip
the token entirely for local testing:

```powershell
$env:DANGEROUSLY_OMIT_AUTH="true"
npx @modelcontextprotocol/inspector node dist/index.js
```

### C. Raw JSON-RPC over stdio (for scripting/debugging)

```powershell
node dist/index.js
```

Feed it JSON-RPC lines on stdin, one per line — this is what's used internally to smoke-test the
server. Example sequence: `initialize` → `notifications/initialized` → `tools/call`.

---

## 9. Troubleshooting

**`GitHub API 404 for /repos//`**
`owner`/`repo` were empty when the tool was called — usually because the prompt to Claude didn't
name a specific repo. Now blocked at the schema level with a clear validation error instead (see
`.min(1)` in `src/index.ts`). Name the repo explicitly in your prompt.

**"Error Connecting to MCP Inspector Proxy"**
The Inspector mints a new session token every time it starts. This almost always means an old
browser tab is using a stale token from a previous run. Close all old Inspector tabs and open the
exact URL (with token) printed by the *current* run.

**`MCP Inspector PORT IS IN USE at http://localhost:6274`**
A leftover `node` process from a prior Inspector run is still holding the port. Find and stop it:
```powershell
netstat -ano | findstr 6274
Stop-Process -Id <PID> -Force
```

**`claude: command not found`**
You're not in a terminal where Claude Code's CLI is on `PATH` — use VS Code's integrated terminal or
your normal system terminal, not a restricted/sandboxed shell.

**Rate limited / can't see private repos / `owner` omitted fails**
Check `GITHUB_TOKEN` is actually set in `.env` and non-empty. Verify it directly:
```powershell
curl -H "Authorization: Bearer $env:GITHUB_TOKEN" https://api.github.com/user
```
A `200` with your account info confirms the token is valid.

---

## 10. Advanced: extending this server

To add a new tool, follow the pattern already in `src/index.ts`:

```ts
server.registerTool(
  "your_tool_name",
  {
    title: "Human-readable title",
    description: "What it does — this is what the model reads to decide when to call it.",
    inputSchema: {
      someParam: z.string().min(1).describe("Shown to the model as field docs"),
    },
  },
  async ({ someParam }) => {
    // ... do work, call APIs, etc.
    return { content: [{ type: "text", text: "result" }] };
  }
);
```

Ideas in scope for this GitHub-focused server: list branches, list commits on a path, search code,
read a single file's content, list pull requests. Each is one more `githubFetch` call plus one more
`registerTool` block — no architectural changes needed.

If you ever need this reachable by more than one local client (e.g. a shared team server), swap
`StdioServerTransport` for the SDK's Streamable HTTP transport — at that point also add real auth in
front of it, since it stops being "only reachable by whoever can spawn the process" and becomes a
network service.

---

## 11. Security notes

- `.env` holds a live GitHub token — it is git-ignored. Never commit it, never paste its contents
  into chat, issues, or logs.
- `Repository_token.txt` is also git-ignored as a safety net, but the token has already been moved
  into `.env` — that plaintext file is redundant and can be deleted.
- Fine-grained PATs scoped to only the repos/permissions you need are safer than classic PATs with
  broad scopes — regenerate and re-scope if the current token was created broadly.
- This server only ever reads from GitHub (no write/delete endpoints are implemented), so the blast
  radius of a leaked token is limited to whatever *that token itself* can do on your account — treat
  it with the same care as a password.

TDQS

A3.9/5.0

Scored across 2 tools

Disambiguation5/5

Each tool targets a distinct resource: one lists directories within a repository, the other lists repositories for a user/org. No overlap in purpose.

Naming Consistency5/5

Both tools follow a consistent 'list_github_<plural>' pattern, using snake_case and similar structure.

Tool Count3/5

With only 2 tools, the server feels thin for a GitHub utility. It may be intentionally scoped, but the count is borderline low.

Completeness2/5

The server only covers listing of directories and repositories, missing essential operations like file content access, creation, update, or deletion for either resource.

Maintenance

ActivityStale
ResponsivenessNo issues