oceanalt-aml-mcp 0.3.0 → 0.5.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 +103 -86
  2. package/package.json +52 -11
  3. package/server.mjs +124 -4
package/README.md CHANGED
@@ -1,86 +1,103 @@
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
- | `recent_flagged` | — | Recently flagged high-risk addresses | Free |
16
- | `compliance_decision` | `to`, `amountUsdc?`, `purpose?`, `network?` | Full gateway verdict (allow / review / decline) + evidence | $0.30 |
17
- | `deep_trace` | `address` (T…) | Tron multi-hop USDT taint trace (depth ≤3) | $0.20 |
18
- | `batch_screen` | `addresses[]` (≤25) | Batch screening | $0.10 |
19
-
20
- `network` (for `0x` addresses): `ethereum` · `base` · `bsc` · `polygon` · `arbitrum` · `optimism` · `avalanche` (auto-detected if omitted). Tron & Solana are auto-detected.
21
-
22
- ## Quick start (free tools)
23
-
24
- Add to your MCP client config (Claude Desktop, Claude Code, Cursor, …):
25
-
26
- ```json
27
- {
28
- "mcpServers": {
29
- "oceanalt-aml": { "command": "npx", "args": ["-y", "oceanalt-aml-mcp"] }
30
- }
31
- }
32
- ```
33
-
34
- The free tools need no API key and no signup.
35
-
36
- ## Enabling the paid (x402) tools
37
-
38
- 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:
39
-
40
- ```json
41
- {
42
- "mcpServers": {
43
- "oceanalt-aml": {
44
- "command": "npx", "args": ["-y", "oceanalt-aml-mcp"],
45
- "env": { "OCEANALT_PAYER_KEY": "0x<your payer wallet private key>" }
46
- }
47
- }
48
- }
49
- ```
50
-
51
- ```bash
52
- npm i @x402/core @x402/evm viem # in the MCP's environment
53
- ```
54
-
55
- > **⚠️ 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**.
56
- >
57
- > **🔐 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.
58
-
59
- ## Environment variables
60
-
61
- | Var | Purpose | Default |
62
- |-----|---------|---------|
63
- | `OCEANALT_BASE` | Backend base URL | `https://oceanalt.com` (production) |
64
- | `OCEANALT_PAYER_KEY` | Payer wallet key — enables the paid tools | unset (free tools only) |
65
-
66
- ## What it is (and isn't)
67
-
68
- 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).
69
-
70
- - **Programmatic SDK** (same capabilities, for Node / browser / Deno / Bun): [`oceanalt-aml`](https://www.npmjs.com/package/oceanalt-aml)
71
- - **API docs & examples**: <https://oceanalt.com/en/api-docs>
72
- - **RAP — the compliance standard behind the verdicts**: <https://oceanalt.com/en/rap>
73
-
74
- MIT © OceanAlt
75
-
76
- ---
77
-
78
- ## 中文
79
-
80
- 让 **Claude / Cursor / 任意 MCP 客户端**原生调用 OceanAlt 的链上地址合规筛查 + 合规网关。给收据,不给黑箱分:每条判决都带**可点开核验的证据**。覆盖 7 条 EVM 链 + 波场 TRON + Solana,付款前先问一句「这个收款方安不安全」。
81
-
82
- - **免费三工具**:`payment_decision`(付款前调一次 → allow/review/decline + 证据 + 可核验结算凭证)、`screen_address`(单查 + 证据)、`recent_flagged`(近期被标记)。无需 key、无需注册。
83
- - **付费三工具(x402)**:`compliance_decision`(网关判决)、`deep_trace`(波场深度追踪)、`batch_screen`(批量)。按需加载 `@x402`+`viem`,设置 `OCEANALT_PAYER_KEY` 才启用。
84
- - **⚠️ 真钱**:付费端点**当前在 Base 主网结算,USDC 为真实资金**(`eip155:8453`),上表 `$` 价按真实 USDC 收取。付款前务必以实时清单 <https://oceanalt.com/api/x402> 为准,并使用**小额专用付款钱包**。
85
- - **🔐 钱包安全**:`OCEANALT_PAYER_KEY` 只在本地签一次 EIP-3009 授权,**绝不随包发布/上传/打日志**;facilitator 代付 gas、不托管资金。**建议用小额专用钱包**,别用主资金钱包私钥。
86
- - 编程 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. 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>。
package/package.json CHANGED
@@ -1,19 +1,60 @@
1
1
  {
2
2
  "name": "oceanalt-aml-mcp",
3
- "version": "0.3.0",
4
- "mcpName": "io.github.chrisssun18-bot/oceanalt-aml",
5
- "description": "OceanAlt AML 的 MCP server —— 让 Claude / 任意 MCP agent 原生调用地址合规筛查(免费)+ x402 付费判决/追踪/批量。",
3
+ "version": "0.5.0",
4
+ "mcpName": "io.github.OceanAlt/oceanalt-aml",
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",
7
- "bin": { "oceanalt-aml-mcp": "server.mjs" },
7
+ "bin": {
8
+ "oceanalt-aml-mcp": "server.mjs"
9
+ },
8
10
  "main": "server.mjs",
9
- "files": ["server.mjs", "README.md"],
10
- "keywords": ["mcp", "aml", "x402", "agent", "compliance", "oceanalt", "claude", "sanctions", "usdc", "web3"],
11
+ "files": [
12
+ "server.mjs",
13
+ "README.md"
14
+ ],
15
+ "keywords": [
16
+ "mcp",
17
+ "model-context-protocol",
18
+ "aml",
19
+ "compliance",
20
+ "sanctions",
21
+ "ofac",
22
+ "payee-screening",
23
+ "address-risk",
24
+ "agent-payments",
25
+ "agentic-payments",
26
+ "x402",
27
+ "stablecoin",
28
+ "usdc",
29
+ "crypto",
30
+ "web3",
31
+ "claude",
32
+ "ai-agent",
33
+ "pre-settlement",
34
+ "wallet-screening",
35
+ "oceanalt"
36
+ ],
11
37
  "license": "MIT",
12
38
  "author": "OceanAlt (https://oceanalt.com)",
13
39
  "homepage": "https://oceanalt.com/en/api-docs",
14
- "repository": { "type": "git", "url": "git+https://github.com/OceanAlt666/oceanalt-aml-mcp.git" },
15
- "bugs": { "url": "https://oceanalt.com/en/partner" },
16
- "publishConfig": { "registry": "https://registry.npmjs.org/", "access": "public" },
17
- "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", "zod": "^3.23.0" },
18
- "optionalDependencies": { "@x402/core": ">=2.21.0", "@x402/evm": ">=2.21.0", "viem": ">=2.0.0" }
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/OceanAlt666/oceanalt-aml-mcp.git"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/OceanAlt666/oceanalt-aml-mcp/issues"
46
+ },
47
+ "publishConfig": {
48
+ "registry": "https://registry.npmjs.org/",
49
+ "access": "public"
50
+ },
51
+ "dependencies": {
52
+ "@modelcontextprotocol/sdk": "^1.0.0",
53
+ "zod": "^3.23.0"
54
+ },
55
+ "optionalDependencies": {
56
+ "@x402/core": ">=2.21.0",
57
+ "@x402/evm": ">=2.21.0",
58
+ "viem": ">=2.0.0"
59
+ }
19
60
  }
package/server.mjs CHANGED
@@ -19,12 +19,23 @@
19
19
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
20
20
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
21
21
  import { z } from "zod";
22
+ import { readFileSync } from "node:fs";
22
23
 
23
24
  const BASE = (process.env.OCEANALT_BASE || "https://oceanalt.com").replace(/\/$/, "");
24
25
  const TIMEOUT = 15000;
25
26
  // Supported EVM chains — kept in lockstep with the site's lib/aml.ts EVM_NETS (only chains proven live).
26
27
  const NETWORKS = ["ethereum", "base", "bsc", "polygon", "arbitrum", "optimism", "avalanche"];
27
28
 
29
+ async function apiPost(path, body) {
30
+ const ctrl = new AbortController();
31
+ const t = setTimeout(() => ctrl.abort(), TIMEOUT);
32
+ try {
33
+ const res = await fetch(BASE + path, { method: "POST", signal: ctrl.signal, headers: { accept: "application/json", "content-type": "application/json" }, body: JSON.stringify(body) });
34
+ if (!res.ok && res.status >= 500) throw new Error(`OceanAlt ${path} → HTTP ${res.status}`);
35
+ return await res.json();
36
+ } finally { clearTimeout(t); }
37
+ }
38
+
28
39
  async function apiGet(path) {
29
40
  const ctrl = new AbortController();
30
41
  const t = setTimeout(() => ctrl.abort(), TIMEOUT);
@@ -35,7 +46,15 @@ async function apiGet(path) {
35
46
  } finally { clearTimeout(t); }
36
47
  }
37
48
 
38
- const server = new McpServer({ name: "oceanalt-aml", version: "0.3.0" });
49
+ // 版本号从 package.json 读,不写死。
50
+ // 2026-09-08:这里曾经写死 "0.4.0",而启动日志写死 "v0.3",package.json 又是第三个数 ——
51
+ // 三处各说各话,发布时还漏过一次版本号(commit 52f351c)。单一事实源之后不会再漂。
52
+ // npm 打包永远会带上 package.json,所以正常情况读得到;万一读不到也不能让 server 起不来。
53
+ const PKG_VERSION = (() => {
54
+ try { return JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8")).version; }
55
+ catch { return "0.0.0-unknown"; }
56
+ })();
57
+ const server = new McpServer({ name: "oceanalt-aml", version: PKG_VERSION });
39
58
 
40
59
  server.registerTool(
41
60
  "screen_address",
@@ -51,12 +70,14 @@ server.registerTool(
51
70
  .enum(NETWORKS)
52
71
  .optional()
53
72
  .describe("EVM chain to screen on. Omit to auto-default to ethereum; ignored for Tron/Solana (auto-detected). One of: ethereum, base, bsc, polygon, arbitrum, optimism, avalanche."),
73
+ lang: z.enum(["en", "zh"]).optional().describe("Language for human-readable fields (signals, note). Defaults to English."),
54
74
  },
55
75
  annotations: { title: "Screen address (AML, free)", readOnlyHint: true, idempotentHint: true, openWorldHint: true },
56
76
  },
57
- async ({ address, network }) => {
77
+ async ({ address, network, lang }) => {
58
78
  const q = new URLSearchParams({ addr: address });
59
79
  if (network) q.set("network", network);
80
+ if (lang) q.set("lang", lang);
60
81
  const r = await apiGet(`/api/risk?${q.toString()}`);
61
82
  const lines = [
62
83
  `Address: ${r.address}`,
@@ -75,6 +96,103 @@ server.registerTool(
75
96
  }
76
97
  );
77
98
 
99
+ server.registerTool(
100
+ "payee_decide",
101
+ {
102
+ title: "Ask the payee whether it accepts this payment (free)",
103
+ description:
104
+ "The other half of a safe agent payment. Before settling, ask the registered payee (by its OceanAlt registry sid — see https://oceanalt.com/registry.json) whether it accepts this incoming payment. Returns status accept | decline | hold_request_info | not_ready, machine-readable reason codes, requested_fields (only on hold), verifiable evidence and a signature. Rule-based, not model judgement: payee readiness comes from the public registry enforcement ladder (https://oceanalt.com/en/registry/rules), the payer address is screened in reverse, and amount / purpose / KYA requirements come from the payee's own policy. decline is final — re-paying does not change it. Free, no API key.",
105
+ inputSchema: {
106
+ payee: z.string().describe("Payee sid from the OceanAlt registry, e.g. oceanalt-200lab-premium"),
107
+ payerAddress: z.string().optional().describe("The paying wallet address (screened in reverse)"),
108
+ payerAgentId: z.string().optional().describe("The paying agent's KYA id, if the payee requires KYA"),
109
+ amountUsdc: z.number().optional().describe("Amount in USDC"),
110
+ purpose: z.string().optional().describe("Purpose of the payment, e.g. api-call"),
111
+ },
112
+ annotations: { title: "Payee decision (free)", readOnlyHint: false, idempotentHint: false, openWorldHint: true },
113
+ },
114
+ async ({ payee, payerAddress, payerAgentId, amountUsdc, purpose }) => {
115
+ const r = await apiPost("/api/payee/decide", { payee, payer: { address: payerAddress, agentId: payerAgentId }, amountUsdc, purpose });
116
+ const lines = [
117
+ `Status: ${r.status}${r.final ? " (final)" : ""}`,
118
+ `Reasons: ${(r.reasons || []).join(", ")}`,
119
+ (r.requested_fields && r.requested_fields.length) ? `Requested fields: ${r.requested_fields.join(", ")}` : "",
120
+ r.expires_at ? `Expires: ${r.expires_at}` : "",
121
+ r.payee_ref ? `Payee: ${r.payee_ref.name} · ${r.payee_ref.rating_status} · open disputes ${r.payee_ref.open_disputes}` : "",
122
+ r.next ? `Next: ${r.next}` : "",
123
+ r.signature ? `Verify: ${r.signature.verify_url}` : "",
124
+ ].filter(Boolean);
125
+ return { content: [{ type: "text", text: lines.join(String.fromCharCode(10)) }], structuredContent: r };
126
+ }
127
+ );
128
+
129
+ server.registerTool(
130
+ "counterparty_control_baseline",
131
+ {
132
+ title: "Check a counterparty's control baseline before paying (free)",
133
+ description:
134
+ "Answers a different question from address screening. Screening asks whether an address is risky; this asks how much damage the agent on the other side could do if it were talked into something — whether a spending ceiling exists, whether payees are restricted, whether its credential can be revoked. It returns that party's self-attestation against the OceanAlt Agent Control Baseline, split into two sets that must NOT be conflated: `verified_by_oceanalt` (controls the gateway enforces on every payment — provable) and `self_claimed` (what the party states about its own side — NOT verified by OceanAlt). IMPORTANT: most parties have not attested. A result of found:false means no information, not a bad signal — never decline a payment on that basis alone. Free, no API key.",
135
+ inputSchema: {
136
+ entity: z.string().optional().describe("The counterparty's entity name, e.g. \"Acme Robotics Ltd\"."),
137
+ agentId: z.string().optional().describe("Their agent id, if you have it instead of an entity name."),
138
+ },
139
+ annotations: { title: "Counterparty control baseline (free)", readOnlyHint: true, idempotentHint: true, openWorldHint: true },
140
+ },
141
+ async ({ entity, agentId }) => {
142
+ const q = new URLSearchParams();
143
+ if (entity) q.set("entity", entity);
144
+ if (agentId) q.set("agentId", agentId);
145
+ if (!q.toString()) return { content: [{ type: "text", text: "Provide either entity or agentId." }], isError: true };
146
+ const r = await apiGet(`/api/baseline/lookup?${q.toString()}`);
147
+ const lines = r.found
148
+ ? [
149
+ `Party: ${r.entity || r.agentId || r.handle}`,
150
+ `Status: ${r.status} (mandatory ${r.mandatory_met}/${r.mandatory_total}, advisory ${r.advisory_met}/${r.advisory_total})`,
151
+ r.missing_mandatory?.length ? `Declared gaps: ${r.missing_mandatory.join(", ")}` : "",
152
+ r.gap_plan ? `Their plan to close them: ${r.gap_plan}` : "",
153
+ `Verified by OceanAlt (enforced on every payment): ${(r.verified_by_oceanalt || []).join(", ") || "none"}`,
154
+ `Self-claimed (NOT verified by OceanAlt): ${(r.self_claimed || []).join(", ") || "none"}`,
155
+ `Attested: ${r.attested_at?.slice(0, 10) || "?"} · Expires: ${r.expires_at?.slice(0, 10) || "?"}`,
156
+ ]
157
+ : [
158
+ "No control-baseline attestation on record for this party.",
159
+ "This is absence of information, not a negative signal — most parties have not attested.",
160
+ "Do not decline on this basis alone; fall back to address screening (payment_decision).",
161
+ ];
162
+ return { content: [{ type: "text", text: [...lines.filter(Boolean), `Baseline: https://oceanalt.com/en/baseline`].join("\n") }], structuredContent: r };
163
+ }
164
+ );
165
+
166
+ server.registerTool(
167
+ "resolve_agent_identity",
168
+ {
169
+ title: "Resolve an agent's chain of accountability (free)",
170
+ description:
171
+ "Resolves who stands behind a paying agent: agent → principal → mandate → credential → wallet. Each link reports whether it holds, and `completeness` counts how many do — it is a factual count, not a score. Wherever the chain breaks is where accountability stops, which is what you actually need to know before accepting a payment from an autonomous agent. Publication is opt-in and only posture is exposed (that a ceiling exists, never its value), so found:false means the agent has not published — absence of information, not a bad signal. Free, no API key.",
172
+ inputSchema: { agentId: z.string().describe("The agent id to resolve.") },
173
+ annotations: { title: "Resolve agent identity (free)", readOnlyHint: true, idempotentHint: true, openWorldHint: true },
174
+ },
175
+ async ({ agentId }) => {
176
+ const r = await apiGet(`/api/registry/agent?agentId=${encodeURIComponent(agentId)}`);
177
+ const lines = r.found
178
+ ? [
179
+ `Agent: ${r.name || r.agentId}`,
180
+ `Chain of accountability: ${r.completeness}`,
181
+ ...["agent", "principal", "mandate", "credential", "wallet"].map(
182
+ (k) => ` ${r.chain?.[k]?.present ? "[ok]" : "[--]"} ${k}: ${r.chain?.[k]?.detail || ""}`
183
+ ),
184
+ r.baseline ? `Control baseline: ${r.baseline.status}` : "",
185
+ r.note,
186
+ ]
187
+ : [
188
+ "No published record for that agent id.",
189
+ "Publication is opt-in, so this is absence of information rather than a negative signal.",
190
+ "Fall back to address screening (payment_decision).",
191
+ ];
192
+ return { content: [{ type: "text", text: lines.filter(Boolean).join("\n") }], structuredContent: r };
193
+ }
194
+ );
195
+
78
196
  server.registerTool(
79
197
  "recent_flagged",
80
198
  {
@@ -112,14 +230,16 @@ server.registerTool(
112
230
  amountUsdc: z.number().optional().describe("Amount in USDC (optional; recorded/echoed, not required for the decision)."),
113
231
  purpose: z.string().optional().describe("Short purpose/memo for the payment (optional)."),
114
232
  network: z.enum(NETWORKS).optional().describe("EVM chain for the payee (same set as screen_address). Optional; ignored for Tron."),
233
+ lang: z.enum(["en", "zh"]).optional().describe("Language for human-readable fields (signals, note, advice). Defaults to English."),
115
234
  },
116
235
  annotations: { title: "Payment decision (free)", readOnlyHint: true, idempotentHint: true, openWorldHint: true },
117
236
  },
118
- async ({ to, amountUsdc, purpose, network }) => {
237
+ async ({ to, amountUsdc, purpose, network, lang }) => {
119
238
  const q = new URLSearchParams({ to });
120
239
  if (amountUsdc != null) q.set("amountUsdc", String(amountUsdc));
121
240
  if (purpose) q.set("purpose", purpose);
122
241
  if (network) q.set("network", network);
242
+ if (lang) q.set("lang", lang);
123
243
  const r = await apiGet(`/api/decide?${q.toString()}`);
124
244
  const lines = [
125
245
  `Decision: ${r.decision} (allow=${r.allow})`,
@@ -235,4 +355,4 @@ server.registerTool(
235
355
  const transport = new StdioServerTransport();
236
356
  await server.connect(transport);
237
357
  // Once connected, don't print to stdout (stdio transport owns it); diagnostics go to stderr.
238
- console.error(`[oceanalt-aml-mcp] v0.3 started, base = ${BASE}, paid tools ${PAYER_KEY ? "enabled" : "disabled (OCEANALT_PAYER_KEY not set — free tools only)"}`);
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)"}`);