The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .dockerignore
├── .github
    ├── CODEOWNERS
    ├── ISSUE_TEMPLATE
    │   ├── bug_report.md
    │   └── feature_request.md
    ├── dependabot.yml
    ├── licenses.tmpl
    ├── pull_request_template.md
    └── workflows
    │   ├── code-scanning.yml
    │   ├── docker-publish.yml
    │   ├── docs-check.yml
    │   ├── go.yml
    │   ├── goreleaser.yml
    │   ├── license-check.yml
    │   └── lint.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yaml
├── .vscode
    └── launch.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Dockerfile
├── LICENSE
├── README.md
├── SECURITY.md
├── SUPPORT.md
├── cmd
    ├── github-mcp-server
    │   ├── generate_docs.go
    │   └── main.go
    └── mcpcurl
    │   ├── README.md
    │   └── main.go
├── docs
    ├── error-handling.md
    ├── host-integration.md
    ├── remote-server.md
    └── testing.md
├── e2e
    ├── README.md
    └── e2e_test.go
├── go.mod
├── go.sum
├── internal
    ├── ghmcp
    │   └── server.go
    ├── githubv4mock
    │   ├── githubv4mock.go
    │   ├── local_round_tripper.go
    │   ├── objects_are_equal_values.go
    │   ├── objects_are_equal_values_test.go
    │   └── query.go
    └── toolsnaps
    │   ├── toolsnaps.go
    │   └── toolsnaps_test.go
├── pkg
    ├── errors
    │   ├── error.go
    │   └── error_test.go
    ├── github
    │   ├── __toolsnaps__
    │   │   ├── add_issue_comment.snap
    │   │   ├── add_pull_request_review_comment_to_pending_review.snap
    │   │   ├── assign_copilot_to_issue.snap
    │   │   ├── create_and_submit_pull_request_review.snap
    │   │   ├── create_branch.snap
    │   │   ├── create_issue.snap
    │   │   ├── create_or_update_file.snap
    │   │   ├── create_pending_pull_request_review.snap
    │   │   ├── create_pull_request.snap
    │   │   ├── create_repository.snap
    │   │   ├── delete_file.snap
    │   │   ├── delete_pending_pull_request_review.snap
    │   │   ├── dismiss_notification.snap
    │   │   ├── fork_repository.snap
    │   │   ├── get_code_scanning_alert.snap
    │   │   ├── get_commit.snap
    │   │   ├── get_dependabot_alert.snap
    │   │   ├── get_file_contents.snap
    │   │   ├── get_issue.snap
    │   │   ├── get_issue_comments.snap
    │   │   ├── get_me.snap
    │   │   ├── get_notification_details.snap
    │   │   ├── get_pull_request.snap
    │   │   ├── get_pull_request_comments.snap
    │   │   ├── get_pull_request_diff.snap
    │   │   ├── get_pull_request_files.snap
    │   │   ├── get_pull_request_reviews.snap
    │   │   ├── get_pull_request_status.snap
    │   │   ├── get_tag.snap
    │   │   ├── list_branches.snap
    │   │   ├── list_code_scanning_alerts.snap
    │   │   ├── list_commits.snap
    │   │   ├── list_dependabot_alerts.snap
    │   │   ├── list_issues.snap
    │   │   ├── list_notifications.snap
    │   │   ├── list_pull_requests.snap
    │   │   ├── list_tags.snap
    │   │   ├── manage_notification_subscription.snap
    │   │   ├── manage_repository_notification_subscription.snap
    │   │   ├── mark_all_notifications_read.snap
    │   │   ├── merge_pull_request.snap
    │   │   ├── push_files.snap
    │   │   ├── request_copilot_review.snap
    │   │   ├── search_code.snap
    │   │   ├── search_issues.snap
    │   │   ├── search_pull_requests.snap
    │   │   ├── search_repositories.snap
    │   │   ├── search_users.snap
    │   │   ├── submit_pending_pull_request_review.snap
    │   │   ├── update_issue.snap
    │   │   ├── update_pull_request.snap
    │   │   └── update_pull_request_branch.snap
    │   ├── actions.go
    │   ├── actions_test.go
    │   ├── code_scanning.go
    │   ├── code_scanning_test.go
    │   ├── context_tools.go
    │   ├── context_tools_test.go
    │   ├── dependabot.go
    │   ├── dependabot_test.go
    │   ├── discussions.go
    │   ├── discussions_test.go
    │   ├── dynamic_tools.go
    │   ├── helper_test.go
    │   ├── issues.go
    │   ├── issues_test.go
    │   ├── notifications.go
    │   ├── notifications_test.go
    │   ├── pullrequests.go
    │   ├── pullrequests_test.go
    │   ├── repositories.go
    │   ├── repositories_test.go
    │   ├── repository_resource.go
    │   ├── repository_resource_test.go
    │   ├── search.go
    │   ├── search_test.go
    │   ├── search_utils.go
    │   ├── secret_scanning.go
    │   ├── secret_scanning_test.go
    │   ├── server.go
    │   ├── server_test.go
    │   └── tools.go
    ├── log
    │   ├── io.go
    │   └── io_test.go
    ├── raw
    │   ├── raw.go
    │   ├── raw_mock.go
    │   └── raw_test.go
    ├── toolsets
    │   ├── toolsets.go
    │   └── toolsets_test.go
    └── translations
    │   └── translations.go
├── script
    ├── generate-docs
    ├── get-discussions
    ├── get-me
    ├── licenses
    ├── licenses-check
    ├── lint
    ├── prettyprint-log
    ├── tag-release
    └── test
├── third-party-licenses.darwin.md
├── third-party-licenses.linux.md
├── third-party-licenses.windows.md
└── third-party
    ├── github.com
        ├── fsnotify
        │   └── fsnotify
        │   │   └── LICENSE
        ├── github
        │   └── github-mcp-server
        │   │   └── LICENSE
        ├── go-openapi
        │   ├── jsonpointer
        │   │   └── LICENSE
        │   └── swag
        │   │   └── LICENSE
        ├── go-viper
        │   └── mapstructure
        │   │   └── v2
        │   │       └── LICENSE
        ├── google
        │   ├── go-github
        │   │   ├── v71
        │   │   │   └── github
        │   │   │   │   └── LICENSE
        │   │   └── v73
        │   │   │   └── github
        │   │   │       └── LICENSE
        │   ├── go-querystring
        │   │   └── query
        │   │   │   └── LICENSE
        │   └── uuid
        │   │   └── LICENSE
        ├── gorilla
        │   └── mux
        │   │   └── LICENSE
        ├── inconshreveable
        │   └── mousetrap
        │   │   └── LICENSE
        ├── josephburnett
        │   └── jd
        │   │   └── v2
        │   │       └── LICENSE
        ├── josharian
        │   └── intern
        │   │   └── license.md
        ├── mailru
        │   └── easyjson
        │   │   └── LICENSE
        ├── mark3labs
        │   └── mcp-go
        │   │   └── LICENSE
        ├── migueleliasweb
        │   └── go-github-mock
        │   │   └── src
        │   │       └── mock
        │   │           └── LICENSE
        ├── pelletier
        │   └── go-toml
        │   │   └── v2
        │   │       └── LICENSE
        ├── sagikazarmark
        │   └── locafero
        │   │   └── LICENSE
        ├── shurcooL
        │   ├── githubv4
        │   │   └── LICENSE
        │   └── graphql
        │   │   └── LICENSE
        ├── sirupsen
        │   └── logrus
        │   │   └── LICENSE
        ├── sourcegraph
        │   └── conc
        │   │   └── LICENSE
        ├── spf13
        │   ├── afero
        │   │   └── LICENSE.txt
        │   ├── cast
        │   │   └── LICENSE
        │   ├── cobra
        │   │   └── LICENSE.txt
        │   ├── pflag
        │   │   └── LICENSE
        │   └── viper
        │   │   └── LICENSE
        ├── subosito
        │   └── gotenv
        │   │   └── LICENSE
        ├── yosida95
        │   └── uritemplate
        │   │   └── v3
        │   │       └── LICENSE
        └── yudai
        │   └── golcs
        │       └── LICENSE
    ├── golang.org
        └── x
        │   ├── exp
        │       └── LICENSE
        │   ├── sys
        │       ├── unix
        │       │   └── LICENSE
        │       └── windows
        │       │   └── LICENSE
        │   ├── text
        │       └── LICENSE
        │   └── time
        │       └── rate
        │           └── LICENSE
    └── gopkg.in
        ├── yaml.v2
            ├── LICENSE
            └── NOTICE
        └── yaml.v3
            ├── LICENSE
            └── NOTICE


/.dockerignore:
--------------------------------------------------------------------------------
 1 | .github
 2 | .vscode
 3 | script
 4 | third-party
 5 | .dockerignore
 6 | .gitignore
 7 | **/*.yml
 8 | **/*.yaml
 9 | **/*.md
10 | **/*_test.go
11 | LICENSE
12 | 


--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @github/github-mcp-server
2 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: "\U0001F41B Bug report"
 3 | about: Report a bug or unexpected behavior while using GitHub MCP Server
 4 | title: ''
 5 | labels: bug
 6 | assignees: ''
 7 | 
 8 | ---
 9 | 
10 | ### Describe the bug
11 | 
12 | A clear and concise description of what the bug is.
13 | 
14 | ### Affected version
15 | 
16 | Please run ` docker run -i --rm ghcr.io/github/github-mcp-server ./github-mcp-server --version` and paste the output below
17 | 
18 | ### Steps to reproduce the behavior
19 | 
20 | 1. Type this '...'
21 | 2. View the output '....'
22 | 3. See error
23 | 
24 | ### Expected vs actual behavior
25 | 
26 | A clear and concise description of what you expected to happen and what actually happened.
27 | 
28 | ### Logs
29 | 
30 | Paste any available logs. Redact if needed.
31 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | name: "⭐ Submit a feature request"
 3 | about: Surface a feature or problem that you think should be solved
 4 | title: ''
 5 | labels: enhancement
 6 | assignees: ''
 7 | 
 8 | ---
 9 | 
10 | ### Describe the feature or problem you’d like to solve
11 | 
12 | A clear and concise description of what the feature or problem is.
13 | 
14 | ### Proposed solution
15 | 
16 | How will it benefit GitHub MCP Server and its users?
17 | 
18 | ### Example prompts or workflows (for tools/toolsets only)
19 | 
20 | If it's a new tool or improvement, share 3–5 example prompts or workflows it would enable. Just enough detail to show the value. Clear, valuable use cases are more likely to get approved.
21 | 
22 | ### Additional context
23 | 
24 | Add any other context like screenshots or mockups are helpful, if applicable.
25 | 


--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
 1 | # To get started with Dependabot version updates, you'll need to specify which
 2 | # package ecosystems to update and where the package manifests are located.
 3 | # Please see the documentation for all configuration options:
 4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
 5 | 
 6 | version: 2
 7 | updates:
 8 |   - package-ecosystem: "gomod"
 9 |     directory: "/"
10 |     schedule:
11 |       interval: "weekly"
12 |   - package-ecosystem: "docker"
13 |     directory: "/"
14 |     schedule:
15 |       interval: "weekly"
16 | 


--------------------------------------------------------------------------------
/.github/licenses.tmpl:
--------------------------------------------------------------------------------
 1 | # GitHub MCP Server dependencies
 2 | 
 3 | The following open source dependencies are used to build the [github/github-mcp-server][] GitHub Model Context Protocol Server.
 4 | 
 5 | ## Go Packages
 6 | 
 7 | Some packages may only be included on certain architectures or operating systems.
 8 | 
 9 | {{ range . }}
10 |  - [{{.Name}}](https://pkg.go.dev/{{.Name}}) ([{{.LicenseName}}]({{.LicenseURL}}))
11 | {{- end }}
12 | 
13 | [github/github-mcp-server]: https://github.com/github/github-mcp-server
14 | 


--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
 1 | <!--
 2 |     Thank you for contributing to GitHub MCP Server!
 3 |     Please reference an existing issue: `Closes #NUMBER`
 4 | 
 5 |     Screenshots or videos of changed behavior is incredibly helpful and always appreciated.
 6 |     Consider addressing the following:
 7 |     - Tradeoffs: List tradeoffs you made to take on or pay down tech debt.
 8 |     - Alternatives: Describe alternative approaches you considered and why you discarded them.
 9 | -->
10 | 
11 | Closes:
12 | 


--------------------------------------------------------------------------------
/.github/workflows/code-scanning.yml:
--------------------------------------------------------------------------------
 1 | name: "CodeQL"
 2 | run-name: ${{ github.event.inputs.code_scanning_run_name }}
 3 | on: [push, pull_request, workflow_dispatch]
 4 | 
 5 | concurrency:
 6 |   group: ${{ github.workflow }}-${{ github.ref }}
 7 |   cancel-in-progress: true
 8 | 
 9 | env:
10 |   CODE_SCANNING_REF: ${{ github.event.inputs.code_scanning_ref }}
11 |   CODE_SCANNING_BASE_BRANCH: ${{ github.event.inputs.code_scanning_base_branch }}
12 |   CODE_SCANNING_IS_ANALYZING_DEFAULT_BRANCH: ${{ github.event.inputs.code_scanning_is_analyzing_default_branch }}
13 | 
14 | jobs:
15 |   analyze:
16 |     name: Analyze (${{ matrix.language }})
17 |     runs-on: ${{ fromJSON(matrix.runner) }}
18 |     permissions:
19 |       actions: read
20 |       contents: read
21 |       packages: read
22 |       security-events: write
23 |     continue-on-error: false
24 |     strategy:
25 |       fail-fast: false
26 |       matrix:
27 |         include:
28 |           - language: actions
29 |             category: /language:actions
30 |             build-mode: none
31 |             runner: '["ubuntu-22.04"]'
32 |           - language: go
33 |             category: /language:go
34 |             build-mode: autobuild
35 |             runner: '["ubuntu-22.04"]'
36 |     steps:
37 |       - name: Checkout repository
38 |         uses: actions/checkout@v4
39 | 
40 |       - name: Initialize CodeQL
41 |         uses: github/codeql-action/init@v3
42 |         with:
43 |           languages: ${{ matrix.language }}
44 |           build-mode: ${{ matrix.build-mode }}
45 |           dependency-caching: ${{ runner.environment == 'github-hosted' }}
46 |           queries: "" # Default query suite
47 |           packs: github/ccr-${{ matrix.language }}-queries
48 |           config: |
49 |             default-setup:
50 |               org:
51 |                 model-packs: [ ${{ github.event.inputs.code_scanning_codeql_packs }} ]
52 |             threat-models: [  ]
53 |       - name: Setup proxy for registries
54 |         id: proxy
55 |         uses: github/codeql-action/start-proxy@v3
56 |         with:
57 |           registries_credentials: ${{ secrets.GITHUB_REGISTRIES_PROXY }}
58 |           language: ${{ matrix.language }}
59 | 
60 |       - name: Configure
61 |         uses: github/codeql-action/resolve-environment@v3
62 |         id: resolve-environment
63 |         with:
64 |           language: ${{ matrix.language }}
65 |       - name: Setup Go
66 |         uses: actions/setup-go@v5
67 |         if: matrix.language == 'go' && fromJSON(steps.resolve-environment.outputs.environment).configuration.go.version
68 |         with:
69 |           go-version: ${{ fromJSON(steps.resolve-environment.outputs.environment).configuration.go.version }}
70 |           cache: false
71 | 
72 |       - name: Autobuild
73 |         uses: github/codeql-action/autobuild@v3
74 | 
75 |       - name: Perform CodeQL Analysis
76 |         uses: github/codeql-action/analyze@v3
77 |         env:
78 |           CODEQL_PROXY_HOST: ${{ steps.proxy.outputs.proxy_host }}
79 |           CODEQL_PROXY_PORT: ${{ steps.proxy.outputs.proxy_port }}
80 |           CODEQL_PROXY_CA_CERTIFICATE: ${{ steps.proxy.outputs.proxy_ca_certificate }}
81 |         with:
82 |           category: ${{ matrix.category }}
83 | 


--------------------------------------------------------------------------------
/.github/workflows/docker-publish.yml:
--------------------------------------------------------------------------------
  1 | name: Docker
  2 | 
  3 | # This workflow uses actions that are not certified by GitHub.
  4 | # They are provided by a third-party and are governed by
  5 | # separate terms of service, privacy policy, and support
  6 | # documentation.
  7 | 
  8 | on:
  9 |   schedule:
 10 |     - cron: "27 0 * * *"
 11 |   push:
 12 |     branches: ["main", "next"]
 13 |     # Publish semver tags as releases.
 14 |     tags: ["v*.*.*"]
 15 |   pull_request:
 16 |     branches: ["main", "next"]
 17 | 
 18 | env:
 19 |   # Use docker.io for Docker Hub if empty
 20 |   REGISTRY: ghcr.io
 21 |   # github.repository as <account>/<repo>
 22 |   IMAGE_NAME: ${{ github.repository }}
 23 | 
 24 | jobs:
 25 |   build:
 26 |     runs-on: ubuntu-latest-xl
 27 |     permissions:
 28 |       contents: read
 29 |       packages: write
 30 |       # This is used to complete the identity challenge
 31 |       # with sigstore/fulcio when running outside of PRs.
 32 |       id-token: write
 33 | 
 34 |     steps:
 35 |       - name: Checkout repository
 36 |         uses: actions/checkout@v4
 37 | 
 38 |       # Install the cosign tool except on PR
 39 |       # https://github.com/sigstore/cosign-installer
 40 |       - name: Install cosign
 41 |         if: github.event_name != 'pull_request'
 42 |         uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0
 43 |         with:
 44 |           cosign-release: "v2.2.4"
 45 | 
 46 |       # Set up BuildKit Docker container builder to be able to build
 47 |       # multi-platform images and export cache
 48 |       # https://github.com/docker/setup-buildx-action
 49 |       - name: Set up Docker Buildx
 50 |         uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
 51 | 
 52 |       # Login against a Docker registry except on PR
 53 |       # https://github.com/docker/login-action
 54 |       - name: Log into registry ${{ env.REGISTRY }}
 55 |         if: github.event_name != 'pull_request'
 56 |         uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
 57 |         with:
 58 |           registry: ${{ env.REGISTRY }}
 59 |           username: ${{ github.actor }}
 60 |           password: ${{ secrets.GITHUB_TOKEN }}
 61 | 
 62 |       # Extract metadata (tags, labels) for Docker
 63 |       # https://github.com/docker/metadata-action
 64 |       - name: Extract Docker metadata
 65 |         id: meta
 66 |         uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
 67 |         with:
 68 |           images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
 69 |           tags: |
 70 |             type=schedule
 71 |             type=ref,event=branch
 72 |             type=ref,event=tag
 73 |             type=ref,event=pr
 74 |             type=semver,pattern={{version}}
 75 |             type=semver,pattern={{major}}.{{minor}}
 76 |             type=semver,pattern={{major}}
 77 |             type=sha
 78 |             type=edge
 79 |             # Custom rule to prevent pre-releases from getting latest tag
 80 |             type=raw,value=latest,enable=${{ github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-') }}
 81 | 
 82 |       - name: Go Build Cache for Docker
 83 |         uses: actions/cache@v4
 84 |         with:
 85 |           path: go-build-cache
 86 |           key: ${{ runner.os }}-go-build-cache-${{ hashFiles('**/go.sum') }}
 87 | 
 88 |       - name: Inject go-build-cache
 89 |         uses: reproducible-containers/buildkit-cache-dance@4b2444fec0c0fb9dbf175a96c094720a692ef810 # v2.1.4
 90 |         with:
 91 |           cache-source: go-build-cache
 92 | 
 93 |       # Build and push Docker image with Buildx (don't push on PR)
 94 |       # https://github.com/docker/build-push-action
 95 |       - name: Build and push Docker image
 96 |         id: build-and-push
 97 |         uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
 98 |         with:
 99 |           context: .
100 |           push: ${{ github.event_name != 'pull_request' }}
101 |           tags: ${{ steps.meta.outputs.tags }}
102 |           labels: ${{ steps.meta.outputs.labels }}
103 |           cache-from: type=gha
104 |           cache-to: type=gha,mode=max
105 |           platforms: linux/amd64,linux/arm64
106 |           build-args: |
107 |             VERSION=${{ github.ref_name }}
108 | 
109 |       # Sign the resulting Docker image digest except on PRs.
110 |       # This will only write to the public Rekor transparency log when the Docker
111 |       # repository is public to avoid leaking data.  If you would like to publish
112 |       # transparency data even for private images, pass --force to cosign below.
113 |       # https://github.com/sigstore/cosign
114 |       - name: Sign the published Docker image
115 |         if: ${{ github.event_name != 'pull_request' }}
116 |         env:
117 |           # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
118 |           TAGS: ${{ steps.meta.outputs.tags }}
119 |           DIGEST: ${{ steps.build-and-push.outputs.digest }}
120 |         # This step uses the identity token to provision an ephemeral certificate
121 |         # against the sigstore community Fulcio instance.
122 |         run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
123 | 


--------------------------------------------------------------------------------
/.github/workflows/docs-check.yml:
--------------------------------------------------------------------------------
 1 | name: Documentation Check
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [ main ]
 6 |   pull_request:
 7 |     branches: [ main ]
 8 | 
 9 | permissions:
10 |   contents: read
11 | 
12 | jobs:
13 |   docs-check:
14 |     runs-on: ubuntu-latest
15 |     steps:
16 |     - name: Checkout code
17 |       uses: actions/checkout@v4
18 | 
19 |     - name: Set up Go
20 |       uses: actions/setup-go@v5
21 |       with:
22 |         go-version-file: 'go.mod'
23 | 
24 |     - name: Build docs generator
25 |       run: go build -o github-mcp-server ./cmd/github-mcp-server
26 | 
27 |     - name: Generate documentation
28 |       run: ./github-mcp-server generate-docs
29 | 
30 |     - name: Check for documentation changes
31 |       run: |
32 |         if ! git diff --exit-code README.md; then
33 |           echo "❌ Documentation is out of date!"
34 |           echo ""
35 |           echo "The generated documentation differs from what's committed."
36 |           echo "Please run the following command to update the documentation:"
37 |           echo ""
38 |           echo "  go run ./cmd/github-mcp-server generate-docs"
39 |           echo ""
40 |           echo "Then commit the changes."
41 |           echo ""
42 |           echo "Changes detected:"
43 |           git diff README.md
44 |           exit 1
45 |         else
46 |           echo "✅ Documentation is up to date!"
47 |         fi
48 | 


--------------------------------------------------------------------------------
/.github/workflows/go.yml:
--------------------------------------------------------------------------------
 1 | name: Build and Test Go Project
 2 | on: [push, pull_request]
 3 | 
 4 | permissions:
 5 |   contents: read
 6 | 
 7 | jobs:
 8 |   build:
 9 |     strategy:
10 |       fail-fast: false
11 |       matrix:
12 |         os: [ubuntu-latest, windows-latest, macos-latest]
13 | 
14 |     runs-on: ${{ matrix.os }}
15 | 
16 |     steps:
17 |       - name: Check out code
18 |         uses: actions/checkout@v4
19 | 
20 |       - name: Set up Go
21 |         uses: actions/setup-go@v5
22 |         with:
23 |           go-version-file: "go.mod"
24 | 
25 |       - name: Download dependencies
26 |         run: go mod download
27 | 
28 |       - name: Run unit tests
29 |         run: script/test
30 | 
31 |       - name: Build
32 |         run: go build -v ./cmd/github-mcp-server
33 | 


--------------------------------------------------------------------------------
/.github/workflows/goreleaser.yml:
--------------------------------------------------------------------------------
 1 | name: GoReleaser Release
 2 | on:
 3 |   push:
 4 |     tags:
 5 |       - "v*"
 6 | permissions:
 7 |   contents: write
 8 |   id-token: write
 9 |   attestations: write
10 | 
11 | jobs:
12 |   release:
13 |     runs-on: ubuntu-latest
14 | 
15 |     steps:
16 |       - name: Check out code
17 |         uses: actions/checkout@v4
18 | 
19 |       - name: Set up Go
20 |         uses: actions/setup-go@v5
21 |         with:
22 |           go-version-file: "go.mod"
23 | 
24 |       - name: Download dependencies
25 |         run: go mod download
26 | 
27 |       - name: Run GoReleaser
28 |         uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552
29 |         with:
30 |           distribution: goreleaser
31 |           # GoReleaser version
32 |           version: "~> v2"
33 |           # Arguments to pass to GoReleaser
34 |           args: release --clean
35 |           workdir: .
36 |         env:
37 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 | 
39 |       - name: Generate signed build provenance attestations for workflow artifacts
40 |         uses: actions/attest-build-provenance@v2
41 |         with:
42 |           subject-path: |
43 |             dist/*.tar.gz
44 |             dist/*.zip
45 |             dist/*.txt
46 | 


--------------------------------------------------------------------------------
/.github/workflows/license-check.yml:
--------------------------------------------------------------------------------
 1 | # Create a github action that runs the license check script and fails if it exits with a non-zero status
 2 | 
 3 | name: License Check
 4 | on: [push, pull_request]
 5 | permissions:
 6 |   contents: read
 7 | 
 8 | jobs:
 9 |   license-check:
10 |     runs-on: ubuntu-latest
11 | 
12 |     steps:
13 |       - name: Check out code
14 |         uses: actions/checkout@v4
15 | 
16 |       - name: Set up Go
17 |         uses: actions/setup-go@v5
18 |         with:
19 |           go-version-file: "go.mod"
20 |       - name: check licenses
21 |         run: ./script/licenses-check
22 | 


--------------------------------------------------------------------------------
/.github/workflows/lint.yml:
--------------------------------------------------------------------------------
 1 | name: golangci-lint
 2 | on:
 3 |   push:
 4 |     branches:
 5 |       - main
 6 |   pull_request:
 7 | 
 8 | permissions:
 9 |   contents: read
10 | 
11 | jobs:
12 |   golangci:
13 |     name: lint
14 |     runs-on: ubuntu-latest
15 |     steps:
16 |       - uses: actions/checkout@v4
17 |       - uses: actions/setup-go@v5
18 |         with:
19 |           go-version: stable
20 |       - name: golangci-lint
21 |         uses: golangci/golangci-lint-action@v8
22 |         with:
23 |           version: v2.1
24 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | .idea
 2 | cmd/github-mcp-server/github-mcp-server
 3 | 
 4 | # VSCode
 5 | .vscode/*
 6 | !.vscode/launch.json
 7 | 
 8 | # Added by goreleaser init:
 9 | dist/
10 | __debug_bin*
11 | 
12 | # Go
13 | vendor
14 | bin/
15 | 
16 | # macOS
17 | .DS_Store


--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
 1 | version: "2"
 2 | run:
 3 |   concurrency: 4
 4 |   tests: true
 5 | linters:
 6 |   enable:
 7 |     - bodyclose
 8 |     - gocritic
 9 |     - gosec
10 |     - makezero
11 |     - misspell
12 |     - nakedret
13 |     - revive
14 |   exclusions:
15 |     generated: lax
16 |     presets:
17 |       - comments
18 |       - common-false-positives
19 |       - legacy
20 |       - std-error-handling
21 |     paths:
22 |       - third_party$
23 |       - builtin$
24 |       - examples$
25 |   settings:
26 |     staticcheck:
27 |       checks:
28 |         - "all"
29 |         - -QF1008
30 |         - -ST1000
31 | formatters:
32 |   exclusions:
33 |     generated: lax
34 |     paths:
35 |       - third_party$
36 |       - builtin$
37 |       - examples$
38 | 


--------------------------------------------------------------------------------
/.goreleaser.yaml:
--------------------------------------------------------------------------------
 1 | version: 2
 2 | project_name: github-mcp-server
 3 | before:
 4 |   hooks:
 5 |     - go mod tidy
 6 |     - go generate ./...
 7 | 
 8 | builds:
 9 |   - env:
10 |       - CGO_ENABLED=0
11 |     ldflags:
12 |       - -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
13 |     goos:
14 |       - linux
15 |       - windows
16 |       - darwin
17 |     main: ./cmd/github-mcp-server
18 | 
19 | archives:
20 |   - formats: tar.gz
21 |     # this name template makes the OS and Arch compatible with the results of `uname`.
22 |     name_template: >-
23 |       {{ .ProjectName }}_
24 |       {{- title .Os }}_
25 |       {{- if eq .Arch "amd64" }}x86_64
26 |       {{- else if eq .Arch "386" }}i386
27 |       {{- else }}{{ .Arch }}{{ end }}
28 |       {{- if .Arm }}v{{ .Arm }}{{ end }}
29 |     # use zip for windows archives
30 |     format_overrides:
31 |       - goos: windows
32 |         formats: zip
33 | 
34 | changelog:
35 |   sort: asc
36 |   filters:
37 |     exclude:
38 |       - "^docs:"
39 |       - "^test:"
40 | 
41 | release:
42 |   draft: true
43 |   prerelease: auto
44 |   name_template: "GitHub MCP Server {{.Version}}"
45 | 


--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
 1 | {
 2 |     // Use IntelliSense to learn about possible attributes.
 3 |     // Hover to view descriptions of existing attributes.
 4 |     // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
 5 |     "version": "0.2.0",
 6 |     "configurations": [
 7 |         {
 8 |             "name": "Launch stdio server",
 9 |             "type": "go",
10 |             "request": "launch",
11 |             "mode": "auto",
12 |             "cwd": "${workspaceFolder}",
13 |             "program": "cmd/github-mcp-server/main.go",
14 |             "args": ["stdio"],
15 |             "console": "integratedTerminal",
16 |         },
17 |         {
18 |             "name": "Launch stdio server (read-only)",
19 |             "type": "go",
20 |             "request": "launch",
21 |             "mode": "auto",
22 |             "cwd": "${workspaceFolder}",
23 |             "program": "cmd/github-mcp-server/main.go",
24 |             "args": ["stdio", "--read-only"],
25 |             "console": "integratedTerminal",
26 |         }
27 |     ]
28 | }


--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
 1 | ## Contributing
 2 | 
 3 | [fork]: https://github.com/github/github-mcp-server/fork
 4 | [pr]: https://github.com/github/github-mcp-server/compare
 5 | [style]: https://github.com/github/github-mcp-server/blob/main/.golangci.yml
 6 | 
 7 | Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
 8 | 
 9 | Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE).
10 | 
11 | Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms.
12 | 
13 | ## Prerequisites for running and testing code
14 | 
15 | These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process.
16 | 
17 | 1. Install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go)
18 | 2. [Install golangci-lint v2](https://golangci-lint.run/welcome/install/#local-installation)
19 | 
20 | ## Submitting a pull request
21 | 
22 | 1. [Fork][fork] and clone the repository
23 | 2. Make sure the tests pass on your machine: `go test -v ./...`
24 | 3. Make sure linter passes on your machine: `golangci-lint run`
25 | 4. Create a new branch: `git checkout -b my-branch-name`
26 | 5. Add your changes and tests, and make sure the Action workflows still pass
27 |     - Run linter: `script/lint`
28 |     - Update snapshots and run tests: `UPDATE_TOOLSNAPS=true go test ./...`
29 |     - Update readme documentation: `script/generate-docs`
30 | 6. Push to your fork and [submit a pull request][pr] targeting the `main` branch
31 | 7. Pat yourself on the back and wait for your pull request to be reviewed and merged.
32 | 
33 | Here are a few things you can do that will increase the likelihood of your pull request being accepted:
34 | 
35 | - Follow the [style guide][style].
36 | - Write tests.
37 | - Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
38 | - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
39 | 
40 | ## Resources
41 | 
42 | - [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
43 | - [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
44 | - [GitHub Help](https://help.github.com)
45 | 


--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
 1 | FROM golang:1.24.4-alpine AS build
 2 | ARG VERSION="dev"
 3 | 
 4 | # Set the working directory
 5 | WORKDIR /build
 6 | 
 7 | # Install git
 8 | RUN --mount=type=cache,target=/var/cache/apk \
 9 |     apk add git
10 | 
11 | # Build the server
12 | # go build automatically download required module dependencies to /go/pkg/mod
13 | RUN --mount=type=cache,target=/go/pkg/mod \
14 |     --mount=type=cache,target=/root/.cache/go-build \
15 |     --mount=type=bind,target=. \
16 |     CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION} -X main.commit=$(git rev-parse HEAD) -X main.date=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
17 |     -o /bin/github-mcp-server cmd/github-mcp-server/main.go
18 | 
19 | # Make a stage to run the app
20 | FROM gcr.io/distroless/base-debian12
21 | # Set the working directory
22 | WORKDIR /server
23 | # Copy the binary from the build stage
24 | COPY --from=build /bin/github-mcp-server .
25 | # Set the entrypoint to the server binary
26 | ENTRYPOINT ["/server/github-mcp-server"]
27 | # Default arguments for ENTRYPOINT
28 | CMD ["stdio"]
29 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2025 GitHub
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
 1 | Thanks for helping make GitHub safe for everyone.
 2 | 
 3 | # Security
 4 | 
 5 | GitHub takes the security of our software products and services seriously, including all of the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub).
 6 | 
 7 | Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation. 
 8 | 
 9 | ## Reporting Security Issues
10 | 
11 | If you believe you have found a security vulnerability in any GitHub-owned repository, please report it to us through coordinated disclosure.
12 | 
13 | **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.**
14 | 
15 | Instead, please send an email to opensource-security[@]github.com.
16 | 
17 | Please include as much of the information listed below as you can to help us better understand and resolve the issue:
18 | 
19 |   * The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting)
20 |   * Full paths of source file(s) related to the manifestation of the issue
21 |   * The location of the affected source code (tag/branch/commit or direct URL)
22 |   * Any special configuration required to reproduce the issue
23 |   * Step-by-step instructions to reproduce the issue
24 |   * Proof-of-concept or exploit code (if possible)
25 |   * Impact of the issue, including how an attacker might exploit the issue
26 | 
27 | This information will help us triage your report more quickly.
28 | 
29 | ## Policy
30 | 
31 | See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms)
32 | 


--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
 1 | # Support
 2 | 
 3 | ## How to file issues and get help
 4 | 
 5 | This project uses GitHub issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue.
 6 | 
 7 | For help or questions about using this project, please open an issue.
 8 | 
 9 | - The `github-mcp-server` is under active development and maintained by GitHub staff **AND THE COMMUNITY**. We will do our best to respond to support, feature requests, and community questions in a timely manner.
10 | 
11 | ## GitHub Support Policy
12 | 
13 | Support for this project is limited to the resources listed above.
14 | 


--------------------------------------------------------------------------------
/cmd/github-mcp-server/main.go:
--------------------------------------------------------------------------------
  1 | package main
  2 | 
  3 | import (
  4 | 	"errors"
  5 | 	"fmt"
  6 | 	"os"
  7 | 	"strings"
  8 | 
  9 | 	"github.com/github/github-mcp-server/internal/ghmcp"
 10 | 	"github.com/github/github-mcp-server/pkg/github"
 11 | 	"github.com/spf13/cobra"
 12 | 	"github.com/spf13/pflag"
 13 | 	"github.com/spf13/viper"
 14 | )
 15 | 
 16 | // These variables are set by the build process using ldflags.
 17 | var version = "version"
 18 | var commit = "commit"
 19 | var date = "date"
 20 | 
 21 | var (
 22 | 	rootCmd = &cobra.Command{
 23 | 		Use:     "server",
 24 | 		Short:   "GitHub MCP Server",
 25 | 		Long:    `A GitHub MCP server that handles various tools and resources.`,
 26 | 		Version: fmt.Sprintf("Version: %s\nCommit: %s\nBuild Date: %s", version, commit, date),
 27 | 	}
 28 | 
 29 | 	stdioCmd = &cobra.Command{
 30 | 		Use:   "stdio",
 31 | 		Short: "Start stdio server",
 32 | 		Long:  `Start a server that communicates via standard input/output streams using JSON-RPC messages.`,
 33 | 		RunE: func(_ *cobra.Command, _ []string) error {
 34 | 			token := viper.GetString("personal_access_token")
 35 | 			if token == "" {
 36 | 				return errors.New("GITHUB_PERSONAL_ACCESS_TOKEN not set")
 37 | 			}
 38 | 
 39 | 			// If you're wondering why we're not using viper.GetStringSlice("toolsets"),
 40 | 			// it's because viper doesn't handle comma-separated values correctly for env
 41 | 			// vars when using GetStringSlice.
 42 | 			// https://github.com/spf13/viper/issues/380
 43 | 			var enabledToolsets []string
 44 | 			if err := viper.UnmarshalKey("toolsets", &enabledToolsets); err != nil {
 45 | 				return fmt.Errorf("failed to unmarshal toolsets: %w", err)
 46 | 			}
 47 | 
 48 | 			stdioServerConfig := ghmcp.StdioServerConfig{
 49 | 				Version:              version,
 50 | 				Host:                 viper.GetString("host"),
 51 | 				Token:                token,
 52 | 				EnabledToolsets:      enabledToolsets,
 53 | 				DynamicToolsets:      viper.GetBool("dynamic_toolsets"),
 54 | 				ReadOnly:             viper.GetBool("read-only"),
 55 | 				ExportTranslations:   viper.GetBool("export-translations"),
 56 | 				EnableCommandLogging: viper.GetBool("enable-command-logging"),
 57 | 				LogFilePath:          viper.GetString("log-file"),
 58 | 			}
 59 | 			return ghmcp.RunStdioServer(stdioServerConfig)
 60 | 		},
 61 | 	}
 62 | )
 63 | 
 64 | func init() {
 65 | 	cobra.OnInitialize(initConfig)
 66 | 	rootCmd.SetGlobalNormalizationFunc(wordSepNormalizeFunc)
 67 | 
 68 | 	rootCmd.SetVersionTemplate("{{.Short}}\n{{.Version}}\n")
 69 | 
 70 | 	// Add global flags that will be shared by all commands
 71 | 	rootCmd.PersistentFlags().StringSlice("toolsets", github.DefaultTools, "An optional comma separated list of groups of tools to allow, defaults to enabling all")
 72 | 	rootCmd.PersistentFlags().Bool("dynamic-toolsets", false, "Enable dynamic toolsets")
 73 | 	rootCmd.PersistentFlags().Bool("read-only", false, "Restrict the server to read-only operations")
 74 | 	rootCmd.PersistentFlags().String("log-file", "", "Path to log file")
 75 | 	rootCmd.PersistentFlags().Bool("enable-command-logging", false, "When enabled, the server will log all command requests and responses to the log file")
 76 | 	rootCmd.PersistentFlags().Bool("export-translations", false, "Save translations to a JSON file")
 77 | 	rootCmd.PersistentFlags().String("gh-host", "", "Specify the GitHub hostname (for GitHub Enterprise etc.)")
 78 | 
 79 | 	// Bind flag to viper
 80 | 	_ = viper.BindPFlag("toolsets", rootCmd.PersistentFlags().Lookup("toolsets"))
 81 | 	_ = viper.BindPFlag("dynamic_toolsets", rootCmd.PersistentFlags().Lookup("dynamic-toolsets"))
 82 | 	_ = viper.BindPFlag("read-only", rootCmd.PersistentFlags().Lookup("read-only"))
 83 | 	_ = viper.BindPFlag("log-file", rootCmd.PersistentFlags().Lookup("log-file"))
 84 | 	_ = viper.BindPFlag("enable-command-logging", rootCmd.PersistentFlags().Lookup("enable-command-logging"))
 85 | 	_ = viper.BindPFlag("export-translations", rootCmd.PersistentFlags().Lookup("export-translations"))
 86 | 	_ = viper.BindPFlag("host", rootCmd.PersistentFlags().Lookup("gh-host"))
 87 | 
 88 | 	// Add subcommands
 89 | 	rootCmd.AddCommand(stdioCmd)
 90 | }
 91 | 
 92 | func initConfig() {
 93 | 	// Initialize Viper configuration
 94 | 	viper.SetEnvPrefix("github")
 95 | 	viper.AutomaticEnv()
 96 | 
 97 | }
 98 | 
 99 | func main() {
100 | 	if err := rootCmd.Execute(); err != nil {
101 | 		fmt.Fprintf(os.Stderr, "%v\n", err)
102 | 		os.Exit(1)
103 | 	}
104 | }
105 | 
106 | func wordSepNormalizeFunc(_ *pflag.FlagSet, name string) pflag.NormalizedName {
107 | 	from := []string{"_"}
108 | 	to := "-"
109 | 	for _, sep := range from {
110 | 		name = strings.ReplaceAll(name, sep, to)
111 | 	}
112 | 	return pflag.NormalizedName(name)
113 | }
114 | 


--------------------------------------------------------------------------------
/docs/error-handling.md:
--------------------------------------------------------------------------------
  1 | # Error Handling
  2 | 
  3 | This document describes the error handling patterns used in the GitHub MCP Server, specifically how we handle GitHub API errors and avoid direct use of mcp-go error types.
  4 | 
  5 | ## Overview
  6 | 
  7 | The GitHub MCP Server implements a custom error handling approach that serves two primary purposes:
  8 | 
  9 | 1. **Tool Response Generation**: Return appropriate MCP tool error responses to clients
 10 | 2. **Middleware Inspection**: Store detailed error information in the request context for middleware analysis
 11 | 
 12 | This dual approach enables better observability and debugging capabilities, particularly for remote server deployments where understanding the nature of failures (rate limiting, authentication, 404s, 500s, etc.) is crucial for validation and monitoring.
 13 | 
 14 | ## Error Types
 15 | 
 16 | ### GitHubAPIError
 17 | 
 18 | Used for REST API errors from the GitHub API:
 19 | 
 20 | ```go
 21 | type GitHubAPIError struct {
 22 |     Message  string           `json:"message"`
 23 |     Response *github.Response `json:"-"`
 24 |     Err      error            `json:"-"`
 25 | }
 26 | ```
 27 | 
 28 | ### GitHubGraphQLError
 29 | 
 30 | Used for GraphQL API errors from the GitHub API:
 31 | 
 32 | ```go
 33 | type GitHubGraphQLError struct {
 34 |     Message string `json:"message"`
 35 |     Err     error  `json:"-"`
 36 | }
 37 | ```
 38 | 
 39 | ## Usage Patterns
 40 | 
 41 | ### For GitHub REST API Errors
 42 | 
 43 | Instead of directly returning `mcp.NewToolResultError()`, use:
 44 | 
 45 | ```go
 46 | return ghErrors.NewGitHubAPIErrorResponse(ctx, message, response, err), nil
 47 | ```
 48 | 
 49 | This function:
 50 | - Creates a `GitHubAPIError` with the provided message, response, and error
 51 | - Stores the error in the context for middleware inspection
 52 | - Returns an appropriate MCP tool error response
 53 | 
 54 | ### For GitHub GraphQL API Errors
 55 | 
 56 | ```go
 57 | return ghErrors.NewGitHubGraphQLErrorResponse(ctx, message, err), nil
 58 | ```
 59 | 
 60 | ### Context Management
 61 | 
 62 | The error handling system uses context to store errors for later inspection:
 63 | 
 64 | ```go
 65 | // Initialize context with error tracking
 66 | ctx = errors.ContextWithGitHubErrors(ctx)
 67 | 
 68 | // Retrieve errors for inspection (typically in middleware)
 69 | apiErrors, err := errors.GetGitHubAPIErrors(ctx)
 70 | graphqlErrors, err := errors.GetGitHubGraphQLErrors(ctx)
 71 | ```
 72 | 
 73 | ## Design Principles
 74 | 
 75 | ### User-Actionable vs. Developer Errors
 76 | 
 77 | - **User-actionable errors** (authentication failures, rate limits, 404s) should be returned as failed tool calls using the error response functions
 78 | - **Developer errors** (JSON marshaling failures, internal logic errors) should be returned as actual Go errors that bubble up through the MCP framework
 79 | 
 80 | ### Context Limitations
 81 | 
 82 | This approach was designed to work around current limitations in mcp-go where context is not propagated through each step of request processing. By storing errors in context values, middleware can inspect them without requiring context propagation.
 83 | 
 84 | ### Graceful Error Handling
 85 | 
 86 | Error storage operations in context are designed to fail gracefully - if context storage fails, the tool will still return an appropriate error response to the client.
 87 | 
 88 | ## Benefits
 89 | 
 90 | 1. **Observability**: Middleware can inspect the specific types of GitHub API errors occurring
 91 | 2. **Debugging**: Detailed error information is preserved without exposing potentially sensitive data in logs
 92 | 3. **Validation**: Remote servers can use error types and HTTP status codes to validate that changes don't break functionality
 93 | 4. **Privacy**: Error inspection can be done programmatically using `errors.Is` checks without logging PII
 94 | 
 95 | ## Example Implementation
 96 | 
 97 | ```go
 98 | func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
 99 |     return mcp.NewTool("get_issue", /* ... */),
100 |         func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
101 |             owner, err := RequiredParam[string](request, "owner")
102 |             if err != nil {
103 |                 return mcp.NewToolResultError(err.Error()), nil
104 |             }
105 |             
106 |             client, err := getClient(ctx)
107 |             if err != nil {
108 |                 return nil, fmt.Errorf("failed to get GitHub client: %w", err)
109 |             }
110 |             
111 |             issue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber)
112 |             if err != nil {
113 |                 return ghErrors.NewGitHubAPIErrorResponse(ctx,
114 |                     "failed to get issue",
115 |                     resp,
116 |                     err,
117 |                 ), nil
118 |             }
119 |             
120 |             return MarshalledTextResult(issue), nil
121 |         }
122 | }
123 | ```
124 | 
125 | This approach ensures that both the client receives an appropriate error response and any middleware can inspect the underlying GitHub API error for monitoring and debugging purposes.
126 | 


--------------------------------------------------------------------------------
/docs/testing.md:
--------------------------------------------------------------------------------
 1 | # Testing
 2 | 
 3 | This project uses a combination of unit tests and end-to-end (e2e) tests to ensure correctness and stability.
 4 | 
 5 | ## Unit Testing Patterns
 6 | 
 7 | - Unit tests are located alongside implementation, with filenames ending in `_test.go`.
 8 | - Currently the preference is to use internal tests i.e. test files do not have `_test` package suffix.
 9 | - Tests use [testify](https://github.com/stretchr/testify) for assertions and require statements. Use `require` when continuing the test is not meaningful, for example it is almost never correct to continue after an error expectation.
10 | - Mocking is performed using [go-github-mock](https://github.com/migueleliasweb/go-github-mock) or `githubv4mock` for simulating GitHub rest and GQL API responses.
11 | - Each tool's schema is snapshotted and checked for changes using the `toolsnaps` utility (see below).
12 | - Tests are designed to be explicit and verbose to aid maintainability and clarity.
13 | - Handler unit tests should take the form of:
14 |     1. Test tool snapshot
15 |     1. Very important expectations against the schema (e.g. `ReadOnly` annotation)
16 |     1. Behavioural tests in table-driven form
17 | 
18 | ## End-to-End (e2e) Tests
19 | 
20 | - E2E tests are located in the [`e2e/`](../e2e/) directory. See the [e2e/README.md](../e2e/README.md) for full details on running and debugging these tests.
21 | 
22 | ## toolsnaps: Tool Schema Snapshots
23 | 
24 | - The `toolsnaps` utility ensures that the JSON schema for each tool does not change unexpectedly.
25 | - Snapshots are stored in `__toolsnaps__/*.snap` files , where `*` represents the name of the tool
26 | - When running tests, the current tool schema is compared to the snapshot. If there is a difference, the test will fail and show a diff.
27 | - If you intentionally change a tool's schema, update the snapshots by running tests with the environment variable: `UPDATE_TOOLSNAPS=true go test ./...`
28 | - In CI (when `GITHUB_ACTIONS=true`), missing snapshots will cause a test failure to ensure snapshots are always
29 | committed.
30 | 
31 | ## Notes
32 | 
33 | - Some tools that mutate global state (e.g., marking all notifications as read) are tested primarily with unit tests, not e2e, to avoid side effects.
34 | - For more on the limitations and philosophy of the e2e suite, see the [e2e/README.md](../e2e/README.md).
35 | 


--------------------------------------------------------------------------------
/e2e/README.md:
--------------------------------------------------------------------------------
 1 | # End To End (e2e) Tests
 2 | 
 3 | The purpose of the E2E tests is to have a simple (currently) test that gives maintainers some confidence in the black box behavior of our artifacts. It does this by:
 4 |  * Building the `github-mcp-server` docker image
 5 |  * Running the image
 6 |  * Interacting with the server via stdio
 7 |  * Issuing requests that interact with the live GitHub API
 8 | 
 9 | ## Running the Tests
10 | 
11 | A service must be running that supports image building and container creation via the `docker` CLI.
12 | 
13 | Since these tests require a token to interact with real resources on the GitHub API, it is gated behind the `e2e` build flag.
14 | 
15 | ```
16 | GITHUB_MCP_SERVER_E2E_TOKEN=<YOUR TOKEN> go test -v --tags e2e ./e2e
17 | ```
18 | 
19 | The `GITHUB_MCP_SERVER_E2E_TOKEN` environment variable is mapped to `GITHUB_PERSONAL_ACCESS_TOKEN` internally, but separated to avoid accidental reuse of credentials.
20 | 
21 | ## Example
22 | 
23 | The following diff adjusts the `get_me` tool to return `foobar` as the user login.
24 | 
25 | ```diff
26 | diff --git a/pkg/github/context_tools.go b/pkg/github/context_tools.go
27 | index 1c91d70..ac4ef2b 100644
28 | --- a/pkg/github/context_tools.go
29 | +++ b/pkg/github/context_tools.go
30 | @@ -39,6 +39,8 @@ func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mc
31 |                                 return mcp.NewToolResultError(fmt.Sprintf("failed to get user: %s", string(body))), nil
32 |                         }
33 | 
34 | +                       user.Login = sPtr("foobar")
35 | +
36 |                         r, err := json.Marshal(user)
37 |                         if err != nil {
38 |                                 return nil, fmt.Errorf("failed to marshal user: %w", err)
39 | @@ -47,3 +49,7 @@ func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mc
40 |                         return mcp.NewToolResultText(string(r)), nil
41 |                 }
42 |  }
43 | +
44 | +func sPtr(s string) *string {
45 | +       return &s
46 | +}
47 | ```
48 | 
49 | Running the tests:
50 | 
51 | ```
52 | ➜ GITHUB_MCP_SERVER_E2E_TOKEN=$(gh auth token) go test -v --tags e2e ./e2e
53 | === RUN   TestE2E
54 |     e2e_test.go:92: Building Docker image for e2e tests...
55 |     e2e_test.go:36: Starting Stdio MCP client...
56 | === RUN   TestE2E/Initialize
57 | === RUN   TestE2E/CallTool_get_me
58 |     e2e_test.go:85:
59 |                 Error Trace:    /Users/williammartin/workspace/github-mcp-server/e2e/e2e_test.go:85
60 |                 Error:          Not equal:
61 |                                 expected: "foobar"
62 |                                 actual  : "williammartin"
63 | 
64 |                                 Diff:
65 |                                 --- Expected
66 |                                 +++ Actual
67 |                                 @@ -1 +1 @@
68 |                                 -foobar
69 |                                 +williammartin
70 |                 Test:           TestE2E/CallTool_get_me
71 |                 Messages:       expected login to match
72 | --- FAIL: TestE2E (1.05s)
73 |     --- PASS: TestE2E/Initialize (0.09s)
74 |     --- FAIL: TestE2E/CallTool_get_me (0.46s)
75 | FAIL
76 | FAIL    github.com/github/github-mcp-server/e2e 1.433s
77 | FAIL
78 | ```
79 | 
80 | ## Debugging the Tests
81 | 
82 | It is possible to provide `GITHUB_MCP_SERVER_E2E_DEBUG=true` to run the e2e tests with an in-process version of the MCP server. This has slightly reduced coverage as it doesn't integrate with Docker, or make use of the cobra/viper configuration parsing. However, it allows for placing breakpoints in the MCP Server internals, supporting much better debugging flows than the fully black-box tests.
83 | 
84 | One might argue that the lack of visibility into failures for the black box tests also indicates a product need, but this solves for the immediate pain point felt as a maintainer.
85 | 
86 | ## Limitations
87 | 
88 | The current test suite is intentionally very limited in scope. This is because the maintenance costs on e2e tests tend to increase significantly over time. To read about some challenges with GitHub integration tests, see [go-github integration tests README](https://github.com/google/go-github/blob/5b75aa86dba5cf4af2923afa0938774f37fa0a67/test/README.md). We will expand this suite circumspectly!
89 | 
90 | The tests are quite repetitive and verbose. This is intentional as we want to see them develop more before committing to abstractions.
91 | 
92 | Currently, visibility into failures is not particularly good. We're hoping that we can pull apart the mcp-go client and have it hook into streams representing stdio without requiring an exec. This way we can get breakpoints in the debugger easily.
93 | 
94 | ### Global State Mutation Tests
95 | 
96 | Some tools (such as those that mark all notifications as read) would change the global state for the tester, and are also not idempotent, so they offer little value for end to end tests and instead should rely on unit testing and manual verifications.
97 | 


--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
 1 | module github.com/github/github-mcp-server
 2 | 
 3 | go 1.23.7
 4 | 
 5 | require (
 6 | 	github.com/google/go-github/v73 v73.0.0
 7 | 	github.com/josephburnett/jd v1.9.2
 8 | 	github.com/mark3labs/mcp-go v0.32.0
 9 | 	github.com/migueleliasweb/go-github-mock v1.3.0
10 | 	github.com/sirupsen/logrus v1.9.3
11 | 	github.com/spf13/cobra v1.9.1
12 | 	github.com/spf13/viper v1.20.1
13 | 	github.com/stretchr/testify v1.10.0
14 | )
15 | 
16 | require (
17 | 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
18 | 	github.com/go-openapi/swag v0.21.1 // indirect
19 | 	github.com/josharian/intern v1.0.0 // indirect
20 | 	github.com/mailru/easyjson v0.7.7 // indirect
21 | 	github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
22 | 	golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
23 | 	gopkg.in/yaml.v2 v2.4.0 // indirect
24 | )
25 | 
26 | require (
27 | 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
28 | 	github.com/fsnotify/fsnotify v1.8.0 // indirect
29 | 	github.com/go-viper/mapstructure/v2 v2.3.0
30 | 	github.com/google/go-github/v71 v71.0.0 // indirect
31 | 	github.com/google/go-querystring v1.1.0 // indirect
32 | 	github.com/google/uuid v1.6.0 // indirect
33 | 	github.com/gorilla/mux v1.8.0 // indirect
34 | 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
35 | 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
36 | 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
37 | 	github.com/rogpeppe/go-internal v1.13.1 // indirect
38 | 	github.com/sagikazarmark/locafero v0.9.0 // indirect
39 | 	github.com/shurcooL/githubv4 v0.0.0-20240727222349-48295856cce7
40 | 	github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
41 | 	github.com/sourcegraph/conc v0.3.0 // indirect
42 | 	github.com/spf13/afero v1.14.0 // indirect
43 | 	github.com/spf13/cast v1.7.1 // indirect
44 | 	github.com/spf13/pflag v1.0.6
45 | 	github.com/subosito/gotenv v1.6.0 // indirect
46 | 	github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
47 | 	go.uber.org/multierr v1.11.0 // indirect
48 | 	golang.org/x/oauth2 v0.29.0 // indirect
49 | 	golang.org/x/sys v0.31.0 // indirect
50 | 	golang.org/x/text v0.23.0 // indirect
51 | 	golang.org/x/time v0.5.0 // indirect
52 | 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
53 | 	gopkg.in/yaml.v3 v3.0.1 // indirect
54 | )
55 | 


--------------------------------------------------------------------------------
/internal/githubv4mock/local_round_tripper.go:
--------------------------------------------------------------------------------
 1 | // Ths contents of this file are taken from https://github.com/shurcooL/graphql/blob/ed46e5a4646634fc16cb07c3b8db389542cc8847/graphql_test.go#L155-L165
 2 | // because they are not exported by the module, and we would like to use them in building the githubv4mock test utility.
 3 | //
 4 | // The original license, copied from https://github.com/shurcooL/graphql/blob/ed46e5a4646634fc16cb07c3b8db389542cc8847/LICENSE
 5 | //
 6 | // MIT License
 7 | 
 8 | // Copyright (c) 2017 Dmitri Shuralyov
 9 | 
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | 
17 | // The above copyright notice and this permission notice shall be included in all
18 | // copies or substantial portions of the Software.
19 | 
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | // SOFTWARE.
27 | package githubv4mock
28 | 
29 | import (
30 | 	"net/http"
31 | 	"net/http/httptest"
32 | )
33 | 
34 | // localRoundTripper is an http.RoundTripper that executes HTTP transactions
35 | // by using handler directly, instead of going over an HTTP connection.
36 | type localRoundTripper struct {
37 | 	handler http.Handler
38 | }
39 | 
40 | func (l localRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
41 | 	w := httptest.NewRecorder()
42 | 	l.handler.ServeHTTP(w, req)
43 | 	return w.Result(), nil
44 | }
45 | 


--------------------------------------------------------------------------------
/internal/githubv4mock/objects_are_equal_values.go:
--------------------------------------------------------------------------------
  1 | // The contents of this file are taken from https://github.com/stretchr/testify/blob/016e2e9c269209287f33ec203f340a9a723fe22c/assert/assertions.go#L166
  2 | // because I do not want to take a dependency on the entire testify module just to use this equality check.
  3 | //
  4 | // There is a modification in objectsAreEqual to check that typed nils are equal, even if their types are different.
  5 | //
  6 | // The original license, copied from https://github.com/stretchr/testify/blob/016e2e9c269209287f33ec203f340a9a723fe22c/LICENSE
  7 | //
  8 | // MIT License
  9 | //
 10 | // Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
 11 | 
 12 | // Permission is hereby granted, free of charge, to any person obtaining a copy
 13 | // of this software and associated documentation files (the "Software"), to deal
 14 | // in the Software without restriction, including without limitation the rights
 15 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 16 | // copies of the Software, and to permit persons to whom the Software is
 17 | // furnished to do so, subject to the following conditions:
 18 | 
 19 | // The above copyright notice and this permission notice shall be included in all
 20 | // copies or substantial portions of the Software.
 21 | 
 22 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 23 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 24 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 25 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 26 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 27 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 28 | // SOFTWARE.
 29 | package githubv4mock
 30 | 
 31 | import (
 32 | 	"bytes"
 33 | 	"reflect"
 34 | )
 35 | 
 36 | func objectsAreEqualValues(expected, actual any) bool {
 37 | 	if objectsAreEqual(expected, actual) {
 38 | 		return true
 39 | 	}
 40 | 
 41 | 	expectedValue := reflect.ValueOf(expected)
 42 | 	actualValue := reflect.ValueOf(actual)
 43 | 	if !expectedValue.IsValid() || !actualValue.IsValid() {
 44 | 		return false
 45 | 	}
 46 | 
 47 | 	expectedType := expectedValue.Type()
 48 | 	actualType := actualValue.Type()
 49 | 	if !expectedType.ConvertibleTo(actualType) {
 50 | 		return false
 51 | 	}
 52 | 
 53 | 	if !isNumericType(expectedType) || !isNumericType(actualType) {
 54 | 		// Attempt comparison after type conversion
 55 | 		return reflect.DeepEqual(
 56 | 			expectedValue.Convert(actualType).Interface(), actual,
 57 | 		)
 58 | 	}
 59 | 
 60 | 	// If BOTH values are numeric, there are chances of false positives due
 61 | 	// to overflow or underflow. So, we need to make sure to always convert
 62 | 	// the smaller type to a larger type before comparing.
 63 | 	if expectedType.Size() >= actualType.Size() {
 64 | 		return actualValue.Convert(expectedType).Interface() == expected
 65 | 	}
 66 | 
 67 | 	return expectedValue.Convert(actualType).Interface() == actual
 68 | }
 69 | 
 70 | // objectsAreEqual determines if two objects are considered equal.
 71 | //
 72 | // This function does no assertion of any kind.
 73 | func objectsAreEqual(expected, actual any) bool {
 74 | 	// There is a modification in objectsAreEqual to check that typed nils are equal, even if their types are different.
 75 | 	// This is required because when a nil is provided as a variable, the type is not known.
 76 | 	if isNil(expected) && isNil(actual) {
 77 | 		return true
 78 | 	}
 79 | 
 80 | 	exp, ok := expected.([]byte)
 81 | 	if !ok {
 82 | 		return reflect.DeepEqual(expected, actual)
 83 | 	}
 84 | 
 85 | 	act, ok := actual.([]byte)
 86 | 	if !ok {
 87 | 		return false
 88 | 	}
 89 | 	if exp == nil || act == nil {
 90 | 		return exp == nil && act == nil
 91 | 	}
 92 | 	return bytes.Equal(exp, act)
 93 | }
 94 | 
 95 | // isNumericType returns true if the type is one of:
 96 | // int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64,
 97 | // float32, float64, complex64, complex128
 98 | func isNumericType(t reflect.Type) bool {
 99 | 	return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128
100 | }
101 | 
102 | func isNil(i any) bool {
103 | 	if i == nil {
104 | 		return true
105 | 	}
106 | 	v := reflect.ValueOf(i)
107 | 	switch v.Kind() {
108 | 	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
109 | 		return v.IsNil()
110 | 	default:
111 | 		return false
112 | 	}
113 | }
114 | 


--------------------------------------------------------------------------------
/internal/githubv4mock/objects_are_equal_values_test.go:
--------------------------------------------------------------------------------
 1 | // The contents of this file are taken from https://github.com/stretchr/testify/blob/016e2e9c269209287f33ec203f340a9a723fe22c/assert/assertions_test.go#L140-L174
 2 | //
 3 | // There is a modification to test objectsAreEqualValues to check that typed nils are equal, even if their types are different.
 4 | 
 5 | // The original license, copied from https://github.com/stretchr/testify/blob/016e2e9c269209287f33ec203f340a9a723fe22c/LICENSE
 6 | //
 7 | // MIT License
 8 | //
 9 | // Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors.
10 | 
11 | // Permission is hereby granted, free of charge, to any person obtaining a copy
12 | // of this software and associated documentation files (the "Software"), to deal
13 | // in the Software without restriction, including without limitation the rights
14 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 | // copies of the Software, and to permit persons to whom the Software is
16 | // furnished to do so, subject to the following conditions:
17 | 
18 | // The above copyright notice and this permission notice shall be included in all
19 | // copies or substantial portions of the Software.
20 | 
21 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 | // SOFTWARE.
28 | package githubv4mock
29 | 
30 | import (
31 | 	"fmt"
32 | 	"math"
33 | 	"testing"
34 | 	"time"
35 | )
36 | 
37 | func TestObjectsAreEqualValues(t *testing.T) {
38 | 	now := time.Now()
39 | 
40 | 	cases := []struct {
41 | 		expected interface{}
42 | 		actual   interface{}
43 | 		result   bool
44 | 	}{
45 | 		{uint32(10), int32(10), true},
46 | 		{0, nil, false},
47 | 		{nil, 0, false},
48 | 		{now, now.In(time.Local), false}, // should not be time zone independent
49 | 		{int(270), int8(14), false},      // should handle overflow/underflow
50 | 		{int8(14), int(270), false},
51 | 		{[]int{270, 270}, []int8{14, 14}, false},
52 | 		{complex128(1e+100 + 1e+100i), complex64(complex(math.Inf(0), math.Inf(0))), false},
53 | 		{complex64(complex(math.Inf(0), math.Inf(0))), complex128(1e+100 + 1e+100i), false},
54 | 		{complex128(1e+100 + 1e+100i), 270, false},
55 | 		{270, complex128(1e+100 + 1e+100i), false},
56 | 		{complex128(1e+100 + 1e+100i), 3.14, false},
57 | 		{3.14, complex128(1e+100 + 1e+100i), false},
58 | 		{complex128(1e+10 + 1e+10i), complex64(1e+10 + 1e+10i), true},
59 | 		{complex64(1e+10 + 1e+10i), complex128(1e+10 + 1e+10i), true},
60 | 		{(*string)(nil), nil, true},         // typed nil vs untyped nil
61 | 		{(*string)(nil), (*int)(nil), true}, // different typed nils
62 | 	}
63 | 
64 | 	for _, c := range cases {
65 | 		t.Run(fmt.Sprintf("ObjectsAreEqualValues(%#v, %#v)", c.expected, c.actual), func(t *testing.T) {
66 | 			res := objectsAreEqualValues(c.expected, c.actual)
67 | 
68 | 			if res != c.result {
69 | 				t.Errorf("ObjectsAreEqualValues(%#v, %#v) should return %#v", c.expected, c.actual, c.result)
70 | 			}
71 | 		})
72 | 	}
73 | }
74 | 


--------------------------------------------------------------------------------
/internal/toolsnaps/toolsnaps.go:
--------------------------------------------------------------------------------
 1 | // Package toolsnaps provides test utilities for ensuring json schemas for tools
 2 | // have not changed unexpectedly.
 3 | package toolsnaps
 4 | 
 5 | import (
 6 | 	"encoding/json"
 7 | 	"fmt"
 8 | 	"os"
 9 | 	"path/filepath"
10 | 
11 | 	"github.com/josephburnett/jd/v2"
12 | )
13 | 
14 | // Test checks that the JSON schema for a tool has not changed unexpectedly.
15 | // It compares the marshaled JSON of the provided tool against a stored snapshot file.
16 | // If the UPDATE_TOOLSNAPS environment variable is set to "true", it updates the snapshot file instead.
17 | // If the snapshot does not exist and not running in CI, it creates the snapshot file.
18 | // If the snapshot does not exist and running in CI (GITHUB_ACTIONS="true"), it returns an error.
19 | // If the snapshot exists, it compares the tool's JSON to the snapshot and returns an error if they differ.
20 | // Returns an error if marshaling, reading, or comparing fails.
21 | func Test(toolName string, tool any) error {
22 | 	toolJSON, err := json.MarshalIndent(tool, "", "  ")
23 | 	if err != nil {
24 | 		return fmt.Errorf("failed to marshal tool %s: %w", toolName, err)
25 | 	}
26 | 
27 | 	snapPath := fmt.Sprintf("__toolsnaps__/%s.snap", toolName)
28 | 
29 | 	// If UPDATE_TOOLSNAPS is set, then we write the tool JSON to the snapshot file and exit
30 | 	if os.Getenv("UPDATE_TOOLSNAPS") == "true" {
31 | 		return writeSnap(snapPath, toolJSON)
32 | 	}
33 | 
34 | 	snapJSON, err := os.ReadFile(snapPath) //nolint:gosec // filepaths are controlled by the test suite, so this is safe.
35 | 	// If the snapshot file does not exist, this must be the first time this test is run.
36 | 	// We write the tool JSON to the snapshot file and exit.
37 | 	if os.IsNotExist(err) {
38 | 		// If we're running in CI, we will error if there is not snapshot because it's important that snapshots
39 | 		// are committed alongside the tests, rather than just being constructed and not committed during a CI run.
40 | 		if os.Getenv("GITHUB_ACTIONS") == "true" {
41 | 			return fmt.Errorf("tool snapshot does not exist for %s. Please run the tests with UPDATE_TOOLSNAPS=true to create it", toolName)
42 | 		}
43 | 
44 | 		return writeSnap(snapPath, toolJSON)
45 | 	}
46 | 
47 | 	// Otherwise we will compare the tool JSON to the snapshot JSON
48 | 	toolNode, err := jd.ReadJsonString(string(toolJSON))
49 | 	if err != nil {
50 | 		return fmt.Errorf("failed to parse tool JSON for %s: %w", toolName, err)
51 | 	}
52 | 
53 | 	snapNode, err := jd.ReadJsonString(string(snapJSON))
54 | 	if err != nil {
55 | 		return fmt.Errorf("failed to parse snapshot JSON for %s: %w", toolName, err)
56 | 	}
57 | 
58 | 	// jd.Set allows arrays to be compared without order sensitivity,
59 | 	// which is useful because we don't really care about this when exposing tool schemas.
60 | 	diff := toolNode.Diff(snapNode, jd.SET).Render()
61 | 	if diff != "" {
62 | 		// If there is a difference, we return an error with the diff
63 | 		return fmt.Errorf("tool schema for %s has changed unexpectedly:\n%s\nrun with `UPDATE_TOOLSNAPS=true` if this is expected", toolName, diff)
64 | 	}
65 | 
66 | 	return nil
67 | }
68 | 
69 | func writeSnap(snapPath string, contents []byte) error {
70 | 	// Ensure the directory exists
71 | 	if err := os.MkdirAll(filepath.Dir(snapPath), 0700); err != nil {
72 | 		return fmt.Errorf("failed to create snapshot directory: %w", err)
73 | 	}
74 | 
75 | 	// Write the snapshot file
76 | 	if err := os.WriteFile(snapPath, contents, 0600); err != nil {
77 | 		return fmt.Errorf("failed to write snapshot file: %w", err)
78 | 	}
79 | 
80 | 	return nil
81 | }
82 | 


--------------------------------------------------------------------------------
/internal/toolsnaps/toolsnaps_test.go:
--------------------------------------------------------------------------------
  1 | package toolsnaps
  2 | 
  3 | import (
  4 | 	"encoding/json"
  5 | 	"os"
  6 | 	"path/filepath"
  7 | 	"testing"
  8 | 
  9 | 	"github.com/stretchr/testify/assert"
 10 | 	"github.com/stretchr/testify/require"
 11 | )
 12 | 
 13 | type dummyTool struct {
 14 | 	Name  string `json:"name"`
 15 | 	Value int    `json:"value"`
 16 | }
 17 | 
 18 | // withIsolatedWorkingDir creates a temp dir, changes to it, and restores the original working dir after the test.
 19 | func withIsolatedWorkingDir(t *testing.T) {
 20 | 	dir := t.TempDir()
 21 | 	origDir, err := os.Getwd()
 22 | 	require.NoError(t, err)
 23 | 	t.Cleanup(func() { require.NoError(t, os.Chdir(origDir)) })
 24 | 	require.NoError(t, os.Chdir(dir))
 25 | }
 26 | 
 27 | func TestSnapshotDoesNotExistNotInCI(t *testing.T) {
 28 | 	withIsolatedWorkingDir(t)
 29 | 
 30 | 	// Given we are not running in CI
 31 | 	t.Setenv("GITHUB_ACTIONS", "false") // This REALLY is required because the tests run in CI
 32 | 	tool := dummyTool{"foo", 42}
 33 | 
 34 | 	// When we test the snapshot
 35 | 	err := Test("dummy", tool)
 36 | 
 37 | 	// Then it should succeed and write the snapshot file
 38 | 	require.NoError(t, err)
 39 | 	path := filepath.Join("__toolsnaps__", "dummy.snap")
 40 | 	_, statErr := os.Stat(path)
 41 | 	assert.NoError(t, statErr, "expected snapshot file to be written")
 42 | }
 43 | 
 44 | func TestSnapshotDoesNotExistInCI(t *testing.T) {
 45 | 	withIsolatedWorkingDir(t)
 46 | 	// Ensure that UPDATE_TOOLSNAPS is not set for this test, which it might be if someone is running
 47 | 	// UPDATE_TOOLSNAPS=true go test ./...
 48 | 	t.Setenv("UPDATE_TOOLSNAPS", "false")
 49 | 
 50 | 	// Given we are running in CI
 51 | 	t.Setenv("GITHUB_ACTIONS", "true")
 52 | 	tool := dummyTool{"foo", 42}
 53 | 
 54 | 	// When we test the snapshot
 55 | 	err := Test("dummy", tool)
 56 | 
 57 | 	// Then it should error about missing snapshot in CI
 58 | 	require.Error(t, err)
 59 | 	assert.Contains(t, err.Error(), "tool snapshot does not exist", "expected error about missing snapshot in CI")
 60 | }
 61 | 
 62 | func TestSnapshotExistsMatch(t *testing.T) {
 63 | 	withIsolatedWorkingDir(t)
 64 | 
 65 | 	// Given a matching snapshot file exists
 66 | 	tool := dummyTool{"foo", 42}
 67 | 	b, _ := json.MarshalIndent(tool, "", "  ")
 68 | 	require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
 69 | 	require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), b, 0600))
 70 | 
 71 | 	// When we test the snapshot
 72 | 	err := Test("dummy", tool)
 73 | 
 74 | 	// Then it should succeed (no error)
 75 | 	require.NoError(t, err)
 76 | }
 77 | 
 78 | func TestSnapshotExistsDiff(t *testing.T) {
 79 | 	withIsolatedWorkingDir(t)
 80 | 	// Ensure that UPDATE_TOOLSNAPS is not set for this test, which it might be if someone is running
 81 | 	// UPDATE_TOOLSNAPS=true go test ./...
 82 | 	t.Setenv("UPDATE_TOOLSNAPS", "false")
 83 | 
 84 | 	// Given a non-matching snapshot file exists
 85 | 	require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
 86 | 	require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), []byte(`{"name":"foo","value":1}`), 0600))
 87 | 	tool := dummyTool{"foo", 2}
 88 | 
 89 | 	// When we test the snapshot
 90 | 	err := Test("dummy", tool)
 91 | 
 92 | 	// Then it should error about the schema diff
 93 | 	require.Error(t, err)
 94 | 	assert.Contains(t, err.Error(), "tool schema for dummy has changed unexpectedly", "expected error about diff")
 95 | }
 96 | 
 97 | func TestUpdateToolsnaps(t *testing.T) {
 98 | 	withIsolatedWorkingDir(t)
 99 | 
100 | 	// Given UPDATE_TOOLSNAPS is set, regardless of whether a matching snapshot file exists
101 | 	t.Setenv("UPDATE_TOOLSNAPS", "true")
102 | 	require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
103 | 	require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), []byte(`{"name":"foo","value":1}`), 0600))
104 | 	tool := dummyTool{"foo", 42}
105 | 
106 | 	// When we test the snapshot
107 | 	err := Test("dummy", tool)
108 | 
109 | 	// Then it should succeed and write the snapshot file
110 | 	require.NoError(t, err)
111 | 	path := filepath.Join("__toolsnaps__", "dummy.snap")
112 | 	_, statErr := os.Stat(path)
113 | 	assert.NoError(t, statErr, "expected snapshot file to be written")
114 | }
115 | 
116 | func TestMalformedSnapshotJSON(t *testing.T) {
117 | 	withIsolatedWorkingDir(t)
118 | 	// Ensure that UPDATE_TOOLSNAPS is not set for this test, which it might be if someone is running
119 | 	// UPDATE_TOOLSNAPS=true go test ./...
120 | 	t.Setenv("UPDATE_TOOLSNAPS", "false")
121 | 
122 | 	// Given a malformed snapshot file exists
123 | 	require.NoError(t, os.MkdirAll("__toolsnaps__", 0700))
124 | 	require.NoError(t, os.WriteFile(filepath.Join("__toolsnaps__", "dummy.snap"), []byte(`not-json`), 0600))
125 | 	tool := dummyTool{"foo", 42}
126 | 
127 | 	// When we test the snapshot
128 | 	err := Test("dummy", tool)
129 | 
130 | 	// Then it should error about malformed snapshot JSON
131 | 	require.Error(t, err)
132 | 	assert.Contains(t, err.Error(), "failed to parse snapshot JSON for dummy", "expected error about malformed snapshot JSON")
133 | }
134 | 


--------------------------------------------------------------------------------
/pkg/errors/error.go:
--------------------------------------------------------------------------------
  1 | package errors
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"fmt"
  6 | 
  7 | 	"github.com/google/go-github/v73/github"
  8 | 	"github.com/mark3labs/mcp-go/mcp"
  9 | )
 10 | 
 11 | type GitHubAPIError struct {
 12 | 	Message  string           `json:"message"`
 13 | 	Response *github.Response `json:"-"`
 14 | 	Err      error            `json:"-"`
 15 | }
 16 | 
 17 | // NewGitHubAPIError creates a new GitHubAPIError with the provided message, response, and error.
 18 | func newGitHubAPIError(message string, resp *github.Response, err error) *GitHubAPIError {
 19 | 	return &GitHubAPIError{
 20 | 		Message:  message,
 21 | 		Response: resp,
 22 | 		Err:      err,
 23 | 	}
 24 | }
 25 | 
 26 | func (e *GitHubAPIError) Error() string {
 27 | 	return fmt.Errorf("%s: %w", e.Message, e.Err).Error()
 28 | }
 29 | 
 30 | type GitHubGraphQLError struct {
 31 | 	Message string `json:"message"`
 32 | 	Err     error  `json:"-"`
 33 | }
 34 | 
 35 | func newGitHubGraphQLError(message string, err error) *GitHubGraphQLError {
 36 | 	return &GitHubGraphQLError{
 37 | 		Message: message,
 38 | 		Err:     err,
 39 | 	}
 40 | }
 41 | 
 42 | func (e *GitHubGraphQLError) Error() string {
 43 | 	return fmt.Errorf("%s: %w", e.Message, e.Err).Error()
 44 | }
 45 | 
 46 | type GitHubErrorKey struct{}
 47 | type GitHubCtxErrors struct {
 48 | 	api     []*GitHubAPIError
 49 | 	graphQL []*GitHubGraphQLError
 50 | }
 51 | 
 52 | // ContextWithGitHubErrors updates or creates a context with a pointer to GitHub error information (to be used by middleware).
 53 | func ContextWithGitHubErrors(ctx context.Context) context.Context {
 54 | 	if ctx == nil {
 55 | 		ctx = context.Background()
 56 | 	}
 57 | 	if val, ok := ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors); ok {
 58 | 		// If the context already has GitHubCtxErrors, we just empty the slices to start fresh
 59 | 		val.api = []*GitHubAPIError{}
 60 | 		val.graphQL = []*GitHubGraphQLError{}
 61 | 	} else {
 62 | 		// If not, we create a new GitHubCtxErrors and set it in the context
 63 | 		ctx = context.WithValue(ctx, GitHubErrorKey{}, &GitHubCtxErrors{})
 64 | 	}
 65 | 
 66 | 	return ctx
 67 | }
 68 | 
 69 | // GetGitHubAPIErrors retrieves the slice of GitHubAPIErrors from the context.
 70 | func GetGitHubAPIErrors(ctx context.Context) ([]*GitHubAPIError, error) {
 71 | 	if val, ok := ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors); ok {
 72 | 		return val.api, nil // return the slice of API errors from the context
 73 | 	}
 74 | 	return nil, fmt.Errorf("context does not contain GitHubCtxErrors")
 75 | }
 76 | 
 77 | // GetGitHubGraphQLErrors retrieves the slice of GitHubGraphQLErrors from the context.
 78 | func GetGitHubGraphQLErrors(ctx context.Context) ([]*GitHubGraphQLError, error) {
 79 | 	if val, ok := ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors); ok {
 80 | 		return val.graphQL, nil // return the slice of GraphQL errors from the context
 81 | 	}
 82 | 	return nil, fmt.Errorf("context does not contain GitHubCtxErrors")
 83 | }
 84 | 
 85 | func NewGitHubAPIErrorToCtx(ctx context.Context, message string, resp *github.Response, err error) (context.Context, error) {
 86 | 	apiErr := newGitHubAPIError(message, resp, err)
 87 | 	if ctx != nil {
 88 | 		_, _ = addGitHubAPIErrorToContext(ctx, apiErr) // Explicitly ignore error for graceful handling
 89 | 	}
 90 | 	return ctx, nil
 91 | }
 92 | 
 93 | func addGitHubAPIErrorToContext(ctx context.Context, err *GitHubAPIError) (context.Context, error) {
 94 | 	if val, ok := ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors); ok {
 95 | 		val.api = append(val.api, err) // append the error to the existing slice in the context
 96 | 		return ctx, nil
 97 | 	}
 98 | 	return nil, fmt.Errorf("context does not contain GitHubCtxErrors")
 99 | }
100 | 
101 | func addGitHubGraphQLErrorToContext(ctx context.Context, err *GitHubGraphQLError) (context.Context, error) {
102 | 	if val, ok := ctx.Value(GitHubErrorKey{}).(*GitHubCtxErrors); ok {
103 | 		val.graphQL = append(val.graphQL, err) // append the error to the existing slice in the context
104 | 		return ctx, nil
105 | 	}
106 | 	return nil, fmt.Errorf("context does not contain GitHubCtxErrors")
107 | }
108 | 
109 | // NewGitHubAPIErrorResponse returns an mcp.NewToolResultError and retains the error in the context for access via middleware
110 | func NewGitHubAPIErrorResponse(ctx context.Context, message string, resp *github.Response, err error) *mcp.CallToolResult {
111 | 	apiErr := newGitHubAPIError(message, resp, err)
112 | 	if ctx != nil {
113 | 		_, _ = addGitHubAPIErrorToContext(ctx, apiErr) // Explicitly ignore error for graceful handling
114 | 	}
115 | 	return mcp.NewToolResultErrorFromErr(message, err)
116 | }
117 | 
118 | // NewGitHubGraphQLErrorResponse returns an mcp.NewToolResultError and retains the error in the context for access via middleware
119 | func NewGitHubGraphQLErrorResponse(ctx context.Context, message string, err error) *mcp.CallToolResult {
120 | 	graphQLErr := newGitHubGraphQLError(message, err)
121 | 	if ctx != nil {
122 | 		_, _ = addGitHubGraphQLErrorToContext(ctx, graphQLErr) // Explicitly ignore error for graceful handling
123 | 	}
124 | 	return mcp.NewToolResultErrorFromErr(message, err)
125 | }
126 | 


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/add_issue_comment.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Add comment to issue",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Add a comment to a specific issue in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "body": {
10 |         "description": "Comment content",
11 |         "type": "string"
12 |       },
13 |       "issue_number": {
14 |         "description": "Issue number to comment on",
15 |         "type": "number"
16 |       },
17 |       "owner": {
18 |         "description": "Repository owner",
19 |         "type": "string"
20 |       },
21 |       "repo": {
22 |         "description": "Repository name",
23 |         "type": "string"
24 |       }
25 |     },
26 |     "required": [
27 |       "owner",
28 |       "repo",
29 |       "issue_number",
30 |       "body"
31 |     ],
32 |     "type": "object"
33 |   },
34 |   "name": "add_issue_comment"
35 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/add_pull_request_review_comment_to_pending_review.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Add comment to the requester's latest pending pull request review",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Add a comment to the requester's latest pending pull request review, a pending review needs to already exist to call this (check with the user if not sure).",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "body": {
10 |         "description": "The text of the review comment",
11 |         "type": "string"
12 |       },
13 |       "line": {
14 |         "description": "The line of the blob in the pull request diff that the comment applies to. For multi-line comments, the last line of the range",
15 |         "type": "number"
16 |       },
17 |       "owner": {
18 |         "description": "Repository owner",
19 |         "type": "string"
20 |       },
21 |       "path": {
22 |         "description": "The relative path to the file that necessitates a comment",
23 |         "type": "string"
24 |       },
25 |       "pullNumber": {
26 |         "description": "Pull request number",
27 |         "type": "number"
28 |       },
29 |       "repo": {
30 |         "description": "Repository name",
31 |         "type": "string"
32 |       },
33 |       "side": {
34 |         "description": "The side of the diff to comment on. LEFT indicates the previous state, RIGHT indicates the new state",
35 |         "enum": [
36 |           "LEFT",
37 |           "RIGHT"
38 |         ],
39 |         "type": "string"
40 |       },
41 |       "startLine": {
42 |         "description": "For multi-line comments, the first line of the range that the comment applies to",
43 |         "type": "number"
44 |       },
45 |       "startSide": {
46 |         "description": "For multi-line comments, the starting side of the diff that the comment applies to. LEFT indicates the previous state, RIGHT indicates the new state",
47 |         "enum": [
48 |           "LEFT",
49 |           "RIGHT"
50 |         ],
51 |         "type": "string"
52 |       },
53 |       "subjectType": {
54 |         "description": "The level at which the comment is targeted",
55 |         "enum": [
56 |           "FILE",
57 |           "LINE"
58 |         ],
59 |         "type": "string"
60 |       }
61 |     },
62 |     "required": [
63 |       "owner",
64 |       "repo",
65 |       "pullNumber",
66 |       "path",
67 |       "body",
68 |       "subjectType"
69 |     ],
70 |     "type": "object"
71 |   },
72 |   "name": "add_pull_request_review_comment_to_pending_review"
73 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/assign_copilot_to_issue.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Assign Copilot to issue",
 4 |     "readOnlyHint": false,
 5 |     "idempotentHint": true
 6 |   },
 7 |   "description": "Assign Copilot to a specific issue in a GitHub repository.\n\nThis tool can help with the following outcomes:\n- a Pull Request created with source code changes to resolve the issue\n\n\nMore information can be found at:\n- https://docs.github.com/en/copilot/using-github-copilot/using-copilot-coding-agent-to-work-on-tasks/about-assigning-tasks-to-copilot\n",
 8 |   "inputSchema": {
 9 |     "properties": {
10 |       "issueNumber": {
11 |         "description": "Issue number",
12 |         "type": "number"
13 |       },
14 |       "owner": {
15 |         "description": "Repository owner",
16 |         "type": "string"
17 |       },
18 |       "repo": {
19 |         "description": "Repository name",
20 |         "type": "string"
21 |       }
22 |     },
23 |     "required": [
24 |       "owner",
25 |       "repo",
26 |       "issueNumber"
27 |     ],
28 |     "type": "object"
29 |   },
30 |   "name": "assign_copilot_to_issue"
31 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/create_and_submit_pull_request_review.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Create and submit a pull request review without comments",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Create and submit a review for a pull request without review comments.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "body": {
10 |         "description": "Review comment text",
11 |         "type": "string"
12 |       },
13 |       "commitID": {
14 |         "description": "SHA of commit to review",
15 |         "type": "string"
16 |       },
17 |       "event": {
18 |         "description": "Review action to perform",
19 |         "enum": [
20 |           "APPROVE",
21 |           "REQUEST_CHANGES",
22 |           "COMMENT"
23 |         ],
24 |         "type": "string"
25 |       },
26 |       "owner": {
27 |         "description": "Repository owner",
28 |         "type": "string"
29 |       },
30 |       "pullNumber": {
31 |         "description": "Pull request number",
32 |         "type": "number"
33 |       },
34 |       "repo": {
35 |         "description": "Repository name",
36 |         "type": "string"
37 |       }
38 |     },
39 |     "required": [
40 |       "owner",
41 |       "repo",
42 |       "pullNumber",
43 |       "body",
44 |       "event"
45 |     ],
46 |     "type": "object"
47 |   },
48 |   "name": "create_and_submit_pull_request_review"
49 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/create_branch.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Create branch",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Create a new branch in a GitHub repository",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "branch": {
10 |         "description": "Name for new branch",
11 |         "type": "string"
12 |       },
13 |       "from_branch": {
14 |         "description": "Source branch (defaults to repo default)",
15 |         "type": "string"
16 |       },
17 |       "owner": {
18 |         "description": "Repository owner",
19 |         "type": "string"
20 |       },
21 |       "repo": {
22 |         "description": "Repository name",
23 |         "type": "string"
24 |       }
25 |     },
26 |     "required": [
27 |       "owner",
28 |       "repo",
29 |       "branch"
30 |     ],
31 |     "type": "object"
32 |   },
33 |   "name": "create_branch"
34 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/create_issue.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Open new issue",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Create a new issue in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "assignees": {
10 |         "description": "Usernames to assign to this issue",
11 |         "items": {
12 |           "type": "string"
13 |         },
14 |         "type": "array"
15 |       },
16 |       "body": {
17 |         "description": "Issue body content",
18 |         "type": "string"
19 |       },
20 |       "labels": {
21 |         "description": "Labels to apply to this issue",
22 |         "items": {
23 |           "type": "string"
24 |         },
25 |         "type": "array"
26 |       },
27 |       "milestone": {
28 |         "description": "Milestone number",
29 |         "type": "number"
30 |       },
31 |       "owner": {
32 |         "description": "Repository owner",
33 |         "type": "string"
34 |       },
35 |       "repo": {
36 |         "description": "Repository name",
37 |         "type": "string"
38 |       },
39 |       "title": {
40 |         "description": "Issue title",
41 |         "type": "string"
42 |       }
43 |     },
44 |     "required": [
45 |       "owner",
46 |       "repo",
47 |       "title"
48 |     ],
49 |     "type": "object"
50 |   },
51 |   "name": "create_issue"
52 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/create_or_update_file.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Create or update file",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Create or update a single file in a GitHub repository. If updating, you must provide the SHA of the file you want to update. Use this tool to create or update a file in a GitHub repository remotely; do not use it for local file operations.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "branch": {
10 |         "description": "Branch to create/update the file in",
11 |         "type": "string"
12 |       },
13 |       "content": {
14 |         "description": "Content of the file",
15 |         "type": "string"
16 |       },
17 |       "message": {
18 |         "description": "Commit message",
19 |         "type": "string"
20 |       },
21 |       "owner": {
22 |         "description": "Repository owner (username or organization)",
23 |         "type": "string"
24 |       },
25 |       "path": {
26 |         "description": "Path where to create/update the file",
27 |         "type": "string"
28 |       },
29 |       "repo": {
30 |         "description": "Repository name",
31 |         "type": "string"
32 |       },
33 |       "sha": {
34 |         "description": "Required if updating an existing file. The blob SHA of the file being replaced.",
35 |         "type": "string"
36 |       }
37 |     },
38 |     "required": [
39 |       "owner",
40 |       "repo",
41 |       "path",
42 |       "content",
43 |       "message",
44 |       "branch"
45 |     ],
46 |     "type": "object"
47 |   },
48 |   "name": "create_or_update_file"
49 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/create_pending_pull_request_review.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Create pending pull request review",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Create a pending review for a pull request. Call this first before attempting to add comments to a pending review, and ultimately submitting it. A pending pull request review means a pull request review, it is pending because you create it first and submit it later, and the PR author will not see it until it is submitted.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "commitID": {
10 |         "description": "SHA of commit to review",
11 |         "type": "string"
12 |       },
13 |       "owner": {
14 |         "description": "Repository owner",
15 |         "type": "string"
16 |       },
17 |       "pullNumber": {
18 |         "description": "Pull request number",
19 |         "type": "number"
20 |       },
21 |       "repo": {
22 |         "description": "Repository name",
23 |         "type": "string"
24 |       }
25 |     },
26 |     "required": [
27 |       "owner",
28 |       "repo",
29 |       "pullNumber"
30 |     ],
31 |     "type": "object"
32 |   },
33 |   "name": "create_pending_pull_request_review"
34 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/create_pull_request.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Open new pull request",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Create a new pull request in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "base": {
10 |         "description": "Branch to merge into",
11 |         "type": "string"
12 |       },
13 |       "body": {
14 |         "description": "PR description",
15 |         "type": "string"
16 |       },
17 |       "draft": {
18 |         "description": "Create as draft PR",
19 |         "type": "boolean"
20 |       },
21 |       "head": {
22 |         "description": "Branch containing changes",
23 |         "type": "string"
24 |       },
25 |       "maintainer_can_modify": {
26 |         "description": "Allow maintainer edits",
27 |         "type": "boolean"
28 |       },
29 |       "owner": {
30 |         "description": "Repository owner",
31 |         "type": "string"
32 |       },
33 |       "repo": {
34 |         "description": "Repository name",
35 |         "type": "string"
36 |       },
37 |       "title": {
38 |         "description": "PR title",
39 |         "type": "string"
40 |       }
41 |     },
42 |     "required": [
43 |       "owner",
44 |       "repo",
45 |       "title",
46 |       "head",
47 |       "base"
48 |     ],
49 |     "type": "object"
50 |   },
51 |   "name": "create_pull_request"
52 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/create_repository.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Create repository",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Create a new GitHub repository in your account",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "autoInit": {
10 |         "description": "Initialize with README",
11 |         "type": "boolean"
12 |       },
13 |       "description": {
14 |         "description": "Repository description",
15 |         "type": "string"
16 |       },
17 |       "name": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       },
21 |       "private": {
22 |         "description": "Whether repo should be private",
23 |         "type": "boolean"
24 |       }
25 |     },
26 |     "required": [
27 |       "name"
28 |     ],
29 |     "type": "object"
30 |   },
31 |   "name": "create_repository"
32 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/delete_file.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Delete file",
 4 |     "readOnlyHint": false,
 5 |     "destructiveHint": true
 6 |   },
 7 |   "description": "Delete a file from a GitHub repository",
 8 |   "inputSchema": {
 9 |     "properties": {
10 |       "branch": {
11 |         "description": "Branch to delete the file from",
12 |         "type": "string"
13 |       },
14 |       "message": {
15 |         "description": "Commit message",
16 |         "type": "string"
17 |       },
18 |       "owner": {
19 |         "description": "Repository owner (username or organization)",
20 |         "type": "string"
21 |       },
22 |       "path": {
23 |         "description": "Path to the file to delete",
24 |         "type": "string"
25 |       },
26 |       "repo": {
27 |         "description": "Repository name",
28 |         "type": "string"
29 |       }
30 |     },
31 |     "required": [
32 |       "owner",
33 |       "repo",
34 |       "path",
35 |       "message",
36 |       "branch"
37 |     ],
38 |     "type": "object"
39 |   },
40 |   "name": "delete_file"
41 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/delete_pending_pull_request_review.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Delete the requester's latest pending pull request review",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Delete the requester's latest pending pull request review. Use this after the user decides not to submit a pending review, if you don't know if they already created one then check first.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "pullNumber": {
14 |         "description": "Pull request number",
15 |         "type": "number"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "pullNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "delete_pending_pull_request_review"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/dismiss_notification.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Dismiss notification",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Dismiss a notification by marking it as read or done",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "state": {
10 |         "description": "The new state of the notification (read/done)",
11 |         "enum": [
12 |           "read",
13 |           "done"
14 |         ],
15 |         "type": "string"
16 |       },
17 |       "threadID": {
18 |         "description": "The ID of the notification thread",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "threadID"
24 |     ],
25 |     "type": "object"
26 |   },
27 |   "name": "dismiss_notification"
28 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/fork_repository.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Fork repository",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Fork a GitHub repository to your account or specified organization",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "organization": {
10 |         "description": "Organization to fork to",
11 |         "type": "string"
12 |       },
13 |       "owner": {
14 |         "description": "Repository owner",
15 |         "type": "string"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo"
25 |     ],
26 |     "type": "object"
27 |   },
28 |   "name": "fork_repository"
29 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_code_scanning_alert.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get code scanning alert",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get details of a specific code scanning alert in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "alertNumber": {
10 |         "description": "The number of the alert.",
11 |         "type": "number"
12 |       },
13 |       "owner": {
14 |         "description": "The owner of the repository.",
15 |         "type": "string"
16 |       },
17 |       "repo": {
18 |         "description": "The name of the repository.",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "alertNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_code_scanning_alert"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_commit.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get commit details",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get details for a commit from a GitHub repository",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "page": {
14 |         "description": "Page number for pagination (min 1)",
15 |         "minimum": 1,
16 |         "type": "number"
17 |       },
18 |       "perPage": {
19 |         "description": "Results per page for pagination (min 1, max 100)",
20 |         "maximum": 100,
21 |         "minimum": 1,
22 |         "type": "number"
23 |       },
24 |       "repo": {
25 |         "description": "Repository name",
26 |         "type": "string"
27 |       },
28 |       "sha": {
29 |         "description": "Commit SHA, branch name, or tag name",
30 |         "type": "string"
31 |       }
32 |     },
33 |     "required": [
34 |       "owner",
35 |       "repo",
36 |       "sha"
37 |     ],
38 |     "type": "object"
39 |   },
40 |   "name": "get_commit"
41 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_dependabot_alert.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get dependabot alert",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get details of a specific dependabot alert in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "alertNumber": {
10 |         "description": "The number of the alert.",
11 |         "type": "number"
12 |       },
13 |       "owner": {
14 |         "description": "The owner of the repository.",
15 |         "type": "string"
16 |       },
17 |       "repo": {
18 |         "description": "The name of the repository.",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "alertNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_dependabot_alert"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_file_contents.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get file or directory contents",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get the contents of a file or directory from a GitHub repository",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner (username or organization)",
11 |         "type": "string"
12 |       },
13 |       "path": {
14 |         "default": "/",
15 |         "description": "Path to file/directory (directories must end with a slash '/')",
16 |         "type": "string"
17 |       },
18 |       "ref": {
19 |         "description": "Accepts optional git refs such as `refs/tags/{tag}`, `refs/heads/{branch}` or `refs/pull/{pr_number}/head`",
20 |         "type": "string"
21 |       },
22 |       "repo": {
23 |         "description": "Repository name",
24 |         "type": "string"
25 |       },
26 |       "sha": {
27 |         "description": "Accepts optional commit SHA. If specified, it will be used instead of ref",
28 |         "type": "string"
29 |       }
30 |     },
31 |     "required": [
32 |       "owner",
33 |       "repo"
34 |     ],
35 |     "type": "object"
36 |   },
37 |   "name": "get_file_contents"
38 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_issue.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get issue details",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get details of a specific issue in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "issue_number": {
10 |         "description": "The number of the issue",
11 |         "type": "number"
12 |       },
13 |       "owner": {
14 |         "description": "The owner of the repository",
15 |         "type": "string"
16 |       },
17 |       "repo": {
18 |         "description": "The name of the repository",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "issue_number"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_issue"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_issue_comments.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get issue comments",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get comments for a specific issue in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "issue_number": {
10 |         "description": "Issue number",
11 |         "type": "number"
12 |       },
13 |       "owner": {
14 |         "description": "Repository owner",
15 |         "type": "string"
16 |       },
17 |       "page": {
18 |         "description": "Page number for pagination (min 1)",
19 |         "minimum": 1,
20 |         "type": "number"
21 |       },
22 |       "perPage": {
23 |         "description": "Results per page for pagination (min 1, max 100)",
24 |         "maximum": 100,
25 |         "minimum": 1,
26 |         "type": "number"
27 |       },
28 |       "repo": {
29 |         "description": "Repository name",
30 |         "type": "string"
31 |       }
32 |     },
33 |     "required": [
34 |       "owner",
35 |       "repo",
36 |       "issue_number"
37 |     ],
38 |     "type": "object"
39 |   },
40 |   "name": "get_issue_comments"
41 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_me.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get my user profile",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get details of the authenticated GitHub user. Use this when a request is about the user's own profile for GitHub. Or when information is missing to build other tool calls.",
 7 |   "inputSchema": {
 8 |     "properties": {},
 9 |     "type": "object"
10 |   },
11 |   "name": "get_me"
12 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_notification_details.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get notification details",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get detailed information for a specific GitHub notification, always call this tool when the user asks for details about a specific notification, if you don't know the ID list notifications first.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "notificationID": {
10 |         "description": "The ID of the notification",
11 |         "type": "string"
12 |       }
13 |     },
14 |     "required": [
15 |       "notificationID"
16 |     ],
17 |     "type": "object"
18 |   },
19 |   "name": "get_notification_details"
20 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_pull_request.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get pull request details",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get details of a specific pull request in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "pullNumber": {
14 |         "description": "Pull request number",
15 |         "type": "number"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "pullNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_pull_request"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_pull_request_comments.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get pull request comments",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get comments for a specific pull request.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "pullNumber": {
14 |         "description": "Pull request number",
15 |         "type": "number"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "pullNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_pull_request_comments"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_pull_request_diff.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get pull request diff",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get the diff of a pull request.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "pullNumber": {
14 |         "description": "Pull request number",
15 |         "type": "number"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "pullNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_pull_request_diff"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_pull_request_files.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get pull request files",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get the files changed in a specific pull request.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "page": {
14 |         "description": "Page number for pagination (min 1)",
15 |         "minimum": 1,
16 |         "type": "number"
17 |       },
18 |       "perPage": {
19 |         "description": "Results per page for pagination (min 1, max 100)",
20 |         "maximum": 100,
21 |         "minimum": 1,
22 |         "type": "number"
23 |       },
24 |       "pullNumber": {
25 |         "description": "Pull request number",
26 |         "type": "number"
27 |       },
28 |       "repo": {
29 |         "description": "Repository name",
30 |         "type": "string"
31 |       }
32 |     },
33 |     "required": [
34 |       "owner",
35 |       "repo",
36 |       "pullNumber"
37 |     ],
38 |     "type": "object"
39 |   },
40 |   "name": "get_pull_request_files"
41 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_pull_request_reviews.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get pull request reviews",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get reviews for a specific pull request.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "pullNumber": {
14 |         "description": "Pull request number",
15 |         "type": "number"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "pullNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_pull_request_reviews"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_pull_request_status.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get pull request status checks",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get the status of a specific pull request.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "pullNumber": {
14 |         "description": "Pull request number",
15 |         "type": "number"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "pullNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_pull_request_status"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/get_tag.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Get tag details",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get details about a specific git tag in a GitHub repository",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "repo": {
14 |         "description": "Repository name",
15 |         "type": "string"
16 |       },
17 |       "tag": {
18 |         "description": "Tag name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "tag"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "get_tag"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_branches.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List branches",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "List branches in a GitHub repository",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "page": {
14 |         "description": "Page number for pagination (min 1)",
15 |         "minimum": 1,
16 |         "type": "number"
17 |       },
18 |       "perPage": {
19 |         "description": "Results per page for pagination (min 1, max 100)",
20 |         "maximum": 100,
21 |         "minimum": 1,
22 |         "type": "number"
23 |       },
24 |       "repo": {
25 |         "description": "Repository name",
26 |         "type": "string"
27 |       }
28 |     },
29 |     "required": [
30 |       "owner",
31 |       "repo"
32 |     ],
33 |     "type": "object"
34 |   },
35 |   "name": "list_branches"
36 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_code_scanning_alerts.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List code scanning alerts",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "List code scanning alerts in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "The owner of the repository.",
11 |         "type": "string"
12 |       },
13 |       "ref": {
14 |         "description": "The Git reference for the results you want to list.",
15 |         "type": "string"
16 |       },
17 |       "repo": {
18 |         "description": "The name of the repository.",
19 |         "type": "string"
20 |       },
21 |       "severity": {
22 |         "description": "Filter code scanning alerts by severity",
23 |         "enum": [
24 |           "critical",
25 |           "high",
26 |           "medium",
27 |           "low",
28 |           "warning",
29 |           "note",
30 |           "error"
31 |         ],
32 |         "type": "string"
33 |       },
34 |       "state": {
35 |         "default": "open",
36 |         "description": "Filter code scanning alerts by state. Defaults to open",
37 |         "enum": [
38 |           "open",
39 |           "closed",
40 |           "dismissed",
41 |           "fixed"
42 |         ],
43 |         "type": "string"
44 |       },
45 |       "tool_name": {
46 |         "description": "The name of the tool used for code scanning.",
47 |         "type": "string"
48 |       }
49 |     },
50 |     "required": [
51 |       "owner",
52 |       "repo"
53 |     ],
54 |     "type": "object"
55 |   },
56 |   "name": "list_code_scanning_alerts"
57 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_commits.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List commits",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Get list of commits of a branch in a GitHub repository. Returns at least 30 results per page by default, but can return more if specified using the perPage parameter (up to 100).",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "author": {
10 |         "description": "Author username or email address to filter commits by",
11 |         "type": "string"
12 |       },
13 |       "owner": {
14 |         "description": "Repository owner",
15 |         "type": "string"
16 |       },
17 |       "page": {
18 |         "description": "Page number for pagination (min 1)",
19 |         "minimum": 1,
20 |         "type": "number"
21 |       },
22 |       "perPage": {
23 |         "description": "Results per page for pagination (min 1, max 100)",
24 |         "maximum": 100,
25 |         "minimum": 1,
26 |         "type": "number"
27 |       },
28 |       "repo": {
29 |         "description": "Repository name",
30 |         "type": "string"
31 |       },
32 |       "sha": {
33 |         "description": "Commit SHA, branch or tag name to list commits of. If not provided, uses the default branch of the repository. If a commit SHA is provided, will list commits up to that SHA.",
34 |         "type": "string"
35 |       }
36 |     },
37 |     "required": [
38 |       "owner",
39 |       "repo"
40 |     ],
41 |     "type": "object"
42 |   },
43 |   "name": "list_commits"
44 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_dependabot_alerts.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List dependabot alerts",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "List dependabot alerts in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "The owner of the repository.",
11 |         "type": "string"
12 |       },
13 |       "repo": {
14 |         "description": "The name of the repository.",
15 |         "type": "string"
16 |       },
17 |       "severity": {
18 |         "description": "Filter dependabot alerts by severity",
19 |         "enum": [
20 |           "low",
21 |           "medium",
22 |           "high",
23 |           "critical"
24 |         ],
25 |         "type": "string"
26 |       },
27 |       "state": {
28 |         "default": "open",
29 |         "description": "Filter dependabot alerts by state. Defaults to open",
30 |         "enum": [
31 |           "open",
32 |           "fixed",
33 |           "dismissed",
34 |           "auto_dismissed"
35 |         ],
36 |         "type": "string"
37 |       }
38 |     },
39 |     "required": [
40 |       "owner",
41 |       "repo"
42 |     ],
43 |     "type": "object"
44 |   },
45 |   "name": "list_dependabot_alerts"
46 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_issues.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List issues",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "List issues in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "direction": {
10 |         "description": "Sort direction",
11 |         "enum": [
12 |           "asc",
13 |           "desc"
14 |         ],
15 |         "type": "string"
16 |       },
17 |       "labels": {
18 |         "description": "Filter by labels",
19 |         "items": {
20 |           "type": "string"
21 |         },
22 |         "type": "array"
23 |       },
24 |       "owner": {
25 |         "description": "Repository owner",
26 |         "type": "string"
27 |       },
28 |       "page": {
29 |         "description": "Page number for pagination (min 1)",
30 |         "minimum": 1,
31 |         "type": "number"
32 |       },
33 |       "perPage": {
34 |         "description": "Results per page for pagination (min 1, max 100)",
35 |         "maximum": 100,
36 |         "minimum": 1,
37 |         "type": "number"
38 |       },
39 |       "repo": {
40 |         "description": "Repository name",
41 |         "type": "string"
42 |       },
43 |       "since": {
44 |         "description": "Filter by date (ISO 8601 timestamp)",
45 |         "type": "string"
46 |       },
47 |       "sort": {
48 |         "description": "Sort order",
49 |         "enum": [
50 |           "created",
51 |           "updated",
52 |           "comments"
53 |         ],
54 |         "type": "string"
55 |       },
56 |       "state": {
57 |         "description": "Filter by state",
58 |         "enum": [
59 |           "open",
60 |           "closed",
61 |           "all"
62 |         ],
63 |         "type": "string"
64 |       }
65 |     },
66 |     "required": [
67 |       "owner",
68 |       "repo"
69 |     ],
70 |     "type": "object"
71 |   },
72 |   "name": "list_issues"
73 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_notifications.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List notifications",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Lists all GitHub notifications for the authenticated user, including unread notifications, mentions, review requests, assignments, and updates on issues or pull requests. Use this tool whenever the user asks what to work on next, requests a summary of their GitHub activity, wants to see pending reviews, or needs to check for new updates or tasks. This tool is the primary way to discover actionable items, reminders, and outstanding work on GitHub. Always call this tool when asked what to work on next, what is pending, or what needs attention in GitHub.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "before": {
10 |         "description": "Only show notifications updated before the given time (ISO 8601 format)",
11 |         "type": "string"
12 |       },
13 |       "filter": {
14 |         "description": "Filter notifications to, use default unless specified. Read notifications are ones that have already been acknowledged by the user. Participating notifications are those that the user is directly involved in, such as issues or pull requests they have commented on or created.",
15 |         "enum": [
16 |           "default",
17 |           "include_read_notifications",
18 |           "only_participating"
19 |         ],
20 |         "type": "string"
21 |       },
22 |       "owner": {
23 |         "description": "Optional repository owner. If provided with repo, only notifications for this repository are listed.",
24 |         "type": "string"
25 |       },
26 |       "page": {
27 |         "description": "Page number for pagination (min 1)",
28 |         "minimum": 1,
29 |         "type": "number"
30 |       },
31 |       "perPage": {
32 |         "description": "Results per page for pagination (min 1, max 100)",
33 |         "maximum": 100,
34 |         "minimum": 1,
35 |         "type": "number"
36 |       },
37 |       "repo": {
38 |         "description": "Optional repository name. If provided with owner, only notifications for this repository are listed.",
39 |         "type": "string"
40 |       },
41 |       "since": {
42 |         "description": "Only show notifications updated after the given time (ISO 8601 format)",
43 |         "type": "string"
44 |       }
45 |     },
46 |     "type": "object"
47 |   },
48 |   "name": "list_notifications"
49 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_pull_requests.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List pull requests",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "List pull requests in a GitHub repository. If the user specifies an author, then DO NOT use this tool and use the search_pull_requests tool instead.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "base": {
10 |         "description": "Filter by base branch",
11 |         "type": "string"
12 |       },
13 |       "direction": {
14 |         "description": "Sort direction",
15 |         "enum": [
16 |           "asc",
17 |           "desc"
18 |         ],
19 |         "type": "string"
20 |       },
21 |       "head": {
22 |         "description": "Filter by head user/org and branch",
23 |         "type": "string"
24 |       },
25 |       "owner": {
26 |         "description": "Repository owner",
27 |         "type": "string"
28 |       },
29 |       "page": {
30 |         "description": "Page number for pagination (min 1)",
31 |         "minimum": 1,
32 |         "type": "number"
33 |       },
34 |       "perPage": {
35 |         "description": "Results per page for pagination (min 1, max 100)",
36 |         "maximum": 100,
37 |         "minimum": 1,
38 |         "type": "number"
39 |       },
40 |       "repo": {
41 |         "description": "Repository name",
42 |         "type": "string"
43 |       },
44 |       "sort": {
45 |         "description": "Sort by",
46 |         "enum": [
47 |           "created",
48 |           "updated",
49 |           "popularity",
50 |           "long-running"
51 |         ],
52 |         "type": "string"
53 |       },
54 |       "state": {
55 |         "description": "Filter by state",
56 |         "enum": [
57 |           "open",
58 |           "closed",
59 |           "all"
60 |         ],
61 |         "type": "string"
62 |       }
63 |     },
64 |     "required": [
65 |       "owner",
66 |       "repo"
67 |     ],
68 |     "type": "object"
69 |   },
70 |   "name": "list_pull_requests"
71 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/list_tags.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "List tags",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "List git tags in a GitHub repository",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "page": {
14 |         "description": "Page number for pagination (min 1)",
15 |         "minimum": 1,
16 |         "type": "number"
17 |       },
18 |       "perPage": {
19 |         "description": "Results per page for pagination (min 1, max 100)",
20 |         "maximum": 100,
21 |         "minimum": 1,
22 |         "type": "number"
23 |       },
24 |       "repo": {
25 |         "description": "Repository name",
26 |         "type": "string"
27 |       }
28 |     },
29 |     "required": [
30 |       "owner",
31 |       "repo"
32 |     ],
33 |     "type": "object"
34 |   },
35 |   "name": "list_tags"
36 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/manage_notification_subscription.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Manage notification subscription",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Manage a notification subscription: ignore, watch, or delete a notification thread subscription.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "action": {
10 |         "description": "Action to perform: ignore, watch, or delete the notification subscription.",
11 |         "enum": [
12 |           "ignore",
13 |           "watch",
14 |           "delete"
15 |         ],
16 |         "type": "string"
17 |       },
18 |       "notificationID": {
19 |         "description": "The ID of the notification thread.",
20 |         "type": "string"
21 |       }
22 |     },
23 |     "required": [
24 |       "notificationID",
25 |       "action"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "manage_notification_subscription"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/manage_repository_notification_subscription.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Manage repository notification subscription",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Manage a repository notification subscription: ignore, watch, or delete repository notifications subscription for the provided repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "action": {
10 |         "description": "Action to perform: ignore, watch, or delete the repository notification subscription.",
11 |         "enum": [
12 |           "ignore",
13 |           "watch",
14 |           "delete"
15 |         ],
16 |         "type": "string"
17 |       },
18 |       "owner": {
19 |         "description": "The account owner of the repository.",
20 |         "type": "string"
21 |       },
22 |       "repo": {
23 |         "description": "The name of the repository.",
24 |         "type": "string"
25 |       }
26 |     },
27 |     "required": [
28 |       "owner",
29 |       "repo",
30 |       "action"
31 |     ],
32 |     "type": "object"
33 |   },
34 |   "name": "manage_repository_notification_subscription"
35 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/mark_all_notifications_read.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Mark all notifications as read",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Mark all notifications as read",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "lastReadAt": {
10 |         "description": "Describes the last point that notifications were checked (optional). Default: Now",
11 |         "type": "string"
12 |       },
13 |       "owner": {
14 |         "description": "Optional repository owner. If provided with repo, only notifications for this repository are marked as read.",
15 |         "type": "string"
16 |       },
17 |       "repo": {
18 |         "description": "Optional repository name. If provided with owner, only notifications for this repository are marked as read.",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "type": "object"
23 |   },
24 |   "name": "mark_all_notifications_read"
25 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/merge_pull_request.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Merge pull request",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Merge a pull request in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "commit_message": {
10 |         "description": "Extra detail for merge commit",
11 |         "type": "string"
12 |       },
13 |       "commit_title": {
14 |         "description": "Title for merge commit",
15 |         "type": "string"
16 |       },
17 |       "merge_method": {
18 |         "description": "Merge method",
19 |         "enum": [
20 |           "merge",
21 |           "squash",
22 |           "rebase"
23 |         ],
24 |         "type": "string"
25 |       },
26 |       "owner": {
27 |         "description": "Repository owner",
28 |         "type": "string"
29 |       },
30 |       "pullNumber": {
31 |         "description": "Pull request number",
32 |         "type": "number"
33 |       },
34 |       "repo": {
35 |         "description": "Repository name",
36 |         "type": "string"
37 |       }
38 |     },
39 |     "required": [
40 |       "owner",
41 |       "repo",
42 |       "pullNumber"
43 |     ],
44 |     "type": "object"
45 |   },
46 |   "name": "merge_pull_request"
47 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/push_files.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Push files to repository",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Push multiple files to a GitHub repository in a single commit",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "branch": {
10 |         "description": "Branch to push to",
11 |         "type": "string"
12 |       },
13 |       "files": {
14 |         "description": "Array of file objects to push, each object with path (string) and content (string)",
15 |         "items": {
16 |           "additionalProperties": false,
17 |           "properties": {
18 |             "content": {
19 |               "description": "file content",
20 |               "type": "string"
21 |             },
22 |             "path": {
23 |               "description": "path to the file",
24 |               "type": "string"
25 |             }
26 |           },
27 |           "required": [
28 |             "path",
29 |             "content"
30 |           ],
31 |           "type": "object"
32 |         },
33 |         "type": "array"
34 |       },
35 |       "message": {
36 |         "description": "Commit message",
37 |         "type": "string"
38 |       },
39 |       "owner": {
40 |         "description": "Repository owner",
41 |         "type": "string"
42 |       },
43 |       "repo": {
44 |         "description": "Repository name",
45 |         "type": "string"
46 |       }
47 |     },
48 |     "required": [
49 |       "owner",
50 |       "repo",
51 |       "branch",
52 |       "files",
53 |       "message"
54 |     ],
55 |     "type": "object"
56 |   },
57 |   "name": "push_files"
58 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/request_copilot_review.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Request Copilot review",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Request a GitHub Copilot code review for a pull request. Use this for automated feedback on pull requests, usually before requesting a human reviewer.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "owner": {
10 |         "description": "Repository owner",
11 |         "type": "string"
12 |       },
13 |       "pullNumber": {
14 |         "description": "Pull request number",
15 |         "type": "number"
16 |       },
17 |       "repo": {
18 |         "description": "Repository name",
19 |         "type": "string"
20 |       }
21 |     },
22 |     "required": [
23 |       "owner",
24 |       "repo",
25 |       "pullNumber"
26 |     ],
27 |     "type": "object"
28 |   },
29 |   "name": "request_copilot_review"
30 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/search_code.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Search code",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Search for code across GitHub repositories",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "order": {
10 |         "description": "Sort order",
11 |         "enum": [
12 |           "asc",
13 |           "desc"
14 |         ],
15 |         "type": "string"
16 |       },
17 |       "page": {
18 |         "description": "Page number for pagination (min 1)",
19 |         "minimum": 1,
20 |         "type": "number"
21 |       },
22 |       "perPage": {
23 |         "description": "Results per page for pagination (min 1, max 100)",
24 |         "maximum": 100,
25 |         "minimum": 1,
26 |         "type": "number"
27 |       },
28 |       "q": {
29 |         "description": "Search query using GitHub code search syntax",
30 |         "type": "string"
31 |       },
32 |       "sort": {
33 |         "description": "Sort field ('indexed' only)",
34 |         "type": "string"
35 |       }
36 |     },
37 |     "required": [
38 |       "q"
39 |     ],
40 |     "type": "object"
41 |   },
42 |   "name": "search_code"
43 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/search_issues.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Search issues",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Search for issues in GitHub repositories using issues search syntax already scoped to is:issue",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "order": {
10 |         "description": "Sort order",
11 |         "enum": [
12 |           "asc",
13 |           "desc"
14 |         ],
15 |         "type": "string"
16 |       },
17 |       "owner": {
18 |         "description": "Optional repository owner. If provided with repo, only notifications for this repository are listed.",
19 |         "type": "string"
20 |       },
21 |       "page": {
22 |         "description": "Page number for pagination (min 1)",
23 |         "minimum": 1,
24 |         "type": "number"
25 |       },
26 |       "perPage": {
27 |         "description": "Results per page for pagination (min 1, max 100)",
28 |         "maximum": 100,
29 |         "minimum": 1,
30 |         "type": "number"
31 |       },
32 |       "query": {
33 |         "description": "Search query using GitHub issues search syntax",
34 |         "type": "string"
35 |       },
36 |       "repo": {
37 |         "description": "Optional repository name. If provided with owner, only notifications for this repository are listed.",
38 |         "type": "string"
39 |       },
40 |       "sort": {
41 |         "description": "Sort field by number of matches of categories, defaults to best match",
42 |         "enum": [
43 |           "comments",
44 |           "reactions",
45 |           "reactions-+1",
46 |           "reactions--1",
47 |           "reactions-smile",
48 |           "reactions-thinking_face",
49 |           "reactions-heart",
50 |           "reactions-tada",
51 |           "interactions",
52 |           "created",
53 |           "updated"
54 |         ],
55 |         "type": "string"
56 |       }
57 |     },
58 |     "required": [
59 |       "query"
60 |     ],
61 |     "type": "object"
62 |   },
63 |   "name": "search_issues"
64 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/search_pull_requests.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Search pull requests",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Search for pull requests in GitHub repositories using issues search syntax already scoped to is:pr",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "order": {
10 |         "description": "Sort order",
11 |         "enum": [
12 |           "asc",
13 |           "desc"
14 |         ],
15 |         "type": "string"
16 |       },
17 |       "owner": {
18 |         "description": "Optional repository owner. If provided with repo, only notifications for this repository are listed.",
19 |         "type": "string"
20 |       },
21 |       "page": {
22 |         "description": "Page number for pagination (min 1)",
23 |         "minimum": 1,
24 |         "type": "number"
25 |       },
26 |       "perPage": {
27 |         "description": "Results per page for pagination (min 1, max 100)",
28 |         "maximum": 100,
29 |         "minimum": 1,
30 |         "type": "number"
31 |       },
32 |       "query": {
33 |         "description": "Search query using GitHub pull request search syntax",
34 |         "type": "string"
35 |       },
36 |       "repo": {
37 |         "description": "Optional repository name. If provided with owner, only notifications for this repository are listed.",
38 |         "type": "string"
39 |       },
40 |       "sort": {
41 |         "description": "Sort field by number of matches of categories, defaults to best match",
42 |         "enum": [
43 |           "comments",
44 |           "reactions",
45 |           "reactions-+1",
46 |           "reactions--1",
47 |           "reactions-smile",
48 |           "reactions-thinking_face",
49 |           "reactions-heart",
50 |           "reactions-tada",
51 |           "interactions",
52 |           "created",
53 |           "updated"
54 |         ],
55 |         "type": "string"
56 |       }
57 |     },
58 |     "required": [
59 |       "query"
60 |     ],
61 |     "type": "object"
62 |   },
63 |   "name": "search_pull_requests"
64 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/search_repositories.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Search repositories",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Search for GitHub repositories",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "page": {
10 |         "description": "Page number for pagination (min 1)",
11 |         "minimum": 1,
12 |         "type": "number"
13 |       },
14 |       "perPage": {
15 |         "description": "Results per page for pagination (min 1, max 100)",
16 |         "maximum": 100,
17 |         "minimum": 1,
18 |         "type": "number"
19 |       },
20 |       "query": {
21 |         "description": "Search query",
22 |         "type": "string"
23 |       }
24 |     },
25 |     "required": [
26 |       "query"
27 |     ],
28 |     "type": "object"
29 |   },
30 |   "name": "search_repositories"
31 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/search_users.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Search users",
 4 |     "readOnlyHint": true
 5 |   },
 6 |   "description": "Search for GitHub users exclusively",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "order": {
10 |         "description": "Sort order",
11 |         "enum": [
12 |           "asc",
13 |           "desc"
14 |         ],
15 |         "type": "string"
16 |       },
17 |       "page": {
18 |         "description": "Page number for pagination (min 1)",
19 |         "minimum": 1,
20 |         "type": "number"
21 |       },
22 |       "perPage": {
23 |         "description": "Results per page for pagination (min 1, max 100)",
24 |         "maximum": 100,
25 |         "minimum": 1,
26 |         "type": "number"
27 |       },
28 |       "query": {
29 |         "description": "Search query using GitHub users search syntax scoped to type:user",
30 |         "type": "string"
31 |       },
32 |       "sort": {
33 |         "description": "Sort field by category",
34 |         "enum": [
35 |           "followers",
36 |           "repositories",
37 |           "joined"
38 |         ],
39 |         "type": "string"
40 |       }
41 |     },
42 |     "required": [
43 |       "query"
44 |     ],
45 |     "type": "object"
46 |   },
47 |   "name": "search_users"
48 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/submit_pending_pull_request_review.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Submit the requester's latest pending pull request review",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Submit the requester's latest pending pull request review, normally this is a final step after creating a pending review, adding comments first, unless you know that the user already did the first two steps, you should check before calling this.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "body": {
10 |         "description": "The text of the review comment",
11 |         "type": "string"
12 |       },
13 |       "event": {
14 |         "description": "The event to perform",
15 |         "enum": [
16 |           "APPROVE",
17 |           "REQUEST_CHANGES",
18 |           "COMMENT"
19 |         ],
20 |         "type": "string"
21 |       },
22 |       "owner": {
23 |         "description": "Repository owner",
24 |         "type": "string"
25 |       },
26 |       "pullNumber": {
27 |         "description": "Pull request number",
28 |         "type": "number"
29 |       },
30 |       "repo": {
31 |         "description": "Repository name",
32 |         "type": "string"
33 |       }
34 |     },
35 |     "required": [
36 |       "owner",
37 |       "repo",
38 |       "pullNumber",
39 |       "event"
40 |     ],
41 |     "type": "object"
42 |   },
43 |   "name": "submit_pending_pull_request_review"
44 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/update_issue.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Edit issue",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Update an existing issue in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "assignees": {
10 |         "description": "New assignees",
11 |         "items": {
12 |           "type": "string"
13 |         },
14 |         "type": "array"
15 |       },
16 |       "body": {
17 |         "description": "New description",
18 |         "type": "string"
19 |       },
20 |       "issue_number": {
21 |         "description": "Issue number to update",
22 |         "type": "number"
23 |       },
24 |       "labels": {
25 |         "description": "New labels",
26 |         "items": {
27 |           "type": "string"
28 |         },
29 |         "type": "array"
30 |       },
31 |       "milestone": {
32 |         "description": "New milestone number",
33 |         "type": "number"
34 |       },
35 |       "owner": {
36 |         "description": "Repository owner",
37 |         "type": "string"
38 |       },
39 |       "repo": {
40 |         "description": "Repository name",
41 |         "type": "string"
42 |       },
43 |       "state": {
44 |         "description": "New state",
45 |         "enum": [
46 |           "open",
47 |           "closed"
48 |         ],
49 |         "type": "string"
50 |       },
51 |       "title": {
52 |         "description": "New title",
53 |         "type": "string"
54 |       }
55 |     },
56 |     "required": [
57 |       "owner",
58 |       "repo",
59 |       "issue_number"
60 |     ],
61 |     "type": "object"
62 |   },
63 |   "name": "update_issue"
64 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/update_pull_request.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Edit pull request",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Update an existing pull request in a GitHub repository.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "base": {
10 |         "description": "New base branch name",
11 |         "type": "string"
12 |       },
13 |       "body": {
14 |         "description": "New description",
15 |         "type": "string"
16 |       },
17 |       "maintainer_can_modify": {
18 |         "description": "Allow maintainer edits",
19 |         "type": "boolean"
20 |       },
21 |       "owner": {
22 |         "description": "Repository owner",
23 |         "type": "string"
24 |       },
25 |       "pullNumber": {
26 |         "description": "Pull request number to update",
27 |         "type": "number"
28 |       },
29 |       "repo": {
30 |         "description": "Repository name",
31 |         "type": "string"
32 |       },
33 |       "state": {
34 |         "description": "New state",
35 |         "enum": [
36 |           "open",
37 |           "closed"
38 |         ],
39 |         "type": "string"
40 |       },
41 |       "title": {
42 |         "description": "New title",
43 |         "type": "string"
44 |       }
45 |     },
46 |     "required": [
47 |       "owner",
48 |       "repo",
49 |       "pullNumber"
50 |     ],
51 |     "type": "object"
52 |   },
53 |   "name": "update_pull_request"
54 | }


--------------------------------------------------------------------------------
/pkg/github/__toolsnaps__/update_pull_request_branch.snap:
--------------------------------------------------------------------------------
 1 | {
 2 |   "annotations": {
 3 |     "title": "Update pull request branch",
 4 |     "readOnlyHint": false
 5 |   },
 6 |   "description": "Update the branch of a pull request with the latest changes from the base branch.",
 7 |   "inputSchema": {
 8 |     "properties": {
 9 |       "expectedHeadSha": {
10 |         "description": "The expected SHA of the pull request's HEAD ref",
11 |         "type": "string"
12 |       },
13 |       "owner": {
14 |         "description": "Repository owner",
15 |         "type": "string"
16 |       },
17 |       "pullNumber": {
18 |         "description": "Pull request number",
19 |         "type": "number"
20 |       },
21 |       "repo": {
22 |         "description": "Repository name",
23 |         "type": "string"
24 |       }
25 |     },
26 |     "required": [
27 |       "owner",
28 |       "repo",
29 |       "pullNumber"
30 |     ],
31 |     "type": "object"
32 |   },
33 |   "name": "update_pull_request_branch"
34 | }


--------------------------------------------------------------------------------
/pkg/github/context_tools.go:
--------------------------------------------------------------------------------
 1 | package github
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"time"
 6 | 
 7 | 	ghErrors "github.com/github/github-mcp-server/pkg/errors"
 8 | 	"github.com/github/github-mcp-server/pkg/translations"
 9 | 	"github.com/mark3labs/mcp-go/mcp"
10 | 	"github.com/mark3labs/mcp-go/server"
11 | )
12 | 
13 | // UserDetails contains additional fields about a GitHub user not already
14 | // present in MinimalUser. Used by get_me context tool but omitted from search_users.
15 | type UserDetails struct {
16 | 	Name              string    `json:"name,omitempty"`
17 | 	Company           string    `json:"company,omitempty"`
18 | 	Blog              string    `json:"blog,omitempty"`
19 | 	Location          string    `json:"location,omitempty"`
20 | 	Email             string    `json:"email,omitempty"`
21 | 	Hireable          bool      `json:"hireable,omitempty"`
22 | 	Bio               string    `json:"bio,omitempty"`
23 | 	TwitterUsername   string    `json:"twitter_username,omitempty"`
24 | 	PublicRepos       int       `json:"public_repos"`
25 | 	PublicGists       int       `json:"public_gists"`
26 | 	Followers         int       `json:"followers"`
27 | 	Following         int       `json:"following"`
28 | 	CreatedAt         time.Time `json:"created_at"`
29 | 	UpdatedAt         time.Time `json:"updated_at"`
30 | 	PrivateGists      int       `json:"private_gists,omitempty"`
31 | 	TotalPrivateRepos int64     `json:"total_private_repos,omitempty"`
32 | 	OwnedPrivateRepos int64     `json:"owned_private_repos,omitempty"`
33 | }
34 | 
35 | // GetMe creates a tool to get details of the authenticated user.
36 | func GetMe(getClient GetClientFn, t translations.TranslationHelperFunc) (mcp.Tool, server.ToolHandlerFunc) {
37 | 	tool := mcp.NewTool("get_me",
38 | 		mcp.WithDescription(t("TOOL_GET_ME_DESCRIPTION", "Get details of the authenticated GitHub user. Use this when a request is about the user's own profile for GitHub. Or when information is missing to build other tool calls.")),
39 | 		mcp.WithToolAnnotation(mcp.ToolAnnotation{
40 | 			Title:        t("TOOL_GET_ME_USER_TITLE", "Get my user profile"),
41 | 			ReadOnlyHint: ToBoolPtr(true),
42 | 		}),
43 | 	)
44 | 
45 | 	type args struct{}
46 | 	handler := mcp.NewTypedToolHandler(func(ctx context.Context, _ mcp.CallToolRequest, _ args) (*mcp.CallToolResult, error) {
47 | 		client, err := getClient(ctx)
48 | 		if err != nil {
49 | 			return mcp.NewToolResultErrorFromErr("failed to get GitHub client", err), nil
50 | 		}
51 | 
52 | 		user, res, err := client.Users.Get(ctx, "")
53 | 		if err != nil {
54 | 			return ghErrors.NewGitHubAPIErrorResponse(ctx,
55 | 				"failed to get user",
56 | 				res,
57 | 				err,
58 | 			), nil
59 | 		}
60 | 
61 | 		// Create minimal user representation instead of returning full user object
62 | 		minimalUser := MinimalUser{
63 | 			Login:      user.GetLogin(),
64 | 			ID:         user.GetID(),
65 | 			ProfileURL: user.GetHTMLURL(),
66 | 			AvatarURL:  user.GetAvatarURL(),
67 | 			Details: &UserDetails{
68 | 				Name:              user.GetName(),
69 | 				Company:           user.GetCompany(),
70 | 				Blog:              user.GetBlog(),
71 | 				Location:          user.GetLocation(),
72 | 				Email:             user.GetEmail(),
73 | 				Hireable:          user.GetHireable(),
74 | 				Bio:               user.GetBio(),
75 | 				TwitterUsername:   user.GetTwitterUsername(),
76 | 				PublicRepos:       user.GetPublicRepos(),
77 | 				PublicGists:       user.GetPublicGists(),
78 | 				Followers:         user.GetFollowers(),
79 | 				Following:         user.GetFollowing(),
80 | 				CreatedAt:         user.GetCreatedAt().Time,
81 | 				UpdatedAt:         user.GetUpdatedAt().Time,
82 | 				PrivateGists:      user.GetPrivateGists(),
83 | 				TotalPrivateRepos: user.GetTotalPrivateRepos(),
84 | 				OwnedPrivateRepos: user.GetOwnedPrivateRepos(),
85 | 			},
86 | 		}
87 | 
88 | 		return MarshalledTextResult(minimalUser), nil
89 | 	})
90 | 
91 | 	return tool, handler
92 | }
93 | 


--------------------------------------------------------------------------------
/pkg/github/context_tools_test.go:
--------------------------------------------------------------------------------
  1 | package github
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"encoding/json"
  6 | 	"testing"
  7 | 	"time"
  8 | 
  9 | 	"github.com/github/github-mcp-server/internal/toolsnaps"
 10 | 	"github.com/github/github-mcp-server/pkg/translations"
 11 | 	"github.com/google/go-github/v73/github"
 12 | 	"github.com/migueleliasweb/go-github-mock/src/mock"
 13 | 	"github.com/stretchr/testify/assert"
 14 | 	"github.com/stretchr/testify/require"
 15 | )
 16 | 
 17 | func Test_GetMe(t *testing.T) {
 18 | 	t.Parallel()
 19 | 
 20 | 	tool, _ := GetMe(nil, translations.NullTranslationHelper)
 21 | 	require.NoError(t, toolsnaps.Test(tool.Name, tool))
 22 | 
 23 | 	// Verify some basic very important properties
 24 | 	assert.Equal(t, "get_me", tool.Name)
 25 | 	assert.True(t, *tool.Annotations.ReadOnlyHint, "get_me tool should be read-only")
 26 | 
 27 | 	// Setup mock user response
 28 | 	mockUser := &github.User{
 29 | 		Login:           github.Ptr("testuser"),
 30 | 		Name:            github.Ptr("Test User"),
 31 | 		Email:           github.Ptr("test@example.com"),
 32 | 		Bio:             github.Ptr("GitHub user for testing"),
 33 | 		Company:         github.Ptr("Test Company"),
 34 | 		Location:        github.Ptr("Test Location"),
 35 | 		HTMLURL:         github.Ptr("https://github.com/testuser"),
 36 | 		CreatedAt:       &github.Timestamp{Time: time.Now().Add(-365 * 24 * time.Hour)},
 37 | 		Type:            github.Ptr("User"),
 38 | 		Hireable:        github.Ptr(true),
 39 | 		TwitterUsername: github.Ptr("testuser_twitter"),
 40 | 		Plan: &github.Plan{
 41 | 			Name: github.Ptr("pro"),
 42 | 		},
 43 | 	}
 44 | 
 45 | 	tests := []struct {
 46 | 		name               string
 47 | 		stubbedGetClientFn GetClientFn
 48 | 		requestArgs        map[string]any
 49 | 		expectToolError    bool
 50 | 		expectedUser       *github.User
 51 | 		expectedToolErrMsg string
 52 | 	}{
 53 | 		{
 54 | 			name: "successful get user",
 55 | 			stubbedGetClientFn: stubGetClientFromHTTPFn(
 56 | 				mock.NewMockedHTTPClient(
 57 | 					mock.WithRequestMatch(
 58 | 						mock.GetUser,
 59 | 						mockUser,
 60 | 					),
 61 | 				),
 62 | 			),
 63 | 			requestArgs:     map[string]any{},
 64 | 			expectToolError: false,
 65 | 			expectedUser:    mockUser,
 66 | 		},
 67 | 		{
 68 | 			name: "successful get user with reason",
 69 | 			stubbedGetClientFn: stubGetClientFromHTTPFn(
 70 | 				mock.NewMockedHTTPClient(
 71 | 					mock.WithRequestMatch(
 72 | 						mock.GetUser,
 73 | 						mockUser,
 74 | 					),
 75 | 				),
 76 | 			),
 77 | 			requestArgs: map[string]any{
 78 | 				"reason": "Testing API",
 79 | 			},
 80 | 			expectToolError: false,
 81 | 			expectedUser:    mockUser,
 82 | 		},
 83 | 		{
 84 | 			name:               "getting client fails",
 85 | 			stubbedGetClientFn: stubGetClientFnErr("expected test error"),
 86 | 			requestArgs:        map[string]any{},
 87 | 			expectToolError:    true,
 88 | 			expectedToolErrMsg: "failed to get GitHub client: expected test error",
 89 | 		},
 90 | 		{
 91 | 			name: "get user fails",
 92 | 			stubbedGetClientFn: stubGetClientFromHTTPFn(
 93 | 				mock.NewMockedHTTPClient(
 94 | 					mock.WithRequestMatchHandler(
 95 | 						mock.GetUser,
 96 | 						badRequestHandler("expected test failure"),
 97 | 					),
 98 | 				),
 99 | 			),
100 | 			requestArgs:        map[string]any{},
101 | 			expectToolError:    true,
102 | 			expectedToolErrMsg: "expected test failure",
103 | 		},
104 | 	}
105 | 
106 | 	for _, tc := range tests {
107 | 		t.Run(tc.name, func(t *testing.T) {
108 | 			_, handler := GetMe(tc.stubbedGetClientFn, translations.NullTranslationHelper)
109 | 
110 | 			request := createMCPRequest(tc.requestArgs)
111 | 			result, err := handler(context.Background(), request)
112 | 			require.NoError(t, err)
113 | 			textContent := getTextResult(t, result)
114 | 
115 | 			if tc.expectToolError {
116 | 				assert.True(t, result.IsError, "expected tool call result to be an error")
117 | 				assert.Contains(t, textContent.Text, tc.expectedToolErrMsg)
118 | 				return
119 | 			}
120 | 
121 | 			// Unmarshal and verify the result
122 | 			var returnedUser MinimalUser
123 | 			err = json.Unmarshal([]byte(textContent.Text), &returnedUser)
124 | 			require.NoError(t, err)
125 | 
126 | 			// Verify minimal user details
127 | 			assert.Equal(t, *tc.expectedUser.Login, returnedUser.Login)
128 | 			assert.Equal(t, *tc.expectedUser.HTMLURL, returnedUser.ProfileURL)
129 | 
130 | 			// Verify user details
131 | 			require.NotNil(t, returnedUser.Details)
132 | 			assert.Equal(t, *tc.expectedUser.Name, returnedUser.Details.Name)
133 | 			assert.Equal(t, *tc.expectedUser.Email, returnedUser.Details.Email)
134 | 			assert.Equal(t, *tc.expectedUser.Bio, returnedUser.Details.Bio)
135 | 			assert.Equal(t, *tc.expectedUser.Company, returnedUser.Details.Company)
136 | 			assert.Equal(t, *tc.expectedUser.Location, returnedUser.Details.Location)
137 | 			assert.Equal(t, *tc.expectedUser.Hireable, returnedUser.Details.Hireable)
138 | 			assert.Equal(t, *tc.expectedUser.TwitterUsername, returnedUser.Details.TwitterUsername)
139 | 		})
140 | 	}
141 | }
142 | 


--------------------------------------------------------------------------------
/pkg/github/search_utils.go:
--------------------------------------------------------------------------------
 1 | package github
 2 | 
 3 | import (
 4 | 	"context"
 5 | 	"encoding/json"
 6 | 	"fmt"
 7 | 	"io"
 8 | 	"net/http"
 9 | 
10 | 	"github.com/google/go-github/v73/github"
11 | 	"github.com/mark3labs/mcp-go/mcp"
12 | )
13 | 
14 | func searchHandler(
15 | 	ctx context.Context,
16 | 	getClient GetClientFn,
17 | 	request mcp.CallToolRequest,
18 | 	searchType string,
19 | 	errorPrefix string,
20 | ) (*mcp.CallToolResult, error) {
21 | 	query, err := RequiredParam[string](request, "query")
22 | 	if err != nil {
23 | 		return mcp.NewToolResultError(err.Error()), nil
24 | 	}
25 | 	query = fmt.Sprintf("is:%s %s", searchType, query)
26 | 
27 | 	owner, err := OptionalParam[string](request, "owner")
28 | 	if err != nil {
29 | 		return mcp.NewToolResultError(err.Error()), nil
30 | 	}
31 | 
32 | 	repo, err := OptionalParam[string](request, "repo")
33 | 	if err != nil {
34 | 		return mcp.NewToolResultError(err.Error()), nil
35 | 	}
36 | 
37 | 	if owner != "" && repo != "" {
38 | 		query = fmt.Sprintf("repo:%s/%s %s", owner, repo, query)
39 | 	}
40 | 
41 | 	sort, err := OptionalParam[string](request, "sort")
42 | 	if err != nil {
43 | 		return mcp.NewToolResultError(err.Error()), nil
44 | 	}
45 | 	order, err := OptionalParam[string](request, "order")
46 | 	if err != nil {
47 | 		return mcp.NewToolResultError(err.Error()), nil
48 | 	}
49 | 	pagination, err := OptionalPaginationParams(request)
50 | 	if err != nil {
51 | 		return mcp.NewToolResultError(err.Error()), nil
52 | 	}
53 | 
54 | 	opts := &github.SearchOptions{
55 | 		// Default to "created" if no sort is provided, as it's a common use case.
56 | 		Sort:  sort,
57 | 		Order: order,
58 | 		ListOptions: github.ListOptions{
59 | 			Page:    pagination.page,
60 | 			PerPage: pagination.perPage,
61 | 		},
62 | 	}
63 | 
64 | 	client, err := getClient(ctx)
65 | 	if err != nil {
66 | 		return nil, fmt.Errorf("%s: failed to get GitHub client: %w", errorPrefix, err)
67 | 	}
68 | 	result, resp, err := client.Search.Issues(ctx, query, opts)
69 | 	if err != nil {
70 | 		return nil, fmt.Errorf("%s: %w", errorPrefix, err)
71 | 	}
72 | 	defer func() { _ = resp.Body.Close() }()
73 | 
74 | 	if resp.StatusCode != http.StatusOK {
75 | 		body, err := io.ReadAll(resp.Body)
76 | 		if err != nil {
77 | 			return nil, fmt.Errorf("%s: failed to read response body: %w", errorPrefix, err)
78 | 		}
79 | 		return mcp.NewToolResultError(fmt.Sprintf("%s: %s", errorPrefix, string(body))), nil
80 | 	}
81 | 
82 | 	r, err := json.Marshal(result)
83 | 	if err != nil {
84 | 		return nil, fmt.Errorf("%s: failed to marshal response: %w", errorPrefix, err)
85 | 	}
86 | 
87 | 	return mcp.NewToolResultText(string(r)), nil
88 | }
89 | 


--------------------------------------------------------------------------------
/pkg/log/io.go:
--------------------------------------------------------------------------------
 1 | package log
 2 | 
 3 | import (
 4 | 	"io"
 5 | 
 6 | 	log "github.com/sirupsen/logrus"
 7 | )
 8 | 
 9 | // IOLogger is a wrapper around io.Reader and io.Writer that can be used
10 | // to log the data being read and written from the underlying streams
11 | type IOLogger struct {
12 | 	reader io.Reader
13 | 	writer io.Writer
14 | 	logger *log.Logger
15 | }
16 | 
17 | // NewIOLogger creates a new IOLogger instance
18 | func NewIOLogger(r io.Reader, w io.Writer, logger *log.Logger) *IOLogger {
19 | 	return &IOLogger{
20 | 		reader: r,
21 | 		writer: w,
22 | 		logger: logger,
23 | 	}
24 | }
25 | 
26 | // Read reads data from the underlying io.Reader and logs it.
27 | func (l *IOLogger) Read(p []byte) (n int, err error) {
28 | 	if l.reader == nil {
29 | 		return 0, io.EOF
30 | 	}
31 | 	n, err = l.reader.Read(p)
32 | 	if n > 0 {
33 | 		l.logger.Infof("[stdin]: received %d bytes: %s", n, string(p[:n]))
34 | 	}
35 | 	return n, err
36 | }
37 | 
38 | // Write writes data to the underlying io.Writer and logs it.
39 | func (l *IOLogger) Write(p []byte) (n int, err error) {
40 | 	if l.writer == nil {
41 | 		return 0, io.ErrClosedPipe
42 | 	}
43 | 	l.logger.Infof("[stdout]: sending %d bytes: %s", len(p), string(p))
44 | 	return l.writer.Write(p)
45 | }
46 | 


--------------------------------------------------------------------------------
/pkg/log/io_test.go:
--------------------------------------------------------------------------------
 1 | package log
 2 | 
 3 | import (
 4 | 	"bytes"
 5 | 	"strings"
 6 | 	"testing"
 7 | 
 8 | 	log "github.com/sirupsen/logrus"
 9 | 	"github.com/stretchr/testify/assert"
10 | )
11 | 
12 | func TestLoggedReadWriter(t *testing.T) {
13 | 	t.Run("Read method logs and passes data", func(t *testing.T) {
14 | 		// Setup
15 | 		inputData := "test input data"
16 | 		reader := strings.NewReader(inputData)
17 | 
18 | 		// Create logger with buffer to capture output
19 | 		var logBuffer bytes.Buffer
20 | 		logger := log.New()
21 | 		logger.SetOutput(&logBuffer)
22 | 		logger.SetFormatter(&log.TextFormatter{
23 | 			DisableTimestamp: true,
24 | 		})
25 | 
26 | 		lrw := NewIOLogger(reader, nil, logger)
27 | 
28 | 		// Test Read
29 | 		buf := make([]byte, 100)
30 | 		n, err := lrw.Read(buf)
31 | 
32 | 		// Assertions
33 | 		assert.NoError(t, err)
34 | 		assert.Equal(t, len(inputData), n)
35 | 		assert.Equal(t, inputData, string(buf[:n]))
36 | 		assert.Contains(t, logBuffer.String(), "[stdin]")
37 | 		assert.Contains(t, logBuffer.String(), inputData)
38 | 	})
39 | 
40 | 	t.Run("Write method logs and passes data", func(t *testing.T) {
41 | 		// Setup
42 | 		outputData := "test output data"
43 | 		var writeBuffer bytes.Buffer
44 | 
45 | 		// Create logger with buffer to capture output
46 | 		var logBuffer bytes.Buffer
47 | 		logger := log.New()
48 | 		logger.SetOutput(&logBuffer)
49 | 		logger.SetFormatter(&log.TextFormatter{
50 | 			DisableTimestamp: true,
51 | 		})
52 | 
53 | 		lrw := NewIOLogger(nil, &writeBuffer, logger)
54 | 
55 | 		// Test Write
56 | 		n, err := lrw.Write([]byte(outputData))
57 | 
58 | 		// Assertions
59 | 		assert.NoError(t, err)
60 | 		assert.Equal(t, len(outputData), n)
61 | 		assert.Equal(t, outputData, writeBuffer.String())
62 | 		assert.Contains(t, logBuffer.String(), "[stdout]")
63 | 		assert.Contains(t, logBuffer.String(), outputData)
64 | 	})
65 | }
66 | 


--------------------------------------------------------------------------------
/pkg/raw/raw.go:
--------------------------------------------------------------------------------
 1 | // Package raw provides a client for interacting with the GitHub raw file API
 2 | package raw
 3 | 
 4 | import (
 5 | 	"context"
 6 | 	"net/http"
 7 | 	"net/url"
 8 | 
 9 | 	gogithub "github.com/google/go-github/v73/github"
10 | )
11 | 
12 | // GetRawClientFn is a function type that returns a RawClient instance.
13 | type GetRawClientFn func(context.Context) (*Client, error)
14 | 
15 | // Client is a client for interacting with the GitHub raw content API.
16 | type Client struct {
17 | 	url    *url.URL
18 | 	client *gogithub.Client
19 | }
20 | 
21 | // NewClient creates a new instance of the raw API Client with the provided GitHub client and provided URL.
22 | func NewClient(client *gogithub.Client, rawURL *url.URL) *Client {
23 | 	client = gogithub.NewClient(client.Client())
24 | 	client.BaseURL = rawURL
25 | 	return &Client{client: client, url: rawURL}
26 | }
27 | 
28 | func (c *Client) newRequest(ctx context.Context, method string, urlStr string, body interface{}, opts ...gogithub.RequestOption) (*http.Request, error) {
29 | 	req, err := c.client.NewRequest(method, urlStr, body, opts...)
30 | 	if err != nil {
31 | 		return nil, err
32 | 	}
33 | 	req = req.WithContext(ctx)
34 | 	return req, nil
35 | }
36 | 
37 | func (c *Client) refURL(owner, repo, ref, path string) string {
38 | 	if ref == "" {
39 | 		return c.url.JoinPath(owner, repo, "HEAD", path).String()
40 | 	}
41 | 	return c.url.JoinPath(owner, repo, ref, path).String()
42 | }
43 | 
44 | func (c *Client) URLFromOpts(opts *ContentOpts, owner, repo, path string) string {
45 | 	if opts == nil {
46 | 		opts = &ContentOpts{}
47 | 	}
48 | 	if opts.SHA != "" {
49 | 		return c.commitURL(owner, repo, opts.SHA, path)
50 | 	}
51 | 	return c.refURL(owner, repo, opts.Ref, path)
52 | }
53 | 
54 | // BlobURL returns the URL for a blob in the raw content API.
55 | func (c *Client) commitURL(owner, repo, sha, path string) string {
56 | 	return c.url.JoinPath(owner, repo, sha, path).String()
57 | }
58 | 
59 | type ContentOpts struct {
60 | 	Ref string
61 | 	SHA string
62 | }
63 | 
64 | // GetRawContent fetches the raw content of a file from a GitHub repository.
65 | func (c *Client) GetRawContent(ctx context.Context, owner, repo, path string, opts *ContentOpts) (*http.Response, error) {
66 | 	url := c.URLFromOpts(opts, owner, repo, path)
67 | 	req, err := c.newRequest(ctx, "GET", url, nil)
68 | 	if err != nil {
69 | 		return nil, err
70 | 	}
71 | 
72 | 	return c.client.Client().Do(req)
73 | }
74 | 


--------------------------------------------------------------------------------
/pkg/raw/raw_mock.go:
--------------------------------------------------------------------------------
 1 | package raw
 2 | 
 3 | import "github.com/migueleliasweb/go-github-mock/src/mock"
 4 | 
 5 | var GetRawReposContentsByOwnerByRepoByPath mock.EndpointPattern = mock.EndpointPattern{
 6 | 	Pattern: "/{owner}/{repo}/HEAD/{path:.*}",
 7 | 	Method:  "GET",
 8 | }
 9 | var GetRawReposContentsByOwnerByRepoByBranchByPath mock.EndpointPattern = mock.EndpointPattern{
10 | 	Pattern: "/{owner}/{repo}/refs/heads/{branch}/{path:.*}",
11 | 	Method:  "GET",
12 | }
13 | var GetRawReposContentsByOwnerByRepoByTagByPath mock.EndpointPattern = mock.EndpointPattern{
14 | 	Pattern: "/{owner}/{repo}/refs/tags/{tag}/{path:.*}",
15 | 	Method:  "GET",
16 | }
17 | var GetRawReposContentsByOwnerByRepoBySHAByPath mock.EndpointPattern = mock.EndpointPattern{
18 | 	Pattern: "/{owner}/{repo}/{sha}/{path:.*}",
19 | 	Method:  "GET",
20 | }
21 | 


--------------------------------------------------------------------------------
/pkg/raw/raw_test.go:
--------------------------------------------------------------------------------
  1 | package raw
  2 | 
  3 | import (
  4 | 	"context"
  5 | 	"net/http"
  6 | 	"net/url"
  7 | 	"testing"
  8 | 
  9 | 	"github.com/google/go-github/v73/github"
 10 | 	"github.com/migueleliasweb/go-github-mock/src/mock"
 11 | 	"github.com/stretchr/testify/require"
 12 | )
 13 | 
 14 | func TestGetRawContent(t *testing.T) {
 15 | 	base, _ := url.Parse("https://raw.example.com/")
 16 | 
 17 | 	tests := []struct {
 18 | 		name              string
 19 | 		pattern           mock.EndpointPattern
 20 | 		opts              *ContentOpts
 21 | 		owner, repo, path string
 22 | 		statusCode        int
 23 | 		contentType       string
 24 | 		body              string
 25 | 		expectError       string
 26 | 	}{
 27 | 		{
 28 | 			name:    "HEAD fetch success",
 29 | 			pattern: GetRawReposContentsByOwnerByRepoByPath,
 30 | 			opts:    nil,
 31 | 			owner:   "octocat", repo: "hello", path: "README.md",
 32 | 			statusCode:  200,
 33 | 			contentType: "text/plain",
 34 | 			body:        "# Test file",
 35 | 		},
 36 | 		{
 37 | 			name:    "branch fetch success",
 38 | 			pattern: GetRawReposContentsByOwnerByRepoByBranchByPath,
 39 | 			opts:    &ContentOpts{Ref: "refs/heads/main"},
 40 | 			owner:   "octocat", repo: "hello", path: "README.md",
 41 | 			statusCode:  200,
 42 | 			contentType: "text/plain",
 43 | 			body:        "# Test file",
 44 | 		},
 45 | 		{
 46 | 			name:    "tag fetch success",
 47 | 			pattern: GetRawReposContentsByOwnerByRepoByTagByPath,
 48 | 			opts:    &ContentOpts{Ref: "refs/tags/v1.0.0"},
 49 | 			owner:   "octocat", repo: "hello", path: "README.md",
 50 | 			statusCode:  200,
 51 | 			contentType: "text/plain",
 52 | 			body:        "# Test file",
 53 | 		},
 54 | 		{
 55 | 			name:    "sha fetch success",
 56 | 			pattern: GetRawReposContentsByOwnerByRepoBySHAByPath,
 57 | 			opts:    &ContentOpts{SHA: "abc123"},
 58 | 			owner:   "octocat", repo: "hello", path: "README.md",
 59 | 			statusCode:  200,
 60 | 			contentType: "text/plain",
 61 | 			body:        "# Test file",
 62 | 		},
 63 | 		{
 64 | 			name:    "not found",
 65 | 			pattern: GetRawReposContentsByOwnerByRepoByPath,
 66 | 			opts:    nil,
 67 | 			owner:   "octocat", repo: "hello", path: "notfound.txt",
 68 | 			statusCode:  404,
 69 | 			contentType: "application/json",
 70 | 			body:        `{"message": "Not Found"}`,
 71 | 		},
 72 | 	}
 73 | 
 74 | 	for _, tc := range tests {
 75 | 		t.Run(tc.name, func(t *testing.T) {
 76 | 			mockedClient := mock.NewMockedHTTPClient(
 77 | 				mock.WithRequestMatchHandler(
 78 | 					tc.pattern,
 79 | 					http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
 80 | 						w.Header().Set("Content-Type", tc.contentType)
 81 | 						w.WriteHeader(tc.statusCode)
 82 | 						_, err := w.Write([]byte(tc.body))
 83 | 						require.NoError(t, err)
 84 | 					}),
 85 | 				),
 86 | 			)
 87 | 			ghClient := github.NewClient(mockedClient)
 88 | 			client := NewClient(ghClient, base)
 89 | 			resp, err := client.GetRawContent(context.Background(), tc.owner, tc.repo, tc.path, tc.opts)
 90 | 			defer func() {
 91 | 				_ = resp.Body.Close()
 92 | 			}()
 93 | 			if tc.expectError != "" {
 94 | 				require.Error(t, err)
 95 | 				return
 96 | 			}
 97 | 			require.NoError(t, err)
 98 | 			require.Equal(t, tc.statusCode, resp.StatusCode)
 99 | 		})
100 | 	}
101 | }
102 | 
103 | func TestUrlFromOpts(t *testing.T) {
104 | 	base, _ := url.Parse("https://raw.example.com/")
105 | 	ghClient := github.NewClient(nil)
106 | 	client := NewClient(ghClient, base)
107 | 
108 | 	tests := []struct {
109 | 		name  string
110 | 		opts  *ContentOpts
111 | 		owner string
112 | 		repo  string
113 | 		path  string
114 | 		want  string
115 | 	}{
116 | 		{
117 | 			name:  "no opts (HEAD)",
118 | 			opts:  nil,
119 | 			owner: "octocat", repo: "hello", path: "README.md",
120 | 			want: "https://raw.example.com/octocat/hello/HEAD/README.md",
121 | 		},
122 | 		{
123 | 			name:  "ref branch",
124 | 			opts:  &ContentOpts{Ref: "refs/heads/main"},
125 | 			owner: "octocat", repo: "hello", path: "README.md",
126 | 			want: "https://raw.example.com/octocat/hello/refs/heads/main/README.md",
127 | 		},
128 | 		{
129 | 			name:  "ref tag",
130 | 			opts:  &ContentOpts{Ref: "refs/tags/v1.0.0"},
131 | 			owner: "octocat", repo: "hello", path: "README.md",
132 | 			want: "https://raw.example.com/octocat/hello/refs/tags/v1.0.0/README.md",
133 | 		},
134 | 		{
135 | 			name:  "sha",
136 | 			opts:  &ContentOpts{SHA: "abc123"},
137 | 			owner: "octocat", repo: "hello", path: "README.md",
138 | 			want: "https://raw.example.com/octocat/hello/abc123/README.md",
139 | 		},
140 | 	}
141 | 
142 | 	for _, tt := range tests {
143 | 		t.Run(tt.name, func(t *testing.T) {
144 | 			got := client.URLFromOpts(tt.opts, tt.owner, tt.repo, tt.path)
145 | 			if got != tt.want {
146 | 				t.Errorf("UrlFromOpts() = %q, want %q", got, tt.want)
147 | 			}
148 | 		})
149 | 	}
150 | }
151 | 


--------------------------------------------------------------------------------
/pkg/translations/translations.go:
--------------------------------------------------------------------------------
 1 | package translations
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"fmt"
 6 | 	"log"
 7 | 	"os"
 8 | 	"strings"
 9 | 
10 | 	"github.com/spf13/viper"
11 | )
12 | 
13 | type TranslationHelperFunc func(key string, defaultValue string) string
14 | 
15 | func NullTranslationHelper(_ string, defaultValue string) string {
16 | 	return defaultValue
17 | }
18 | 
19 | func TranslationHelper() (TranslationHelperFunc, func()) {
20 | 	var translationKeyMap = map[string]string{}
21 | 	v := viper.New()
22 | 
23 | 	// Load from JSON file
24 | 	v.SetConfigName("github-mcp-server-config")
25 | 	v.SetConfigType("json")
26 | 	v.AddConfigPath(".")
27 | 
28 | 	if err := v.ReadInConfig(); err != nil {
29 | 		// ignore error if file not found as it is not required
30 | 		if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
31 | 			log.Printf("Could not read JSON config: %v", err)
32 | 		}
33 | 	}
34 | 
35 | 	// create a function that takes both a key, and a default value and returns either the default value or an override value
36 | 	return func(key string, defaultValue string) string {
37 | 			key = strings.ToUpper(key)
38 | 			if value, exists := translationKeyMap[key]; exists {
39 | 				return value
40 | 			}
41 | 			// check if the env var exists
42 | 			if value, exists := os.LookupEnv("GITHUB_MCP_" + key); exists {
43 | 				// TODO I could not get Viper to play ball reading the env var
44 | 				translationKeyMap[key] = value
45 | 				return value
46 | 			}
47 | 
48 | 			v.SetDefault(key, defaultValue)
49 | 			translationKeyMap[key] = v.GetString(key)
50 | 			return translationKeyMap[key]
51 | 		}, func() {
52 | 			// dump the translationKeyMap to a json file
53 | 			if err := DumpTranslationKeyMap(translationKeyMap); err != nil {
54 | 				log.Fatalf("Could not dump translation key map: %v", err)
55 | 			}
56 | 		}
57 | }
58 | 
59 | // DumpTranslationKeyMap writes the translation map to a json file called github-mcp-server-config.json
60 | func DumpTranslationKeyMap(translationKeyMap map[string]string) error {
61 | 	file, err := os.Create("github-mcp-server-config.json")
62 | 	if err != nil {
63 | 		return fmt.Errorf("error creating file: %v", err)
64 | 	}
65 | 	defer func() { _ = file.Close() }()
66 | 
67 | 	// marshal the map to json
68 | 	jsonData, err := json.MarshalIndent(translationKeyMap, "", "  ")
69 | 	if err != nil {
70 | 		return fmt.Errorf("error marshaling map to JSON: %v", err)
71 | 	}
72 | 
73 | 	// write the json data to the file
74 | 	if _, err := file.Write(jsonData); err != nil {
75 | 		return fmt.Errorf("error writing to file: %v", err)
76 | 	}
77 | 
78 | 	return nil
79 | }
80 | 


--------------------------------------------------------------------------------
/script/generate-docs:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | # This script generates documentation for the GitHub MCP server.
4 | # It needs to be run after tool updates to ensure the latest changes are reflected in the documentation. 
5 | go run ./cmd/github-mcp-server generate-docs


--------------------------------------------------------------------------------
/script/get-discussions:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | # echo '{"jsonrpc":"2.0","id":3,"params":{"name":"list_discussions","arguments": {"owner": "github", "repo": "securitylab", "first": 10, "since": "2025-04-01T00:00:00Z"}},"method":"tools/call"}' | go run  cmd/github-mcp-server/main.go stdio  | jq .
4 | echo '{"jsonrpc":"2.0","id":3,"params":{"name":"list_discussions","arguments": {"owner": "github", "repo": "securitylab", "first": 10, "since": "2025-04-01T00:00:00Z", "sort": "CREATED_AT", "direction": "DESC"}},"method":"tools/call"}' | go run  cmd/github-mcp-server/main.go stdio  | jq .
5 | 
6 | 


--------------------------------------------------------------------------------
/script/get-me:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | echo '{"jsonrpc":"2.0","id":3,"params":{"name":"get_me"},"method":"tools/call"}' | go run  cmd/github-mcp-server/main.go stdio  | jq .
4 | 


--------------------------------------------------------------------------------
/script/licenses:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | go install github.com/google/go-licenses@latest
 4 | 
 5 | rm -rf third-party
 6 | mkdir -p third-party
 7 | export TEMPDIR="$(mktemp -d)"
 8 | 
 9 | trap "rm -fr ${TEMPDIR}" EXIT
10 | 
11 | for goos in linux darwin windows ; do
12 |     # Note: we ignore warnings because we want the command to succeed, however the output should be checked
13 |     #       for any new warnings, and potentially we may need to add license information. 
14 |     #
15 |     #       Normally these warnings are packages containing non go code, which may or may not require explicit attribution,
16 |     #       depending on the license.
17 |     GOOS="${goos}" go-licenses save ./... --save_path="${TEMPDIR}/${goos}" --force || echo "Ignore warnings"
18 |     GOOS="${goos}" go-licenses report ./... --template .github/licenses.tmpl > third-party-licenses.${goos}.md || echo "Ignore warnings"
19 |     cp -fR "${TEMPDIR}/${goos}"/* third-party/
20 | done
21 | 
22 | 


--------------------------------------------------------------------------------
/script/licenses-check:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | go install github.com/google/go-licenses@latest
 4 | 
 5 | for goos in linux darwin windows ; do
 6 |     # Note: we ignore warnings because we want the command to succeed, however the output should be checked
 7 |     #       for any new warnings, and potentially we may need to add license information. 
 8 |     #
 9 |     #       Normally these warnings are packages containing non go code, which may or may not require explicit attribution,
10 |     #       depending on the license.
11 |     GOOS="${goos}" go-licenses report ./... --template .github/licenses.tmpl > third-party-licenses.${goos}.copy.md || echo "Ignore warnings"
12 |     if ! diff -s third-party-licenses.${goos}.copy.md third-party-licenses.${goos}.md; then
13 |         echo "License check failed.\n\nPlease update the license file by running \`.script/licenses\` and committing the output."
14 |         rm -f third-party-licenses.${goos}.copy.md
15 |         exit 1
16 |     fi
17 |     rm -f third-party-licenses.${goos}.copy.md
18 | done
19 | 
20 | 
21 | 
22 | 


--------------------------------------------------------------------------------
/script/lint:
--------------------------------------------------------------------------------
 1 | set -eu
 2 | 
 3 | # first run go fmt
 4 | gofmt -s -w .
 5 | 
 6 | BINDIR="$(git rev-parse --show-toplevel)"/bin
 7 | BINARY=$BINDIR/golangci-lint
 8 | GOLANGCI_LINT_VERSION=v2.2.1
 9 | 
10 | if [ ! -f "$BINARY" ]; then
11 |     curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s "$GOLANGCI_LINT_VERSION"
12 | fi
13 | 
14 | $BINARY run


--------------------------------------------------------------------------------
/script/prettyprint-log:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | # Script to pretty print the output of the github-mcp-server
 4 | # log.
 5 | #
 6 | # It uses colored output when running on a terminal.
 7 | 
 8 | # show script help
 9 | show_help() {
10 |   cat <<EOF
11 | Usage: $(basename "$0") [file]
12 | 
13 | If [file] is provided, input is read from that file.
14 | If no argument is given, input is read from stdin.
15 | 
16 | Options:
17 |   -h, --help      Show this help message and exit
18 | EOF
19 | }
20 | 
21 | # choose color for stdin or stdout if we are printing to
22 | # an actual terminal
23 | color(){
24 |   io="$1"
25 |   if [[ "$io" == "stdin" ]]; then
26 |     color="\033[0;32m" # green
27 |   else
28 |     color="\033[0;36m" # cyan
29 |   fi
30 |   if [ ! $is_terminal = "1" ]; then
31 |     color=""
32 |   fi
33 |   echo -e "${color}[$io]"
34 | }
35 | 
36 | # reset code if we are printing to an actual terminal
37 | reset(){
38 |   if [ ! $is_terminal = "1" ]; then
39 |     return
40 |   fi
41 |   echo -e "\033[0m"
42 | }
43 | 
44 | 
45 | # Handle -h or --help
46 | if [[ "$1" == "-h" || "$1" == "--help" ]]; then
47 |   show_help
48 |   exit 0
49 | fi
50 | 
51 | # Determine input source
52 | if [[ -n "$1" ]]; then
53 |   if [[ ! -r "$1" ]]; then
54 |     echo "Error: File '$1' not found or not readable." >&2
55 |     exit 1
56 |   fi
57 |   input="$1"
58 | else
59 |   input="/dev/stdin"
60 | fi
61 | 
62 | # check if we are in a terminal for showing colors
63 | if test -t 1; then
64 |   is_terminal="1"
65 | else
66 |   is_terminal="0"
67 | fi
68 | 
69 | # Processs each log line, print whether is stdin or stdout, using different
70 | # colors if we output to a terminal, and pretty print json data using jq
71 | sed -nE 's/^.*\[(stdin|stdout)\]:.* ([0-9]+) bytes: (.*)\\n"$/\1 \2 \3/p' $input |
72 | while read -r io bytes json; do
73 |   # Unescape the JSON string safely
74 |   unescaped=$(echo "$json" | awk '{ print "echo -e \"" $0 "\" | jq ." }' | bash)
75 |   echo  "$(color $io)($bytes bytes):$(reset)"
76 |   echo "$unescaped" | jq .
77 |   echo
78 | done
79 | 


--------------------------------------------------------------------------------
/script/tag-release:
--------------------------------------------------------------------------------
  1 | #!/bin/bash
  2 | 
  3 | # Exit immediately if a command exits with a non-zero status.
  4 | set -e
  5 | 
  6 | # Initialize variables
  7 | TAG=""
  8 | DRY_RUN=false
  9 | 
 10 | # Parse arguments
 11 | for arg in "$@"; do
 12 |   case $arg in
 13 |     --dry-run)
 14 |       DRY_RUN=true
 15 |       ;;
 16 |     *)
 17 |       # The first non-flag argument is the tag
 18 |       if [[ ! $arg == --* ]]; then
 19 |         if [ -z "$TAG" ]; then
 20 |           TAG=$arg
 21 |         fi
 22 |       fi
 23 |       ;;
 24 |   esac
 25 | done
 26 | 
 27 | if [ "$DRY_RUN" = true ]; then
 28 |     echo "DRY RUN: No changes will be pushed to the remote repository."
 29 |     echo
 30 | fi
 31 | 
 32 | # 1. Validate input
 33 | if [ -z "$TAG" ]; then
 34 |   echo "Error: No tag specified."
 35 |   echo "Usage: ./script/tag-release vX.Y.Z [--dry-run]"
 36 |   exit 1
 37 | fi
 38 | 
 39 | # Regular expression for semantic versioning (vX.Y.Z or vX.Y.Z-suffix)
 40 | if [[ ! $TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-.*)?$ ]]; then
 41 |     echo "Error: Tag must be in format vX.Y.Z or vX.Y.Z-suffix (e.g., v1.0.0 or v1.0.0-rc1)"
 42 |     exit 1
 43 | fi
 44 | 
 45 | # 2. Check current branch
 46 | CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
 47 | if [ "$CURRENT_BRANCH" != "main" ]; then
 48 |   echo "Error: You must be on the 'main' branch to create a release."
 49 |   echo "Current branch is '$CURRENT_BRANCH'."
 50 |   exit 1
 51 | fi
 52 | 
 53 | # 3. Fetch latest from origin
 54 | echo "Fetching latest changes from origin..."
 55 | git fetch origin main
 56 | 
 57 | # 4. Check if the working directory is clean
 58 | if ! git diff-index --quiet HEAD --; then
 59 |     echo "Error: Working directory is not clean. Please commit or stash your changes."
 60 |     exit 1
 61 | fi
 62 | 
 63 | # 5. Check if main is up-to-date with origin/main
 64 | LOCAL_SHA=$(git rev-parse @)
 65 | REMOTE_SHA=$(git rev-parse @{u})
 66 | 
 67 | if [ "$LOCAL_SHA" != "$REMOTE_SHA" ]; then
 68 |     echo "Error: Your local 'main' branch is not up-to-date with 'origin/main'. Please pull the latest changes."
 69 |     exit 1
 70 | fi
 71 | echo "✅ Local 'main' branch is up-to-date with 'origin/main'."
 72 | 
 73 | # 6. Check if tag already exists
 74 | if git tag -l | grep -q "^${TAG}
quot;; then
 75 |     echo "Error: Tag ${TAG} already exists locally."
 76 |     exit 1
 77 | fi
 78 | if git ls-remote --tags origin | grep -q "refs/tags/${TAG}
quot;; then
 79 |     echo "Error: Tag ${TAG} already exists on remote 'origin'."
 80 |     exit 1
 81 | fi
 82 | 
 83 | # 7. Confirm release with user
 84 | echo
 85 | LATEST_TAG=$(git tag --sort=-version:refname | head -n 1)
 86 | if [ -n "$LATEST_TAG" ]; then
 87 |     echo "Current latest release: $LATEST_TAG"
 88 | fi
 89 | echo "Proposed new release:   $TAG"
 90 | echo
 91 | read -p "Do you want to proceed with the release? (y/n) " -n 1 -r
 92 | echo # Move to a new line
 93 | if [[ ! $REPLY =~ ^[Yy]$ ]]; then
 94 |     echo "Release cancelled."
 95 |     exit 1
 96 | fi
 97 | echo
 98 | 
 99 | # 8. Create the new release tag
100 | if [ "$DRY_RUN" = true ]; then
101 |     echo "DRY RUN: Skipping creation of tag $TAG."
102 | else
103 |     echo "Creating new release tag: $TAG"
104 |     git tag -a "$TAG" -m "Release $TAG"
105 | fi
106 | 
107 | # 9. Push the new tag to the remote repository
108 | if [ "$DRY_RUN" = true ]; then
109 |     echo "DRY RUN: Skipping push of tag $TAG to origin."
110 | else
111 |     echo "Pushing tag $TAG to origin..."
112 |     git push origin "$TAG"
113 | fi
114 | 
115 | # 10. Update and push the 'latest-release' tag
116 | if [ "$DRY_RUN" = true ]; then
117 |     echo "DRY RUN: Skipping update and push of 'latest-release' tag."
118 | else
119 |     echo "Updating 'latest-release' tag to point to $TAG..."
120 |     git tag -f latest-release "$TAG"
121 |     echo "Pushing 'latest-release' tag to origin..."
122 |     git push origin latest-release --force
123 | fi
124 | 
125 | if [ "$DRY_RUN" = true ]; then
126 |     echo "✅ DRY RUN complete. No tags were created or pushed."
127 | else
128 |     echo "✅ Successfully tagged and pushed release $TAG."
129 |     echo "✅ 'latest-release' tag has been updated."
130 | fi
131 | 
132 | # 11. Post-release instructions
133 | REPO_URL=$(git remote get-url origin)
134 | REPO_SLUG=$(echo "$REPO_URL" | sed -e 's/.*github.com[:\/]//' -e 's/\.git$//')
135 | 
136 | cat << EOF
137 | 
138 | ## 🎉 Release $TAG has been initiated!
139 | 
140 | ### Next steps:
141 | 1. 📋 Check https://github.com/$REPO_SLUG/releases and wait for the draft release to show up (after the goreleaser workflow completes)
142 | 2. ✏️  Edit the new release, delete the existing notes and click the auto-generate button GitHub provides
143 | 3. ✨ Add a section at the top calling out the main features
144 | 4. 🚀 Publish the release
145 | 5. 📢 Post message in #gh-mcp-releases channel in Slack and then share to the other mcp channels
146 | 
147 | ### Resources:
148 | - 📦 Draft Release: https://github.com/$REPO_SLUG/releases/tag/$TAG
149 | 
150 | The release process is now ready for your review and completion!
151 | EOF
152 | 


--------------------------------------------------------------------------------
/script/test:
--------------------------------------------------------------------------------
1 | set -eu
2 | 
3 | go test -race ./...


--------------------------------------------------------------------------------
/third-party/github.com/fsnotify/fsnotify/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright © 2012 The Go Authors. All rights reserved.
 2 | Copyright © fsnotify Authors. All rights reserved.
 3 | 
 4 | Redistribution and use in source and binary forms, with or without modification,
 5 | are permitted provided that the following conditions are met:
 6 | 
 7 | * Redistributions of source code must retain the above copyright notice, this
 8 |   list of conditions and the following disclaimer.
 9 | * Redistributions in binary form must reproduce the above copyright notice, this
10 |   list of conditions and the following disclaimer in the documentation and/or
11 |   other materials provided with the distribution.
12 | * Neither the name of Google Inc. nor the names of its contributors may be used
13 |   to endorse or promote products derived from this software without specific
14 |   prior written permission.
15 | 
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | 


--------------------------------------------------------------------------------
/third-party/github.com/github/github-mcp-server/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2025 GitHub
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/go-viper/mapstructure/v2/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2013 Mitchell Hashimoto
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/google/go-github/v71/github/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2013 The go-github AUTHORS. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/github.com/google/go-github/v73/github/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2013 The go-github AUTHORS. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/github.com/google/go-querystring/query/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2013 Google. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/github.com/google/uuid/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2009,2014 Google Inc. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/github.com/gorilla/mux/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2012-2018 The Gorilla Authors. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 | 	 * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 | 	 * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 | 	 * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/github.com/josephburnett/jd/v2/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2016 Joseph Burnett
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/josharian/intern/license.md:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2019 Josh Bleecher Snyder
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/mailru/easyjson/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2016 Mail.Ru Group
2 | 
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 | 
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 | 
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 | 


--------------------------------------------------------------------------------
/third-party/github.com/mark3labs/mcp-go/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2024 Anthropic, PBC
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/migueleliasweb/go-github-mock/src/mock/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2021 Miguel Elias dos Santos
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/pelletier/go-toml/v2/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | go-toml v2
 4 | Copyright (c) 2021 - 2023 Thomas Pelletier
 5 | 
 6 | Permission is hereby granted, free of charge, to any person obtaining a copy
 7 | of this software and associated documentation files (the "Software"), to deal
 8 | in the Software without restriction, including without limitation the rights
 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 | 
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 | 
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 | 


--------------------------------------------------------------------------------
/third-party/github.com/sagikazarmark/locafero/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2023 Márk Sági-Kazár <mark.sagikazar@gmail.com>
 2 | 
 3 | Permission is hereby granted, free of charge, to any person obtaining a copy
 4 | of this software and associated documentation files (the "Software"), to deal
 5 | in the Software without restriction, including without limitation the rights
 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 7 | copies of the Software, and to permit persons to whom the Software is furnished
 8 | to do so, subject to the following conditions:
 9 | 
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 | 
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 | 


--------------------------------------------------------------------------------
/third-party/github.com/shurcooL/githubv4/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2017 Dmitri Shuralyov
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/shurcooL/graphql/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2017 Dmitri Shuralyov
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/sirupsen/logrus/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2014 Simon Eskildsen
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/sourcegraph/conc/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2023 Sourcegraph
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/spf13/cast/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2014 Steve Francia
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.


--------------------------------------------------------------------------------
/third-party/github.com/spf13/pflag/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2012 Alex Ogier. All rights reserved.
 2 | Copyright (c) 2012 The Go Authors. All rights reserved.
 3 | 
 4 | Redistribution and use in source and binary forms, with or without
 5 | modification, are permitted provided that the following conditions are
 6 | met:
 7 | 
 8 |    * Redistributions of source code must retain the above copyright
 9 | notice, this list of conditions and the following disclaimer.
10 |    * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the
13 | distribution.
14 |    * Neither the name of Google Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 | 
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | 


--------------------------------------------------------------------------------
/third-party/github.com/spf13/viper/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2014 Steve Francia
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.


--------------------------------------------------------------------------------
/third-party/github.com/subosito/gotenv/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2013 Alif Rachmawadi
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/github.com/yosida95/uritemplate/v3/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (C) 2016, Kohei YOSHIDA <https://yosida95.com/>. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are met:
 5 | 
 6 |     * Redistributions of source code must retain the above copyright
 7 |       notice, this list of conditions and the following disclaimer.
 8 |     * Redistributions in binary form must reproduce the above copyright
 9 |       notice, this list of conditions and the following disclaimer in the
10 |       documentation and/or other materials provided with the distribution.
11 |     * Neither the name of the copyright holder nor the names of its
12 |       contributors may be used to endorse or promote products derived from
13 |       this software without specific prior written permission.
14 | 
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 | 


--------------------------------------------------------------------------------
/third-party/github.com/yudai/golcs/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2015 Iwasaki Yudai
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/third-party/golang.org/x/exp/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright 2009 The Go Authors.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google LLC nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/golang.org/x/sys/unix/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright 2009 The Go Authors.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google LLC nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/golang.org/x/sys/windows/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright 2009 The Go Authors.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google LLC nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/golang.org/x/text/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright 2009 The Go Authors.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google LLC nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/golang.org/x/time/rate/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2009 The Go Authors. All rights reserved.
 2 | 
 3 | Redistribution and use in source and binary forms, with or without
 4 | modification, are permitted provided that the following conditions are
 5 | met:
 6 | 
 7 |    * Redistributions of source code must retain the above copyright
 8 | notice, this list of conditions and the following disclaimer.
 9 |    * Redistributions in binary form must reproduce the above
10 | copyright notice, this list of conditions and the following disclaimer
11 | in the documentation and/or other materials provided with the
12 | distribution.
13 |    * Neither the name of Google Inc. nor the names of its
14 | contributors may be used to endorse or promote products derived from
15 | this software without specific prior written permission.
16 | 
17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 | 


--------------------------------------------------------------------------------
/third-party/gopkg.in/yaml.v2/NOTICE:
--------------------------------------------------------------------------------
 1 | Copyright 2011-2016 Canonical Ltd.
 2 | 
 3 | Licensed under the Apache License, Version 2.0 (the "License");
 4 | you may not use this file except in compliance with the License.
 5 | You may obtain a copy of the License at
 6 | 
 7 |     http://www.apache.org/licenses/LICENSE-2.0
 8 | 
 9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | 


--------------------------------------------------------------------------------
/third-party/gopkg.in/yaml.v3/LICENSE:
--------------------------------------------------------------------------------
 1 | 
 2 | This project is covered by two different licenses: MIT and Apache.
 3 | 
 4 | #### MIT License ####
 5 | 
 6 | The following files were ported to Go from C files of libyaml, and thus
 7 | are still covered by their original MIT license, with the additional
 8 | copyright staring in 2011 when the project was ported over:
 9 | 
10 |     apic.go emitterc.go parserc.go readerc.go scannerc.go
11 |     writerc.go yamlh.go yamlprivateh.go
12 | 
13 | Copyright (c) 2006-2010 Kirill Simonov
14 | Copyright (c) 2006-2011 Kirill Simonov
15 | 
16 | Permission is hereby granted, free of charge, to any person obtaining a copy of
17 | this software and associated documentation files (the "Software"), to deal in
18 | the Software without restriction, including without limitation the rights to
19 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
20 | of the Software, and to permit persons to whom the Software is furnished to do
21 | so, subject to the following conditions:
22 | 
23 | The above copyright notice and this permission notice shall be included in all
24 | copies or substantial portions of the Software.
25 | 
26 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 | SOFTWARE.
33 | 
34 | ### Apache License ###
35 | 
36 | All the remaining project files are covered by the Apache license:
37 | 
38 | Copyright (c) 2011-2019 Canonical Ltd
39 | 
40 | Licensed under the Apache License, Version 2.0 (the "License");
41 | you may not use this file except in compliance with the License.
42 | You may obtain a copy of the License at
43 | 
44 |     http://www.apache.org/licenses/LICENSE-2.0
45 | 
46 | Unless required by applicable law or agreed to in writing, software
47 | distributed under the License is distributed on an "AS IS" BASIS,
48 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
49 | See the License for the specific language governing permissions and
50 | limitations under the License.
51 | 


--------------------------------------------------------------------------------
/third-party/gopkg.in/yaml.v3/NOTICE:
--------------------------------------------------------------------------------
 1 | Copyright 2011-2016 Canonical Ltd.
 2 | 
 3 | Licensed under the Apache License, Version 2.0 (the "License");
 4 | you may not use this file except in compliance with the License.
 5 | You may obtain a copy of the License at
 6 | 
 7 |     http://www.apache.org/licenses/LICENSE-2.0
 8 | 
 9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 | 


--------------------------------------------------------------------------------