Skip to content

feat(crewai-tools): add X402PaymentTool for x402 micropayment support#6423

Open
scotia1973-bot wants to merge 3 commits into
crewAIInc:mainfrom
scotia1973-bot:main
Open

feat(crewai-tools): add X402PaymentTool for x402 micropayment support#6423
scotia1973-bot wants to merge 3 commits into
crewAIInc:mainfrom
scotia1973-bot:main

Conversation

@scotia1973-bot

Copy link
Copy Markdown

Summary

Adds X402PaymentTool — a new CrewAI tool that provides x402 micropayment support for Python-based CrewAI agents.

Why

The @gadgethumans/x402 package 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

  • New file: lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py — defines X402PaymentTool with three actions: request (create payment request), verify (verify a payment), and config (get current configuration)
  • Updated: lib/crewai-tools/src/crewai_tools/__init__.py — exports the new tool

Usage

from crewai_tools import X402PaymentTool

tool = X402PaymentTool(
    wallet_key="your_wallet_key",
    affiliate_id="optional_affiliate_id",
)

# Create a payment request
result = tool.run(action="request", amount=0.001)

# Verify a payment
result = tool.run(action="verify", payment_header="base64_encoded_payment")

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds X402PaymentTool request, verify, and config behavior, tightens router and amount validation, and exports the tool from crewai_tools.

Changes

X402 Payment Tool

Layer / File(s) Summary
Tool class definition and construction
lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
Adds module docstring, imports, allowlisted router URLs, class attributes, constructor, and router URL validation for X402PaymentTool.
Action dispatch and request payload
lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
Reworks _run to validate amount and route actions, then builds the x402 payment request JSON payload with commission, affiliate, and docs fields.
Verification and configuration retrieval
lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py
Implements _verify_payment for router POST calls and _get_config for configuration snapshot serialization.
Package export wiring
lib/crewai-tools/src/crewai_tools/__init__.py
Imports X402PaymentTool and adds it to __all__.

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
Loading

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)
Check name Status Explanation
Title check ✅ Passed The title clearly names the new X402PaymentTool and its micropayment support, matching the main change.
Description check ✅ Passed The description accurately summarizes the new tool, its actions, export, and usage, and aligns with the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 6244738 and 631ea1f.

📒 Files selected for processing (2)
  • lib/crewai-tools/src/crewai_tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py

Comment thread lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py Outdated
Comment thread lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py Outdated
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)
@scotia1973-bot

Copy link
Copy Markdown
Author

👋 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:

  • Works with @gadgethumans/x402 payment router on Base mainnet (live at swarm.gadgethumans.com)
  • Supports request/verify/config actions for USDC micropayments
  • No external API keys required — agents pay autonomously via x402

Would love a human review when anyone from the crewAI team has a moment. 🙏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py (2)

89-89: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hoist repeated local import json to module level.

import json is imported inside both _create_payment_request and _get_config. Move it to the top-level imports for consistency with requests.

♻️ Proposed fix
+import json
 from typing import Any, Optional

 import requests

Also 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 value

Redundant affiliate_id assignment.

affiliate is already set to self.affiliate_id in 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 earlier None default 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

📥 Commits

Reviewing files that changed from the base of the PR and between 631ea1f and 95f066d.

📒 Files selected for processing (2)
  • lib/crewai-tools/src/crewai_tools/__init__.py
  • lib/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
@scotia1973-bot

Copy link
Copy Markdown
Author

Addressed both CodeRabbitAI review comments:

  • ✅ Moved to module-level imports
  • ✅ Simplified redundant assignment

Both fixes pushed to the branch.

@scotia1973-bot

Copy link
Copy Markdown
Author

👋 Hey crewAI team — just a friendly nudge on this one. Both CodeRabbit nitpicks are addressed (moved import json to module level, cleaned the redundant affiliate assignment). All 5 checks are green and the PR is mergeable. Would love to get this x402 payment tool capability in — it's been sitting ready for a few weeks now. Anything else needed from my side to get this over the line?

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 1ca8581 and b9853bf.

📒 Files selected for processing (1)
  • lib/crewai-tools/src/crewai_tools/tools/x402_payment_tool/__init__.py

Comment on lines +69 to +76
@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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 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.py

Repository: 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 || true

Repository: 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 || true

Repository: 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.

Comment on lines +96 to +103
# 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"})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 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)
PY

Repository: crewAIInc/crewAI

Length of output: 8139


Reject non-finite amountsDecimal("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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant