AMOCA Solana MCP Server
by manolaz
Verified
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ACTIONS, SolanaAgentKit , startMcpServer } from "solana-agent-kit";
import * as dotenv from "dotenv";
import { z } from "zod";
import { Connection, PublicKey, LAMPORTS_PER_SOL, clusterApiUrl, Keypair, Transaction } from "@solana/web3.js";
import { getExplorerLink } from "gill";
import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token";
import bs58 from "bs58";
import { Helius } from "helius-sdk";
dotenv.config();
// Initialize Helius client with API key from environment variables
const helius = new Helius(process.env.HELIUS_API_KEY || "");
// Type definitions for Helius API responses
interface HeliusAsset {
id: string;
content: {
metadata: any;
files?: any[];
json_uri?: string;
links?: any;
};
authorities: any[];
compression: any;
grouping: any[];
royalty: any;
creators: any[];
ownership: any;
supply: any;
mutable: boolean;
burnt: boolean;
}
interface HeliusAssetsByOwnerResponse {
items: HeliusAsset[];
total: number;
limit: number;
page: number;
}
// Jupiter API constants
const JUPITER_QUOTE_API = 'https://quote-api.jup.ag/v6';
// Type definitions for Solana Trading features
interface SwapQuote {
inputMint: string;
outputMint: string;
amount: string;
slippage: number;
}
interface WalletDetails {
publicKey: string;
privateKey: string;
}
interface SwapResult {
txid: string;
status: 'confirmed' | 'failed';
}
interface TokenBalanceSummary {
tokens: Record<string, {
amount: string;
usdValue: number;
symbol?: string;
}>;
histogram: {
ranges: string[];
counts: number[];
distribution: string;
};
totalUsdValue: number;
}
// Renamed from SolanaTrader to AMOCASolanaAgent
class AMOCASolanaAgent {
private connection: Connection;
constructor(connection: Connection) {
this.connection = connection;
}
createWallet(): WalletDetails {
const keypair = Keypair.generate();
return {
publicKey: keypair.publicKey.toString(),
privateKey: bs58.encode(keypair.secretKey),
};
}
importWallet(privateKey: string): WalletDetails {
try {
const keypair = Keypair.fromSecretKey(bs58.decode(privateKey));
return {
publicKey: keypair.publicKey.toString(),
privateKey,
};
} catch (error) {
throw new Error('Invalid private key');
}
}
async getTokenBalance(walletAddress: string, tokenMint: string): Promise<string> {
try {
const wallet = new PublicKey(walletAddress);
const mint = new PublicKey(tokenMint);
const tokenAccount = await getOrCreateAssociatedTokenAccount(
this.connection,
Keypair.generate(), // dummy signer for read-only
mint,
wallet
);
const balance = await this.connection.getTokenAccountBalance(tokenAccount.address);
return balance.value.uiAmountString || '0';
} catch (error) {
console.error('Error getting token balance:', error);
throw error;
}
}
async getTokenPrices(mintAddresses: string[]): Promise<Record<string, { price: number, symbol?: string }>> {
try {
// Use Jupiter Price API to fetch token prices
const response = await fetch("https://price.jup.ag/v4/price", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
ids: mintAddresses,
}),
});
if (!response.ok) {
throw new Error("Failed to fetch token prices");
}
const data = await response.json();
const prices: Record<string, { price: number, symbol?: string }> = {};
// Format the price data
for (const mint of mintAddresses) {
if (data.data && data.data[mint]) {
prices[mint] = {
price: data.data[mint].price || 0,
symbol: data.data[mint].symbol
};
} else {
prices[mint] = { price: 0 };
}
}
return prices;
} catch (error) {
console.error("Error fetching token prices:", error);
// Return empty prices if API fails
return mintAddresses.reduce((acc, mint) => {
acc[mint] = { price: 0 };
return acc;
}, {} as Record<string, { price: number, symbol?: string }>);
}
}
async getAllTokenBalances(walletAddress: string): Promise<TokenBalanceSummary> {
try {
const wallet = new PublicKey(walletAddress);
// Get all token accounts owned by this wallet
const tokenAccounts = await this.connection.getTokenAccountsByOwner(
wallet,
{
programId: new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'), // SPL Token program ID
}
);
const balances: Record<string, string> = {};
const mintAddresses: string[] = [];
// Process each token account
for (const tokenAccount of tokenAccounts.value) {
const accountInfo = tokenAccount.account;
const accountData = Buffer.from(accountInfo.data);
// Extract mint address from token account data (first 32 bytes)
const mintAddress = new PublicKey(accountData.slice(0, 32)).toString();
mintAddresses.push(mintAddress);
// Get parsed token info for better data presentation
const parsedAccountInfo = await this.connection.getParsedAccountInfo(tokenAccount.pubkey);
if (parsedAccountInfo.value && 'parsed' in parsedAccountInfo.value.data) {
const parsedData = parsedAccountInfo.value.data.parsed;
if ('info' in parsedData && 'tokenAmount' in parsedData.info) {
const amount = parsedData.info.tokenAmount.uiAmountString || '0';
balances[mintAddress] = amount;
}
}
}
// Get token prices
const prices = await this.getTokenPrices(mintAddresses);
// Calculate USD values for each token
const tokenDetails: Record<string, {amount: string; usdValue: number; symbol?: string}> = {};
let totalUsdValue = 0;
for (const [mint, amount] of Object.entries(balances)) {
const price = prices[mint]?.price || 0;
const usdValue = parseFloat(amount) * price;
totalUsdValue += usdValue;
tokenDetails[mint] = {
amount,
usdValue,
symbol: prices[mint]?.symbol
};
}
// Create histogram data for value distribution
const ranges = ['$0-$1', '$1-$10', '$10-$100', '$100-$1K', '$1K-$10K', '$10K+'];
const thresholds = [0, 1, 10, 100, 1000, 10000, Infinity];
const counts = new Array(ranges.length).fill(0);
// Count tokens in each value range
for (const token of Object.values(tokenDetails)) {
for (let i = 0; i < thresholds.length - 1; i++) {
if (token.usdValue >= thresholds[i] && token.usdValue < thresholds[i + 1]) {
counts[i]++;
break;
}
}
}
// Create ASCII histogram
const maxCount = Math.max(...counts);
const histogramWidth = 30; // Max width of histogram bars
let distribution = 'Token Value Distribution:\n\n';
for (let i = 0; i < ranges.length; i++) {
const barWidth = maxCount > 0 ? Math.round((counts[i] / maxCount) * histogramWidth) : 0;
const bar = '█'.repeat(barWidth);
distribution += `${ranges[i].padEnd(10)} | ${bar} ${counts[i]}\n`;
}
return {
tokens: tokenDetails,
histogram: {
ranges,
counts,
distribution
},
totalUsdValue
};
} catch (error) {
console.error('Error getting all token balances:', error);
throw error;
}
}
async getSwapQuote(params: SwapQuote): Promise<unknown> {
try {
const response = await fetch(
`${JUPITER_QUOTE_API}/quote?inputMint=${params.inputMint}&outputMint=${params.outputMint}&amount=${params.amount}&slippageBps=${params.slippage * 100}`,
{
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error('Failed to get swap quote');
}
return await response.json();
} catch (error) {
console.error('Error getting swap quote:', error);
throw error;
}
}
async executeSwap(
quote: unknown,
walletPrivateKey: string
): Promise<SwapResult> {
try {
// Get serialized transactions from Jupiter
const swapResponse = await fetch(`${JUPITER_QUOTE_API}/swap`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
quoteResponse: quote,
userPublicKey: Keypair.fromSecretKey(
bs58.decode(walletPrivateKey)
).publicKey.toString(),
}),
});
if (!swapResponse.ok) {
throw new Error('Failed to prepare swap transaction');
}
const swapData = await swapResponse.json() as { swapTransaction: string };
const swapTransactionBuf = Buffer.from(swapData.swapTransaction, 'base64');
// Deserialize and sign transaction
const transaction = Transaction.from(swapTransactionBuf);
const keypair = Keypair.fromSecretKey(bs58.decode(walletPrivateKey));
transaction.sign(keypair);
// Send transaction
const txid = await this.connection.sendRawTransaction(
transaction.serialize(),
{ skipPreflight: true }
);
// Wait for confirmation
const confirmation = await this.connection.confirmTransaction(txid);
if (confirmation.value.err) {
throw new Error('Transaction failed');
}
return {
txid,
status: 'confirmed',
};
} catch (error) {
console.error('Error executing swap:', error);
throw error;
}
}
}
// Create an MCP server
const server = new McpServer({
name: "Solana RPC Tools",
version: "1.0.0",
});
// Initialize Solana connection
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
// const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
// Initialize AMOCASolanaAgent with our connection (renamed from trader)
const agent = new AMOCASolanaAgent(connection);
// Solana RPC Methods as Tools
// Get Account Info
server.tool(
"getAccountInfo",
"Used to look up account info by public key (32 byte base58 encoded address)",
{ publicKey: z.string() },
async ({ publicKey }) => {
try {
const pubkey = new PublicKey(publicKey);
const accountInfo = await connection.getAccountInfo(pubkey);
return {
content: [{ type: "text", text: JSON.stringify(accountInfo, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Balance
server.tool(
"getBalance",
"Get comprehensive token portfolio for a wallet with visual charts",
{ publicKey: z.string() },
async ({ publicKey }) => {
try {
const pubkey = new PublicKey(publicKey);
// Get SOL balance
const solBalance = await connection.getBalance(pubkey);
// Get all token balances with their values
const tokenBalances = await agent.getAllTokenBalances(publicKey);
// Create a more visual representation with ASCII art
let response = `📊 WALLET PORTFOLIO SUMMARY 📊\n\n`;
response += `🔷 SOL Balance: ${(solBalance / LAMPORTS_PER_SOL).toFixed(6)} SOL (${solBalance} lamports)\n`;
response += `💰 Total Portfolio Value: $${tokenBalances.totalUsdValue.toFixed(2)}\n\n`;
// Value distribution histogram
response += `📈 TOKEN VALUE DISTRIBUTION 📈\n`;
response += tokenBalances.histogram.distribution;
// Top tokens table with better formatting
response += `\n🏆 TOP TOKENS BY VALUE 🏆\n`;
response += `${"TOKEN".padEnd(12)} | ${"AMOUNT".padEnd(18)} | ${"USD VALUE".padEnd(12)}\n`;
response += `${"-".repeat(50)}\n`;
// Sort tokens by USD value and get top 8
const topTokens = Object.entries(tokenBalances.tokens)
.sort((a, b) => b[1].usdValue - a[1].usdValue)
.slice(0, 8);
for (const [mint, details] of topTokens) {
const symbol = details.symbol || "Unknown";
const formattedAmount = details.amount.length > 15
? `${details.amount.substring(0, 12)}...`
: details.amount.padEnd(15);
response += `${symbol.padEnd(12)} | ${formattedAmount.padEnd(18)} | $${details.usdValue.toFixed(2).padEnd(12)}\n`;
}
// Add a pie chart representation using ASCII
const totalValue = tokenBalances.totalUsdValue;
if (totalValue > 0) {
response += `\n🥧 PORTFOLIO COMPOSITION 🥧\n`;
for (const [mint, details] of topTokens) {
if (details.usdValue > 0) {
const percentage = (details.usdValue / totalValue) * 100;
const barLength = Math.max(1, Math.round((percentage / 100) * 30));
const symbol = details.symbol || "Unknown";
response += `${symbol.padEnd(10)} | ${"█".repeat(barLength)} ${percentage.toFixed(1)}%\n`;
}
}
}
return {
content: [{ type: "text", text: response }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Minimum Balance For Rent Exemption
server.tool(
"getMinimumBalanceForRentExemption",
"Used to look up minimum balance required for rent exemption by data size",
{ dataSize: z.number() },
async ({ dataSize }) => {
try {
const minBalance = await connection.getMinimumBalanceForRentExemption(dataSize);
return {
content: [{ type: "text", text: `${minBalance / LAMPORTS_PER_SOL} SOL (${minBalance} lamports)` }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Transaction
server.tool("getTransaction",
"Used to look up transaction by signature (64 byte base58 encoded string)",
{ signature: z.string() },
async ({ signature }) => {
try {
const transaction = await connection.getParsedTransaction(signature, { maxSupportedTransactionVersion: 0 });
return {
content: [{ type: "text", text: JSON.stringify(transaction, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// New trading tools
// Create Wallet
server.tool(
"createWallet",
"Create a new Solana wallet keypair",
{},
async () => {
try {
const wallet = agent.createWallet();
return {
content: [{ type: "text", text: JSON.stringify(wallet, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Import Wallet
server.tool(
"importWallet",
"Import an existing Solana wallet using private key",
{ privateKey: z.string() },
async ({ privateKey }) => {
try {
const wallet = agent.importWallet(privateKey);
return {
content: [{ type: "text", text: JSON.stringify(wallet, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Token Balance
server.tool(
"getTokenBalance",
"Get token balance for a wallet",
{
walletAddress: z.string(),
tokenMint: z.string()
},
async ({ walletAddress, tokenMint }) => {
try {
const balance = await agent.getTokenBalance(walletAddress, tokenMint);
return {
content: [{ type: "text", text: balance }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get All Token Balances
server.tool(
"getAllTokenBalances",
"Get all token balances for a wallet with USD value distribution",
{
walletAddress: z.string()
},
async ({ walletAddress }) => {
try {
const balanceSummary = await agent.getAllTokenBalances(walletAddress);
// Format the response for better readability
let response = `Wallet: ${walletAddress}\n`;
response += `Total Value: $${balanceSummary.totalUsdValue.toFixed(2)}\n\n`;
response += balanceSummary.histogram.distribution;
response += "\n\nTop 5 tokens by value:\n";
// Sort tokens by USD value and get top 5
const topTokens = Object.entries(balanceSummary.tokens)
.sort((a, b) => b[1].usdValue - a[1].usdValue)
.slice(0, 5);
for (const [mint, details] of topTokens) {
const symbol = details.symbol || "Unknown";
response += `${symbol.padEnd(10)} | ${details.amount.padEnd(15)} | $${details.usdValue.toFixed(2)}\n`;
}
return {
content: [{ type: "text", text: response }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Swap Quote
server.tool(
"getSwapQuote",
"Get a quote for swapping tokens via Jupiter",
{
inputMint: z.string(),
outputMint: z.string(),
amount: z.string(),
slippage: z.number()
},
async ({ inputMint, outputMint, amount, slippage }) => {
try {
const quote = await agent.getSwapQuote({
inputMint,
outputMint,
amount,
slippage
});
return {
content: [{ type: "text", text: JSON.stringify(quote, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Execute Swap
server.tool(
"executeSwap",
"Execute a token swap using Jupiter",
{
quote: z.any(),
walletPrivateKey: z.string()
},
async ({ quote, walletPrivateKey }) => {
try {
const result = await agent.executeSwap(quote, walletPrivateKey);
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Add a dynamic account info resource
// Setup specific resources to read from solana.com/docs pages
server.resource(
"solanaDocsInstallation",
new ResourceTemplate("solana://docs/intro/installation", { list: undefined }),
async (uri) => {
try {
const response = await fetch(`https://raw.githubusercontent.com/solana-foundation/solana-com/main/content/docs/intro/installation.mdx`);
const fileContent = await response.text();
return {
contents: [{
uri: uri.href,
text: fileContent
}]
};
} catch (error) {
return {
contents: [{
uri: uri.href,
text: `Error: ${(error as Error).message}`
}]
};
}
}
);
server.resource(
"solanaDocsClusters",
new ResourceTemplate("solana://docs/references/clusters", { list: undefined }),
async (uri) => {
try {
const response = await fetch(`https://raw.githubusercontent.com/solana-foundation/solana-com/main/content/docs/references/clusters.mdx`);
const fileContent = await response.text();
return {
contents: [{
uri: uri.href,
text: fileContent
}]
};
} catch (error) {
return {
contents: [{
uri: uri.href,
text: `Error: ${(error as Error).message}`
}]
};
}
}
);
// Helius API Tools
// Get Assets By Owner
server.tool(
"getAssetsByOwner",
"Get DAS API compliant NFTs owned by a specific address",
{
ownerAddress: z.string(),
page: z.number().optional(),
limit: z.number().optional()
},
async ({ ownerAddress, page, limit }) => {
try {
const response = await helius.rpc.getAssetsByOwner({
ownerAddress,
page: page || 1,
limit: limit || 100
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Asset
server.tool(
"getAsset",
"Get detailed information about a specific asset by its ID",
{
id: z.string()
},
async ({ id }) => {
try {
const response = await helius.rpc.getAsset({
id
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Assets By Group
server.tool(
"getAssetsByGroup",
"Get assets that belong to a specific group (like collection)",
{
groupKey: z.string(),
groupValue: z.string(),
page: z.number().optional(),
limit: z.number().optional()
},
async ({ groupKey, groupValue, page, limit }) => {
try {
const response = await helius.rpc.getAssetsByGroup({
groupKey,
groupValue,
page: page || 1,
limit: limit || 100
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Additional DAS API Tools
// Search Assets
server.tool(
"searchAssets",
"Search for assets using complex query parameters",
{
query: z.object({
ownerAddress: z.string().optional(),
creatorAddress: z.string().optional(),
collectionAddress: z.string().optional(),
grouping: z.array(z.object({
group_key: z.string(),
group_value: z.string()
})).optional(),
burnt: z.boolean().optional(),
compressible: z.boolean().optional(),
compressed: z.boolean().optional(),
tokenType: z.enum(["fungible", "nonFungible"]).optional()
}).optional(),
options: z.object({
limit: z.number().optional(),
page: z.number().optional(),
sortBy: z.enum(["created", "updated"]).optional(),
sortDirection: z.enum(["asc", "desc"]).optional()
}).optional()
},
async ({ query, options }) => {
try {
const response = await helius.rpc.searchAssets({
query: query || {},
options: options || { limit: 100, page: 1 }
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Asset Proof
server.tool(
"getAssetProof",
"Get the merkle proof for a compressed NFT",
{
id: z.string()
},
async ({ id }) => {
try {
const response = await helius.rpc.getAssetProof({
id
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Assets By Creator
server.tool(
"getAssetsByCreator",
"Get assets created by a specific creator address",
{
creatorAddress: z.string(),
page: z.number().optional(),
limit: z.number().optional()
},
async ({ creatorAddress, page, limit }) => {
try {
const response = await helius.rpc.getAssetsByCreator({
creatorAddress,
page: page || 1,
limit: limit || 100
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Assets By Authority
server.tool(
"getAssetsByAuthority",
"Get assets by update authority address",
{
authorityAddress: z.string(),
page: z.number().optional(),
limit: z.number().optional()
},
async ({ authorityAddress, page, limit }) => {
try {
const response = await helius.rpc.getAssetsByAuthority({
authorityAddress,
page: page || 1,
limit: limit || 100
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get NFT Editions
server.tool(
"getNftEditions",
"Get all editions of a master edition NFT",
{
masterEditionAddress: z.string(),
page: z.number().optional(),
limit: z.number().optional()
},
async ({ masterEditionAddress, page, limit }) => {
try {
const response = await helius.rpc.getNftEditions({
masterEditionAddress,
page: page || 1,
limit: limit || 100
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Mint API Tools
// Get Mintlist
server.tool(
"getMintlist",
"Get mintlist for a collection",
{
collectionAddress: z.string(),
page: z.number().optional(),
limit: z.number().optional()
},
async ({ collectionAddress, page, limit }) => {
try {
const response = await helius.mint.getMintlist({
collectionAddress,
page: page || 1,
limit: limit || 100
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Webhook Management Tools
// Get All Webhooks
server.tool(
"getAllWebhooks",
"Get all webhooks for your Helius API key",
{},
async () => {
try {
const response = await helius.webhooks.getAllWebhooks();
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Webhook By ID
server.tool(
"getWebhookByID",
"Get webhook details by webhook ID",
{
webhookID: z.string()
},
async ({ webhookID }) => {
try {
const response = await helius.webhooks.getWebhookByID({
webhookID
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Create Webhook
server.tool(
"createWebhook",
"Create a new webhook for address activity monitoring",
{
webhook: z.object({
webhookURL: z.string(),
transactionTypes: z.array(z.string()),
accountAddresses: z.array(z.string()),
webhookType: z.string(),
authHeader: z.string().optional(),
txnStatus: z.enum(["all", "success", "fail"]).optional()
})
},
async ({ webhook }) => {
try {
const response = await helius.webhooks.createWebhook({
webhook
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Create Collection Webhook
server.tool(
"createCollectionWebhook",
"Create a webhook to monitor NFT collections",
{
webhook: z.object({
webhookURL: z.string(),
transactionTypes: z.array(z.string()),
collectionQuery: z.object({
firstVerifiedCreators: z.array(z.string()).optional(),
verifiedCollectionAddresses: z.array(z.string()).optional()
}),
webhookType: z.string(),
authHeader: z.string().optional(),
txnStatus: z.enum(["all", "success", "fail"]).optional()
})
},
async ({ webhook }) => {
try {
const response = await helius.webhooks.createCollectionWebhook({
webhook
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Delete Webhook
server.tool(
"deleteWebhook",
"Delete a webhook by its ID",
{
webhookID: z.string()
},
async ({ webhookID }) => {
try {
const response = await helius.webhooks.deleteWebhook({
webhookID
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Smart Transactions and Helper Tools
// Get Current TPS
server.tool(
"getCurrentTPS",
"Get current transactions per second on Solana",
{},
async () => {
try {
const response = await helius.utils.getCurrentTPS();
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Token Holders
server.tool(
"getTokenHolders",
"Get holders of a specific token by mint address",
{
mintAddress: z.string(),
page: z.number().optional(),
limit: z.number().optional()
},
async ({ mintAddress, page, limit }) => {
try {
const response = await helius.utils.getTokenHolders({
mintAddress,
page: page || 1,
limit: limit || 100
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Priority Fee Estimate
server.tool(
"getPriorityFeeEstimate",
"Get estimated priority fees for transactions",
{
accountKeys: z.array(z.string())
},
async ({ accountKeys }) => {
try {
const response = await helius.utils.getPriorityFeeEstimate({
accountKeys
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Stake Accounts
server.tool(
"getStakeAccounts",
"Get stake accounts by their owner address",
{
ownerAddress: z.string()
},
async ({ ownerAddress }) => {
try {
const response = await helius.utils.getStakeAccounts({
ownerAddress
});
return {
content: [{ type: "text", text: JSON.stringify(response, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Get Compute Units
server.tool(
"getComputeUnits",
"Simulate a transaction to get the total compute units consumed",
{
instructions: z.array(z.any()),
payerKey: z.string(),
lookupTables: z.array(z.any()).optional(),
signers: z.array(z.any()).optional()
},
async ({ instructions, payerKey, lookupTables, signers }) => {
try {
const response = await helius.rpc.getComputeUnits({
instructions,
payerKey,
lookupTables: lookupTables || [],
signers: signers || []
});
if (response === null) {
return {
content: [{ type: "text", text: "Simulation failed: Unable to determine compute units" }]
};
}
return {
content: [{
type: "text",
text: `Simulation successful!\nCompute Units Consumed: ${response}`
}]
};
} catch (error) {
return {
content: [{ type: "text", text: `Error: ${(error as Error).message}` }]
};
}
}
);
// Enhanced Prompts for New Helius Features
server.prompt(
'collection-analysis',
'Get a detailed analysis of an NFT collection',
{ collectionAddress: z.string() },
({ collectionAddress }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Analyze the NFT collection with address ${collectionAddress}. Use the Helius API getAssetsByGroup endpoint with groupKey="collection" and groupValue being the collection address. For deeper insight, also fetch mintlist information using getMintlist. Provide statistics about rarity distribution, holder distribution, and market activity if available.`
}
}]
})
);
server.prompt(
'monitor-nft-collection',
'Set up a webhook to monitor NFT collection activity',
{
collectionAddress: z.string(),
webhookUrl: z.string().optional()
},
({ collectionAddress, webhookUrl }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Help me set up a webhook to monitor activity for the NFT collection with address ${collectionAddress}. ${webhookUrl ? `The webhook URL is ${webhookUrl}.` : 'Generate sample code that I can use to set up a webhook later.'} Include monitoring for mints, sales, listings, and transfers. Explain how I can process webhook events when they arrive.`
}
}]
})
);
server.prompt(
'network-status',
'Get Solana network status information',
{},
() => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Get the current Solana network status including TPS, average block time, and priority fee estimates. Analyze if this is a good time to send transactions or if the network is congested.`
}
}]
})
);
// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
server.connect(transport);