@t402/mcp 1.0.0 → 2.4.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/README.md +44 -42
- package/bin/t402-mcp.js +17 -17
- package/dist/cjs/index.d.ts +4 -4
- package/dist/cjs/index.js +13 -26
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/server/index.d.ts +1 -1
- package/dist/cjs/server/index.js +13 -26
- package/dist/cjs/server/index.js.map +1 -1
- package/dist/cjs/tools/index.d.ts +7 -7
- package/dist/cjs/tools/index.js +9 -26
- package/dist/cjs/tools/index.js.map +1 -1
- package/dist/cjs/{types-BINGE6ja.d.ts → types-CWK2p9_n.d.ts} +2 -2
- package/dist/esm/{chunk-SB4O25HA.mjs → chunk-5IVOABVF.mjs} +19 -50
- package/dist/esm/chunk-5IVOABVF.mjs.map +1 -0
- package/dist/esm/{chunk-AN6P2M7N.mjs → chunk-U2V6MOSU.mjs} +6 -6
- package/dist/esm/chunk-U2V6MOSU.mjs.map +1 -0
- package/dist/esm/index.d.mts +4 -4
- package/dist/esm/index.mjs +2 -2
- package/dist/esm/server/index.d.mts +1 -1
- package/dist/esm/server/index.mjs +2 -2
- package/dist/esm/tools/index.d.mts +7 -7
- package/dist/esm/tools/index.mjs +1 -1
- package/dist/esm/{types-BINGE6ja.d.mts → types-CWK2p9_n.d.mts} +2 -2
- package/package.json +25 -22
- package/dist/esm/chunk-AN6P2M7N.mjs.map +0 -1
- package/dist/esm/chunk-SB4O25HA.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -13,20 +13,21 @@ Enable AI agents like Claude to make stablecoin payments across multiple blockch
|
|
|
13
13
|
|
|
14
14
|
## Available Tools
|
|
15
15
|
|
|
16
|
-
| Tool
|
|
17
|
-
|
|
18
|
-
| `t402/getBalance`
|
|
19
|
-
| `t402/getAllBalances` | Get balances across all supported networks
|
|
20
|
-
| `t402/pay`
|
|
21
|
-
| `t402/payGasless`
|
|
22
|
-
| `t402/getBridgeFee`
|
|
23
|
-
| `t402/bridge`
|
|
16
|
+
| Tool | Description |
|
|
17
|
+
| --------------------- | ----------------------------------------------------- |
|
|
18
|
+
| `t402/getBalance` | Get token balances for a wallet on a specific network |
|
|
19
|
+
| `t402/getAllBalances` | Get balances across all supported networks |
|
|
20
|
+
| `t402/pay` | Execute a stablecoin payment |
|
|
21
|
+
| `t402/payGasless` | Execute a gasless payment using ERC-4337 |
|
|
22
|
+
| `t402/getBridgeFee` | Get fee quote for bridging USDT0 |
|
|
23
|
+
| `t402/bridge` | Bridge USDT0 between chains |
|
|
24
24
|
|
|
25
25
|
## Claude Desktop Integration
|
|
26
26
|
|
|
27
27
|
### Quick Start
|
|
28
28
|
|
|
29
29
|
1. Install the package globally:
|
|
30
|
+
|
|
30
31
|
```bash
|
|
31
32
|
npm install -g @t402/mcp
|
|
32
33
|
```
|
|
@@ -132,39 +133,40 @@ Configure custom RPC endpoints for better reliability:
|
|
|
132
133
|
|
|
133
134
|
## Environment Variables
|
|
134
135
|
|
|
135
|
-
| Variable
|
|
136
|
-
|
|
137
|
-
| `T402_PRIVATE_KEY`
|
|
138
|
-
| `T402_DEMO_MODE`
|
|
139
|
-
| `T402_BUNDLER_URL`
|
|
140
|
-
| `T402_PAYMASTER_URL` | Paymaster URL for sponsored gas
|
|
141
|
-
| `T402_RPC_ETHEREUM`
|
|
142
|
-
| `T402_RPC_BASE`
|
|
143
|
-
| `T402_RPC_ARBITRUM`
|
|
144
|
-
| `T402_RPC_OPTIMISM`
|
|
145
|
-
| `T402_RPC_POLYGON`
|
|
146
|
-
| `T402_RPC_AVALANCHE` | Custom RPC URL for Avalanche
|
|
147
|
-
| `T402_RPC_INK`
|
|
148
|
-
| `T402_RPC_BERACHAIN` | Custom RPC URL for Berachain
|
|
149
|
-
| `T402_RPC_UNICHAIN`
|
|
136
|
+
| Variable | Description |
|
|
137
|
+
| -------------------- | --------------------------------------------- |
|
|
138
|
+
| `T402_PRIVATE_KEY` | Wallet private key (hex with 0x prefix) |
|
|
139
|
+
| `T402_DEMO_MODE` | Set to "true" for simulated transactions |
|
|
140
|
+
| `T402_BUNDLER_URL` | ERC-4337 bundler URL for gasless transactions |
|
|
141
|
+
| `T402_PAYMASTER_URL` | Paymaster URL for sponsored gas |
|
|
142
|
+
| `T402_RPC_ETHEREUM` | Custom RPC URL for Ethereum |
|
|
143
|
+
| `T402_RPC_BASE` | Custom RPC URL for Base |
|
|
144
|
+
| `T402_RPC_ARBITRUM` | Custom RPC URL for Arbitrum |
|
|
145
|
+
| `T402_RPC_OPTIMISM` | Custom RPC URL for Optimism |
|
|
146
|
+
| `T402_RPC_POLYGON` | Custom RPC URL for Polygon |
|
|
147
|
+
| `T402_RPC_AVALANCHE` | Custom RPC URL for Avalanche |
|
|
148
|
+
| `T402_RPC_INK` | Custom RPC URL for Ink |
|
|
149
|
+
| `T402_RPC_BERACHAIN` | Custom RPC URL for Berachain |
|
|
150
|
+
| `T402_RPC_UNICHAIN` | Custom RPC URL for Unichain |
|
|
150
151
|
|
|
151
152
|
## Supported Networks
|
|
152
153
|
|
|
153
|
-
| Network
|
|
154
|
-
|
|
155
|
-
| Ethereum
|
|
156
|
-
| Base
|
|
157
|
-
| Arbitrum
|
|
158
|
-
| Optimism
|
|
159
|
-
| Polygon
|
|
160
|
-
| Avalanche | 43114
|
|
161
|
-
| Ink
|
|
162
|
-
| Berachain | 80094
|
|
163
|
-
| Unichain
|
|
154
|
+
| Network | Chain ID | Tokens |
|
|
155
|
+
| --------- | -------- | ----------------- |
|
|
156
|
+
| Ethereum | 1 | USDC, USDT, USDT0 |
|
|
157
|
+
| Base | 8453 | USDC |
|
|
158
|
+
| Arbitrum | 42161 | USDC, USDT, USDT0 |
|
|
159
|
+
| Optimism | 10 | USDC, USDT |
|
|
160
|
+
| Polygon | 137 | USDC, USDT |
|
|
161
|
+
| Avalanche | 43114 | USDC, USDT |
|
|
162
|
+
| Ink | 57073 | USDT0 |
|
|
163
|
+
| Berachain | 80094 | USDT0 |
|
|
164
|
+
| Unichain | 130 | USDT0 |
|
|
164
165
|
|
|
165
166
|
## Cross-Chain Bridging
|
|
166
167
|
|
|
167
168
|
USDT0 can be bridged between these chains using LayerZero:
|
|
169
|
+
|
|
168
170
|
- Ethereum
|
|
169
171
|
- Arbitrum
|
|
170
172
|
- Ink
|
|
@@ -184,23 +186,23 @@ Once configured, you can ask Claude to:
|
|
|
184
186
|
## Programmatic Usage
|
|
185
187
|
|
|
186
188
|
```typescript
|
|
187
|
-
import { createT402McpServer, loadConfigFromEnv } from '@t402/mcp'
|
|
189
|
+
import { createT402McpServer, loadConfigFromEnv } from '@t402/mcp'
|
|
188
190
|
|
|
189
|
-
const config = loadConfigFromEnv()
|
|
190
|
-
const server = createT402McpServer(config)
|
|
191
|
-
await server.run()
|
|
191
|
+
const config = loadConfigFromEnv()
|
|
192
|
+
const server = createT402McpServer(config)
|
|
193
|
+
await server.run()
|
|
192
194
|
```
|
|
193
195
|
|
|
194
196
|
## Using Individual Tools
|
|
195
197
|
|
|
196
198
|
```typescript
|
|
197
|
-
import { executeGetBalance, executePay } from '@t402/mcp/tools'
|
|
199
|
+
import { executeGetBalance, executePay } from '@t402/mcp/tools'
|
|
198
200
|
|
|
199
201
|
// Check balance
|
|
200
202
|
const balance = await executeGetBalance({
|
|
201
203
|
network: 'base',
|
|
202
204
|
address: '0x...',
|
|
203
|
-
})
|
|
205
|
+
})
|
|
204
206
|
|
|
205
207
|
// Execute payment
|
|
206
208
|
const result = await executePay(
|
|
@@ -212,8 +214,8 @@ const result = await executePay(
|
|
|
212
214
|
},
|
|
213
215
|
{
|
|
214
216
|
privateKey: '0x...',
|
|
215
|
-
}
|
|
216
|
-
)
|
|
217
|
+
},
|
|
218
|
+
)
|
|
217
219
|
```
|
|
218
220
|
|
|
219
221
|
## Security Considerations
|
package/bin/t402-mcp.js
CHANGED
|
@@ -20,34 +20,34 @@
|
|
|
20
20
|
* ... (other networks follow same pattern)
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
|
-
import { createT402McpServer, loadConfigFromEnv } from
|
|
23
|
+
import { createT402McpServer, loadConfigFromEnv } from '../dist/esm/index.mjs'
|
|
24
24
|
|
|
25
25
|
async function main() {
|
|
26
|
-
const config = loadConfigFromEnv()
|
|
26
|
+
const config = loadConfigFromEnv()
|
|
27
27
|
|
|
28
28
|
// Log configuration status (to stderr so it doesn't interfere with stdio)
|
|
29
|
-
console.error(
|
|
30
|
-
console.error(` Private Key: ${config.privateKey ?
|
|
31
|
-
console.error(` Demo Mode: ${config.demoMode ?
|
|
32
|
-
console.error(` Bundler URL: ${config.bundlerUrl ?
|
|
33
|
-
console.error(` Paymaster URL: ${config.paymasterUrl ?
|
|
29
|
+
console.error('t402 MCP Server Configuration:')
|
|
30
|
+
console.error(` Private Key: ${config.privateKey ? 'configured' : 'not set'}`)
|
|
31
|
+
console.error(` Demo Mode: ${config.demoMode ? 'enabled' : 'disabled'}`)
|
|
32
|
+
console.error(` Bundler URL: ${config.bundlerUrl ? 'configured' : 'not set'}`)
|
|
33
|
+
console.error(` Paymaster URL: ${config.paymasterUrl ? 'configured' : 'not set'}`)
|
|
34
34
|
|
|
35
35
|
if (config.rpcUrls) {
|
|
36
|
-
console.error(` Custom RPC URLs: ${Object.keys(config.rpcUrls).join(
|
|
36
|
+
console.error(` Custom RPC URLs: ${Object.keys(config.rpcUrls).join(', ')}`)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if (!config.privateKey && !config.demoMode) {
|
|
40
|
-
console.error(
|
|
41
|
-
console.error(
|
|
42
|
-
console.error(
|
|
43
|
-
console.error(
|
|
40
|
+
console.error('')
|
|
41
|
+
console.error('Warning: No private key configured.')
|
|
42
|
+
console.error('Set T402_PRIVATE_KEY env var or enable T402_DEMO_MODE=true')
|
|
43
|
+
console.error('')
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const server = createT402McpServer(config)
|
|
47
|
-
await server.run()
|
|
46
|
+
const server = createT402McpServer(config)
|
|
47
|
+
await server.run()
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
main().catch((error) => {
|
|
51
|
-
console.error(
|
|
52
|
-
process.exit(1)
|
|
53
|
-
})
|
|
51
|
+
console.error('Fatal error:', error)
|
|
52
|
+
process.exit(1)
|
|
53
|
+
})
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { T402McpServer, createT402McpServer, loadConfigFromEnv } from './server/index.js';
|
|
2
2
|
export { AllBalancesResult, BridgeInput, BridgeOptions, GASLESS_SUPPORTED_NETWORKS, GetAllBalancesInput, GetBalanceInput, GetBridgeFeeInput, PayGaslessInput, PayGaslessOptions, PayInput, PayOptions, TOOL_DEFINITIONS, bridgeInputSchema, executeBridge, executeGetAllBalances, executeGetBalance, executeGetBridgeFee, executePay, executePayGasless, formatAllBalancesResult, formatBalanceResult, formatBridgeFeeResult, formatBridgeResult, formatGaslessPaymentResult, formatPaymentResult, getAllBalancesInputSchema, getBalanceInputSchema, getBridgeFeeInputSchema, payGaslessInputSchema, payInputSchema } from './tools/index.js';
|
|
3
|
-
import { S as SupportedNetwork } from './types-
|
|
4
|
-
export { B as BridgeFeeQuote, b as BridgeResult, C as ChainBalance, G as GaslessPaymentResult, M as McpServerConfig, P as PaymentParams, a as PaymentResult, T as TokenBalance, c as ToolContext } from './types-
|
|
3
|
+
import { S as SupportedNetwork } from './types-CWK2p9_n.js';
|
|
4
|
+
export { B as BridgeFeeQuote, b as BridgeResult, C as ChainBalance, G as GaslessPaymentResult, M as McpServerConfig, P as PaymentParams, a as PaymentResult, T as TokenBalance, c as ToolContext } from './types-CWK2p9_n.js';
|
|
5
5
|
import { Address } from 'viem';
|
|
6
6
|
import 'zod';
|
|
7
7
|
|
|
@@ -52,11 +52,11 @@ declare function getLayerZeroScanUrl(messageGuid: string): string;
|
|
|
52
52
|
/**
|
|
53
53
|
* Check if a network supports a specific token
|
|
54
54
|
*/
|
|
55
|
-
declare function supportsToken(network: SupportedNetwork, token:
|
|
55
|
+
declare function supportsToken(network: SupportedNetwork, token: 'USDC' | 'USDT' | 'USDT0'): boolean;
|
|
56
56
|
/**
|
|
57
57
|
* Get token address for a network
|
|
58
58
|
*/
|
|
59
|
-
declare function getTokenAddress(network: SupportedNetwork, token:
|
|
59
|
+
declare function getTokenAddress(network: SupportedNetwork, token: 'USDC' | 'USDT' | 'USDT0'): Address | undefined;
|
|
60
60
|
/**
|
|
61
61
|
* Format token amount for display
|
|
62
62
|
*/
|
package/dist/cjs/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
9
|
var __export = (target, all) => {
|
|
9
10
|
for (var name in all)
|
|
10
11
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -26,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
27
|
mod
|
|
27
28
|
));
|
|
28
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
29
31
|
|
|
30
32
|
// src/index.ts
|
|
31
33
|
var src_exports = {};
|
|
@@ -145,7 +147,7 @@ var USDT0_ADDRESSES = {
|
|
|
145
147
|
arbitrum: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
146
148
|
ink: "0x0200C29006150606B650577BBE7B6248F58470c1",
|
|
147
149
|
berachain: "0x779Ded0c9e1022225f8E0630b35a9b54bE713736",
|
|
148
|
-
unichain: "
|
|
150
|
+
unichain: "0x9151434b16b9763660705744891fA906F660EcC5"
|
|
149
151
|
};
|
|
150
152
|
var BRIDGEABLE_CHAINS = [
|
|
151
153
|
"ethereum",
|
|
@@ -345,13 +347,9 @@ async function executeGetBalance(input, rpcUrls) {
|
|
|
345
347
|
tokenAddresses.push({ token: USDT0_ADDRESSES[network], expected: "USDT0" });
|
|
346
348
|
}
|
|
347
349
|
const tokenBalances = await Promise.all(
|
|
348
|
-
tokenAddresses.map(
|
|
349
|
-
({ token }) => getTokenBalance(client, token, walletAddress)
|
|
350
|
-
)
|
|
351
|
-
);
|
|
352
|
-
const tokens = tokenBalances.filter(
|
|
353
|
-
(t) => t !== null
|
|
350
|
+
tokenAddresses.map(({ token }) => getTokenBalance(client, token, walletAddress))
|
|
354
351
|
);
|
|
352
|
+
const tokens = tokenBalances.filter((t) => t !== null);
|
|
355
353
|
return {
|
|
356
354
|
network,
|
|
357
355
|
chainId: CHAIN_IDS[network],
|
|
@@ -539,7 +537,7 @@ function getViemChain2(network) {
|
|
|
539
537
|
}
|
|
540
538
|
}
|
|
541
539
|
async function executePay(input, options) {
|
|
542
|
-
const { to, amount, token, network, memo } = input;
|
|
540
|
+
const { to, amount, token, network, memo: _memo } = input;
|
|
543
541
|
const { privateKey, rpcUrl, demoMode } = options;
|
|
544
542
|
if (!supportsToken(network, token)) {
|
|
545
543
|
throw new Error(`Token ${token} is not supported on ${network}`);
|
|
@@ -625,14 +623,7 @@ var payGaslessInputSchema = import_zod4.z.object({
|
|
|
625
623
|
to: import_zod4.z.string().regex(/^0x[a-fA-F0-9]{40}$/).describe("Recipient address"),
|
|
626
624
|
amount: import_zod4.z.string().regex(/^\d+(\.\d+)?$/).describe("Amount to pay (e.g., '10.50' for 10.50 USDC)"),
|
|
627
625
|
token: import_zod4.z.enum(["USDC", "USDT", "USDT0"]).describe("Token to use for payment"),
|
|
628
|
-
network: import_zod4.z.enum([
|
|
629
|
-
"ethereum",
|
|
630
|
-
"base",
|
|
631
|
-
"arbitrum",
|
|
632
|
-
"optimism",
|
|
633
|
-
"polygon",
|
|
634
|
-
"avalanche"
|
|
635
|
-
]).describe("Network to execute gasless payment on (must support ERC-4337)")
|
|
626
|
+
network: import_zod4.z.enum(["ethereum", "base", "arbitrum", "optimism", "polygon", "avalanche"]).describe("Network to execute gasless payment on (must support ERC-4337)")
|
|
636
627
|
});
|
|
637
628
|
var GASLESS_SUPPORTED_NETWORKS = [
|
|
638
629
|
"ethereum",
|
|
@@ -668,7 +659,7 @@ function getViemChain3(network) {
|
|
|
668
659
|
}
|
|
669
660
|
async function executePayGasless(input, options) {
|
|
670
661
|
const { to, amount, token, network } = input;
|
|
671
|
-
const { privateKey, bundlerUrl, paymasterUrl, rpcUrl, demoMode } = options;
|
|
662
|
+
const { privateKey, bundlerUrl, paymasterUrl: _paymasterUrl, rpcUrl, demoMode } = options;
|
|
672
663
|
if (!GASLESS_SUPPORTED_NETWORKS.includes(network)) {
|
|
673
664
|
throw new Error(
|
|
674
665
|
`Network ${network} does not support ERC-4337 gasless transactions. Supported: ${GASLESS_SUPPORTED_NETWORKS.join(", ")}`
|
|
@@ -1058,9 +1049,7 @@ var OFT_ABI2 = [
|
|
|
1058
1049
|
]
|
|
1059
1050
|
}
|
|
1060
1051
|
];
|
|
1061
|
-
var OFT_SENT_EVENT_TOPIC = (0, import_viem5.keccak256)(
|
|
1062
|
-
(0, import_viem5.toBytes)("OFTSent(bytes32,uint32,address,uint256,uint256)")
|
|
1063
|
-
);
|
|
1052
|
+
var OFT_SENT_EVENT_TOPIC = (0, import_viem5.keccak256)((0, import_viem5.toBytes)("OFTSent(bytes32,uint32,address,uint256,uint256)"));
|
|
1064
1053
|
function getViemChain5(network) {
|
|
1065
1054
|
switch (network) {
|
|
1066
1055
|
case "ethereum":
|
|
@@ -1160,11 +1149,7 @@ async function executeBridge(input, options) {
|
|
|
1160
1149
|
address: usdt0Address,
|
|
1161
1150
|
abi: OFT_ABI2,
|
|
1162
1151
|
functionName: "send",
|
|
1163
|
-
args: [
|
|
1164
|
-
sendParam,
|
|
1165
|
-
{ nativeFee: nativeFeeWithBuffer, lzTokenFee: 0n },
|
|
1166
|
-
account.address
|
|
1167
|
-
],
|
|
1152
|
+
args: [sendParam, { nativeFee: nativeFeeWithBuffer, lzTokenFee: 0n }, account.address],
|
|
1168
1153
|
value: nativeFeeWithBuffer
|
|
1169
1154
|
});
|
|
1170
1155
|
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
@@ -1207,7 +1192,7 @@ function formatBridgeResult(result) {
|
|
|
1207
1192
|
`- [View on LayerZero Scan](${result.trackingUrl})`,
|
|
1208
1193
|
`- [View Source TX](${getExplorerTxUrl(result.fromChain, result.txHash)})`,
|
|
1209
1194
|
"",
|
|
1210
|
-
|
|
1195
|
+
`_Your USDT0 will arrive on ${result.toChain} once the LayerZero message is delivered._`
|
|
1211
1196
|
].join("\n");
|
|
1212
1197
|
}
|
|
1213
1198
|
|
|
@@ -1415,6 +1400,8 @@ var TOOL_DEFINITIONS = {
|
|
|
1415
1400
|
// src/server/t402Server.ts
|
|
1416
1401
|
var T402McpServer = class {
|
|
1417
1402
|
constructor(config = {}) {
|
|
1403
|
+
__publicField(this, "server");
|
|
1404
|
+
__publicField(this, "config");
|
|
1418
1405
|
this.config = config;
|
|
1419
1406
|
this.server = new import_server.Server(
|
|
1420
1407
|
{
|