-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patherrors.go
More file actions
122 lines (106 loc) · 3.28 KB
/
Copy patherrors.go
File metadata and controls
122 lines (106 loc) · 3.28 KB
1
2
3
4
5
6
7
8
9
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package adcp
import (
"encoding/json"
"errors"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// ErrorOptions configures an AdCP error response.
type ErrorOptions struct {
Message string
Recovery string // "retry", "revise", "contact_support", "terminal"
Field string
Suggestion string
RetryAfter int
Details map[string]any
}
// NewError creates an error that Register handlers can return to produce a
// typed AdCP error response instead of a generic INTERNAL_ERROR.
//
// Usage in a handler:
//
// return nil, adcp.NewError("BUDGET_TOO_LOW", adcp.ErrorOptions{
// Message: "Budget $500 is below the $1,000 minimum for video",
// Field: "budget",
// })
func NewError(code string, opts ErrorOptions) error {
return &handlerError{code: code, opts: opts}
}
type handlerError struct {
code string
opts ErrorOptions
}
func (e *handlerError) Error() string {
if e.opts.Message != "" {
return e.opts.Message
}
return e.code
}
// errorToResult converts a handler error to an MCP CallToolResult. If the
// error is an AdCP typed error (from NewError), it uses the code and options.
// Otherwise it wraps as INTERNAL_ERROR.
func errorToResult(err error) (*mcp.CallToolResult, any, error) {
var he *handlerError
if errors.As(err, &he) {
return Errorf(he.code, he.opts)
}
// Don't leak internal error details to the calling agent.
return Errorf("INTERNAL_ERROR", ErrorOptions{Message: "An internal error occurred"})
}
type adcpErrorPayload struct {
Code string `json:"code"`
Message string `json:"message"`
Recovery string `json:"recovery"`
Field string `json:"field,omitempty"`
Suggestion string `json:"suggestion,omitempty"`
RetryAfter int `json:"retry_after,omitempty"`
Details map[string]any `json:"details,omitempty"`
}
type adcpErrorWrapper struct {
ADCPError adcpErrorPayload `json:"adcp_error"`
}
// Error builds an L3-compliant AdCP error response.
// Returns isError: true + structuredContent.adcp_error.
func Error[T any](code string, opts ErrorOptions) (*mcp.CallToolResult, T, error) {
recovery := opts.Recovery
if recovery == "" {
recovery = defaultRecovery(code)
}
payload := adcpErrorPayload{
Code: code,
Message: opts.Message,
Recovery: recovery,
Field: opts.Field,
Suggestion: opts.Suggestion,
RetryAfter: opts.RetryAfter,
Details: opts.Details,
}
wrapper := adcpErrorWrapper{ADCPError: payload}
b, _ := json.Marshal(wrapper)
result := &mcp.CallToolResult{
Content: []mcp.Content{
&mcp.TextContent{Text: string(b)},
},
IsError: true,
StructuredContent: jsonRoundTrip(wrapper),
}
var zero T
return result, zero, nil
}
// Errorf is a convenience wrapper for Error that returns (*mcp.CallToolResult, any, error),
// matching the adcp.AddTool handler signature without requiring a type parameter.
func Errorf(code string, opts ErrorOptions) (*mcp.CallToolResult, any, error) {
return Error[any](code, opts)
}
func defaultRecovery(code string) string {
switch code {
case "RATE_LIMITED":
return "retry"
case "BUDGET_TOO_LOW", "INVALID_REQUEST", "MISSING_FIELD", "INVALID_FIELD",
"ACCOUNT_NOT_FOUND", "TERMS_REJECTED":
return "revise"
case "INTERNAL_ERROR", "SERVICE_UNAVAILABLE":
return "contact_support"
default:
return "terminal"
}
}