Skip to main content
Glama
PaulliDev

GitHub Issues MCP Server

by PaulliDev
README.md
# GitHub Issues MCP Server

A Model Context Protocol (MCP) server in TypeScript that lets Claude, Cursor, or any MCP client
list, create, search, and comment on GitHub issues.

This is the companion code for the Snippets Ltd tutorial
**[Building a Custom MCP Server in TypeScript](https://snippets.ltd/blog/building-custom-mcp-server-in-typescript)**.
Read the article for the step-by-step explanation.

## What it exposes

| Type | Name | What it does |
|---|---|---|
| Tool | `list_issues` | List open, closed, or all issues (pull requests excluded), most recently updated first |
| Tool | `create_issue` | Create an issue with a title, body, and labels |
| Tool | `add_comment` | Comment on an existing issue |
| Tool | `search_issues` | Search issues with GitHub search syntax |
| Resource | `repo://readme` | The repository README |
| Resource | `repo://issues/recent` | The 10 most recently updated open issues |

## Prerequisites

- Node.js 20.12 or newer
- A GitHub [fine-grained personal access token](https://github.com/settings/personal-access-tokens)
  with **Issues: Read and write** on the repository you want to use (use a test repo while experimenting)

## Setup

```bash
git clone https://github.com/PaulliDev/github-issues-mcp.git
cd github-issues-mcp
npm install
cp .env.example .env   # then fill in GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO
npm run build
```

## Run it

| Command | What it does |
|---|---|
| `npm run inspect` | Opens the MCP Inspector in your browser, connected to the server over stdio |
| `npm start` | Runs the stdio server (what desktop clients spawn) |
| `npm run start:http` | Runs the Streamable HTTP server on `PORT` (default 3001), protected by `MCP_AUTH_TOKEN` |

## Call the HTTP server

Add `MCP_AUTH_TOKEN` to `.env`, run `npm run start:http`, then from another terminal:

```bash
curl -s http://localhost:3001/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Authorization: Bearer <your MCP_AUTH_TOKEN>' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

Every MCP message is a POST to `/mcp` with a JSON-RPC body. Replies arrive as Server-Sent Events
(`event: message` / `data: {...}`). Without the header you get `401`; `GET` returns `405`.
The MCP Inspector can also connect: choose **Streamable HTTP**, URL `http://localhost:3001/mcp`,
and add the `Authorization` header. To publish online, run it on any Node host behind HTTPS.

## Connect to Claude Desktop

Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or
`%APPDATA%\Claude\claude_desktop_config.json` (Windows):

```json
{
  "mcpServers": {
    "github-issues": {
      "command": "node",
      "args": ["/absolute/path/to/github-issues-mcp/dist/index.js"],
      "env": {
        "GITHUB_TOKEN": "github_pat_your_token_here",
        "GITHUB_OWNER": "your-org",
        "GITHUB_REPO": "your-repo"
      }
    }
  }
}
```

Restart Claude Desktop and ask things like "show me open issues labeled bug".

## Project structure

```
src/
├── index.ts                  # stdio entry point
├── http-server.ts            # Streamable HTTP entry point (auth + rate limiting)
├── server.ts                 # createServer(): registers tools and resources
├── configuration.ts          # loads .env and validates environment variables
├── github-client.ts          # GitHub REST API wrapper
├── types.ts                  # shared and GitHub API types
├── tools/                    # Zod input schemas, one file per tool
└── security/
    ├── sanitize-tool-output.ts
    └── create-authentication-middleware.ts
```

## Security notes

- Tool results contain text written by anyone who can open an issue. The server filters common
  prompt-injection phrases, but your client should still treat tool output as untrusted.
- The HTTP server refuses to start without `MCP_AUTH_TOKEN`. Use a long random value.
- Give the GitHub token the fewest permissions possible.

## License

MIT

TDQS

A3.6/5.0

Scored across 4 tools

Disambiguation5/5

Each tool has a clearly distinct purpose: listing, creating, commenting, and searching. There is no meaningful overlap between list_issues (broad browsing) and search_issues (query-based filtering), and the descriptions make this distinction explicit.

Naming Consistency5/5

All four tool names follow a consistent verb_noun snake_case pattern: list_issues, create_issue, add_comment, search_issues. The naming is predictable and immediately communicates the action and target.

Tool Count3/5

With only 4 tools, the server is quite focused but still covers core issue interaction. The count is on the low end but not unreasonably thin for a single-purpose GitHub issues server, so it earns a borderline score.

Completeness3/5

The server covers listing, creating, commenting, and searching issues, which handles the most common workflows. However, it lacks obvious lifecycle operations like updating, closing, or fetching a single issue, leaving notable gaps that agents cannot work around without external tools.

Maintenance

ActivityMaintained
ResponsivenessNo issues