Files
github--github-mcp-server/docs/error-handling.md
copilot-swe-agent[bot] df19fc28ea
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
Build and Test Go Project / build (macos-latest) (push) Has been cancelled
Build and Test Go Project / build (ubuntu-latest) (push) Has been cancelled
Build and Test Go Project / build (windows-latest) (push) Has been cancelled
Replace context-based error handling with CallToolResult.SetError
Migrate from storing GitHub API errors in context via middleware to
embedding typed errors directly in CallToolResult using the Go SDK
v1.3.0 SetError/GetError API.

Key changes:
- NewGitHubAPIErrorResponse/NewGitHubGraphQLErrorResponse/
  NewGitHubRawAPIErrorResponse now use result.SetError() with typed
  errors instead of storing in context
- Add Unwrap() to all error types for errors.As/errors.Is support
- Export error constructors (NewGitHubAPIError, etc.) for direct use
- Remove context-based error infrastructure (ContextWithGitHubErrors,
  GetGitHubAPIErrors, GitHubCtxErrors, etc.)
- Remove addGitHubAPIErrorToContext middleware from server.go
- Remove NewGitHubAPIErrorToCtx/NewGitHubGraphQLErrorToCtx and their
  callers in repositories_helper.go and actions.go
- Update tests to verify SetError/GetError and errors.As extraction
- Update error-handling.md documentation

Co-authored-by: SamMorrowDrums <4811358+SamMorrowDrums@users.noreply.github.com>
2026-02-18 15:22:37 +00:00

5.0 KiB

Error Handling

This document describes the error handling patterns used in the GitHub MCP Server, specifically how we handle GitHub API errors using the MCP SDK's SetError/GetError mechanism.

Overview

The GitHub MCP Server uses the Go SDK's CallToolResult.SetError() to embed typed GitHub API errors directly in tool results. This approach enables:

  1. Tool Response Generation: Return appropriate MCP tool error responses to clients
  2. Error Type Inspection: Consumers can use result.GetError() with errors.As to extract typed errors for analysis

This is powered by the Go SDK v1.3.0+ SetError/GetError methods on CallToolResult, which embed a Go error in the result alongside the error text content.

Error Types

GitHubAPIError

Used for REST API errors from the GitHub API:

type GitHubAPIError struct {
    Message  string           `json:"message"`
    Response *github.Response `json:"-"`
    Err      error            `json:"-"`
}

GitHubGraphQLError

Used for GraphQL API errors from the GitHub API:

type GitHubGraphQLError struct {
    Message string `json:"message"`
    Err     error  `json:"-"`
}

GitHubRawAPIError

Used for raw HTTP API errors from the GitHub API:

type GitHubRawAPIError struct {
    Message  string         `json:"message"`
    Response *http.Response `json:"-"`
    Err      error          `json:"-"`
}

Usage Patterns

For GitHub REST API Errors

When a GitHub REST API call fails, use:

return ghErrors.NewGitHubAPIErrorResponse(ctx, message, response, err), nil

This function:

  • Creates a GitHubAPIError with the provided message, response, and error
  • Calls result.SetError() to embed the typed error in the tool result
  • Returns a CallToolResult with IsError: true and error text content

For GitHub GraphQL API Errors

return ghErrors.NewGitHubGraphQLErrorResponse(ctx, message, err), nil

For Raw HTTP API Errors

return ghErrors.NewGitHubRawAPIErrorResponse(ctx, message, response, err), nil

Extracting Errors from Results

Consumers (such as middleware or the remote server) can extract typed errors from results:

if err := result.GetError(); err != nil {
    var apiErr *errors.GitHubAPIError
    if errors.As(err, &apiErr) {
        // Access apiErr.Response.StatusCode, apiErr.Message, etc.
    }

    var gqlErr *errors.GitHubGraphQLError
    if errors.As(err, &gqlErr) {
        // Access gqlErr.Message, gqlErr.Err, etc.
    }
}

Design Principles

User-Actionable vs. Developer Errors

  • User-actionable errors (authentication failures, rate limits, 404s) should be returned as failed tool calls using the error response functions
  • Developer errors (JSON marshaling failures, internal logic errors) should be returned as actual Go errors that bubble up through the MCP framework

Type Safety with SetError/GetError

All GitHub API error types implement the error interface with Unwrap() support, enabling:

  • errors.As() to extract the specific error type (e.g., *GitHubAPIError)
  • errors.Is() to check for the underlying cause
  • Standard Go error handling patterns

Benefits

  1. Type Safety: Errors are embedded in the result as typed Go errors, not just strings
  2. Observability: Middleware can inspect the specific types of GitHub API errors using errors.As
  3. Simplicity: No context-based error storage or middleware setup required
  4. Debugging: Detailed error information (HTTP status codes, response objects) is preserved
  5. Privacy: Error inspection can be done programmatically using errors.Is/errors.As checks

Example Implementation

func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool mcp.Tool, handler server.ToolHandlerFunc) {
    return mcp.NewTool("get_issue", /* ... */),
        func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
            owner, err := RequiredParam[string](request, "owner")
            if err != nil {
                return mcp.NewToolResultError(err.Error()), nil
            }

            client, err := getClient(ctx)
            if err != nil {
                return nil, fmt.Errorf("failed to get GitHub client: %w", err)
            }

            issue, resp, err := client.Issues.Get(ctx, owner, repo, issueNumber)
            if err != nil {
                return ghErrors.NewGitHubAPIErrorResponse(ctx,
                    "failed to get issue",
                    resp,
                    err,
                ), nil
            }

            return MarshalledTextResult(issue), nil
        }
}

The error can then be inspected by consumers:

result, err := handler(ctx, request)
if err == nil && result.IsError {
    if apiErr := result.GetError(); apiErr != nil {
        var ghErr *errors.GitHubAPIError
        if errors.As(apiErr, &ghErr) {
            log.Printf("GitHub API error: status=%d message=%s",
                ghErr.Response.StatusCode, ghErr.Message)
        }
    }
}