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.
Files changed (94) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +42 -30
  3. package/dist/http.d.ts +1 -1
  4. package/dist/index.js +2 -56
  5. package/dist/results/store.d.ts +8 -0
  6. package/dist/results/store.js +72 -0
  7. package/dist/results/types.d.ts +47 -0
  8. package/dist/results/types.js +1 -0
  9. package/dist/router/action-groups.d.ts +6 -0
  10. package/dist/router/action-groups.js +32 -0
  11. package/dist/router/action-handlers.d.ts +20 -0
  12. package/dist/router/action-handlers.js +125 -0
  13. package/dist/router/actions.d.ts +12 -0
  14. package/dist/router/actions.js +123 -0
  15. package/dist/router/catalog.d.ts +6 -0
  16. package/dist/router/catalog.js +388 -0
  17. package/dist/router/context.d.ts +5 -0
  18. package/dist/router/context.js +10 -0
  19. package/dist/router/dispatch.d.ts +4 -0
  20. package/dist/router/dispatch.js +276 -0
  21. package/dist/router/instructions.d.ts +1 -0
  22. package/dist/router/instructions.js +25 -0
  23. package/dist/router/register.d.ts +2 -0
  24. package/dist/router/register.js +15 -0
  25. package/dist/router/required-params.d.ts +9 -0
  26. package/dist/router/required-params.js +66 -0
  27. package/dist/router/responses.d.ts +29 -0
  28. package/dist/router/responses.js +186 -0
  29. package/dist/router/schemas.d.ts +216 -0
  30. package/dist/router/schemas.js +195 -0
  31. package/dist/router/telemetry.d.ts +27 -0
  32. package/dist/router/telemetry.js +52 -0
  33. package/dist/router/types.d.ts +46 -0
  34. package/dist/router/types.js +1 -0
  35. package/dist/scripts/validate-catalog.d.ts +2 -2
  36. package/dist/scripts/validate-catalog.js +10 -10
  37. package/dist/tools/accounts.js +5 -5
  38. package/dist/tools/assets.js +5 -5
  39. package/dist/tools/auth.js +392 -288
  40. package/dist/tools/config.js +3 -3
  41. package/dist/tools/das-extras.js +6 -6
  42. package/dist/tools/docs.js +55 -41
  43. package/dist/tools/enhanced-websockets.js +13 -13
  44. package/dist/tools/fees.js +3 -3
  45. package/dist/tools/index.d.ts +1 -1
  46. package/dist/tools/index.js +2 -80
  47. package/dist/tools/laserstream.js +20 -23
  48. package/dist/tools/network.js +41 -2
  49. package/dist/tools/plans.d.ts +0 -5
  50. package/dist/tools/plans.js +167 -12
  51. package/dist/tools/product-catalog.d.ts +1 -0
  52. package/dist/tools/product-catalog.js +51 -16
  53. package/dist/tools/recommend.d.ts +0 -1
  54. package/dist/tools/recommend.js +9 -28
  55. package/dist/tools/shared.d.ts +1 -0
  56. package/dist/tools/shared.js +10 -2
  57. package/dist/tools/solana-knowledge.js +23 -7
  58. package/dist/tools/staking.d.ts +2 -0
  59. package/dist/tools/staking.js +268 -0
  60. package/dist/tools/transactions.js +167 -3
  61. package/dist/tools/transfers.js +38 -43
  62. package/dist/tools/wallet.js +27 -16
  63. package/dist/tools/webhooks.js +3 -3
  64. package/dist/tools/zk-compression.d.ts +2 -0
  65. package/dist/tools/zk-compression.js +781 -0
  66. package/dist/utils/config.d.ts +2 -2
  67. package/dist/utils/config.js +68 -6
  68. package/dist/utils/errors.d.ts +10 -1
  69. package/dist/utils/errors.js +46 -12
  70. package/dist/utils/feedback.js +1 -4
  71. package/dist/utils/helius.js +2 -1
  72. package/dist/utils/ows.d.ts +74 -0
  73. package/dist/utils/ows.js +155 -0
  74. package/dist/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/package.json +2 -2
  77. package/system-prompts/helius/claude.system.md +56 -25
  78. package/system-prompts/helius/full.md +474 -130
  79. package/system-prompts/helius/openai.developer.md +56 -25
  80. package/system-prompts/helius-dflow/claude.system.md +41 -6
  81. package/system-prompts/helius-dflow/full.md +581 -92
  82. package/system-prompts/helius-dflow/openai.developer.md +41 -6
  83. package/system-prompts/helius-jupiter/claude.system.md +333 -0
  84. package/system-prompts/helius-jupiter/full.md +5109 -0
  85. package/system-prompts/helius-jupiter/openai.developer.md +333 -0
  86. package/system-prompts/helius-okx/claude.system.md +182 -0
  87. package/system-prompts/helius-okx/full.md +584 -0
  88. package/system-prompts/helius-okx/openai.developer.md +182 -0
  89. package/system-prompts/helius-phantom/claude.system.md +15 -2
  90. package/system-prompts/helius-phantom/full.md +254 -101
  91. package/system-prompts/helius-phantom/openai.developer.md +15 -2
  92. package/system-prompts/svm/claude.system.md +1 -0
  93. package/system-prompts/svm/full.md +1 -0
  94. package/system-prompts/svm/openai.developer.md +1 -0
@@ -1,10 +1,12 @@
1
1
  import { z } from 'zod';
2
2
  import { fetchDoc, extractSections } from '../utils/docs.js';
3
- import { mcpError } from '../utils/errors.js';
3
+ import { mcpText, mcpError } from '../utils/errors.js';
4
+ import { hasApiKey } from '../utils/helius.js';
4
5
  import { getJwt } from '../utils/config.js';
5
6
  import { listProjects } from 'helius-sdk/auth/listProjects';
6
7
  import { getProject } from 'helius-sdk/auth/getProject';
7
8
  import { MCP_USER_AGENT } from '../http.js';
9
+ import { PRODUCT_CATALOG, PLAN_RANK } from './product-catalog.js';
8
10
  /**
9
11
  * Static plan metadata — NOT the source of truth for pricing or billing data.
10
12
  *
@@ -17,8 +19,8 @@ import { MCP_USER_AGENT } from '../http.js';
17
19
  * from the billing docs via fetchDoc('billing').
18
20
  */
19
21
  export const HELIUS_PLANS = {
20
- free: {
21
- name: 'Free',
22
+ agent: {
23
+ name: 'Agent',
22
24
  features: { webhooks: true, standardWebSockets: true, enhancedWebSockets: false, laserstream: false, stakedConnections: true, archivalData: true },
23
25
  },
24
26
  developer: {
@@ -37,11 +39,30 @@ export const HELIUS_PLANS = {
37
39
  const BILLING_FETCH_ERROR = 'Could not fetch live billing data. Try:\n' +
38
40
  '- `lookupHeliusDocs({ topic: \'billing\' })` for full billing documentation\n' +
39
41
  '- Visit https://www.helius.dev/docs/billing directly';
42
+ const BILLING_FETCH_META = {
43
+ type: 'API',
44
+ code: 'FETCH_FAILED',
45
+ retryable: true,
46
+ recovery: 'Try lookupHeliusDocs({ topic: "billing" }) or visit https://www.helius.dev/docs/billing directly',
47
+ };
40
48
  /**
41
49
  * Detect the user's current Helius plan from their JWT session.
42
50
  * Returns the plan key (e.g. "developer") or undefined if unavailable.
43
51
  * Best-effort — never throws.
44
52
  */
53
+ /**
54
+ * Normalize backend plan keys (e.g. `agent_v4`, `developer_v4`) to the
55
+ * canonical short keys used in HELIUS_PLANS / PLAN_RANK / PRODUCT_CATALOG.
56
+ * Backend appends `_v4` for current-generation plans; the MCP/CLI surface
57
+ * uses the short form throughout.
58
+ */
59
+ function normalizePlanKey(raw) {
60
+ if (!raw)
61
+ return undefined;
62
+ const trimmed = raw.trim().toLowerCase();
63
+ // Strip `_v4` (and any future `_v<N>`) suffix.
64
+ return trimmed.replace(/_v\d+$/, '');
65
+ }
45
66
  export async function detectCurrentPlan() {
46
67
  const jwt = getJwt();
47
68
  if (!jwt)
@@ -50,9 +71,9 @@ export async function detectCurrentPlan() {
50
71
  const projects = await listProjects(jwt, MCP_USER_AGENT);
51
72
  if (projects.length > 0) {
52
73
  const details = await getProject(jwt, projects[0].id, MCP_USER_AGENT);
53
- const raw = details.subscriptionPlanDetails?.currentPlan?.trim().toLowerCase();
54
- if (raw && raw in HELIUS_PLANS)
55
- return raw;
74
+ const normalized = normalizePlanKey(details.subscriptionPlanDetails?.currentPlan);
75
+ if (normalized && normalized in HELIUS_PLANS)
76
+ return normalized;
56
77
  }
57
78
  }
58
79
  catch {
@@ -62,19 +83,19 @@ export async function detectCurrentPlan() {
62
83
  }
63
84
  export function registerPlanTools(server) {
64
85
  server.tool('getHeliusPlanInfo', 'BEST FOR: pricing and plan questions. PREFER compareHeliusPlans for side-by-side category comparisons, getRateLimitInfo for per-method credit costs. Get Helius plan pricing, credits, rate limits, and feature availability. Fetches live from billing docs.', {
65
- plan: z.enum(['free', 'developer', 'business', 'professional', 'all']).optional().default('all').describe('Specific plan to show details for, or "all" for comparison'),
86
+ plan: z.enum(['agent', 'developer', 'business', 'professional', 'all']).optional().default('all').describe('Specific plan to show details for, or "all" for comparison. Note: the MCP/CLI signup flow uses Agent ($10 one-time) as the entry tier; the dashboard\'s Free tier is not available through this path.'),
66
87
  }, async ({ plan }) => {
67
88
  let billingDoc;
68
89
  try {
69
90
  billingDoc = await fetchDoc('billing');
70
91
  }
71
92
  catch {
72
- return mcpError(BILLING_FETCH_ERROR);
93
+ return mcpError(BILLING_FETCH_ERROR, BILLING_FETCH_META);
73
94
  }
74
95
  // Required: plans table
75
96
  const plansTable = extractSections(billingDoc, ['standard plans', 'standard pricing'], { includeLooseMatches: false });
76
97
  if (!plansTable) {
77
- return mcpError(BILLING_FETCH_ERROR);
98
+ return mcpError(BILLING_FETCH_ERROR, BILLING_FETCH_META);
78
99
  }
79
100
  // Optional sections
80
101
  const credits = extractSections(billingDoc, ['credits system', 'credit costs'], { includeLooseMatches: false });
@@ -98,7 +119,7 @@ export function registerPlanTools(server) {
98
119
  sections.push('', addOns);
99
120
  if (rateLimits)
100
121
  sections.push('', rateLimits);
101
- sections.push('', '---', '', '## Actions', '', '- To upgrade, use the `upgradePlan` tool with a plan name (developer, business, professional)', '- To preview pricing before upgrading, use the `previewUpgrade` tool', '', 'Source: https://www.helius.dev/docs/billing (fetched live)');
122
+ sections.push('', '---', '', '## Actions', '', '- To sign up for the Agent plan ($10 one-time, 1M credits), use the `signup` tool', '- To upgrade an existing account, use the `upgradePlan` tool with a plan name (developer, business, professional)', '- To preview pricing before upgrading, use the `previewUpgrade` tool', '', 'Source: https://www.helius.dev/docs/billing (fetched live)');
102
123
  return { content: [{ type: 'text', text: sections.join('\n') }] };
103
124
  });
104
125
  server.tool('compareHeliusPlans', 'BEST FOR: side-by-side plan comparison in a specific category. Compare Helius plans for rate limits, features, or pricing. Fetches live from billing docs.', {
@@ -109,7 +130,7 @@ export function registerPlanTools(server) {
109
130
  billingDoc = await fetchDoc('billing');
110
131
  }
111
132
  catch {
112
- return mcpError(BILLING_FETCH_ERROR);
133
+ return mcpError(BILLING_FETCH_ERROR, BILLING_FETCH_META);
113
134
  }
114
135
  const sectionMap = {
115
136
  rates: ['rate limits', 'standard rate limits', 'special rate limits'],
@@ -118,7 +139,7 @@ export function registerPlanTools(server) {
118
139
  };
119
140
  const extracted = extractSections(billingDoc, sectionMap[category], { includeLooseMatches: false });
120
141
  if (!extracted) {
121
- return mcpError(BILLING_FETCH_ERROR);
142
+ return mcpError(BILLING_FETCH_ERROR, BILLING_FETCH_META);
122
143
  }
123
144
  const lines = [];
124
145
  const currentPlanKey = await detectCurrentPlan();
@@ -130,4 +151,138 @@ export function registerPlanTools(server) {
130
151
  lines.push('', '---', 'Source: https://www.helius.dev/docs/billing (fetched live)');
131
152
  return { content: [{ type: 'text', text: lines.join('\n') }] };
132
153
  });
154
+ // ── getAccountPlan — lightweight pre-flight check ──
155
+ server.tool('getAccountPlan', 'Lightweight pre-flight check: returns current plan, credit balance, and which MCP tools require an upgrade. 0 credits. Call before gated tools (transactionSubscribe, laserstreamSubscribe, etc.).', {}, async () => {
156
+ // ── Tier 1: not authenticated at all ──
157
+ if (!hasApiKey()) {
158
+ return mcpText(`## Account Plan\n\n` +
159
+ `**Auth:** Not authenticated\n\n` +
160
+ `No API key or session found. To get started:\n` +
161
+ `- If you have a key: use the \`setHeliusApiKey\` tool\n` +
162
+ `- If you need an account: use \`generateKeypair\` → \`signup\` (link mode prints a payment URL)`);
163
+ }
164
+ // ── Tier 2: API key present but no JWT ──
165
+ const jwt = getJwt();
166
+ if (!jwt) {
167
+ return mcpText(`## Account Plan\n\n` +
168
+ `**Auth:** API key configured, plan unknown\n\n` +
169
+ `To see your plan and tool eligibility, call \`signup\`.\n` +
170
+ `Your existing account will be detected automatically — no payment needed.`);
171
+ }
172
+ // ── Tier 3: full status via JWT ──
173
+ try {
174
+ const projects = await listProjects(jwt, MCP_USER_AGENT);
175
+ if (projects.length === 0) {
176
+ return mcpError('No projects found. Call `signup` to create an account first.');
177
+ }
178
+ const projectId = projects[0].id;
179
+ const details = await getProject(jwt, projectId, MCP_USER_AGENT);
180
+ const planKey = normalizePlanKey(details.subscriptionPlanDetails?.currentPlan) ?? 'unknown';
181
+ const planInfo = HELIUS_PLANS[planKey];
182
+ if (!planInfo) {
183
+ console.warn(`[getAccountPlan] Unrecognized plan "${planKey}" — tool eligibility may be inaccurate`);
184
+ }
185
+ const planName = planInfo?.name ?? planKey;
186
+ // Credit info
187
+ const totalCredits = details.subscriptionPlanDetails?.totalCredits ?? 0;
188
+ const usedCredits = details.subscriptionPlanDetails?.usedCredits ?? 0;
189
+ const remainingCredits = totalCredits - usedCredits;
190
+ const usedPct = totalCredits > 0 ? ((usedCredits / totalCredits) * 100).toFixed(1) : '0.0';
191
+ // Tool eligibility
192
+ const eligibility = computeToolEligibility(planKey);
193
+ const lines = [
194
+ `## Account Plan`,
195
+ ``,
196
+ `**Plan:** ${planName}`,
197
+ ``,
198
+ `### Credits`,
199
+ `- **Remaining:** ${remainingCredits.toLocaleString()} / ${totalCredits.toLocaleString()} (${usedPct}% used)`,
200
+ ``,
201
+ `### Gated Tool Eligibility`,
202
+ `All Agent-tier tools are available on every plan.`,
203
+ ``,
204
+ ];
205
+ if (eligibility.length > 0) {
206
+ lines.push(`| Tool | Status | Requires |`, `|------|--------|----------|`);
207
+ for (const e of eligibility) {
208
+ lines.push(`| ${e.tool} | ${e.status} | ${e.requires} |`);
209
+ }
210
+ }
211
+ else {
212
+ lines.push(`All tools are available on your plan.`);
213
+ }
214
+ // Next steps — find tools that need upgrade
215
+ const upgradeNeeded = eligibility.filter(e => e.status.includes('UPGRADE REQUIRED'));
216
+ if (upgradeNeeded.length > 0) {
217
+ lines.push(``, `### Next Steps`);
218
+ const toolNames = upgradeNeeded.map(e => e.tool);
219
+ const uniqueRequires = [...new Set(upgradeNeeded.map(e => e.requires))];
220
+ lines.push(`To unlock ${toolNames.join(', ')}, upgrade to ${uniqueRequires.join(' or ')}.`);
221
+ lines.push(`→ Use \`previewUpgrade\` to see pricing`);
222
+ }
223
+ return mcpText(lines.join('\n'));
224
+ }
225
+ catch (err) {
226
+ return mcpError(`Failed to fetch account plan: ${err instanceof Error ? err.message : String(err)}`);
227
+ }
228
+ });
229
+ }
230
+ function computeToolEligibility(planKey) {
231
+ const userRank = PLAN_RANK[planKey] ?? 0;
232
+ // Collect all non-free product entries per tool
233
+ const toolProducts = {};
234
+ for (const product of Object.values(PRODUCT_CATALOG)) {
235
+ if (product.minimumPlan === 'agent')
236
+ continue;
237
+ for (const tool of product.mcpTools) {
238
+ if (!toolProducts[tool])
239
+ toolProducts[tool] = [];
240
+ toolProducts[tool].push({ productName: product.name, minimumPlan: product.minimumPlan });
241
+ }
242
+ }
243
+ const results = [];
244
+ for (const [tool, products] of Object.entries(toolProducts)) {
245
+ // Also check if this tool appears in any baseline product (Agent tier)
246
+ const inBaselineToo = Object.values(PRODUCT_CATALOG).some(p => p.minimumPlan === 'agent' && p.mcpTools.includes(tool));
247
+ if (products.length === 1) {
248
+ const p = products[0];
249
+ const available = userRank >= (PLAN_RANK[p.minimumPlan] ?? 99);
250
+ const planDisplay = HELIUS_PLANS[p.minimumPlan]?.name ?? p.minimumPlan;
251
+ if (inBaselineToo && available) {
252
+ // Tool is available at baseline tier AND at this gated tier — show available
253
+ results.push({ tool, status: 'AVAILABLE', requires: planDisplay });
254
+ }
255
+ else if (inBaselineToo && !available) {
256
+ // Tool works at baseline tier but the enhanced version needs upgrade
257
+ results.push({ tool, status: `AVAILABLE (basic) / UPGRADE REQUIRED (${p.productName})`, requires: planDisplay });
258
+ }
259
+ else {
260
+ results.push({ tool, status: available ? 'AVAILABLE' : 'UPGRADE REQUIRED', requires: planDisplay });
261
+ }
262
+ }
263
+ else {
264
+ // Multiple gated products (e.g. laserstreamSubscribe in devnet + mainnet)
265
+ const statuses = [];
266
+ const requires = [];
267
+ for (const p of products) {
268
+ const available = userRank >= (PLAN_RANK[p.minimumPlan] ?? 99);
269
+ const planDisplay = HELIUS_PLANS[p.minimumPlan]?.name ?? p.minimumPlan;
270
+ const label = p.productName.includes('(') ? p.productName.replace(/.*\(/, '').replace(/\).*/, '').toLowerCase() : p.productName;
271
+ statuses.push(available ? `AVAILABLE (${label})` : `UPGRADE REQUIRED (${label})`);
272
+ requires.push(planDisplay);
273
+ }
274
+ const allAvailable = statuses.every(s => s.startsWith('AVAILABLE'));
275
+ const allUnavailable = statuses.every(s => s.startsWith('UPGRADE'));
276
+ if (allAvailable) {
277
+ results.push({ tool, status: 'AVAILABLE', requires: requires.join(' / ') });
278
+ }
279
+ else if (allUnavailable) {
280
+ results.push({ tool, status: 'UPGRADE REQUIRED', requires: requires[requires.length - 1] });
281
+ }
282
+ else {
283
+ results.push({ tool, status: statuses.join(' / '), requires: requires.join(' / ') });
284
+ }
285
+ }
286
+ }
287
+ return results;
133
288
  }
@@ -7,4 +7,5 @@ export interface CatalogProduct {
7
7
  referenceFile?: string;
8
8
  description: string;
9
9
  }
10
+ export declare const PLAN_RANK: Record<string, number>;
10
11
  export declare const PRODUCT_CATALOG: Record<string, CatalogProduct>;
@@ -3,12 +3,17 @@
3
3
  // Centralized product definitions — each Helius product defined once with its
4
4
  // invariant properties. The recommend tool groups products by minimumPlan into
5
5
  // tiers and uses the description field to give AI consumers rich context.
6
+ // ─── Plan Ranking ───
7
+ // Pure data constant — lives here (zero imports) to avoid circular dependencies.
8
+ // Plan tiers, lowest to highest. The MCP/CLI signup flow uses Agent as the
9
+ // entry tier (the dashboard's Free tier is not reachable through this surface).
10
+ export const PLAN_RANK = { agent: 0, developer: 1, business: 2, professional: 3 };
6
11
  export const PRODUCT_CATALOG = {
7
12
  'das-api': {
8
13
  name: 'DAS API',
9
14
  mcpTools: ['getAssetsByOwner', 'getAssetsByGroup', 'searchAssets', 'getAsset', 'getTokenBalances', 'getTokenAccounts'],
10
15
  creditCostPerCall: '10 credits',
11
- minimumPlan: 'free',
16
+ minimumPlan: 'agent',
12
17
  docKey: 'das',
13
18
  referenceFile: 'references/das.md',
14
19
  description: 'Query tokens, NFTs, collections, and digital assets. Fetches wallet token holdings with names, symbols, and prices. Browse NFT collections, search assets by creator/authority/owner.',
@@ -17,7 +22,7 @@ export const PRODUCT_CATALOG = {
17
22
  name: 'Standard RPC',
18
23
  mcpTools: ['getBalance', 'getAccountInfo', 'getBlock', 'getNetworkStatus'],
19
24
  creditCostPerCall: '1-10 credits',
20
- minimumPlan: 'free',
25
+ minimumPlan: 'agent',
21
26
  docKey: 'rpc',
22
27
  description: 'Core Solana RPC for native SOL balances, account data, block info, and network status. Foundation for any Solana app.',
23
28
  },
@@ -25,7 +30,7 @@ export const PRODUCT_CATALOG = {
25
30
  name: 'Enhanced Transactions API',
26
31
  mcpTools: ['parseTransactions', 'getTransactionHistory'],
27
32
  creditCostPerCall: '100 credits',
28
- minimumPlan: 'free',
33
+ minimumPlan: 'agent',
29
34
  docKey: 'enhanced-transactions',
30
35
  referenceFile: 'references/enhanced-transactions.md',
31
36
  description: 'Parse any transaction into human-readable format with types (SWAP, TRANSFER, NFT_SALE), descriptions, token transfers, and fees. Powers transaction history views and trade verification.',
@@ -41,9 +46,9 @@ export const PRODUCT_CATALOG = {
41
46
  },
42
47
  'webhooks': {
43
48
  name: 'Webhooks',
44
- mcpTools: ['createWebhook', 'getAllWebhooks', 'updateWebhook', 'deleteWebhook'],
49
+ mcpTools: ['createWebhook', 'getAllWebhooks', 'getWebhookByID', 'updateWebhook', 'deleteWebhook'],
45
50
  creditCostPerCall: '100 credits to create, 1 credit per event',
46
- minimumPlan: 'free',
51
+ minimumPlan: 'agent',
47
52
  docKey: 'webhooks',
48
53
  referenceFile: 'references/webhooks.md',
49
54
  description: 'HTTP POST notifications when monitored addresses have on-chain activity. Event-driven architecture without polling. Monitor up to 100k addresses per webhook. Filter by 150+ transaction types (SWAP, NFT_SALE, TRANSFER, etc.).',
@@ -51,8 +56,8 @@ export const PRODUCT_CATALOG = {
51
56
  'enhanced-websockets': {
52
57
  name: 'Enhanced WebSockets',
53
58
  mcpTools: ['transactionSubscribe', 'accountSubscribe', 'getEnhancedWebSocketInfo'],
54
- creditCostPerCall: '3 credits per 0.1 MB streamed',
55
- minimumPlan: 'business',
59
+ creditCostPerCall: '2 credits per 0.1 MB streamed',
60
+ minimumPlan: 'developer',
56
61
  docKey: 'enhanced-websockets',
57
62
  referenceFile: 'references/websockets.md',
58
63
  description: 'Real-time transaction and account streaming over persistent WebSocket connections. 1.5-2x faster than standard WebSockets with advanced filtering (up to 50k addresses per filter). Powers live dashboards and real-time UIs.',
@@ -61,7 +66,7 @@ export const PRODUCT_CATALOG = {
61
66
  name: 'Helius Sender',
62
67
  mcpTools: ['getSenderInfo'],
63
68
  creditCostPerCall: '0 credits',
64
- minimumPlan: 'free',
69
+ minimumPlan: 'agent',
65
70
  docKey: 'sender',
66
71
  referenceFile: 'references/sender.md',
67
72
  description: 'Submit transactions with SWQoS routing, staked connections, and Jito tip integration for optimal landing rates. Essential for trading bots, token launches, and any app that sends transactions.',
@@ -70,7 +75,7 @@ export const PRODUCT_CATALOG = {
70
75
  name: 'Priority Fee API',
71
76
  mcpTools: ['getPriorityFeeEstimate'],
72
77
  creditCostPerCall: '1 credit',
73
- minimumPlan: 'free',
78
+ minimumPlan: 'agent',
74
79
  docKey: 'priority-fee',
75
80
  referenceFile: 'references/priority-fees.md',
76
81
  description: 'Real-time fee estimates for transaction prioritization during network congestion. Returns recommended fees at multiple priority levels (Min, Low, Medium, High, VeryHigh).',
@@ -78,8 +83,8 @@ export const PRODUCT_CATALOG = {
78
83
  'standard-websockets': {
79
84
  name: 'Standard WebSockets',
80
85
  mcpTools: ['transactionSubscribe'],
81
- creditCostPerCall: '3 credits per 0.1 MB streamed',
82
- minimumPlan: 'free',
86
+ creditCostPerCall: '2 credits per 0.1 MB streamed',
87
+ minimumPlan: 'agent',
83
88
  docKey: 'websocket',
84
89
  referenceFile: 'references/websockets.md',
85
90
  description: 'Persistent connection for real-time transaction and account updates using standard Solana WebSocket subscriptions. Good for confirmation tracking and basic streaming.',
@@ -87,7 +92,7 @@ export const PRODUCT_CATALOG = {
87
92
  'laserstream-devnet': {
88
93
  name: 'Laserstream (Devnet)',
89
94
  mcpTools: ['laserstreamSubscribe', 'getLaserstreamInfo'],
90
- creditCostPerCall: '3 credits per 0.1 MB streamed',
95
+ creditCostPerCall: '2 credits per 0.1 MB streamed',
91
96
  minimumPlan: 'developer',
92
97
  docKey: 'laserstream',
93
98
  referenceFile: 'references/laserstream.md',
@@ -96,8 +101,8 @@ export const PRODUCT_CATALOG = {
96
101
  'laserstream-mainnet': {
97
102
  name: 'Laserstream (Mainnet)',
98
103
  mcpTools: ['laserstreamSubscribe', 'getLaserstreamInfo'],
99
- creditCostPerCall: '3 credits per 0.1 MB streamed',
100
- minimumPlan: 'professional',
104
+ creditCostPerCall: '2 credits per 0.1 MB streamed',
105
+ minimumPlan: 'business',
101
106
  docKey: 'laserstream',
102
107
  referenceFile: 'references/laserstream.md',
103
108
  description: 'Lowest-latency gRPC streaming with 24h historical replay for production indexers, trading infrastructure, and data pipelines. Subscribe to all transactions, accounts, blocks, or entries on mainnet.',
@@ -106,16 +111,46 @@ export const PRODUCT_CATALOG = {
106
111
  name: 'Token Transfers',
107
112
  mcpTools: ['transferSol', 'transferToken'],
108
113
  creditCostPerCall: '~3-13 credits + on-chain fees',
109
- minimumPlan: 'free',
114
+ minimumPlan: 'agent',
110
115
  docKey: 'sender',
111
116
  referenceFile: 'references/sender.md',
112
117
  description: 'Send native SOL or SPL tokens from the MCP keypair to any Solana address. Uses Helius Sender for optimal landing rates. Requires a configured keypair.',
113
118
  },
119
+ 'zk-compression': {
120
+ name: 'ZK Compression',
121
+ mcpTools: [
122
+ 'getCompressedAccount', 'getCompressedAccountsByOwner', 'getMultipleCompressedAccounts',
123
+ 'getCompressedBalance', 'getCompressedBalanceByOwner',
124
+ 'getCompressedMintTokenHolders', 'getCompressedTokenAccountBalance',
125
+ 'getCompressedTokenAccountsByOwner', 'getCompressedTokenAccountsByDelegate',
126
+ 'getCompressedTokenBalancesByOwnerV2',
127
+ 'getCompressedAccountProof', 'getMultipleCompressedAccountProofs', 'getMultipleNewAddressProofs',
128
+ 'getCompressionSignaturesForAccount', 'getCompressionSignaturesForAddress',
129
+ 'getCompressionSignaturesForOwner', 'getCompressionSignaturesForTokenOwner',
130
+ 'getLatestCompressionSignatures', 'getLatestNonVotingSignatures',
131
+ 'getTransactionWithCompressionInfo', 'getValidityProof',
132
+ 'getIndexerHealth', 'getIndexerSlot',
133
+ ],
134
+ creditCostPerCall: '10 credits',
135
+ minimumPlan: 'agent',
136
+ docKey: 'zk-compression',
137
+ referenceFile: 'references/zk-compression.md',
138
+ description: 'Query compressed accounts, token balances, Merkle proofs, validity proofs, and compression transaction history via the ZK Compression / Light Protocol indexer. Powers state compression for cost-efficient on-chain data storage.',
139
+ },
140
+ 'native-staking': {
141
+ name: 'Native Staking',
142
+ mcpTools: ['stakeSOL', 'unstakeSOL', 'withdrawStake', 'getStakeAccounts', 'getWithdrawableAmount'],
143
+ creditCostPerCall: '~3-10 credits + on-chain fees',
144
+ minimumPlan: 'agent',
145
+ docKey: 'sender',
146
+ referenceFile: 'references/sender.md',
147
+ description: 'Stake, unstake, and withdraw native SOL via the Helius validator. Earn staking yield with one-click delegation. Query stake accounts and withdrawable amounts.',
148
+ },
114
149
  'token-holders': {
115
150
  name: 'Token Holders',
116
151
  mcpTools: ['getTokenHolders'],
117
152
  creditCostPerCall: '20 credits',
118
- minimumPlan: 'free',
153
+ minimumPlan: 'agent',
119
154
  docKey: 'das',
120
155
  referenceFile: 'references/das.md',
121
156
  description: 'Top 20 holders of any SPL token. Check token distribution, find whale wallets, verify decentralization. Useful for token launches and analytics.',
@@ -1,4 +1,3 @@
1
1
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
2
  export declare const KNOWN_TOOLS: Set<string>;
3
- export declare const PLAN_RANK: Record<string, number>;
4
3
  export declare function registerRecommendTools(server: McpServer): void;
@@ -3,31 +3,12 @@ import { mcpText } from '../utils/errors.js';
3
3
  import { hasApiKey } from '../utils/helius.js';
4
4
  import { getPreferences, savePreferences } from '../utils/config.js';
5
5
  import { HELIUS_PLANS, detectCurrentPlan } from './plans.js';
6
- import { PRODUCT_CATALOG } from './product-catalog.js';
6
+ import { PRODUCT_CATALOG, PLAN_RANK } from './product-catalog.js';
7
+ import { ACTION_NAME_SET } from '../router/actions.js';
7
8
  // fetchDoc/extractSections no longer needed — live billing fetch removed
8
9
  // ─── Known MCP Tools (for validation script) ───
9
- export const KNOWN_TOOLS = new Set([
10
- 'getStarted', 'setHeliusApiKey', 'generateKeypair', 'checkSignupBalance',
11
- 'agenticSignup', 'getAccountStatus', 'previewUpgrade', 'upgradePlan', 'payRenewal',
12
- 'getBalance', 'getTokenBalances', 'getWalletBalances',
13
- 'parseTransactions', 'getTransactionHistory', 'getWalletHistory', 'getWalletTransfers',
14
- 'getAsset', 'getAssetsByOwner', 'searchAssets', 'getAssetsByGroup',
15
- 'getAssetProof', 'getAssetProofBatch', 'getSignaturesForAsset', 'getNftEditions',
16
- 'getAccountInfo', 'getTokenAccounts', 'getProgramAccounts', 'getTokenHolders',
17
- 'getBlock', 'getNetworkStatus',
18
- 'getPriorityFeeEstimate',
19
- 'createWebhook', 'getAllWebhooks', 'getWebhookByID', 'updateWebhook', 'deleteWebhook',
20
- 'transactionSubscribe', 'accountSubscribe', 'getEnhancedWebSocketInfo',
21
- 'laserstreamSubscribe', 'getLaserstreamInfo',
22
- 'getWalletIdentity', 'batchWalletIdentity', 'getWalletFundedBy',
23
- 'getHeliusPlanInfo', 'compareHeliusPlans',
24
- 'lookupHeliusDocs', 'listHeliusDocTopics', 'getHeliusCreditsInfo', 'getRateLimitInfo',
25
- 'troubleshootError', 'getSenderInfo', 'getWebhookGuide', 'getLatencyComparison', 'getPumpFunGuide',
26
- 'recommendStack',
27
- 'transferSol', 'transferToken',
28
- ]);
29
- // ─── Plan Ranking ───
30
- export const PLAN_RANK = { free: 0, developer: 1, business: 2, professional: 3 };
10
+ export const KNOWN_TOOLS = ACTION_NAME_SET;
11
+ // ─── Plan Ranking (re-exported from product-catalog.ts) ───
31
12
  function planAtOrBelow(plan, maxPlan) {
32
13
  return (PLAN_RANK[plan] ?? 99) <= (PLAN_RANK[maxPlan] ?? 99);
33
14
  }
@@ -57,8 +38,8 @@ const TIER_MAP = {
57
38
  business: 'production',
58
39
  professional: 'production',
59
40
  };
60
- const TIER_DISPLAY = { budget: 'Free', standard: 'Developer', production: 'Business / Professional' };
61
- const TIER_PLANS = { budget: 'free', standard: 'developer', production: 'business' };
41
+ const TIER_DISPLAY = { budget: 'Agent', standard: 'Developer', production: 'Business / Professional' };
42
+ const TIER_PLANS = { budget: 'agent', standard: 'developer', production: 'business' };
62
43
  function groupCatalogByTier() {
63
44
  const groups = { budget: [], standard: [], production: [] };
64
45
  for (const product of Object.values(PRODUCT_CATALOG)) {
@@ -67,7 +48,7 @@ function groupCatalogByTier() {
67
48
  }
68
49
  const tiers = [];
69
50
  if (groups.budget.length > 0)
70
- tiers.push({ tier: 'budget', tierPlan: 'free', products: groups.budget });
51
+ tiers.push({ tier: 'budget', tierPlan: 'agent', products: groups.budget });
71
52
  if (groups.standard.length > 0)
72
53
  tiers.push({ tier: 'standard', tierPlan: 'developer', products: groups.standard });
73
54
  if (groups.production.length > 0)
@@ -133,7 +114,7 @@ function formatCatalog(availableTiers, upgradeTiers, description, complexity, de
133
114
  }
134
115
  lines.push('---', '');
135
116
  if (complexity) {
136
- const label = { low: 'free tier only', medium: 'free + developer tiers', high: 'all tiers' };
117
+ const label = { low: 'agent tier only', medium: 'agent + developer tiers', high: 'all tiers' };
137
118
  lines.push(`_Showing ${label[complexity]}_`, '', '---', '');
138
119
  }
139
120
  for (let i = 0; i < availableTiers.length; i++) {
@@ -170,7 +151,7 @@ const SCALE_TIERS = {
170
151
  export function registerRecommendTools(server) {
171
152
  server.tool('recommendStack', 'BEST FOR: project architecture when user describes a Solana app to build. PREFER getHeliusPlanInfo for pricing-only, lookupHeliusDocs for API docs. Get tiered architecture recommendations. Returns Helius products, MCP tools, credit costs, minimum plan, and reference files.', {
172
153
  description: z.string().describe('What the user wants to build, in their own words'),
173
- budget: z.enum(['free', 'developer', 'business', 'professional']).optional(),
154
+ budget: z.enum(['agent', 'developer', 'business', 'professional']).optional(),
174
155
  complexity: z.enum(['low', 'medium', 'high']).optional(),
175
156
  scale: z.enum(['budget', 'standard', 'production', 'all']).optional().default('all'),
176
157
  remember: z.boolean().optional().describe('Save budget/complexity preferences for future sessions'),
@@ -3,4 +3,5 @@ export declare function noApiKeyResponse(): {
3
3
  type: "text";
4
4
  text: string;
5
5
  }[];
6
+ isError: boolean;
6
7
  };
@@ -1,3 +1,9 @@
1
+ const NO_API_KEY_META = {
2
+ type: 'AUTH',
3
+ code: 'NO_API_KEY',
4
+ retryable: false,
5
+ recovery: 'Call generateKeypair + signup, or setHeliusApiKey',
6
+ };
1
7
  const NO_API_KEY_MESSAGE = `**Helius API Key Required**
2
8
 
3
9
  I need a Helius API key to query the Solana blockchain.
@@ -5,14 +11,16 @@ I need a Helius API key to query the Solana blockchain.
5
11
  **Option 1 — Sign up here (recommended):**
6
12
  1. Call \`generateKeypair\` to create a signup wallet
7
13
  2. Fund the wallet with ~0.001 SOL + 1 USDC
8
- 3. Call \`agenticSignup\` to create your account — the API key will be configured automatically
14
+ 3. Call \`signup\` to create your account — the API key will be configured automatically
9
15
 
10
16
  **Option 2 — Use an existing key:**
11
17
  1. Go to https://dashboard.helius.dev/api-keys
12
18
  2. Copy your API key
13
19
  3. Call \`setHeliusApiKey\` with your key`;
14
20
  export function noApiKeyResponse() {
21
+ const body = '```json\n' + JSON.stringify(NO_API_KEY_META) + '\n```\n\n' + NO_API_KEY_MESSAGE;
15
22
  return {
16
- content: [{ type: 'text', text: NO_API_KEY_MESSAGE }]
23
+ content: [{ type: 'text', text: body }],
24
+ isError: true,
17
25
  };
18
26
  }
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  import { MCP_USER_AGENT } from '../http.js';
3
- import { mcpText, handleToolError } from '../utils/errors.js';
3
+ import { mcpText, mcpError, handleToolError } from '../utils/errors.js';
4
4
  // ---------------------------------------------------------------------------
5
5
  // Cache
6
6
  // ---------------------------------------------------------------------------
@@ -271,7 +271,7 @@ export function registerSolanaKnowledgeTools(server) {
271
271
  })
272
272
  .map((e) => ` SIMD-${e.number}: ${e.slug}`)
273
273
  .join('\n');
274
- return mcpText(`SIMD-${paddedNumber} not found.\n\n${nearby ? `Nearby proposals:\n${nearby}` : `Total proposals available: ${index.length}`}`);
274
+ return mcpError(`SIMD-${paddedNumber} not found.\n\n${nearby ? `Nearby proposals:\n${nearby}` : `Total proposals available: ${index.length}`}`, { type: 'NOT_FOUND', code: 'RESOURCE_NOT_FOUND', retryable: false, recovery: 'Check the SIMD number. Use listSIMDs to see available proposals.' });
275
275
  }
276
276
  const url = `${SIMD_RAW_BASE}/${entry.filename}`;
277
277
  const content = await cachedFetch(url);
@@ -331,7 +331,7 @@ export function registerSolanaKnowledgeTools(server) {
331
331
  }, async ({ path, repo, branch }) => {
332
332
  try {
333
333
  if (path.includes('..')) {
334
- return mcpText('Invalid path: must not contain ".." segments.');
334
+ return mcpError('Invalid path: must not contain ".." segments.', { type: 'VALIDATION', code: 'INVALID_PARAM', retryable: false, recovery: 'Remove ".." segments from the path.' });
335
335
  }
336
336
  const repoMap = {
337
337
  agave: { fullName: 'anza-xyz/agave', defaultBranch: 'master' },
@@ -366,7 +366,7 @@ export function registerSolanaKnowledgeTools(server) {
366
366
  return handleToolError(error, 'Error reading source file', [
367
367
  {
368
368
  match: (msg) => msg.includes('404'),
369
- respond: () => mcpText(`**File not found:** \`${path}\`\n\nTips:\n- Check the path is correct\n- Browse https://github.com/${repoInfo} to find the right path\n- The default branch for ${repo} is "${defaultBr}"`),
369
+ respond: () => mcpError(`**File not found:** \`${path}\`\n\nTips:\n- Check the path is correct\n- Browse https://github.com/${repoInfo} to find the right path\n- The default branch for ${repo} is "${defaultBr}"`, { type: 'NOT_FOUND', code: 'HTTP_404', retryable: false, recovery: `Check the file path. Browse https://github.com/${repoInfo} to find the right path.` }),
370
370
  },
371
371
  ]);
372
372
  }
@@ -476,7 +476,8 @@ export function registerSolanaKnowledgeTools(server) {
476
476
  server.tool('fetchHeliusBlog', 'Fetch a technical blog post from Helius (helius.dev/blog). The Helius blog covers SVM internals, consensus, transactions, fees, MEV, validator economics, development frameworks, security, and more. Use "list" to see available posts or "fetch" to read one.', {
477
477
  action: z
478
478
  .enum(['fetch', 'list'])
479
- .describe('"fetch" to read a specific post, "list" to see available posts by category'),
479
+ .optional()
480
+ .describe('"fetch" to read a specific post, "list" to see available posts by category. Defaults to "fetch" if slug provided, "list" otherwise.'),
480
481
  slug: z
481
482
  .string()
482
483
  .optional()
@@ -485,7 +486,9 @@ export function registerSolanaKnowledgeTools(server) {
485
486
  .enum(['all', 'runtime', 'consensus', 'transactions', 'validators', 'data', 'security', 'development', 'tokens'])
486
487
  .default('all')
487
488
  .describe('Filter by category when listing posts'),
488
- }, async ({ action, slug, category }) => {
489
+ }, async ({ action: rawAction, slug: rawSlug, category }) => {
490
+ let slug = rawSlug;
491
+ const action = rawAction ?? (slug ? 'fetch' : 'list');
489
492
  try {
490
493
  if (action === 'list') {
491
494
  const entries = Object.entries(BLOG_INDEX);
@@ -524,7 +527,20 @@ export function registerSolanaKnowledgeTools(server) {
524
527
  }
525
528
  // Fetch a specific post
526
529
  if (!slug) {
527
- return mcpText('Please provide a slug. Use `fetchHeliusBlog` with action "list" to see available posts.');
530
+ return mcpError('Please provide a slug. Use `fetchHeliusBlog` with action "list" to see available posts.', { type: 'VALIDATION', code: 'MISSING_PARAM', retryable: false, recovery: 'Provide a slug. Use fetchHeliusBlog with action "list" to see available posts.' });
531
+ }
532
+ // Fuzzy slug matching — if exact slug isn't in index, find close matches
533
+ if (!BLOG_INDEX[slug]) {
534
+ const lower = slug.toLowerCase();
535
+ const matches = Object.keys(BLOG_INDEX).filter((k) => k.includes(lower) || lower.includes(k) || k.split('-').some((w) => lower.includes(w) && w.length > 3));
536
+ if (matches.length === 1) {
537
+ // Single match — auto-correct
538
+ slug = matches[0];
539
+ }
540
+ else if (matches.length > 1) {
541
+ return mcpError(`No exact blog post "${slug}". Did you mean one of these?\n${matches.map((m) => `- \`${m}\` — ${BLOG_INDEX[m].title}`).join('\n')}`, { type: 'VALIDATION', code: 'UNKNOWN_SLUG', retryable: true, recovery: 'Retry with one of the suggested slugs.' });
542
+ }
543
+ // If no matches, let it proceed — the fetch will 404 with a helpful message
528
544
  }
529
545
  const url = `https://www.helius.dev/blog/${slug}`;
530
546
  const html = await cachedFetch(url);
@@ -0,0 +1,2 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ export declare function registerStakingTools(server: McpServer): void;