Skip to content

Commit 5fdeb85

Browse files
committed
Fix critical MCP integration bugs from code review
Address 6 issues identified in PR #13 review: - Honor network flag in server/client (was always Base) - Only treat 402 errors as payment failures after retry - Include all payment sentinels in IsPaymentError - Use settle timeout for HTTP client (was too short) - Copy requirements slice to prevent concurrent mutation
1 parent 1e87c1c commit 5fdeb85

File tree

5 files changed

+42
-13
lines changed

5 files changed

+42
-13
lines changed

examples/mcp/main.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,26 @@ func runServer() {
5555
}
5656

5757
// Determine network and payment requirements
58-
var requirement func(string, string, string) x402.PaymentRequirement
59-
networkName := *network
60-
if *testnet {
58+
var (
59+
requirement func(string, string, string) x402.PaymentRequirement
60+
networkName string
61+
)
62+
63+
switch {
64+
case *testnet:
6165
requirement = server.RequireUSDCBaseSepolia
6266
networkName = "base-sepolia"
63-
} else {
67+
case *network == "base":
6468
requirement = server.RequireUSDCBase
69+
networkName = "base"
70+
case *network == "polygon":
71+
requirement = server.RequireUSDCPolygon
72+
networkName = "polygon"
73+
case *network == "solana":
74+
requirement = server.RequireUSDCSolana
75+
networkName = "solana"
76+
default:
77+
log.Fatalf("unsupported network: %s", *network)
6578
}
6679

6780
// Create x402 MCP server
@@ -158,10 +171,17 @@ func runClient() {
158171

159172
// Determine chain configuration
160173
var chain x402.ChainConfig
161-
if *testnet {
174+
switch {
175+
case *testnet:
162176
chain = x402.BaseSepolia
163-
} else {
177+
case *network == "base":
164178
chain = x402.BaseMainnet
179+
case *network == "polygon":
180+
chain = x402.PolygonMainnet
181+
case *network == "solana":
182+
chain = x402.SolanaMainnet
183+
default:
184+
log.Fatalf("unsupported network: %s", *network)
165185
}
166186

167187
// Create EVM signer with network and USDC token

mcp/client/transport.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ func (t *Transport) retryWithPayment(ctx context.Context, req transport.JSONRPCR
215215

216216
// Check if payment succeeded
217217
if resp.Error != nil {
218-
if t.config.OnPaymentFailure != nil {
218+
if resp.Error.Code == 402 && t.config.OnPaymentFailure != nil {
219219
t.config.OnPaymentFailure(PaymentEvent{
220220
Type: PaymentFailure,
221221
Error: fmt.Errorf("payment rejected: %s", resp.Error.Message),

mcp/errors.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,14 @@ func IsPaymentError(err error) bool {
9999
return errors.As(err, &paymentErr) ||
100100
errors.Is(err, ErrPaymentRequired) ||
101101
errors.Is(err, ErrNoMatchingSigner) ||
102+
errors.Is(err, ErrInsufficientBalance) ||
102103
errors.Is(err, ErrPaymentRejected) ||
103104
errors.Is(err, ErrSettlementFailed) ||
105+
errors.Is(err, ErrInvalidPaymentPayload) ||
106+
errors.Is(err, ErrNoPaymentRequirements) ||
107+
errors.Is(err, ErrFacilitatorUnavailable) ||
108+
errors.Is(err, ErrVerificationTimeout) ||
109+
errors.Is(err, ErrSettlementTimeout) ||
104110
errors.Is(err, x402.ErrNoValidSigner) ||
105111
errors.Is(err, x402.ErrSigningFailed) ||
106112
errors.Is(err, x402.ErrVerificationFailed)

mcp/server/facilitator.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type HTTPFacilitator struct {
2828
func NewHTTPFacilitator(facilitatorURL string) *HTTPFacilitator {
2929
client := &http.FacilitatorClient{
3030
BaseURL: facilitatorURL,
31-
Client: &nethttp.Client{Timeout: mcp.PaymentVerifyTimeout},
31+
Client: &nethttp.Client{Timeout: mcp.PaymentSettleTimeout},
3232
VerifyTimeout: mcp.PaymentVerifyTimeout,
3333
SettleTimeout: mcp.PaymentSettleTimeout,
3434
MaxRetries: 2,

mcp/server/handler.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,17 @@ func (h *X402Handler) checkPaymentRequired(toolName string) ([]x402.PaymentRequi
188188
return nil, false
189189
}
190190

191-
// Set resource field on requirements
192-
for i := range requirements {
193-
if requirements[i].Resource == "" {
194-
requirements[i].Resource = fmt.Sprintf("mcp://tools/%s", toolName)
191+
// Work on a copy to avoid mutating shared config
192+
reqCopy := make([]x402.PaymentRequirement, len(requirements))
193+
copy(reqCopy, requirements)
194+
195+
for i := range reqCopy {
196+
if reqCopy[i].Resource == "" {
197+
reqCopy[i].Resource = fmt.Sprintf("mcp://tools/%s", toolName)
195198
}
196199
}
197200

198-
return requirements, true
201+
return reqCopy, true
199202
}
200203

201204
// extractPayment extracts payment from params._meta["x402/payment"]

0 commit comments

Comments
 (0)