GitHub MCP Server
README.md
# GitHub MCP Server
A project that lets an **AI assistant talk to GitHub** using safe, structured tools.
In plain words: instead of the AI guessing how GitHub works, this project gives it a clear menu of actions — like “list my repos”, “show open issues”, or “read a file”. The AI picks the right action, this server talks to GitHub, and the answer comes back in a clean format the AI can understand.
---
## What problem does this solve?
Chatbots are good at language, but they do not automatically have live access to your GitHub account.
This project builds a **bridge**:
1. You ask something in normal English (“Show open issues in microsoft/vscode”).
2. An AI model (via Groq) decides which GitHub tool to use.
3. The **MCP server** runs that tool against the real GitHub API.
4. Results are cleaned up (normalized) and returned to the AI.
5. The AI explains the result to you in simple language.
**MCP** means **Model Context Protocol**. Think of it as a standard plug: any compatible AI client can connect to this server and use its tools.
---
## Big picture (architecture)
```
You
↓
AI Agent (client/agent.py) ← talks to Groq LLM
↓
MCP Server (notebooks/server.py) ← menu of GitHub tools
↓
GitHub Client ← HTTP calls with your token
↓
GitHub REST API
↓
GitHub
```
### Design rule (important)
Tools stay **thin**:
1. Check the input (is the repo name valid?).
2. Call the GitHub client.
3. **Normalize** the response into a stable shape.
4. Return that clean data to the agent.
All messy GitHub details stay inside the client layer — not scattered across tools.
---
## Project folders (what each part is for)
| Path | What it is |
|------|------------|
| `notebooks/server.py` | **Main MCP server** — the production entrypoint the agent starts |
| `notebooks/schemas.py` | Stable data shapes (Pydantic models) for agents |
| `notebooks/normalize.py` | Converts raw GitHub JSON → those stable shapes |
| `notebooks/safety.py` | Confirm / dry-run / allowlist for dangerous tools |
| `notebooks/pagination.py` | Page helpers for list tools (`page`, `has_next`, …) |
| `notebooks/logging_utils.py` | JSON logs to **stderr** (never prints secrets) |
| `notebooks/server_1.py` | Older/experimental copy — prefer `server.py` |
| `notebooks/01_github_mcp_server.ipynb` | Learning notebook (how the server was built step by step) |
| `client/agent.py` | Chat agent that connects to the MCP server over stdio |
| `client/test_tool_picking.py` | Checks whether the AI picks the **right tool** for sample prompts |
| `.env` | Your private keys (never commit this) |
| `.env.example` | Template showing which keys you need |
| `requirements.txt` | Python packages to install |
| `SETUP.md` | **Step-by-step setup** for non-technical users |
---
## What you can do with the tools
The server exposes many GitHub actions. Grouped simply:
### Read (safe to explore)
- List your repositories
- Get repo details
- List / get issues and pull requests
- Get PR diffs
- List branches, commits, labels
- Search code in a repo
- Read file contents
- List GitHub Actions workflow runs
### Write (changes GitHub)
- Create issues, comments, PRs, branches, labels
- Update issues, add/remove labels
- Reopen issues
### Destructive (can hurt things — protected)
These need extra confirmation by default:
- `merge_pull_request`
- `delete_file`
- `create_repository`
- `create_or_update_file`
- `close_issue`
For these, the agent should usually:
1. Call with `dry_run=true` → preview only
2. Call again with `confirm=true` → actually do it
You can tighten or loosen this with environment settings (see below).
---
## Normalized responses (why agents like this)
Raw GitHub responses are huge and change often. This project returns **stable** shapes.
**List tools** always look like:
```json
{
"count": 20,
"items": [ ... ],
"page": 1,
"per_page": 20,
"has_next": true,
"has_prev": false,
"next_page": 2,
"prev_page": null,
"last_page": 5
}
```
To get the next page, call the same tool again with `page=2` (or `page=next_page`).
**Issue example:**
```json
{
"number": 42,
"title": "Bug in login",
"state": "open",
"author": "some-user",
"labels": ["bug"],
"comments": 3,
"html_url": "https://github.com/...",
"is_pull_request": false
}
```
Also: `get_issues` **filters out pull requests** (GitHub’s issues API mixes them in).
---
## Safety features
| Feature | Meaning |
|---------|---------|
| `confirm=true` | Required to run destructive tools (default mode) |
| `dry_run=true` | Shows what would happen; does **not** change GitHub |
| `destructiveHint` | MCP annotation so clients know a tool is risky |
| Allowlist | Optional list of which destructive tools are even allowed |
| Mode | `confirm` (default), `allow` (no confirm), or `deny` (block all) |
Environment variables (optional):
```env
GITHUB_MCP_DESTRUCTIVE_MODE=confirm
GITHUB_MCP_DESTRUCTIVE_ALLOWLIST=merge_pull_request,delete_file
```
---
## Logging (for debugging)
The server writes **JSON logs to stderr only**.
Why stderr? MCP uses **stdout** for the protocol. If we printed logs there, the AI connection would break.
Logs include things like:
- request method and path
- HTTP status
- duration
- rate-limit remaining
They **never** log:
- your GitHub token
- Authorization headers
- secret-looking values (PATs, bearer tokens, etc.)
Example log line:
```json
{"ts":"2026-08-23T12:00:00+00:00","level":"INFO","event":"github_request","method":"GET","path":"/repos/microsoft/vscode/issues","status_code":200,"duration_ms":120.5}
```
---
## The AI agent (`client/agent.py`)
The agent:
1. Starts the MCP server as a subprocess (`notebooks/server.py`).
2. Asks the server for the tool list.
3. Sends your question + tools to Groq.
4. If Groq wants a tool, the agent calls it through MCP.
5. Sends the tool result back to Groq for a final answer.
Useful commands (from the project folder, with the virtual environment active):
```powershell
# See all registered tools
python client/agent.py --list-tools
# Only show which tool the AI would pick (no GitHub write)
python client/agent.py --dry-run "list my github repos"
# One real question, then exit
python client/agent.py --once "show open issues for microsoft/vscode"
# Interactive chat
python client/agent.py
# Check tool-picking quality on many sample prompts
python client/test_tool_picking.py
```
Loop limits (optional):
```powershell
python client/agent.py --max-rounds 5 --once "..."
```
Or in `.env`:
```env
AGENT_MAX_TOOL_ROUNDS=8
AGENT_MAX_TOOL_CALLS=16
AGENT_MAX_CONSECUTIVE_ERRORS=3
```
---
## Environment variables
### Required for the MCP server
| Variable | Purpose |
|----------|---------|
| `GITHUB_TOKEN` | Personal access token so the server can call GitHub |
| `GITHUB_USERNAME` | Your GitHub username (used at startup validation) |
| `GITHUB_REPO` | A default repo name (used at startup validation) |
### Required for the agent (chat / tool picking)
| Variable | Purpose |
|----------|---------|
| `GROQ_API_KEY` | API key for Groq (LLM) |
### Optional
| Variable | Purpose |
|----------|---------|
| `GROQ_MODEL` | Default: `openai/gpt-oss-20b` |
| `GITHUB_MCP_DESTRUCTIVE_MODE` | `confirm` / `allow` / `deny` |
| `GITHUB_MCP_DESTRUCTIVE_ALLOWLIST` | Comma-separated destructive tool names |
| `AGENT_MAX_TOOL_ROUNDS` | Max tool rounds per user message |
| `AGENT_MAX_TOOL_CALLS` | Max tool executions per user message |
| `AGENT_MAX_CONSECUTIVE_ERRORS` | Stop after N tool failures in a row |
Copy `.env.example` → `.env` and fill in real values. See **SETUP.md** for the full walkthrough.
---
## Tech stack (for the curious)
- **Python 3.13+** (project was developed on 3.13)
- **MCP** (`mcp` Python package) — tool server protocol
- **httpx** — HTTP client for GitHub
- **Pydantic** — schemas / validation
- **python-dotenv** — load `.env`
- **OpenAI-compatible client** → Groq for the agent
- **Jupyter** (optional) — the learning notebook
---
## How to set up and run
Follow the friendly guide:
👉 **[SETUP.md](SETUP.md)** — install Python, create keys, configure `.env`, and run your first commands.
Short version (if you already know Python):
```powershell
cd "path\to\Github-MCP-server"
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
copy .env.example .env
# edit .env with your tokens
python client/agent.py --list-tools
python client/agent.py --once "list my github repos"
```
---
## Learning path (recommended)
1. Read this README (you are here).
2. Complete **SETUP.md** until `--list-tools` works.
3. Read **[docs/ARCHITECTURE_HLD_LLD.md](docs/ARCHITECTURE_HLD_LLD.md)** for HLD + LLD flows.
4. Try `--dry-run` and `--once` with simple read-only questions.
5. Run the **50-scenario manual test plan**: [tests/MANUAL_TESTING_50_SCENARIOS.md](tests/MANUAL_TESTING_50_SCENARIOS.md)
- Auto picking: `python client/run_manual_scenarios.py`
6. Open `notebooks/01_github_mcp_server.ipynb` to see how each layer was built.
7. Only then try write/destructive tools with `dry_run` + `confirm`.
---
## Troubleshooting (quick)
| Problem | Likely fix |
|---------|------------|
| `No module named 'mcp'` | Activate `.venv` or use `.\.venv\Scripts\python.exe` |
| Groq model 404 | Set `GROQ_MODEL=openai/gpt-oss-20b` (or another model from your Groq account) |
| Missing env vars | Fill `GITHUB_TOKEN`, `GITHUB_USERNAME`, `GITHUB_REPO` in `.env` |
| Destructive tool blocked | Expected — use `dry_run=true` then `confirm=true`, or set mode in `.env` |
| Agent hangs on exit (Windows) | Known stdio quirk; one-shot commands force-exit after finishing |
---
## Security reminders
- Never commit `.env`.
- Never paste your GitHub or Groq tokens into chat, screenshots, or GitHub issues.
- Prefer a GitHub token with **only the scopes you need**.
- Keep `GITHUB_MCP_DESTRUCTIVE_MODE=confirm` (or `deny`) unless you fully trust the environment.
- Do not share `server_1.py` debug output if it ever printed tokens in older experiments — use `server.py`.
---
## License / ownership
This is a personal / learning Gen-AI project for a GitHub MCP server and agent. Adjust ownership and license as needed before publishing publicly.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues