oceanalt-aml-mcp 0.5.1 → 0.6.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 +4 -1
  2. package/package.json +1 -1
  3. package/server.mjs +71 -1
package/README.md CHANGED
@@ -15,6 +15,9 @@ Built for [agentic payments](https://oceanalt.com/en/rap): ask *"is this payee s
15
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
16
  | `resolve_agent_identity` | `agentId` | Chain of accountability: agent → principal → mandate → credential → wallet. Wherever it breaks is where accountability stops | Free |
17
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 |
18
21
  | `compliance_decision` | `to`, `amountUsdc?`, `purpose?`, `network?` | Full gateway verdict (allow / review / decline) + evidence | $0.30 |
19
22
  | `deep_trace` | `address` (T…) | Tron multi-hop USDT taint trace (depth ≤3) | $0.20 |
20
23
  | `batch_screen` | `addresses[]` (≤25) | Batch screening | $0.10 |
@@ -96,7 +99,7 @@ MIT © OceanAlt
96
99
 
97
100
  让 **Claude / Cursor / 任意 MCP 客户端**原生调用 OceanAlt 的链上地址合规筛查 + 合规网关。给收据,不给黑箱分:每条判决都带**可点开核验的证据**。覆盖 7 条 EVM 链 + 波场 TRON + Solana,付款前先问一句「这个收款方安不安全」。
98
101
 
99
- - **免费三工具**:`payment_decision`(付款前调一次 → allow/review/decline + 证据 + 可核验结算凭证)、`screen_address`(单查 + 证据)、`recent_flagged`(近期被标记)。无需 key、无需注册。
102
+ - **免费工具**:`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、无需注册。
100
103
  - **付费三工具(x402)**:`compliance_decision`(网关判决)、`deep_trace`(波场深度追踪)、`batch_screen`(批量)。按需加载 `@x402`+`viem`,设置 `OCEANALT_PAYER_KEY` 才启用。
101
104
  - **⚠️ 真钱**:付费端点**当前在 Base 主网结算,USDC 为真实资金**(`eip155:8453`),上表 `$` 价按真实 USDC 收取。付款前务必以实时清单 <https://oceanalt.com/api/x402> 为准,并使用**小额专用付款钱包**。
102
105
  - **🔐 钱包安全**:`OCEANALT_PAYER_KEY` 只在本地签一次 EIP-3009 授权,**绝不随包发布/上传/打日志**;facilitator 代付 gas、不托管资金。**建议用小额专用钱包**,别用主资金钱包私钥。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oceanalt-aml-mcp",
3
- "version": "0.5.1",
3
+ "version": "0.6.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
@@ -258,6 +259,75 @@ 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