helius-mcp 1.2.0 → 2.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/CHANGELOG.md +27 -0
- package/README.md +42 -30
- package/dist/http.d.ts +1 -1
- package/dist/index.js +2 -56
- package/dist/results/store.d.ts +8 -0
- package/dist/results/store.js +72 -0
- package/dist/results/types.d.ts +47 -0
- package/dist/results/types.js +1 -0
- package/dist/router/action-groups.d.ts +6 -0
- package/dist/router/action-groups.js +32 -0
- package/dist/router/action-handlers.d.ts +20 -0
- package/dist/router/action-handlers.js +125 -0
- package/dist/router/actions.d.ts +12 -0
- package/dist/router/actions.js +123 -0
- package/dist/router/catalog.d.ts +6 -0
- package/dist/router/catalog.js +388 -0
- package/dist/router/context.d.ts +5 -0
- package/dist/router/context.js +10 -0
- package/dist/router/dispatch.d.ts +4 -0
- package/dist/router/dispatch.js +276 -0
- package/dist/router/instructions.d.ts +1 -0
- package/dist/router/instructions.js +25 -0
- package/dist/router/register.d.ts +2 -0
- package/dist/router/register.js +15 -0
- package/dist/router/required-params.d.ts +9 -0
- package/dist/router/required-params.js +66 -0
- package/dist/router/responses.d.ts +29 -0
- package/dist/router/responses.js +186 -0
- package/dist/router/schemas.d.ts +216 -0
- package/dist/router/schemas.js +195 -0
- package/dist/router/telemetry.d.ts +27 -0
- package/dist/router/telemetry.js +52 -0
- package/dist/router/types.d.ts +46 -0
- package/dist/router/types.js +1 -0
- package/dist/scripts/validate-catalog.d.ts +2 -2
- package/dist/scripts/validate-catalog.js +10 -10
- package/dist/tools/accounts.js +5 -5
- package/dist/tools/assets.js +5 -5
- package/dist/tools/auth.js +392 -288
- package/dist/tools/config.js +3 -3
- package/dist/tools/das-extras.js +6 -6
- package/dist/tools/docs.js +55 -41
- package/dist/tools/enhanced-websockets.js +13 -13
- package/dist/tools/fees.js +3 -3
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +2 -80
- package/dist/tools/laserstream.js +20 -23
- package/dist/tools/network.js +41 -2
- package/dist/tools/plans.d.ts +0 -5
- package/dist/tools/plans.js +167 -12
- package/dist/tools/product-catalog.d.ts +1 -0
- package/dist/tools/product-catalog.js +51 -16
- package/dist/tools/recommend.d.ts +0 -1
- package/dist/tools/recommend.js +9 -28
- package/dist/tools/shared.d.ts +1 -0
- package/dist/tools/shared.js +10 -2
- package/dist/tools/solana-knowledge.js +23 -7
- package/dist/tools/staking.d.ts +2 -0
- package/dist/tools/staking.js +268 -0
- package/dist/tools/transactions.js +167 -3
- package/dist/tools/transfers.js +38 -43
- package/dist/tools/wallet.js +27 -16
- package/dist/tools/webhooks.js +3 -3
- package/dist/tools/zk-compression.d.ts +2 -0
- package/dist/tools/zk-compression.js +781 -0
- package/dist/utils/config.d.ts +2 -2
- package/dist/utils/config.js +68 -6
- package/dist/utils/errors.d.ts +10 -1
- package/dist/utils/errors.js +46 -12
- package/dist/utils/feedback.js +1 -4
- package/dist/utils/helius.js +2 -1
- package/dist/utils/ows.d.ts +74 -0
- package/dist/utils/ows.js +155 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/system-prompts/helius/claude.system.md +56 -25
- package/system-prompts/helius/full.md +474 -130
- package/system-prompts/helius/openai.developer.md +56 -25
- package/system-prompts/helius-dflow/claude.system.md +41 -6
- package/system-prompts/helius-dflow/full.md +581 -92
- package/system-prompts/helius-dflow/openai.developer.md +41 -6
- package/system-prompts/helius-jupiter/claude.system.md +333 -0
- package/system-prompts/helius-jupiter/full.md +5109 -0
- package/system-prompts/helius-jupiter/openai.developer.md +333 -0
- package/system-prompts/helius-okx/claude.system.md +182 -0
- package/system-prompts/helius-okx/full.md +584 -0
- package/system-prompts/helius-okx/openai.developer.md +182 -0
- package/system-prompts/helius-phantom/claude.system.md +15 -2
- package/system-prompts/helius-phantom/full.md +254 -101
- package/system-prompts/helius-phantom/openai.developer.md +15 -2
- package/system-prompts/svm/claude.system.md +1 -0
- package/system-prompts/svm/full.md +1 -0
- package/system-prompts/svm/openai.developer.md +1 -0
package/dist/tools/transfers.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import {
|
|
2
|
+
import { address } from '@solana/kit';
|
|
3
3
|
import { getTransferSolInstruction } from '@solana-program/system';
|
|
4
4
|
import { findAssociatedTokenPda, getCloseAccountInstruction, getCreateAssociatedTokenIdempotentInstruction, getTransferCheckedInstruction, TOKEN_PROGRAM_ADDRESS, } from '@solana-program/token';
|
|
5
|
-
import { getHeliusClient, hasApiKey
|
|
5
|
+
import { getHeliusClient, hasApiKey } from '../utils/helius.js';
|
|
6
6
|
import { mcpText, mcpError, handleToolError, isValidAddressFormat } from '../utils/errors.js';
|
|
7
7
|
import { noApiKeyResponse } from './shared.js';
|
|
8
|
+
import { resolveOwsOrKeypairSigner } from '../utils/ows.js';
|
|
8
9
|
const TOKEN_2022_PROGRAM_ID = 'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb';
|
|
9
10
|
// ── Tool Registration ──
|
|
10
11
|
export function registerTransferTools(server) {
|
|
@@ -19,24 +20,22 @@ export function registerTransferTools(server) {
|
|
|
19
20
|
recipientAddress: z.string().describe('Recipient Solana wallet address (base58 encoded)'),
|
|
20
21
|
amount: z.number().positive().optional().describe('Amount of SOL to send (e.g., 0.5 for half a SOL). Required unless sendMax is true.'),
|
|
21
22
|
sendMax: z.boolean().optional().default(false).describe('Send the maximum possible amount (entire balance minus transaction fees). When true, amount is ignored.'),
|
|
22
|
-
|
|
23
|
+
owsWallet: z.string().optional().describe('OWS wallet name for policy-gated signing (requires `ows` CLI installed). When provided, signs via Open Wallet Standard instead of the local keypair.'),
|
|
24
|
+
}, async ({ recipientAddress, amount, sendMax, owsWallet }) => {
|
|
23
25
|
if (!hasApiKey())
|
|
24
26
|
return noApiKeyResponse();
|
|
25
27
|
try {
|
|
26
|
-
// Load keypair
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
catch {
|
|
32
|
-
return mcpError('No keypair found. Call `generateKeypair` first to create a wallet, then fund it before sending.');
|
|
33
|
-
}
|
|
28
|
+
// Load signer — OWS wallet or local keypair
|
|
29
|
+
const resolved = await resolveOwsOrKeypairSigner(owsWallet);
|
|
30
|
+
if (!resolved.ok)
|
|
31
|
+
return resolved.error;
|
|
32
|
+
const { signer, walletAddress } = resolved;
|
|
34
33
|
// Validate recipient address
|
|
35
34
|
if (!isValidAddressFormat(recipientAddress)) {
|
|
36
|
-
return mcpError(`Invalid recipient address "${recipientAddress}". Expected a valid Solana address (32-44 base58 characters)
|
|
35
|
+
return mcpError(`Invalid recipient address "${recipientAddress}". Expected a valid Solana address (32-44 base58 characters).`, { type: 'VALIDATION', code: 'INVALID_ADDRESS', retryable: false, recovery: 'Provide a valid base58-encoded Solana address (32-44 characters).' });
|
|
37
36
|
}
|
|
38
37
|
const helius = getHeliusClient();
|
|
39
|
-
const balanceResult = await helius.getBalance(
|
|
38
|
+
const balanceResult = await helius.getBalance(walletAddress);
|
|
40
39
|
const balanceLamports = BigInt(balanceResult.value);
|
|
41
40
|
let lamports;
|
|
42
41
|
let sendAmount;
|
|
@@ -56,13 +55,13 @@ export function registerTransferTools(server) {
|
|
|
56
55
|
if (lamports <= 0n) {
|
|
57
56
|
const available = Number(balanceLamports) / 1_000_000_000;
|
|
58
57
|
return mcpError(`Balance too low to cover the transaction fee. You have ${available} SOL.\n\n` +
|
|
59
|
-
`Wallet: \`${
|
|
58
|
+
`Wallet: \`${walletAddress}\``, { type: 'INSUFFICIENT_FUNDS', code: 'LOW_SOL', retryable: false, recovery: `Fund the wallet with more SOL. Current balance: ${available} SOL.` });
|
|
60
59
|
}
|
|
61
60
|
sendAmount = Number(lamports) / 1_000_000_000;
|
|
62
61
|
}
|
|
63
62
|
else {
|
|
64
63
|
if (amount === undefined) {
|
|
65
|
-
return mcpError('Either `amount` must be specified or `sendMax` must be true.');
|
|
64
|
+
return mcpError('Either `amount` must be specified or `sendMax` must be true.', { type: 'VALIDATION', code: 'MISSING_PARAM', retryable: false, recovery: 'Provide `amount` or set `sendMax` to true.' });
|
|
66
65
|
}
|
|
67
66
|
lamports = BigInt(Math.round(amount * 1_000_000_000));
|
|
68
67
|
sendAmount = amount;
|
|
@@ -71,11 +70,9 @@ export function registerTransferTools(server) {
|
|
|
71
70
|
if (balanceLamports < lamports + reserveLamports) {
|
|
72
71
|
const available = Number(balanceLamports) / 1_000_000_000;
|
|
73
72
|
return mcpError(`Insufficient SOL balance. You have ${available} SOL but need ${sendAmount} SOL plus ~0.005 SOL for transaction fees.\n\n` +
|
|
74
|
-
`Wallet: \`${
|
|
73
|
+
`Wallet: \`${walletAddress}\``, { type: 'INSUFFICIENT_FUNDS', code: 'LOW_SOL', retryable: false, recovery: `Fund the wallet with at least ${sendAmount + 0.005} SOL.` });
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
|
-
// Create signer from keypair bytes
|
|
78
|
-
const signer = await createKeyPairSignerFromBytes(signerData.secretKey);
|
|
79
76
|
// Build transfer instruction
|
|
80
77
|
const ix = getTransferSolInstruction({
|
|
81
78
|
source: signer,
|
|
@@ -100,8 +97,9 @@ export function registerTransferTools(server) {
|
|
|
100
97
|
region: 'Default',
|
|
101
98
|
});
|
|
102
99
|
}
|
|
100
|
+
const signerLabel = owsWallet ? `\`${walletAddress}\` (OWS: ${owsWallet})` : `\`${walletAddress}\``;
|
|
103
101
|
return mcpText(`**SOL Transfer Sent**\n\n` +
|
|
104
|
-
`- **From:**
|
|
102
|
+
`- **From:** ${signerLabel}\n` +
|
|
105
103
|
`- **To:** \`${recipientAddress}\`\n` +
|
|
106
104
|
`- **Amount:** ${sendAmount} SOL${sendMax ? ' (max)' : ''}\n` +
|
|
107
105
|
`- **Signature:** \`${signature}\`\n` +
|
|
@@ -124,24 +122,22 @@ export function registerTransferTools(server) {
|
|
|
124
122
|
mintAddress: z.string().describe('Token mint address (base58 encoded, e.g., EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v for USDC)'),
|
|
125
123
|
amount: z.number().positive().optional().describe('Amount of tokens to send in human-readable units (e.g., 10 for 10 USDC). Required unless sendMax is true.'),
|
|
126
124
|
sendMax: z.boolean().optional().default(false).describe('Send the entire token balance and close the sender token account to reclaim rent. When true, amount is ignored.'),
|
|
127
|
-
|
|
125
|
+
owsWallet: z.string().optional().describe('OWS wallet name for policy-gated signing (requires `ows` CLI installed). When provided, signs via Open Wallet Standard instead of the local keypair.'),
|
|
126
|
+
}, async ({ recipientAddress, mintAddress, amount, sendMax, owsWallet }) => {
|
|
128
127
|
if (!hasApiKey())
|
|
129
128
|
return noApiKeyResponse();
|
|
130
129
|
try {
|
|
131
|
-
// Load keypair
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
catch {
|
|
137
|
-
return mcpError('No keypair found. Call `generateKeypair` first to create a wallet, then fund it before sending.');
|
|
138
|
-
}
|
|
130
|
+
// Load signer — OWS wallet or local keypair
|
|
131
|
+
const resolved = await resolveOwsOrKeypairSigner(owsWallet);
|
|
132
|
+
if (!resolved.ok)
|
|
133
|
+
return resolved.error;
|
|
134
|
+
const { signer, walletAddress } = resolved;
|
|
139
135
|
// Validate addresses
|
|
140
136
|
if (!isValidAddressFormat(recipientAddress)) {
|
|
141
|
-
return mcpError(`Invalid recipient address "${recipientAddress}". Expected a valid Solana address (32-44 base58 characters)
|
|
137
|
+
return mcpError(`Invalid recipient address "${recipientAddress}". Expected a valid Solana address (32-44 base58 characters).`, { type: 'VALIDATION', code: 'INVALID_ADDRESS', retryable: false, recovery: 'Provide a valid base58-encoded Solana address (32-44 characters).' });
|
|
142
138
|
}
|
|
143
139
|
if (!isValidAddressFormat(mintAddress)) {
|
|
144
|
-
return mcpError(`Invalid mint address "${mintAddress}". Expected a valid Solana address (32-44 base58 characters)
|
|
140
|
+
return mcpError(`Invalid mint address "${mintAddress}". Expected a valid Solana address (32-44 base58 characters).`, { type: 'VALIDATION', code: 'INVALID_ADDRESS', retryable: false, recovery: 'Provide a valid base58-encoded token mint address (32-44 characters).' });
|
|
145
141
|
}
|
|
146
142
|
// Fetch token metadata via DAS to get decimals and token program
|
|
147
143
|
const helius = getHeliusClient();
|
|
@@ -150,10 +146,10 @@ export function registerTransferTools(server) {
|
|
|
150
146
|
asset = await helius.getAsset({ id: mintAddress });
|
|
151
147
|
}
|
|
152
148
|
catch {
|
|
153
|
-
return mcpError(`Could not fetch token metadata for mint \`${mintAddress}\`. Verify the mint address is correct
|
|
149
|
+
return mcpError(`Could not fetch token metadata for mint \`${mintAddress}\`. Verify the mint address is correct.`, { type: 'NOT_FOUND', code: 'RESOURCE_NOT_FOUND', retryable: false, recovery: 'Verify the mint address is correct. Use `getAsset` to check if the mint exists.' });
|
|
154
150
|
}
|
|
155
151
|
if (!asset?.token_info?.decimals && asset?.token_info?.decimals !== 0) {
|
|
156
|
-
return mcpError(`Mint \`${mintAddress}\` does not appear to be a fungible token (no decimals info found). Use \`getAsset\` to inspect it
|
|
152
|
+
return mcpError(`Mint \`${mintAddress}\` does not appear to be a fungible token (no decimals info found). Use \`getAsset\` to inspect it.`, { type: 'NOT_FOUND', code: 'RESOURCE_NOT_FOUND', retryable: false, recovery: 'This mint may not be a fungible token. Use `getAsset` to inspect it.' });
|
|
157
153
|
}
|
|
158
154
|
const decimals = asset.token_info.decimals;
|
|
159
155
|
const tokenProgram = asset.token_info.token_program;
|
|
@@ -161,10 +157,8 @@ export function registerTransferTools(server) {
|
|
|
161
157
|
const tokenSymbol = asset.content?.metadata?.symbol || '';
|
|
162
158
|
// Token-2022 check
|
|
163
159
|
if (tokenProgram === TOKEN_2022_PROGRAM_ID) {
|
|
164
|
-
return mcpError(`Token-2022 transfers are not yet supported. This token (\`${tokenName}\`${tokenSymbol ? ` / ${tokenSymbol}` : ''}) uses the Token-2022 program. Standard SPL Token transfers are supported
|
|
160
|
+
return mcpError(`Token-2022 transfers are not yet supported. This token (\`${tokenName}\`${tokenSymbol ? ` / ${tokenSymbol}` : ''}) uses the Token-2022 program. Standard SPL Token transfers are supported.`, { type: 'UNSUPPORTED', code: 'TOKEN_2022', retryable: false, recovery: 'Token-2022 transfers are not yet supported. Only standard SPL Token transfers are available.' });
|
|
165
161
|
}
|
|
166
|
-
// Create signer from keypair bytes
|
|
167
|
-
const signer = await createKeyPairSignerFromBytes(signerData.secretKey);
|
|
168
162
|
const mint = address(mintAddress);
|
|
169
163
|
const recipient = address(recipientAddress);
|
|
170
164
|
// Derive ATAs
|
|
@@ -188,20 +182,20 @@ export function registerTransferTools(server) {
|
|
|
188
182
|
}
|
|
189
183
|
catch {
|
|
190
184
|
return mcpError(`No token account found for ${tokenSymbol || tokenName}. You may not hold this token.\n\n` +
|
|
191
|
-
`Wallet: \`${
|
|
192
|
-
`Mint: \`${mintAddress}
|
|
185
|
+
`Wallet: \`${walletAddress}\`\n` +
|
|
186
|
+
`Mint: \`${mintAddress}\``, { type: 'INSUFFICIENT_FUNDS', code: 'LOW_TOKEN', retryable: false, recovery: `You may not hold ${tokenSymbol || tokenName}. Check your token balances with getTokenBalances.` });
|
|
193
187
|
}
|
|
194
188
|
rawAmount = BigInt(tokenBalance.value.amount);
|
|
195
189
|
if (rawAmount === 0n) {
|
|
196
190
|
return mcpError(`${tokenSymbol || tokenName} balance is 0. Nothing to send.\n\n` +
|
|
197
|
-
`Wallet: \`${
|
|
198
|
-
`Mint: \`${mintAddress}
|
|
191
|
+
`Wallet: \`${walletAddress}\`\n` +
|
|
192
|
+
`Mint: \`${mintAddress}\``, { type: 'INSUFFICIENT_FUNDS', code: 'ZERO_BALANCE', retryable: false, recovery: `${tokenSymbol || tokenName} balance is zero. Fund the wallet with tokens before sending.` });
|
|
199
193
|
}
|
|
200
194
|
sendAmount = Number(rawAmount) / 10 ** decimals;
|
|
201
195
|
}
|
|
202
196
|
else {
|
|
203
197
|
if (amount === undefined) {
|
|
204
|
-
return mcpError('Either `amount` must be specified or `sendMax` must be true.');
|
|
198
|
+
return mcpError('Either `amount` must be specified or `sendMax` must be true.', { type: 'VALIDATION', code: 'MISSING_PARAM', retryable: false, recovery: 'Provide `amount` or set `sendMax` to true.' });
|
|
205
199
|
}
|
|
206
200
|
rawAmount = BigInt(Math.round(amount * 10 ** decimals));
|
|
207
201
|
sendAmount = amount;
|
|
@@ -212,8 +206,8 @@ export function registerTransferTools(server) {
|
|
|
212
206
|
if (currentRaw < rawAmount) {
|
|
213
207
|
const currentHuman = Number(currentRaw) / 10 ** decimals;
|
|
214
208
|
return mcpError(`Insufficient ${tokenSymbol || tokenName} balance. You have ${currentHuman} but are trying to send ${sendAmount}.\n\n` +
|
|
215
|
-
`Wallet: \`${
|
|
216
|
-
`Mint: \`${mintAddress}
|
|
209
|
+
`Wallet: \`${walletAddress}\`\n` +
|
|
210
|
+
`Mint: \`${mintAddress}\``, { type: 'INSUFFICIENT_FUNDS', code: 'LOW_TOKEN', retryable: false, recovery: `You have ${currentHuman} ${tokenSymbol || tokenName} but need ${sendAmount}. Fund the wallet with more tokens.` });
|
|
217
211
|
}
|
|
218
212
|
}
|
|
219
213
|
catch {
|
|
@@ -255,8 +249,9 @@ export function registerTransferTools(server) {
|
|
|
255
249
|
const displayName = tokenSymbol
|
|
256
250
|
? `${sendAmount} ${tokenSymbol} (${tokenName})`
|
|
257
251
|
: `${sendAmount} ${tokenName}`;
|
|
252
|
+
const signerLabel = owsWallet ? `\`${walletAddress}\` (OWS: ${owsWallet})` : `\`${walletAddress}\``;
|
|
258
253
|
return mcpText(`**Token Transfer Sent**\n\n` +
|
|
259
|
-
`- **From:**
|
|
254
|
+
`- **From:** ${signerLabel}\n` +
|
|
260
255
|
`- **To:** \`${recipientAddress}\`\n` +
|
|
261
256
|
`- **Amount:** ${displayName}${sendMax ? ' (max)' : ''}\n` +
|
|
262
257
|
`- **Mint:** \`${mintAddress}\`\n` +
|
package/dist/tools/wallet.js
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { getHeliusClient, hasApiKey } from '../utils/helius.js';
|
|
3
|
-
import { formatAddress,
|
|
3
|
+
import { formatAddress, formatTimestamp } from '../utils/formatters.js';
|
|
4
4
|
import { noApiKeyResponse } from './shared.js';
|
|
5
5
|
import { mcpText, mcpError, validateEnum, handleToolError, http404Error, addressError } from '../utils/errors.js';
|
|
6
6
|
export function registerWalletTools(server) {
|
|
7
7
|
// ─── Get Wallet Identity ───
|
|
8
|
-
server.tool('getWalletIdentity', 'BEST FOR: identifying a single known wallet. PREFER batchWalletIdentity for multiple addresses. Identify known wallets (exchanges, protocols, institutions). Returns name, type, category, tags. Credit cost: 100 credits.', {
|
|
9
|
-
address: z.string().describe('Solana wallet address (base58
|
|
8
|
+
server.tool('getWalletIdentity', 'BEST FOR: identifying a single known wallet. PREFER batchWalletIdentity for multiple addresses. Identify known wallets (exchanges, protocols, institutions). Returns name, type, category, tags. Also accepts SNS/ANS domains (e.g., `toly.sol`, `helius.bonk`) — mainnet only. Credit cost: 100 credits.', {
|
|
9
|
+
address: z.string().describe('Solana wallet address (base58) or SNS/ANS domain (e.g., toly.sol, helius.bonk — mainnet only)')
|
|
10
10
|
}, async ({ address }) => {
|
|
11
11
|
if (!hasApiKey())
|
|
12
12
|
return noApiKeyResponse();
|
|
13
13
|
try {
|
|
14
14
|
const client = getHeliusClient();
|
|
15
|
+
// TODO: drop `any` once helius-sdk types include inputDomain on the identity response
|
|
15
16
|
const data = await client.wallet.getIdentity({ wallet: address });
|
|
16
17
|
const lines = ['**Wallet Identity**', ''];
|
|
17
|
-
|
|
18
|
+
if (data.inputDomain)
|
|
19
|
+
lines.push(`**Input:** ${data.inputDomain}`);
|
|
20
|
+
lines.push(`**Address:** ${formatAddress(data.address || address)}`);
|
|
18
21
|
if (data.name)
|
|
19
22
|
lines.push(`**Name:** ${data.name}`);
|
|
20
23
|
if (data.type)
|
|
@@ -32,25 +35,33 @@ export function registerWalletTools(server) {
|
|
|
32
35
|
}
|
|
33
36
|
});
|
|
34
37
|
// ─── Batch Wallet Identity ───
|
|
35
|
-
server.tool('batchWalletIdentity', 'BEST FOR: identifying multiple wallets at once (up to 100). PREFER getWalletIdentity for a single address. Look up identities for up to 100
|
|
36
|
-
|
|
37
|
-
}, async ({
|
|
38
|
+
server.tool('batchWalletIdentity', 'BEST FOR: identifying multiple wallets at once (up to 100). PREFER getWalletIdentity for a single address. Look up identities for up to 100 entries. Returns names, types, and categories. Entries may be addresses or SNS/ANS domains (mainnet only); unresolved domains are returned with `unresolved: true`. Credit cost: 100 credits.', {
|
|
39
|
+
entries: z.array(z.string()).describe('Array of up to 100 entries — each a base58 address or SNS/ANS domain')
|
|
40
|
+
}, async ({ entries }) => {
|
|
38
41
|
if (!hasApiKey())
|
|
39
42
|
return noApiKeyResponse();
|
|
40
|
-
if (
|
|
41
|
-
return mcpError('
|
|
43
|
+
if (entries.length > 100) {
|
|
44
|
+
return mcpError('Maximum 100 entries per batch request.', { type: 'VALIDATION', code: 'TOO_MANY_ITEMS', retryable: false, recovery: 'Reduce batch to 100 or fewer entries.' });
|
|
42
45
|
}
|
|
43
46
|
try {
|
|
44
47
|
const client = getHeliusClient();
|
|
45
|
-
const results = await client.wallet.getBatchIdentity({ addresses });
|
|
48
|
+
const results = await client.wallet.getBatchIdentity({ addresses: entries });
|
|
46
49
|
if (results.length === 0) {
|
|
47
|
-
return mcpText(`**Batch Identity Lookup** (${
|
|
50
|
+
return mcpText(`**Batch Identity Lookup** (${entries.length} entries)\n\nNo identities found.`);
|
|
48
51
|
}
|
|
49
52
|
const lines = [`**Batch Identity Lookup** (${results.length} results)`, ''];
|
|
53
|
+
// TODO: drop `Array<any>` once helius-sdk types include inputDomain/unresolved on the batch response
|
|
50
54
|
for (const entry of results) {
|
|
55
|
+
if (entry.unresolved) {
|
|
56
|
+
lines.push(`- \`${entry.inputDomain || '?'}\` — Unresolved domain`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
51
59
|
const addr = formatAddress(entry.address || '');
|
|
60
|
+
const subject = entry.inputDomain && entry.address
|
|
61
|
+
? `\`${entry.inputDomain}\` → ${addr}`
|
|
62
|
+
: addr;
|
|
52
63
|
if (entry.name) {
|
|
53
|
-
lines.push(`- **${entry.name}** — ${
|
|
64
|
+
lines.push(`- **${entry.name}** — ${subject}`);
|
|
54
65
|
const details = [];
|
|
55
66
|
if (entry.type)
|
|
56
67
|
details.push(entry.type);
|
|
@@ -60,14 +71,14 @@ export function registerWalletTools(server) {
|
|
|
60
71
|
lines.push(` ${details.join(' | ')}`);
|
|
61
72
|
}
|
|
62
73
|
else {
|
|
63
|
-
lines.push(`- ${
|
|
74
|
+
lines.push(`- ${subject} — Unknown`);
|
|
64
75
|
}
|
|
65
76
|
}
|
|
66
77
|
return mcpText(lines.join('\n'));
|
|
67
78
|
}
|
|
68
79
|
catch (err) {
|
|
69
80
|
return handleToolError(err, 'Error fetching batch identities', [
|
|
70
|
-
addressError('Batch Identity'),
|
|
81
|
+
addressError('Batch Identity', 'Entries must be base58 addresses or SNS/ANS domains.'),
|
|
71
82
|
]);
|
|
72
83
|
}
|
|
73
84
|
});
|
|
@@ -164,7 +175,7 @@ export function registerWalletTools(server) {
|
|
|
164
175
|
transactions.forEach((tx, i) => {
|
|
165
176
|
const time = tx.timestamp ? formatTimestamp(tx.timestamp) : 'N/A';
|
|
166
177
|
const status = tx.error ? 'Failed' : 'Success';
|
|
167
|
-
const fee = tx.fee ?
|
|
178
|
+
const fee = tx.fee != null ? `${tx.fee} SOL` : 'N/A';
|
|
168
179
|
lines.push(`${i + 1}. ${time} — ${status} — Fee: ${fee}`);
|
|
169
180
|
lines.push(` Signature: \`${tx.signature}\``);
|
|
170
181
|
// Balance changes
|
|
@@ -252,7 +263,7 @@ export function registerWalletTools(server) {
|
|
|
252
263
|
if (data.funderType)
|
|
253
264
|
lines.push(`**Type:** ${data.funderType}`);
|
|
254
265
|
if (data.amount !== undefined) {
|
|
255
|
-
lines.push(`**Amount:** ${
|
|
266
|
+
lines.push(`**Amount:** ${data.amount} SOL`);
|
|
256
267
|
}
|
|
257
268
|
if (data.date)
|
|
258
269
|
lines.push(`**Date:** ${data.date}`);
|
package/dist/tools/webhooks.js
CHANGED
|
@@ -3,7 +3,7 @@ import { getHeliusClient, hasApiKey } from '../utils/helius.js';
|
|
|
3
3
|
import { formatAddress } from '../utils/formatters.js';
|
|
4
4
|
import { noApiKeyResponse } from './shared.js';
|
|
5
5
|
import { TRANSACTION_TYPES } from '../types/transaction-types.js';
|
|
6
|
-
import { mcpText, validateEnum, handleToolError, http404Error, http400Error } from '../utils/errors.js';
|
|
6
|
+
import { mcpText, mcpError, validateEnum, handleToolError, http404Error, http400Error } from '../utils/errors.js';
|
|
7
7
|
export function registerWebhookTools(server) {
|
|
8
8
|
server.tool('getAllWebhooks', 'List all active webhooks for your Helius account. Shows webhook IDs, URLs, and monitored addresses. Credit cost: 100 credits/call (management operation).', {}, async () => {
|
|
9
9
|
if (!hasApiKey())
|
|
@@ -77,7 +77,7 @@ export function registerWebhookTools(server) {
|
|
|
77
77
|
if (transactionTypes) {
|
|
78
78
|
const invalid = transactionTypes.filter(t => !TRANSACTION_TYPES.includes(t));
|
|
79
79
|
if (invalid.length > 0) {
|
|
80
|
-
return
|
|
80
|
+
return mcpError(`**Create Webhook Error**\n\nInvalid transaction type(s): ${invalid.join(', ')}. See valid types: https://www.helius.dev/docs/webhooks/transaction-types`, { type: 'VALIDATION', code: 'INVALID_ENUM', retryable: false, recovery: `Remove invalid transaction types: ${invalid.join(', ')}. See https://www.helius.dev/docs/webhooks/transaction-types for valid types.` });
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
try {
|
|
@@ -113,7 +113,7 @@ export function registerWebhookTools(server) {
|
|
|
113
113
|
if (transactionTypes) {
|
|
114
114
|
const invalid = transactionTypes.filter(t => !TRANSACTION_TYPES.includes(t));
|
|
115
115
|
if (invalid.length > 0) {
|
|
116
|
-
return
|
|
116
|
+
return mcpError(`**Update Webhook Error**\n\nInvalid transaction type(s): ${invalid.join(', ')}. See valid types: https://www.helius.dev/docs/webhooks/transaction-types`, { type: 'VALIDATION', code: 'INVALID_ENUM', retryable: false, recovery: `Remove invalid transaction types: ${invalid.join(', ')}. See https://www.helius.dev/docs/webhooks/transaction-types for valid types.` });
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
try {
|