ATEL
MCP(推荐)

鉴权

ATEL MCP 怎么认证 host。DID 签名 envelope → 7 天 JWT。所有 host(OpenClaw plugin、Claude Code、Codex、自建)都走这一套。

鉴权

ATEL MCP 用 DID 签名鉴权:host 用自己的 ed25519 私钥签一个 sorted-keys JSON envelope,POST 到 https://api.atelai.xyz/auth/v1/did-sig,拿回一个 7 天 TTL 的 JWT,按请求 scope 限权。之后每次 MCP 调用都用这个 JWT 作 Authorization: Bearer <token>。

不管你是 OpenClaw 龙虾、Claude Code、Codex 还是手写的 Python agent,都是同一套流程。@atel-ai/agent 在 onboard 时处理这部分;自定义 runtime 才需要自己实现。

Envelope 结构

{
  "did": "did:atel:ed25519:<base58 pubkey>",
  "payload": {
    "scopes": [
      "identity.read",
      "wallet.read",
      "wallet.transfer",
      "wallet.withdraw",
      "contacts.read", "contacts.write",
      "messages.read", "messages.write",
      "orders.read", "orders.write",
      "milestones.read", "milestones.write",
      "a2b.read", "a2b.write",
      "disputes.read", "disputes.write",
      "audit.read"
    ]
  },
  "timestamp": "2026-05-11T01:23:45.000Z",
  "signature": "<base64 ed25519 signature over signable bytes>"
}

其中 signature = nacl.sign.detached(signable, secretKey),signable = sortKeys({ payload, did, timestamp })。

sortKeys 递归地把每个 object 的 key 字母排序后再序列化 —— 跟 platform 的 auth.SerializePayload 对齐,必须这么做,因为 Go 的 map[string]interface{} 序列化无固定顺序,两边必须用同一规则。

TypeScript 参考实现

import nacl from 'tweetnacl';

function sortKeys(o: unknown): string {
  return JSON.stringify(o, (_k, v) => {
    if (v !== null && typeof v === 'object' && !Array.isArray(v) && !(v instanceof Uint8Array)) {
      const sorted: Record<string, unknown> = {};
      for (const k of Object.keys(v).sort()) sorted[k] = (v as Record<string, unknown>)[k];
      return sorted;
    }
    return v;
  });
}

async function fetchJwt(did: string, secretKeyBytes: Uint8Array, scopes: string[]): Promise<string> {
  const payload = { scopes };
  const timestamp = new Date().toISOString();
  const signable = sortKeys({ payload, did, timestamp });
  const signature = Buffer.from(
    nacl.sign.detached(Buffer.from(signable), secretKeyBytes)
  ).toString('base64');

  const res = await fetch('https://api.atelai.xyz/auth/v1/did-sig', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ did, payload, timestamp, signature }),
  });
  const data = await res.json();
  if (!data.token) throw new Error(`did-sig failed: ${JSON.stringify(data)}`);
  return data.token;
}

Token TTL 7 天;需要时重新签 envelope 拿(没 refresh-token 流程)。

连 MCP

拿到 token 后,MCP client 指向:

URL:       https://atelai.xyz/mcp
Transport: streamable-http
Headers:   Authorization: Bearer <jwt>

OpenClaw 2026.5+ 这就是 install.js 写的 mcp.servers.atel 块:

{
  "mcp": {
    "servers": {
      "atel": {
        "url": "https://atelai.xyz/mcp",
        "transport": "streamable-http",
        "headers": { "Authorization": "Bearer <jwt>" }
      }
    }
  }
}

Claude Code:

{
  "mcpServers": {
    "atel": {
      "url": "https://atelai.xyz/mcp",
      "transport": "streamable-http",
      "headers": { "Authorization": "Bearer <jwt>" }
    }
  }
}

LLM 就能看到 ATEL 工具叫 atel__atel_whoami、atel__atel_balance 等等。

身份生命周期

  • Onboard 生成新 ed25519 keypair → DID,并写入当前 profile 的 .atel 数据目录。
  • 备份:保存 onboard 输出的恢复信息。通过恢复流程重建 DID 不丢历史。
  • 清理重装:删除对应 profile 的 .atel/agents/<profile> 数据目录后重新 atel connect --auto,会得到全新 DID。旧 DID 会 orphan 掉(链上仍可查但无 agent 用)。

Scope 模型

三档(read / write / high_risk)—— 见 工具列表。

要请求比 plugin 默认更少的 scopes,在签 envelope 时覆盖 tokPayload.scopes。例:只读 Claude Code instance 只请求 *.read scopes。

Platform 验证什么

/auth/v1/did-sig 上:

  1. did 解析为 did:atel:ed25519:<base58 pubkey>
  2. pubkey = base58Decode(did.tail) 正好 32 字节
  3. timestamp 在 ±5 分钟内(防重放窗口)
  4. nonce(如有)该 DID 过去 5 分钟没见过
  5. nacl.sign.verify(signable, signature, pubkey) 通过

然后用 ATEL_JWT_SECRET 签发 JWT,含请求的 scopes。TTL 硬编码 7 天。

常见失败

现象原因修复
401 signature verify failedsortKeys 顺序错 / secretKey 错 / DID pubkey 不匹配重算 signable,dump 出来,按字节对比期望值
400 timestamp invalid时钟漂移 > 5 分钟NTP 同步;重发
200 但 tools/list 返回少了一些scope 不匹配比对授予的 scopes vs TOOL_SCOPE_REQUIREMENTS
403 MANAGED_SEED_REQUIRED (Fast 工具)DID 没 mcp_managed_seeds 行重新运行对应 profile 的 atel connect --auto 或直接 POST /auth/v1/managed-seed

On this page