helius-mcp 0.5.3 → 1.2.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 +52 -0
- package/LICENSE +1 -1
- package/README.md +97 -21
- package/dist/http.d.ts +1 -0
- package/dist/http.js +2 -0
- package/dist/index.js +93 -2
- package/dist/scripts/validate-catalog.d.ts +13 -0
- package/dist/scripts/validate-catalog.js +76 -0
- package/dist/tools/accounts.js +114 -204
- package/dist/tools/assets.js +109 -123
- package/dist/tools/auth.d.ts +2 -0
- package/dist/tools/auth.js +459 -0
- package/dist/tools/balance.js +28 -32
- package/dist/tools/blocks.js +68 -87
- package/dist/tools/config.js +18 -79
- package/dist/tools/das-extras.js +56 -41
- package/dist/tools/docs.js +12 -54
- package/dist/tools/enhanced-websockets.js +104 -74
- package/dist/tools/fees.js +42 -61
- package/dist/tools/guides.js +126 -515
- package/dist/tools/index.js +50 -2
- package/dist/tools/laserstream.js +107 -53
- package/dist/tools/network.js +47 -69
- package/dist/tools/plans.d.ts +21 -0
- package/dist/tools/plans.js +105 -246
- package/dist/tools/product-catalog.d.ts +10 -0
- package/dist/tools/product-catalog.js +123 -0
- package/dist/tools/recommend.d.ts +4 -0
- package/dist/tools/recommend.js +233 -0
- package/dist/tools/shared.js +8 -3
- package/dist/tools/solana-knowledge.d.ts +2 -0
- package/dist/tools/solana-knowledge.js +544 -0
- package/dist/tools/tokens.js +17 -18
- package/dist/tools/transactions.js +232 -302
- package/dist/tools/transfers.d.ts +2 -0
- package/dist/tools/transfers.js +270 -0
- package/dist/tools/wallet.js +175 -177
- package/dist/tools/webhooks.js +80 -82
- package/dist/types/transaction-types.d.ts +1 -1
- package/dist/types/transaction-types.js +2 -1
- package/dist/utils/config.d.ts +27 -0
- package/dist/utils/config.js +76 -0
- package/dist/utils/docs.d.ts +24 -0
- package/dist/utils/docs.js +72 -0
- package/dist/utils/errors.d.ts +32 -0
- package/dist/utils/errors.js +157 -0
- package/dist/utils/feedback.d.ts +16 -0
- package/dist/utils/feedback.js +87 -0
- package/dist/utils/formatters.d.ts +0 -1
- package/dist/utils/formatters.js +0 -3
- package/dist/utils/helius.d.ts +15 -5
- package/dist/utils/helius.js +52 -45
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +17 -7
- package/system-prompts/helius/claude.system.md +170 -0
- package/system-prompts/helius/full.md +2868 -0
- package/system-prompts/helius/openai.developer.md +170 -0
- package/system-prompts/helius-dflow/claude.system.md +290 -0
- package/system-prompts/helius-dflow/full.md +3647 -0
- package/system-prompts/helius-dflow/openai.developer.md +290 -0
- package/system-prompts/helius-phantom/claude.system.md +348 -0
- package/system-prompts/helius-phantom/full.md +5472 -0
- package/system-prompts/helius-phantom/openai.developer.md +348 -0
- package/system-prompts/svm/claude.system.md +174 -0
- package/system-prompts/svm/full.md +699 -0
- package/system-prompts/svm/openai.developer.md +174 -0
package/dist/tools/assets.js
CHANGED
|
@@ -2,9 +2,10 @@ import { z } from 'zod';
|
|
|
2
2
|
import { getHeliusClient, hasApiKey } from '../utils/helius.js';
|
|
3
3
|
import { formatAddress } from '../utils/formatters.js';
|
|
4
4
|
import { noApiKeyResponse } from './shared.js';
|
|
5
|
+
import { mcpText, handleToolError, addressError, paginationError, notFoundError } from '../utils/errors.js';
|
|
5
6
|
export function registerAssetTools(server) {
|
|
6
7
|
// Get Assets by Owner (NFTs and tokens via DAS)
|
|
7
|
-
server.tool('getAssetsByOwner', '
|
|
8
|
+
server.tool('getAssetsByOwner', 'BEST FOR: listing NFTs/digital assets owned by a wallet. PREFER getTokenBalances for fungible tokens, getAsset for a specific mint. Get all NFTs and digital assets owned by a wallet. Returns asset names, types, and mint addresses. Supports regular NFTs and cNFTs. Credit cost: 10 credits (DAS API).', {
|
|
8
9
|
address: z.string().describe('Solana wallet address (base58 encoded)'),
|
|
9
10
|
limit: z.number().optional().default(20).describe('Number of assets to return (default 20). Increase for wallets with many NFTs.'),
|
|
10
11
|
page: z.number().optional().default(1).describe('Page number (starts at 1)')
|
|
@@ -12,18 +13,23 @@ export function registerAssetTools(server) {
|
|
|
12
13
|
if (!hasApiKey())
|
|
13
14
|
return noApiKeyResponse();
|
|
14
15
|
const helius = getHeliusClient();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
let response;
|
|
17
|
+
try {
|
|
18
|
+
response = await helius.getAssetsByOwner({
|
|
19
|
+
ownerAddress: address,
|
|
20
|
+
page,
|
|
21
|
+
limit
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
const header = `Assets for ${formatAddress(address)}`;
|
|
26
|
+
return handleToolError(err, 'Error fetching assets', [
|
|
27
|
+
addressError(header, 'Invalid Solana address. Please provide a valid base58-encoded wallet address.'),
|
|
28
|
+
paginationError(header),
|
|
29
|
+
]);
|
|
30
|
+
}
|
|
20
31
|
if (!response.items || response.items.length === 0) {
|
|
21
|
-
return {
|
|
22
|
-
content: [{
|
|
23
|
-
type: 'text',
|
|
24
|
-
text: `**Assets for ${formatAddress(address)}**\n\nNo assets found.`
|
|
25
|
-
}]
|
|
26
|
-
};
|
|
32
|
+
return mcpText(`**Assets for ${formatAddress(address)}**\n\nNo assets found.`);
|
|
27
33
|
}
|
|
28
34
|
const items = response.items;
|
|
29
35
|
// Enrich assets missing name/symbol
|
|
@@ -51,57 +57,41 @@ export function registerAssetTools(server) {
|
|
|
51
57
|
lines.push(`- **${name}** (${asset.interface})`);
|
|
52
58
|
lines.push(` ID: ${formatAddress(asset.id)}`);
|
|
53
59
|
});
|
|
54
|
-
return
|
|
55
|
-
content: [{
|
|
56
|
-
type: 'text',
|
|
57
|
-
text: lines.join('\n')
|
|
58
|
-
}]
|
|
59
|
-
};
|
|
60
|
+
return mcpText(lines.join('\n'));
|
|
60
61
|
});
|
|
61
62
|
// Get Asset (single or batch)
|
|
62
|
-
server.tool('getAsset', 'Get detailed information about one or more NFTs/tokens by mint address. For a single asset: returns name, symbol, description, image, owner, creators, authorities, supply, decimals, royalties, mutability. For batch: pass an array of up to 1000 mint addresses in "ids" for fast bulk lookups. Use this to find who created/deployed a token, verify token details, or get full NFT metadata.', {
|
|
63
|
+
server.tool('getAsset', 'Get detailed information about one or more NFTs/tokens by mint address. For a single asset: returns name, symbol, description, image, owner, creators, authorities, supply, decimals, royalties, mutability. For batch: pass an array of up to 1000 mint addresses in "ids" for fast bulk lookups. Use this to find who created/deployed a token, verify token details, or get full NFT metadata. DAS API (10 credits/call).', {
|
|
63
64
|
id: z.string().optional().describe('Single asset mint address (base58 encoded). Use this OR ids, not both.'),
|
|
64
|
-
ids: z.array(z.string()).optional().describe('Array of asset mint addresses for batch lookup (up to 1000). Use this OR id, not both.')
|
|
65
|
+
ids: z.array(z.string()).optional().describe('Array of asset mint addresses for batch lookup (base58 encoded, up to 1000). Use this OR id, not both.')
|
|
65
66
|
}, async ({ id, ids }) => {
|
|
66
67
|
if (!hasApiKey())
|
|
67
68
|
return noApiKeyResponse();
|
|
68
69
|
const helius = getHeliusClient();
|
|
69
70
|
// Validate: must provide exactly one of id or ids
|
|
70
71
|
if (!id && (!ids || ids.length === 0)) {
|
|
71
|
-
return
|
|
72
|
-
content: [{
|
|
73
|
-
type: 'text',
|
|
74
|
-
text: `**Error:** Provide either "id" (single asset) or "ids" (batch of up to 1000).`
|
|
75
|
-
}]
|
|
76
|
-
};
|
|
72
|
+
return mcpText(`**Error:** Provide either "id" (single asset) or "ids" (batch of up to 1000).`);
|
|
77
73
|
}
|
|
78
74
|
if (id && ids && ids.length > 0) {
|
|
79
|
-
return
|
|
80
|
-
content: [{
|
|
81
|
-
type: 'text',
|
|
82
|
-
text: `**Error:** Provide either "id" or "ids", not both.`
|
|
83
|
-
}]
|
|
84
|
-
};
|
|
75
|
+
return mcpText(`**Error:** Provide either "id" or "ids", not both.`);
|
|
85
76
|
}
|
|
86
77
|
// --- Batch mode ---
|
|
87
78
|
if (ids && ids.length > 0) {
|
|
88
79
|
if (ids.length > 1000) {
|
|
89
|
-
return {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
80
|
+
return mcpText(`**Error:** Maximum 1000 assets per batch. You provided ${ids.length}.`);
|
|
81
|
+
}
|
|
82
|
+
let assets;
|
|
83
|
+
try {
|
|
84
|
+
assets = await helius.getAssetBatch({ ids });
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
return handleToolError(err, 'Error fetching assets', [
|
|
88
|
+
addressError('Asset Batch Error', 'One or more provided IDs are not valid Solana addresses. Please check the mint addresses and try again.'),
|
|
89
|
+
notFoundError('Asset Batch Results', 'One or more assets were not found. Please check the mint addresses and try again.'),
|
|
90
|
+
]);
|
|
95
91
|
}
|
|
96
|
-
const assets = await helius.getAssetBatch({ ids });
|
|
97
92
|
const items = (assets || []);
|
|
98
93
|
if (items.length === 0) {
|
|
99
|
-
return
|
|
100
|
-
content: [{
|
|
101
|
-
type: 'text',
|
|
102
|
-
text: `**Asset Batch Results**\n\nNo assets found.`
|
|
103
|
-
}]
|
|
104
|
-
};
|
|
94
|
+
return mcpText(`**Asset Batch Results**\n\nNo assets found.`);
|
|
105
95
|
}
|
|
106
96
|
const lines = [`**Asset Batch Results** (${items.length} assets)`, ''];
|
|
107
97
|
items.forEach((asset) => {
|
|
@@ -114,22 +104,22 @@ export function registerAssetTools(server) {
|
|
|
114
104
|
lines.push(` Owner: ${formatAddress(asset.ownership.owner)}`);
|
|
115
105
|
}
|
|
116
106
|
});
|
|
117
|
-
return
|
|
118
|
-
content: [{
|
|
119
|
-
type: 'text',
|
|
120
|
-
text: lines.join('\n')
|
|
121
|
-
}]
|
|
122
|
-
};
|
|
107
|
+
return mcpText(lines.join('\n'));
|
|
123
108
|
}
|
|
124
109
|
// --- Single asset mode ---
|
|
125
|
-
|
|
110
|
+
let asset;
|
|
111
|
+
try {
|
|
112
|
+
asset = await helius.getAsset({ id: id });
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
const header = `Asset ${formatAddress(id)}`;
|
|
116
|
+
return handleToolError(err, 'Error fetching asset', [
|
|
117
|
+
addressError(header, 'Invalid Solana address. Please provide a valid base58-encoded mint address.'),
|
|
118
|
+
notFoundError(header, 'Asset not found. This mint address does not exist or has not been indexed.'),
|
|
119
|
+
]);
|
|
120
|
+
}
|
|
126
121
|
if (!asset) {
|
|
127
|
-
return {
|
|
128
|
-
content: [{
|
|
129
|
-
type: 'text',
|
|
130
|
-
text: `Asset ${formatAddress(id)} not found.`
|
|
131
|
-
}]
|
|
132
|
-
};
|
|
122
|
+
return mcpText(`Asset ${formatAddress(id)} not found.`);
|
|
133
123
|
}
|
|
134
124
|
const metadata = asset.content?.metadata;
|
|
135
125
|
const tokenInfo = asset.token_info;
|
|
@@ -204,18 +194,13 @@ export function registerAssetTools(server) {
|
|
|
204
194
|
if (flags.length > 0) {
|
|
205
195
|
lines.push(`**Status:** ${flags.join(', ')}`);
|
|
206
196
|
}
|
|
207
|
-
return
|
|
208
|
-
content: [{
|
|
209
|
-
type: 'text',
|
|
210
|
-
text: lines.join('\n')
|
|
211
|
-
}]
|
|
212
|
-
};
|
|
197
|
+
return mcpText(lines.join('\n'));
|
|
213
198
|
});
|
|
214
199
|
// Search Assets — unified search with smart routing for creator/authority/general queries
|
|
215
|
-
server.tool('searchAssets', '
|
|
216
|
-
ownerAddress: z.string().optional().describe('Filter by owner wallet address'),
|
|
217
|
-
creatorAddress: z.string().optional().describe('Filter by creator address'),
|
|
218
|
-
authorityAddress: z.string().optional().describe('Filter by authority address (finds assets this address has update/freeze control over)'),
|
|
200
|
+
server.tool('searchAssets', 'BEST FOR: filtered multi-criteria asset search. PREFER getAssetsByOwner for simple wallet NFT listing, getAssetsByGroup for collection browsing. Search digital assets by owner, creator, authority, name, compression, burnt, or frozen status. Also supports getAssetsByCreator and getAssetsByAuthority queries. Credit cost: 10 credits (DAS API).', {
|
|
201
|
+
ownerAddress: z.string().optional().describe('Filter by owner wallet address (base58 encoded)'),
|
|
202
|
+
creatorAddress: z.string().optional().describe('Filter by creator address (base58 encoded)'),
|
|
203
|
+
authorityAddress: z.string().optional().describe('Filter by authority address (base58 encoded, finds assets this address has update/freeze control over)'),
|
|
219
204
|
onlyVerified: z.boolean().optional().default(false).describe('Only return assets where the creator is verified (used with creatorAddress)'),
|
|
220
205
|
name: z.string().optional().describe('Search by asset name (partial match). Requires ownerAddress to be provided.'),
|
|
221
206
|
compressed: z.boolean().optional().describe('Filter for compressed NFTs (cNFTs) only'),
|
|
@@ -231,32 +216,45 @@ export function registerAssetTools(server) {
|
|
|
231
216
|
let headerLabel;
|
|
232
217
|
// Smart routing: authority-only → getAssetsByAuthority
|
|
233
218
|
if (authorityAddress && !creatorAddress && !ownerAddress && !name && compressed === undefined && burnt === undefined && frozen === undefined) {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
219
|
+
try {
|
|
220
|
+
response = await helius.getAssetsByAuthority({
|
|
221
|
+
authorityAddress,
|
|
222
|
+
page,
|
|
223
|
+
limit
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
227
|
+
const header = `Assets by Authority ${formatAddress(authorityAddress)}`;
|
|
228
|
+
return handleToolError(err, 'Error searching assets', [
|
|
229
|
+
addressError(header, 'Invalid Solana address. Please provide a valid base58-encoded address.'),
|
|
230
|
+
paginationError(header),
|
|
231
|
+
]);
|
|
232
|
+
}
|
|
239
233
|
headerLabel = `Assets by Authority ${formatAddress(authorityAddress)}`;
|
|
240
234
|
}
|
|
241
235
|
// Smart routing: creator (with optional onlyVerified) → getAssetsByCreator
|
|
242
236
|
else if (creatorAddress && !authorityAddress && !ownerAddress && !name && compressed === undefined && burnt === undefined && frozen === undefined) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
237
|
+
try {
|
|
238
|
+
response = await helius.getAssetsByCreator({
|
|
239
|
+
creatorAddress,
|
|
240
|
+
onlyVerified,
|
|
241
|
+
page,
|
|
242
|
+
limit
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
const header = `Assets by Creator ${formatAddress(creatorAddress)}`;
|
|
247
|
+
return handleToolError(err, 'Error searching assets', [
|
|
248
|
+
addressError(header, 'Invalid Solana address. Please provide a valid base58-encoded address.'),
|
|
249
|
+
paginationError(header),
|
|
250
|
+
]);
|
|
251
|
+
}
|
|
249
252
|
headerLabel = `Assets by Creator ${formatAddress(creatorAddress)}`;
|
|
250
253
|
}
|
|
251
254
|
// General search → searchAssets
|
|
252
255
|
else {
|
|
253
256
|
if (name && !ownerAddress) {
|
|
254
|
-
return {
|
|
255
|
-
content: [{
|
|
256
|
-
type: 'text',
|
|
257
|
-
text: `**Search Error:** Searching by name requires an owner address. Please also provide \`ownerAddress\` to search for assets named "${name}".`
|
|
258
|
-
}]
|
|
259
|
-
};
|
|
257
|
+
return mcpText(`**Search Error:** Searching by name requires an owner address. Please also provide \`ownerAddress\` to search for assets named "${name}".`);
|
|
260
258
|
}
|
|
261
259
|
const params = { page, limit };
|
|
262
260
|
if (ownerAddress)
|
|
@@ -275,23 +273,16 @@ export function registerAssetTools(server) {
|
|
|
275
273
|
response = await helius.searchAssets(params);
|
|
276
274
|
}
|
|
277
275
|
catch (err) {
|
|
278
|
-
return
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
}]
|
|
283
|
-
};
|
|
276
|
+
return handleToolError(err, 'Error searching assets', [
|
|
277
|
+
addressError('Asset Search'),
|
|
278
|
+
paginationError('Asset Search'),
|
|
279
|
+
]);
|
|
284
280
|
}
|
|
285
281
|
headerLabel = 'Asset Search Results';
|
|
286
282
|
}
|
|
287
283
|
const items = (response.items || []);
|
|
288
284
|
if (items.length === 0) {
|
|
289
|
-
return {
|
|
290
|
-
content: [{
|
|
291
|
-
type: 'text',
|
|
292
|
-
text: `**${headerLabel}**\n\nNo assets found matching the criteria.`
|
|
293
|
-
}]
|
|
294
|
-
};
|
|
285
|
+
return mcpText(`**${headerLabel}**\n\nNo assets found matching the criteria.`);
|
|
295
286
|
}
|
|
296
287
|
const lines = [`**${headerLabel}** (${response.total || items.length} total, page ${page})`, ''];
|
|
297
288
|
items.forEach((asset) => {
|
|
@@ -310,37 +301,37 @@ export function registerAssetTools(server) {
|
|
|
310
301
|
lines.push(` Owner: ${formatAddress(asset.ownership.owner)}`);
|
|
311
302
|
}
|
|
312
303
|
});
|
|
313
|
-
return
|
|
314
|
-
content: [{
|
|
315
|
-
type: 'text',
|
|
316
|
-
text: lines.join('\n')
|
|
317
|
-
}]
|
|
318
|
-
};
|
|
304
|
+
return mcpText(lines.join('\n'));
|
|
319
305
|
});
|
|
320
306
|
// Get Assets by Group (Collection)
|
|
321
|
-
server.tool('getAssetsByGroup', 'Get all NFTs in a collection by group key/value. The groupKey is usually "collection" and groupValue is the collection mint address. Use this to browse all NFTs in a specific collection.', {
|
|
307
|
+
server.tool('getAssetsByGroup', 'Get all NFTs in a collection by group key/value. The groupKey is usually "collection" and groupValue is the collection mint address. Use this to browse all NFTs in a specific collection. DAS API (10 credits/call).', {
|
|
322
308
|
groupKey: z.string().describe('Group key - usually "collection"'),
|
|
323
|
-
groupValue: z.string().describe('Group value - usually the collection mint address'),
|
|
309
|
+
groupValue: z.string().describe('Group value - usually the collection mint address (base58 encoded)'),
|
|
324
310
|
page: z.number().optional().default(1).describe('Page number (starts at 1)'),
|
|
325
311
|
limit: z.number().optional().default(20).describe('Results per page (max 1000)')
|
|
326
312
|
}, async ({ groupKey, groupValue, page, limit }) => {
|
|
327
313
|
if (!hasApiKey())
|
|
328
314
|
return noApiKeyResponse();
|
|
329
315
|
const helius = getHeliusClient();
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
316
|
+
let response;
|
|
317
|
+
try {
|
|
318
|
+
response = await helius.getAssetsByGroup({
|
|
319
|
+
groupKey,
|
|
320
|
+
groupValue,
|
|
321
|
+
page,
|
|
322
|
+
limit
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
catch (err) {
|
|
326
|
+
const header = `Assets in Group ${groupKey}=${formatAddress(groupValue)}`;
|
|
327
|
+
return handleToolError(err, 'Error fetching group assets', [
|
|
328
|
+
addressError(header, 'Invalid Solana address. Please provide a valid base58-encoded address.'),
|
|
329
|
+
paginationError(header),
|
|
330
|
+
]);
|
|
331
|
+
}
|
|
336
332
|
const items = (response.items || []);
|
|
337
333
|
if (items.length === 0) {
|
|
338
|
-
return {
|
|
339
|
-
content: [{
|
|
340
|
-
type: 'text',
|
|
341
|
-
text: `**Assets in Group ${groupKey}=${formatAddress(groupValue)}**\n\nNo assets found.`
|
|
342
|
-
}]
|
|
343
|
-
};
|
|
334
|
+
return mcpText(`**Assets in Group ${groupKey}=${formatAddress(groupValue)}**\n\nNo assets found.`);
|
|
344
335
|
}
|
|
345
336
|
const lines = [`**Assets in Group ${groupKey}=${formatAddress(groupValue)}** (${response.total || items.length} total, page ${page})`, ''];
|
|
346
337
|
items.forEach((asset) => {
|
|
@@ -351,11 +342,6 @@ export function registerAssetTools(server) {
|
|
|
351
342
|
lines.push(` Owner: ${formatAddress(asset.ownership.owner)}`);
|
|
352
343
|
}
|
|
353
344
|
});
|
|
354
|
-
return
|
|
355
|
-
content: [{
|
|
356
|
-
type: 'text',
|
|
357
|
-
text: lines.join('\n')
|
|
358
|
-
}]
|
|
359
|
-
};
|
|
345
|
+
return mcpText(lines.join('\n'));
|
|
360
346
|
});
|
|
361
347
|
}
|