helius-mcp 1.3.0 → 2.1.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 +151 -79
- package/LICENSE +21 -21
- package/README.md +144 -132
- 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 +125 -0
- package/dist/router/catalog.d.ts +6 -0
- package/dist/router/catalog.js +394 -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 +68 -0
- package/dist/router/responses.d.ts +29 -0
- package/dist/router/responses.js +186 -0
- package/dist/router/schemas.d.ts +224 -0
- package/dist/router/schemas.js +204 -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 -319
- 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 +10 -4
- 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 +52 -17
- 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 +21 -13
- 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 +256 -3
- package/dist/tools/transfers.js +38 -43
- package/dist/tools/wallet.js +77 -17
- 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 +25 -14
- package/dist/utils/ows.d.ts +74 -0
- package/dist/utils/ows.js +155 -0
- package/dist/utils/resilience.d.ts +39 -0
- package/dist/utils/resilience.js +130 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +63 -64
- package/system-prompts/helius/claude.system.md +200 -170
- package/system-prompts/helius/full.md +3236 -2869
- package/system-prompts/helius/openai.developer.md +200 -170
- package/system-prompts/helius-dflow/claude.system.md +324 -290
- package/system-prompts/helius-dflow/full.md +4160 -3648
- package/system-prompts/helius-dflow/openai.developer.md +324 -290
- package/system-prompts/helius-jupiter/claude.system.md +333 -0
- package/system-prompts/helius-jupiter/full.md +5133 -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 +345 -333
- package/system-prompts/helius-phantom/full.md +5649 -5473
- package/system-prompts/helius-phantom/openai.developer.md +345 -333
- package/system-prompts/svm/claude.system.md +159 -159
- package/system-prompts/svm/full.md +631 -631
- package/system-prompts/svm/openai.developer.md +159 -159
- package/dist/scripts/test-htmltotext.d.ts +0 -5
- package/dist/scripts/test-htmltotext.js +0 -67
- package/dist/scripts/test-solana-knowledge.d.ts +0 -9
- package/dist/scripts/test-solana-knowledge.js +0 -272
- package/dist/scripts/validate-templates.d.ts +0 -12
- package/dist/scripts/validate-templates.js +0 -94
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
import { HELIUS_ACCOUNT_ACTIONS, HELIUS_ASSET_ACTIONS, HELIUS_CHAIN_ACTIONS, HELIUS_COMPRESSION_ACTIONS, HELIUS_KNOWLEDGE_ACTIONS, HELIUS_STREAMING_ACTIONS, HELIUS_TRANSACTION_ACTIONS, HELIUS_WALLET_ACTIONS, HELIUS_WRITE_ACTIONS, ACTION_NAMES, } from './actions.js';
|
|
2
|
+
import { findPublicToolForAction } from './action-groups.js';
|
|
3
|
+
const always = { kind: 'always' };
|
|
4
|
+
function gate(minimumPlan, label, variants) {
|
|
5
|
+
return {
|
|
6
|
+
baseline: {
|
|
7
|
+
id: `${minimumPlan}:${label}`,
|
|
8
|
+
minimumPlan,
|
|
9
|
+
predicate: always,
|
|
10
|
+
label,
|
|
11
|
+
},
|
|
12
|
+
...(variants ? { variants } : {}),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function makeEntry(action, overrides = {}) {
|
|
16
|
+
const publicTool = overrides.publicTool ?? findPublicToolForAction(action);
|
|
17
|
+
const responseFamily = overrides.responseFamily ?? 'record';
|
|
18
|
+
const defaultDetail = overrides.defaultDetail ?? (responseFamily === 'scalar' || responseFamily === 'mutationReceipt'
|
|
19
|
+
? 'full'
|
|
20
|
+
: responseFamily === 'document' || responseFamily === 'streamingConfig' || responseFamily === 'catalog'
|
|
21
|
+
? 'summary'
|
|
22
|
+
: 'standard');
|
|
23
|
+
return {
|
|
24
|
+
action,
|
|
25
|
+
publicTool,
|
|
26
|
+
aliases: overrides.aliases ?? [],
|
|
27
|
+
authRequirement: overrides.authRequirement ?? 'apiKey',
|
|
28
|
+
capabilityGate: overrides.capabilityGate ?? gate('agent', 'Available on every plan'),
|
|
29
|
+
mutability: overrides.mutability ?? (publicTool === 'heliusWrite' ? 'write' : 'read'),
|
|
30
|
+
responseFamily,
|
|
31
|
+
defaultDetail,
|
|
32
|
+
handleEligibility: overrides.handleEligibility ?? !['scalar', 'mutationReceipt'].includes(responseFamily),
|
|
33
|
+
normalizer: overrides.normalizer ?? responseFamily,
|
|
34
|
+
continuationModel: overrides.continuationModel ?? 'none',
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function addEntries(target, actions, overrides = {}) {
|
|
38
|
+
for (const action of actions) {
|
|
39
|
+
target[action] = makeEntry(action, overrides);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const catalog = {};
|
|
43
|
+
addEntries(catalog, HELIUS_ACCOUNT_ACTIONS, {
|
|
44
|
+
authRequirement: 'none',
|
|
45
|
+
capabilityGate: gate('agent', 'Account setup and plan metadata'),
|
|
46
|
+
responseFamily: 'record',
|
|
47
|
+
});
|
|
48
|
+
addEntries(catalog, HELIUS_WALLET_ACTIONS, {
|
|
49
|
+
capabilityGate: gate('agent', 'Wallet data'),
|
|
50
|
+
responseFamily: 'record',
|
|
51
|
+
});
|
|
52
|
+
addEntries(catalog, HELIUS_ASSET_ACTIONS, {
|
|
53
|
+
capabilityGate: gate('agent', 'Asset and DAS queries'),
|
|
54
|
+
responseFamily: 'record',
|
|
55
|
+
});
|
|
56
|
+
addEntries(catalog, HELIUS_TRANSACTION_ACTIONS, {
|
|
57
|
+
capabilityGate: gate('agent', 'Transaction parsing and history'),
|
|
58
|
+
responseFamily: 'history',
|
|
59
|
+
});
|
|
60
|
+
addEntries(catalog, HELIUS_CHAIN_ACTIONS, {
|
|
61
|
+
capabilityGate: gate('agent', 'Core chain state'),
|
|
62
|
+
responseFamily: 'record',
|
|
63
|
+
});
|
|
64
|
+
addEntries(catalog, HELIUS_STREAMING_ACTIONS, {
|
|
65
|
+
capabilityGate: gate('agent', 'Streaming and webhook operations'),
|
|
66
|
+
responseFamily: 'record',
|
|
67
|
+
});
|
|
68
|
+
addEntries(catalog, HELIUS_KNOWLEDGE_ACTIONS, {
|
|
69
|
+
authRequirement: 'none',
|
|
70
|
+
capabilityGate: gate('agent', 'Documentation and knowledge'),
|
|
71
|
+
responseFamily: 'document',
|
|
72
|
+
});
|
|
73
|
+
addEntries(catalog, HELIUS_WRITE_ACTIONS, {
|
|
74
|
+
authRequirement: 'signer',
|
|
75
|
+
capabilityGate: gate('agent', 'Transaction sending and staking'),
|
|
76
|
+
mutability: 'write',
|
|
77
|
+
responseFamily: 'mutationReceipt',
|
|
78
|
+
});
|
|
79
|
+
addEntries(catalog, HELIUS_COMPRESSION_ACTIONS, {
|
|
80
|
+
capabilityGate: gate('agent', 'Compression state queries'),
|
|
81
|
+
responseFamily: 'record',
|
|
82
|
+
});
|
|
83
|
+
const developerWalletGate = gate('developer', 'Developer plan or higher');
|
|
84
|
+
const businessStreamingGate = gate('business', 'Business plan or higher');
|
|
85
|
+
const professionalLaserstreamGate = gate('developer', 'Developer plan on devnet', [
|
|
86
|
+
{
|
|
87
|
+
id: 'professional:laserstream-mainnet',
|
|
88
|
+
minimumPlan: 'professional',
|
|
89
|
+
predicate: { kind: 'network', oneOf: ['mainnet-beta'] },
|
|
90
|
+
label: 'Professional plan on mainnet',
|
|
91
|
+
},
|
|
92
|
+
]);
|
|
93
|
+
const scalarActions = [
|
|
94
|
+
'getBalance',
|
|
95
|
+
'getAccountPlan',
|
|
96
|
+
'getNetworkStatus',
|
|
97
|
+
'getPriorityFeeEstimate',
|
|
98
|
+
'getCompressedBalance',
|
|
99
|
+
'getCompressedBalanceByOwner',
|
|
100
|
+
'getCompressedTokenAccountBalance',
|
|
101
|
+
'getIndexerHealth',
|
|
102
|
+
'getIndexerSlot',
|
|
103
|
+
'getWithdrawableAmount',
|
|
104
|
+
];
|
|
105
|
+
for (const action of scalarActions) {
|
|
106
|
+
catalog[action] = makeEntry(action, {
|
|
107
|
+
...catalog[action],
|
|
108
|
+
responseFamily: 'scalar',
|
|
109
|
+
defaultDetail: 'full',
|
|
110
|
+
handleEligibility: false,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const mutationActions = [
|
|
114
|
+
'createWebhook',
|
|
115
|
+
'updateWebhook',
|
|
116
|
+
'deleteWebhook',
|
|
117
|
+
'transferSol',
|
|
118
|
+
'transferToken',
|
|
119
|
+
'stakeSOL',
|
|
120
|
+
'unstakeSOL',
|
|
121
|
+
'withdrawStake',
|
|
122
|
+
];
|
|
123
|
+
for (const action of mutationActions) {
|
|
124
|
+
catalog[action] = makeEntry(action, {
|
|
125
|
+
...catalog[action],
|
|
126
|
+
responseFamily: 'mutationReceipt',
|
|
127
|
+
defaultDetail: 'full',
|
|
128
|
+
handleEligibility: false,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const listActions = [
|
|
132
|
+
'getTokenBalances',
|
|
133
|
+
'getWalletBalances',
|
|
134
|
+
'getWalletHistory',
|
|
135
|
+
'getWalletTransfers',
|
|
136
|
+
'batchWalletIdentity',
|
|
137
|
+
'getAssetsByOwner',
|
|
138
|
+
'searchAssets',
|
|
139
|
+
'getAssetsByGroup',
|
|
140
|
+
'getNftEditions',
|
|
141
|
+
'getTokenHolders',
|
|
142
|
+
'getTokenAccounts',
|
|
143
|
+
'getProgramAccounts',
|
|
144
|
+
'getStakeAccounts',
|
|
145
|
+
'getAllWebhooks',
|
|
146
|
+
'listHeliusDocTopics',
|
|
147
|
+
'listSIMDs',
|
|
148
|
+
'getCompressedAccountsByOwner',
|
|
149
|
+
'getMultipleCompressedAccounts',
|
|
150
|
+
'getCompressedMintTokenHolders',
|
|
151
|
+
'getCompressedTokenAccountsByOwner',
|
|
152
|
+
'getCompressedTokenAccountsByDelegate',
|
|
153
|
+
'getCompressedTokenBalancesByOwnerV2',
|
|
154
|
+
];
|
|
155
|
+
for (const action of listActions) {
|
|
156
|
+
catalog[action] = makeEntry(action, {
|
|
157
|
+
...catalog[action],
|
|
158
|
+
responseFamily: 'list',
|
|
159
|
+
defaultDetail: 'standard',
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
const historyActions = [
|
|
163
|
+
'parseTransactions',
|
|
164
|
+
'getTransactionHistory',
|
|
165
|
+
'getSignaturesForAsset',
|
|
166
|
+
'getCompressionSignaturesForAccount',
|
|
167
|
+
'getCompressionSignaturesForAddress',
|
|
168
|
+
'getCompressionSignaturesForOwner',
|
|
169
|
+
'getCompressionSignaturesForTokenOwner',
|
|
170
|
+
'getLatestCompressionSignatures',
|
|
171
|
+
'getLatestNonVotingSignatures',
|
|
172
|
+
];
|
|
173
|
+
for (const action of historyActions) {
|
|
174
|
+
catalog[action] = makeEntry(action, {
|
|
175
|
+
...catalog[action],
|
|
176
|
+
responseFamily: 'history',
|
|
177
|
+
defaultDetail: 'standard',
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
const documentActions = [
|
|
181
|
+
'lookupHeliusDocs',
|
|
182
|
+
'getHeliusCreditsInfo',
|
|
183
|
+
'getRateLimitInfo',
|
|
184
|
+
'getSIMD',
|
|
185
|
+
'searchSolanaDocs',
|
|
186
|
+
'readSolanaSourceFile',
|
|
187
|
+
'fetchHeliusBlog',
|
|
188
|
+
'getSenderInfo',
|
|
189
|
+
'getWebhookGuide',
|
|
190
|
+
'getLatencyComparison',
|
|
191
|
+
'getEnhancedWebSocketInfo',
|
|
192
|
+
'getLaserstreamInfo',
|
|
193
|
+
'getPumpFunGuide',
|
|
194
|
+
];
|
|
195
|
+
for (const action of documentActions) {
|
|
196
|
+
catalog[action] = makeEntry(action, {
|
|
197
|
+
...catalog[action],
|
|
198
|
+
responseFamily: 'document',
|
|
199
|
+
defaultDetail: 'summary',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
catalog.recommendStack = makeEntry('recommendStack', {
|
|
203
|
+
responseFamily: 'catalog',
|
|
204
|
+
defaultDetail: 'summary',
|
|
205
|
+
});
|
|
206
|
+
for (const action of ['transactionSubscribe', 'accountSubscribe', 'laserstreamSubscribe']) {
|
|
207
|
+
catalog[action] = makeEntry(action, {
|
|
208
|
+
...catalog[action],
|
|
209
|
+
responseFamily: 'streamingConfig',
|
|
210
|
+
defaultDetail: 'summary',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
catalog.setHeliusApiKey = makeEntry('setHeliusApiKey', {
|
|
214
|
+
authRequirement: 'none',
|
|
215
|
+
capabilityGate: gate('agent', 'API key configuration'),
|
|
216
|
+
responseFamily: 'record',
|
|
217
|
+
defaultDetail: 'standard',
|
|
218
|
+
handleEligibility: false,
|
|
219
|
+
});
|
|
220
|
+
catalog.generateKeypair = makeEntry('generateKeypair', {
|
|
221
|
+
authRequirement: 'none',
|
|
222
|
+
capabilityGate: gate('agent', 'Local keypair generation'),
|
|
223
|
+
responseFamily: 'record',
|
|
224
|
+
defaultDetail: 'standard',
|
|
225
|
+
handleEligibility: false,
|
|
226
|
+
});
|
|
227
|
+
catalog.getStarted = makeEntry('getStarted', {
|
|
228
|
+
authRequirement: 'none',
|
|
229
|
+
capabilityGate: gate('agent', 'Setup instructions'),
|
|
230
|
+
responseFamily: 'record',
|
|
231
|
+
defaultDetail: 'standard',
|
|
232
|
+
handleEligibility: false,
|
|
233
|
+
});
|
|
234
|
+
catalog.signup = makeEntry('signup', {
|
|
235
|
+
authRequirement: 'signer',
|
|
236
|
+
capabilityGate: gate('agent', 'Signup flow'),
|
|
237
|
+
responseFamily: 'record',
|
|
238
|
+
defaultDetail: 'standard',
|
|
239
|
+
handleEligibility: false,
|
|
240
|
+
});
|
|
241
|
+
catalog.purchaseCredits = makeEntry('purchaseCredits', {
|
|
242
|
+
authRequirement: 'jwt',
|
|
243
|
+
capabilityGate: gate('agent', 'Prepaid credits top-up'),
|
|
244
|
+
responseFamily: 'record',
|
|
245
|
+
defaultDetail: 'standard',
|
|
246
|
+
handleEligibility: false,
|
|
247
|
+
});
|
|
248
|
+
catalog.getAccountStatus = makeEntry('getAccountStatus', {
|
|
249
|
+
authRequirement: 'jwt',
|
|
250
|
+
capabilityGate: gate('agent', 'Authenticated account status'),
|
|
251
|
+
responseFamily: 'record',
|
|
252
|
+
});
|
|
253
|
+
catalog.previewUpgrade = makeEntry('previewUpgrade', {
|
|
254
|
+
authRequirement: 'jwt',
|
|
255
|
+
capabilityGate: gate('agent', 'Upgrade preview'),
|
|
256
|
+
responseFamily: 'record',
|
|
257
|
+
handleEligibility: false,
|
|
258
|
+
});
|
|
259
|
+
catalog.upgradePlan = makeEntry('upgradePlan', {
|
|
260
|
+
authRequirement: 'jwtAndSigner',
|
|
261
|
+
capabilityGate: gate('agent', 'Upgrade checkout'),
|
|
262
|
+
mutability: 'write',
|
|
263
|
+
responseFamily: 'mutationReceipt',
|
|
264
|
+
handleEligibility: false,
|
|
265
|
+
});
|
|
266
|
+
catalog.payRenewal = makeEntry('payRenewal', {
|
|
267
|
+
authRequirement: 'jwtAndSigner',
|
|
268
|
+
capabilityGate: gate('agent', 'Renewal checkout'),
|
|
269
|
+
mutability: 'write',
|
|
270
|
+
responseFamily: 'mutationReceipt',
|
|
271
|
+
handleEligibility: false,
|
|
272
|
+
});
|
|
273
|
+
catalog.compareHeliusPlans = makeEntry('compareHeliusPlans', {
|
|
274
|
+
authRequirement: 'none',
|
|
275
|
+
capabilityGate: gate('agent', 'Plan comparison'),
|
|
276
|
+
responseFamily: 'catalog',
|
|
277
|
+
defaultDetail: 'summary',
|
|
278
|
+
});
|
|
279
|
+
catalog.getHeliusPlanInfo = makeEntry('getHeliusPlanInfo', {
|
|
280
|
+
authRequirement: 'none',
|
|
281
|
+
capabilityGate: gate('agent', 'Plan details'),
|
|
282
|
+
responseFamily: 'catalog',
|
|
283
|
+
defaultDetail: 'summary',
|
|
284
|
+
});
|
|
285
|
+
catalog.getWalletBalances = makeEntry('getWalletBalances', {
|
|
286
|
+
capabilityGate: developerWalletGate,
|
|
287
|
+
responseFamily: 'list',
|
|
288
|
+
defaultDetail: 'standard',
|
|
289
|
+
});
|
|
290
|
+
catalog.getWalletBalanceAt = makeEntry('getWalletBalanceAt', {
|
|
291
|
+
capabilityGate: developerWalletGate,
|
|
292
|
+
responseFamily: 'record',
|
|
293
|
+
defaultDetail: 'standard',
|
|
294
|
+
handleEligibility: false,
|
|
295
|
+
});
|
|
296
|
+
catalog.getWalletHistory = makeEntry('getWalletHistory', {
|
|
297
|
+
capabilityGate: developerWalletGate,
|
|
298
|
+
responseFamily: 'history',
|
|
299
|
+
defaultDetail: 'standard',
|
|
300
|
+
});
|
|
301
|
+
catalog.getWalletTransfers = makeEntry('getWalletTransfers', {
|
|
302
|
+
capabilityGate: developerWalletGate,
|
|
303
|
+
responseFamily: 'history',
|
|
304
|
+
defaultDetail: 'standard',
|
|
305
|
+
});
|
|
306
|
+
catalog.getWalletIdentity = makeEntry('getWalletIdentity', {
|
|
307
|
+
capabilityGate: developerWalletGate,
|
|
308
|
+
responseFamily: 'record',
|
|
309
|
+
defaultDetail: 'standard',
|
|
310
|
+
handleEligibility: false,
|
|
311
|
+
});
|
|
312
|
+
catalog.batchWalletIdentity = makeEntry('batchWalletIdentity', {
|
|
313
|
+
capabilityGate: developerWalletGate,
|
|
314
|
+
responseFamily: 'list',
|
|
315
|
+
defaultDetail: 'standard',
|
|
316
|
+
});
|
|
317
|
+
catalog.getWalletFundedBy = makeEntry('getWalletFundedBy', {
|
|
318
|
+
capabilityGate: developerWalletGate,
|
|
319
|
+
responseFamily: 'record',
|
|
320
|
+
defaultDetail: 'standard',
|
|
321
|
+
handleEligibility: false,
|
|
322
|
+
});
|
|
323
|
+
catalog.transactionSubscribe = makeEntry('transactionSubscribe', {
|
|
324
|
+
capabilityGate: businessStreamingGate,
|
|
325
|
+
responseFamily: 'streamingConfig',
|
|
326
|
+
defaultDetail: 'summary',
|
|
327
|
+
});
|
|
328
|
+
catalog.accountSubscribe = makeEntry('accountSubscribe', {
|
|
329
|
+
capabilityGate: businessStreamingGate,
|
|
330
|
+
responseFamily: 'streamingConfig',
|
|
331
|
+
defaultDetail: 'summary',
|
|
332
|
+
});
|
|
333
|
+
catalog.laserstreamSubscribe = makeEntry('laserstreamSubscribe', {
|
|
334
|
+
capabilityGate: professionalLaserstreamGate,
|
|
335
|
+
responseFamily: 'streamingConfig',
|
|
336
|
+
defaultDetail: 'summary',
|
|
337
|
+
});
|
|
338
|
+
catalog.getEnhancedWebSocketInfo = makeEntry('getEnhancedWebSocketInfo', {
|
|
339
|
+
authRequirement: 'none',
|
|
340
|
+
capabilityGate: gate('agent', 'Enhanced WebSocket info', [
|
|
341
|
+
{
|
|
342
|
+
id: 'business:enhanced-websockets',
|
|
343
|
+
minimumPlan: 'business',
|
|
344
|
+
predicate: always,
|
|
345
|
+
label: 'Enhanced WebSockets require Business plan or higher',
|
|
346
|
+
},
|
|
347
|
+
]),
|
|
348
|
+
responseFamily: 'document',
|
|
349
|
+
defaultDetail: 'summary',
|
|
350
|
+
});
|
|
351
|
+
catalog.getLaserstreamInfo = makeEntry('getLaserstreamInfo', {
|
|
352
|
+
authRequirement: 'none',
|
|
353
|
+
capabilityGate: gate('agent', 'Laserstream info', [
|
|
354
|
+
{
|
|
355
|
+
id: 'developer:laserstream-devnet',
|
|
356
|
+
minimumPlan: 'developer',
|
|
357
|
+
predicate: { kind: 'network', oneOf: ['devnet'] },
|
|
358
|
+
label: 'Laserstream on devnet requires Developer plan or higher',
|
|
359
|
+
},
|
|
360
|
+
{
|
|
361
|
+
id: 'professional:laserstream-mainnet-info',
|
|
362
|
+
minimumPlan: 'professional',
|
|
363
|
+
predicate: { kind: 'network', oneOf: ['mainnet-beta'] },
|
|
364
|
+
label: 'Laserstream on mainnet requires Professional plan',
|
|
365
|
+
},
|
|
366
|
+
]),
|
|
367
|
+
responseFamily: 'document',
|
|
368
|
+
defaultDetail: 'summary',
|
|
369
|
+
});
|
|
370
|
+
catalog.getTransactionHistory = makeEntry('getTransactionHistory', {
|
|
371
|
+
responseFamily: 'history',
|
|
372
|
+
defaultDetail: 'standard',
|
|
373
|
+
continuationModel: 'transactionHistory',
|
|
374
|
+
});
|
|
375
|
+
catalog.getTransfersByAddress = makeEntry('getTransfersByAddress', {
|
|
376
|
+
responseFamily: 'history',
|
|
377
|
+
defaultDetail: 'standard',
|
|
378
|
+
continuationModel: 'transactionHistory',
|
|
379
|
+
});
|
|
380
|
+
for (const action of ACTION_NAMES) {
|
|
381
|
+
if (!catalog[action]) {
|
|
382
|
+
throw new Error(`Missing action catalog entry for ${action}`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
export const ACTION_CATALOG = catalog;
|
|
386
|
+
export function getActionCatalogEntry(action) {
|
|
387
|
+
return ACTION_CATALOG[action];
|
|
388
|
+
}
|
|
389
|
+
export function getActionsForTool(tool) {
|
|
390
|
+
return (Object.values(ACTION_CATALOG)
|
|
391
|
+
.filter((entry) => entry.publicTool === tool)
|
|
392
|
+
.map((entry) => entry.action)
|
|
393
|
+
.sort());
|
|
394
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { getNetwork } from '../utils/helius.js';
|
|
3
|
+
const SESSION_KEY = randomUUID();
|
|
4
|
+
export function getRouterContext() {
|
|
5
|
+
const network = getNetwork();
|
|
6
|
+
return {
|
|
7
|
+
sessionKey: SESSION_KEY,
|
|
8
|
+
network: network === 'devnet' ? 'devnet' : 'mainnet-beta',
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RoutedPublicToolName } from './action-groups.js';
|
|
2
|
+
import { type RouterResponse } from './responses.js';
|
|
3
|
+
export declare function dispatchRoutedTool(publicTool: RoutedPublicToolName, params: Record<string, unknown>, extra: unknown): Promise<RouterResponse>;
|
|
4
|
+
export declare function expandStoredResult(params: Record<string, unknown>, extra: unknown): Promise<RouterResponse>;
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { getStoredResult, putStoredResult } from '../results/store.js';
|
|
2
|
+
import { callActionHandler } from './action-handlers.js';
|
|
3
|
+
import { getActionCatalogEntry } from './catalog.js';
|
|
4
|
+
import { getRouterContext } from './context.js';
|
|
5
|
+
import { ACTION_REQUIRED_PARAMS } from './required-params.js';
|
|
6
|
+
import { applyItemSelection, applyRangeSelection, applySectionSelection, buildTextVariant, collectSectionHints, compactErrorText, estimateRenderedSize, getFamilyLimit, mcpErrorCompact, mcpResultHandle, mcpText, toPlainText, } from './responses.js';
|
|
7
|
+
function isHandleEligible(entry) {
|
|
8
|
+
return entry.handleEligibility;
|
|
9
|
+
}
|
|
10
|
+
function pickRequestedDetail(entry, detail) {
|
|
11
|
+
if (detail === 'summary' || detail === 'standard' || detail === 'full') {
|
|
12
|
+
return detail;
|
|
13
|
+
}
|
|
14
|
+
return entry.defaultDetail;
|
|
15
|
+
}
|
|
16
|
+
function buildActionParams(input) {
|
|
17
|
+
const { action: _action, detail: _detail, args, ...topLevel } = input;
|
|
18
|
+
const merged = {
|
|
19
|
+
...(args && typeof args === 'object' && !Array.isArray(args) ? args : {}),
|
|
20
|
+
};
|
|
21
|
+
for (const [key, value] of Object.entries(topLevel)) {
|
|
22
|
+
if (value !== undefined) {
|
|
23
|
+
merged[key] = value;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Normalize common address field mismatches
|
|
27
|
+
if (!merged.address && merged.ownerAddress)
|
|
28
|
+
merged.address = merged.ownerAddress;
|
|
29
|
+
if (!merged.owner && merged.address && !merged.ownerAddress)
|
|
30
|
+
merged.owner = merged.address;
|
|
31
|
+
if (!merged.address && merged.owner)
|
|
32
|
+
merged.address = merged.owner;
|
|
33
|
+
return merged;
|
|
34
|
+
}
|
|
35
|
+
function detectContinuation(action, params, text) {
|
|
36
|
+
if (action === 'getTransactionHistory') {
|
|
37
|
+
// Fragile: depends on handler markdown format "**Next Page Token:** `<token>`"
|
|
38
|
+
const tokenMatch = text.match(/\*\*Next Page Token:\*\*\s+`([^`]+)`/);
|
|
39
|
+
if (tokenMatch) {
|
|
40
|
+
const mode = typeof params.mode === 'string' ? params.mode : 'parsed';
|
|
41
|
+
const next = mode === 'raw'
|
|
42
|
+
? { kind: 'rawApi', paginationToken: tokenMatch[1] }
|
|
43
|
+
: { kind: 'historyApi', paginationToken: tokenMatch[1] };
|
|
44
|
+
return { model: 'transactionHistory', next };
|
|
45
|
+
}
|
|
46
|
+
const hasFilters = [
|
|
47
|
+
'paginationToken',
|
|
48
|
+
'status',
|
|
49
|
+
'tokenAccounts',
|
|
50
|
+
'blockTimeGte',
|
|
51
|
+
'blockTimeLte',
|
|
52
|
+
'slotGte',
|
|
53
|
+
'slotLte',
|
|
54
|
+
].some((key) => params[key] !== undefined);
|
|
55
|
+
const mode = typeof params.mode === 'string' ? params.mode : 'parsed';
|
|
56
|
+
const sortOrder = typeof params.sortOrder === 'string' ? params.sortOrder : 'desc';
|
|
57
|
+
if (mode === 'signatures' && sortOrder === 'desc' && !hasFilters) {
|
|
58
|
+
// Fragile: depends on handler "✅/❌ <base58sig>" line format for signature extraction
|
|
59
|
+
const signatures = Array.from(text.matchAll(/^[^\S\r\n]*[✅❌]\s+([1-9A-HJ-NP-Za-km-z]{86,88})$/gm)).map((match) => match[1]);
|
|
60
|
+
const lastSignature = signatures.at(-1);
|
|
61
|
+
if (lastSignature) {
|
|
62
|
+
return {
|
|
63
|
+
model: 'transactionHistory',
|
|
64
|
+
next: {
|
|
65
|
+
kind: 'signaturesQuick',
|
|
66
|
+
nextBefore: lastSignature,
|
|
67
|
+
lastSeenSignature: lastSignature,
|
|
68
|
+
until: typeof params.until === 'string' ? params.until : undefined,
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (action === 'getTransfersByAddress') {
|
|
75
|
+
// Fragile: depends on handler markdown format "**Next Page Token:** `<token>`" (mirrors getTransactionHistory)
|
|
76
|
+
const tokenMatch = text.match(/\*\*Next Page Token:\*\*\s+`([^`]+)`/);
|
|
77
|
+
if (tokenMatch) {
|
|
78
|
+
return {
|
|
79
|
+
model: 'transactionHistory',
|
|
80
|
+
next: { kind: 'rawApi', paginationToken: tokenMatch[1] },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (typeof params.page === 'number') {
|
|
85
|
+
return { model: 'page', nextPage: params.page + 1 };
|
|
86
|
+
}
|
|
87
|
+
return { model: 'none' };
|
|
88
|
+
}
|
|
89
|
+
function buildAvailableExpansions(family, continuation, sectionHints) {
|
|
90
|
+
const expansions = new Set(['full']);
|
|
91
|
+
if (family === 'document' || sectionHints.length > 0) {
|
|
92
|
+
expansions.add('section');
|
|
93
|
+
expansions.add('range');
|
|
94
|
+
}
|
|
95
|
+
if (family === 'list' || family === 'history') {
|
|
96
|
+
expansions.add('item');
|
|
97
|
+
}
|
|
98
|
+
if (continuation.model === 'page') {
|
|
99
|
+
expansions.add('page');
|
|
100
|
+
}
|
|
101
|
+
if (continuation.model === 'transactionHistory' && continuation.next) {
|
|
102
|
+
expansions.add('continuation');
|
|
103
|
+
}
|
|
104
|
+
return Array.from(expansions);
|
|
105
|
+
}
|
|
106
|
+
function buildStoredResult(entry, publicTool, params, summary, text) {
|
|
107
|
+
const context = getRouterContext();
|
|
108
|
+
const continuation = detectContinuation(entry.action, params, text);
|
|
109
|
+
const sectionHints = collectSectionHints(text);
|
|
110
|
+
const stored = putStoredResult({
|
|
111
|
+
kind: entry.responseFamily,
|
|
112
|
+
ownerSessionKey: context.sessionKey,
|
|
113
|
+
summary,
|
|
114
|
+
availableExpansions: buildAvailableExpansions(entry.responseFamily, continuation, sectionHints),
|
|
115
|
+
payload: {
|
|
116
|
+
recipe: {
|
|
117
|
+
publicTool,
|
|
118
|
+
action: entry.action,
|
|
119
|
+
params,
|
|
120
|
+
responseFamily: entry.responseFamily,
|
|
121
|
+
defaultDetail: entry.defaultDetail,
|
|
122
|
+
},
|
|
123
|
+
continuation,
|
|
124
|
+
sectionHints,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
return stored;
|
|
128
|
+
}
|
|
129
|
+
function normalizeSuccessResponse(entry, publicTool, params, text, requestedDetail, allowHandles) {
|
|
130
|
+
const baseText = text.trim();
|
|
131
|
+
const summaryText = buildTextVariant(entry.responseFamily, 'summary', baseText);
|
|
132
|
+
const standardText = buildTextVariant(entry.responseFamily, 'standard', baseText);
|
|
133
|
+
const fullText = buildTextVariant(entry.responseFamily, 'full', baseText);
|
|
134
|
+
const requestedText = requestedDetail === 'summary'
|
|
135
|
+
? summaryText
|
|
136
|
+
: requestedDetail === 'standard'
|
|
137
|
+
? standardText
|
|
138
|
+
: fullText;
|
|
139
|
+
const size = estimateRenderedSize(requestedText);
|
|
140
|
+
const limit = getFamilyLimit(entry.responseFamily, requestedDetail);
|
|
141
|
+
const needsHandle = allowHandles
|
|
142
|
+
&& isHandleEligible(entry)
|
|
143
|
+
&& (requestedDetail === 'summary' || size > limit);
|
|
144
|
+
if (needsHandle) {
|
|
145
|
+
const stored = buildStoredResult(entry, publicTool, params, summaryText, fullText);
|
|
146
|
+
return mcpResultHandle(summaryText, stored.resultId, stored.availableExpansions, {
|
|
147
|
+
family: entry.responseFamily,
|
|
148
|
+
action: entry.action,
|
|
149
|
+
defaultDetail: entry.defaultDetail,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return mcpText(requestedText, {
|
|
153
|
+
family: entry.responseFamily,
|
|
154
|
+
action: entry.action,
|
|
155
|
+
detail: requestedDetail,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function normalizeActionResponse(entry, publicTool, params, result, requestedDetail, allowHandles) {
|
|
159
|
+
const text = toPlainText(result);
|
|
160
|
+
if (result.isError) {
|
|
161
|
+
return mcpErrorCompact(compactErrorText(text), {
|
|
162
|
+
action: entry.action,
|
|
163
|
+
publicTool,
|
|
164
|
+
error: true,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return normalizeSuccessResponse(entry, publicTool, params, text, requestedDetail, allowHandles);
|
|
168
|
+
}
|
|
169
|
+
async function executeActionViaRouter(publicTool, action, params, requestedDetail, extra, allowHandles) {
|
|
170
|
+
const entry = getActionCatalogEntry(action);
|
|
171
|
+
try {
|
|
172
|
+
const result = await callActionHandler(action, params, extra);
|
|
173
|
+
return normalizeActionResponse(entry, publicTool, params, result, requestedDetail, allowHandles);
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
177
|
+
return mcpErrorCompact(message, { code: 'HANDLER_ERROR', action, publicTool });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export async function dispatchRoutedTool(publicTool, params, extra) {
|
|
181
|
+
const action = params.action;
|
|
182
|
+
if (!action) {
|
|
183
|
+
return mcpErrorCompact('Missing required "action" field.', { code: 'MISSING_ACTION' });
|
|
184
|
+
}
|
|
185
|
+
const requestedDetail = pickRequestedDetail(getActionCatalogEntry(action), params.detail);
|
|
186
|
+
const actionParams = buildActionParams(params);
|
|
187
|
+
const requiredFields = ACTION_REQUIRED_PARAMS[action];
|
|
188
|
+
if (requiredFields) {
|
|
189
|
+
const missing = requiredFields.filter((f) => actionParams[f] === undefined);
|
|
190
|
+
if (missing.length > 0) {
|
|
191
|
+
return mcpErrorCompact(`Missing required parameter${missing.length > 1 ? 's' : ''} for ${action}: ${missing.join(', ')}`, { code: 'MISSING_PARAMS', action, missing });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return executeActionViaRouter(publicTool, action, actionParams, requestedDetail, extra, true);
|
|
195
|
+
}
|
|
196
|
+
function applyContinuation(params, stored, continuation) {
|
|
197
|
+
if (!continuation) {
|
|
198
|
+
return params;
|
|
199
|
+
}
|
|
200
|
+
if (continuation !== 'next') {
|
|
201
|
+
return {
|
|
202
|
+
...params,
|
|
203
|
+
continuation,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (stored.payload.continuation.model === 'transactionHistory' && stored.payload.continuation.next) {
|
|
207
|
+
const next = stored.payload.continuation.next;
|
|
208
|
+
if (next.kind === 'signaturesQuick') {
|
|
209
|
+
return {
|
|
210
|
+
...params,
|
|
211
|
+
before: next.nextBefore,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
...params,
|
|
216
|
+
paginationToken: next.paginationToken,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
if (stored.payload.continuation.model === 'page' && stored.payload.continuation.nextPage !== undefined) {
|
|
220
|
+
return {
|
|
221
|
+
...params,
|
|
222
|
+
page: stored.payload.continuation.nextPage,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return params;
|
|
226
|
+
}
|
|
227
|
+
export async function expandStoredResult(params, extra) {
|
|
228
|
+
const resultId = typeof params.resultId === 'string' ? params.resultId : '';
|
|
229
|
+
if (!resultId) {
|
|
230
|
+
return mcpErrorCompact('Missing required "resultId".', { code: 'MISSING_RESULT_ID' });
|
|
231
|
+
}
|
|
232
|
+
const context = getRouterContext();
|
|
233
|
+
const stored = getStoredResult(resultId, context.sessionKey);
|
|
234
|
+
if (!stored) {
|
|
235
|
+
return mcpErrorCompact('Result handle not found or no longer available. Re-run the original action.', {
|
|
236
|
+
code: 'RESULT_NOT_FOUND',
|
|
237
|
+
resultId,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
let nextParams = {
|
|
241
|
+
...stored.payload.recipe.params,
|
|
242
|
+
};
|
|
243
|
+
if (typeof params.page === 'number') {
|
|
244
|
+
nextParams.page = params.page;
|
|
245
|
+
}
|
|
246
|
+
nextParams = applyContinuation(nextParams, stored, typeof params.continuation === 'string' ? params.continuation : undefined);
|
|
247
|
+
const requestedDetail = (params.detail === 'summary' || params.detail === 'standard' || params.detail === 'full')
|
|
248
|
+
? params.detail
|
|
249
|
+
: 'full';
|
|
250
|
+
try {
|
|
251
|
+
const rawResponse = await callActionHandler(stored.payload.recipe.action, nextParams, extra);
|
|
252
|
+
const rawText = toPlainText(rawResponse);
|
|
253
|
+
if (rawResponse.isError) {
|
|
254
|
+
return mcpErrorCompact(compactErrorText(rawText), {
|
|
255
|
+
code: 'EXPAND_FAILED',
|
|
256
|
+
action: stored.payload.recipe.action,
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
const selected = applyRangeSelection(applyItemSelection(applySectionSelection(rawText, typeof params.section === 'string' ? params.section : undefined), typeof params.item === 'number' ? params.item : undefined), typeof params.range === 'string' ? params.range : undefined);
|
|
260
|
+
const entry = getActionCatalogEntry(stored.payload.recipe.action);
|
|
261
|
+
if (params.continuation === 'next' || typeof params.page === 'number') {
|
|
262
|
+
return normalizeSuccessResponse(entry, stored.payload.recipe.publicTool, nextParams, selected, requestedDetail, true);
|
|
263
|
+
}
|
|
264
|
+
const rendered = buildTextVariant(entry.responseFamily, requestedDetail, selected);
|
|
265
|
+
return mcpText(rendered, {
|
|
266
|
+
action: stored.payload.recipe.action,
|
|
267
|
+
family: stored.kind,
|
|
268
|
+
resultId,
|
|
269
|
+
detail: requestedDetail,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
274
|
+
return mcpErrorCompact(message, { code: 'EXPAND_HANDLER_ERROR', action: stored.payload.recipe.action });
|
|
275
|
+
}
|
|
276
|
+
}
|