#
tokens: 12393/50000 18/18 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .gitignore
├── claude_desktop_config.json
├── package.json
├── pnpm-lock.yaml
├── README.md
├── src
│   ├── actions
│   │   ├── index.ts
│   │   ├── priorityFee
│   │   │   └── getPriorityFeeEstimate.ts
│   │   ├── security
│   │   │   └── getSecurityTxt.ts
│   │   ├── transaction
│   │   │   └── getTransactionHistory.ts
│   │   └── validator
│   │       └── getValidatorInfo.ts
│   ├── index.ts
│   ├── tests
│   │   ├── test-priority-fee.ts
│   │   └── test-transaction-history.ts
│   ├── tools
│   │   ├── priorityFee.ts
│   │   ├── security.ts
│   │   ├── transaction.ts
│   │   └── validator.ts
│   └── types
│       └── action.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
.env
node_modules
build

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# Solana Agent Kit MCP Server

[![npm version](https://badge.fury.io/js/solana-mpc.svg)](https://www.npmjs.com/package/solana-mpc)
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://opensource.org/licenses/ISC)

A Model Context Protocol (MCP) server that provides onchain tools for Claude AI, allowing it to interact with the Solana blockchain through a standardized interface. This implementation is based on the Solana Agent Kit and enables AI agents to perform blockchain operations seamlessly.

# DEMO VIDEO
https://www.youtube.com/watch?v=VbfSzFuIzn8

# Actions
### GET_VALIDATOR_INFO
Retrieves detailed information about Solana validators
Shows stake amounts, commission rates, and performance metrics

### GET_PRIORITY_FEE_ESTIMATE
Estimates optimal transaction fees based on current network conditions
Provides different fee tiers (low, medium, high) with expected confirmation times

### GET_TRANSACTION_HISTORY
Fetches transaction history for any Solana wallet or token account
Supports filtering by transaction types and pagination

### GET_SECURITY_TXT
Extracts security contact information from Solana programs
Helps users find proper channels for reporting vulnerabilities
Integrated Functions
Your MCP server also includes core Solana functionality for:

Asset management (GET_ASSET, MINT_NFT)
Token operations (DEPLOY_TOKEN, TRANSFER, TRADE)
Network information (GET_TPS, BALANCE)
Utility functions (REQUEST_FUNDS, REGISTER_DOMAIN)
These functions together provide a comprehensive interface for interacting with the Solana blockchain through a unified MCP server.


```

--------------------------------------------------------------------------------
/src/types/action.ts:
--------------------------------------------------------------------------------

```typescript
import { z } from "zod";
import { SolanaAgentKit } from "solana-agent-kit";

export interface Action {
  name: string;
  similes: string[];
  description: string;
  examples: any[];
  schema: z.ZodObject<any>;
  handler: (agent: SolanaAgentKit, input: Record<string, any>) => Promise<any>;
}

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
    "compilerOptions": {
      "target": "ES2022",
      "module": "Node16",
      "moduleResolution": "Node16",
      "outDir": "./build",
      "rootDir": "./src",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
  }
```

--------------------------------------------------------------------------------
/claude_desktop_config.json:
--------------------------------------------------------------------------------

```json
{
    "mcpServers": {
        "agent-kit": {
            "command": "node",
            "env" : {
                "OPENAI_API_KEY": "optional_openai_api_key_here",
                "RPC_URL": "your_rpc_url_here",
                "SOLANA_PRIVATE_KEY": "your_private_key_here"
            },
            "args": [
                "/absolute/path/to/build/index.js"
            ]
        }
    }
}


```

--------------------------------------------------------------------------------
/src/actions/index.ts:
--------------------------------------------------------------------------------

```typescript
import getValidatorInfoAction from "./validator/getValidatorInfo.js";
import getPriorityFeeEstimateAction from "./priorityFee/getPriorityFeeEstimate.js";
import getTransactionHistoryAction from "./transaction/getTransactionHistory.js";
import getSecurityTxtAction from "./security/getSecurityTxt.js";

// Export all actions
export const ACTIONS = {
  GET_VALIDATOR_INFO_ACTION: getValidatorInfoAction,
  GET_PRIORITY_FEE_ESTIMATE_ACTION: getPriorityFeeEstimateAction,
  GET_TRANSACTION_HISTORY_ACTION: getTransactionHistoryAction,
  GET_SECURITY_TXT_ACTION: getSecurityTxtAction,
};

// Export individual actions for direct imports
export { default as GET_VALIDATOR_INFO_ACTION } from "./validator/getValidatorInfo.js";
export { default as GET_PRIORITY_FEE_ESTIMATE_ACTION } from "./priorityFee/getPriorityFeeEstimate.js";
export { default as GET_TRANSACTION_HISTORY_ACTION } from "./transaction/getTransactionHistory.js";
export { default as GET_SECURITY_TXT_ACTION } from "./security/getSecurityTxt.js";

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "solana-mcp-sendai-hackathon",
  "version": "1.0.0",
  "description": "A Model Context Protocol server for interacting with the Solana blockchain, powered by the [Solana Agent Kit](https://github.com/sendaifun/solana-agent-kit)",
  "main": "build/index.js",
  "type": "module",
  "bin": {
    "solana-mcp": "./build/index.js"
  },
  "scripts": {
    "build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
    "start": "node build/index.js",
    "dev": "tsx watch src/index.ts"
  },
  "files": [
    "build"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/cryptoleek/solana-mcp-sendai-hackathon.git"
  },
  "keywords": [
    "solana",
    "mcp",
    "solana-agent-kit",
    "solana-mcp"
  ],
  "author": "cryptoleek",
  "license": "MIT",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "@solana/spl-memo": "^0.2.5",
    "@solana/spl-token": "^0.4.13",
    "@solana/web3.js": "^1.98.0",
    "bs58": "^6.0.0",
    "dotenv": "^16.4.7",
    "helius-sdk": "^1.4.2",
    "solana-agent-kit": "1.4.8",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/node": "^22.13.4",
    "ts-node": "^10.9.2",
    "typescript": "^5.7.3"
  },
  "packageManager": "[email protected]"
}

```

--------------------------------------------------------------------------------
/src/tools/validator.ts:
--------------------------------------------------------------------------------

```typescript
import { Connection, PublicKey } from "@solana/web3.js";

export async function getValidatorInfo(validatorPubkey: PublicKey, connection: Connection) {
  try {
    // Get the current epoch info
    const epochInfo = await connection.getEpochInfo();
    
    // Get vote accounts to find our validator
    const voteAccounts = await connection.getVoteAccounts();
    
    // Find the validator in either current or delinquent vote accounts
    const allAccounts = [...voteAccounts.current, ...voteAccounts.delinquent];
    const validatorAccount = allAccounts.find(
      account => account.votePubkey === validatorPubkey.toString()
    );
    
    if (!validatorAccount) {
      throw new Error("Validator not found");
    }
    
    // Get validator's identity account balance
    const balance = await connection.getBalance(new PublicKey(validatorAccount.nodePubkey));
    
    return {
      identity: validatorAccount.nodePubkey,
      vote: validatorAccount.votePubkey,
      commission: validatorAccount.commission,
      activatedStake: validatorAccount.activatedStake,
      epochVoteAccount: validatorAccount.epochVoteAccount,
      epochCredits: validatorAccount.epochCredits,
      delinquent: voteAccounts.delinquent.some(
        account => account.votePubkey === validatorPubkey.toString()
      ),
      lastVote: validatorAccount.lastVote, 
      balance: balance,
      currentEpoch: epochInfo.epoch,
    };
  } catch (error) {
    throw error;
  }
}

```

--------------------------------------------------------------------------------
/src/tests/test-transaction-history.ts:
--------------------------------------------------------------------------------

```typescript
import { Connection, PublicKey } from '@solana/web3.js';
import * as dotenv from 'dotenv';
import { getTransactionHistory } from '../tools/transaction.js';

dotenv.config();

async function main() {
  try {
    // Validate environment variables
    if (!process.env.RPC_URL) {
      throw new Error('RPC_URL environment variable is required');
    }

    // Create a connection to the Solana cluster
    const connection = new Connection(process.env.RPC_URL);
    
    // Print connection info for debugging
    console.log(`Using RPC URL: ${process.env.RPC_URL.substring(0, 30)}...`);
    
    try {
      const version = await connection.getVersion();
      console.log(`Connected to Solana ${version["solana-core"]}`);
    } catch (err) {
      console.log(`Failed to get version: ${err}`);
    }
    
    // Test address - using the provided address
    const testAddress = new PublicKey('DWdBJfMzVXJCB3TMdFzxhTeap6pMQCajamApbqXHbkQ4');
    
    console.log(`Fetching transaction history for address: ${testAddress.toString()}`);
    
    // Test with default options
    console.log('\n--- Test 1: Retrieve all txns ---');
    const defaultResults = await getTransactionHistory(testAddress, connection);
    console.log(`Retrieved ${defaultResults.length} transactions`);
    // Test with options
    console.log('\n--- Test 2: Options (limit: 10, type: TRANSFER) ---');
    const options = {
      limit: 10,
      types: ['TRANSFER']
    };
    const optionResults = await getTransactionHistory(testAddress, connection, options);
    console.log(`Retrieved ${optionResults.length} transactions`);

    console.log('--- Printing the first 10 transactions ---');
    for (const txn of optionResults.slice(0, 10)) {
      console.log(txn);
    }
    
    console.log('\nAll tests completed!');
  } catch (error) {
    console.error('Error running tests:', error);
    process.exit(1);
  }
}

main();

```

--------------------------------------------------------------------------------
/src/actions/validator/getValidatorInfo.ts:
--------------------------------------------------------------------------------

```typescript
import { Action } from "../../types/action.js";
import { SolanaAgentKit } from "solana-agent-kit";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { getValidatorInfo } from "../../tools/validator.js";

const getValidatorInfoAction: Action = {
  name: "GET_VALIDATOR_INFO",
  similes: [
    "validator status",
    "check validator",
    "validator info",
    "validator details",
    "node information",
  ],
  description:
    "Get detailed information about a Solana validator including stake, commission, and performance",
  examples: [
    [
      {
        input: {
          validatorAddress: "he1iusunGwqrNtafDtLdhsUQDFvo13z9sUa36PauBtk",
        },
        output: {
          status: "success",
          info: {
            identity: "HEL1USMZKAL2odpNBj2oCjffnFGaYwmbGmyewGv1e2TU",
            vote: "he1iusunGwqrNtafDtLdhsUQDFvo13z9sUa36PauBtk",
            commission: 10,
            activatedStake: 1520000000,
            delinquent: false,
            skipRate: 0.0234,
          },
          message: "Successfully retrieved validator information",
        },
        explanation: "Get information about a specific Solana validator",
      },
    ],
  ],
  schema: z.object({
    validatorAddress: z
      .string()
      .describe("The public key of the validator to get information about"),
  }),
  handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
    try {
      const validatorPubkey = new PublicKey(input.validatorAddress);
      const connection = agent.connection;
      
      const validatorInfo = await getValidatorInfo(validatorPubkey, connection);

      return {
        status: "success",
        info: validatorInfo,
        message: "Successfully retrieved validator information",
      };
    } catch (error: any) {
      return {
        status: "error",
        message: `Failed to get validator info: ${error.message}`,
      };
    }
  },
};

export default getValidatorInfoAction;

```

--------------------------------------------------------------------------------
/src/actions/security/getSecurityTxt.ts:
--------------------------------------------------------------------------------

```typescript
import { Action } from "../../types/action.js";
import { SolanaAgentKit } from "solana-agent-kit";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { getSecurityTxtInfo } from "../../tools/security.js";

const getSecurityTxtAction: Action = {
  name: "GET_SECURITY_TXT",
  similes: [
    "security.txt",
    "security contact",
    "program security",
    "security disclosure",
    "vulnerability reporting",
    "security information",
    "contact maintainers",
    "security policy",
  ],
  description:
    "Extract and display the security.txt file information for a given Solana program, making it easier to contact the program's maintainers with security concerns",
  examples: [
    [
      {
        input: {
          programId: "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
        },
        output: {
          status: "success",
          info: {
            contact: "mailto:[email protected]",
            expires: "2023-12-31T23:59:59.000Z",
            encryption: "https://solana.com/pgp-key.txt",
            acknowledgments: "https://solana.com/hall-of-fame",
            preferredLanguages: "en",
          },
          message: "Successfully retrieved security.txt information",
        },
        explanation: "Get security contact information for the Solana Token Program",
      },
    ],
  ],
  schema: z.object({
    programId: z
      .string()
      .describe("The program ID (public key) of the Solana program to inspect"),
  }),
  handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
    try {
      const programPubkey = new PublicKey(input.programId);
      const connection = agent.connection;
      
      const securityInfo = await getSecurityTxtInfo(programPubkey, connection);

      // If we couldn't find any security information
      if (Object.keys(securityInfo).length === 0) {
        return {
          status: "warning",
          info: null,
          message: "No security.txt information found for this program",
        };
      }

      return {
        status: "success",
        info: securityInfo,
        message: "Successfully retrieved security.txt information",
      };
    } catch (error: any) {
      return {
        status: "error",
        message: `Failed to get security.txt info: ${error.message}`,
      };
    }
  },
};

export default getSecurityTxtAction;

```

--------------------------------------------------------------------------------
/src/tests/test-priority-fee.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

/**
 * Simple test script to debug the priority fee estimation tool
 * Tests both account keys and transaction-based methods
 */

import { Connection } from '@solana/web3.js';
import { 
  estimatePriorityFee, 
  estimatePriorityFeeByAccountKeys, 
  estimatePriorityFeeByTransaction, 
  PriorityFeeEstimate
} from '../tools/priorityFee.js'; 

import * as dotenv from "dotenv";

dotenv.config();

// Set up Solana connection (using a public RPC endpoint)
// const RPC_ENDPOINT = 'https://api.mainnet-beta.solana.com';
// For Helius API, you might want to use your own endpoint with API key
const RPC_ENDPOINT = process.env.RPC_URL;

async function testPriorityFeeEstimate() {
  try {
    console.log('=== Testing Priority Fee Estimation ===');
    console.log('Using RPC endpoint:', RPC_ENDPOINT);
    
    // Create Solana connection
    const connection = new Connection(RPC_ENDPOINT || "");
    
    // Test 1: Default method (account keys with default Jupiter account)
    console.log('\n--- Test 1: Default method ---');
    const defaultEstimate = await estimatePriorityFee(connection);
    printEstimate(defaultEstimate);
    
    // Test 2: Account keys method with custom accounts
    console.log('\n--- Test 2: Account keys method with custom accounts ---');
    const customAccountsEstimate = await estimatePriorityFeeByAccountKeys(
      connection,
      [
        'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA', // Token program
        'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL'  // Associated token program
      ]
    );
    printEstimate(customAccountsEstimate);
    
    // Test 3: Transaction method
    console.log('\n--- Test 3: Transaction method ---');
    const transactionEstimate = await estimatePriorityFeeByTransaction(connection);
    printEstimate(transactionEstimate);
    
    console.log('\n=== All tests completed ===');
  } catch (error: any ) {
    console.error('Failed to run tests:', error);
    if (error.cause) {
      console.error('Error cause:', error.cause);
    }
  }
}

/**
 * Helper function to print the fee estimate in a readable format
 */
function printEstimate(estimate: PriorityFeeEstimate) {
  console.log('Priority Fee Estimate:');
  console.log('- Low:', estimate.low, 'microLamports');
  console.log('- Medium:', estimate.medium, 'microLamports');
  console.log('- High:', estimate.high, 'microLamports');
  console.log('- Suggested:', estimate.suggested, 'microLamports');
  console.log('- Time Estimates:', estimate.timeEstimates);
  console.log('- High Load Behavior:', estimate.highLoadBehavior);
}

// Run the test
await testPriorityFeeEstimate()
  .catch(err => {
    console.error('Unhandled error:', err);
    process.exit(1);
  });

```

--------------------------------------------------------------------------------
/src/tools/transaction.ts:
--------------------------------------------------------------------------------

```typescript
import { Connection, PublicKey } from '@solana/web3.js';
import { parseTransaction } from 'solana-agent-kit/dist/tools/index.js';

// Interface for transaction history options
interface TransactionHistoryOptions {
  limit?: number;
  before?: string;
  until?: string;
  types?: string[];
  minContextSlot?: number;
}

// Interface for transaction data
interface TransactionData {
  signature: string;
  slot: number;
  timestamp: number;
  err: any;
  memo: string | null;
  blockTime: number;
  type: string;
  fee: number;
  status: string;
}

/**
 * Get transaction history for a Solana wallet address or token account using Helius API
 * @param publicKey - The public key to get transaction history for
 * @param connection - Solana connection object (not used with Helius but kept for compatibility)
 * @param options - Optional parameters for filtering transactions
 * @returns Array of transaction data
 */
export async function getTransactionHistory(
  publicKey: PublicKey,
  connection: Connection,
  options: TransactionHistoryOptions = {}
): Promise<TransactionData[]> {
  try {
    const address = publicKey.toString();
    
    // Helius SDK doesn't have a direct method for transaction history
    // We'll use the fetchTransactionHistory helper function to make the API call
    const transactions = await fetchTransactionHistory(connection, address, options);
    
    return transactions;
  } catch (error) {
    console.error('Error fetching transaction history:', error);
    throw error;
  }
}

/**
 * Helper function to fetch transaction history using Helius API directly
 * @param address - The address to get transaction history for
 * @param options - Query parameters
 * @returns Array of transaction data from Helius
 */
async function fetchTransactionHistory(
  connection: Connection,
  address: string,
  options: TransactionHistoryOptions = {}
): Promise<TransactionData[]> {
  try {
    // Build query parameters
    const queryParams: Record<string, string> = {};
    
    if (options.limit) {
      queryParams.limit = options.limit.toString();
    }
    
    if (options.before) {
      queryParams.before = options.before;
    }
    
    if (options.until) {
      queryParams.until = options.until;
    }
    
    if (options.types && options.types.length > 0) {
      queryParams.type = options.types.join(',');
    }
    
    // Convert params to URL query string
    const queryString = Object.entries(queryParams)
      .map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
      .join('&');

    
    const rpcUrl = connection.rpcEndpoint;
    const apiKey = rpcUrl.split('api-key=')[1];   

    if (!apiKey) {
      throw new Error('Missing Helius API key');
    }
    
    // Build the URL
    const url = `https://api.helius.xyz/v0/addresses/${address}/transactions?api-key=${apiKey}${queryString ? `&${queryString}` : ''}`;
    
    // Fetch data from Helius API
    const response = await fetch(url);
    
    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`Helius API error: ${response.status} ${errorText}`);
    }
    
    const data = await response.json();
    
    // Map the response to our expected format
    return data.map((tx: any) => ({
      signature: tx.signature,
      slot: tx.slot,
      timestamp: tx.timestamp,
      err: tx.err,
      memo: tx.memo || null,
      blockTime: tx.timestamp,
      type: tx.type || 'Unknown',
      fee: tx.fee || 0,      
      status: tx.err ? 'Failed' : 'Success'
    }));
  } catch (error) {
    console.error('Error in fetchTransactionHistory:', error);
    throw error;
  }
}


```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node

import { ACTIONS as SAK_ACTIONS, SolanaAgentKit, startMcpServer } from "solana-agent-kit";
import { ACTIONS as CUSTOM_ACTIONS } from "./actions/index.js";
import * as dotenv from "dotenv";

dotenv.config();

// Validate required environment variables
function validateEnvironment() {
    const requiredEnvVars = {
        'SOLANA_PRIVATE_KEY': process.env.SOLANA_PRIVATE_KEY,
        'RPC_URL': process.env.RPC_URL
    };

    const missingVars = Object.entries(requiredEnvVars)
        .filter(([_, value]) => !value)
        .map(([key]) => key);

    if (missingVars.length > 0) {
        throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`);
    }
}

async function main() {
    try {
        // Validate environment before proceeding
        validateEnvironment();

        const RPC_URL = process.env.RPC_URL as string
        const HELIUS_API_KEY = RPC_URL.split('api-key=')[1]

        // Initialize the agent with error handling
        const agent = new SolanaAgentKit(
            process.env.SOLANA_PRIVATE_KEY!,
            process.env.RPC_URL!,
            {
                OPENAI_API_KEY: process.env.OPENAI_API_KEY || "",
                PERPLEXITY_API_KEY: process.env.PERPLEXITY_API_KEY || "",
                HELIUS_API_KEY: HELIUS_API_KEY || ""
            }
        );

        // Debug: Log our custom actions
        console.log("Registering custom actions:");
        console.log("GET_VALIDATOR_INFO_ACTION schema:", CUSTOM_ACTIONS.GET_VALIDATOR_INFO_ACTION.schema);
        console.log("GET_PRIORITY_FEE_ESTIMATE_ACTION schema:", CUSTOM_ACTIONS.GET_PRIORITY_FEE_ESTIMATE_ACTION.schema);
        console.log("GET_TRANSACTION_HISTORY_ACTION schema:", CUSTOM_ACTIONS.GET_TRANSACTION_HISTORY_ACTION.schema);
        console.log("GET_SECURITY_TXT_ACTION schema:", CUSTOM_ACTIONS.GET_SECURITY_TXT_ACTION.schema);

        const mcp_actions = {
            GET_ASSET: SAK_ACTIONS.GET_ASSET_ACTION,
            DEPLOY_TOKEN: SAK_ACTIONS.DEPLOY_TOKEN_ACTION,
            FETCH_PRICE: SAK_ACTIONS.FETCH_PRICE_ACTION,
            GET_WALLET_ADDRESS: SAK_ACTIONS.WALLET_ADDRESS_ACTION,
            BALANCE: SAK_ACTIONS.BALANCE_ACTION,
            TRANSFER: SAK_ACTIONS.TRANSFER_ACTION,
            MINT_NFT: SAK_ACTIONS.MINT_NFT_ACTION,
            TRADE: SAK_ACTIONS.TRADE_ACTION,
            REQUEST_FUNDS: SAK_ACTIONS.REQUEST_FUNDS_ACTION,
            REGISTER_DOMAIN: SAK_ACTIONS.RESOLVE_DOMAIN_ACTION,
            GET_TPS: SAK_ACTIONS.GET_TPS_ACTION,
            PARSE_TRANSACTION_ACTION: SAK_ACTIONS.PARSE_TRANSACTION_ACTION,
            // Add our custom actions
            GET_VALIDATOR_INFO: CUSTOM_ACTIONS.GET_VALIDATOR_INFO_ACTION,
            GET_PRIORITY_FEE_ESTIMATE: CUSTOM_ACTIONS.GET_PRIORITY_FEE_ESTIMATE_ACTION,
            GET_TRANSACTION_HISTORY: CUSTOM_ACTIONS.GET_TRANSACTION_HISTORY_ACTION,
            GET_SECURITY_TXT: CUSTOM_ACTIONS.GET_SECURITY_TXT_ACTION,
        };

        // Debug: Log all registered actions
        console.log("All registered MCP actions:", Object.keys(mcp_actions));

        // Start the MCP server with error handling
        await startMcpServer(mcp_actions, agent, { 
            name: "solana-agent", 
            version: "0.0.1"
        });

        // Add console logging for debugging
        console.log("MCP server started successfully");        
    } catch (error) {
        console.error('Failed to start MCP server:', error instanceof Error ? error.message : String(error));
        process.exit(1);
    }
}

// Handle uncaught exceptions and rejections
process.on('uncaughtException', (error) => {
    console.error('Uncaught Exception:', error);
    process.exit(1);
});

process.on('unhandledRejection', (reason, promise) => {
    console.error('Unhandled Rejection at:', promise, 'reason:', reason);
    process.exit(1);
});

main();
```

--------------------------------------------------------------------------------
/src/actions/priorityFee/getPriorityFeeEstimate.ts:
--------------------------------------------------------------------------------

```typescript
import { Action } from "../../types/action.js";
import { SolanaAgentKit } from "solana-agent-kit";
import { z } from "zod";
import { estimatePriorityFee } from "../../tools/priorityFee.js";

const getPriorityFeeEstimateAction: Action = {
  name: "GET_PRIORITY_FEE_ESTIMATE",
  similes: [
    "estimate priority fee",
    "transaction fee estimate",
    "optimal priority fee",
    "solana fee estimate",
    "transaction cost estimate",
  ],
  description:
    "Estimates optimal priority fees for Solana transactions based on recent network activity",
  examples: [
    [
      {
        input: {},
        output: {
          status: "success",
          estimate: {
            low: 1000,
            medium: 10000,
            high: 100000,
            suggested: 12000,
            timeEstimates: {
              low: "3-5 seconds",
              medium: "2-3 seconds",
              high: "1-2 seconds"
            }
          },
          message: "Successfully estimated priority fees",
        },
        explanation: "Get current priority fee estimates for Solana transactions",
      },
    ],
    [
      {
        input: { method: "transaction" },
        output: {
          status: "success",
          estimate: {
            low: 1000,
            medium: 10000,
            high: 100000,
            suggested: 12000,
            timeEstimates: {
              low: "3-5 seconds",
              medium: "2-3 seconds",
              high: "1-2 seconds"
            }
          },
          message: "Successfully estimated priority fees using transaction method",
        },
        explanation: "Get priority fee estimates using the transaction-based method",
      },
    ],
    [
      {
        input: { 
          method: "accountKeys",
          accountKeys: ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"]
        },
        output: {
          status: "success",
          estimate: {
            low: 1000,
            medium: 10000,
            high: 100000,
            suggested: 12000,
            timeEstimates: {
              low: "3-5 seconds",
              medium: "2-3 seconds",
              high: "1-2 seconds"
            }
          },
          message: "Successfully estimated priority fees for specific account keys",
        },
        explanation: "Get priority fee estimates for specific account keys",
      },
    ],
  ],
  // Define schema with optional parameters
  schema: z.object({
    method: z.enum(["accountKeys", "transaction"]).optional(),
    accountKeys: z.array(z.string()).optional(),
    serializedTransaction: z.string().optional(),
  }),
  handler: async (agent: SolanaAgentKit, params: any) => {
    try {
      const connection = agent.connection;
      
      // Extract parameters
      const method = params.method;
      const accountKeys = params.accountKeys;
      const serializedTransaction = params.serializedTransaction;
      
      // Call the estimatePriorityFee function with the provided parameters
      const feeEstimate = await estimatePriorityFee(connection, {
        method,
        accountKeys,
        serializedTransaction
      });

      // Prepare success message based on the method used
      let message = "Successfully estimated priority fees";
      if (method === "transaction") {
        message = "Successfully estimated priority fees using transaction method";
      } else if (method === "accountKeys" && accountKeys) {
        message = "Successfully estimated priority fees for specific account keys";
      }

      return {
        status: "success",
        estimate: feeEstimate,
        message,
      };
    } catch (error: any) {
      return {
        status: "error",
        message: `Failed to estimate priority fees: ${error.message}`,
        error: error.toString(),
        stack: error.stack
      };
    }
  },
};

export default getPriorityFeeEstimateAction;

```

--------------------------------------------------------------------------------
/src/tools/security.ts:
--------------------------------------------------------------------------------

```typescript
import { Connection, PublicKey } from "@solana/web3.js";
import * as bs58 from "bs58";

// Interface for security.txt content
export interface SecurityTxtInfo {
  contact?: string;
  expires?: string;
  encryption?: string;
  acknowledgments?: string;
  preferredLanguages?: string;
  canonical?: string;
  policy?: string;
  hiring?: string;
  // Any additional fields that might be in the security.txt
  [key: string]: string | undefined;
}

/**
 * Extracts and parses security.txt information from a Solana program
 * 
 * @param programId - The PublicKey of the Solana program to inspect
 * @param connection - The Solana connection object
 * @returns The parsed security.txt information
 */
export async function getSecurityTxtInfo(programId: PublicKey, connection: Connection): Promise<SecurityTxtInfo> {
  try {
    // Get the program account data
    const programAccount = await connection.getAccountInfo(programId);
    
    if (!programAccount) {
      throw new Error("Program account not found");
    }
    
    // Check if this is an upgradeable program
    const BPF_UPGRADEABLE_LOADER_ID = new PublicKey("BPFLoaderUpgradeab1e11111111111111111111111");
    
    let programData: Buffer;
    
    if (programAccount.owner.equals(BPF_UPGRADEABLE_LOADER_ID)) {
      // This is an upgradeable program, we need to find the program data account
      // The first 4 bytes indicate the account type
      const accountType = programAccount.data.slice(0, 4);
      
      // Check if this is a Program account (type 2)
      if (accountType[0] === 2) {
        // Extract the program data address (next 32 bytes)
        const programDataAddress = new PublicKey(programAccount.data.slice(4, 36));
        
        // Get the program data account
        const programDataAccount = await connection.getAccountInfo(programDataAddress);
        
        if (!programDataAccount) {
          throw new Error("Program data account not found");
        }
        
        // The program data starts after the header (first 8 bytes)
        // First byte is account type, next 7 bytes are the slot
        programData = programDataAccount.data.slice(8);
      } else {
        throw new Error("Not a valid upgradeable program account");
      }
    } else {
      // For non-upgradeable programs, use the account data directly
      programData = programAccount.data;
    }
    
    // Now search for security.txt in the program data
    return findAndParseSecurityTxt(programData);
  } catch (error) {
    throw error;
  }
}

/**
 * Finds and parses security.txt content in program data
 * 
 * @param programData - The program binary data
 * @returns Parsed security.txt information
 */
function findAndParseSecurityTxt(programData: Buffer): SecurityTxtInfo {
  try {
    // Convert the binary data to a string
    // Note: This is a simplification, as the security.txt might be encoded in various ways
    const dataString = new TextDecoder().decode(programData);
    
    // Look for security.txt pattern
    // Pattern 1: Standard security.txt format
    const securityTxtMatch = dataString.match(/(?:^|\n)# ?security\.txt(?:\n|$)([\s\S]*?)(?:\n\n|\n#|$)/i);
    
    if (securityTxtMatch && securityTxtMatch[1]) {
      return parseSecurityTxt(securityTxtMatch[1]);
    }
    
    // Pattern 2: Look for BEGIN/END SECURITY.TXT markers
    const securityTxtBlockMatch = dataString.match(/-----BEGIN SECURITY\.TXT-----([\s\S]*?)-----END SECURITY\.TXT-----/i);
    
    if (securityTxtBlockMatch && securityTxtBlockMatch[1]) {
      return parseSecurityTxt(securityTxtBlockMatch[1]);
    }
    
    // Pattern 3: Look for common security.txt fields
    const fieldMatch = dataString.match(/Contact: ([^\n]+)/i) || 
                       dataString.match(/Security-Contact: ([^\n]+)/i) ||
                       dataString.match(/mailto:([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/i);
    
    if (fieldMatch) {
      const result: SecurityTxtInfo = {};
      result.contact = fieldMatch[1].trim();
      return result;
    }
    
    // If we couldn't find any specific security.txt format, try to extract any security-related info
    return extractSecurityInfoFromText(dataString);
  } catch (error) {
    // If we encounter any error during parsing, return an empty object
    console.error("Error parsing security.txt:", error);
    return {};
  }
}

/**
 * Parses security.txt content into a structured object
 * 
 * @param content - The raw security.txt content
 * @returns Structured security.txt information
 */
function parseSecurityTxt(content: string): SecurityTxtInfo {
  const result: SecurityTxtInfo = {};
  
  // Split by lines and process each line
  const lines = content.split('\n');
  
  for (const line of lines) {
    // Skip empty lines and comments
    if (!line.trim() || line.trim().startsWith('#')) {
      continue;
    }
    
    // Extract field and value
    const match = line.match(/^([^:]+):\s*(.*)$/);
    if (match) {
      const [_, field, value] = match;
      const fieldName = field.trim().toLowerCase();
      
      // Convert kebab-case to camelCase
      const camelCaseField = fieldName.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
      
      result[camelCaseField] = value.trim();
    }
  }
  
  return result;
}

/**
 * Attempts to extract security information from program text when no explicit security.txt is found
 * 
 * @param text - The program text to analyze
 * @returns Any security information found
 */
function extractSecurityInfoFromText(text: string): SecurityTxtInfo {
  const result: SecurityTxtInfo = {};
  
  // Look for common patterns that might indicate security contact info
  const contactMatches = [
    // Email patterns
    ...text.match(/security@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g) || [],
    ...text.match(/mailto:[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g) || [],
    // URL patterns
    ...text.match(/https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\/security/g) || [],
    ...text.match(/https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\/responsible-disclosure/g) || [],
    ...text.match(/https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\/vulnerability/g) || [],
  ];
  
  if (contactMatches.length > 0) {
    result.contact = contactMatches[0];
  }
  
  // Look for PGP key patterns
  const pgpMatches = text.match(/-----BEGIN PGP PUBLIC KEY BLOCK-----([\s\S]*?)-----END PGP PUBLIC KEY BLOCK-----/g);
  if (pgpMatches) {
    result.encryption = "PGP key found in program data";
  }
  
  return result;
}

```

--------------------------------------------------------------------------------
/src/actions/transaction/getTransactionHistory.ts:
--------------------------------------------------------------------------------

```typescript
import { Action } from "../../types/action.js";
import { SolanaAgentKit } from "solana-agent-kit";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
import { getTransactionHistory } from "../../tools/transaction.js";
import dotenv from "dotenv";

// Load environment variables
dotenv.config();

const getTransactionHistoryAction: Action = {
  name: "GET_TRANSACTION_HISTORY",
  similes: [
    "transaction history",
    "account transactions",
    "transaction list",
    "tx history",
    "recent transactions",
  ],
  description:
    "Get transaction history for a Solana wallet address or token account using Helius API",
  examples: [
    [
      {
        input: { 
          address: "8zFZHuSRuDpuAR7J6FzwyF3vKNx4CVW3DFHJerQhc7Zd"
        },
        output: {
          status: "success",
          transactions: [
            {
              signature: "5UJpjrKQ8641Q8kPudtvtqR2SgZMz5rSbdpuNP1qy6BxAw4aY3bRfyZQqGKEK5yQXi3yk4pVMRLqzYjnb3bawRn5",
              slot: 172492080,
              timestamp: 1678901234,
              err: null,
              memo: null,
              blockTime: 1678901234,
              type: "System Transfer",
              fee: 5000,
              status: "Success"
            }
          ],
          message: "Successfully retrieved transaction history"
        },
        explanation: "Get recent transaction history for a specific Solana address",
      },
    ],
    [
      {
        input: { 
          address: "8zFZHuSRuDpuAR7J6FzwyF3vKNx4CVW3DFHJerQhc7Zd",
          limit: 5
        },
        output: {
          status: "success",
          transactions: [
            {
              signature: "5UJpjrKQ8641Q8kPudtvtqR2SgZMz5rSbdpuNP1qy6BxAw4aY3bRfyZQqGKEK5yQXi3yk4pVMRLqzYjnb3bawRn5",
              slot: 172492080,
              timestamp: 1678901234,
              err: null,
              memo: null,
              blockTime: 1678901234,
              type: "System Transfer",
              fee: 5000,
              status: "Success"
            }
          ],
          message: "Successfully retrieved 5 most recent transactions"
        },
        explanation: "Get the 5 most recent transactions for a Solana address",
      },
    ],
    [
      {
        input: { 
          address: "8zFZHuSRuDpuAR7J6FzwyF3vKNx4CVW3DFHJerQhc7Zd",
          before: "5UJpjrKQ8641Q8kPudtvtqR2SgZMz5rSbdpuNP1qy6BxAw4aY3bRfyZQqGKEK5yQXi3yk4pVMRLqzYjnb3bawRn5"
        },
        output: {
          status: "success",
          transactions: [
            {
              signature: "4tSRZ8QVNfUyHuJGZQvJzuUbq3nBpZ9QFNEsrey9mEbY6iN7VDuZtFGBciogSGkAiKwbVL8YgYNJZP1XNqXhRmML",
              slot: 172492070,
              timestamp: 1678901200,
              err: null,
              memo: null,
              blockTime: 1678901200,
              type: "Token Transfer",
              fee: 5000,
              status: "Success"
            }
          ],
          message: "Successfully retrieved transaction history before specified signature"
        },
        explanation: "Get transaction history before a specific transaction signature",
      },
    ],
    [
      {
        input: { 
          address: "8zFZHuSRuDpuAR7J6FzwyF3vKNx4CVW3DFHJerQhc7Zd",
          types: ["NFT_SALE", "NFT_LISTING"]
        },
        output: {
          status: "success",
          transactions: [
            {
              signature: "4tSRZ8QVNfUyHuJGZQvJzuUbq3nBpZ9QFNEsrey9mEbY6iN7VDuZtFGBciogSGkAiKwbVL8YgYNJZP1XNqXhRmML",
              slot: 172492070,
              timestamp: 1678901200,
              err: null,
              memo: null,
              blockTime: 1678901200,
              type: "NFT_SALE",
              fee: 5000,
              status: "Success"
            }
          ],
          message: "Successfully retrieved NFT sales and listings transactions"
        },
        explanation: "Get NFT sales and listings transactions for a Solana address",
      },
    ],
  ],
  // Define schema with optional address and optional parameters
  schema: z.object({
    address: z.string().min(32).max(44).optional(),
    limit: z.number().min(1).max(100).optional(),
    before: z.string().optional(),
    until: z.string().optional(),
    minContextSlot: z.number().optional(),
    types: z.array(z.string()).optional(),
  }),
  handler: async (agent: SolanaAgentKit, params: any) => {
    try {
      // Check if address is provided
      if (!params.address) {
        return {
          status: "input_needed",
          message: "Please provide a Solana wallet address to view transaction history.",
          error: "Missing address parameter"
        };
      }
      
      const connection = agent.connection;
      
      // Extract parameters
      const address = params.address;
      const limit = params.limit || 10;
      const before = params.before;
      const until = params.until;
      const minContextSlot = params.minContextSlot;
      const types = params.types;
      
      // Validate address
      let publicKey: PublicKey;
      try {
        publicKey = new PublicKey(address);
      } catch (error) {
        return {
          status: "error",
          message: "Invalid Solana address provided",
          error: "Invalid public key format"
        };
      }
      
      // Call the getTransactionHistory function with the provided parameters
      const transactions = await getTransactionHistory(publicKey, connection, {
        limit,
        before,
        until,
        minContextSlot,
        types
      });
      
      // Prepare success message based on the parameters used
      let message = "Successfully retrieved transaction history";
      if (limit) {
        message = `Successfully retrieved ${limit} most recent transactions`;
      }
      if (before) {
        message = "Successfully retrieved transaction history before specified signature";
      }
      if (until) {
        message = "Successfully retrieved transaction history until specified signature";
      }
      if (types && types.length > 0) {
        message = `Successfully retrieved ${types.join(', ')} transactions`;
      }
      
      return {
        status: "success",
        transactions: transactions.map(tx => ({
          signature: tx.signature,
          slot: tx.slot,
          timestamp: tx.timestamp,
          err: tx.err,
          memo: tx.memo,
          blockTime: tx.blockTime,
          type: tx.type,
          fee: tx.fee,
          status: tx.status
        })),
        message,
      };
    } catch (error: any) {
      return {
        status: "error",
        message: `Failed to retrieve transaction history: ${error.message}`,
        error: error.toString(),
        stack: error.stack
      };
    }
  },
};

export default getTransactionHistoryAction;

```

--------------------------------------------------------------------------------
/src/tools/priorityFee.ts:
--------------------------------------------------------------------------------

```typescript
import { Connection, ComputeBudgetProgram, Transaction, PublicKey, Keypair } from "@solana/web3.js";

export interface PriorityFeeEstimate {
  low: number;
  medium: number;
  high: number;
  veryHigh?: number;
  min?: number;
  unsafeMax?: number;
  suggested: number;
  timeEstimates: {
    low: string;
    medium: string;
    high: string;
  };
  highLoadBehavior?: {
    low: string;
    medium: string;
    high: string;
  };
  // Raw Helius API response
  priorityFeeEstimate?: number;
  priorityFeeLevels?: {
    min?: number;
    low: number;
    medium: number;
    high: number;
    veryHigh?: number;
    unsafeMax?: number;
  };
}

/**
 * Estimates priority fees using Helius API via account keys method
 * @param connection Solana connection object
 * @param accountKeys Optional array of account keys to use for estimation (defaults to Jupiter account)
 * @param options Optional configuration for fee estimation
 * @returns Priority fee estimate with different fee levels
 */
export async function estimatePriorityFeeByAccountKeys(
  connection: Connection,
  accountKeys: string[] = ["JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"],
  options?: {
    lookbackSlots?: number;
    includeVote?: boolean;
  }
): Promise<PriorityFeeEstimate> {
  try {
    // Get RPC URL from connection
    const rpcUrl = connection.rpcEndpoint;
    
    // Make request to Helius API for priority fee estimate using account keys
    const response = await fetch(rpcUrl, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        jsonrpc: "2.0",
        id: 1,
        method: "getPriorityFeeEstimate",
        params: [{
          "accountKeys": accountKeys,
          "options": {
            "includeAllPriorityFeeLevels": true,
            "lookbackSlots": options?.lookbackSlots || 150,
            "includeVote": options?.includeVote !== undefined ? options.includeVote : true
          }
        }]
      }),
    });
    
    const data = await response.json();

    console.log(data)
    
    if (data.error) {
      throw new Error(`Helius API error: ${data.error.message}`);
    }
    
    // Extract fee levels from the response
    const result = data.result;
    
    // If using Helius API, the response will have priorityFeeLevels
    if (result) {
      const priorityFeeEstimate = result.priorityFeeEstimate || result.priorityFee;
      const priorityFeeLevels = result.priorityFeeLevels;
      
      if (priorityFeeLevels) {
        const { min, low, medium, high, veryHigh, unsafeMax } = priorityFeeLevels;
        // Use the recommended fee or calculate a suggested value based on medium
        const suggested = priorityFeeEstimate || Math.ceil(medium * 1.2);
        
        return {
          low: low || 0,
          medium: medium || 0,
          high: high || 0,
          veryHigh: veryHigh,
          min: min,
          unsafeMax: unsafeMax,
          suggested,
          timeEstimates: {
            low: "1-2 blocks (~0.8s)",
            medium: "1 block (~0.4s)",
            high: "Usually immediate"
          },
          highLoadBehavior: {
            low: "May be delayed or dropped",
            medium: "More consistent inclusion",
            high: "Very likely first-in"
          },
          // Include the raw Helius API response
          priorityFeeEstimate,
          priorityFeeLevels
        };
      }
    }
    
    // Fallback to default values if the API doesn't return expected format
    return getDefaultPriorityFees();
  } catch (error) {
    console.error("Error fetching priority fee estimate by account keys:", error);
    return getDefaultPriorityFees();
  }
}

/**
 * Estimates priority fees using Helius API via serialized transaction method
 * @param connection Solana connection object
 * @param serializedTransaction Base58 encoded serialized transaction (optional)
 * @param options Optional configuration for fee estimation
 * @returns Priority fee estimate with different fee levels
 */
export async function estimatePriorityFeeByTransaction(
  connection: Connection,
  serializedTransaction?: string,
  options?: {
    lookbackSlots?: number;
    includeVote?: boolean;
  }
): Promise<PriorityFeeEstimate> {
  try {
    // Get RPC URL from connection
    const rpcUrl = connection.rpcEndpoint;
    
    // If no serialized transaction is provided, create a dummy transaction
    let encodedTransaction = serializedTransaction;
    if (!encodedTransaction) {
      try {
        // Since the transaction-based method is having issues with encoding,
        // let's fall back to the account keys method which is more reliable
        console.log("Using account keys method as fallback for transaction method");
        return await estimatePriorityFeeByAccountKeys(connection);
      } catch (txError) {
        console.error("Error creating dummy transaction:", txError);
        // If we can't create a transaction, fall back to account keys method
        return estimatePriorityFeeByAccountKeys(connection);
      }
    }
    
    // If we have a serialized transaction (provided externally), use it
    if (encodedTransaction) {
      // Make request to Helius API for priority fee estimate using serialized transaction
      const response = await fetch(rpcUrl, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          jsonrpc: "2.0",
          id: 1,
          method: "getPriorityFeeEstimate",
          params: [{
            "transaction": encodedTransaction,
            "options": {
              "includeAllPriorityFeeLevels": true,
              "lookbackSlots": options?.lookbackSlots || 150,
              "includeVote": options?.includeVote !== undefined ? options.includeVote : true
            }
          }]
        }),
      });
      
      const data = await response.json();
      
      if (data.error) {
        throw new Error(`Helius API error: ${data.error.message}`);
      }
      
      // Extract fee levels from the response
      const result = data.result;
      
      // If using Helius API, the response will have priorityFeeLevels
      if (result) {
        const priorityFeeEstimate = result.priorityFeeEstimate || result.priorityFee;
        const priorityFeeLevels = result.priorityFeeLevels;
        
        if (priorityFeeLevels) {
          const { min, low, medium, high, veryHigh, unsafeMax } = priorityFeeLevels;
          // Use the recommended fee or calculate a suggested value based on medium
          const suggested = priorityFeeEstimate || Math.ceil(medium * 1.2);
          
          return {
            low: low || 0,
            medium: medium || 0,
            high: high || 0,
            veryHigh: veryHigh,
            min: min,
            unsafeMax: unsafeMax,
            suggested,
            timeEstimates: {
              low: "3-5 seconds",
              medium: "2-3 seconds",
              high: "1-2 seconds"
            },
            highLoadBehavior: {
              low: "May be delayed or dropped",
              medium: "More consistent inclusion",
              high: "Very likely first-in"
            },
            // Include the raw Helius API response
            priorityFeeEstimate,
            priorityFeeLevels
          };
        }
      }
    }
    
    // Fallback to default values if the API doesn't return expected format
    return getDefaultPriorityFees();
  } catch (error) {
    console.error("Error fetching priority fee estimate by transaction:", error);
    return getDefaultPriorityFees();
  }
}

/**
 * Main function to estimate priority fees using the preferred method
 * @param connection Solana connection object
 * @param options Optional configuration for fee estimation
 * @returns Priority fee estimate with different fee levels
 */
export async function estimatePriorityFee(
  connection: Connection,
  options?: {
    method?: 'accountKeys' | 'transaction';
    accountKeys?: string[];
    serializedTransaction?: string;
    lookbackSlots?: number;
    includeVote?: boolean;
  }
): Promise<PriorityFeeEstimate> {
  try {
    const method = options?.method || 'accountKeys';
    
    if (method === 'transaction') {
      // If a serialized transaction is provided, use the transaction method
      if (options?.serializedTransaction) {
        return await estimatePriorityFeeByTransaction(connection, options.serializedTransaction, {
          lookbackSlots: options?.lookbackSlots,
          includeVote: options?.includeVote
        });
      } else {
        // Otherwise, fall back to account keys method for reliability
        console.log("No serialized transaction provided, using account keys method");
        return await estimatePriorityFeeByAccountKeys(connection, options?.accountKeys, {
          lookbackSlots: options?.lookbackSlots,
          includeVote: options?.includeVote
        });
      }
    } else {
      return await estimatePriorityFeeByAccountKeys(connection, options?.accountKeys, {
        lookbackSlots: options?.lookbackSlots,
        includeVote: options?.includeVote
      });
    }
  } catch (error) {
    console.error("Error estimating priority fee:", error);
    return getDefaultPriorityFees();
  }
}

/**
 * Returns default priority fee values when API calls fail
 */
function getDefaultPriorityFees(): PriorityFeeEstimate {
  return {
    low: 1000,
    medium: 10000,
    high: 100000,
    veryHigh: 150000,
    min: 0,
    unsafeMax: 200000,
    suggested: 10000,
    timeEstimates: {
      low: "1-2 blocks (~0.8s)",
      medium: "1 block (~0.4s)",
      high: "Usually immediate"
    },
    highLoadBehavior: {
      low: "May be delayed or dropped",
      medium: "More consistent inclusion",
      high: "Very likely first-in"
    },
    priorityFeeEstimate: 10000,
    priorityFeeLevels: {
      min: 0,
      low: 1000,
      medium: 10000,
      high: 100000,
      veryHigh: 150000,
      unsafeMax: 200000
    }
  };
}

// Helper function to create a ComputeBudgetInstruction with the priority fee
export function createPriorityFeeInstruction(microLamports: number) {
  return ComputeBudgetProgram.setComputeUnitPrice({
    microLamports
  });
}

```