Dynamic toolset discovery (the meta-tools enable_toolset, list_available_toolsets, get_toolset_tools and the --dynamic-toolsets / GITHUB_DYNAMIC_TOOLSETS switch) was a local-only feature never offered by the remote server. Removing it deletes a meaningful chunk of branching, configuration surface and tests for a path no longer in active use. The deprecated closure-based NewServerToolWithDeps generic constructor was only kept around for the dynamic tool registration path and is removed together with it. Going forward there are exactly two constructors: - NewServerTool — raw mcp.ToolHandler, no closure, no unmarshalling - NewServerToolWithContextHandler[In, Out] — typed handler, deps via context Inventory methods that only existed for the dynamic path (ToolsForToolset, IsToolsetEnabled, EnableToolset, EnabledToolsetIDs) are removed. ResolvedEnabledToolsets loses its dynamic flag. Also strips dynamic references from the README, server configuration docs, copilot-instructions, mcp-diff workflow, and conformance-test script. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
13 KiB
GitHub MCP Server - Copilot Instructions
Project Overview
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
Critical Build & Validation Steps
Required Commands (Run Before Committing)
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
When Modifying MCP Tools/Endpoints
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
Common Build Commands
# 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
Project Structure
Directory Layout
.
├── 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
Key Configuration Files
- 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.8-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
Important Scripts (script/ directory)
- 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
GitHub Workflows (CI/CD)
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.
Testing Guidelines
Unit Tests
- 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
Toolsnaps (Tool Schema Snapshots)
- 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
End-to-End Tests
- 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)
Code Style & Linting
Go Code Requirements
- 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
Go Naming Conventions
- Acronyms in identifiers: Use
IDnotId,APInotApi,URLnotUrl,HTTPnotHttp - Examples:
userID,getAPI,parseURL,HTTPClient - This applies to variable names, function names, struct fields, etc.
Code Patterns
- 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
Common Development Workflows
Adding a New MCP Tool
- 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
Fixing a Bug
- 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)
Updating Dependencies
- 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
Common Errors & Solutions
"Documentation is out of date" in CI
Fix: Run script/generate-docs and commit README.md changes
Toolsnap mismatch failures
Fix: Run UPDATE_TOOLSNAPS=true go test ./... and commit updated .snap files
Lint failures
Fix: Run script/lint locally - it will auto-format and show issues. Fix manually reported issues.
License check failures
Fix: Run script/licenses to regenerate license files after dependency changes
Test failures after changing a tool
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
Environment Variables
- 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
- 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
Key Files Reference
Root Directory Files
.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
Main Entry Point
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, --gh-host, --log-file
Important Reminders
- 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