Documentation menu

MCP server reference

Every tool, resource and prompt of the Settlens MCP server with its inputs and outputs, the stdio and Streamable HTTP transports, authentication, limits and errors.

In short

The Settlens MCP server has 8 read-only tools over stdio and Streamable HTTP: 6 offline tools, public at https://settlens.com/mcp, and 2 passive network tools that need a token there. evaluate_payment_requirements answers pay, review or do_not_pay; pay needs pay_to, assets, networks and max_amount in the policy.

On this page

The Settlens MCP server lets an AI agent check an x402 payment request before it pays, and verify payments, reports and attestations, from any Model Context Protocol client. This reference is generated from the server code of Settlens 0.5.0 when the site is built: the tables are the server's own tools/list, resources/list and prompts/list answers, and every example response was produced by it.

For an overview and setup in one page, see the MCP server. For the reasoning behind the policy fields, read the agent spending policy guide and why agents should verify before paying. To try evaluate_payment_requirements without a client, use the x402 payment request checker in your browser.

Server name
settlens (Settlens), version 0.5.0
Protocol revisions
2025-06-18, 2025-03-26 and 2024-11-05; 2025-06-18 when the client asks for another one
Transports
stdio (settlens mcp) and Streamable HTTP at https://settlens.com/mcp
Capabilities
tools, resources and prompts, none of which change at runtime
Tools
8: 6 offline, 2 network (passive only)
Never
signs, pays, settles, broadcasts, holds funds or needs a wallet or payment key

Connect

The remote endpoint needs nothing installed. The local server runs from the Settlens source, which pilot participants receive (Node.js 20.19 or newer, then npm ci --ignore-scripts).

Where each tool is available
SetupCommand or URLTools
Remote, publichttps://settlens.com/mcp6 offline tools
Remote, with tokenhttps://settlens.com/mcp + Authorization: Bearer <token>All 8
Local stdionode src/cli.js mcpAll 8, with no destination restriction
Containerdocker run -i --rm settlens:local mcpAll 8 (stdio; keep -i)
ShellClaude Code
# Remote, offline tools
claude mcp add --transport http settlens https://settlens.com/mcp

# Remote, with the network tools
claude mcp add --transport http settlens https://settlens.com/mcp \
  --header "Authorization: Bearer ${SETTLENS_API_TOKEN}"

# Local stdio, every tool
claude mcp add settlens -- node /absolute/path/to/settlens/src/cli.js mcp
JSONclaude_desktop_config.json (local stdio)
{
  "mcpServers": {
    "settlens": {
      "command": "node",
      "args": [
        "/absolute/path/to/settlens/src/cli.js",
        "mcp"
      ]
    }
  }
}

Claude Desktop reads that file from ~/Library/Application Support/Claude/ on macOS and %APPDATA%\Claude\ on Windows; restart it after editing. On Windows, escape backslashes in the path ("C:\\settlens\\src\\cli.js").

Agent workflow

The server sends these instructions to the client in its initialize result, so a capable client tells the model when to call it:

Settlens checks x402 payment requests before you pay. It never pays, never signs and never needs a wallet or key. On every HTTP 402 response, call evaluate_payment_requirements with the PAYMENT-REQUIRED header value, the URL you requested (resource_url) and your spending policy (at least pay_to, assets, networks and max_amount) BEFORE paying. Never pay when the decision is do_not_pay. When it is review, show the summary and reasons to the human and ask for explicit approval before paying. When it is pay, pay only with one of the entries listed in acceptable.

  1. Request the paid URL without any payment header.
  2. On HTTP 402, read the PAYMENT-REQUIRED header.
  3. Call evaluate_payment_requirements with payment_required (the raw header value), resource_url (the exact URL requested) and policy.
  4. do_not_pay: never pay. review: show the summary and reasons, wait for explicit approval. pay: pay with one entry of acceptable only, using your own wallet tooling.

Enforce the decision outside the model

The decision is advice to the model. If your agent can pay, make the payment tool itself refuse unless the last decision for that URL was pay and the chosen option is in acceptable.

Decision rules

A PaymentRequired usually advertises several options in accepts, and the agent pays with one. The server's terms are judged as a whole (decoding, the official @x402/core 2.28.0 schema, the V2 shape, the resource.url binding, well-formed options, duplicates, authorization windows). The policy is applied to each option:

  • An option is acceptable when it is well formed and satisfies every policy constraint you set.
  • policy.acceptable_option (high) passes when at least one option is acceptable. Its message counts the rejected options by constraint.
  • The per-constraint checks policy.scheme, policy.network, policy.recipient, policy.asset and policy.maximum_amount only summarize: pass when every option complies, warn (severity info) when some do not, fail when none does. They never decide on their own.

The decision, for evaluate_payment_requirements and scan_endpoint alike:

  1. Any failing check of severity high or critical (policy.acceptable_option included, the per-constraint summaries excluded): do_not_pay, and acceptable is empty.
  2. Otherwise, any failing or warning check other than the per-constraint summaries: review. A missing resource_url lands here, because binding.resource_url becomes a medium warning.
  3. Otherwise, when no option could be evaluated against the policy: review.
  4. Otherwise, when the policy does not set pay_to, assets, networks and max_amount: review, with a reason naming the missing fields.
  5. Otherwise: pay, with an option from acceptable only.

x402 over HTTP only

The tools evaluate x402 over HTTP. x402 carried over MCP itself, where a paid tool answers with isError and a PaymentRequired in structuredContent and the client pays in _meta["x402/payment"], is not supported. The transport's own example names an mcp:// resource: schema.payment_required rejects it, because Settlens requires an absolute http(s) resource.url where the x402 specification sets no scheme, so the decision is do_not_pay. resource_url accepts only http(s) URLs.

The CLI keeps the stricter rule meant for merchants: in settlens scan, every advertised option must be within the configured policy (see configuration).

The policy object

Optional on both tools that take it; no other fields are accepted. Atomic units: USDC has 6 decimals, so "10000" is 0.01 USDC.

policy fields
FieldType and rulesDescription
max_amountstring
digits only (integer string); at most 78 characters
Maximum amount you accept to pay, as an integer string in the asset's atomic units (USDC has 6 decimals: "10000" = 0.01 USDC). Set assets too, so the unit is fixed.
networksarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed CAIP-2 networks, for example ["eip155:8453"] for Base mainnet.
schemesarray of string
1 to 32 items; each item: non-empty, at most 64 characters
Allowed x402 schemes, for example ["exact"].
pay_toarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed recipient addresses (payTo) you have reviewed. EVM 0x addresses are compared case-insensitively, other formats (Solana base58, ...) exactly.
assetsarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed tokens (asset): contract addresses or mints you have reviewed, for example ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"] for USDC on Base. EVM 0x addresses are compared case-insensitively, other formats (Solana mints, ...) exactly.

The same values are validated as a configuration file policy; a value the schema accepts but that validation rejects makes the call fail with isError. With several assets, max_amount applies to each in its own decimals: list only assets with the same decimals.

Tools

Tools at a glance
ToolModeRemote accessSummary
evaluate_payment_requirementsofflinepublicShould I pay this x402 request?
verify_payment_signatureofflinepublicVerify an exact EVM payment signature
verify_solana_transactionofflinepublicVerify Solana signatures and payment instructions
verify_reportofflinepublicVerify a Settlens report
verify_attestationofflinepublicVerify a Settlens evidence attestation
list_checksofflinepublicList Settlens checks
scan_endpointnetwork, passivetokenPassively scan an x402 endpoint
scan_facilitatornetwork, passivetokenPassively scan an x402 facilitator

Arguments use snake_case; URL arguments may not contain whitespace. A call whose arguments do not match the input schema is rejected with JSON-RPC error -32602 before the tool runs. Every successful result carries structuredContent, matching the tool's output schema, and the same JSON as one text content block.

evaluate_payment_requirements

Offline Should I pay this x402 request?

Call this on every HTTP 402 response BEFORE paying. Give it the PAYMENT-REQUIRED header value, the URL you requested and your spending policy (pay_to, assets, networks and max_amount, optionally schemes). Offline: it decodes and validates the x402 V2 payment request with the official @x402/core schema, checks that it is bound to the URL you requested, validates every payment option (scheme, CAIP-2 network, amount, asset, payTo, timeout window) and applies your policy to each option, since you pay with only one. Returns decision 'pay', 'review' (ask the human first) or 'do_not_pay' (never pay), the reasons, and the payment options that satisfy your policy (acceptable). It does not check balances, settlement or what the token at asset does, and it never pays.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false.

evaluate_payment_requirements arguments
FieldType and rulesDescription
payment_requiredstring or object, required
at most 131072 characters
The raw PAYMENT-REQUIRED response header value (Base64 JSON), the same object as a JSON string, or the decoded object.
resource_urlstring
starts with http:// or https://; at least 8 characters; at most 2048 characters
The exact URL you requested. Strongly recommended: without it the resource binding cannot be confirmed and the decision is at best 'review'.
policyobjectYour spending policy, applied to each advertised payment option. Set at least pay_to, assets, networks and max_amount: without all four, the decision can never be 'pay'.
policy.max_amountstring
digits only (integer string); at most 78 characters
Maximum amount you accept to pay, as an integer string in the asset's atomic units (USDC has 6 decimals: "10000" = 0.01 USDC). Set assets too, so the unit is fixed.
policy.networksarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed CAIP-2 networks, for example ["eip155:8453"] for Base mainnet.
policy.schemesarray of string
1 to 32 items; each item: non-empty, at most 64 characters
Allowed x402 schemes, for example ["exact"].
policy.pay_toarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed recipient addresses (payTo) you have reviewed. EVM 0x addresses are compared case-insensitively, other formats (Solana base58, ...) exactly.
policy.assetsarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed tokens (asset): contract addresses or mints you have reviewed, for example ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"] for USDC on Base. EVM 0x addresses are compared case-insensitively, other formats (Solana mints, ...) exactly.
evaluate_payment_requirements structuredContent
FieldType and rulesDescription
decisionone of pay, review, do_not_pay, requiredpay, review or do_not_pay, from the decision rules above.
summarystring, requiredOne sentence to show the human.
reasonsarray of string, requiredWhy the decision is not pay: one line per deciding check ("<check id>: <message>") or the missing policy fields. Empty for pay.
acceptablearray of object, requiredThe advertised options (accepts entries) that are well formed and satisfy every policy constraint you set. Always empty for do_not_pay. Pay with one of these only.
checksarray of object, requiredEvery check evaluated, including the per-constraint policy summaries and policy.acceptable_option (see check result).

verify_payment_signature

Offline Verify an exact EVM payment signature.

Offline cryptographic check of an x402 'exact' EVM payment authorization (EIP-3009 TransferWithAuthorization, EIP-712). Rebuilds the EIP-712 domain from the requirement (extra.name, extra.version, asset, chain id from the eip155 network), recovers the signer and checks it equals authorization.from, that the transfer is bound to the requirement's payTo, amount and asset, and that the time is inside validAfter/validBefore. It does NOT check on-chain balances, nonce reuse/replay or settlement, and needs no RPC.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false.

verify_payment_signature arguments
FieldType and rulesDescription
requirementobject, requiredThe payment requirement (one accepts entry) the payment was made for: scheme, network (eip155:<chainId>), amount, asset, payTo, maxTimeoutSeconds, extra { name, version }.
payloadobject or string, required
at most 131072 characters
The payment payload { signature, authorization }, a full PaymentPayload object, or the raw Base64 PAYMENT-SIGNATURE header value.
nowstring or integer
at most 64 characters; ≥ 0
Time to check the authorization window against, as an ISO 8601 string or Unix seconds. Defaults to the current time.
verify_payment_signature structuredContent
FieldType and rulesDescription
schemestring, requiredAlways exact for this tool.
verifiedboolean, requiredTrue only when the signature, recipient, amount and time window all hold.
signatureValidboolean, requiredThe recovered signer equals authorization.from.
recoveredstring or nullThe recovered signer address, or null when no signature could be recovered.
recipientBoundbooleanauthorization.to equals the requirement's payTo. Omitted when the payload has no signature or authorization.
amountBoundbooleanauthorization.value equals the requirement's amount. Omitted in the same cases.
freshbooleancheckedAt is inside validAfter and validBefore. Omitted in the same cases.
reasonsarray of string, requiredWhy verification failed; empty when verified.
checkedAtinteger, requiredThe Unix time, in seconds, used for the window check.

verify_solana_transaction

Offline Verify Solana signatures and payment instructions.

Offline check of every ed25519 signature of a Base64 Solana transaction (legacy or versioned), for example payload.transaction of an x402 Solana payment. Each required signer slot is checked against its signer key. An x402 exact SVM payload is partially signed: the client signs, and the facilitator adds the fee-payer signature at settlement, so empty (all-zero) slots are reported as pendingSigners (feePayerPending is true when the fee payer has not signed yet) rather than as failures. verified is true only when at least one signature is present, every present signature is valid and the number of signature slots matches the message header. Transactions larger than 1232 bytes are refused. An optional reviewed requirement adds conservative offline TransferChecked inspection in payment: mint, merchant ATA, exact amount, signed client, sponsor isolation, compute caps and memo. payment.verified is separate from signature-only verified. Unsupported programs, unresolved address lookup tables and Token-2022 extensions cannot be verified offline. It does NOT check account state, balances, execution, replay or settlement.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false.

verify_solana_transaction arguments
FieldType and rulesDescription
transactionstring, required
matches ^[A-Za-z0-9+/]+={0,2}$; at least 4 characters; at most 16384 characters
The Base64-encoded serialized Solana transaction.
requirementobjectReviewed exact SVM terms for optional conservative instruction inspection; never execution proof.
requirement.schemeone of exact
requirement.networkstring
matches ^solana:[A-Za-z0-9]{1,64}$; at most 71 characters
requirement.assetstring, required
at least 32 characters; at most 44 characters
requirement.payTostring, required
at least 32 characters; at most 44 characters
requirement.amountstring, required
matches ^[1-9][0-9]{0,19}$; at most 20 characters
requirement.maxTimeoutSecondsinteger
≥ 1
requirement.extraobject, required
requirement.extra.feePayerstring, required
at least 32 characters; at most 44 characters
requirement.extra.memostring
non-empty; at most 256 characters
requirement.extra.decimalsinteger
≥ 0; ≤ 255
requirement.extra.recentBlockhashstring
at most 44 characters
requirement.extra.lastValidBlockHeightstring
at most 20 characters
policyobject
policy.maxComputeUnitsinteger
≥ 1; ≤ 1400000
policy.maxPriorityFeeMicroLamportsstring
matches ^(0|[1-9][0-9]{0,19})$; at most 20 characters
verify_solana_transaction structuredContent
FieldType and rulesDescription
schemestring, requiredAlways exact-solana for this tool.
versionstringlegacy or v0. Omitted when the transaction carries no signature.
signatureCountintegerNumber of signature slots in the transaction, empty ones included. Omitted in the same case.
feePayerstring or null, requiredBase58 fee-payer public key, or null when the transaction carries no signature.
feePayerPendingbooleanTrue while the fee payer's slot (slot 0) is empty, as in an x402 payload before the facilitator signs at settlement. Omitted when the transaction carries no signature.
signersarray of objectOne entry per signature slot, in order (slot 0 is the fee payer), with its base58 publicKey and status: valid, invalid or pending.
pendingSignersarray of stringBase58 public keys of the signer slots that are still empty (all zero). Omitted in the same case.
signatureValidboolean, requiredAt least one signature is present and every present signature verifies over the message with ed25519. Empty slots are pending, not invalid.
verifiedboolean, requiredSignature-only: signatureValid and matching required slot count. This never proves a correct or settled payment.
paymentobjectPresent only with a reviewed requirement. Separate conservative offline instruction result: status, verified, checks, transfers and explicit limitations. Unresolved lookup accounts, unknown/CPI programs and Token-2022 effects cannot be verified offline.
payment.statusone of verified, rejected, unverified, required
payment.verifiedboolean, required
payment.checksarray of object, required
payment.transfersarray of object, required
payment.limitationsarray of string, required
reasonsarray of string, requiredWhy verification failed; empty when verified.

verify_report

Offline Verify a Settlens report.

Offline check that a Settlens JSON report has not been modified since it was generated, by recomputing its deterministic SHA-256 integrity digest.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false.

verify_report arguments
FieldType and rulesDescription
reportobject, requiredThe complete Settlens JSON report.
verify_report structuredContent
FieldType and rulesDescription
validboolean, requiredThe recomputed digest equals the digest stored in the report.
expectedstring or null, requiredThe digest stored in the report's integrity block, or null without one.
actualstring or null, requiredThe digest recomputed from the report content, or null.
reasonstring, requiredOne sentence explaining the result.

verify_attestation

Offline Verify a Settlens evidence attestation.

Offline check of a Settlens evidence attestation: verifies its ed25519 signature and, when the report is supplied, that the report is intact and is the one the attestation covers. A valid signature only proves the attestation was signed by the key it embeds; pass expected_public_key to require a specific signer.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false.

verify_attestation arguments
FieldType and rulesDescription
attestationobject, requiredThe attestation JSON object.
reportobjectOptional Settlens JSON report the attestation should cover.
expected_public_keystring
at most 4096 characters
Optional signer the attestation must come from: an ed25519 public key PEM, or the 0x-prefixed 32-byte hex key printed by evidence keygen.
verify_attestation structuredContent
FieldType and rulesDescription
validboolean, requiredThe signature is valid, the report matches when one was supplied, and the signer equals expected_public_key when one was supplied.
signatureValidboolean, requiredThe ed25519 signature verifies with the attestation's public key.
reportMatchesboolean or null, requiredThe supplied report is intact and is the one attested; null when no report was supplied.
publicKeyMatchesboolean or nullThe attestation's publicKey equals expected_public_key; null when none was supplied.
publicKeystring or null, requiredThe signer's public key from the attestation, or null.
attestedAtstring or null, requiredThe attestation timestamp, or null.
reasonsarray of string, requiredWhy verification failed; empty when valid.

list_checks

Offline List Settlens checks.

Lists the checks Settlens can report, with what each verifies, why it matters and how to fix it. Use it to explain a check id returned by another tool.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false.

list_checks arguments
FieldType and rulesDescription
categoryone of transport, schema, binding, freshness, policy, stability, security, safety, facilitatorOnly return checks of this category.
modeone of passive, offline, active, facilitatorOnly return checks of this mode: offline (from the PaymentRequired content), passive (live request), active (authorized probe) or facilitator.
list_checks structuredContent
FieldType and rulesDescription
countinteger, requiredNumber of checks returned.
checksarray of object, requiredCatalog entries (see catalog entry).

scan_endpoint

Network, passive Passively scan an x402 endpoint. On the remote endpoint, only with a token.

Sends unauthenticated GET requests (2 samples, redirects not followed) to an x402 paid resource and runs Settlens' passive checks: HTTP 402, PAYMENT-REQUIRED header and decoding, official @x402/core schema, resource binding, payment options, HTTPS, response size and term stability, then applies your policy (pay_to, assets, max_amount, networks, schemes) to each payment option. Passive only: it never sends a payment or a PAYMENT-SIGNATURE. Returns the same pay/review/do_not_pay decision rules as evaluate_payment_requirements, the issues found, the payment options seen, those that satisfy your policy (acceptable) and the report integrity digest.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true.

scan_endpoint arguments
FieldType and rulesDescription
urlstring, required
starts with http:// or https://; at least 8 characters; at most 2048 characters
The x402 paid resource URL to scan.
policyobjectYour spending policy, applied to each advertised payment option. Set at least pay_to, assets, networks and max_amount: without all four, the decision can never be 'pay'.
policy.max_amountstring
digits only (integer string); at most 78 characters
Maximum amount you accept to pay, as an integer string in the asset's atomic units (USDC has 6 decimals: "10000" = 0.01 USDC). Set assets too, so the unit is fixed.
policy.networksarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed CAIP-2 networks, for example ["eip155:8453"] for Base mainnet.
policy.schemesarray of string
1 to 32 items; each item: non-empty, at most 64 characters
Allowed x402 schemes, for example ["exact"].
policy.pay_toarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed recipient addresses (payTo) you have reviewed. EVM 0x addresses are compared case-insensitively, other formats (Solana base58, ...) exactly.
policy.assetsarray of string
1 to 32 items; each item: non-empty, at most 128 characters
Allowed tokens (asset): contract addresses or mints you have reviewed, for example ["0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"] for USDC on Base. EVM 0x addresses are compared case-insensitively, other formats (Solana mints, ...) exactly.
timeout_msinteger
≥ 1000; ≤ 30000
Per-request timeout in milliseconds (default 8000).
scan_endpoint structuredContent
FieldType and rulesDescription
urlstring, requiredThe scanned URL, sanitized.
httpStatusinteger or null, requiredStatus of the first response, or null when no response was received.
modestring, requiredAlways passive.
decisionone of pay, review, do_not_pay, requiredpay, review or do_not_pay, from the decision rules above.
summarystring, requiredOne sentence to show the human.
reasonsarray of string, requiredWhy the decision is not pay: one line per deciding check ("<check id>: <message>") or the missing policy fields. Empty for pay.
countsobject, requiredNumber of checks per status, over the scan and policy checks.
counts.passinteger, requiredPassing checks.
counts.failinteger, requiredFailing checks.
counts.warninteger, requiredWarnings.
counts.skipinteger, requiredSkipped checks.
issuesarray of object, requiredThe failing and warning checks (see check result).
paymentRequirementsarray of object, requiredThe accepts entries of the first response that are objects, at most 32.
acceptablearray of object, requiredThe advertised options (accepts entries) that are well formed and satisfy every policy constraint you set. Always empty for do_not_pay. Pay with one of these only.
reportIdstring, requiredIdentifier of the report built for this scan.
reportDigeststring, requiredSHA-256 digest of that report. It covers the server's terms, not your policy.

scan_facilitator

Network, passive Passively scan an x402 facilitator. On the remote endpoint, only with a token.

Reads GET <url>/supported from an x402 facilitator (redirects not followed) and checks reachability, HTTP status, JSON media type, HTTPS, the SupportedResponse schema, x402 V2 capability and duplicate kinds. Passive only: it never calls /verify or /settle.

Annotations: readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: true.

scan_facilitator arguments
FieldType and rulesDescription
urlstring, required
starts with http:// or https://; at least 8 characters; at most 2048 characters
The facilitator base URL (or its /supported URL).
timeout_msinteger
≥ 1000; ≤ 30000
Request timeout in milliseconds (default 8000).
scan_facilitator structuredContent
FieldType and rulesDescription
urlstring, requiredThe facilitator URL that was read, sanitized.
httpStatusinteger or null, requiredStatus of GET /supported, or null when no response was received.
okboolean, requiredNo check failed.
countsobject, requiredNumber of checks per status.
counts.passinteger, requiredPassing checks.
counts.failinteger, requiredFailing checks.
counts.warninteger, requiredWarnings.
counts.skipinteger, requiredSkipped checks.
issuesarray of object, requiredThe failing and warning checks (see check result).
supportedKindsarray of object, requiredUp to 100 advertised kinds, each with x402Version, scheme and network.
extensionsarray of string, requiredExtensions the facilitator advertises.
signerNetworksarray of string, requiredNetwork patterns that have signer metadata, for example eip155:*.
reportIdstring, requiredIdentifier of the report built for this scan.
reportDigeststring, requiredSHA-256 digest of that report.

Shared shapes

check result (items of checks and issues)
FieldType and rulesDescription
idstring, requiredCheck id; settlens://checks/<id> explains it.
titlestring, requiredShort title.
statusone of pass, fail, warn, skip, requiredOutcome of the check.
severityone of info, low, medium, high, critical, requiredHow serious a failure is. A high or critical failure, other than a per-constraint policy summary, makes the decision do_not_pay.
messagestring, requiredWhat was observed: one line, control characters removed, at most 300 characters.
recommendationstringHow to fix it, when the check did not pass.
catalog entry (items of list_checks checks)
FieldType and rulesDescription
idstring, requiredCheck id.
titlestring, requiredShort title.
categorystring, requiredFirst segment of the id.
modestring, requiredoffline, passive, active or facilitator.
severitystring, requiredDefault severity.
verifiesstring, requiredWhat the check verifies.
whystring, requiredWhy it matters.
fixstring, requiredHow to fix a failure.
specstringSpecification the check refers to, or empty.
specUrlstringLink to that specification, or empty.

Example: an over-budget request

The bundled demo resource (npm run demo:server) asks for 10000 atomic units on eip155:84532. With max_amount at 5000, no option is acceptable:

JSONtools/call request
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "evaluate_payment_requirements",
    "arguments": {
      "payment_required": "eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZWFkZXIgaXMgcmVxdWlyZWQiLCJyZXNvdXJjZSI6eyJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjQ0MDIvc2VjdXJlIiwiZGVzY3JpcHRpb24iOiJTZXR0bGVucyBsb2NhbCBkZW1vbnN0cmF0aW9uIHJlc291cmNlIiwibWltZVR5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwic2VydmljZU5hbWUiOiJTZXR0bGVucyBEZW1vIiwidGFncyI6WyJzZWN1cml0eSIsImRlbW8iXX0sImFjY2VwdHMiOlt7InNjaGVtZSI6ImV4YWN0IiwibmV0d29yayI6ImVpcDE1NTo4NDUzMiIsImFtb3VudCI6IjEwMDAwIiwiYXNzZXQiOiIweDAzNkNiRDUzODQyYzU0MjY2MzRlNzkyOTU0MWVDMjMxOGYzZENGN2UiLCJwYXlUbyI6IjB4MjA5NjkzQmM2YWZjMEM1MzI4YkEzNkZhRjAzQzUxNEVGMzEyMjg3QyIsIm1heFRpbWVvdXRTZWNvbmRzIjo2MCwiZXh0cmEiOnsibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9fV0sImV4dGVuc2lvbnMiOnt9fQ==",
      "resource_url": "http://127.0.0.1:4402/secure",
      "policy": {
        "pay_to": [
          "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
        ],
        "assets": [
          "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
        ],
        "networks": [
          "eip155:84532"
        ],
        "max_amount": "5000",
        "schemes": [
          "exact"
        ]
      }
    }
  }
}
JSONresult.structuredContent
{
  "decision": "do_not_pay",
  "summary": "Do not pay: 1 blocking problem(s) found.",
  "reasons": [
    "policy.acceptable_option: 0 of 1 payment option(s) satisfy the policy; 1 rejected (max_amount: 1)."
  ],
  "acceptable": [],
  "checks": [
    {
      "id": "transport.payment_required_encoding",
      "title": "PAYMENT-REQUIRED decodes as Base64 JSON",
      "status": "pass",
      "severity": "high",
      "message": "The canonical header decoded successfully."
    },
    {
      "id": "schema.official_x402_sdk",
      "title": "Official x402 SDK accepts PaymentRequired",
      "status": "pass",
      "severity": "high",
      "message": "The header passes @x402/core 2.28.0 schema validation."
    },
    {
      "id": "schema.payment_required",
      "title": "PaymentRequired matches the x402 V2 core shape",
      "status": "pass",
      "severity": "high",
      "message": "The decoded header contains the required V2 fields."
    },
    {
      "id": "binding.resource_url",
      "title": "Payment requirement is bound to the requested resource",
      "status": "pass",
      "severity": "high",
      "message": "resource.url exactly matches the protected request URL."
    },
    {
      "id": "schema.payment_requirements",
      "title": "Accepted payment requirements are well formed",
      "status": "pass",
      "severity": "high",
      "message": "1 payment requirement(s) use valid core fields."
    },
    {
      "id": "schema.duplicate_requirements",
      "title": "Payment choices are unambiguous",
      "status": "pass",
      "severity": "low",
      "message": "No duplicate accepted requirements were found."
    },
    {
      "id": "freshness.timeout_window",
      "title": "Authorization windows are reasonably bounded",
      "status": "pass",
      "severity": "medium",
      "message": "All advertised maxTimeoutSeconds values are at most one hour."
    },
    {
      "id": "policy.scheme",
      "title": "Schemes match the local policy",
      "status": "pass",
      "severity": "high",
      "message": "All 1 payment option(s) satisfy the schemes policy."
    },
    {
      "id": "policy.network",
      "title": "Networks match the local policy",
      "status": "pass",
      "severity": "high",
      "message": "All 1 payment option(s) satisfy the networks policy."
    },
    {
      "id": "policy.recipient",
      "title": "Recipients match the local policy",
      "status": "pass",
      "severity": "high",
      "message": "All 1 payment option(s) satisfy the pay_to policy."
    },
    {
      "id": "policy.asset",
      "title": "Assets match the local policy",
      "status": "pass",
      "severity": "high",
      "message": "All 1 payment option(s) satisfy the assets policy."
    },
    {
      "id": "policy.maximum_amount",
      "title": "Amounts stay within the approved ceiling",
      "status": "fail",
      "severity": "high",
      "message": "All 1 payment option(s) violate the max_amount policy."
    },
    {
      "id": "policy.acceptable_option",
      "title": "At least one payment option satisfies the policy",
      "status": "fail",
      "severity": "high",
      "message": "0 of 1 payment option(s) satisfy the policy; 1 rejected (max_amount: 1).",
      "recommendation": "Do not pay. Ask the merchant for terms within your policy or change the policy deliberately."
    }
  ]
}

Resources

Resources
URITypeContent
settlens://checksapplication/jsonEvery check Settlens can report, as JSON.
settlens://policy-guidetext/markdownHow to write the policy passed to evaluate_payment_requirements.
settlens://checks/{id}text/markdownExplanation of one Settlens check id, for example settlens://checks/binding.resource_url. Template from resources/templates/list.

resources/list returns the 2 fixed resources and one settlens://checks/{id} entry per check (36, the full check catalog of 36 ids). An unknown URI returns error -32002.

Textresources/read settlens://checks/binding.resource_url
# Payment requirement is bound to the requested resource

`binding.resource_url` · category binding · mode offline · default severity high

## What it verifies

resource.url equals the URL that was requested, including path and query (the fragment is ignored). In evaluate_payment_requirements this check is a warning when no resource URL is supplied, because the binding cannot be confirmed.

## Why it matters

When the advertised resource differs from the requested one, a payment can be steered to, or reused for, a different resource than the one the agent asked for.

## How to fix

Bind the payment requirement to the exact canonical resource URL, including its path and relevant query.

## Specification

[x402 V2 core specification](https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md)

Prompts

Prompts
NameArgumentsDescription
x402_preflighturl (required): The x402 paid resource URL.Guides the agent to read the 402 PAYMENT-REQUIRED header, call evaluate_payment_requirements with its policy and act on the decision.

The URL must be an http(s) URL without whitespace. The message returned by prompts/get for https://merchant.example/paid:

Textx402_preflight
Before paying for the x402 resource at https://merchant.example/paid:

1. Request https://merchant.example/paid without any payment header.
2. If the response is not HTTP 402, do not pay and report what you received.
3. Read the PAYMENT-REQUIRED response header (a Base64 JSON value).
4. Call the Settlens tool evaluate_payment_requirements with:
   - payment_required: the exact PAYMENT-REQUIRED header value
   - resource_url: https://merchant.example/paid
   - policy: your spending policy, at least pay_to (allowed recipients), assets (allowed token addresses), networks (allowed CAIP-2 networks) and max_amount (maximum amount in atomic units); add schemes when you know them.
5. Act on the decision:
   - do_not_pay: do not pay. Report the summary and reasons to the user.
   - review: do not pay yet. Show the summary and reasons to the user and ask for explicit approval.
   - pay: you may pay, using only one of the entries listed in acceptable, never above max_amount.

Settlens never pays, never holds keys and never calls /settle: the payment itself is made by your own wallet tooling.

Transports

stdio

settlens mcp (or node src/cli.js mcp) reads one JSON-RPC message per line on stdin and writes one per line on stdout. Stdout carries protocol messages only; diagnostics go to stderr. Requests are handled concurrently, so responses can arrive in a different order than the requests: match them by id. The process exits when its client closes stdin.

The local server includes the network tools and applies no destination restriction, so you can scan a test server on 127.0.0.1. Only the client that launched the process can talk to it.

Streamable HTTP

https://settlens.com/mcp implements MCP Streamable HTTP in stateless JSON-response mode: one endpoint, POST only, no session id, no SSE stream. Send Content-Type: application/json and an Accept header that allows application/json. MCP-Protocol-Version is optional; when present it must be one of 2025-06-18, 2025-03-26 or 2024-11-05.

On settlens.com, a GET or HEAD of /mcp whose Accept does not include text/event-stream, from a browser, crawler or link checker, receives the MCP web page. Every other request goes to the MCP transport.

HTTP status codes of /mcp
StatusWhenBody
200A JSON-RPC request or batch was handled, including tool results with isError and JSON-RPC errors that carry an id.JSON-RPC response or array
202The body held only notifications or client responses.None
204OPTIONS (CORS preflight). Allows POST, OPTIONS and the headers content-type, accept, authorization, mcp-protocol-version.None
400Parse error (-32700), unsupported MCP-Protocol-Version (-32600), or a message rejected without an id, such as an empty batch.JSON-RPC error, id null
403An Origin header that is not allowed.{"error":"Origin not allowed."}
405A GET or HEAD whose Accept asks for text/event-stream (an SSE request), or any other method than GET, HEAD, POST and OPTIONS, such as DELETE. Allow: POST.JSON error
406Accept does not allow application/json.JSON error
413Body larger than 256 KiB.JSON error
415Content-Type is not application/json.JSON error
429More than 60 requests in 60 seconds from one client IP, where each JSON-RPC message counts (a batch of n counts n). Retry-After gives the seconds to wait.JSON error
500Unexpected server error.JSON-RPC error -32603

Checks run in this order: origin, method, rate limit, content type, Accept, protocol version, size, then JSON parsing. Responses carry Cache-Control: no-store.

Origin rule

Requests without an Origin header, which is what MCP clients outside a browser send, are accepted. A browser request is accepted only from https://settlens.com and https://claude.ai, so the x402 payment request checker on settlens.com can call it from your browser; any other origin gets 403. This blocks DNS-rebinding and cross-site calls; allowed origins get Access-Control-Allow-Origin back.

Protocol versions and batches

The server implements revision 2025-06-18 and answers initialize with the client's requested revision when it is one of 2025-06-18, 2025-03-26 or 2024-11-05, otherwise with 2025-06-18. JSON-RPC batches of 1 to 32 messages are accepted, as revision 2025-03-26 requires; their entries run one after another and the answer is an array. Notifications, such as notifications/initialized, are never answered.

Authentication

The offline tools, resources and prompt need no authentication. scan_endpoint and scan_facilitator are listed by tools/list and callable only when the request carries Authorization: Bearer <token> matching the site's API token, the same token as the HTTP scan API. The comparison is done in constant time on SHA-256 digests. Tokens are issued to pilot participants: request a pilot.

A missing or wrong token is not an HTTP error: the request is served by the public tool set, the network tools are absent from tools/list, and calling one returns:

JSONtools/call scan_endpoint without a token
{
  "jsonrpc": "2.0",
  "id": 3,
  "error": {
    "code": -32602,
    "message": "Unknown tool: scan_endpoint. Network tools require a Settlens API token (Authorization: Bearer) on this endpoint."
  }
}

Before any remote network request, the server refuses non-HTTP(S) URLs, ports other than 80 and 443, URLs with credentials, localhost, .local and .internal names, and hosts whose address, literal or resolved, is private, loopback, link-local, CGNAT, reserved, multicast or used for documentation, including IPv4 addresses embedded in IPv6. Redirects are never followed. The security model covers the remaining assumptions.

Limits

Limits
LimitValueApplies to
Rate limit60 requests per 60 seconds per client IP; each JSON-RPC message counts, so a batch of n counts nHTTP endpoint, every POST
Request body256 KiBHTTP endpoint
Batch size1 to 32 messagesBoth transports
payment_required and Base64 payloads131,072 charactersevaluate_payment_requirements, verify_payment_signature
JSON nesting64 levelsDecoded PaymentRequired objects
Solana transaction1,232 bytes once decodedverify_solana_transaction
Policy lists1 to 32 entries eachpay_to, assets, networks, schemes
Request timeout1,000 to 30,000 ms, default 8,000 msscan_endpoint, scan_facilitator
SamplesUp to 2 identical GET requests; the second only after a decodable PAYMENT-REQUIREDscan_endpoint
Response body read1 MiBscan_endpoint

The stdio transport does not bound line length: its only peer is the local client that started it.

Errors

Two kinds of failure exist, and a do_not_pay is neither: it is a successful result with isError: false.

JSON-RPC errors: the request was not executed
CodeMeaning
-32700Parse error: the body or line is not JSON.
-32600Invalid request: not a JSON-RPC 2.0 object, a bad id, an empty or oversized batch, or an unsupported MCP-Protocol-Version header.
-32601Method not found.
-32602Invalid params: arguments that do not match the input schema, an unknown tool or prompt, or a network tool called without a token.
-32002Resource not found; data.uri echoes the URI.
-32603Internal error.
JSONInvalid arguments: max_amount must be an integer string
{"jsonrpc":"2.0","id":7,"error":{"code":-32602,"message":"Invalid arguments for evaluate_payment_requirements: arguments.policy.max_amount has an invalid format."}}
JSONUnknown method and unknown resource
{"jsonrpc":"2.0","id":9,"error":{"code":-32601,"message":"Method not found: completion/complete"}}
{"jsonrpc":"2.0","id":10,"error":{"code":-32002,"message":"Resource not found","data":{"uri":"settlens://checks/no.such_check"}}}

A tool that starts but cannot complete, for example because a payload is not valid Base64, returns a normal result with isError: true and a text explanation, without structuredContent:

JSONTool failure (verify_payment_signature)
{
  "jsonrpc": "2.0",
  "id": 8,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "verify_payment_signature failed: Header contains characters outside Base64/Base64URL."
      }
    ],
    "isError": true
  }
}

Example sessions

stdio session

ShellThree messages on stdin
printf '%s\n' \
  '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"shell","version":"1"}}}' \
  '{"jsonrpc":"2.0","method":"notifications/initialized"}' \
  '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_checks","arguments":{"category":"binding"}}}' \
  | node src/cli.js mcp

The notification gets no answer, so two lines come back. They are shown indented here; on stdout each is a single line:

JSONstdout
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2025-06-18",
    "capabilities": {
      "tools": {
        "listChanged": false
      },
      "resources": {
        "listChanged": false
      },
      "prompts": {
        "listChanged": false
      }
    },
    "serverInfo": {
      "name": "settlens",
      "title": "Settlens",
      "version": "0.5.0"
    },
    "instructions": "Settlens checks x402 payment requests before you pay. It never pays, never signs and never needs a wallet or key. On every HTTP 402 response, call evaluate_payment_requirements with the PAYMENT-REQUIRED header value, the URL you requested (resource_url) and your spending policy (at least pay_to, assets, networks and max_amount) BEFORE paying. Never pay when the decision is do_not_pay. When it is review, show the summary and reasons to the human and ask for explicit approval before paying. When it is pay, pay only with one of the entries listed in acceptable."
  }
}
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\"count\":1,\"checks\":[{\"id\":\"binding.resource_url\",\"title\":\"Payment requirement is bound to the requested resource\",\"category\":\"binding\",\"mode\":\"offline\",\"severity\":\"high\",\"verifies\":\"resource.url equals the URL that was requested, including path and query (the fragment is ignored). In evaluate_payment_requirements this check is a warning when no resource URL is supplied, because the binding cannot be confirmed.\",\"why\":\"When the advertised resource differs from the requested one, a payment can be steered to, or reused for, a different resource than the one the agent asked for.\",\"fix\":\"Bind the payment requirement to the exact canonical resource URL, including its path and relevant query.\",\"spec\":\"x402 V2 core specification\",\"specUrl\":\"https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md\"}]}"
      }
    ],
    "structuredContent": {
      "count": 1,
      "checks": [
        {
          "id": "binding.resource_url",
          "title": "Payment requirement is bound to the requested resource",
          "category": "binding",
          "mode": "offline",
          "severity": "high",
          "verifies": "resource.url equals the URL that was requested, including path and query (the fragment is ignored). In evaluate_payment_requirements this check is a warning when no resource URL is supplied, because the binding cannot be confirmed.",
          "why": "When the advertised resource differs from the requested one, a payment can be steered to, or reused for, a different resource than the one the agent asked for.",
          "fix": "Bind the payment requirement to the exact canonical resource URL, including its path and relevant query.",
          "spec": "x402 V2 core specification",
          "specUrl": "https://github.com/x402-foundation/x402/blob/main/specs/x402-specification-v2.md"
        }
      ]
    },
    "isError": false
  }
}

HTTP session

ShellInitialize, then call a tool
curl -si https://settlens.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1"}}}'

curl -s https://settlens.com/mcp \
  -H 'Content-Type: application/json' \
  -H 'MCP-Protocol-Version: 2025-06-18' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"evaluate_payment_requirements","arguments":{"payment_required":"eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZWFkZXIgaXMgcmVxdWlyZWQiLCJyZXNvdXJjZSI6eyJ1cmwiOiJodHRwOi8vMTI3LjAuMC4xOjQ0MDIvc2VjdXJlIiwiZGVzY3JpcHRpb24iOiJTZXR0bGVucyBsb2NhbCBkZW1vbnN0cmF0aW9uIHJlc291cmNlIiwibWltZVR5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwic2VydmljZU5hbWUiOiJTZXR0bGVucyBEZW1vIiwidGFncyI6WyJzZWN1cml0eSIsImRlbW8iXX0sImFjY2VwdHMiOlt7InNjaGVtZSI6ImV4YWN0IiwibmV0d29yayI6ImVpcDE1NTo4NDUzMiIsImFtb3VudCI6IjEwMDAwIiwiYXNzZXQiOiIweDAzNkNiRDUzODQyYzU0MjY2MzRlNzkyOTU0MWVDMjMxOGYzZENGN2UiLCJwYXlUbyI6IjB4MjA5NjkzQmM2YWZjMEM1MzI4YkEzNkZhRjAzQzUxNEVGMzEyMjg3QyIsIm1heFRpbWVvdXRTZWNvbmRzIjo2MCwiZXh0cmEiOnsibmFtZSI6IlVTREMiLCJ2ZXJzaW9uIjoiMiJ9fV0sImV4dGVuc2lvbnMiOnt9fQ==","resource_url":"http://127.0.0.1:4402/secure","policy":{"pay_to":["0x209693Bc6afc0C5328bA36FaF03C514EF312287C"],"assets":["0x036CbD53842c5426634e7929541eC2318f3dCF7e"],"networks":["eip155:84532"],"max_amount":"5000","schemes":["exact"]}}}}'
HTTPFirst response (headers abridged)
HTTP/1.1 200 OK
cache-control: no-store
content-type: application/json
vary: Accept

{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{"listChanged":false},"resources":{"listChanged":false},"prompts":{"listChanged":false}},"serverInfo":{"name":"settlens","title":"Settlens","version":"0.5.0"},"instructions":"Settlens checks x402 payment requests before you pay. It never pays, never signs and never needs a wallet or key. On every HTTP 402 response, call evaluate_payment_requirements with the PAYMENT-REQUIRED header value, the URL you requested (resource_url) and your spending policy (at least pay_to, assets, networks and max_amount) BEFORE paying. Never pay when the decision is do_not_pay. When it is review, show the summary and reasons to the human and ask for explicit approval before paying. When it is pay, pay only with one of the entries listed in acceptable."}}

The second call returns the result shown in the over-budget example. A notifications/initialized POST returns 202 Accepted with no body, and a GET asking for text/event-stream, as an MCP client opening an SSE stream does, returns 405 with Allow: POST.

Troubleshooting

Common problems
SymptomCause and fix
scan_endpoint and scan_facilitator are missingThe remote endpoint lists them only with a valid bearer token. Check the Authorization header, or use the local stdio server.
The decision is always reviewRead reasons: the policy lacks pay_to, assets, networks or max_amount, or resource_url is missing.
do_not_pay with binding.resource_urlresource.url in the 402 differs from the URL you passed. Pass the exact URL you requested, including path and query; if they truly differ, do not pay.
-32602: max_amount has an invalid formatmax_amount is an integer string in atomic units, such as "10000", never "0.01" or a number.
405 on GET /mcpThe client asked for an SSE stream (Accept: text/event-stream); the endpoint is POST-only and has none. Configure the client for Streamable HTTP; it will POST. Any other GET returns the documentation page.
403 Origin not allowedThe request came from a web page on another origin. Call the endpoint from a server-side client, or from a page on https://settlens.com or https://claude.ai.
415 or 406Send Content-Type: application/json and an Accept header that includes application/json.
429 Rate limit exceededWait the number of seconds in Retry-After. Every POST counts, including notifications, and each entry of a batch counts as one request.
The stdio server never answersA wrapper script prints to stdout before the server starts, or the client sends pretty-printed JSON across several lines. Send one message per line; keep stdout for the server.
Responses arrive out of order on stdioExpected: requests run concurrently. Match responses by id.
Claude Desktop does not show the serverCheck that the path is absolute, that node is on the PATH Claude Desktop sees (or use the full path to node), that the JSON is valid, and restart the app.

Related: the check catalog explains every check id, the CLI reference covers settlens mcp, and offline verification describes what the signature tools prove.

Sources

  1. Model Context Protocol specification 2025-06-18: ToolsModel Context Protocol · modelcontextprotocol.io
  2. Model Context Protocol specification 2025-06-18: TransportsModel Context Protocol · modelcontextprotocol.io
  3. JSON-RPC 2.0 specificationJSON-RPC Working Group · jsonrpc.org
  4. x402 protocol specification, version 2x402 Foundation · github.com
  5. x402 HTTP transport, version 2x402 Foundation · github.com
  6. x402 MCP transport, version 2x402 Foundation · github.com

Last updated