You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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>
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.
3
+
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.
4
4
5
5
## Overview
6
6
7
-
The GitHub MCP Server implements a custom error handling approach that serves two primary purposes:
7
+
The GitHub MCP Server uses the Go SDK's `CallToolResult.SetError()` to embed typed GitHub API errors directly in tool results. This approach enables:
2.**Middleware Inspection**: Store detailed error information in the request context for middleware analysis
10
+
2.**Error Type Inspection**: Consumers can use `result.GetError()` with `errors.As` to extract typed errors for analysis
11
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.
12
+
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.
13
13
14
14
## Error Types
15
15
@@ -36,38 +36,61 @@ type GitHubGraphQLError struct {
36
36
}
37
37
```
38
38
39
+
### GitHubRawAPIError
40
+
41
+
Used for raw HTTP API errors from the GitHub API:
42
+
43
+
```go
44
+
typeGitHubRawAPIErrorstruct {
45
+
Messagestring`json:"message"`
46
+
Response *http.Response`json:"-"`
47
+
Errerror`json:"-"`
48
+
}
49
+
```
50
+
39
51
## Usage Patterns
40
52
41
53
### For GitHub REST API Errors
42
54
43
-
Instead of directly returning `mcp.NewToolResultError()`, use:
-**User-actionable errors** (authentication failures, rate limits, 404s) should be returned as failed tool calls using the error response functions
78
101
-**Developer errors** (JSON marshaling failures, internal logic errors) should be returned as actual Go errors that bubble up through the MCP framework
79
102
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
103
+
### Type Safety with SetError/GetError
85
104
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.
105
+
All GitHub API error types implement the `error` interface with `Unwrap()` support, enabling:
106
+
-`errors.As()` to extract the specific error type (e.g., `*GitHubAPIError`)
107
+
-`errors.Is()` to check for the underlying cause
108
+
- Standard Go error handling patterns
87
109
88
110
## Benefits
89
111
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
112
+
1.**Type Safety**: Errors are embedded in the result as typed Go errors, not just strings
113
+
2.**Observability**: Middleware can inspect the specific types of GitHub API errors using `errors.As`
114
+
3.**Simplicity**: No context-based error storage or middleware setup required
115
+
4.**Debugging**: Detailed error information (HTTP status codes, response objects) is preserved
116
+
5.**Privacy**: Error inspection can be done programmatically using `errors.Is`/`errors.As` checks
94
117
95
118
## Example Implementation
96
119
@@ -102,12 +125,12 @@ func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool
102
125
if err != nil {
103
126
return mcp.NewToolResultError(err.Error()), nil
104
127
}
105
-
128
+
106
129
client, err:=getClient(ctx)
107
130
if err != nil {
108
131
returnnil, fmt.Errorf("failed to get GitHub client: %w", err)
@@ -116,10 +139,23 @@ func GetIssue(getClient GetClientFn, t translations.TranslationHelperFunc) (tool
116
139
err,
117
140
), nil
118
141
}
119
-
142
+
120
143
returnMarshalledTextResult(issue), nil
121
144
}
122
145
}
123
146
```
124
147
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.
148
+
The error can then be inspected by consumers:
149
+
150
+
```go
151
+
result, err:=handler(ctx, request)
152
+
if err == nil && result.IsError {
153
+
ifapiErr:= result.GetError(); apiErr != nil {
154
+
varghErr *errors.GitHubAPIError
155
+
if errors.As(apiErr, &ghErr) {
156
+
log.Printf("GitHub API error: status=%d message=%s",
0 commit comments