hchain-mcp 1.0.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.
- package/.env.example +5 -0
- package/AGENT-MCP-RULES.md +248 -0
- package/OnchainOS-API/345/257/271/346/216/245/350/247/204/350/214/203.md +329 -0
- package/README.md +106 -0
- package/dist/adapters/onchainos-ws.d.ts +31 -0
- package/dist/adapters/onchainos-ws.js +103 -0
- package/dist/adapters/onchainos.d.ts +343 -0
- package/dist/adapters/onchainos.js +275 -0
- package/dist/adapters/shared.d.ts +23 -0
- package/dist/adapters/shared.js +107 -0
- package/dist/http.d.ts +9 -0
- package/dist/http.js +124 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +54 -0
- package/dist/tools/balance.d.ts +4 -0
- package/dist/tools/balance.js +69 -0
- package/dist/tools/defi.d.ts +4 -0
- package/dist/tools/defi.js +268 -0
- package/dist/tools/gateway.d.ts +4 -0
- package/dist/tools/gateway.js +107 -0
- package/dist/tools/intent.d.ts +4 -0
- package/dist/tools/intent.js +111 -0
- package/dist/tools/market.d.ts +4 -0
- package/dist/tools/market.js +612 -0
- package/dist/tools/payments.d.ts +4 -0
- package/dist/tools/payments.js +100 -0
- package/dist/tools/skills.d.ts +4 -0
- package/dist/tools/skills.js +464 -0
- package/dist/tools/trade.d.ts +4 -0
- package/dist/tools/trade.js +195 -0
- package/dist/tools/txhistory.d.ts +4 -0
- package/dist/tools/txhistory.js +49 -0
- package/dist/tools/ws.d.ts +4 -0
- package/dist/tools/ws.js +64 -0
- package/docs/DEFI.md +65 -0
- package/docs/GATEWAY.md +45 -0
- package/docs/MARKET.md +109 -0
- package/docs/PAYMENTS.md +83 -0
- package/docs/TRADE.md +103 -0
- package/package.json +25 -0
- package/src/adapters/onchainos-ws.ts +126 -0
- package/src/adapters/onchainos.ts +488 -0
- package/src/adapters/shared.ts +100 -0
- package/src/http.ts +132 -0
- package/src/index.ts +57 -0
- package/src/tools/balance.ts +67 -0
- package/src/tools/defi.ts +224 -0
- package/src/tools/gateway.ts +115 -0
- package/src/tools/intent.ts +106 -0
- package/src/tools/market.ts +543 -0
- package/src/tools/payments.ts +105 -0
- package/src/tools/skills.ts +489 -0
- package/src/tools/trade.ts +197 -0
- package/src/tools/txhistory.ts +51 -0
- package/src/tools/ws.ts +72 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Onchain OS REST API 适配层
|
|
3
|
+
* Base: https://web3.okx.com
|
|
4
|
+
* 按官方文档逐端点对接,杜绝幻觉
|
|
5
|
+
*/
|
|
6
|
+
import crypto from "node:crypto";
|
|
7
|
+
const BASE = "https://web3.okx.com";
|
|
8
|
+
function ts() { return new Date().toISOString().replace(/(\.\d{3})\d*Z/, "$1Z"); }
|
|
9
|
+
function buildQuery(p) {
|
|
10
|
+
const s = new URLSearchParams();
|
|
11
|
+
for (const [k, v] of Object.entries(p))
|
|
12
|
+
if (v !== undefined && v !== "")
|
|
13
|
+
s.append(k, String(v));
|
|
14
|
+
return s.size ? "?" + s.toString() : "";
|
|
15
|
+
}
|
|
16
|
+
async function request(method, path, opts = {}) {
|
|
17
|
+
const query = opts.params ? buildQuery(opts.params) : "";
|
|
18
|
+
const fullPath = path + query;
|
|
19
|
+
const bodyStr = opts.body ? JSON.stringify(opts.body) : "";
|
|
20
|
+
const headers = { "Content-Type": "application/json", "Accept": "application/json" };
|
|
21
|
+
if (opts.auth) {
|
|
22
|
+
const t = ts();
|
|
23
|
+
const sign = crypto.createHmac("sha256", opts.auth.secret).update(t + method + fullPath + bodyStr).digest("base64");
|
|
24
|
+
headers["OK-ACCESS-KEY"] = opts.auth.apiKey;
|
|
25
|
+
headers["OK-ACCESS-SIGN"] = sign;
|
|
26
|
+
headers["OK-ACCESS-TIMESTAMP"] = t;
|
|
27
|
+
headers["OK-ACCESS-PASSPHRASE"] = opts.auth.passphrase;
|
|
28
|
+
}
|
|
29
|
+
const res = await fetch(BASE + fullPath, { method, headers, ...(bodyStr ? { body: bodyStr } : {}) });
|
|
30
|
+
if (!res.ok) {
|
|
31
|
+
const t = await res.text().catch(() => "");
|
|
32
|
+
throw new Error(`HTTP ${res.status}: ${t}`);
|
|
33
|
+
}
|
|
34
|
+
const json = await res.json();
|
|
35
|
+
if (json.code && json.code !== "0")
|
|
36
|
+
throw new Error(`OKX ${json.code}: ${json.msg ?? ""}`);
|
|
37
|
+
return (json.data ?? json);
|
|
38
|
+
}
|
|
39
|
+
// ═══════════════════════════════════════════════════════════════
|
|
40
|
+
// 按官方文档逐模块添加
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════
|
|
42
|
+
// ── Balance API v6 ─────────────────────────────────────────────
|
|
43
|
+
export const balanceApi = {
|
|
44
|
+
/** GET /api/v6/dex/balance/supported/chain — 返回 name/logoUrl/shortName/chainIndex */
|
|
45
|
+
supportedChain: (auth) => request("GET", "/api/v6/dex/balance/supported/chain", { auth }),
|
|
46
|
+
/** GET /api/v6/dex/balance/total-value-by-address?address=&chains=&assetType=&excludeRiskToken= */
|
|
47
|
+
totalValue: (auth, address, chains, assetType, excludeRiskToken) => request("GET", "/api/v6/dex/balance/total-value-by-address", {
|
|
48
|
+
params: { address, chains, assetType, excludeRiskToken },
|
|
49
|
+
auth,
|
|
50
|
+
}),
|
|
51
|
+
/** GET /api/v6/dex/balance/all-token-balances-by-address?address=&chains=&excludeRiskToken= */
|
|
52
|
+
allTokenBalances: (auth, address, chains, excludeRiskToken) => request("GET", "/api/v6/dex/balance/all-token-balances-by-address", {
|
|
53
|
+
params: { address, chains, excludeRiskToken },
|
|
54
|
+
auth,
|
|
55
|
+
}),
|
|
56
|
+
/** POST /api/v6/dex/balance/token-balances-by-address — body: { address, tokenContractAddresses: [{chainIndex, tokenContractAddress}], excludeRiskToken? } */
|
|
57
|
+
specificTokenBalance: (auth, address, tokenContractAddresses, excludeRiskToken) => request("POST", "/api/v6/dex/balance/token-balances-by-address", {
|
|
58
|
+
body: { address, tokenContractAddresses, ...(excludeRiskToken !== undefined ? { excludeRiskToken } : {}) },
|
|
59
|
+
auth,
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
// ── Gateway API v6 (交易上链) ──────────────────────────────────
|
|
63
|
+
export const gatewayApi = {
|
|
64
|
+
/** GET /api/v6/dex/pre-transaction/supported/chain */
|
|
65
|
+
supportedChain: (auth) => request("GET", "/api/v6/dex/pre-transaction/supported/chain", { auth }),
|
|
66
|
+
/** GET /api/v6/dex/pre-transaction/gas-price?chainIndex= */
|
|
67
|
+
gasPrice: (auth, chainIndex) => request("GET", "/api/v6/dex/pre-transaction/gas-price", { params: { chainIndex }, auth }),
|
|
68
|
+
/** POST /api/v6/dex/pre-transaction/gas-limit */
|
|
69
|
+
gasLimit: (auth, body) => request("POST", "/api/v6/dex/pre-transaction/gas-limit", { body, auth }),
|
|
70
|
+
/** POST /api/v6/dex/pre-transaction/simulate — 模拟执行, 返回 intention/assetChange/gasUsed/failReason/risks */
|
|
71
|
+
simulate: (auth, body) => request("POST", "/api/v6/dex/pre-transaction/simulate", { body, auth }),
|
|
72
|
+
/** POST /api/v6/dex/pre-transaction/broadcast-transaction — 返回 orderId/txHash */
|
|
73
|
+
broadcast: (auth, body) => request("POST", "/api/v6/dex/pre-transaction/broadcast-transaction", { body, auth }),
|
|
74
|
+
};
|
|
75
|
+
// ── Post-transaction API v6 ───────────────────────────────────
|
|
76
|
+
export const postTxApi = {
|
|
77
|
+
/** GET /api/v6/dex/post-transaction/orders — 广播订单列表 */
|
|
78
|
+
orders: (auth, address, chainIndex, txStatus, orderId, cursor, limit) => request("GET", "/api/v6/dex/post-transaction/orders", {
|
|
79
|
+
params: { address, chainIndex, txStatus, orderId, cursor, limit },
|
|
80
|
+
auth,
|
|
81
|
+
}),
|
|
82
|
+
/** GET /api/v6/dex/post-transaction/supported/chain — 交易历史支持的链 */
|
|
83
|
+
supportedChain: (auth) => request("GET", "/api/v6/dex/post-transaction/supported/chain", { auth }),
|
|
84
|
+
/** GET /api/v6/dex/post-transaction/transactions-by-address?address=&chains=&tokenContractAddress=&begin=&end=&cursor=&limit= */
|
|
85
|
+
transactions: (auth, address, chains, tokenContractAddress, begin, end, cursor, limit) => request("GET", "/api/v6/dex/post-transaction/transactions-by-address", {
|
|
86
|
+
params: { address, chains, tokenContractAddress, begin, end, cursor, limit },
|
|
87
|
+
auth,
|
|
88
|
+
}),
|
|
89
|
+
/** GET /api/v6/dex/post-transaction/transaction-detail-by-txhash?chainIndex=&txHash=&itype= */
|
|
90
|
+
transactionDetail: (auth, chainIndex, txHash, itype) => request("GET", "/api/v6/dex/post-transaction/transaction-detail-by-txhash", {
|
|
91
|
+
params: { chainIndex, txHash, itype },
|
|
92
|
+
auth,
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
// ── DeFi API v6 ──────────────────────────────────────────────
|
|
96
|
+
export const defiApi = {
|
|
97
|
+
/** GET /api/v6/defi/product/supported-chains — 返回 chainIndex(String)+network */
|
|
98
|
+
supportedChains: (auth) => request("GET", "/api/v6/defi/product/supported-chains", { auth }),
|
|
99
|
+
/** GET /api/v6/defi/product/supported-platforms — 返回 analysisPlatformId+platformName+investmentCount */
|
|
100
|
+
supportedPlatforms: (auth) => request("GET", "/api/v6/defi/product/supported-platforms", { auth }),
|
|
101
|
+
/** POST /api/v6/defi/product/search — body: { tokenKeywordList, platformKeywordList?, pageNum?, chainIndex?, productGroup? } */
|
|
102
|
+
searchProducts: (auth, body) => request("POST", "/api/v6/defi/product/search", { body, auth }),
|
|
103
|
+
/** GET /api/v6/defi/product/detail?investmentId= */
|
|
104
|
+
productDetail: (auth, investmentId) => request("GET", "/api/v6/defi/product/detail", { params: { investmentId }, auth }),
|
|
105
|
+
/** GET /api/v6/defi/product/rate/chart?investmentId=&timeRange= — APY 折线图 */
|
|
106
|
+
rateChart: (auth, investmentId, timeRange) => request("GET", "/api/v6/defi/product/rate/chart", { params: { investmentId, timeRange }, auth }),
|
|
107
|
+
/** GET /api/v6/defi/product/tvl/chart?investmentId=&timeRange= — TVL 折线图 */
|
|
108
|
+
tvlChart: (auth, investmentId, timeRange) => request("GET", "/api/v6/defi/product/tvl/chart", { params: { investmentId, timeRange }, auth }),
|
|
109
|
+
/** GET /api/v6/defi/product/depth-price/chart?investmentId=&chartType=&timeRange= — V3 深度/价格图 */
|
|
110
|
+
depthPriceChart: (auth, investmentId, chartType, timeRange) => request("GET", "/api/v6/defi/product/depth-price/chart", { params: { investmentId, chartType, timeRange }, auth }),
|
|
111
|
+
/** POST /api/v6/defi/product/detail/prepare — 交易前准备, 返回 investWithTokenList/receiveTokenInfo */
|
|
112
|
+
prepareTransaction: (auth, investmentId) => request("POST", "/api/v6/defi/product/detail/prepare", { body: { investmentId }, auth }),
|
|
113
|
+
/** POST /api/v6/defi/calculator/enter/info — V3 Pool 双币分配计算 */
|
|
114
|
+
calcEnterInfo: (auth, body) => request("POST", "/api/v6/defi/calculator/enter/info", { body, auth }),
|
|
115
|
+
/** POST /api/v6/defi/transaction/enter — 申购/存款/借款, 返回 dataList(APPROVE→DEPOSIT) */
|
|
116
|
+
enter: (auth, body) => request("POST", "/api/v6/defi/transaction/enter", { body, auth }),
|
|
117
|
+
/** POST /api/v6/defi/transaction/exit — 赎回/还款, 返回 dataList */
|
|
118
|
+
exit: (auth, body) => request("POST", "/api/v6/defi/transaction/exit", { body, auth }),
|
|
119
|
+
/** POST /api/v6/defi/transaction/claim — 领取奖励, 返回 dataList */
|
|
120
|
+
claim: (auth, body) => request("POST", "/api/v6/defi/transaction/claim", { body, auth }),
|
|
121
|
+
/** POST /api/v6/defi/user/asset/platform/list — 用户持仓概览(协议维度) */
|
|
122
|
+
userPlatformList: (auth, body) => request("POST", "/api/v6/defi/user/asset/platform/list", { body, auth }),
|
|
123
|
+
/** POST /api/v6/defi/user/asset/platform/detail — 用户持仓明细(投资品维度) */
|
|
124
|
+
userPlatformDetail: (auth, body) => request("POST", "/api/v6/defi/user/asset/platform/detail", { body, auth }),
|
|
125
|
+
};
|
|
126
|
+
// ── Payments API (Agent Payments Protocol) ──────────────────
|
|
127
|
+
// 路径来源: API 参考 (HTTP端 + Agent端)
|
|
128
|
+
export const paymentsApi = {
|
|
129
|
+
// ── Agent 端 A2A (单次支付) ────────────────────────────
|
|
130
|
+
/** POST /api/v6/pay/a2a/payment/create — Seller 创建付款, 返回 paymentId+challenge+付款链接 */
|
|
131
|
+
create: (auth, body) => request("POST", "/api/v6/pay/a2a/payment/create", { body, auth }),
|
|
132
|
+
/** GET /api/v6/pay/a2a/p/{paymentId} — Buyer 拉取付款详情, PUBLIC */
|
|
133
|
+
detail: (paymentId) => request("GET", `/api/v6/pay/a2a/p/${paymentId}`),
|
|
134
|
+
/** POST /api/v6/pay/a2a/p/{paymentId}/credential — Buyer 提交 EIP-3009 签名凭证, PUBLIC */
|
|
135
|
+
submit: (paymentId, body) => request("POST", `/api/v6/pay/a2a/p/${paymentId}/credential`, { body }),
|
|
136
|
+
/** GET /api/v6/pay/a2a/p/{paymentId}/status — 查询支付状态, PUBLIC */
|
|
137
|
+
status: (paymentId) => request("GET", `/api/v6/pay/a2a/p/${paymentId}/status`),
|
|
138
|
+
// ── HTTP 端 x402 (单次/批量) ──────────────────────────
|
|
139
|
+
/** GET /api/v6/pay/x402/supported — 支持的网络/代币/scheme */
|
|
140
|
+
supported: (auth) => request("GET", "/api/v6/pay/x402/supported", { auth }),
|
|
141
|
+
/** POST /api/v6/pay/x402/verify — 验签 */
|
|
142
|
+
verify: (auth, body) => request("POST", "/api/v6/pay/x402/verify", { body, auth }),
|
|
143
|
+
/** POST /api/v6/pay/x402/settle — 上链结算 */
|
|
144
|
+
settle: (auth, body) => request("POST", "/api/v6/pay/x402/settle", { body, auth }),
|
|
145
|
+
/** GET /api/v6/pay/x402/settle/status?txHash= — 轮询结算状态 */
|
|
146
|
+
settleStatus: (auth, txHash) => request("GET", "/api/v6/pay/x402/settle/status", { params: { txHash }, auth }),
|
|
147
|
+
};
|
|
148
|
+
// ── Trade / DEX API v6 (经典兑换) ────────────────────────────
|
|
149
|
+
// 来源: Solana/EVM/Sui/Ton 搭建指南 + API 参考
|
|
150
|
+
export const tradeApi = {
|
|
151
|
+
/** GET /api/v6/dex/aggregator/supported/chain?chainIndex= — 返回 chainIndex/chainName/dexTokenApproveAddress */
|
|
152
|
+
supportedChain: (auth, chainIndex) => request("GET", "/api/v6/dex/aggregator/supported/chain", { params: { chainIndex }, auth }),
|
|
153
|
+
allTokens: (auth, chainIndex) => request("GET", "/api/v6/dex/aggregator/all-tokens", { params: { chainIndex }, auth }),
|
|
154
|
+
liquidity: (auth, chainIndex) => request("GET", "/api/v6/dex/aggregator/get-liquidity", { params: { chainIndex }, auth }),
|
|
155
|
+
approveTransaction: (auth, chainIndex, tokenContractAddress, approveAmount) => request("GET", "/api/v6/dex/aggregator/approve-transaction", { params: { chainIndex, tokenContractAddress, approveAmount }, auth }),
|
|
156
|
+
quote: (auth, params) => request("GET", "/api/v6/dex/aggregator/quote", { params, auth }),
|
|
157
|
+
swap: (auth, params) => request("GET", "/api/v6/dex/aggregator/swap", { params, auth }),
|
|
158
|
+
swapInstruction: (auth, params) => request("GET", "/api/v6/dex/aggregator/swap-instruction", { params, auth }),
|
|
159
|
+
swapHistory: (auth, chainIndex, txHash, isFromMyProject) => request("GET", "/api/v6/dex/aggregator/history", { params: { chainIndex, txHash, isFromMyProject }, auth }),
|
|
160
|
+
};
|
|
161
|
+
// ── Intent Swap API v6 ──────────────────────────────────────
|
|
162
|
+
export const intentApi = {
|
|
163
|
+
/** POST /api/v6/dex/aggregator/intent/create-order — 创建意图订单, 返回 orderUid */
|
|
164
|
+
createOrder: (auth, body) => request("POST", "/api/v6/dex/aggregator/intent/create-order", { body, auth }),
|
|
165
|
+
/** GET /api/v6/dex/aggregator/intent/order-list?userWalletAddress=&orderUid=&cursor=&limit= — 订单列表 */
|
|
166
|
+
orderList: (auth, params) => request("GET", "/api/v6/dex/aggregator/intent/order-list", { params, auth }),
|
|
167
|
+
/** GET /api/v6/dex/aggregator/intent/order-status?orderUid= — 查订单状态 */
|
|
168
|
+
orderStatus: (auth, orderUid) => request("GET", "/api/v6/dex/aggregator/intent/order-status", { params: { orderUid }, auth }),
|
|
169
|
+
/** POST /api/v6/dex/aggregator/intent/cancel-signdata — 获取取消签名数据 */
|
|
170
|
+
cancelSignData: (auth, userWalletAddress, orderUid) => request("POST", "/api/v6/dex/aggregator/intent/cancel-signdata", { body: { userWalletAddress, orderUid }, auth }),
|
|
171
|
+
/** POST /api/v6/dex/aggregator/intent/cancel-order — 取消订单 */
|
|
172
|
+
cancelOrder: (auth, userWalletAddress, orderUid, signature) => request("POST", "/api/v6/dex/aggregator/intent/cancel-order", { body: { userWalletAddress, orderUid, signature }, auth }),
|
|
173
|
+
/** GET /api/v6/dex/aggregator/intent/auction-info?auctionId=&txHash= — 查拍卖结果 */
|
|
174
|
+
auctionInfo: (auth, params) => request("GET", "/api/v6/dex/aggregator/intent/auction-info", { params, auth }),
|
|
175
|
+
};
|
|
176
|
+
// ── Market API v6 (行情 — x402 付费) ──────────────────────
|
|
177
|
+
export const marketApi = {
|
|
178
|
+
/** GET /api/v6/dex/market/supported/chain?chainIndex= */
|
|
179
|
+
supportedChain: (auth, chainIndex) => request("GET", "/api/v6/dex/market/supported/chain", { params: { chainIndex }, auth }),
|
|
180
|
+
/** POST /api/v6/dex/market/price — body: [{chainIndex, tokenContractAddress}] */
|
|
181
|
+
price: (auth, body) => request("POST", "/api/v6/dex/market/price", { body, auth }),
|
|
182
|
+
/** GET /api/v6/dex/market/candles?chainIndex=&tokenContractAddress=&bar=&after=&before=&limit= */
|
|
183
|
+
candles: (auth, params) => request("GET", "/api/v6/dex/market/candles", { params, auth }),
|
|
184
|
+
/** GET /api/v6/dex/market/historical-candles?chainIndex=&tokenContractAddress=&bar=&after=&before=&limit= */
|
|
185
|
+
historicalCandles: (auth, params) => request("GET", "/api/v6/dex/market/historical-candles", { params, auth }),
|
|
186
|
+
// ── 综合币价 ──────────────────────────────────────
|
|
187
|
+
/** POST /api/v6/dex/index/current-price — body: [{chainIndex, tokenContractAddress}] */
|
|
188
|
+
indexCurrentPrice: (auth, body) => request("POST", "/api/v6/dex/index/current-price", { body, auth }),
|
|
189
|
+
/** GET /api/v6/dex/index/historical-price?chainIndex=&tokenContractAddress=&limit=&cursor=&begin=&end=&period= */
|
|
190
|
+
indexHistoricalPrice: (auth, params) => request("GET", "/api/v6/dex/index/historical-price", { params, auth }),
|
|
191
|
+
// ── 代币 API ──────────────────────────────────────
|
|
192
|
+
/** GET /api/v6/dex/market/token/search?chains=&search=&cursor=&limit= — 搜索代币 */
|
|
193
|
+
searchToken: (auth, chains, search, cursor, limit) => request("GET", "/api/v6/dex/market/token/search", { params: { chains, search, cursor, limit }, auth }),
|
|
194
|
+
/** POST /api/v6/dex/market/token/basic-info — body: [{chainIndex, tokenContractAddress}] */
|
|
195
|
+
tokenBasicInfo: (auth, body) => request("POST", "/api/v6/dex/market/token/basic-info", { body, auth }),
|
|
196
|
+
/** GET /api/v6/dex/market/token/top-liquidity?chainIndex=&tokenContractAddress= — 流动性池 */
|
|
197
|
+
tokenTopLiquidity: (auth, chainIndex, tokenContractAddress) => request("GET", "/api/v6/dex/market/token/top-liquidity", { params: { chainIndex, tokenContractAddress }, auth }),
|
|
198
|
+
/** POST /api/v6/dex/market/price-info — 批量交易信息, body: [{chainIndex, tokenContractAddress}], max 100 */
|
|
199
|
+
priceInfo: (auth, body) => request("POST", "/api/v6/dex/market/price-info", { body, auth }),
|
|
200
|
+
/** GET /api/v6/dex/market/token/advanced-info?chainIndex=&tokenContractAddress= — 代币安全分析(貔貅/开发者/狙击手) */
|
|
201
|
+
tokenAdvancedInfo: (auth, chainIndex, tokenContractAddress) => request("GET", "/api/v6/dex/market/token/advanced-info", { params: { chainIndex, tokenContractAddress }, auth }),
|
|
202
|
+
/** GET /api/v6/dex/market/token/hot-token?rankingType=&chainIndex=&rankBy=... — 热门代币(最多100) */
|
|
203
|
+
tokenHot: (auth, params) => request("GET", "/api/v6/dex/market/token/hot-token", { params, auth }),
|
|
204
|
+
/** GET /api/v6/dex/market/token/holder?chainIndex=&tokenContractAddress=&tagFilter=&cursor=&limit= — 前100持有人 */
|
|
205
|
+
tokenHolder: (auth, params) => request("GET", "/api/v6/dex/market/token/holder", { params, auth }),
|
|
206
|
+
/** GET /api/v6/dex/market/token/cluster/supported/chain — 聚类支持链 */
|
|
207
|
+
tokenClusterSupportedChain: (auth) => request("GET", "/api/v6/dex/market/token/cluster/supported/chain", { auth }),
|
|
208
|
+
/** GET /api/v6/dex/market/token/cluster/overview?chainIndex=&tokenContractAddress= — 持仓集中度 */
|
|
209
|
+
tokenClusterOverview: (auth, chainIndex, tokenContractAddress) => request("GET", "/api/v6/dex/market/token/cluster/overview", { params: { chainIndex, tokenContractAddress }, auth }),
|
|
210
|
+
/** GET /api/v6/dex/market/token/cluster/list?chainIndex=&tokenContractAddress= — 聚类列表(top100集群) */
|
|
211
|
+
tokenClusterList: (auth, chainIndex, tokenContractAddress) => request("GET", "/api/v6/dex/market/token/cluster/list", { params: { chainIndex, tokenContractAddress }, auth }),
|
|
212
|
+
/** GET /api/v6/dex/market/token/cluster/top-holders?chainIndex=&tokenContractAddress=&rangeFilter= — 前10/50/100持仓 */
|
|
213
|
+
tokenClusterTopHolders: (auth, chainIndex, tokenContractAddress, rangeFilter) => request("GET", "/api/v6/dex/market/token/cluster/top-holders", { params: { chainIndex, tokenContractAddress, rangeFilter }, auth }),
|
|
214
|
+
/** GET /api/v6/dex/market/token/top-trader?chainIndex=&tokenContractAddress=&tagFilter=&cursor=&limit= — 前100盈利地址 */
|
|
215
|
+
tokenTopTrader: (auth, params) => request("GET", "/api/v6/dex/market/token/top-trader", { params, auth }),
|
|
216
|
+
// ── 信号 API ──────────────────────────────────────
|
|
217
|
+
/** GET /api/v6/dex/market/signal/supported/chain — 信号支持链 */
|
|
218
|
+
signalSupportedChain: (auth) => request("GET", "/api/v6/dex/market/signal/supported/chain", { auth }),
|
|
219
|
+
/** POST /api/v6/dex/market/signal/list — 获取信号 */
|
|
220
|
+
signalList: (auth, body) => request("POST", "/api/v6/dex/market/signal/list", { body, auth }),
|
|
221
|
+
/** GET /api/v6/dex/market/leaderboard/supported/chain — 聪明钱榜单支持链 */
|
|
222
|
+
leaderboardSupportedChain: (auth) => request("GET", "/api/v6/dex/market/leaderboard/supported/chain", { auth }),
|
|
223
|
+
/** GET /api/v6/dex/market/leaderboard/list?chainIndex=&timeFrame=&sortBy=&walletType=... — 聪明钱榜单 */
|
|
224
|
+
leaderboardList: (auth, params) => request("GET", "/api/v6/dex/market/leaderboard/list", { params, auth }),
|
|
225
|
+
// ── Memepump / 扫链 ──────────────────────────────────
|
|
226
|
+
/** GET /api/v6/dex/market/memepump/supported/chainsProtocol — 支持的链+协议 */
|
|
227
|
+
memepumpSupported: (auth) => request("GET", "/api/v6/dex/market/memepump/supported/chainsProtocol", { auth }),
|
|
228
|
+
/** GET /api/v6/dex/market/memepump/tokenList?chainIndex=&stage=&... — 代币列表(最多30) */
|
|
229
|
+
memepumpTokenList: (auth, params) => request("GET", "/api/v6/dex/market/memepump/tokenList", { params, auth }),
|
|
230
|
+
/** GET /api/v6/dex/market/memepump/tokenDetails?chainIndex=&tokenContractAddress=&walletAddress= */
|
|
231
|
+
memepumpTokenDetails: (auth, chainIndex, tokenContractAddress, walletAddress) => request("GET", "/api/v6/dex/market/memepump/tokenDetails", { params: { chainIndex, tokenContractAddress, walletAddress }, auth }),
|
|
232
|
+
/** GET /api/v6/dex/market/memepump/tokenDevInfo?chainIndex=&tokenContractAddress= */
|
|
233
|
+
memepumpTokenDevInfo: (auth, chainIndex, tokenContractAddress) => request("GET", "/api/v6/dex/market/memepump/tokenDevInfo", { params: { chainIndex, tokenContractAddress }, auth }),
|
|
234
|
+
/** GET /api/v6/dex/market/memepump/similarToken?chainIndex=&tokenContractAddress= */
|
|
235
|
+
memepumpSimilarToken: (auth, chainIndex, tokenContractAddress) => request("GET", "/api/v6/dex/market/memepump/similarToken", { params: { chainIndex, tokenContractAddress }, auth }),
|
|
236
|
+
/** GET /api/v6/dex/market/memepump/tokenBundleInfo?chainIndex=&tokenContractAddress= */
|
|
237
|
+
memepumpBundleInfo: (auth, chainIndex, tokenContractAddress) => request("GET", "/api/v6/dex/market/memepump/tokenBundleInfo", { params: { chainIndex, tokenContractAddress }, auth }),
|
|
238
|
+
/** GET /api/v6/dex/market/memepump/apedWallet?chainIndex=&tokenContractAddress=&walletAddress= */
|
|
239
|
+
memepumpApedWallet: (auth, chainIndex, tokenContractAddress, walletAddress) => request("GET", "/api/v6/dex/market/memepump/apedWallet", { params: { chainIndex, tokenContractAddress, walletAddress }, auth }),
|
|
240
|
+
// ── Portfolio / 地址分析 ─────────────────────────────
|
|
241
|
+
/** GET /api/v6/dex/market/portfolio/supported/chain */
|
|
242
|
+
portfolioSupportedChain: (auth) => request("GET", "/api/v6/dex/market/portfolio/supported/chain", { auth }),
|
|
243
|
+
/** GET /api/v6/dex/market/portfolio/overview?chainIndex=&walletAddress=&timeFrame= */
|
|
244
|
+
portfolioOverview: (auth, chainIndex, walletAddress, timeFrame) => request("GET", "/api/v6/dex/market/portfolio/overview", { params: { chainIndex, walletAddress, timeFrame }, auth }),
|
|
245
|
+
/** GET /api/v6/dex/market/portfolio/recent-pnl?chainIndex=&walletAddress=&cursor=&limit= */
|
|
246
|
+
portfolioRecentPnl: (auth, params) => request("GET", "/api/v6/dex/market/portfolio/recent-pnl", { params, auth }),
|
|
247
|
+
/** GET /api/v6/dex/market/portfolio/token/latest-pnl?chainIndex=&walletAddress=&tokenContractAddress= */
|
|
248
|
+
portfolioTokenLatestPnl: (auth, chainIndex, walletAddress, tokenContractAddress) => request("GET", "/api/v6/dex/market/portfolio/token/latest-pnl", { params: { chainIndex, walletAddress, tokenContractAddress }, auth }),
|
|
249
|
+
/** GET /api/v6/dex/market/portfolio/dex-history?chainIndex=&walletAddress=&begin=&end=&... */
|
|
250
|
+
portfolioDexHistory: (auth, params) => request("GET", "/api/v6/dex/market/portfolio/dex-history", { params, auth }),
|
|
251
|
+
/** GET /api/v6/dex/market/address-tracker/trades?trackerType=&walletAddress=&tradeType=... */
|
|
252
|
+
addressTrackerTrades: (auth, params) => request("GET", "/api/v6/dex/market/address-tracker/trades", { params, auth }),
|
|
253
|
+
// ── Social / 社媒 ──────────────────────────────────
|
|
254
|
+
/** GET /api/v6/dex/market/social/news/latest */
|
|
255
|
+
socialNewsLatest: (auth, params) => request("GET", "/api/v6/dex/market/social/news/latest", { params, auth }),
|
|
256
|
+
/** GET /api/v6/dex/market/social/news/by-symbol */
|
|
257
|
+
socialNewsBySymbol: (auth, params) => request("GET", "/api/v6/dex/market/social/news/by-symbol", { params, auth }),
|
|
258
|
+
/** GET /api/v6/dex/market/social/news/search */
|
|
259
|
+
socialNewsSearch: (auth, params) => request("GET", "/api/v6/dex/market/social/news/search", { params, auth }),
|
|
260
|
+
/** GET /api/v6/dex/market/social/news/detail?articleId= */
|
|
261
|
+
socialNewsDetail: (auth, articleId, language) => request("GET", "/api/v6/dex/market/social/news/detail", { params: { articleId, language }, auth }),
|
|
262
|
+
/** GET /api/v6/dex/market/social/news/platforms */
|
|
263
|
+
socialNewsPlatforms: (auth) => request("GET", "/api/v6/dex/market/social/news/platforms", { auth }),
|
|
264
|
+
/** GET /api/v6/dex/market/social/sentiment/symbol?tokenSymbols=&timeFrame=&trendPoints= */
|
|
265
|
+
socialSentimentSymbol: (auth, params) => request("GET", "/api/v6/dex/market/social/sentiment/symbol", { params, auth }),
|
|
266
|
+
/** GET /api/v6/dex/market/social/sentiment/ranking?timeFrame=&sortBy=&limit= */
|
|
267
|
+
socialSentimentRanking: (auth, params) => request("GET", "/api/v6/dex/market/social/sentiment/ranking", { params, auth }),
|
|
268
|
+
/** GET /api/v6/dex/market/social/vibe/timeline?chainIndex=&tokenAddress=&timeFrame= */
|
|
269
|
+
socialVibeTimeline: (auth, params) => request("GET", "/api/v6/dex/market/social/vibe/timeline", { params, auth }),
|
|
270
|
+
/** GET /api/v6/dex/market/social/vibe/top-kols?chainIndex=&tokenAddress=&sortBy=&timeFrame=&limit= */
|
|
271
|
+
socialVibeTopKols: (auth, params) => request("GET", "/api/v6/dex/market/social/vibe/top-kols", { params, auth }),
|
|
272
|
+
/** GET /api/v6/dex/market/trades?chainIndex=&tokenContractAddress=&after=&limit=&tagFilter=&walletAddressFilter= */
|
|
273
|
+
trades: (auth, params) => request("GET", "/api/v6/dex/market/trades", { params, auth }),
|
|
274
|
+
};
|
|
275
|
+
//# sourceMappingURL=onchainos.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* h-mcp — 共享工具层
|
|
3
|
+
* 规范: OnchainOS-API对接规范.md §四/§六 + AGENT-MCP-RULES.md §4/§8
|
|
4
|
+
*/
|
|
5
|
+
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
export interface Auth {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
secret: string;
|
|
9
|
+
passphrase: string;
|
|
10
|
+
}
|
|
11
|
+
export interface NextStep {
|
|
12
|
+
action: string;
|
|
13
|
+
tool: string;
|
|
14
|
+
params?: Record<string, unknown>;
|
|
15
|
+
condition?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function toResult<T>(data: T, opts?: {
|
|
18
|
+
warnings?: string[];
|
|
19
|
+
nextSteps?: NextStep[];
|
|
20
|
+
}): CallToolResult;
|
|
21
|
+
export declare function toError(e: unknown): CallToolResult;
|
|
22
|
+
export declare function AUTH_REQUIRED(scope?: "READ" | "TRADE"): CallToolResult;
|
|
23
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// ── toResult ────────────────────────────────────────────────
|
|
2
|
+
export function toResult(data, opts) {
|
|
3
|
+
const body = { success: true, data, tsIso: new Date().toISOString() };
|
|
4
|
+
if (opts?.warnings?.length)
|
|
5
|
+
body.warnings = opts.warnings;
|
|
6
|
+
if (opts?.nextSteps?.length)
|
|
7
|
+
body.nextSteps = opts.nextSteps;
|
|
8
|
+
return { content: [{ type: "text", text: JSON.stringify(body) }] };
|
|
9
|
+
}
|
|
10
|
+
// ── toError ─────────────────────────────────────────────────
|
|
11
|
+
export function toError(e) {
|
|
12
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
13
|
+
let error;
|
|
14
|
+
// OKX 业务错误码 (HTTP 200, code != "0")
|
|
15
|
+
if (msg.includes("OKX 50001")) {
|
|
16
|
+
error = { code: "SERVICE_UNAVAILABLE", message: msg, fix: "服务暂不可用, 等待几秒后重试", retryAfter: 3000 };
|
|
17
|
+
}
|
|
18
|
+
else if (msg.includes("OKX 50011")) {
|
|
19
|
+
error = { code: "RATE_LIMITED", message: msg, fix: "超出频率限制, 请降低请求频率", retryAfter: 5000 };
|
|
20
|
+
}
|
|
21
|
+
else if (msg.includes("OKX 50103") || msg.includes("OKX 50104") || msg.includes("OKX 50105") || msg.includes("OKX 50106") || msg.includes("OKX 50107") || msg.includes("OKX 50111") || msg.includes("OKX 50112") || msg.includes("OKX 50113")) {
|
|
22
|
+
error = { code: "AUTH_ERROR", message: msg, fix: "API Key 签名错误, 请检查 OKX_API_KEY/OKX_SECRET_KEY/OKX_PASSPHRASE" };
|
|
23
|
+
}
|
|
24
|
+
else if (msg.includes("OKX 81001")) {
|
|
25
|
+
error = { code: "BAD_PARAMETER", message: msg, fix: "参数不正确, 请检查参数名/类型/必填项" };
|
|
26
|
+
}
|
|
27
|
+
else if (msg.includes("OKX 81108")) {
|
|
28
|
+
error = { code: "WALLET_TYPE_MISMATCH", message: msg, fix: "钱包类型不匹配, 请确认链和地址格式一致" };
|
|
29
|
+
}
|
|
30
|
+
else if (msg.includes("OKX 81104")) {
|
|
31
|
+
error = { code: "CHAIN_NOT_SUPPORT", message: msg, fix: "该链不支持此操作, 请调用 supported_chain 确认可用链" };
|
|
32
|
+
}
|
|
33
|
+
else if (msg.includes("OKX 81152")) {
|
|
34
|
+
error = { code: "COIN_NOT_EXIST", message: msg, fix: "代币不存在, 请用 onchainos_token_search 搜索正确地址" };
|
|
35
|
+
}
|
|
36
|
+
else if (msg.includes("OKX 81451")) {
|
|
37
|
+
error = { code: "NODE_FAILED", message: msg, fix: "节点返回失败, 请稍后重试", retryAfter: 2000 };
|
|
38
|
+
}
|
|
39
|
+
else if (msg.includes("OKX 84001") || msg.includes("OKX 84003")) {
|
|
40
|
+
error = { code: "PROTOCOL_NOT_SUPPORT", message: msg, fix: "协议不支持, 请用 onchainos_defi_supported_platforms 确认支持列表" };
|
|
41
|
+
}
|
|
42
|
+
else if (msg.includes("OKX 84007") || msg.includes("OKX 84024")) {
|
|
43
|
+
error = { code: "PRODUCT_NOT_SUPPORT", message: msg, fix: "投资品不支持或不存在, 请用 onchainos_defi_search_products 重新搜索" };
|
|
44
|
+
}
|
|
45
|
+
else if (msg.includes("OKX 84010")) {
|
|
46
|
+
error = { code: "TOKEN_NOT_SUPPORT", message: msg, fix: "代币不支持, 请确认 tokenAddress 正确" };
|
|
47
|
+
}
|
|
48
|
+
else if (msg.includes("OKX 84014")) {
|
|
49
|
+
error = { code: "BALANCE_FAILED", message: msg, fix: "余额验证失败, 请确认钱包余额充足" };
|
|
50
|
+
}
|
|
51
|
+
else if (msg.includes("OKX 84016")) {
|
|
52
|
+
error = { code: "CONTRACT_FAILED", message: msg, fix: "智能合约执行失败, 请检查参数和链上状态" };
|
|
53
|
+
}
|
|
54
|
+
else if (msg.includes("OKX 84019")) {
|
|
55
|
+
error = { code: "ADDRESS_MISMATCH", message: msg, fix: "地址格式不匹配, 请用 onchainos_wallet_validate_address 校验" };
|
|
56
|
+
}
|
|
57
|
+
else if (msg.includes("OKX 84021")) {
|
|
58
|
+
error = { code: "SYNCING", message: msg, fix: "资产正在同步中, 请等待片刻后重试", retryAfter: 3000 };
|
|
59
|
+
}
|
|
60
|
+
else if (msg.includes("OKX 84025")) {
|
|
61
|
+
error = { code: "NO_REWARD", message: msg, fix: "当前无可领取的奖励" };
|
|
62
|
+
}
|
|
63
|
+
else if (msg.includes("OKX 84029")) {
|
|
64
|
+
error = { code: "LOCKED", message: msg, fix: "本金仍在锁定期, 无法领取" };
|
|
65
|
+
}
|
|
66
|
+
else if (msg.includes("OKX 84030")) {
|
|
67
|
+
error = { code: "EXPIRED", message: msg, fix: "本金领取已过期" };
|
|
68
|
+
}
|
|
69
|
+
else if (msg.includes("OKX 84032")) {
|
|
70
|
+
error = { code: "V3_ONLY", message: msg, fix: "此接口仅适用于 V3 DEX Pool 投资品" };
|
|
71
|
+
}
|
|
72
|
+
// HTTP 传输层错误
|
|
73
|
+
else if (msg.includes("429") || msg.includes("RATE")) {
|
|
74
|
+
error = { code: "RATE_LIMITED", message: msg, fix: "请等待 1-2 秒后重试", retryAfter: 1000 };
|
|
75
|
+
}
|
|
76
|
+
else if (msg.includes("503")) {
|
|
77
|
+
error = { code: "UNAVAILABLE", message: msg, fix: "服务暂时不可用,请等待 5 秒后重试", retryAfter: 5000 };
|
|
78
|
+
}
|
|
79
|
+
else if (msg.includes("400")) {
|
|
80
|
+
error = { code: "BAD_REQUEST", message: msg, fix: "请检查参数格式和必填字段" };
|
|
81
|
+
}
|
|
82
|
+
else if (msg.includes("422")) {
|
|
83
|
+
error = { code: "BUSINESS_REJECT", message: msg, fix: "参数合法但业务不可行,请检查余额/授权等条件" };
|
|
84
|
+
}
|
|
85
|
+
else if (msg.includes("500")) {
|
|
86
|
+
error = { code: "SYSTEM_ERROR", message: msg, fix: "系统错误,不建议重试" };
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
error = { code: "ERROR", message: msg, fix: "请检查网络和 API 配置" };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: JSON.stringify({ success: false, error, tsIso: new Date().toISOString() }) }],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// ── AUTH_REQUIRED ───────────────────────────────────────────
|
|
97
|
+
export function AUTH_REQUIRED(scope = "READ") {
|
|
98
|
+
return {
|
|
99
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
100
|
+
success: false,
|
|
101
|
+
error: { code: "AUTH_REQUIRED", message: `需要 OKX API Key(${scope} 权限)`, fix: "请设置环境变量 OKX_API_KEY / OKX_SECRET_KEY / OKX_PASSPHRASE" },
|
|
102
|
+
tsIso: new Date().toISOString(),
|
|
103
|
+
}) }],
|
|
104
|
+
isError: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=shared.js.map
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* h-mcp Server — HTTP 传输层
|
|
3
|
+
* 规范: AGENT-MCP-RULES.md §12 (传输层透明 + 无状态)
|
|
4
|
+
* 使用 StreamableHTTPServerTransport (SDK 推荐), 废弃的 SSE transport 已跳过
|
|
5
|
+
*
|
|
6
|
+
* 使用原生 node:http 而非 Express, 避免 Express 5 与 @hono/node-server 兼容问题
|
|
7
|
+
*/
|
|
8
|
+
import "dotenv/config";
|
|
9
|
+
//# sourceMappingURL=http.d.ts.map
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* h-mcp Server — HTTP 传输层
|
|
3
|
+
* 规范: AGENT-MCP-RULES.md §12 (传输层透明 + 无状态)
|
|
4
|
+
* 使用 StreamableHTTPServerTransport (SDK 推荐), 废弃的 SSE transport 已跳过
|
|
5
|
+
*
|
|
6
|
+
* 使用原生 node:http 而非 Express, 避免 Express 5 与 @hono/node-server 兼容问题
|
|
7
|
+
*/
|
|
8
|
+
import "dotenv/config";
|
|
9
|
+
import { createServer } from "node:http";
|
|
10
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
12
|
+
import { registerBalanceTools } from "./tools/balance.js";
|
|
13
|
+
import { registerGatewayTools } from "./tools/gateway.js";
|
|
14
|
+
import { registerTxHistoryTools } from "./tools/txhistory.js";
|
|
15
|
+
import { registerDefiTools } from "./tools/defi.js";
|
|
16
|
+
import { registerPaymentsTools } from "./tools/payments.js";
|
|
17
|
+
import { registerTradeTools } from "./tools/trade.js";
|
|
18
|
+
import { registerIntentTools } from "./tools/intent.js";
|
|
19
|
+
import { registerMarketTools } from "./tools/market.js";
|
|
20
|
+
import { registerWsTools } from "./tools/ws.js";
|
|
21
|
+
import { registerSkillTools } from "./tools/skills.js";
|
|
22
|
+
function resolveAuth() {
|
|
23
|
+
const k = process.env.OKX_API_KEY, s = process.env.OKX_SECRET_KEY, p = process.env.OKX_PASSPHRASE;
|
|
24
|
+
if (k && s && p)
|
|
25
|
+
return { apiKey: k, secret: s, passphrase: p };
|
|
26
|
+
const a = process.argv.slice(2), g = (f) => { const i = a.indexOf(f); return i >= 0 ? a[i + 1] : undefined; };
|
|
27
|
+
const ka = g("--okx-api-key") ?? g("-k"), sa = g("--okx-secret") ?? g("-s"), pa = g("--okx-passphrase") ?? g("-p");
|
|
28
|
+
if (ka && sa && pa)
|
|
29
|
+
return { apiKey: ka, secret: sa, passphrase: pa };
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
/** 简陋 body parser — 读 JSON body 到 req.body */
|
|
33
|
+
function jsonBody(req) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const chunks = [];
|
|
36
|
+
req.on("data", c => chunks.push(c));
|
|
37
|
+
req.on("end", () => {
|
|
38
|
+
const raw = Buffer.concat(chunks).toString();
|
|
39
|
+
try {
|
|
40
|
+
resolve(raw ? JSON.parse(raw) : undefined);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
reject(new Error("Invalid JSON body"));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
req.on("error", reject);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async function main() {
|
|
50
|
+
const auth = resolveAuth();
|
|
51
|
+
if (!auth) {
|
|
52
|
+
console.error("[h-mcp] 未配置 API Key。设置 OKX_API_KEY / OKX_SECRET_KEY / OKX_PASSPHRASE");
|
|
53
|
+
console.error("[h-mcp] 获取: https://web3.okx.com/onchainos/dev-portal");
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.error("[h-mcp] Auth 已配置");
|
|
57
|
+
}
|
|
58
|
+
const host = process.env.HOST ?? "127.0.0.1";
|
|
59
|
+
const port = parseInt(process.env.PORT ?? "3000", 10);
|
|
60
|
+
// 1. 创建 HTTP transport (有状态模式, 自动生成 sessionId)
|
|
61
|
+
const transport = new StreamableHTTPServerTransport({
|
|
62
|
+
sessionIdGenerator: () => crypto.randomUUID(),
|
|
63
|
+
});
|
|
64
|
+
// 2. MCP Server + 全部工具注册
|
|
65
|
+
const server = new McpServer({ name: "hchain-mcp", version: "1.0.0" });
|
|
66
|
+
registerBalanceTools(server, auth);
|
|
67
|
+
registerGatewayTools(server, auth);
|
|
68
|
+
registerTxHistoryTools(server, auth);
|
|
69
|
+
registerDefiTools(server, auth);
|
|
70
|
+
registerPaymentsTools(server, auth);
|
|
71
|
+
registerTradeTools(server, auth);
|
|
72
|
+
registerIntentTools(server, auth);
|
|
73
|
+
registerMarketTools(server, auth);
|
|
74
|
+
registerWsTools(server, auth);
|
|
75
|
+
registerSkillTools(server, auth);
|
|
76
|
+
// 3. 连接 server 到 HTTP transport
|
|
77
|
+
await server.connect(transport);
|
|
78
|
+
// 4. 原生 HTTP server
|
|
79
|
+
const httpServer = createServer(async (req, res) => {
|
|
80
|
+
// CORS
|
|
81
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
82
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS");
|
|
83
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, MCP-Protocol-Version");
|
|
84
|
+
if (req.method === "OPTIONS") {
|
|
85
|
+
res.writeHead(204);
|
|
86
|
+
res.end();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// 健康检查
|
|
90
|
+
if (req.method === "GET" && req.url === "/health") {
|
|
91
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
92
|
+
res.end(JSON.stringify({ status: "ok", tsIso: new Date().toISOString() }));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// MCP 端点
|
|
96
|
+
if (req.url === "/mcp" || req.url?.startsWith("/mcp")) {
|
|
97
|
+
try {
|
|
98
|
+
let body;
|
|
99
|
+
if (req.method === "POST") {
|
|
100
|
+
body = await jsonBody(req);
|
|
101
|
+
}
|
|
102
|
+
await transport.handleRequest(req, res, body);
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
console.error("[h-mcp] handleRequest error:", e);
|
|
106
|
+
if (!res.headersSent) {
|
|
107
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
108
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// 404
|
|
114
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
115
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
116
|
+
});
|
|
117
|
+
httpServer.listen(port, host, () => {
|
|
118
|
+
console.error(`[h-mcp] HTTP MCP Server 已启动 → http://${host}:${port}`);
|
|
119
|
+
console.error(`[h-mcp] MCP endpoint: POST http://${host}:${port}/mcp`);
|
|
120
|
+
console.error(`[h-mcp] Health check: GET http://${host}:${port}/health`);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
main().catch(e => { console.error("[h-mcp] 启动失败:", e); process.exit(1); });
|
|
124
|
+
//# sourceMappingURL=http.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* h-mcp Server — stdio 传输
|
|
3
|
+
* 规范: OnchainOS-API对接规范.md §五
|
|
4
|
+
*/
|
|
5
|
+
import "dotenv/config";
|
|
6
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
import { registerBalanceTools } from "./tools/balance.js";
|
|
9
|
+
import { registerGatewayTools } from "./tools/gateway.js";
|
|
10
|
+
import { registerTxHistoryTools } from "./tools/txhistory.js";
|
|
11
|
+
import { registerDefiTools } from "./tools/defi.js";
|
|
12
|
+
import { registerPaymentsTools } from "./tools/payments.js";
|
|
13
|
+
import { registerTradeTools } from "./tools/trade.js";
|
|
14
|
+
import { registerIntentTools } from "./tools/intent.js";
|
|
15
|
+
import { registerMarketTools } from "./tools/market.js";
|
|
16
|
+
import { registerWsTools } from "./tools/ws.js";
|
|
17
|
+
import { registerSkillTools } from "./tools/skills.js";
|
|
18
|
+
function resolveAuth() {
|
|
19
|
+
const k = process.env.OKX_API_KEY, s = process.env.OKX_SECRET_KEY, p = process.env.OKX_PASSPHRASE;
|
|
20
|
+
if (k && s && p)
|
|
21
|
+
return { apiKey: k, secret: s, passphrase: p };
|
|
22
|
+
const a = process.argv.slice(2), g = (f) => { const i = a.indexOf(f); return i >= 0 ? a[i + 1] : undefined; };
|
|
23
|
+
const ka = g("--okx-api-key") ?? g("-k"), sa = g("--okx-secret") ?? g("-s"), pa = g("--okx-passphrase") ?? g("-p");
|
|
24
|
+
if (ka && sa && pa)
|
|
25
|
+
return { apiKey: ka, secret: sa, passphrase: pa };
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
async function main() {
|
|
29
|
+
const auth = resolveAuth();
|
|
30
|
+
if (!auth) {
|
|
31
|
+
console.error("[h-mcp] 未配置 API Key。设置 OKX_API_KEY / OKX_SECRET_KEY / OKX_PASSPHRASE");
|
|
32
|
+
console.error("[h-mcp] 获取: https://web3.okx.com/onchainos/dev-portal");
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
console.error("[h-mcp] Auth 已配置");
|
|
36
|
+
}
|
|
37
|
+
const server = new McpServer({ name: "hchain-mcp", version: "1.0.0" });
|
|
38
|
+
// 逐模块注册工具 (按官方文档对接)
|
|
39
|
+
registerBalanceTools(server, auth);
|
|
40
|
+
registerGatewayTools(server, auth);
|
|
41
|
+
registerTxHistoryTools(server, auth);
|
|
42
|
+
registerDefiTools(server, auth);
|
|
43
|
+
registerPaymentsTools(server, auth);
|
|
44
|
+
registerTradeTools(server, auth);
|
|
45
|
+
registerIntentTools(server, auth);
|
|
46
|
+
registerMarketTools(server, auth);
|
|
47
|
+
registerWsTools(server, auth);
|
|
48
|
+
registerSkillTools(server, auth);
|
|
49
|
+
const transport = new StdioServerTransport();
|
|
50
|
+
await server.connect(transport);
|
|
51
|
+
console.error("[h-mcp] 就绪");
|
|
52
|
+
}
|
|
53
|
+
main().catch(e => { console.error("[h-mcp] 启动失败:", e); process.exit(1); });
|
|
54
|
+
//# sourceMappingURL=index.js.map
|