This is the GitHub MCP Server, a Model Context Protocol (MCP) server that connects AI tools to GitHub's platform. It enables AI agents to manage repositories, issues, pull requests, workflows, and more through natural language.
Key Details:
- Language: Go 1.24+ (~38k lines of code)
- Type: MCP server application with CLI interface
- Primary Package: github-mcp-server (stdio MCP server - this is the main focus)
- Secondary Package: mcpcurl (testing utility - don't break it, but not the priority)
- Framework: Uses modelcontextprotocol/go-sdk for MCP protocol, google/go-github for GitHub API
- Size: ~60MB repository, 70 Go files
- Library Usage: This repository is also used as a library by the remote server. Functions that could be called by other repositories should be exported (capitalized), even if not required internally. Preserve existing export patterns.
Code Quality Standards:
- Popular Open Source Repository - High bar for code quality and clarity
- Comprehension First - Code must be clear to a wide audience
- Clean Commits - Atomic, focused changes with clear messages
- Structure - Always maintain or improve, never degrade
- Code over Comments - Prefer self-documenting code; comment only when necessary
ALWAYS run these commands in this exact order before using report_progress or finishing work:
- Format Code:
script/lint(runsgofmt -s -w .thengolangci-lint) - Run Tests:
script/test(runsgo test -race ./...) - Update Documentation:
script/generate-docs(if you modified MCP tools/toolsets)
These commands are FAST: Lint ~1s, Tests ~1s (cached), Build ~1s
If you change any MCP tool definitions or schemas:
- Run tests with
UPDATE_TOOLSNAPS=true go test ./...to update toolsnaps - Commit the updated
.snapfiles inpkg/github/__toolsnaps__/ - Run
script/generate-docsto update README.md - Toolsnaps document API surface and ensure changes are intentional
# Download dependencies (rarely needed - usually cached)
go mod download
# Build the server binary
go build -v ./cmd/github-mcp-server
# Run the server
./github-mcp-server stdio
# Run specific package tests
go test ./pkg/github -v
# Run specific test
go test ./pkg/github -run TestGetMe.
├── cmd/
│ ├── github-mcp-server/ # Main MCP server entry point (PRIMARY FOCUS)
│ └── mcpcurl/ # MCP testing utility (secondary - don't break it)
├── pkg/ # Public API packages
│ ├── github/ # GitHub API MCP tools implementation
│ │ └── __toolsnaps__/ # Tool schema snapshots (*.snap files)
│ ├── toolsets/ # Toolset configuration & management
│ ├── errors/ # Error handling utilities
│ ├── sanitize/ # HTML/content sanitization
│ ├── log/ # Logging utilities
│ ├── raw/ # Raw data handling
│ ├── buffer/ # Buffer utilities
│ └── translations/ # i18n translation support
├── internal/ # Internal implementation packages
│ ├── ghmcp/ # GitHub MCP server core logic
│ ├── githubv4mock/ # GraphQL API mocking for tests
│ ├── toolsnaps/ # Toolsnap validation system
│ └── profiler/ # Performance profiling
├── e2e/ # End-to-end tests (require GitHub PAT)
├── script/ # Build and maintenance scripts
├── docs/ # Documentation
├── .github/workflows/ # CI/CD workflows
└── [config files] # See below
- go.mod / go.sum: Go module dependencies (Go 1.24.0+)
- .golangci.yml: Linter configuration (v2 format, ~15 linters enabled)
- Dockerfile: Multi-stage build (golang:1.25.3-alpine → distroless)
- server.json: MCP server metadata for registry
- .goreleaser.yaml: Release automation config
- .gitignore: Excludes bin/, dist/, vendor/, *.DS_Store, github-mcp-server binary
- script/lint - Runs
gofmt+golangci-lint. MUST RUN before committing - script/test - Runs
go test -race ./...(full test suite) - script/generate-docs - Updates README.md tool documentation. Run after tool changes
- script/licenses - Updates third-party license files when dependencies change
- script/licenses-check - Validates license compliance (runs in CI)
- script/get-me - Quick test script for get_me tool
- script/get-discussions - Quick test for discussions
- script/tag-release - NEVER USE THIS - releases are managed separately
All workflows run on push/PR unless noted. Located in .github/workflows/:
- go.yml - Build and test on ubuntu/windows/macos. Runs
script/testand builds binary - lint.yml - Runs golangci-lint-action v2.5 (GitHub Action) with actions/setup-go stable
- docs-check.yml - Verifies README.md is up-to-date by running generate-docs and checking git diff
- code-scanning.yml - CodeQL security analysis for Go and GitHub Actions
- license-check.yml - Runs
script/licenses-checkto validate compliance - docker-publish.yml - Publishes container image to ghcr.io
- goreleaser.yml - Creates releases (main branch only)
- registry-releaser.yml - Updates MCP registry
All of these must pass for PR merge. If docs-check fails, run script/generate-docs and commit changes.
- Use
testifyfor assertions (requirefor critical checks,assertfor non-blocking) - Tests are in
*_test.gofiles alongside implementation (internal tests, not_testpackage) - Mock GitHub API with
go-github-mock(REST) orgithubv4mock(GraphQL) - Test structure for tools:
- Test tool snapshot
- Verify critical schema properties (e.g., ReadOnly annotation)
- Table-driven behavioral tests
- Every MCP tool has a JSON schema snapshot in
pkg/github/__toolsnaps__/*.snap - Tests fail if current schema differs from snapshot (shows diff)
- To update after intentional changes:
UPDATE_TOOLSNAPS=true go test ./... - MUST commit updated .snap files - they document API changes
- Missing snapshots cause CI failure
- Located in
e2e/directory withe2e_test.go - Require GitHub PAT token - you usually cannot run these yourself
- Run with:
GITHUB_MCP_SERVER_E2E_TOKEN=<token> go test -v --tags e2e ./e2e - Tests interact with live GitHub API via Docker container
- Keep e2e tests updated when changing MCP tools
- Use only the e2e test style when modifying tests in this directory
- For debugging:
GITHUB_MCP_SERVER_E2E_DEBUG=trueruns in-process (no Docker)
- gofmt with simplify flag (-s) - Automatically run by
script/lint - golangci-lint with these linters enabled:
- bodyclose, gocritic, gosec, makezero, misspell, nakedret, revive
- errcheck, staticcheck, govet, ineffassign, unused
- Exclusions for: third_party/, builtin/, examples/, generated code
- Acronyms in identifiers: Use
IDnotId,APInotApi,URLnotUrl,HTTPnotHttp - Examples:
userID,getAPI,parseURL,HTTPClient - This applies to variable names, function names, struct fields, etc.
- Keep changes minimal and focused on the specific issue being addressed
- Prefer clarity over cleverness - code must be understandable by a wide audience
- Atomic commits - each commit should be a complete, logical change
- Maintain or improve structure - never degrade code organization
- Use table-driven tests for behavioral testing
- Comment sparingly - code should be self-documenting
- Follow standard Go conventions (Effective Go, Go proverbs)
- Test changes thoroughly before committing
- Export functions (capitalize) if they could be used by other repos as a library
- Add tool implementation in
pkg/github/(e.g.,foo_tools.go) - Register tool in appropriate toolset in
pkg/github/orpkg/toolsets/ - Write unit tests following the tool test pattern
- Run
UPDATE_TOOLSNAPS=true go test ./...to create snapshot - Run
script/generate-docsto update README - Run
script/lintandscript/testbefore committing - If e2e tests are relevant, update
e2e/e2e_test.gousing existing test style - Commit code + snapshots + README changes together
- Write a failing test that reproduces the bug
- Fix the bug with minimal changes
- Verify test passes and existing tests still pass
- Run
script/lintandscript/test - If tool schema changed, update toolsnaps (see above)
- Update
go.mod(e.g.,go get -u ./...or manually) - Run
go mod tidy - Run
script/licensesto update license files - Run
script/testto verify nothing broke - Commit go.mod, go.sum, and third-party-licenses* files
Fix: Run script/generate-docs and commit README.md changes
Fix: Run UPDATE_TOOLSNAPS=true go test ./... and commit updated .snap files
Fix: Run script/lint locally - it will auto-format and show issues. Fix manually reported issues.
Fix: Run script/licenses to regenerate license files after dependency changes
Likely causes:
- Forgot to update toolsnaps - run with
UPDATE_TOOLSNAPS=true - Changed behavior broke existing tests - verify intent and fix tests
- Schema change not reflected in test - update test expectations
- GITHUB_PERSONAL_ACCESS_TOKEN - Required for server operation and e2e tests
- GITHUB_HOST - For GitHub Enterprise Server (prefix with
https://) - GITHUB_TOOLSETS - Comma-separated toolset list (overrides --toolsets flag)
- GITHUB_READ_ONLY - Set to "1" for read-only mode
- GITHUB_DYNAMIC_TOOLSETS - Set to "1" for dynamic toolset discovery
- UPDATE_TOOLSNAPS - Set to "true" when running tests to update snapshots
- GITHUB_MCP_SERVER_E2E_TOKEN - Token for e2e tests
- GITHUB_MCP_SERVER_E2E_DEBUG - Set to "true" for in-process e2e debugging
.dockerignore - Docker build exclusions
.gitignore - Git exclusions (includes bin/, dist/, vendor/, binaries)
.golangci.yml - Linter configuration
.goreleaser.yaml - Release automation
CODE_OF_CONDUCT.md - Community guidelines
CONTRIBUTING.md - Contribution guide (fork, clone, test, lint workflow)
Dockerfile - Multi-stage Go build
LICENSE - MIT license
README.md - Main documentation (auto-generated sections)
SECURITY.md - Security policy
SUPPORT.md - Support resources
gemini-extension.json - Gemini CLI configuration
go.mod / go.sum - Go dependencies
server.json - MCP server registry metadata
cmd/github-mcp-server/main.go - Uses cobra for CLI, viper for config, supports:
stdiocommand (default) - MCP stdio transportgenerate-docscommand - Documentation generation- Flags: --toolsets, --read-only, --dynamic-toolsets, --gh-host, --log-file
- PRIMARY FOCUS: The local stdio MCP server (github-mcp-server) - this is what you should work on and test with
- REMOTE SERVER: Ignore remote server instructions when making code changes (unless specifically asked). This repo is used as a library by the remote server, so keep functions exported (capitalized) if they could be called by other repos, even if not needed internally.
- ALWAYS trust these instructions first - only search if information is incomplete or incorrect
- NEVER use
script/tag-releaseor push tags - NEVER skip
script/lintbefore committing Go code changes - ALWAYS update toolsnaps when changing MCP tool schemas
- ALWAYS run
script/generate-docsafter modifying tools - For specific test files, use
go test ./path -run TestNamenot full suite - E2E tests require PAT token - you likely cannot run them
- Toolsnaps are API documentation - treat changes seriously
- Build/test/lint are very fast (~1s each) - run frequently
- CI failures for docs-check or license-check have simple fixes (run the script)
- mcpcurl is secondary - don't break it, but it's not the priority