Documentation
¶
Overview ¶
Package client pays for MCP tool calls made with the official MCP Go SDK (github.com/modelcontextprotocol/go-sdk), matching mppx's McpClient.wrap.
Wrap installs sending middleware on an *mcp.Client, so every session it connects, and every CallTool on them, handles payment challenges whether they arrive as payment-required JSON-RPC errors (-32042 or -32043) or as tool results carrying _meta["org.paymentauth/payment-required"]. The client pays with a configured method and retries the call with the Credential in _meta["org.paymentauth/credential"]; Receipt reads the receipt from the result:
c := mcp.NewClient(&mcp.Implementation{Name: "my-client", Version: "1.0.0"}, nil)
mcpclient.Wrap(c, mcpclient.Config{Methods: []client.Method{method}})
session, err := c.Connect(ctx, transport, nil)
...
result, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "premium_tool"})
receipt, err := mcpclient.Receipt(result)
Middleware returns the same handling as sending middleware to install yourself, without Wrap's per-client registry:
c.AddSendingMiddleware(mcpclient.Middleware(mcpclient.Config{Methods: methods}))
A zero-amount Tempo proof authenticates the payer at the challenge realm, so the client binds it to the session that received the challenge: it signs one only for a session connected with Connect (a Streamable HTTP transport whose HTTP client refuses cross-origin redirects) and whose endpoint origin the payment method trusts for that realm (by default an HTTPS endpoint on the default port whose hostname is the realm; the Tempo method's Config.TrustedProofRealms adds others by origin). A server cannot relay another server's challenge through a shared client:
session, err := mcpclient.Connect(ctx, c, &mcp.StreamableClientTransport{
Endpoint: "https://service.example/mcp",
}, nil)
Sessions without an endpoint (stdio, in-memory) get proofs only when Config.ValidateTempoProofSession authorizes them. A Streamable HTTP session whose client follows cross-origin redirects (http.DefaultClient does) never gets one, since a redirect could deliver it to another server.
Index ¶
- Variables
- func Connect(ctx context.Context, c *mcp.Client, transport *mcp.StreamableClientTransport, ...) (*mcp.ClientSession, error)
- func HTTPClient(base *http.Client) *http.Client
- func IsPaymentRequiredError(err error) bool
- func Middleware(config Config) mcp.Middleware
- func Receipt(result *mcp.CallToolResult) (*mpp.MCPReceipt, error)
- func WithOnPaymentRequired(ctx context.Context, fn OnPaymentRequired) context.Context
- func Wrap(c *mcp.Client, config Config) *mcp.Client
- type Config
- type OnPaymentRequired
- type OrderChallenges
Constants ¶
This section is empty.
Variables ¶
var ErrPaymentDeclined = mppclient.ErrPaymentDeclined
ErrPaymentDeclined is returned, wrapped, when the approval hook declines a payment. It is client.ErrPaymentDeclined, which the HTTP client returns when an OnChallenge hook declines, so errors.Is matches either.
Functions ¶
func Connect ¶ added in v0.7.0
func Connect(ctx context.Context, c *mcp.Client, transport *mcp.StreamableClientTransport, opts *mcp.ClientSessionOptions) (*mcp.ClientSession, error)
Connect connects c to the Streamable HTTP endpoint of transport, like c.Connect, with the transport's HTTPClient (http.DefaultClient when nil) wrapped by HTTPClient so it refuses cross-origin redirects. Sessions connected this way get zero-amount Tempo proofs for realms their endpoint's origin is trusted for, with no other configuration. transport is copied, not modified.
func HTTPClient ¶ added in v0.7.0
HTTPClient returns a copy of base (http.DefaultClient when nil) whose CheckRedirect refuses every redirect away from the origin of the request it follows, after base's own CheckRedirect (or net/http's 10-redirect limit). Connect sets it for you; otherwise give it to mcp.StreamableClientTransport as HTTPClient: zero-amount Tempo proofs are signed only for Streamable HTTP sessions whose client refuses cross-origin redirects, since a redirect would deliver the proof to another server than the endpoint whose origin vouched for the realm.
It is not inlined, so every client it returns shares one CheckRedirect code pointer, which refusesCrossOriginRedirects recognizes.
func IsPaymentRequiredError ¶
IsPaymentRequiredError reports whether err is an MCP payment-required or payment-verification-failed error that carries at least one Challenge.
func Middleware ¶ added in v0.6.0
func Middleware(config Config) mcp.Middleware
Middleware returns sending middleware that pays for tool calls, to install with c.AddSendingMiddleware(mcpclient.Middleware(config)). It is the explicit form of Wrap: every session of c, connected before or after, uses it, and installing it again adds a second payment layer rather than replacing the first.
func Receipt ¶
func Receipt(result *mcp.CallToolResult) (*mpp.MCPReceipt, error)
Receipt returns the payment receipt in a tool result's _meta["org.paymentauth/receipt"], or nil when it has none.
func WithOnPaymentRequired ¶
func WithOnPaymentRequired(ctx context.Context, fn OnPaymentRequired) context.Context
WithOnPaymentRequired returns a context whose tool calls use fn instead of Config.OnPaymentRequired. A nil fn pays without asking.
func Wrap ¶
Wrap adds automatic payment handling to tool calls made through sessions of c and returns c. Sessions connected before and after Wrap both use it. Calling Wrap again on the same client replaces its payment configuration. Middleware installs the same handling without Wrap's per-client registry.
Types ¶
type Config ¶
type Config struct {
// Methods are the payment methods the client can pay with.
Methods []mppclient.Method
// OnPaymentRequired, when set, approves every payment before it is made.
OnPaymentRequired OnPaymentRequired
// OrderChallenges, when set, filters and sorts payable Challenges.
OrderChallenges OrderChallenges
// ValidateTempoProofSession, when set, also authorizes the session for a
// zero-amount Tempo proof's realm before a proof is signed or reused;
// return an error to refuse. Most clients leave it nil: a proof is then
// bound to the session's transport, so only a session the client connected
// with Connect (a StreamableClientTransport with HTTPClient's redirect guard), to
// an endpoint whose origin the method trusts for the realm (by default an
// HTTPS endpoint on the default port whose hostname is the realm), gets
// one, whichever session relays the challenge. Set it for sessions without
// an endpoint (stdio, in-memory or wrapped transports), which are otherwise
// refused proofs. It cannot authorize a Streamable HTTP session whose
// client follows cross-origin redirects, which is always refused; compare the
// session pointer with sessions you connected yourself, never with a name
// or realm the peer sent. WithOnPaymentRequired does not bypass it, and the
// method's OriginValidator still runs with the session's origin.
ValidateTempoProofSession func(context.Context, *mcp.ClientSession, string) error
// PaymentPreferences ranks Challenges by "method/intent" q-values.
PaymentPreferences mppclient.PaymentPreferences
}
Config configures payment handling for an MCP client.
type OnPaymentRequired ¶
OnPaymentRequired approves a payment before a Credential is created for challenge. Returning false declines it.
type OrderChallenges ¶
type OrderChallenges func( ctx context.Context, candidates []mppclient.ChallengeCandidate, ) ([]mppclient.ChallengeCandidate, error)
OrderChallenges filters and sorts the payable Challenges before one is paid; the first one returned that passes the realm checks is paid. It has the semantics and signature of client.OrderChallenges, which the HTTP client takes with client.WithOrderChallenges, so one hook converts to either: mcpclient.OrderChallenges(fn).