feat: x402 payment tool for agent-to-service payments#2123
feat: x402 payment tool for agent-to-service payments#2123up2itnow0822 wants to merge 1 commit intohuggingface:mainfrom
Conversation
Adds a native Tool subclass that handles HTTP 402 (Payment Required) responses using the x402 protocol, enabling smolagents to access paid APIs with configurable spending guardrails. Key features: - Three modes: simulation (default), informational, and live - SpendingPolicy with per-transaction limits, rolling caps, merchant allowlists, and human approval gates - Fail-closed design: policy engine errors always reject, never approve - Full audit trail for every payment attempt - MCP integration via agentpay-mcp (zero changes to smolagents core) - Optional legal_entity_id for entity-tagged audit entries Closes huggingface#2112 Production reference: Merged into NVIDIA NeMo Agent Toolkit Examples (PR huggingface#17) as the official x402 payment tool for the NVIDIA catalog.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 47679bda98
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| env = {"CHAIN_ID": str(chain_id), "SPENDING_LIMIT": str(spending_limit)} | ||
| if wallet_private_key: | ||
| env["WALLET_PRIVATE_KEY"] = wallet_private_key | ||
|
|
||
| return MCPClient( |
There was a problem hiding this comment.
Pass payment env vars into MCP client
The helper builds an env dict from wallet_private_key, chain_id, and spending_limit, but never passes it to MCPClient, so those function arguments are silently ignored. In practice, callers who set these parameters will still start agentpay-mcp without the expected configuration, which can break live-payment setup and spending limits.
Useful? React with 👍 / 👎.
| }) | ||
|
|
||
| merchant = request.get("merchant", "unknown") | ||
| amount = float(request.get("amount", 0)) |
There was a problem hiding this comment.
Validate amount before float conversion
amount is converted with float(...) before any fail-closed handling, so non-numeric values (for example "amount": "abc" or null) raise ValueError/TypeError and escape forward() entirely. That causes an unhandled tool failure instead of a structured rejection/audit entry for malformed external 402 payloads.
Useful? React with 👍 / 👎.
| if amount > policy.max_per_transaction: | ||
| return ( | ||
| f"Amount ${amount:.4f} exceeds per-transaction limit " | ||
| f"${policy.max_per_transaction:.2f}" | ||
| ) |
There was a problem hiding this comment.
Reject negative payment amounts in policy checks
Policy validation only enforces an upper bound (amount > max_per_transaction) and never rejects non-positive values. A malicious or malformed 402 with a negative amount will pass checks, be recorded as simulated/approved, and reduce rolling spend totals, which lets later transactions bypass the rolling-cap guardrail.
Useful? React with 👍 / 👎.
Security Note: CVE-2025-5120 ImmunityWith CVE-2025-5120 (CVSS 7.6 sandbox escape in smolagents ≤v1.14.0) now public, I wanted to clarify this PR's security posture to help with review: This x402 payment tool is immune to CVE-2025-5120 because it:
The tool's attack surface is limited to outbound HTTPS calls to payment endpoints, which is the minimum required for any payment integration. This is consistent with the security model already merged in the NVIDIA NeMo-Agent-Toolkit-Examples integration (PR #17), which uses the same HTTP-only pattern. Happy to add any additional security documentation the maintainers would find useful. |
|
Thanks for the thorough review. All three P1 findings are valid and I'll address them: 1. Pass payment env vars into MCP client - You're right, the env dict is constructed but never forwarded to 2. Validate amount before float conversion - Good catch. The 3. Reject negative payment amounts - Correct. A negative amount slipping through policy checks would corrupt rolling spend totals. Adding I'll push a fix commit addressing all three. |
Summary
Adds a native
Toolsubclass that handles HTTP 402 (Payment Required) responses using the x402 protocol, enabling smolagents to access paid APIs with configurable spending guardrails.Closes #2112
Why This Matters: The Enterprise Trust Gap
McKinsey's 2026 AI Trust Maturity Survey quantifies the problem this PR addresses:
Agents that can pay for APIs need trust infrastructure — not just capability. This tool provides that trust layer: spending caps, human approval gates, merchant allowlists, fail-closed policy, and full audit trail.
What this adds
X402PaymentTool— a native smolagentsToolsubclassThree operating modes:
SpendingPolicy— configurable guardrailsTwo integration paths
X402PaymentToolsubclassesTooldirectly, works withCodeAgentandToolCallingAgentcreate_agentpay_mcp_client()helper uses existingMCPClientwith agentpay-mcp server (zero changes to smolagents core)Design decisions
legal_entity_id— optional field for entity-tagged audit entries (informational first, per Feature: x402 payment handling for agents accessing paid APIs #2112 discussion)web3oragentpay-mcp.Production Credentials
Human-Approval Wrapper (addresses #2112)
The
require_human_approvalparameter ensures no payment executes without explicit human confirmation:Usage
Tests
Comprehensive test suite covering:
Files
src/smolagents/x402_payment_tool.py— Tool implementationtests/x402/test_x402_payment_tool.py— Test suiteexamples/x402_payment_example.py— Usage examples for all modes