# 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:** 1. **Format Code:** `script/lint` (runs `gofmt -s -w .` then `golangci-lint`) 2. **Run Tests:** `script/test` (runs `go test -race ./...`) 3. **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: 1. Run tests with `UPDATE_TOOLSNAPS=true go test ./...` to update toolsnaps 2. Commit the updated `.snap` files in `pkg/github/__toolsnaps__/` 3. Run `script/generate-docs` to update README.md 4. Toolsnaps document API surface and ensure changes are intentional ### Common Build Commands ```bash # 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/`: 1. **go.yml** - Build and test on ubuntu/windows/macos. Runs `script/test` and builds binary 2. **lint.yml** - Runs golangci-lint-action v2.5 (GitHub Action) with actions/setup-go stable 3. **docs-check.yml** - Verifies README.md is up-to-date by running generate-docs and checking git diff 4. **code-scanning.yml** - CodeQL security analysis for Go and GitHub Actions 5. **license-check.yml** - Runs `script/licenses-check` to validate compliance 6. **docker-publish.yml** - Publishes container image to ghcr.io 7. **goreleaser.yml** - Creates releases (main branch only) 8. **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 `testify` for assertions (`require` for critical checks, `assert` for non-blocking) - Tests are in `*_test.go` files alongside implementation (internal tests, not `_test` package) - Mock GitHub API with `go-github-mock` (REST) or `githubv4mock` (GraphQL) - Test structure for tools: 1. Test tool snapshot 2. Verify critical schema properties (e.g., ReadOnly annotation) 3. 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 with `e2e_test.go` - **Require GitHub PAT token** - you usually cannot run these yourself - Run with: `GITHUB_MCP_SERVER_E2E_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=true` runs 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 `ID` not `Id`, `API` not `Api`, `URL` not `Url`, `HTTP` not `Http` - 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 1. Add tool implementation in `pkg/github/` (e.g., `foo_tools.go`) 2. Register tool in appropriate toolset in `pkg/github/` or `pkg/toolsets/` 3. Write unit tests following the tool test pattern 4. Run `UPDATE_TOOLSNAPS=true go test ./...` to create snapshot 5. Run `script/generate-docs` to update README 6. Run `script/lint` and `script/test` before committing 7. If e2e tests are relevant, update `e2e/e2e_test.go` using existing test style 8. Commit code + snapshots + README changes together ### Fixing a Bug 1. Write a failing test that reproduces the bug 2. Fix the bug with minimal changes 3. Verify test passes and existing tests still pass 4. Run `script/lint` and `script/test` 5. If tool schema changed, update toolsnaps (see above) ### Updating Dependencies 1. Update `go.mod` (e.g., `go get -u ./...` or manually) 2. Run `go mod tidy` 3. Run `script/licenses` to update license files 4. Run `script/test` to verify nothing broke 5. 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:** 1. Forgot to update toolsnaps - run with `UPDATE_TOOLSNAPS=true` 2. Changed behavior broke existing tests - verify intent and fix tests 3. 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: - `stdio` command (default) - MCP stdio transport - `generate-docs` command - Documentation generation - Flags: --toolsets, --read-only, --gh-host, --log-file ## Important Reminders 1. **PRIMARY FOCUS:** The local stdio MCP server (github-mcp-server) - this is what you should work on and test with 2. **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. 3. **ALWAYS** trust these instructions first - only search if information is incomplete or incorrect 4. **NEVER** use `script/tag-release` or push tags 5. **NEVER** skip `script/lint` before committing Go code changes 6. **ALWAYS** update toolsnaps when changing MCP tool schemas 7. **ALWAYS** run `script/generate-docs` after modifying tools 8. For specific test files, use `go test ./path -run TestName` not full suite 9. E2E tests require PAT token - you likely cannot run them 10. Toolsnaps are API documentation - treat changes seriously 11. Build/test/lint are very fast (~1s each) - run frequently 12. CI failures for docs-check or license-check have simple fixes (run the script) 13. mcpcurl is secondary - don't break it, but it's not the priority