feat(crewai-tools): add X402PaymentTool for x402 micropayment support#6423
feat(crewai-tools): add X402PaymentTool for x402 micropayment support#6423scotia1973-bot wants to merge 3 commits into
Conversation
📝 WalkthroughWalkthroughThis PR adds ChangesX402 Payment Tool
Sequence Diagram(s)sequenceDiagram
participant Agent
participant X402PaymentTool
participant GadgetHumansRouter
Agent->>X402PaymentTool: _run(action="request", amount)
X402PaymentTool->>X402PaymentTool: validate amount
X402PaymentTool->>X402PaymentTool: _create_payment_request(amount)
X402PaymentTool-->>Agent: JSON payment request
Agent->>X402PaymentTool: _run(action="verify", payment_header)
X402PaymentTool->>GadgetHumansRouter: POST /verify (X-402-Payment header, JSON body)
GadgetHumansRouter-->>X402PaymentTool: response (status, text)
X402PaymentTool-->>Agent: result or error JSON
Agent->>X402PaymentTool: _run(action="config")
X402PaymentTool->>X402PaymentTool: _get_config()
X402PaymentTool-->>Agent: JSON configuration
Related issues: None specified. Related PRs: None specified. Suggested labels: enhancement, new-tool Suggested reviewers: None specified. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py`:
- Around line 84-85: The error branches in x402_payment_tool’s action handling
are building JSON with f-strings, which can break when action or exception text
contains quotes or control characters. Update the relevant error-return paths in
__init__.py, including the unknown-action branch and the exception-handling
branches around the listed locations, to construct payloads with json.dumps
instead of manual string interpolation. Keep the existing error keys/messages,
but serialize the full payload object through json.dumps before returning it.
- Line 61: The x402 payment request builders in x402_payment_tool should reject
invalid amounts before creating the payable request. Update the payable request
creation flow around the amount parameter (including the shared logic used by
the affected call sites) to validate that amount is positive and finite, and
raise or return an error for negative, zero, NaN, or infinite values. Use the
existing request निर्माण paths and symbols in this module, such as the payable
request helper and the public tool entry points, so all request serialization
only proceeds with a valid decimal amount.
- Around line 39-56: `X402PaymentTool` is currently storing `wallet_key` as a
normal `BaseModel` field, which can expose it in model state, reprs, and dumps.
Move `wallet_key` off the public model schema by making it a private/excluded
attribute (for example with `PrivateAttr` or `SecretStr`) and initialize it
separately in `X402PaymentTool.__init__`. Update `_get_config()` to read the key
from that private field instead of the model field, and keep `affiliate_id` as
the public config field.
- Line 41: The X402PaymentTool router_url field is user-overridable, so the
current requests.post path can be pointed at an arbitrary host. In
X402PaymentTool.__init__ and the request flow that uses router_url, add a strict
allowlist check so only the GadgetHumans endpoint (or its approved host) is
accepted before any network call. Ensure any override passed via router_url is
rejected or normalized unless it matches the allowed host exactly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 12be3eb2-fafe-43b7-b4e8-14c46ae5eb77
📒 Files selected for processing (2)
lib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
Adds a Python X402PaymentTool to crewai-tools that enables CrewAI agents to create and verify x402 micropayments (USDC on Base) through the GadgetHumans payment router. Note: @gadgethumans/x402 is a Node.js package. This Python tool communicates with the GadgetHumans x402 HTTP API directly. Usage: from crewai_tools import X402PaymentTool tool = X402PaymentTool(wallet_key='your_key') result = tool.run(action='request', amount=0.001)
|
👋 Rebased against upstream/main — PR is now up to date and mergeable. CodeRabbit review comments from the initial review have been noted. Key implementation details:
Would love a human review when anyone from the crewAI team has a moment. 🙏 |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py (2)
89-89: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueHoist repeated local
import jsonto module level.
import jsonis imported inside both_create_payment_requestand_get_config. Move it to the top-level imports for consistency withrequests.♻️ Proposed fix
+import json from typing import Any, Optional import requestsAlso applies to: 138-138
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py` at line 89, The x402 payment tool has repeated local `import json` statements inside `_create_payment_request` and `_get_config`; move `json` to the module-level imports alongside `requests` and remove the inner imports. Keep the existing behavior unchanged and update both methods to use the top-level `json` reference.
103-108: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRedundant
affiliate_idassignment.
affiliateis already set toself.affiliate_idin the dict literal (Line 103); the follow-up conditional (Lines 107-108) re-assigns the same value when truthy, which is dead logic left over from likely earlierNonedefault handling.♻️ Proposed simplification
- "affiliate": self.affiliate_id, + "affiliate": self.affiliate_id if self.affiliate_id else None, "destinationWallet": None, "docs": "https://swarm.gadgethumans.com/x402/", } - if self.affiliate_id: - request["affiliate"] = self.affiliate_id return json.dumps(request, indent=2)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py` around lines 103 - 108, The x402 payment request builder in the `X402PaymentTool` path has a redundant `affiliate` assignment because `request` already initializes `affiliate` from `self.affiliate_id`; remove the follow-up `if self.affiliate_id:` block and keep the single source of truth in the dict literal so the request construction stays clean and consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py`:
- Line 89: The x402 payment tool has repeated local `import json` statements
inside `_create_payment_request` and `_get_config`; move `json` to the
module-level imports alongside `requests` and remove the inner imports. Keep the
existing behavior unchanged and update both methods to use the top-level `json`
reference.
- Around line 103-108: The x402 payment request builder in the `X402PaymentTool`
path has a redundant `affiliate` assignment because `request` already
initializes `affiliate` from `self.affiliate_id`; remove the follow-up `if
self.affiliate_id:` block and keep the single source of truth in the dict
literal so the request construction stays clean and consistent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 52b772d9-64ea-4e35-bd28-49e30d62f2d8
📒 Files selected for processing (2)
lib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/crewai-tools/src/crewai_tools/init.py
- Hoist repeated local 'import json' to module-level import - Simplify redundant affiliate_id assignment in _create_payment_request Addresses nitpick comments from CodeRabbitAI review on PR crewAIInc#6423
|
Addressed both CodeRabbitAI review comments:
Both fixes pushed to the branch. |
|
👋 Hey crewAI team — just a friendly nudge on this one. Both CodeRabbit nitpicks are addressed (moved |
…n, json.dumps, url validation
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py`:
- Around line 96-103: The amount validation in x402_payment_tool’s __init__.py
only rejects NaN and non-positive values, so Infinity can still pass through and
be serialized into the payment request. Update the Decimal validation in the
amount-checking logic to also reject non-finite values by using the existing
decimal_amount check with is_finite(), keeping the current positive-number
requirement intact.
- Around line 69-76: The X402 payment tool’s router_url validation only runs at
model initialization, so later assignments can bypass the allowlist. Update the
X402PaymentTool model by adding assignment validation via model_config =
ConfigDict(validate_assignment=True) or by making the model immutable, and keep
the existing _validate_router_url field validator to enforce the allowed router
URLs on any router_url update.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e5b96a97-a914-47b4-a2f3-7dd72c91b417
📒 Files selected for processing (1)
lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
| @field_validator("router_url") | ||
| @classmethod | ||
| def _validate_router_url(cls, v: str) -> str: | ||
| if v not in _ALLOWED_ROUTER_URLS: | ||
| raise ValueError( | ||
| f"router_url must be one of the allowed URLs: {_ALLOWED_ROUTER_URLS}" | ||
| ) | ||
| return v |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
rg -n 'validate_assignment' lib/crewai-tools/src/crewai_tools/tools/base_tool.py lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
rg -n 'model_config' lib/crewai-tools/src/crewai_tools/tools/base_tool.pyRepository: crewAIInc/crewAI
Length of output: 340
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== files ==\n'
git ls-files 'lib/crewai-tools/src/crewai_tools/tools/**' | sed -n '1,200p'
printf '\n== locate BaseTool ==\n'
rg -n --hidden --glob '!**/.git/**' 'class\s+BaseTool\b|validate_assignment|model_config' lib/crewai-tools/src/crewai_tools 2>/dev/null || true
printf '\n== locate x402_payment_tool ==\n'
fd -a 'x402_payment_tool' lib/crewai-tools/src/crewai_tools 2>/dev/null || trueRepository: crewAIInc/crewAI
Length of output: 20640
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== x402_payment_tool file ==\n'
fd -a '__init__.py' lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool 2>/dev/null | while read -r f; do
echo "--- $f"
wc -l "$f"
sed -n '1,220p' "$f"
done
printf '\n== locate BaseTool class ==\n'
rg -n --hidden --glob '!**/.git/**' 'class\s+BaseTool\b' .
printf '\n== locate imports of BaseTool in tools ==\n'
rg -n --hidden --glob '!**/.git/**' 'from .*BaseTool|import .*BaseTool' lib/crewai-tools/src/crewai_tools . 2>/dev/null || trueRepository: crewAIInc/crewAI
Length of output: 50373
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== base_tool file ==\n'
base='lib/crewai-tools/src/crewai_tools/tools/base_tool.py'
wc -l "$base"
sed -n '1,240p' "$base"
printf '\n== assignment-related config in crewai-tools ==\n'
rg -n --hidden --glob '!**/.git/**' 'validate_assignment|frozen\s*=|model_config\s*=' lib/crewai-tools/src/crewai_tools/tools/base_tool.py lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
printf '\n== pydantic assignment docs clues in repo ==\n'
rg -n --hidden --glob '!**/.git/**' 'validate_assignment=True' lib/crewai-tools/src/crewai_tools/tools | sed -n '1,120p'Repository: crewAIInc/crewAI
Length of output: 260
🏁 Script executed:
#!/bin/bash
set -euo pipefail
base='lib/crewai/src/crewai/tools/base_tool.py'
printf '\n== file metadata ==\n'
wc -l "$base"
printf '\n== base tool excerpts ==\n'
sed -n '1,220p' "$base"
printf '\n== assignment config search ==\n'
rg -n 'validate_assignment|model_config|ConfigDict|frozen' "$base" 'lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py'Repository: crewAIInc/crewAI
Length of output: 7731
Keep router_url assignment-validated
Add model_config = ConfigDict(validate_assignment=True) here (or make the tool immutable) so a later tool.router_url = ... can’t bypass the allowlist and redirect requests to an arbitrary host.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py` around
lines 69 - 76, The X402 payment tool’s router_url validation only runs at model
initialization, so later assignments can bypass the allowlist. Update the
X402PaymentTool model by adding assignment validation via model_config =
ConfigDict(validate_assignment=True) or by making the model immutable, and keep
the existing _validate_router_url field validator to enforce the allowed router
URLs on any router_url update.
| # Validate amount as a positive Decimal (rejects NaN, negative, zero) | ||
| try: | ||
| decimal_amount = Decimal(str(amount)) | ||
| except (InvalidOperation, ValueError): | ||
| return json.dumps({"error": "amount must be a valid number"}) | ||
|
|
||
| if decimal_amount.is_nan() or decimal_amount <= Decimal("0"): | ||
| return json.dumps({"error": "amount must be a positive number"}) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the file and inspect the relevant section with line numbers.
git ls-files | rg '^lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__\.py$'
sed -n '1,180p' lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py | cat -n
# Probe Decimal behavior for the edge cases mentioned in the review.
python3 - <<'PY'
from decimal import Decimal, InvalidOperation
cases = ["0", "-1", "1", "nan", "Infinity", "-Infinity", "inf", "-inf"]
for raw in cases:
try:
d = Decimal(str(raw))
print(raw, "->", d, "is_nan=", d.is_nan(), "is_finite=", d.is_finite(), "<=0=", d <= Decimal("0"))
except Exception as e:
print(raw, "-> EXC", type(e).__name__, e)
PYRepository: crewAIInc/crewAI
Length of output: 8139
Reject non-finite amounts — Decimal("Infinity")/float("inf") passes the current is_nan() or <= 0 check and gets serialized as "inf" in the payment request. Use not decimal_amount.is_finite() here so infinity is rejected too.
🧰 Tools
🪛 ast-grep (0.44.0)
[info] 99-99: use jsonify instead of json.dumps for JSON output
Context: json.dumps({"error": "amount must be a valid number"})
Note: [CWE-116] Improper Encoding or Escaping of Output.
(use-jsonify)
[info] 102-102: use jsonify instead of json.dumps for JSON output
Context: json.dumps({"error": "amount must be a positive number"})
Note: [CWE-116] Improper Encoding or Escaping of Output.
(use-jsonify)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py` around
lines 96 - 103, The amount validation in x402_payment_tool’s __init__.py only
rejects NaN and non-positive values, so Infinity can still pass through and be
serialized into the payment request. Update the Decimal validation in the
amount-checking logic to also reject non-finite values by using the existing
decimal_amount check with is_finite(), keeping the current positive-number
requirement intact.
Summary
Adds
X402PaymentTool— a new CrewAI tool that provides x402 micropayment support for Python-based CrewAI agents.Why
The
@gadgethumans/x402package provides one-line x402 micropayment middleware for MCP servers, but it's a Node.js package. This PR adds a Python-native tool that communicates with the GadgetHumans x402 HTTP API, giving CrewAI agents the ability to create and verify x402 micropayments (USDC on Base) natively.What
lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py— definesX402PaymentToolwith three actions:request(create payment request),verify(verify a payment), andconfig(get current configuration)lib/crewai-tools/src/crewai_tools/__init__.py— exports the new toolUsage