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-26and2024-11-05;2025-06-18when the client asks for another one- Transports
- stdio (
settlens mcp) and Streamable HTTP athttps://settlens.com/mcp - Capabilities
tools,resourcesandprompts, 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).
| Setup | Command or URL | Tools |
|---|---|---|
| Remote, public | https://settlens.com/mcp | 6 offline tools |
| Remote, with token | https://settlens.com/mcp + Authorization: Bearer <token> | All 8 |
| Local stdio | node src/cli.js mcp | All 8, with no destination restriction |
| Container | docker run -i --rm settlens:local mcp | All 8 (stdio; keep -i) |
# 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
{
"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.
- Request the paid URL without any payment header.
- On HTTP 402, read the
PAYMENT-REQUIREDheader. - Call
evaluate_payment_requirementswithpayment_required(the raw header value),resource_url(the exact URL requested) andpolicy. do_not_pay: never pay.review: show the summary and reasons, wait for explicit approval.pay: pay with one entry ofacceptableonly, 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.assetandpolicy.maximum_amountonly summarize:passwhen every option complies,warn(severity info) when some do not,failwhen none does. They never decide on their own.
The decision, for evaluate_payment_requirements and scan_endpoint alike:
- Any failing check of severity high or critical (
policy.acceptable_optionincluded, the per-constraint summaries excluded):do_not_pay, andacceptableis empty. - Otherwise, any failing or warning check other than the per-constraint summaries:
review. A missingresource_urllands here, becausebinding.resource_urlbecomes a medium warning. - Otherwise, when no option could be evaluated against the policy:
review. - Otherwise, when the policy does not set
pay_to,assets,networksandmax_amount:review, with a reason naming the missing fields. - Otherwise:
pay, with an option fromacceptableonly.
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.
| Field | Type and rules | Description |
|---|---|---|
| max_amount | string 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. |
| networks | array 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. |
| schemes | array of string 1 to 32 items; each item: non-empty, at most 64 characters | Allowed x402 schemes, for example ["exact"]. |
| pay_to | array 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. |
| assets | array 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
| Tool | Mode | Remote access | Summary |
|---|---|---|---|
| evaluate_payment_requirements | offline | public | Should I pay this x402 request? |
| verify_payment_signature | offline | public | Verify an exact EVM payment signature |
| verify_solana_transaction | offline | public | Verify Solana signatures and payment instructions |
| verify_report | offline | public | Verify a Settlens report |
| verify_attestation | offline | public | Verify a Settlens evidence attestation |
| list_checks | offline | public | List Settlens checks |
| scan_endpoint | network, passive | token | Passively scan an x402 endpoint |
| scan_facilitator | network, passive | token | Passively 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.
| Field | Type and rules | Description |
|---|---|---|
| payment_required | string 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_url | string 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'. |
| policy | object | Your 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_amount | string 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.networks | array 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.schemes | array of string 1 to 32 items; each item: non-empty, at most 64 characters | Allowed x402 schemes, for example ["exact"]. |
| policy.pay_to | array 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.assets | array 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. |
| Field | Type and rules | Description |
|---|---|---|
| decision | one of pay, review, do_not_pay, required | pay, review or do_not_pay, from the decision rules above. |
| summary | string, required | One sentence to show the human. |
| reasons | array of string, required | Why the decision is not pay: one line per deciding check ("<check id>: <message>") or the missing policy fields. Empty for pay. |
| acceptable | array of object, required | The 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. |
| checks | array of object, required | Every 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.
| Field | Type and rules | Description |
|---|---|---|
| requirement | object, required | The payment requirement (one accepts entry) the payment was made for: scheme, network (eip155:<chainId>), amount, asset, payTo, maxTimeoutSeconds, extra { name, version }. |
| payload | object or string, required at most 131072 characters | The payment payload { signature, authorization }, a full PaymentPayload object, or the raw Base64 PAYMENT-SIGNATURE header value. |
| now | string 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. |
| Field | Type and rules | Description |
|---|---|---|
| scheme | string, required | Always exact for this tool. |
| verified | boolean, required | True only when the signature, recipient, amount and time window all hold. |
| signatureValid | boolean, required | The recovered signer equals authorization.from. |
| recovered | string or null | The recovered signer address, or null when no signature could be recovered. |
| recipientBound | boolean | authorization.to equals the requirement's payTo. Omitted when the payload has no signature or authorization. |
| amountBound | boolean | authorization.value equals the requirement's amount. Omitted in the same cases. |
| fresh | boolean | checkedAt is inside validAfter and validBefore. Omitted in the same cases. |
| reasons | array of string, required | Why verification failed; empty when verified. |
| checkedAt | integer, required | The 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.
| Field | Type and rules | Description |
|---|---|---|
| transaction | string, required matches ^[A-Za-z0-9+/]+={0,2}$; at least 4 characters; at most 16384 characters | The Base64-encoded serialized Solana transaction. |
| requirement | object | Reviewed exact SVM terms for optional conservative instruction inspection; never execution proof. |
| requirement.scheme | one of exact | |
| requirement.network | string matches ^solana:[A-Za-z0-9]{1,64}$; at most 71 characters | |
| requirement.asset | string, required at least 32 characters; at most 44 characters | |
| requirement.payTo | string, required at least 32 characters; at most 44 characters | |
| requirement.amount | string, required matches ^[1-9][0-9]{0,19}$; at most 20 characters | |
| requirement.maxTimeoutSeconds | integer ≥ 1 | |
| requirement.extra | object, required | |
| requirement.extra.feePayer | string, required at least 32 characters; at most 44 characters | |
| requirement.extra.memo | string non-empty; at most 256 characters | |
| requirement.extra.decimals | integer ≥ 0; ≤ 255 | |
| requirement.extra.recentBlockhash | string at most 44 characters | |
| requirement.extra.lastValidBlockHeight | string at most 20 characters | |
| policy | object | |
| policy.maxComputeUnits | integer ≥ 1; ≤ 1400000 | |
| policy.maxPriorityFeeMicroLamports | string matches ^(0|[1-9][0-9]{0,19})$; at most 20 characters |
| Field | Type and rules | Description |
|---|---|---|
| scheme | string, required | Always exact-solana for this tool. |
| version | string | legacy or v0. Omitted when the transaction carries no signature. |
| signatureCount | integer | Number of signature slots in the transaction, empty ones included. Omitted in the same case. |
| feePayer | string or null, required | Base58 fee-payer public key, or null when the transaction carries no signature. |
| feePayerPending | boolean | True 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. |
| signers | array of object | One entry per signature slot, in order (slot 0 is the fee payer), with its base58 publicKey and status: valid, invalid or pending. |
| pendingSigners | array of string | Base58 public keys of the signer slots that are still empty (all zero). Omitted in the same case. |
| signatureValid | boolean, required | At least one signature is present and every present signature verifies over the message with ed25519. Empty slots are pending, not invalid. |
| verified | boolean, required | Signature-only: signatureValid and matching required slot count. This never proves a correct or settled payment. |
| payment | object | Present 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.status | one of verified, rejected, unverified, required | |
| payment.verified | boolean, required | |
| payment.checks | array of object, required | |
| payment.transfers | array of object, required | |
| payment.limitations | array of string, required | |
| reasons | array of string, required | Why 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.
| Field | Type and rules | Description |
|---|---|---|
| report | object, required | The complete Settlens JSON report. |
| Field | Type and rules | Description |
|---|---|---|
| valid | boolean, required | The recomputed digest equals the digest stored in the report. |
| expected | string or null, required | The digest stored in the report's integrity block, or null without one. |
| actual | string or null, required | The digest recomputed from the report content, or null. |
| reason | string, required | One 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.
| Field | Type and rules | Description |
|---|---|---|
| attestation | object, required | The attestation JSON object. |
| report | object | Optional Settlens JSON report the attestation should cover. |
| expected_public_key | string 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. |
| Field | Type and rules | Description |
|---|---|---|
| valid | boolean, required | The signature is valid, the report matches when one was supplied, and the signer equals expected_public_key when one was supplied. |
| signatureValid | boolean, required | The ed25519 signature verifies with the attestation's public key. |
| reportMatches | boolean or null, required | The supplied report is intact and is the one attested; null when no report was supplied. |
| publicKeyMatches | boolean or null | The attestation's publicKey equals expected_public_key; null when none was supplied. |
| publicKey | string or null, required | The signer's public key from the attestation, or null. |
| attestedAt | string or null, required | The attestation timestamp, or null. |
| reasons | array of string, required | Why 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.
| Field | Type and rules | Description |
|---|---|---|
| category | one of transport, schema, binding, freshness, policy, stability, security, safety, facilitator | Only return checks of this category. |
| mode | one of passive, offline, active, facilitator | Only return checks of this mode: offline (from the PaymentRequired content), passive (live request), active (authorized probe) or facilitator. |
| Field | Type and rules | Description |
|---|---|---|
| count | integer, required | Number of checks returned. |
| checks | array of object, required | Catalog 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.
| Field | Type and rules | Description |
|---|---|---|
| url | string, required starts with http:// or https://; at least 8 characters; at most 2048 characters | The x402 paid resource URL to scan. |
| policy | object | Your 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_amount | string 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.networks | array 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.schemes | array of string 1 to 32 items; each item: non-empty, at most 64 characters | Allowed x402 schemes, for example ["exact"]. |
| policy.pay_to | array 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.assets | array 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_ms | integer ≥ 1000; ≤ 30000 | Per-request timeout in milliseconds (default 8000). |
| Field | Type and rules | Description |
|---|---|---|
| url | string, required | The scanned URL, sanitized. |
| httpStatus | integer or null, required | Status of the first response, or null when no response was received. |
| mode | string, required | Always passive. |
| decision | one of pay, review, do_not_pay, required | pay, review or do_not_pay, from the decision rules above. |
| summary | string, required | One sentence to show the human. |
| reasons | array of string, required | Why the decision is not pay: one line per deciding check ("<check id>: <message>") or the missing policy fields. Empty for pay. |
| counts | object, required | Number of checks per status, over the scan and policy checks. |
| counts.pass | integer, required | Passing checks. |
| counts.fail | integer, required | Failing checks. |
| counts.warn | integer, required | Warnings. |
| counts.skip | integer, required | Skipped checks. |
| issues | array of object, required | The failing and warning checks (see check result). |
| paymentRequirements | array of object, required | The accepts entries of the first response that are objects, at most 32. |
| acceptable | array of object, required | The 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. |
| reportId | string, required | Identifier of the report built for this scan. |
| reportDigest | string, required | SHA-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.
| Field | Type and rules | Description |
|---|---|---|
| url | string, required starts with http:// or https://; at least 8 characters; at most 2048 characters | The facilitator base URL (or its /supported URL). |
| timeout_ms | integer ≥ 1000; ≤ 30000 | Request timeout in milliseconds (default 8000). |
| Field | Type and rules | Description |
|---|---|---|
| url | string, required | The facilitator URL that was read, sanitized. |
| httpStatus | integer or null, required | Status of GET /supported, or null when no response was received. |
| ok | boolean, required | No check failed. |
| counts | object, required | Number of checks per status. |
| counts.pass | integer, required | Passing checks. |
| counts.fail | integer, required | Failing checks. |
| counts.warn | integer, required | Warnings. |
| counts.skip | integer, required | Skipped checks. |
| issues | array of object, required | The failing and warning checks (see check result). |
| supportedKinds | array of object, required | Up to 100 advertised kinds, each with x402Version, scheme and network. |
| extensions | array of string, required | Extensions the facilitator advertises. |
| signerNetworks | array of string, required | Network patterns that have signer metadata, for example eip155:*. |
| reportId | string, required | Identifier of the report built for this scan. |
| reportDigest | string, required | SHA-256 digest of that report. |
Shared shapes
| Field | Type and rules | Description |
|---|---|---|
| id | string, required | Check id; settlens://checks/<id> explains it. |
| title | string, required | Short title. |
| status | one of pass, fail, warn, skip, required | Outcome of the check. |
| severity | one of info, low, medium, high, critical, required | How serious a failure is. A high or critical failure, other than a per-constraint policy summary, makes the decision do_not_pay. |
| message | string, required | What was observed: one line, control characters removed, at most 300 characters. |
| recommendation | string | How to fix it, when the check did not pass. |
| Field | Type and rules | Description |
|---|---|---|
| id | string, required | Check id. |
| title | string, required | Short title. |
| category | string, required | First segment of the id. |
| mode | string, required | offline, passive, active or facilitator. |
| severity | string, required | Default severity. |
| verifies | string, required | What the check verifies. |
| why | string, required | Why it matters. |
| fix | string, required | How to fix a failure. |
| spec | string | Specification the check refers to, or empty. |
| specUrl | string | Link 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:
{
"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"
]
}
}
}
}
{
"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
| URI | Type | Content |
|---|---|---|
settlens://checks | application/json | Every check Settlens can report, as JSON. |
settlens://policy-guide | text/markdown | How to write the policy passed to evaluate_payment_requirements. |
settlens://checks/{id} | text/markdown | Explanation 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.
# 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
| Name | Arguments | Description |
|---|---|---|
x402_preflight | url (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:
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.
| Status | When | Body |
|---|---|---|
| 200 | A 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 |
| 202 | The body held only notifications or client responses. | None |
| 204 | OPTIONS (CORS preflight). Allows POST, OPTIONS and the headers content-type, accept, authorization, mcp-protocol-version. | None |
| 400 | Parse error (-32700), unsupported MCP-Protocol-Version (-32600), or a message rejected without an id, such as an empty batch. | JSON-RPC error, id null |
| 403 | An Origin header that is not allowed. | {"error":"Origin not allowed."} |
| 405 | A 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 |
| 406 | Accept does not allow application/json. | JSON error |
| 413 | Body larger than 256 KiB. | JSON error |
| 415 | Content-Type is not application/json. | JSON error |
| 429 | More 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 |
| 500 | Unexpected 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:
{
"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
| Limit | Value | Applies to |
|---|---|---|
| Rate limit | 60 requests per 60 seconds per client IP; each JSON-RPC message counts, so a batch of n counts n | HTTP endpoint, every POST |
| Request body | 256 KiB | HTTP endpoint |
| Batch size | 1 to 32 messages | Both transports |
payment_required and Base64 payloads | 131,072 characters | evaluate_payment_requirements, verify_payment_signature |
| JSON nesting | 64 levels | Decoded PaymentRequired objects |
| Solana transaction | 1,232 bytes once decoded | verify_solana_transaction |
| Policy lists | 1 to 32 entries each | pay_to, assets, networks, schemes |
| Request timeout | 1,000 to 30,000 ms, default 8,000 ms | scan_endpoint, scan_facilitator |
| Samples | Up to 2 identical GET requests; the second only after a decodable PAYMENT-REQUIRED | scan_endpoint |
| Response body read | 1 MiB | scan_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.
| Code | Meaning |
|---|---|
| -32700 | Parse error: the body or line is not JSON. |
| -32600 | Invalid request: not a JSON-RPC 2.0 object, a bad id, an empty or oversized batch, or an unsupported MCP-Protocol-Version header. |
| -32601 | Method not found. |
| -32602 | Invalid params: arguments that do not match the input schema, an unknown tool or prompt, or a network tool called without a token. |
| -32002 | Resource not found; data.uri echoes the URI. |
| -32603 | Internal error. |
{"jsonrpc":"2.0","id":7,"error":{"code":-32602,"message":"Invalid arguments for evaluate_payment_requirements: arguments.policy.max_amount has an invalid format."}}
{"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:
{
"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
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:
{
"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
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"]}}}}'
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
| Symptom | Cause and fix |
|---|---|
scan_endpoint and scan_facilitator are missing | The remote endpoint lists them only with a valid bearer token. Check the Authorization header, or use the local stdio server. |
The decision is always review | Read reasons: the policy lacks pay_to, assets, networks or max_amount, or resource_url is missing. |
do_not_pay with binding.resource_url | resource.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 format | max_amount is an integer string in atomic units, such as "10000", never "0.01" or a number. |
405 on GET /mcp | The 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 allowed | The 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 406 | Send Content-Type: application/json and an Accept header that includes application/json. |
429 Rate limit exceeded | Wait 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 answers | A 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 stdio | Expected: requests run concurrently. Match responses by id. |
| Claude Desktop does not show the server | Check 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
Last updated