Repository navigation
Conversation
Strix Security ReviewWarning This pull request has 2 commits after the last Strix review ( ✅ No open security findings. PR summary: Adds `StallRowPageMetadataTool` to `crewai_tools`, a pay-per-call tool that fetches a page's title, description, OpenGraph/Twitter fields, heading outline and word count from a StallRow endpoint, paying $0.005 USDC per call over x402 on Base via a new bundled `x402_client` that signs an EIP-3009 authorization from a wallet key in `STALLROW_WALLET_PRIVATE_KEY`. Packaging (`crewai-tools[stallrow]` extra), exports, docs, unit tests for the payment/cap paths and a regenerated `tool.specs.json` entry are included. Review summaryI reviewed the full diff of the new StallRow pay-per-call tool and its x402 payment client, including the EIP-3009/EIP-712 signing path, the cap and asset enforcement, the private-key handling, the packaging/exports, and the regenerated tool spec. The payment gates hold up: the asset is pinned to USDC on Base, the network is pinned to eip155:8453, the amount is refused before signing when it exceeds the cap, and with no wallet configured the client fails closed; a hostile 402 challenge (over-cap, non-USDC, foreign network, malformed fields) was probed with a real signer and never produced an over-cap or wrong-domain signature. The payer key is read from the environment only, is never a tool field, is never logged, and does not appear in request bodies/headers, tool output, exception text or the generated spec. The one finding raised during the review (redirect following forwarding the signed PAYMENT-SIGNATURE off-origin) was independently validated and withdrawn: it requires control of the payment endpoint's response, which already allows the same outcome, and the forwarded authorization is single-use and bound to the legitimate recipient. A few non-blocking hardening notes for the author: Also note the regenerated Ruled out (7)
Review 1 on this PR · Reviewed by Strix |
Preview approval requiredThis pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit |
The x402 challenge's maxTimeoutSeconds was signed into the EIP-3009 validBefore with no client ceiling, so an endpoint could mint an authorization settleable for years and keep it after a failed response. Refuse any window outside 1-300 seconds before signing, with a refusal test.
If the paid x402 retry's response is lost in transport, settlement is unconfirmed and the metadata was not delivered. The client now re-reads the challenge with one unpaid probe, names any payment terms the fresh challenge changed (network, asset, payTo, amount), and never authorizes a second payment for a call whose first payment may still settle. The new X402ResponseLostError distinguishes payment_confirmed from metadata_delivered instead of reporting the paid call as a plain failure. Adds the response-loss test discussed in crewAIInc#9295 (13 tests total, mock 402 + mocked transport only).
|
Follow-up on the response-loss case raised in #9295: the test is in, as It mocks the flow end to end — 402 challenge, paid retry whose response is lost in transport (
To make that behavior real, |
Resolves #9295
What this adds
StallRowPageMetadataTool— a new tool increwai_toolsthat fetches one web page's declared metadata in a single call: title, description, canonical URL, OpenGraph and Twitter card fields, document language, favicon, the h1/h2 heading outline, and word count. It wraps StallRow's livePOST /v1/page-metadataendpoint and mirrors the layout of the existing API tools (pydantic args schema,env_varsdeclaration, package README, docs page, unit tests, regeneratedtool.specs.jsonentry).Why
CrewAI has no pay-per-call tool today — there is no x402 support anywhere in the codebase. API-key tools force users to create accounts and manage subscriptions for utilities they may call a handful of times. With x402, payment is the auth: an agent pays $0.005 USDC per call, only when it calls, with no signup. Page metadata is the highest-frequency shape for this (an agent checks a page's identity and outline before deciding whether the full content is worth fetching), which makes it the right first tool of this class.
How payment works
x402_client.pyhelper selects theexact/Base requirement, signs an EIP-3009TransferWithAuthorizationas EIP-712 typed data, and retries with thePAYMENT-SIGNATUREheader. The service's facilitator settles USDC on Base server-side. The challenge'sextensions(Bazaar discovery declaration) are echoed into the payment payload, as facilitator catalogues index from the payload.max_price_usdcparameter, orSTALLROW_MAX_PRICE_USDC) defaults to exactly that price and refuses any higher price before anything is signed.STALLROW_WALLET_PRIVATE_KEYenvironment variable only — never a tool parameter, never logged. With no wallet configured, the tool fails closed with an explanation and pays nothing. The tool module imports and constructs fine without the signer installed;eth-accountis imported lazily at payment time and declared viapackage_dependenciesplus a newcrewai-tools[stallrow]optional-dependencies extra.Disclosure
I operate the StallRow service this tool calls — this is a first-party service tool, in the same sense as the Apify and Firecrawl tools already in this directory. The endpoint is live, listed in the CDP Bazaar, and priced at $0.005/call. I'm disclosing the affiliation plainly so maintainers and users can weigh it; the spend cap and fail-closed defaults are designed so an agent can never be charged more than the stated price for a call.
Tests
lib/crewai-tools/tests/tools/test_stallrow_page_metadata_tool.py, HTTP fully mocked): the 402 → sign → retry flow (asserting the decoded payment payload: accepted amount/payTo, EIP-3009 authorization fields, signature shape, bazaar-extension echo), challenge parsing from the response body when the header is absent, cap refusal before signing (default cap, env override, tool-parameter override), fail-closed behaviour with no wallet configured, an upstream 503 after payment surfaced as an error, and a second 402 after payment treated as an error.tool.specs.jsonregenerated with the repo's own generator (one new entry, no unrelated churn).ruff checkandruff format --checkclean under the repo config;mypy(strict, repo config) clean on the new package.Notes for maintainers
docs/edge/en/tools/search-research/and registered indocs/docs.json(edge nav only). Translations (ar, ko, pt-BR) not synced in this PR — happy to add them or leave them to the docs pipeline, as you prefer.Note
High Risk
Adds private-key handling and on-chain USDC payments for a third-party endpoint; mistakes or misconfiguration could spend wallet funds, though caps and fail-closed paths mitigate surprise charges.
Overview
Introduces CrewAI’s first pay-per-call tool,
StallRowPageMetadataTool, which returns structured page metadata (title, OG/Twitter, headings, word count, etc.) from StallRow’s/v1/page-metadataendpoint.Payment uses a new
x402_client(402 challenge → EIP-3009 USDC signing on Base →PAYMENT-SIGNATUREretry) with fail-closed behavior: no wallet means no payment, hard per-call cap (default $0.005), USDC-only settlement, validity-window limits, and no double-pay if the paid response is lost. Signing depends on optionalcrewai-tools[stallrow](eth-account, lazy import).Also adds docs nav/page, package exports, regenerated
tool.specs.json, broad unit tests (mocked HTTP), anduv.lockentries for the Ethereum stack.Reviewed by Cursor Bugbot for commit d64ab00. Bugbot is set up for automated code reviews on this repo. Configure here.