@zenonft/mcp
v1.4.1
Published
ZenoNFT MCP Server — Agent-ready NFT marketplace ops: AI chat (customer support / sales / on-chain transactions), 21+ zenonft_ tools, chain sign-challenge auth, API Key / SIWE
Maintainers
Readme
@zenonft/mcp v1.4.1
ZenoNFT MCP Server — JSON-RPC 2.0 工具集 + AI Agent 对话(客服 / 销售 / 链上交易三 Bot 自动路由),部署于 OxaChain L1 主网。
三种安全接入方式:签名挑战(外部 Agent 自持私钥)、SIWE 会话(带钱包 Agent)、API Key(无钱包 Agent)。
鉴权概览
| 模式 | 适用 agent | 鉴权头 | 链上签名 | 身份归属 |
|------|-----------|--------|---------|---------|
| 签名挑战(推荐) | 用户自有外部 Agent(持私钥) | Authorization: Bearer zns_... | per-call signerKey(必须与会话恢复地址一致) | agent 自身钱包 |
| SIWE 会话 | 带钱包 agent(网页流程) | Authorization: Bearer zns_... | per-call signerKey(地址必须与会话一致) | agent 自身钱包 |
| API Key | 无钱包(Codex 等) | Authorization: Bearer znf_... | 服务端绑定的 executor 钱包 | 用户钱包地址(Key 与钱包绑定) |
| Legacy | 存量客户端 | Authorization: Bearer <BRIDGE_MCP_TOKEN> | SIGNER_KEY 或 per-call | 平台 |
- 读接口:匿名可调(限流内)。
- 写接口:必须有上述任一鉴权;API Key 模式下调用方传的
signerKey会被忽略(防越权)。 - 签名挑战 vs SIWE:两者最终都绑定钱包地址、行为一致;区别在登录方式——签名挑战用「随机 nonce 消息 + 私钥 signMessage」,服务端
recoverMessageAddress恢复地址,不预绑定地址、更适合"用户自己的外部 Agent"直接调用。
安装与启动
npm install @zenonft/mcp
# 配置环境变量
export BRIDGE_MCP_PORT=3300 # 监听端口(默认 3300)
export ZENONFT_MASTER_KEY=0x... # API Key 主密钥(可选,用于加密 executor 私钥)
export ALLOWED_ORIGINS=https://your-domain
# AI 对话(Reach 平台;可选,缺省降级为纯工具服务)
export REACH_BASE_URL=https://reach.codes
export REACH_API_KEY=aisvc-...
export REACH_PROJECT_ID=...
export REACH_BOT_SUPPORT=... # 客服 Bot ID
export REACH_BOT_SALES=... # 销售 Bot ID
export REACH_BOT_TRANSACTION=... # 链上交易 Bot ID
export REACH_LLM_API_KEY=... # BYOK LLM(Reach 商户必需)
export REACH_LLM_ENDPOINT=https://api.deepseek.com/v1
export REACH_LLM_MODEL=deepseek-chat
# 启动
npx @zenonft/mcp
# 或
npm start链默认 OxaChain 主网(Chain ID 19505),可用
MCP_CHAIN/MCP_RPC_URL/MCP_MARKETPLACE_ADDRESS/MCP_FACTORY_ADDRESS/TUSDC_ADDRESS覆盖(详见下方环境变量表)。
让"你自己的 Agent"接入
方式 A:签名挑战(推荐,外部 Agent 自持私钥)
# 1. 获取随机挑战消息
curl -X POST https://43.156.99.215:3300/auth/sign-challenge \
-H "Content-Type: application/json"
# → { "message": "ZenoNFT MCP login: <nonce>", "nonce": "..." }
# 2. 用你的私钥对 message 做 signMessage(personal_sign)
# 3. 验签 → 服务端恢复出你的钱包地址并签发会话
curl -X POST https://43.156.99.215:3300/auth/sign-verify \
-H "Content-Type: application/json" \
-d '{"message":"...", "signature":"0x..."}'
# → { "token": "zns_...", "address": "0x...", "expiresAt": 1234567890 }之后调用写工具时带 Authorization: Bearer zns_...,参数中传 signerKey(其地址必须等于服务端恢复出的地址),私钥始终不出本地。
方式 B:SIWE 会话(网页钱包流程)
# 1. 获取 EIP-4361 签名消息
curl -X POST http://127.0.0.1:3300/auth/challenge \
-H "Content-Type: application/json" \
-d '{"address":"0x你的钱包地址"}'
# 2. 用钱包对 message 做 personal_sign,验签换会话
curl -X POST http://127.0.0.1:3300/auth/verify \
-H "Content-Type: application/json" \
-d '{"message":"...", "signature":"0x..."}'
# → { "token": "zns_...", "address": "0x...", "expiresAt": 1234567890 }方式 C:API Key(无钱包 agent)
- 打开 ZenoNFT 前端 AI 助手页 → API Key,连接钱包签发 Key(绑定钱包地址)。
- 将 Key 配置到 agent:
{
"mcpServers": {
"zenonft": {
"url": "http://43.156.99.215:3300",
"headers": { "Authorization": "Bearer znf_你的APIKey" }
}
}
}AI Agent 对话(SSE)
前端 AI 助手页与外部 agent 共用同一套对话能力。POST /agent/chat 返回 SSE 事件流:
| 事件 | 说明 |
|------|------|
| chunk | 回复文本片段({ content }) |
| action | 链上操作载荷,需钱包签名确认({ id, type, summary, tx: { to, data, value } }) |
| done | 会话结束({ conversationId }) |
| error | 错误信息({ message }) |
三 Bot 自动路由:服务端把用户消息交给 Reach 平台,自动判断走哪个 Bot——
| 场景 | Bot | 举例 | |------|-----|------| | 咨询求助 | 客服 | "平台怎么收费?" | | 项目了解 | 销售 | "这个市场有什么优势?" | | 动手交易 | 链上交易 | "帮我买下 3 号挂单" |
21 个 zenonft_ 对话工具(LLM 经工具回调执行,POST /agent/tools):
- 查询(10):
zenonft_get_active_listings/get_listing_detail/get_listing_count/get_collections/get_collection_info/get_nft_owner/get_offer/get_platform_fee/get_deploy_fee/get_user_owned - 领水(1):
zenonft_faucet_claim - 构建交易(10):
zenonft_build_mint_tx/build_list_tx/build_buy_tx/build_bid_tx/build_offer_tx/build_accept_offer_tx/build_cancel_listing_tx/build_end_auction_tx/build_create_auction_tx/build_deploy_collection_tx
构建工具在服务端用 viem 编码真实 calldata 并暂存(会话关联),通过
action事件推给前端——AI 只准备交易,最终签名永远由钱包完成。
对话历史按钱包地址隔离:GET /agent/history?address=0x...、POST /agent/history/clear(携带有效 zns_ 会话时以会话地址为准)。
REST 接口
| 接口 | 鉴权 | 说明 |
|------|------|------|
| POST /auth/sign-challenge | 公开 | 生成随机 nonce 挑战消息(签名挑战登录) |
| POST /auth/sign-verify | 公开 | 验签(恢复地址)并签发会话 token |
| POST /auth/challenge | 公开 | 生成 EIP-4361 签名消息(SIWE 登录) |
| POST /auth/verify | 公开 | 验签并签发会话 token |
| GET /auth/session | 会话 | 当前会话信息 |
| POST /keys | 会话 | 生成 API Key + 独立 executor 钱包 |
| GET /keys | 会话 | 列出我的 Key |
| DELETE /keys/:id | 会话 | 吊销 Key |
| POST /agent/chat | 公开* | AI 对话(SSE),可携带 zns_ 会话绑定地址 |
| POST /agent/tools | 公开* | Reach 工具回调端点(X-Tool-Key 校验;工具名由 callback_url 的 tool query 参数传递) |
| GET /agent/history | 公开* | 对话历史(按地址隔离) |
| POST /agent/history/clear | 公开* | 清空对话历史 |
| POST /ipfs/pin-file | 公开(限流) | 文件上传代理(multipart file)→ Pinata pinFileToIPFS,JWT 仅存服务端 |
| POST /ipfs/pin-json | 公开(限流) | JSON 元数据上传代理 → Pinata pinJSONToIPFS |
| GET /health | 公开 | 健康检查 |
*公开路径也解析 Bearer 会话 token(
zns_),用于地址绑定与历史隔离。
23 个 JSON-RPC MCP Tools
写操作(需鉴权)
| # | Tool | 参数 | 说明 |
|---|---|---|---|
| 1 | nft_listItem | nftContract, tokenId, priceWei, quoteToken? | 定价挂单 |
| 2 | nft_createAuction | nftContract, tokenId, startingPriceWei, durationSeconds, quoteToken? | 创建英式拍卖 |
| 3 | nft_buy | listingId, priceWei | 定价购买 NFT |
| 4 | nft_bid | listingId, bidWei | 拍卖出价 |
| 5 | nft_deployCollection | name, symbol, maxSupply, mintPrice?, royaltyBps?, soulbound?, quoteToken? | 部署合集 |
| 6 | nft_mint | collectionAddress, tokenURI, quantity? | 铸造 NFT(1-50) |
| 7 | nft_cancelListing | listingId | 取消挂单 |
| 8 | nft_endAuction | listingId | 结算拍卖 |
| 9 | nft_makeOffer | listingId, offerWei | 对定价挂单报价 |
| 10 | nft_acceptOffer | listingId | 卖家接受报价 |
| 11 | nft_deployAndBatchMint | name, symbol, maxSupply, mintPrice?, royaltyBps?, baseURI?, soulbound?, maxMintsPerAddress?, tokenURIs, quoteToken? | 一键部署 + 批量铸造 |
| 12 | nft_ownerBatchMint | collectionAddress, tokenURIs | Owner 批量空投 |
| 13 | nft_manageCollection | collectionAddress, mintPrice?, royaltyBps?, baseURI?, soulbound?, mintingEnabled? | 合集管理 |
| 14 | nft_updateWhitelist | collectionAddress, action, addresses | 白名单增删 |
| 15 | nft_withdraw | collectionAddress | Owner 提取资金 |
写工具参数
quoteToken可选:传 TUSDC 地址则按 ERC20 计价(内部自动approve),省略 = 原生币。 仅会话模式(签名挑战 / SIWE)支持 per-callsignerKey;API Key 模式由服务端 executor 钱包签名,不可覆盖。
只读查询(匿名可调)
| # | Tool | 参数 | 说明 |
|---|---|---|---|
| 16 | nft_getListing | listingId | 挂单详情 |
| 17 | nft_getActiveListings | offset?, limit? | 活跃挂单(分页) |
| 18 | nft_getOffer | listingId | 最佳报价 |
| 19 | nft_getCollections | offset?, limit? | 全部合集 |
| 20 | nft_getCollectionInfo | collectionAddress | 合集信息 |
| 21 | nft_getTokenInfo | collectionAddress, tokenId | NFT 详情 |
| 22 | nft_getBalance | collectionAddress, address | NFT 余额 |
| 23 | nft_getStatus | address? | 链状态 + 市场统计 |
JSON-RPC 2.0 API
initialize
{ "jsonrpc": "2.0", "method": "initialize", "id": 1 }tools/list
{ "jsonrpc": "2.0", "method": "tools/list", "id": 2 }tools/call
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": { "name": "nft_getStatus", "arguments": { "address": "0x..." } },
"id": 3
}写工具示例(会话模式带 signerKey):
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "nft_listItem",
"arguments": { "signerKey": "0x...", "nftContract": "0x...", "tokenId": "42", "priceWei": "1000000000000000000" }
},
"id": 4
}GET /health
curl http://127.0.0.1:3300/health
# { "ok": true, "port": 3300, "tools": 23 }环境变量
| 变量 | 必需 | 默认值 | 说明 |
|---|---|---|---|
| BRIDGE_MCP_PORT | 否 | 3300 | 监听端口 |
| BRIDGE_MCP_TOKEN | 否 | "" | legacy 静态 Bearer token(存量客户端兼容) |
| SIGNER_KEY | 否 | "" | legacy 模式默认签名私钥 |
| ZENONFT_MASTER_KEY | 否* | "" | API Key 主密钥(加密 executor 私钥);未设置时用 sha256(BRIDGE_MCP_TOKEN) 派生 |
| MCP_CHAIN | 否 | oxachain | 链键(默认 OxaChain 主网) |
| MCP_CHAIN_ID | 否 | 19505 | 链 ID |
| MCP_CHAIN_NAME | 否 | OxaChain | 链名 |
| MCP_RPC_URL | 否 | https://rpc-oxa.0xainet.top | RPC URL |
| MCP_EXPLORER_URL | 否 | https://explorer-oxa.0xainet.top | 区块浏览器 |
| MCP_MARKETPLACE_ADDRESS | 否 | 主网地址 | Marketplace 合约 |
| MCP_FACTORY_ADDRESS | 否 | 主网地址 | Factory 合约 |
| TUSDC_ADDRESS | 否 | 主网地址 | TUSDC 计价代币 |
| ALLOWED_ADDRESSES | 否 | "" | 写接口地址白名单(逗号分隔;空 = 不限) |
| ALLOWED_ORIGINS | 否 | * | 浏览器 CORS 来源(逗号分隔) |
| SESSION_TTL_SECONDS | 否 | 86400 | 会话有效期(秒) |
| CHALLENGE_TTL_SECONDS | 否 | 300 | challenge 有效期(秒) |
| RATE_LIMIT_WRITE_MAX | 否 | 10 | 写操作每分钟上限 |
| RATE_LIMIT_READ_MAX | 否 | 120 | 读操作每分钟上限 |
| RATE_LIMIT_WINDOW_MS | 否 | 60000 | 限流窗口 |
| ZENONFT_DATA_DIR | 否 | ./data | 数据目录(keys/sessions/challenges/audit.log) |
| REACH_BASE_URL | 否 | https://reach.codes | Reach 客服域 REST(Gateway /api/v1) |
| REACH_SERVER_URL | 否 | https://reach.codes | Reach Server API(与 Gateway 同域) |
| REACH_API_KEY | 否 | "" | Reach 平台 API Key(aisvc- 前缀;AI 对话必需) |
| REACH_PROJECT_ID | 否 | "" | Reach 项目 ID |
| REACH_BOT_SUPPORT | 否 | "" | 客服 Bot ID |
| REACH_BOT_SALES | 否 | "" | 销售 Bot ID |
| REACH_BOT_TRANSACTION | 否 | "" | 链上交易 Bot ID |
| REACH_CALLBACK_BASE | 否 | "" | 工具回调公网地址(Reach 回调本服务) |
| REACH_CALLBACK_KEY | 否 | "" | 工具回调校验密钥(X-Tool-Key 头) |
| REACH_LLM_API_KEY | 否 | "" | BYOK LLM API Key(Reach 商户必需) |
| REACH_LLM_ENDPOINT | 否 | "" | BYOK LLM 端点 |
| REACH_LLM_MODEL | 否 | "" | BYOK LLM 模型(如 deepseek-chat) |
| REACH_ROUTE_TIMEOUT_MS | 否 | 8000 | LLM 意图路由超时(毫秒) |
| FAUCET_API_URL | 否 | https://explorer-oxa.0xainet.top/faucet/api | 领水服务地址 |
| PINATA_JWT | 否 | 空 | Pinata JWT,用于 IPFS 上传代理(仅存服务端) |
| PINATA_API_URL | 否 | https://api.pinata.cloud | Pinata API 地址 |
| RATE_LIMIT_IPFS_MAX | 否 | 20 | IPFS 上传每分钟上限(按 IP) |
* 要使用 API Key 模式则需设置主密钥;只跑会话/legacy 模式可不设。
安全说明
- 签名挑战 / SIWE:服务端只保存恢复出的地址与会话,永不接触私钥;写操作强制
signerKey地址 = 会话地址。 - executor 私钥以 AES-256-GCM 加密落盘(主密钥
ZENONFT_MASTER_KEY)。 - API Key 明文只返回一次(
POST /keys),服务端仅存sha256哈希。 - 会话 token 服务端存储,吊销/过期即失效。
- 写操作按身份限流(默认 10/min)并写入
audit.log。
架构
┌──────────────┐ JSON-RPC 2.0 / REST ┌───────────────────────────────────┐
│ Claude/Cursor│ ◄─────── HTTP ────────► │ @zenonft/mcp (Express, :3300) │
│ (AI Agent) │ Bearer znf_/zns_ │ ├─ Auth: 签名挑战/SIWE/APIKey/legacy│
└──────────────┘ │ ├─ 23 JSON-RPC Tools │
┌──────────────┐ /zenonft-api(同源代理)│ ├─ /agent/chat (SSE) + 三 Bot 路由 │
│ 前端 AI 助手页 │ ◄──────────────────────►│ ├─ /agent/tools(21 个 zenonft_ 工具)│
│ (对话 UI+Key 管理)│ │ └─ @zenonft/sdk ──► OxaChain RPC │
└──────────────┘ └───────────────────────────────────┘
Reach 平台(三 Bot + BYOK LLM)◄── 意图路由/工具回调 ──┘本地开发
cd mcp
npm install
BRIDGE_MCP_PORT=3300 ZENONFT_MASTER_KEY=dev-secret npm start