Skip to main content
Glama
jersonmartinez

github-project-management

GitHub Projects V2 — MCP Server

CI License: MIT Python 3.12+ FastMCP Docker

A Model Context Protocol (MCP) server that lets AI assistants and agents manage GitHub Projects V2 boards programmatically — issues, fields, milestones, labels, sub-issues, and full sprint/planning workflows. Built with Python 3.12 and FastMCP 3.x, it speaks stdio JSON-RPC and runs entirely inside a standalone Docker container — no host toolchain required beyond Docker.

The server exposes 100+ tools: ~40 operational primitives plus a suite of ~60 higher-level capabilities for reporting, planning, roadmaps, and automation.


Features

  • 100+ MCP tools covering the full GitHub Projects V2 surface: discovery, board operations, issue lifecycle, milestones, labels, sub-issues, and strategic planning.

  • GitHub Projects V2 native — GraphQL v4 for field/board mutations, REST/gh for issue CRUD, with automatic delegation to the right API per operation.

  • Organization and user projects via a single GH_PROJECT_OWNER_TYPE switch.

  • Access levels — MCP_ACCESS_LEVEL=read|write|full decides which tools are even registered: read exposes read-only tools, write (default) adds create/update/close/archive, full also exposes permanent-delete tools (each gated behind confirm:true). See docs/CAPABILITIES.md.

  • Scope lock — GH_PROJECT_SCOPE_LOCK=true fences every tool to the configured org/repo/project; foreign targets are refused before any mutation.

  • Docker-first — one image, zero host dependencies, launched on demand by the MCP client over stdio.

  • MCP-client agnostic — works with any client that speaks MCP over stdio; no IDE lock-in.

  • Least-privilege ready — every tool maps to a documented capability (docs/CAPABILITIES.md) so you can scope tokens tightly.

  • Hardened runtime — bounded timeouts/retries, atomic owner-only metadata cache, target-namespaced isolation, and token redaction in all diagnostics.

  • Multi-target profiles — manage several boards from one install via named profiles/*.env files.


Related MCP server: GitHub Projects MCP Server

Quick Start

1. Build the image

docker build -t mcp-github-projects:latest .   # or: make build

Or use a released multi-arch image instead of building: docker pull ghcr.io/jersonmartinez/mcp-github-projects:1.1.0 (also tagged 1.1 and latest; see docs/RELEASING.md) and use that name wherever the examples say mcp-github-projects:latest.

2. Configure your target

Copy the template and fill in your token and board coordinates:

cp .env.example .env
# Authentication — a GitHub PAT (classic or fine-grained)
GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# GH_TOKEN is also accepted as a fallback

# Target board
GH_PROJECT_ORG_NAME=my-org          # organization login or username
GH_PROJECT_REPO_NAME=my-repo        # repository within the owner
GH_PROJECT_PROJECT_NUMBER=1         # Project V2 number (from the board URL)
GH_PROJECT_OWNER_TYPE=organization  # 'organization' or 'user'

Token requirements:

  • Classic PAT scopes: repo, project, read:org

  • Fine-grained PAT permissions: Issues (RW), Projects (RW), Organization → Projects (RW), Organization → Members (R)

See docs/SETUP.md for token generation and rotation.

3. Verify the setup

make verify          # validates auth + scopes + target config inside Docker

4. Wire it into your MCP client

The server is launched on demand — one docker run per session, torn down with --rm when the client disconnects:

docker run --rm -i --env-file .env mcp-github-projects:latest python server.py

On startup it prints to stderr and then waits for JSON-RPC on stdin:

github-project-management MCP server ready. Authentication validated successfully.

MCP Client Configuration

The server works with any MCP client that supports the stdio transport. Add an entry to your client's MCP server configuration:

{
  "mcpServers": {
    "github-projects": {
      "command": "docker",
      "args": [
        "run", "--rm", "-i",
        "--env-file", "/absolute/path/to/.env",
        "mcp-github-projects:latest",
        "python", "server.py"
      ]
    }
  }
}

The config key (github-projects) is arbitrary — name it whatever your client displays. Credentials are supplied through --env-file; nothing sensitive lives in the JSON.

IDE-specific setup (config file locations, per-client quirks) is documented in docs/SETUP.md.


Tool Catalog

The server registers 129 tools at MCP_ACCESS_LEVEL=full (fewer at read/write). A category overview — the generated, per-tool catalog is docs/TOOLS.md:

Category

What it covers

Representative tools

Discovery & Board

Resolve IDs, list/filter items, create items, move across columns

discover_ids, list_project_items, create_project_item, move_to_status, move_to_done, move_to_trash, archive_project_item

Fields & Estimates

Update Status/Priority/Due date, set story points

update_project_item_fields, set_estimate

Issues

Full issue lifecycle and sub-issues

create_project_item, edit_issue, close_issue, reopen_issue, comment_issue, get_issue_detail, search_issues, add_sub_issue, remove_sub_issue, list_sub_issues

Bulk operations

Batch updates across many items

bulk_update_items, bulk_close_issues, bulk_assign

Milestones & Labels

Create/close/list milestones; create/list labels

create_milestone, close_milestone, list_milestones, create_label, list_labels

Repository provisioning

Create a user or organization repository with explicit visibility

create_repository

Planning & Workflows

Sprints, standups, epics, triage, releases

sprint_planning, create_epic, close_sprint, daily_standup, sprint_review, triage_new_issues, escalate_overdue, generate_release_notes, complete_issue

PR ↔ Issue lifecycle

Verify acceptance, link PRs, gate closures

verify_acceptance_criteria, get_pr_linked_issues, validate_issue_closure_readiness, close_issue_on_pr_merge

CI / GitHub Actions

Read PR checks, runs, jobs and logs; re-run or dispatch workflows

get_pr_checks, list_workflows, list_workflow_runs, get_workflow_run, get_job_logs, rerun_workflow_run, dispatch_workflow

Metrics

Board and sprint statistics

get_project_stats, get_sprint_summary

Diagnostics

Report effective access level, scope lock, and target

server_info

Board structure

Plan/apply Status (single-select) options without losing item values; list/create views

set_field_options, list_project_views, create_project_view

Permanent delete (access level full, each needs confirm:true)

Irreversible removals

delete_project_item, delete_issue, delete_issue_comment, delete_label, delete_milestone

Extended capability suite (~60)

Reporting, roadmaps, changelogs, backlog ranking, risk registers, retrospectives

project_health_report, project_export_markdown, plan_next_sprint, prioritize_backlog, generate_risk_register, build_roadmap_markdown, build_sprint_retrospective

Tools that could perform broad mutations return a dry_run plan by default. The capability suite asserts its 60 unique additions at import time, CI verifies the registered-tool count stays at 100+, and tests/test_tool_docs.py fails when docs/TOOLS.md drifts from the registry or any tool lacks a description.

Full input/output reference: docs/USAGE.md · Parameter reference: docs/PARAMETERS.md · Least-privilege capability matrix: docs/CAPABILITIES.md


Architecture

Layered, dependency-inward design. Each layer depends only on the one below it:

MCP Client (stdio JSON-RPC)
        │
        ▼
server.py          FastMCP instance — registers every tool, validates auth on startup
        │
        ▼
tools/             MCP tool definitions (thin, declarative, dry_run-aware)
        │
        ▼
services/          Business logic & orchestration (project / issue / field / discovery)
        │
        ▼
clients/           GraphQL client + gh CLI client + metadata cache
        │
        ▼
graphql/           Query & mutation strings for the GitHub GraphQL v4 API
        │
        ▼
GitHub APIs        GraphQL v4 (fields, board, sub-issues) + REST v3 (issue CRUD)

Supporting modules at the root: config.py (validated Pydantic settings), auth.py (token resolution + scope checks), capabilities.py (tool → permission map), profiles.py (multi-target profiles), hardening.py and error_handling.py (runtime safety), models/ (Pydantic response/context models).

API delegation

Operation kind

Backend used

Issue CRUD, comments, project item-add, close

gh CLI

Field updates, archival, discovery, sub-issues

Custom GraphQL v4


Configuration

All settings use the GH_PROJECT_ prefix and are validated at startup by config.py. Target fields are mandatory — the server refuses to start without them.

Variable

Required

Default

Description

GITHUB_TOKEN

yes¹

—

GitHub PAT (classic or fine-grained)

GH_TOKEN

—

—

Fallback token if GITHUB_TOKEN is unset

GH_PROJECT_ORG_NAME

yes

—

Owner: organization login or username

GH_PROJECT_REPO_NAME

yes

—

Repository within the owner

GH_PROJECT_PROJECT_NUMBER

yes

—

Project V2 number (1–100000)

GH_PROJECT_OWNER_TYPE

—

organization

organization or user

MCP_ACCESS_LEVEL

—

write

Which tools are exposed: read, write, or full. Note: MCP_-prefixed, not GH_PROJECT_.

GH_PROJECT_SCOPE_LOCK

—

false

Confine every tool to the configured org/repo/project

GH_PROJECT_PROFILE

—

—

Load profiles/<name>.env instead of root .env

GH_PROJECT_TIMEOUT_SECONDS

—

10

Per-call timeout (1–120)

GH_PROJECT_RETRY_ATTEMPTS

—

1

Read retries (0–5; mutations never retry)

GH_PROJECT_CACHE_TTL_HOURS

—

24

Metadata cache TTL (1–720)

GH_PROJECT_MAX_ITEMS

—

200

Max items per list/query (1–1000)

GH_PROJECT_PAGE_SIZE

—

100

Page size (1–100)

¹ Token resolution order: GITHUB_TOKEN → GH_TOKEN → gh auth token.

The metadata cache is written atomically with owner-only permissions (0600), namespaced per owner/repo/project, rejects future timestamps, and is never reused across targets. See docs/PARAMETERS.md for the full range table and docs/HARDENING_200.md for the runtime hardening register.

Access levels & scope lock

Two independent switches narrow what an MCP client can do — enforced by the server, not by trust in the client.

MCP_ACCESS_LEVEL decides which tools are registered (a hidden tool is invisible to the client, not merely refused):

Level

Exposes

Permanent deletes

read

read-only tools (discovery, listing, reporting)

hidden

write (default)

read + create / update / close / archive / move

hidden

full

everything

exposed, each requiring confirm:true

The five permanent-delete tools (delete_project_item, delete_issue, delete_issue_comment, delete_label, delete_milestone) exist ONLY at full, and each refuses unless called with confirm:true, pointing you at the reversible alternative (close_issue, archive_project_item, …).

MCP_ACCESS_LEVEL is MCP_-prefixed (not GH_PROJECT_), matching the write-policy variable convention in the sibling mcp-monday-projects server.

GH_PROJECT_SCOPE_LOCK=true confines every tool to the configured GH_PROJECT_ORG_NAME / GH_PROJECT_REPO_NAME / GH_PROJECT_PROJECT_NUMBER. A call that targets any other owner/repo/project is refused with a typed error naming the variable, before any mutation runs. Creating repositories or new projects is disabled while the lock is on. (Parity concept with mcp-monday-projects' MONDAY_WORKSPACE_ID.)

Call the server_info tool at any time to see the effective access level, scope lock, and target — no credentials are ever included in its output.


Development

Everything runs inside Docker — there are no host Python dependencies. The Makefile is the entry point:

make help        # list all targets
make build       # build mcp-github-projects:latest
make rebuild     # build with --no-cache
make verify      # validate auth + scopes + target config
make test        # run unit tests inside the container
make syntax      # ast.parse every .py file
make tools       # count registered tools (must be >= 100)
make secrets     # scan the source tree for leaked credentials
make validate    # full CI mirror: build + syntax + test + tools + secrets
make shell       # interactive shell inside the container
make run         # start the server (stdio) via compose.yaml
make clean       # remove built images

If make is unavailable, invoke targets directly, e.g. docker run --rm --env-file .env mcp-github-projects:latest python3 scripts/verify_setup.py.

Local validation before opening a PR (mirrors CI):

bash scripts/validate.sh            # full run (builds image + all checks)
bash scripts/validate.sh --quick    # reuse cached image, skip rebuild
bash scripts/validate.sh --fix      # auto-fix known issues (e.g. UTF-8 BOM)

Helper scripts under scripts/:

Script

Purpose

validate.sh

Full CI mirror — run before every push/PR

preflight.sh

Prerequisite check (Docker, token, image, target config, scopes; --fix supported)

scan_secrets.sh

Token-pattern detection in tracked files

smoke_build.sh

Minimal build + tool count sanity check

run_contract_tests.sh

Multi-target contract suite

count_tools.py / check_syntax.py / verify_setup.py

Individual checks used by the Makefile

Contributions follow CONTRIBUTING.md; security reports go through SECURITY.md.


Documentation

Document

Purpose

docs/SETUP.md

Token generation, rotation, and per-IDE integration

docs/TOOLS.md

Generated catalog: every tool, access tier, capabilities

docs/USAGE.md

Input/output examples per category

docs/PARAMETERS.md

Full parameter and setting reference

docs/CAPABILITIES.md

Tool → permission matrix for least-privilege tokens

docs/GRAPHQL_REFERENCE.md

GraphQL queries/mutations used internally

docs/TROUBLESHOOTING.md

Common errors and fixes

docs/HARDENING_200.md

Runtime hardening register

docs/RELEASING.md

Versioning, the release workflow and the GHCR image


License

Released under the MIT License. See CHANGELOG.md for release history; this project follows Semantic Versioning.

Maintenance

ActivityMaintained
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers