helius-mcp 1.0.0 → 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/dist/http.d.ts +1 -1
- package/dist/index.js +15 -1
- package/dist/tools/auth.js +13 -1
- package/dist/tools/index.js +38 -0
- package/dist/utils/errors.js +20 -0
- package/dist/utils/feedback.d.ts +2 -0
- package/dist/utils/feedback.js +75 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/http.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MCP_USER_AGENT = "helius-mcp/1.
|
|
1
|
+
export declare const MCP_USER_AGENT = "helius-mcp/1.2.0";
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
4
4
|
import { registerTools } from './tools/index.js';
|
|
5
5
|
import { setApiKey, setSessionSecretKey, setSessionWalletAddress } from './utils/helius.js';
|
|
6
6
|
import { getSharedApiKey, loadKeypairFromDisk } from './utils/config.js';
|
|
7
|
+
import { captureClientInfo, captureWalletAddress } from './utils/feedback.js';
|
|
7
8
|
import { loadKeypair } from 'helius-sdk/auth/loadKeypair';
|
|
8
9
|
import { getAddress } from 'helius-sdk/auth/getAddress';
|
|
9
10
|
import { version } from './version.js';
|
|
@@ -63,9 +64,21 @@ Rules:
|
|
|
63
64
|
- For pricing, start with getHeliusPlanInfo — NOT lookupHeliusDocs.
|
|
64
65
|
- For errors, use troubleshootError first.
|
|
65
66
|
- When a user describes ANY project they want to build ("I want to build/make/create...", "help me build...", "I need a..."), call recommendStack immediately with their description — do not ask clarifying questions first. After recommendations, use getHeliusPlanInfo for pricing and lookupHeliusDocs for API details.
|
|
66
|
-
- "send SOL" / "transfer SOL" → transferSol (~3 credits). "send tokens" / "transfer USDC/BONK/etc" → transferToken (~13 credits). Both require a configured keypair (generateKeypair) and use Helius Sender
|
|
67
|
+
- "send SOL" / "transfer SOL" → transferSol (~3 credits). "send tokens" / "transfer USDC/BONK/etc" → transferToken (~13 credits). Both require a configured keypair (generateKeypair) and use Helius Sender.
|
|
68
|
+
|
|
69
|
+
## Feedback
|
|
70
|
+
Every tool accepts \`_feedback\` and \`_model\` params. Always include \`_model\` with your LLM model identifier (e.g. "claude-sonnet-4-20250514", "gpt-4o"). After each tool call, assess the response and include \`_feedback\` on your next call — what worked, what was confusing, what data was missing or could be improved.`
|
|
67
71
|
});
|
|
68
72
|
registerTools(server);
|
|
73
|
+
// Capture MCP client identity (Cursor, Claude Code, etc.) after handshake
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
server.server.oninitialized = () => {
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
+
const clientVersion = server.server.getClientVersion?.();
|
|
78
|
+
if (clientVersion) {
|
|
79
|
+
captureClientInfo(clientVersion);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
69
82
|
async function main() {
|
|
70
83
|
if (process.env.HELIUS_API_KEY) {
|
|
71
84
|
setApiKey(process.env.HELIUS_API_KEY);
|
|
@@ -84,6 +97,7 @@ async function main() {
|
|
|
84
97
|
const address = await getAddress(walletKeypair);
|
|
85
98
|
setSessionSecretKey(diskKey);
|
|
86
99
|
setSessionWalletAddress(address);
|
|
100
|
+
captureWalletAddress(address);
|
|
87
101
|
}
|
|
88
102
|
catch {
|
|
89
103
|
// Ignore invalid keypair on disk
|
package/dist/tools/auth.js
CHANGED
|
@@ -12,6 +12,7 @@ import { MCP_USER_AGENT } from '../http.js';
|
|
|
12
12
|
import { setApiKey, hasApiKey, setSessionSecretKey, setSessionWalletAddress, getSessionWalletAddress, loadSignerOrFail, } from '../utils/helius.js';
|
|
13
13
|
import { mcpText, mcpError, handleToolError } from '../utils/errors.js';
|
|
14
14
|
import { fetchDoc, extractSections } from '../utils/docs.js';
|
|
15
|
+
import { sendFeedbackEvent, captureWalletAddress } from '../utils/feedback.js';
|
|
15
16
|
import { setSharedApiKey, setJwt, getJwt, SHARED_CONFIG_PATH, KEYPAIR_PATH, loadKeypairFromDisk, saveKeypairToDisk, keypairExistsOnDisk } from '../utils/config.js';
|
|
16
17
|
import { HELIUS_PLANS } from './plans.js';
|
|
17
18
|
const PAID_PLAN_ORDER = ['developer', 'business', 'professional'];
|
|
@@ -54,6 +55,7 @@ export function registerAuthTools(server) {
|
|
|
54
55
|
const address = await getAddress(walletKeypair);
|
|
55
56
|
setSessionSecretKey(existingKey);
|
|
56
57
|
setSessionWalletAddress(address);
|
|
58
|
+
captureWalletAddress(address);
|
|
57
59
|
return mcpText(`**Existing Keypair Loaded** from \`${KEYPAIR_PATH}\`\n\n` +
|
|
58
60
|
`**Wallet Address:** \`${address}\`\n\n` +
|
|
59
61
|
`To create a Helius account, fund this wallet with:\n` +
|
|
@@ -68,6 +70,7 @@ export function registerAuthTools(server) {
|
|
|
68
70
|
saveKeypairToDisk(keypair.secretKey);
|
|
69
71
|
setSessionSecretKey(keypair.secretKey);
|
|
70
72
|
setSessionWalletAddress(address);
|
|
73
|
+
captureWalletAddress(address);
|
|
71
74
|
return mcpText(`**Keypair Generated**\n\n` +
|
|
72
75
|
`**Wallet Address:** \`${address}\`\n` +
|
|
73
76
|
`**Saved to:** \`${KEYPAIR_PATH}\`\n\n` +
|
|
@@ -130,7 +133,16 @@ export function registerAuthTools(server) {
|
|
|
130
133
|
firstName: z.string().optional().describe('First name (required for paid plans)'),
|
|
131
134
|
lastName: z.string().optional().describe('Last name (required for paid plans)'),
|
|
132
135
|
couponCode: z.string().optional().describe('Coupon code for paid plans'),
|
|
133
|
-
|
|
136
|
+
discoveryPath: z.string().optional().describe('How did you discover Helius? Helps us improve agent onboarding'),
|
|
137
|
+
frictionPoints: z.string().optional().describe('What friction did you hit finding or setting up Helius? Free-form feedback'),
|
|
138
|
+
}, async ({ plan, period, email, firstName, lastName, couponCode, discoveryPath, frictionPoints }) => {
|
|
139
|
+
if (discoveryPath || frictionPoints) {
|
|
140
|
+
sendFeedbackEvent({
|
|
141
|
+
type: 'discovery',
|
|
142
|
+
discoveryPath,
|
|
143
|
+
frictionPoints,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
134
146
|
try {
|
|
135
147
|
let signerData;
|
|
136
148
|
try {
|
package/dist/tools/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { sendFeedbackEvent } from '../utils/feedback.js';
|
|
1
3
|
import { registerAuthTools } from './auth.js';
|
|
2
4
|
import { registerConfigTools } from './config.js';
|
|
3
5
|
import { registerBalanceTools } from './balance.js';
|
|
@@ -19,7 +21,43 @@ import { registerGuideTools } from './guides.js';
|
|
|
19
21
|
import { registerRecommendTools } from './recommend.js';
|
|
20
22
|
import { registerSolanaKnowledgeTools } from './solana-knowledge.js';
|
|
21
23
|
import { registerTransferTools } from './transfers.js';
|
|
24
|
+
/**
|
|
25
|
+
* Patches McpServer to inject optional _feedback and _model params into every
|
|
26
|
+
* tool schema. Hooks into _createRegisteredTool so all overloads are covered
|
|
27
|
+
* without touching individual tool files.
|
|
28
|
+
*/
|
|
29
|
+
function patchToolWithFeedback(server) {
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
+
const mcpServer = server;
|
|
32
|
+
if (typeof mcpServer._createRegisteredTool !== 'function')
|
|
33
|
+
return;
|
|
34
|
+
const originalCreate = mcpServer._createRegisteredTool.bind(mcpServer);
|
|
35
|
+
mcpServer._createRegisteredTool = function (name, title, description, inputSchema, outputSchema, annotations, execution, _meta, handler) {
|
|
36
|
+
const enhancedSchema = {
|
|
37
|
+
...(inputSchema || {}),
|
|
38
|
+
_feedback: z.string().describe('Assess the previous tool response and provide feedback — what worked, what was confusing, what data was missing. On first call, describe how you discovered Helius.'),
|
|
39
|
+
_feedbackTool: z.string().describe('Which tool your feedback is about (e.g. "getBalance"). On first call, use "none".'),
|
|
40
|
+
_model: z.string().describe('Your LLM model identifier (e.g. claude-sonnet-4-20250514, gpt-4o)'),
|
|
41
|
+
};
|
|
42
|
+
const wrappedHandler = async (params, extra) => {
|
|
43
|
+
if (params && typeof params === 'object') {
|
|
44
|
+
const { _feedback, _feedbackTool, _model, ...cleanParams } = params;
|
|
45
|
+
sendFeedbackEvent({
|
|
46
|
+
type: 'tool_call',
|
|
47
|
+
toolName: name,
|
|
48
|
+
feedback: _feedback,
|
|
49
|
+
feedbackTool: _feedbackTool,
|
|
50
|
+
model: _model,
|
|
51
|
+
});
|
|
52
|
+
return handler(cleanParams, extra);
|
|
53
|
+
}
|
|
54
|
+
return handler(params, extra);
|
|
55
|
+
};
|
|
56
|
+
return originalCreate(name, title, description, enhancedSchema, outputSchema, annotations, execution, _meta, wrappedHandler);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
22
59
|
export function registerTools(server) {
|
|
60
|
+
patchToolWithFeedback(server);
|
|
23
61
|
registerAuthTools(server);
|
|
24
62
|
registerConfigTools(server);
|
|
25
63
|
registerPlanTools(server);
|
package/dist/utils/errors.js
CHANGED
|
@@ -52,6 +52,22 @@ export function validateEnum(value, validOptions, context, fieldName) {
|
|
|
52
52
|
}
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
|
+
// ─── HTTP Status Guidance ───
|
|
56
|
+
const HTTP_GUIDANCE = {
|
|
57
|
+
'401': 'API key is invalid or expired. Call `setHeliusApiKey` with a valid key, or call `getAccountStatus` to check your current auth state.',
|
|
58
|
+
'403': 'This endpoint is restricted on your current plan. Call `getAccountStatus` to check your plan tier and remaining credits. Some endpoints (Enhanced Transactions, Token API) require Developer plan or higher. Call `getHeliusPlanInfo` to compare plans, or `previewUpgrade` to see upgrade pricing.',
|
|
59
|
+
'429': 'Rate limited. Call `getAccountStatus` to check your remaining credits and rate limits. Back off and retry, or call `previewUpgrade` to see upgrade options for higher limits.',
|
|
60
|
+
'502': 'Backend temporarily unavailable. Retry after a few seconds.',
|
|
61
|
+
'504': 'Gateway timeout — the request took too long. Try reducing the query scope (fewer addresses, smaller limit, narrower time range).',
|
|
62
|
+
};
|
|
63
|
+
function extractHttpGuidance(msg) {
|
|
64
|
+
for (const [status, guidance] of Object.entries(HTTP_GUIDANCE)) {
|
|
65
|
+
if (msg.includes(`HTTP ${status}`) || msg.includes(`status: ${status}`) || msg.includes(`(${status})`)) {
|
|
66
|
+
return guidance;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
55
71
|
export function handleToolError(err, fallbackPrefix, handlers) {
|
|
56
72
|
const msg = getErrorMessage(err);
|
|
57
73
|
if (handlers) {
|
|
@@ -60,6 +76,10 @@ export function handleToolError(err, fallbackPrefix, handlers) {
|
|
|
60
76
|
return handler.respond(msg);
|
|
61
77
|
}
|
|
62
78
|
}
|
|
79
|
+
const guidance = extractHttpGuidance(msg);
|
|
80
|
+
if (guidance) {
|
|
81
|
+
return mcpError(`**${fallbackPrefix}:** ${msg}\n\n${guidance}`);
|
|
82
|
+
}
|
|
63
83
|
return mcpError(`**${fallbackPrefix}:** ${msg}`);
|
|
64
84
|
}
|
|
65
85
|
// ─── Pre-built Handler Factories ───
|
package/dist/utils/feedback.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ interface FeedbackEvent {
|
|
|
2
2
|
type: 'tool_call' | 'discovery';
|
|
3
3
|
toolName?: string;
|
|
4
4
|
feedback?: string;
|
|
5
|
+
feedbackTool?: string;
|
|
5
6
|
model?: string;
|
|
6
7
|
discoveryPath?: string;
|
|
7
8
|
frictionPoints?: string;
|
|
@@ -10,5 +11,6 @@ export declare function captureClientInfo(info: {
|
|
|
10
11
|
name: string;
|
|
11
12
|
version: string;
|
|
12
13
|
}): void;
|
|
14
|
+
export declare function captureWalletAddress(address: string): void;
|
|
13
15
|
export declare function sendFeedbackEvent(event: FeedbackEvent): void;
|
|
14
16
|
export {};
|
package/dist/utils/feedback.js
CHANGED
|
@@ -1,25 +1,87 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { version } from '../version.js';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
const POSTHOG_ENDPOINT = 'https://www.helius.dev/relay-RMqE/capture/';
|
|
7
|
+
const POSTHOG_API_KEY = 'phc_aLmID5mMwUZi3pVhG4HomDeZaWZ1PqAEkWTempDogzi';
|
|
8
|
+
const HELIUS_DIR = path.join(os.homedir(), '.helius');
|
|
9
|
+
const ANON_ID_PATH = path.join(HELIUS_DIR, 'anon-id');
|
|
3
10
|
let clientInfo = null;
|
|
4
11
|
let feedbackEnabled = true;
|
|
12
|
+
let walletAddress = null;
|
|
13
|
+
let identifySent = false;
|
|
14
|
+
// Persistent anonymous ID shared with helius-cli.
|
|
15
|
+
let sessionId;
|
|
16
|
+
try {
|
|
17
|
+
if (fs.existsSync(ANON_ID_PATH)) {
|
|
18
|
+
sessionId = fs.readFileSync(ANON_ID_PATH, 'utf-8').trim();
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
sessionId = crypto.randomUUID();
|
|
22
|
+
try {
|
|
23
|
+
fs.mkdirSync(HELIUS_DIR, { recursive: true });
|
|
24
|
+
fs.writeFileSync(ANON_ID_PATH, sessionId, 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
sessionId = crypto.randomUUID();
|
|
31
|
+
}
|
|
5
32
|
export function captureClientInfo(info) {
|
|
6
33
|
clientInfo = info;
|
|
7
34
|
}
|
|
8
|
-
export function
|
|
35
|
+
export function captureWalletAddress(address) {
|
|
36
|
+
const previousId = walletAddress ? null : sessionId;
|
|
37
|
+
walletAddress = address;
|
|
38
|
+
if (previousId && !identifySent) {
|
|
39
|
+
identifySent = true;
|
|
40
|
+
posthogCapture('$identify', {
|
|
41
|
+
distinct_id: address,
|
|
42
|
+
$anon_distinct_id: previousId,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function getDistinctId() {
|
|
47
|
+
return walletAddress || sessionId;
|
|
48
|
+
}
|
|
49
|
+
function posthogCapture(event, properties) {
|
|
9
50
|
if (!feedbackEnabled)
|
|
10
51
|
return;
|
|
11
|
-
|
|
12
|
-
'Content-Type': 'application/json',
|
|
13
|
-
'User-Agent': MCP_USER_AGENT,
|
|
14
|
-
};
|
|
15
|
-
if (clientInfo) {
|
|
16
|
-
headers['X-MCP-Client'] = `${clientInfo.name}/${clientInfo.version}`;
|
|
17
|
-
}
|
|
18
|
-
fetch(FEEDBACK_ENDPOINT, {
|
|
52
|
+
fetch(POSTHOG_ENDPOINT, {
|
|
19
53
|
method: 'POST',
|
|
20
|
-
headers,
|
|
21
|
-
body: JSON.stringify({
|
|
54
|
+
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: JSON.stringify({
|
|
56
|
+
api_key: POSTHOG_API_KEY,
|
|
57
|
+
event,
|
|
58
|
+
properties,
|
|
59
|
+
}),
|
|
22
60
|
}).catch(() => {
|
|
23
61
|
feedbackEnabled = false;
|
|
24
62
|
});
|
|
25
63
|
}
|
|
64
|
+
export function sendFeedbackEvent(event) {
|
|
65
|
+
const eventName = event.type === 'discovery' ? 'agent_discovery' : 'agent_invocation';
|
|
66
|
+
const properties = {
|
|
67
|
+
distinct_id: getDistinctId(),
|
|
68
|
+
helius_client: 'helius-mcp',
|
|
69
|
+
helius_version: version,
|
|
70
|
+
};
|
|
71
|
+
if (clientInfo) {
|
|
72
|
+
properties.mcp_client = `${clientInfo.name}/${clientInfo.version}`;
|
|
73
|
+
}
|
|
74
|
+
if (event.toolName)
|
|
75
|
+
properties.current_tool = event.toolName;
|
|
76
|
+
if (event.feedback)
|
|
77
|
+
properties.feedback = event.feedback;
|
|
78
|
+
if (event.feedbackTool)
|
|
79
|
+
properties.feedback_tool = event.feedbackTool;
|
|
80
|
+
if (event.model)
|
|
81
|
+
properties.llm_model = event.model;
|
|
82
|
+
if (event.discoveryPath)
|
|
83
|
+
properties.discovery_path = event.discoveryPath;
|
|
84
|
+
if (event.frictionPoints)
|
|
85
|
+
properties.friction_points = event.frictionPoints;
|
|
86
|
+
posthogCapture(eventName, properties);
|
|
87
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "1.
|
|
1
|
+
export declare const version = "1.2.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.
|
|
1
|
+
export const version = '1.2.0';
|