Commit e183dd1
authored
Feature: Request Hooks (mark3labs#62)
* Add Callbacks Feature
MCPServer is upgraded with the capacity to notify before and/or
after requests run. Developers can choose Before / After events,
and can submit callbacks that are notified during EVERY request
lifecycle, or else specific request methods. The specific request
method callbacks receive typed message and/or result objects.
A special OnError callback is also provided.
To accomplish this with a minimal margin for error, a bit of a
refactoring was done:
- The `MCPServer.HandleMessage` method was moved from server.go
into its own file.
- The individual request handlers' prototypes were changed.
rather than returning `mcp.JSONRPCMessage`, they return a tuple
of their specific result type and error.
- A code generation schema was introduced, to facilitate adding
functionality to each clause in the switch statement within
`HandleMessage()`.
- Now that HandleMessage() now receives a typed result object
and possible error, it is able to pass those results to any
configured callbacks.
I considered several options including a strategy pattern to make
HandleMessage() more DRY, but opted to stick with the minimal
reorganization of the code, and to keep the algorithm as flat
and obvious as possible. I think the codegen mitigates the
repetitiveness of that section of code.
* Added documentation to MCP method descriptions
* Rename Callbacks --> Hooks
Reinstate test
* Improved error propagation via hooks
This commit enhances the MCP server's error handling capabilities by:
1. Improving error propagation through the OnError hook system
2. Adding comprehensive documentation with usage examples
3. Ensuring error types can be properly inspected with Go's standard error handling patterns
We've added better support for typed errors that propagate through the request handling chain:
- `ErrUnsupported`: Used when a capability is not enabled on the server
- `UnparseableMessageError`: Used when parsing a message fails
- `ErrResourceNotFound`: Used when a requested resource doesn't exist
- `ErrPromptNotFound`: Used when a requested prompt doesn't exist
- `ErrToolNotFound`: Used when a requested tool doesn't exist
These errors can be interrogated using `errors.Is` and `errors.As` to provide more targeted error handling.
The documentation now includes examples like:
```go
hooks.AddOnError(func(id any, method mcp.MCPMethod, message any, err error) {
// Check for specific error types using errors.Is
if errors.Is(err, ErrUnsupported) {
// Handle capability not supported errors
log.Printf("Capability not supported: %v", err)
}
// Use errors.As to get specific error types
var parseErr = &UnparseableMessageError{}
if errors.As(err, &parseErr) {
// Access specific methods/fields of the error type
log.Printf("Failed to parse message for method %s: %v",
parseErr.GetMethod(), parseErr.Unwrap())
// Access the raw message that failed to parse
rawMsg := parseErr.GetMessage()
}
// Check for specific resource/prompt/tool errors
switch {
case errors.Is(err, ErrResourceNotFound):
log.Printf("Resource not found: %v", err)
case errors.Is(err, ErrPromptNotFound):
log.Printf("Prompt not found: %v", err)
case errors.Is(err, ErrToolNotFound):
log.Printf("Tool not found: %v", err)
}
})
```
We've also added examples for testing scenarios:
```go
// Create a channel to receive errors for testing
errChan := make(chan error, 1)
// Register hook to capture and inspect errors
hooks := &Hooks{}
hooks.AddOnError(func(id any, method mcp.MCPMethod, message any, err error) {
// For capability-related errors
if errors.Is(err, ErrUnsupported) {
// Handle capability not supported
errChan <- err
return
}
// For parsing errors
var parseErr = &UnparseableMessageError{}
if errors.As(err, &parseErr) {
// Handle unparseable message errors
fmt.Printf("Failed to parse %s request: %v\n",
parseErr.GetMethod(), parseErr.Unwrap())
errChan <- parseErr
return
}
// For resource/prompt/tool not found errors
if errors.Is(err, ErrResourceNotFound) ||
errors.Is(err, ErrPromptNotFound) ||
errors.Is(err, ErrToolNotFound) {
// Handle not found errors
errChan <- err
return
}
// For other errors
errChan <- err
})
```
These improvements make the MCP server more robust and user-friendly by providing clear error patterns and detailed documentation.
* Correct the indirection of message propagation
Added tests to verify that messages are propagated to the BeforeAny
and AfterAny hooks
* Rename AfterAny --> OnSuccess to clarify convention1 parent efc428d commit e183dd1
File tree
13 files changed
+1683
-248
lines changed- examples/everything
- mcp
- server
- internal/gen
13 files changed
+1683
-248
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
509 | 509 | | |
510 | 510 | | |
511 | 511 | | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
512 | 524 | | |
513 | 525 | | |
514 | 526 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
32 | 57 | | |
33 | 58 | | |
34 | 59 | | |
35 | 60 | | |
36 | 61 | | |
37 | 62 | | |
| 63 | + | |
38 | 64 | | |
39 | 65 | | |
40 | 66 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| 8 | + | |
8 | 9 | | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
12 | 13 | | |
13 | | - | |
14 | 14 | | |
15 | 15 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
11 | 51 | | |
12 | 52 | | |
13 | 53 | | |
| |||
0 commit comments