An MCP (Model Context Protocol) server that lets AI assistants manage GitHub Issues across multiple projects — without ever leaving the chat.
You describe a task in plain language and the AI publishes it as a GitHub Issue on your confirmation — no silent API calls, no intermediate files.
AI assistant
│
├─ list_issues ─────────► shows open issues in the repo
│
├─ fetch_issue ─────────► reads full issue context (body + comments)
│
├─ publish_issue ───────► github.com/your-org/your-repo/issues/42 (only after you confirm)
│
├─ add_comment ─────────► posts a comment on an existing issue
│
└─ update_issue ────────► changes state, title, assignee, or labels
- You describe a task to your AI assistant in plain language.
- The AI proposes the issue content (title, context, files, checklist).
- You confirm — the AI calls
publish_issueand the Issue is created via the GitHub API.
- Node.js 18+
- Claude Code CLI —
npm install -g @anthropic-ai/claude-code - A GitHub Personal Access Token with
reposcope
git clone https://github.com/prog-time/mcp-github-issues.git
cd mcp-github-issues
cp .env.example .env
# edit .env — add your GitHub token(s)
cp projects.yaml.example projects.yaml
# edit projects.yaml — add your projects
./mcp.sh setupmcp.sh setup installs dependencies and registers the MCP server with Claude Code in one step.
Create .env in the project root (gitignored):
GITHUB_TOKEN_MYPROJECT=ghp_xxxxxxxxxxxxxxxxxxxxEach project can use a separate token, allowing different permission scopes across personal and organization repositories.
To generate a token: GitHub → Settings → Developer settings → Personal access tokens → Fine-grained tokens (or classic with repo scope).
projects:
myproject:
owner: your-org # GitHub organization or username
repo: your-repo # Repository name
tokenEnv: GITHUB_TOKEN_MYPROJECT # Must match a key in .envMultiple projects:
projects:
api:
owner: acme-corp
repo: backend-api
tokenEnv: GITHUB_TOKEN_API
web:
owner: acme-corp
repo: frontend
tokenEnv: GITHUB_TOKEN_WEB
oss:
owner: your-username
repo: open-source-lib
tokenEnv: GITHUB_TOKEN_OSSThe config is validated with Zod on startup — missing or empty fields are reported immediately with clear error messages.
./mcp.sh setupRegisters the server as mcp-github-issues with user-level scope (visible in all your projects):
=== mcp-github-issues MCP Setup ===
Server name : mcp-github-issues
Scope : user
Dir : /path/to/mcp-github-issues
[1/3] Installing dependencies... Done.
[2/3] Registering MCP server with Claude CLI... Registered 'mcp-github-issues' (scope: user).
[3/3] Verifying...
mcp-github-issues: /path/to/mcp.sh - ✓ Connected
=== Setup complete! ===
Custom server name or scope:
./mcp.sh setup my-server # custom name, user scope
./mcp.sh setup my-server local # local scope (current project only)
./mcp.sh setup my-server project # project scope (.mcp.json)npm install
claude mcp add -s user -- mcp-github-issues /absolute/path/to/mcp-github-issues/mcp.shAfter running ./mcp.sh setup, the server is registered automatically. Verify:
claude mcp list
# mcp-github-issues: /path/to/mcp.sh - ✓ ConnectedAdd to ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"mcp-github-issues": {
"command": "/absolute/path/to/mcp-github-issues/mcp.sh",
"env": {}
}
}
}Tokens are loaded from .env by mcp.sh — no need to duplicate them in the Desktop config. Restart Claude Desktop after editing the config.
Lists all projects defined in projects.yaml.
No input required.
Lists GitHub Issues for a project with optional filters.
| Parameter | Type | Required | Description |
|---|---|---|---|
project |
string |
yes | Project name from projects.yaml |
state |
string |
no | open | closed | all (default: open) |
label |
string |
no | Filter by label name |
assignee |
string |
no | Filter by assignee username |
limit |
number |
no | Max results to return, 1–100 (default: 30) |
Fetches a GitHub Issue by number or URL and returns its full context (description, labels, assignees, comments).
| Parameter | Type | Required | Description |
|---|---|---|---|
project |
string |
yes | Project name from projects.yaml |
issue |
string |
yes | Issue number (e.g. 123) or full GitHub issue URL |
include_comments |
boolean |
no | Whether to fetch comments (default: true) |
comment_limit |
number |
no | Max comments to fetch, 1–100 (default: 50) |
Creates a new GitHub Issue directly, with structured context. Call this only after the user has reviewed and confirmed the content.
| Parameter | Type | Required | Description |
|---|---|---|---|
project |
string |
yes | Project name from projects.yaml |
title |
string |
yes | Issue title (without type prefix) |
context |
string |
yes | Description / background |
files |
string[] |
no | Affected file paths |
checklist |
string[] |
no | Checklist items |
assignee |
string |
no | GitHub username to assign |
type |
string |
no | bug | feature | task (default: task) |
What happens at publish time:
- Title — prefixed with the type tag:
[BUG] Fix ...,[FEATURE] Add ...,[TASK] Refactor ... - Label — applied automatically based on type:
bug→bugfeature→enhancementtask→task
- Body — generated from
context,files, andchecklistwith a "generated by MCP" badge prepended
Example body:
> [!NOTE]
> The task was generated using the MCP server — prog-time/mcp-github-issues
**Type**: bug
**Assignee**: @johndoe
## Context
After OAuth callback, users are redirected back to /login instead of the dashboard.
## Affected Files
- `src/auth/callback.ts`
- `src/middleware/session.ts`
## Checklist
- [ ] Reproduce the issue locally
- [ ] Check session token expiry logic
- [ ] Add integration test for OAuth flowAdds a comment to an existing GitHub Issue.
| Parameter | Type | Required | Description |
|---|---|---|---|
project |
string |
yes | Project name from projects.yaml |
issue |
string |
yes | Issue number (e.g. 123) or full GitHub issue URL |
body |
string |
yes | Comment text (Markdown supported) |
Updates an existing GitHub Issue: change state, title, assignee, or labels.
| Parameter | Type | Required | Description |
|---|---|---|---|
project |
string |
yes | Project name from projects.yaml |
issue |
string |
yes | Issue number or full GitHub issue URL |
state |
string |
no | open | closed |
title |
string |
no | New title |
assignee |
string|null |
no | Set assignee username, or null to remove all |
add_labels |
string[] |
no | Labels to add |
remove_labels |
string[] |
no | Labels to remove |
Creates a new GitHub Pull Request for a configured project.
| Parameter | Type | Required | Description |
|---|---|---|---|
project |
string |
yes | Project name from projects.yaml |
title |
string |
yes | Pull request title |
head |
string |
yes | Branch with changes (e.g. feature/my-feature or fork-owner:branch) |
base |
string |
no | Target branch to merge into (default: main) |
body |
string |
no | PR description (raw Markdown) |
draft |
boolean |
no | Create as draft PR (default: false) |
maintainer_can_modify |
boolean |
no | Allow maintainers to edit the PR head branch (applies to cross-repo PRs only) |
Returns the PR number and html_url.
You: In project "api", create a task: the login page has a redirect loop after OAuth.
Affected files: src/auth/callback.ts. Add a checklist for the fix.
AI: Here's the proposed issue:
Title: Fix login redirect loop after OAuth
Type: bug
Context: ...
Files: src/auth/callback.ts
Checklist: reproduce, check session expiry, add test
Publish it?
You: Yes, publish and assign to johndoe.
AI: Issue #42 created: https://github.com/acme-corp/backend-api/issues/42
You: Close issue #38 — it was fixed in the last release.
AI: Issue #38 updated. State → closed.
mcp-github-issues/
├── src/
│ ├── server.ts # MCP server entry point
│ ├── config.ts # Loads & validates projects.yaml, Octokit cache
│ ├── router.ts # Registers all tools
│ ├── logger.ts # File + stderr logger
│ └── tools/
│ ├── listProjects.ts
│ ├── listIssues.ts
│ ├── fetchIssue.ts
│ ├── publish.ts
│ ├── addComment.ts
│ ├── updateIssue.ts
│ └── createPullRequest.ts
├── tests/
│ └── src/
│ ├── config.test.ts
│ ├── router.test.ts
│ └── tools/
│ ├── fetchIssue.test.ts
│ ├── listProjects.test.ts
│ ├── listIssues.test.ts
│ ├── addComment.test.ts
│ ├── updateIssue.test.ts
│ └── publish.test.ts
├── logs/
│ └── server.log # Server log (auto-created)
├── projects.yaml # Your project definitions
├── projects.yaml.example # Template
├── .env # GitHub tokens (gitignored)
├── .env.example # Template
├── mcp.sh # Server launcher + setup script
├── vitest.config.ts
├── package.json
└── tsconfig.json
The server writes logs to two places simultaneously:
| Destination | Purpose |
|---|---|
logs/server.log |
Persistent file log, human-readable |
stderr |
Captured by Claude Desktop into its mcp-*.log |
tail -f logs/server.logClaude Desktop log location (macOS):
~/Library/Logs/Claude/mcp-server-mcp-github-issues.log
git pull
./mcp.sh setup # re-installs deps and re-registers the server- Fork the repository
- Create a branch:
git checkout -b feature/my-feature - Make your changes and add tests
- Open a pull request
npm run dev # dev mode (tsx watch)
npm test # run tests once
npm run test:watch # watch mode
npx tsc --noEmit # type check
npx eslint . # lint
npm run build # compile to dist/CI runs ESLint, type-check, and the full test suite on every push and pull request against main.
MIT