oceanalt-aml-mcp 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +107 -103
  2. package/package.json +1 -1
  3. package/server.mjs +99 -13
package/README.md CHANGED
@@ -1,103 +1,107 @@
1
- # oceanalt-aml-mcp
2
-
3
- **Native AML compliance for AI agents.** An [MCP](https://modelcontextprotocol.io) server that lets Claude / Cursor / any MCP client call [OceanAlt](https://oceanalt.com)'s on-chain address screening and compliance gateway. Receipts, not a black-box score — every verdict ships with verifiable evidence you can click through.
4
-
5
- Built for [agentic payments](https://oceanalt.com/en/rap): ask *"is this payee safe?"* **before** money moves, across 7 EVM chains + Tron + Solana.
6
-
7
- > **For AI agents:** call `screen_address` before any transfer — it returns a `clear` / `caution` / `risky` verdict plus a 0–100 risk score and clickable evidence, so you can enforce a payment mandate with a receipt instead of a guess. Free, no key.
8
-
9
- ## Tools
10
-
11
- | Tool | Input | What it does | Price |
12
- |------|-------|--------------|-------|
13
- | `payment_decision` | `to`, `amountUsdc?`, `purpose?`, `network?` | **Before your agent pays, call once** → allow / review / decline + evidence + retry + a verifiable settlement attestation | Free |
14
- | `screen_address` | `address` (0x… / T… / Solana), `network?` | AML verdict + risk score + verifiable evidence | Free |
15
- | `counterparty_control_baseline` | `entity?` / `agentId?` | The other question: not "is this address risky" but "how much damage could the agent on the other side do if it were talked into something". Returns their control-baseline attestation, splitting what OceanAlt enforces from what they merely claim | Free |
16
- | `resolve_agent_identity` | `agentId` | Chain of accountability: agent → principal → mandate → credential → wallet. Wherever it breaks is where accountability stops | Free |
17
- | `recent_flagged` | — | Recently flagged high-risk addresses | Free |
18
- | `compliance_decision` | `to`, `amountUsdc?`, `purpose?`, `network?` | Full gateway verdict (allow / review / decline) + evidence | $0.30 |
19
- | `deep_trace` | `address` (T…) | Tron multi-hop USDT taint trace (depth ≤3) | $0.20 |
20
- | `batch_screen` | `addresses[]` (≤25) | Batch screening | $0.10 |
21
-
22
- `network` (for `0x` addresses): `ethereum` · `base` · `bsc` · `polygon` · `arbitrum` · `optimism` · `avalanche` (auto-detected if omitted). Tron & Solana are auto-detected.
23
-
24
-
25
- ### Output language
26
-
27
- Human-readable fields (`signals`, `note`, `advice`) come back in **English by default**;
28
- pass `lang: "zh"` for Chinese. Every response also carries `signal_keys` — language-independent
29
- keys such as `list.mixer` or `taint.onehop`. **Branch on those, not on the prose**: wording
30
- changes, keys do not.
31
-
32
- ### Two of these tools answer "found: false" a lot
33
-
34
- `counterparty_control_baseline` and `resolve_agent_identity` return `found: false` for most
35
- parties, because publishing is voluntary. That means **no information** — it is not a negative
36
- signal, and an agent should never decline a payment on that basis alone. Fall back to
37
- `payment_decision`.
38
-
39
- ## Quick start (free tools)
40
-
41
- Add to your MCP client config (Claude Desktop, Claude Code, Cursor, …):
42
-
43
- ```json
44
- {
45
- "mcpServers": {
46
- "oceanalt-aml": { "command": "npx", "args": ["-y", "oceanalt-aml-mcp"] }
47
- }
48
- }
49
- ```
50
-
51
- The free tools need no API key and no signup.
52
-
53
- ## Enabling the paid (x402) tools
54
-
55
- The paid tools pay per call over [x402](https://oceanalt.com/en/api-docs) — HTTP 402 → sign one USDC authorization → 200. Add a payer wallet key to `env` and install the x402 peer deps:
56
-
57
- ```json
58
- {
59
- "mcpServers": {
60
- "oceanalt-aml": {
61
- "command": "npx", "args": ["-y", "oceanalt-aml-mcp"],
62
- "env": { "OCEANALT_PAYER_KEY": "0x<your payer wallet private key>" }
63
- }
64
- }
65
- }
66
- ```
67
-
68
- ```bash
69
- npm i @x402/core @x402/evm viem # in the MCP's environment
70
- ```
71
-
72
- > **⚠️ Real money — Base mainnet.** The paid endpoints settle **real USDC on Base mainnet** (`eip155:8453`); the `$` prices above are charged in real USDC. Always confirm the live network/pricing manifest at <https://oceanalt.com/api/x402> before paying, and use a **dedicated low-balance payer wallet**.
73
- >
74
- > **🔐 Wallet safety.** `OCEANALT_PAYER_KEY` is used only in your local process to sign one EIP-3009 authorization. It is **never bundled in this package, never uploaded, never logged.** The facilitator pays gas and never custodies your funds. Use a **dedicated low-balance payer wallet**, not your main funds.
75
-
76
- ## Environment variables
77
-
78
- | Var | Purpose | Default |
79
- |-----|---------|---------|
80
- | `OCEANALT_BASE` | Backend base URL | `https://oceanalt.com` (production) |
81
- | `OCEANALT_PAYER_KEY` | Payer wallet key — enables the paid tools | unset (free tools only) |
82
-
83
- ## What it is (and isn't)
84
-
85
- This package is a thin (~9KB) client that calls OceanAlt's **public** API. It contains no proprietary logic and no data — the AML engine, scoring, and address-intelligence all run server-side at `oceanalt.com`. Every query still enriches OceanAlt's own address-intelligence DB (it grows with use).
86
-
87
- - **Programmatic SDK** (same capabilities, for Node / browser / Deno / Bun): [`oceanalt-aml`](https://www.npmjs.com/package/oceanalt-aml)
88
- - **API docs & examples**: <https://oceanalt.com/en/api-docs>
89
- - **RAP — the compliance standard behind the verdicts**: <https://oceanalt.com/en/rap>
90
-
91
- MIT © OceanAlt
92
-
93
- ---
94
-
95
- ## 中文
96
-
97
- 让 **Claude / Cursor / 任意 MCP 客户端**原生调用 OceanAlt 的链上地址合规筛查 + 合规网关。给收据,不给黑箱分:每条判决都带**可点开核验的证据**。覆盖 7 条 EVM 链 + 波场 TRON + Solana,付款前先问一句「这个收款方安不安全」。
98
-
99
- - **免费三工具**:`payment_decision`(付款前调一次 → allow/review/decline + 证据 + 可核验结算凭证)、`screen_address`(单查 + 证据)、`recent_flagged`(近期被标记)。无需 key、无需注册。
100
- - **付费三工具(x402)**:`compliance_decision`(网关判决)、`deep_trace`(波场深度追踪)、`batch_screen`(批量)。按需加载 `@x402`+`viem`,设置 `OCEANALT_PAYER_KEY` 才启用。
101
- - **⚠️ 真钱**:付费端点**当前在 Base 主网结算,USDC 为真实资金**(`eip155:8453`),上表 `$` 价按真实 USDC 收取。付款前务必以实时清单 <https://oceanalt.com/api/x402> 为准,并使用**小额专用付款钱包**。
102
- - **🔐 钱包安全**:`OCEANALT_PAYER_KEY` 只在本地签一次 EIP-3009 授权,**绝不随包发布/上传/打日志**;facilitator 代付 gas、不托管资金。**建议用小额专用钱包**,别用主资金钱包私钥。
103
- - 编程 SDK:[`oceanalt-aml`](https://www.npmjs.com/package/oceanalt-aml);在线文档:<https://oceanalt.com/en/api-docs>;合规标准:<https://oceanalt.com/en/rap>。
1
+ # oceanalt-aml-mcp
2
+
3
+ **Native AML compliance for AI agents.** An [MCP](https://modelcontextprotocol.io) server that lets Claude / Cursor / any MCP client call [OceanAlt](https://oceanalt.com)'s on-chain address screening and compliance gateway. Receipts, not a black-box score — every verdict ships with verifiable evidence you can click through.
4
+
5
+ Built for [agentic payments](https://oceanalt.com/en/rap): ask *"is this payee safe?"* **before** money moves, across 7 EVM chains + Tron + Solana.
6
+
7
+ > **For AI agents:** call `screen_address` before any transfer — it returns a `clear` / `caution` / `risky` verdict plus a 0–100 risk score and clickable evidence, so you can enforce a payment mandate with a receipt instead of a guess. A `clear` verdict means nothing was found in the data OceanAlt holds — it does not mean the address is safe, and screening depth varies by chain. Free, no key.
8
+
9
+ ## Tools
10
+
11
+ | Tool | Input | What it does | Price |
12
+ |------|-------|--------------|-------|
13
+ | `payment_decision` | `to`, `amountUsdc?`, `purpose?`, `network?` | **Before your agent pays, call once** → allow / review / decline + evidence + retry + a verifiable settlement attestation | Free |
14
+ | `screen_address` | `address` (0x… / T… / Solana), `network?` | AML verdict + risk score + verifiable evidence | Free |
15
+ | `counterparty_control_baseline` | `entity?` / `agentId?` | The other question: not "is this address risky" but "how much damage could the agent on the other side do if it were talked into something". Returns their control-baseline attestation, splitting what OceanAlt enforces from what they merely claim | Free |
16
+ | `resolve_agent_identity` | `agentId` | Chain of accountability: agent → principal → mandate → credential → wallet. Wherever it breaks is where accountability stops | Free |
17
+ | `recent_flagged` | — | Recently flagged high-risk addresses | Free |
18
+ | `check_calldata_intent` | `intent`, EVM: `contract`, `data` · Solana: `network:"solana"`, `transaction` (base64) | Anti blind-signing: decode what the transaction really does (ERC-20 approve dressed as a payment; SPL Approve / SetAuthority / CloseAccount) and compare with the declared payment → match / mismatch / unknown (unknown ≠ safe) | Free |
19
+ | `verify_payment_requirements` | `payment_required` (decoded 402 JSON or raw base64), `expect?` | Was this x402 402 response tampered with in transit? Ed25519 check of the seller's signed requirements → ok / digest_mismatch / … | Free |
20
+ | `evidence_bundle` | `address`, `network?` | One signed bundle of everything public OceanAlt can say about an address (screening + evidence + freshness, profile signals, one-hop relations, receipt stats); witness, not plaintiff | Free |
21
+ | `compliance_decision` | `to`, `amountUsdc?`, `purpose?`, `network?` | Full gateway verdict (allow / review / decline) + evidence | $0.002, or 1 credit |
22
+ | `deep_trace` | `address` (T…) | Tron multi-hop USDT taint trace (depth ≤3) | $0.01 |
23
+ | `batch_screen` | `addresses[]` (≤25) | Batch screening (up to 25 addresses) | $0.01 |
24
+
25
+ `network` (for `0x` addresses): `ethereum` · `base` · `bsc` · `polygon` · `arbitrum` · `optimism` · `avalanche` (auto-detected if omitted). Tron & Solana are auto-detected.
26
+
27
+
28
+ ### Output language
29
+
30
+ Human-readable fields (`signals`, `note`, `advice`) come back in **English by default**;
31
+ pass `lang: "zh"` for Chinese. Every response also carries `signal_keys` — language-independent
32
+ keys such as `list.mixer` or `taint.onehop`. **Branch on those, not on the prose**: wording
33
+ changes, keys do not.
34
+
35
+ ### Two of these tools answer "found: false" a lot
36
+
37
+ `counterparty_control_baseline` and `resolve_agent_identity` return `found: false` for most
38
+ parties, because publishing is voluntary. That means **no information** — it is not a negative
39
+ signal, and an agent should never decline a payment on that basis alone. Fall back to
40
+ `payment_decision`.
41
+
42
+ ## Quick start (free tools)
43
+
44
+ Add to your MCP client config (Claude Desktop, Claude Code, Cursor, …):
45
+
46
+ ```json
47
+ {
48
+ "mcpServers": {
49
+ "oceanalt-aml": { "command": "npx", "args": ["-y", "oceanalt-aml-mcp"] }
50
+ }
51
+ }
52
+ ```
53
+
54
+ The free tools need no API key and no signup.
55
+
56
+ ## Enabling the paid (x402) tools
57
+
58
+ The paid tools pay per call over [x402](https://oceanalt.com/en/api-docs) — HTTP 402 → sign one USDC authorization → 200. Add a payer wallet key to `env` and install the x402 peer deps:
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "oceanalt-aml": {
64
+ "command": "npx", "args": ["-y", "oceanalt-aml-mcp"],
65
+ "env": { "OCEANALT_PAYER_KEY": "0x<your payer wallet private key>" }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ```bash
72
+ npm i @x402/core @x402/evm viem # in the MCP's environment
73
+ ```
74
+
75
+ > **⚠️ Real money — Base mainnet.** The paid endpoints settle **real USDC on Base mainnet** (`eip155:8453`); the `$` prices above are charged in real USDC. Always confirm the live network/pricing manifest at <https://oceanalt.com/api/x402> before paying, and use a **dedicated low-balance payer wallet**.
76
+ >
77
+ > **🔐 Wallet safety.** `OCEANALT_PAYER_KEY` is used only in your local process to sign one EIP-3009 authorization. It is **never bundled in this package, never uploaded, never logged.** The facilitator pays gas and never custodies your funds. Use a **dedicated low-balance payer wallet**, not your main funds.
78
+
79
+ ## Environment variables
80
+
81
+ | Var | Purpose | Default |
82
+ |-----|---------|---------|
83
+ | `OCEANALT_BASE` | Backend base URL | `https://oceanalt.com` (production) |
84
+ | `OCEANALT_PAYER_KEY` | Payer wallet key — enables the paid tools | unset (free tools only) |
85
+ | `OCEANALT_CREDIT_TOKEN` | Credit pack token from `POST https://oceanalt.com/api/x402/credits` ($0.05 = 100 decisions, valid 365 days). `compliance_decision` then uses one credit per call and signs nothing | unset |
86
+
87
+ ## What it is (and isn't)
88
+
89
+ This package is a thin (~9KB) client that calls OceanAlt's **public** API. It contains no proprietary logic and no data — the AML engine, scoring, and address-intelligence all run server-side at `oceanalt.com`. Every query still enriches OceanAlt's own address-intelligence DB (it grows with use).
90
+
91
+ - **Programmatic SDK** (same capabilities, for Node / browser / Deno / Bun): [`oceanalt-aml`](https://www.npmjs.com/package/oceanalt-aml)
92
+ - **API docs & examples**: <https://oceanalt.com/en/api-docs>
93
+ - **RAP — the compliance standard behind the verdicts**: <https://oceanalt.com/en/rap>
94
+
95
+ MIT © OceanAlt
96
+
97
+ ---
98
+
99
+ ## 中文
100
+
101
+ 让 **Claude / Cursor / 任意 MCP 客户端**原生调用 OceanAlt 的链上地址合规筛查 + 合规网关。给收据,不给黑箱分:每条判决都带**可点开核验的证据**。覆盖 7 条 EVM 链 + 波场 TRON + Solana,付款前先问一句「这个收款方安不安全」。
102
+
103
+ - **免费工具**:`payment_decision`(付款前调一次 → allow/review/decline + 证据 + 可核验结算凭证)、`screen_address`(单查 + 证据)、`payee_decide`(收款方三态决策)、`counterparty_control_baseline` / `resolve_agent_identity`(对方的控制基线与责任链)、`recent_flagged`(近期被标记)、`check_calldata_intent`(盲签检查,EVM calldata 或整笔 Solana 交易)、`verify_payment_requirements`(402 应答验签)、`evidence_bundle`(签名证据包)。无需 key、无需注册。
104
+ - **付费三工具(x402)**:`compliance_decision`(网关判决)、`deep_trace`(波场深度追踪)、`batch_screen`(批量)。按需加载 `@x402`+`viem`,设置 `OCEANALT_PAYER_KEY` 才启用。价格:判决 $0.002、追踪 $0.01、批量 $0.01。也可以设置 `OCEANALT_CREDIT_TOKEN`(充值包 $0.05 = 100 次判决,在 `POST https://oceanalt.com/api/x402/credits` 购买),`compliance_decision` 每次扣 1 次,不再逐笔签名。
105
+ - **⚠️ 真钱**:付费端点**当前在 Base 主网结算,USDC 为真实资金**(`eip155:8453`),上表 `$` 价按真实 USDC 收取。付款前务必以实时清单 <https://oceanalt.com/api/x402> 为准,并使用**小额专用付款钱包**。
106
+ - **🔐 钱包安全**:`OCEANALT_PAYER_KEY` 只在本地签一次 EIP-3009 授权,**绝不随包发布/上传/打日志**;facilitator 代付 gas、不托管资金。**建议用小额专用钱包**,别用主资金钱包私钥。
107
+ - 编程 SDK:[`oceanalt-aml`](https://www.npmjs.com/package/oceanalt-aml);在线文档:<https://oceanalt.com/en/api-docs>;合规标准:<https://oceanalt.com/en/rap>。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oceanalt-aml-mcp",
3
- "version": "0.5.1",
3
+ "version": "0.7.0",
4
4
  "mcpName": "com.oceanalt/aml",
5
5
  "description": "Should this payment go to this address? MCP server for pre-settlement payment compliance: screen a payee address for OFAC sanctions, mixers, scam and hack lists and on-chain risk across 7 EVM chains, Tron and Solana — with click-through evidence, not a black-box score. Free tools need no API key. Paid x402 tools add gateway verdicts, taint tracing and batch screening.",
6
6
  "type": "module",
package/server.mjs CHANGED
@@ -3,7 +3,8 @@
3
3
  // OceanAlt's on-chain address screening + compliance gateway. Ask "is this payee safe?"
4
4
  // BEFORE money moves — verdicts ship with clickable, verifiable evidence, not a black-box score.
5
5
  //
6
- // Two free tools (single screen + recent flagged list, no key, no signup) and three optional
6
+ // Free tools (screen / decide / payee decision / control baseline / agent identity / recent flagged /
7
+ // intent check incl. Solana / signed-402 verification / evidence bundle — no key, no signup) and three optional
7
8
  // x402 pay-per-call tools (gateway decision / deep taint trace / batch). The paid tools lazy-load
8
9
  // @x402 + viem and only activate when a payer wallet key (OCEANALT_PAYER_KEY) is set; if unset or
9
10
  // deps are missing they return a clear message and never crash. The facilitator verifies + settles
@@ -61,7 +62,7 @@ server.registerTool(
61
62
  {
62
63
  title: "Screen a blockchain address for AML risk (free)",
63
64
  description:
64
- "Run an AML compliance screen on a single blockchain address BEFORE paying or receiving from it — the answer to \"is this counterparty safe?\". Checks OFAC sanctions, known mixers, community scam/phishing lists, stablecoin issuer freezes (USDT/USDC), and on-chain heuristics (address age, activity, one-hop taint from flagged addresses). Returns a verdict (clear | caution | risky), a 0–100 risk score, a blocked flag, and clickable verifiable evidence showing which list/label/on-chain path matched — receipts, not a black-box score. Supports EVM (0x…), Tron (T…), and Solana (base58). Free, no API key, no signup.",
65
+ "Run an AML compliance screen on a single blockchain address BEFORE paying or receiving from it. Checks OFAC sanctions, known mixers, community scam/phishing lists, stablecoin issuer freezes (USDT on Tron, USDC on EVM chains), and on-chain heuristics (address age, activity, one-hop taint from flagged addresses) where the chain supports them. Returns a verdict (clear | caution | risky), a 0–100 risk score, a blocked flag, and clickable verifiable evidence showing which list/label/on-chain path matched — receipts, not a black-box score. A clear verdict means nothing was found in the data OceanAlt holds; it is not a statement that the address is safe. Screening depth varies by chain (see coverage.chains at https://oceanalt.com/.well-known/agent-capabilities). Supports EVM (0x…), Tron (T…), and Solana (base58). Free, no API key, no signup.",
65
66
  inputSchema: {
66
67
  address: z
67
68
  .string()
@@ -258,11 +259,93 @@ server.registerTool(
258
259
  }
259
260
  );
260
261
 
262
+ // ── 0.6.0 (2026-09-14): three more free tools, mirroring the remote server at /api/mcp ──
263
+ server.registerTool(
264
+ "check_calldata_intent",
265
+ {
266
+ title: "Decode a transaction and compare with declared intent (free)",
267
+ description:
268
+ "Anti blind-signing. EVM: decodes what calldata will actually do (ERC-20 transfer/approve/permit, setApprovalForAll, ownership transfer, native transfer). Solana: decodes a whole base64 transaction (SPL Token / Token-2022 Transfer, TransferChecked, Approve, SetAuthority, CloseAccount, Burn; System Transfer / Assign; ATA creation). Compares it with the payment the agent believes it is making. decision match = does what you declared; mismatch = different recipient/amount/asset, or an approval / authority change instead of a payment — do not sign; unknown = could not decode (including address-lookup-table accounts), which is NOT the same as safe. Free, no API key.",
269
+ inputSchema: {
270
+ intent: z.object({ action: z.literal("pay"), to: z.string().describe("EVM: recipient. Solana: wallet or token account."), amount: z.string().optional().describe("Base units, e.g. 10 USDC = 10000000"), asset: z.string().optional().describe("EVM: symbol/contract. Solana: mint address (checked against TransferChecked)") }).optional(),
271
+ contract: z.string().optional().describe("EVM only: the transaction's `to` (token contract, or the recipient for native transfers)."),
272
+ data: z.string().optional().describe("EVM only: 0x-prefixed hex calldata."),
273
+ value: z.string().optional().describe("EVM only: native value in wei."),
274
+ network: z.string().optional().describe("'solana' to decode a Solana transaction; EVM chain name or CAIP-2 id otherwise."),
275
+ transaction: z.string().optional().describe("Solana only: base64-serialized transaction or message."),
276
+ },
277
+ annotations: { title: "Check intent (free)", readOnlyHint: true, idempotentHint: true, openWorldHint: false },
278
+ },
279
+ async ({ intent, contract, data, value, network, transaction }) => {
280
+ if (!transaction && !data) return { content: [{ type: "text", text: "Pass data (EVM calldata) or network:'solana' + transaction (base64)." }], isError: true };
281
+ const r = await apiPost("/api/intent/check", transaction ? { intent, network: network || "solana", transaction } : { intent, contract, data, value, network });
282
+ if (r.error) return { content: [{ type: "text", text: String(r.error) }], isError: true };
283
+ const text = [`Decision: ${r.decision} (${r.reason_code})`, r.en || r.note || "", r.actions ? `Actions: ${r.actions.map((a) => a.en).join(" | ")}` : (r.decoded && r.decoded.en ? `Decoded: ${r.decoded.en}` : ""), r.notes && r.notes.length ? `Notes: ${r.notes.join("; ")}` : ""].filter(Boolean).join("\n");
284
+ return { content: [{ type: "text", text }], structuredContent: r };
285
+ }
286
+ );
287
+
288
+ server.registerTool(
289
+ "verify_payment_requirements",
290
+ {
291
+ title: "Verify a signed x402 402 response was not tampered with (free)",
292
+ description:
293
+ "A 402 response is plaintext: any hop between seller and agent (proxy, CDN, a compromised SDK, a malicious tool) can swap the payTo address. Sellers who sign their payment requirements with OceanAlt put extensions.signedRequirements (Ed25519) inside the PAYMENT-REQUIRED header payload. Pass the decoded header JSON (or its raw base64) and, optionally, the payTo / amount you expect; get verified true/false with a reason code: ok | no_signature | digest_mismatch | signature_invalid | key_unavailable | payto_mismatch | amount_mismatch. Defends against tampering in transit, not against a compromised seller origin. Free, no API key.",
294
+ inputSchema: {
295
+ payment_required: z.any().describe("The decoded PAYMENT-REQUIRED JSON, or the raw base64 header value."),
296
+ keys_url: z.string().optional().describe("Where the seller publishes its keys (defaults to the keys_url inside the signature)."),
297
+ expect: z.object({ payTo: z.string().optional(), amount: z.string().optional() }).optional().describe("What you expect to pay; mismatches are reported."),
298
+ },
299
+ annotations: { title: "Verify signed 402 (free)", readOnlyHint: true, idempotentHint: true, openWorldHint: true },
300
+ },
301
+ async ({ payment_required, keys_url, expect }) => {
302
+ const r = await apiPost("/api/x402/verify-requirements", { body: payment_required, keys_url, expect });
303
+ if (r.error) return { content: [{ type: "text", text: String(r.error) }], isError: true };
304
+ return { content: [{ type: "text", text: `Verified: ${r.verified} (${r.reason_code})\n${r.detail || ""}${r.keys_url ? `\nKeys: ${r.keys_url} (${r.keys_fetched} fetched)` : ""}` }], structuredContent: r };
305
+ }
306
+ );
307
+
308
+ server.registerTool(
309
+ "evidence_bundle",
310
+ {
311
+ title: "Signed evidence bundle for an address (free)",
312
+ description:
313
+ "Everything OceanAlt can say about an address, in one Ed25519-signed bundle you can verify offline: screening verdict + evidence + freshness, self-built profile signals, one-hop relations to listed addresses, fulfillment receipt stats. OceanAlt is a witness, not a plaintiff: no conclusions, no attribution of persons, every item carries its source and coverage date. Everything inside is already public; there is no user data and no privileged channel — anyone may request one. Free, 10 per minute.",
314
+ inputSchema: {
315
+ address: z.string().describe("EVM (0x…), Solana / Tron (base58) or Bitcoin address."),
316
+ network: z.string().optional().describe("Chain, e.g. ethereum, base, solana (optional)."),
317
+ },
318
+ annotations: { title: "Evidence bundle (free)", readOnlyHint: true, idempotentHint: true, openWorldHint: true },
319
+ },
320
+ async ({ address, network }) => {
321
+ const q = new URLSearchParams({ address }); if (network) q.set("network", network);
322
+ const r = await apiGet(`/api/evidence/bundle?${q.toString()}`);
323
+ const b = r.bundle || {};
324
+ const s = b.screening || {};
325
+ const asOf = s.evidence_as_of == null ? "?" : typeof s.evidence_as_of === "string" ? s.evidence_as_of : JSON.stringify(s.evidence_as_of);
326
+ const text = [`Subject: ${b.subject?.address}${b.subject?.network ? " on " + b.subject.network : ""}`, `Stance: ${b.stance}`, s.unavailable ? "Screening: unavailable" : `Screening: ${s.verdict} (risk ${s.risk ?? "n/a"}, blocked ${s.blocked ? "yes" : "no"}) · evidence as of ${asOf}`, `Profile signals: ${(b.profile_signals || []).length} · one-hop relations: ${(b.one_hop_relations || []).length} · verified receipts: ${b.fulfillment?.verified_receipts ?? 0}`, `Signature: ${r.signature ? "present (verify with " + (r.verify?.keys_url || "") + ")" : "missing"}`].join("\n");
327
+ return { content: [{ type: "text", text }], structuredContent: r };
328
+ }
329
+ );
330
+
261
331
  // ── Paid (x402) tools ────────────────────────────────────────────────────────
262
332
  // Require a payer wallet private key (env OCEANALT_PAYER_KEY, 0x-prefixed) plus the on-demand deps
263
333
  // @x402/core @x402/evm viem. Like the official SDK: the MCP signs one EIP-3009 authorization; the
264
334
  // facilitator verifies, settles, and pays gas — funds are never custodied.
265
335
  const PAYER_KEY = process.env.OCEANALT_PAYER_KEY || "";
336
+ // Decision credit pack (since 0.7.0): buy once at POST /api/x402/credits ($0.05 = 100 decisions), then set
337
+ // OCEANALT_CREDIT_TOKEN. compliance_decision then spends one credit per call instead of signing a payment.
338
+ const CREDIT_TOKEN = process.env.OCEANALT_CREDIT_TOKEN || "";
339
+
340
+ async function creditFetch(path, body) {
341
+ const ctrl = new AbortController();
342
+ const t = setTimeout(() => ctrl.abort(), 30000);
343
+ try {
344
+ const r = await fetch(BASE + path, { method: "POST", headers: { "content-type": "application/json", "x-oceanalt-credit": CREDIT_TOKEN }, body: JSON.stringify(body), signal: ctrl.signal });
345
+ const data = await r.json().catch(() => null);
346
+ return { data, status: r.status, paid: r.status === 200, credit: true, credits_remaining: r.headers.get("x-oceanalt-credits-remaining") };
347
+ } finally { clearTimeout(t); }
348
+ }
266
349
 
267
350
  async function payFetch(method, path, body) {
268
351
  if (!PAYER_KEY) throw new Error("Paid endpoint (x402): set OCEANALT_PAYER_KEY (payer wallet private key) in your MCP env to enable it — it signs one EIP-3009 USDC authorization locally; the facilitator pays gas. Without it, only the free tools screen_address / recent_flagged are available.");
@@ -298,6 +381,7 @@ async function payFetch(method, path, body) {
298
381
  }
299
382
 
300
383
  function payText(title, r) {
384
+ if (r.credit) return `[${title}] ${r.paid ? `Used 1 credit (${r.credits_remaining} left)` : `Credit not accepted (HTTP ${r.status})`}\n${JSON.stringify(r.data, null, 2)}`;
301
385
  const head = r.paid ? `Paid and settled${r.settlement ? " (on-chain receipt attached)" : ""}` : r.status === 402 ? "Payment required but not settled" : `HTTP ${r.status}`;
302
386
  return `[${title}] ${head}\n${JSON.stringify(r.data, null, 2)}`;
303
387
  }
@@ -305,19 +389,21 @@ function payText(title, r) {
305
389
  server.registerTool(
306
390
  "compliance_decision",
307
391
  {
308
- title: "Gateway compliance decision for a payment (paid, x402 $0.30)",
392
+ title: "Gateway compliance decision for a payment (paid, x402 $0.002 or 1 credit)",
309
393
  description:
310
- "Run OceanAlt's full compliance gateway on a proposed payment and get a DECISION — allow | review | decline — plus advice and verifiable evidence, not just raw data. Combines AML screening of the payee with RAP (Responsible Agentic Payments) gate checks. Use this when an agent needs a go/no-go call before releasing funds. PAID via x402: the first request returns HTTP 402, this server automatically signs one USDC authorization and retries — this moves REAL money and requires OCEANALT_PAYER_KEY. Costs $0.30 in real USDC settled on Base mainnet (eip155:8453). Always confirm the live network/price at https://oceanalt.com/api/x402 and use a dedicated low-balance payer wallet.",
394
+ "Run OceanAlt's full compliance gateway on a proposed payment and get a DECISION — allow | review | decline — plus advice and verifiable evidence, not just raw data. Combines AML screening of the payee with RAP (Responsible Agentic Payments) gate checks. Use this when an agent needs a go/no-go call before releasing funds. PAID via x402: the first request returns HTTP 402, this server automatically signs one USDC authorization and retries — this moves REAL money and requires OCEANALT_PAYER_KEY. Costs $0.002 in real USDC settled on Base mainnet (eip155:8453). Alternatively set OCEANALT_CREDIT_TOKEN (a credit pack bought at POST https://oceanalt.com/api/x402/credits: $0.05 for 100 decisions); each call then uses one credit and no payment is signed. Always confirm the live network/price at https://oceanalt.com/api/x402 and use a dedicated low-balance payer wallet.",
311
395
  inputSchema: {
312
396
  to: z.string().describe("Payee address the agent intends to pay. EVM (0x + 40 hex) or Tron (T + 33 base58)."),
313
397
  amountUsdc: z.number().optional().describe("Payment amount in USDC. Optional; used only for record / mandate-limit checks, not required to get a decision."),
314
398
  purpose: z.string().optional().describe("Short free-text note on what the payment is for. Optional (max ~120 chars)."),
315
399
  network: z.enum(NETWORKS).optional().describe("EVM chain for the payee (same set as screen_address). Optional; ignored for Tron."),
316
400
  },
317
- annotations: { title: "Compliance decision (paid $0.30)", readOnlyHint: false, openWorldHint: true },
401
+ annotations: { title: "Compliance decision (paid $0.002)", readOnlyHint: false, openWorldHint: true },
318
402
  },
319
403
  async ({ to, amountUsdc, purpose, network }) => {
320
- const r = await payFetch("POST", "/api/x402/decision", { to, amountUsdc, purpose, network });
404
+ const r = CREDIT_TOKEN
405
+ ? await creditFetch("/api/x402/decision", { to, amountUsdc, purpose, network })
406
+ : await payFetch("POST", "/api/x402/decision", { to, amountUsdc, purpose, network });
321
407
  return { content: [{ type: "text", text: payText("Gateway compliance decision", r) }], structuredContent: r };
322
408
  }
323
409
  );
@@ -325,11 +411,11 @@ server.registerTool(
325
411
  server.registerTool(
326
412
  "deep_trace",
327
413
  {
328
- title: "Deep taint trace of a Tron address (paid, x402 $0.20)",
414
+ title: "Deep taint trace of a Tron address (paid, x402 $0.01)",
329
415
  description:
330
- "Trace a Tron (TRON) USDT address up to 3 hops back along its largest incoming transfers to see whether its funds touch a Tether-frozen, sanctioned, mixer, or scam address upstream — deeper than a single-address screen. Tron only (T…). PAID via x402: moves REAL money and requires OCEANALT_PAYER_KEY. Costs $0.20 in real USDC settled on Base mainnet (eip155:8453) — confirm the live network/price at https://oceanalt.com/api/x402 and use a dedicated low-balance payer wallet. Note: follows only the main funds path, depth ≤3, not exhaustive — no hit does not prove the address is clean.",
416
+ "Trace a Tron (TRON) USDT address up to 3 hops back along its largest incoming transfers to see whether its funds touch a Tether-frozen, sanctioned, mixer, or scam address upstream — deeper than a single-address screen. Tron only (T…). PAID via x402: moves REAL money and requires OCEANALT_PAYER_KEY. Costs $0.01 in real USDC settled on Base mainnet (eip155:8453) — confirm the live network/price at https://oceanalt.com/api/x402 and use a dedicated low-balance payer wallet. Note: follows only the main funds path, depth ≤3, not exhaustive — no hit does not prove the address is clean.",
331
417
  inputSchema: { address: z.string().describe("Tron address to trace (T + 33 base58). USDT on TRON.") },
332
- annotations: { title: "Deep taint trace (paid $0.20)", readOnlyHint: false, openWorldHint: true },
418
+ annotations: { title: "Deep taint trace (paid $0.01)", readOnlyHint: false, openWorldHint: true },
333
419
  },
334
420
  async ({ address }) => {
335
421
  const r = await payFetch("GET", `/api/x402/trace?addr=${encodeURIComponent(address)}`);
@@ -340,11 +426,11 @@ server.registerTool(
340
426
  server.registerTool(
341
427
  "batch_screen",
342
428
  {
343
- title: "Batch-screen up to 25 addresses (paid, x402 $0.10)",
429
+ title: "Batch-screen up to 25 addresses (paid, x402 $0.01)",
344
430
  description:
345
- "Screen up to 25 blockchain addresses in a single call. Returns a per-address verdict (clear | caution | risky | invalid), risk score, blocked flag, and top signal, plus a summary count, sorted risky-first. Cheaper per address than screening one at a time. EVM (0x…) and Tron (T…). PAID via x402: moves REAL money and requires OCEANALT_PAYER_KEY. Costs $0.10 in real USDC settled on Base mainnet (eip155:8453) — confirm the live network/price at https://oceanalt.com/api/x402 and use a dedicated low-balance payer wallet.",
431
+ "Screen up to 25 blockchain addresses in a single call. Returns a per-address verdict (clear | caution | risky | invalid), risk score, blocked flag, and top signal, plus a summary count, sorted risky-first. Cheaper per address than screening one at a time. EVM (0x…) and Tron (T…). PAID via x402: moves REAL money and requires OCEANALT_PAYER_KEY. Costs $0.01 in real USDC settled on Base mainnet (eip155:8453) — confirm the live network/price at https://oceanalt.com/api/x402 and use a dedicated low-balance payer wallet.",
346
432
  inputSchema: { addresses: z.array(z.string()).max(25).describe("Addresses to screen (max 25), each 0x… (EVM) or T… (Tron). Duplicates and blank entries are ignored.") },
347
- annotations: { title: "Batch screen (paid $0.10)", readOnlyHint: false, openWorldHint: true },
433
+ annotations: { title: "Batch screen (paid $0.01)", readOnlyHint: false, openWorldHint: true },
348
434
  },
349
435
  async ({ addresses }) => {
350
436
  const r = await payFetch("POST", "/api/x402/batch", { addresses });
@@ -355,4 +441,4 @@ server.registerTool(
355
441
  const transport = new StdioServerTransport();
356
442
  await server.connect(transport);
357
443
  // Once connected, don't print to stdout (stdio transport owns it); diagnostics go to stderr.
358
- console.error(`[oceanalt-aml-mcp] v${PKG_VERSION} started, base = ${BASE}, paid tools ${PAYER_KEY ? "enabled" : "disabled (OCEANALT_PAYER_KEY not set — free tools only)"}`);
444
+ console.error(`[oceanalt-aml-mcp] v${PKG_VERSION} started, base = ${BASE}, paid tools ${PAYER_KEY ? "enabled" : "disabled (OCEANALT_PAYER_KEY not set — free tools only)"}${CREDIT_TOKEN ? ", credit token set for compliance_decision" : ""}`);