MCP Context Integration

Build SEI AI agents using Model Context Protocol (MCP) for seamless blockchain interactions

SEI MCP Context Integration

The SEI MCP (Model Context Protocol) integration allows you to create AI agents that can interact with the SEI blockchain through standardized MCP tools. This approach provides a clean, protocol-based interface for blockchain operations that can be used across different AI platforms and clients.

What is MCP?

Model Context Protocol (MCP) is a standard for connecting AI assistants to external data sources and tools. It provides:

  • Standardized Interface: Consistent API for AI tools across platforms
  • Tool Discovery: Automatic discovery of available capabilities
  • Type Safety: Strong typing for all tool parameters and responses
  • Cross-Platform: Works with Claude Desktop, custom clients, and more

Available MCP Tools

The SEI MCP server provides the following tools:

🏦 Balance Management

  • get_balance - Get SEI or ERC-20 token balance
  • get_wallet_info - Get comprehensive wallet information

💸 Token Operations

  • transfer_tokens - Transfer SEI or ERC-20 tokens
  • get_token_address - Get token contract address from ticker

Quick Start

1. Install Dependencies

# Install required packages
pnpm add @axiomkit/sei @axiomkit/mcp @axiomkit/core
pnpm add viem zod

2. Environment Setup

Create a .env file with your SEI configuration:

# .env
SEI_RPC_URL=https://evm-rpc.sei-apis.com/
SEI_PRIVATE_KEY=0x... # Your private key (with 0x prefix)

3. Basic MCP Server

Create a simple SEI MCP server using createMcpServer:

import { createMcpServer } from "@axiomkit/mcp";
import { AxiomSeiWallet } from "@axiomkit/sei";
import { type Address } from "viem";
import { validateEnv } from "@axiomkit/core";
import * as z from "zod";
import { seiTestnet } from "viem/chains";

// Create an MCP server for SEI blockchain operations
const server = createMcpServer({
  name: "SEI MCP Agent",
  version: "1.0.0",
});

// Initialize Axiom Sei Wallet
let seiWallet: AxiomSeiWallet | null = null;

// Initialize the SEI wallet
function initializeSeiWallet() {
  const env = validateEnv(
    z.object({
      SEI_PRIVATE_KEY: z.string().min(1, "SEI_PRIVATE_KEY is required"),
      SEI_RPC_URL: z.string().min(1, "SEI_RPC_URL is required"),
    })
  );
  const rpcUrl = env.SEI_RPC_URL;
  const privateKey = env.SEI_PRIVATE_KEY;

  try {
    seiWallet = new AxiomSeiWallet({
      rpcUrl,
      privateKey: privateKey as `0x${string}`,
      chain: seiTestnet,
    });
    console.error(
      `SEI wallet initialized for address: ${seiWallet.walletAdress}`
    );
    return true;
  } catch (error) {
    console.error("Failed to initialize SEI wallet:", error);
    return false;
  }
}

// Tool: Get SEI or ERC-20 token balance
server.tool(
  "get_balance",
  {
    tokenAddress: z
      .string()
      .optional()
      .describe(
        "ERC-20 token contract address (optional, defaults to native SEI)"
      ),
  },
  async ({ tokenAddress }) => {
    if (!seiWallet) {
      return {
        content: [
          {
            type: "text",
            text: "Error: SEI wallet not initialized. Please set SEI_PRIVATE_KEY environment variable.",
          },
        ],
      };
    }

    try {
      const balance = await seiWallet.getERC20Balance(tokenAddress as Address);
      const tokenName = tokenAddress ? `Token (${tokenAddress})` : "SEI";

      return {
        content: [
          {
            type: "text",
            text: `Balance: ${balance} ${tokenName}`,
          },
        ],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Error getting balance: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
      };
    }
  }
);

// Tool: Transfer SEI or ERC-20 tokens
server.tool(
  "transfer_tokens",
  {
    amount: z.string().describe("Amount to transfer (e.g., '1.5')"),
    recipient: z.string().describe("Recipient wallet address"),
    ticker: z
      .string()
      .optional()
      .describe("Token ticker symbol (optional, defaults to native SEI)"),
  },
  async ({ amount, recipient, ticker }) => {
    if (!seiWallet) {
      return {
        content: [
          {
            type: "text",
            text: "Error: SEI wallet not initialized. Please set SEI_PRIVATE_KEY environment variable.",
          },
        ],
      };
    }

    try {
      const result = await seiWallet.ERC20Transfer(
        amount,
        recipient as Address,
        ticker
      );

      return {
        content: [
          {
            type: "text",
            text: result,
          },
        ],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Error transferring tokens: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
      };
    }
  }
);

// Tool: Get token address from ticker symbol
server.tool(
  "get_token_address",
  {
    ticker: z.string().describe("Token ticker symbol (e.g., 'SEI', 'USDC')"),
  },
  async ({ ticker }) => {
    if (!seiWallet) {
      return {
        content: [
          {
            type: "text",
            text: "Error: SEI wallet not initialized. Please set SEI_PRIVATE_KEY environment variable.",
          },
        ],
      };
    }

    try {
      const tokenAddress = await seiWallet.getTokenAddressFromTicker(ticker);

      if (!tokenAddress) {
        return {
          content: [
            {
              type: "text",
              text: `No token found for ticker: ${ticker}`,
            },
          ],
        };
      }

      return {
        content: [
          {
            type: "text",
            text: `Token address for ${ticker}: ${tokenAddress}`,
          },
        ],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Error getting token address: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
      };
    }
  }
);

// Tool: Get wallet information
server.tool("get_wallet_info", {}, async () => {
  if (!seiWallet) {
    return {
      content: [
        {
          type: "text",
          text: "Error: SEI wallet not initialized. Please set SEI_PRIVATE_KEY environment variable.",
        },
      ],
    };
  }

  try {
    const balance = await seiWallet.getERC20Balance();

    return {
      content: [
        {
          type: "text",
          text: `Wallet Address: ${seiWallet.walletAdress}\nSEI Balance: ${balance}`,
        },
      ],
    };
  } catch (error) {
    return {
      content: [
        {
          type: "text",
          text: `Error getting wallet info: ${
            error instanceof Error ? error.message : String(error)
          }`,
        },
      ],
    };
  }
});

async function main() {
  // Initialize SEI wallet
  if (!initializeSeiWallet()) {
    console.error(
      "Failed to initialize SEI wallet. Server will start but tools will not work."
    );
  }

  await server.connect();
}

main().catch((error) => {
  console.error("Fatal error in main():", error);
  process.exit(1);
});

Tool Reference

get_balance

Get the balance of native SEI or an ERC-20 token.

Parameters:

  • tokenAddress (optional): ERC-20 token contract address. If omitted, returns native SEI balance.

Example:

// Get native SEI balance
await get_balance({});

// Get ERC-20 token balance
await get_balance({ tokenAddress: "0x..." });

transfer_tokens

Transfer SEI or ERC-20 tokens to another address.

Parameters:

  • amount (required): Amount to transfer as a string (e.g., "1.5")
  • recipient (required): Recipient wallet address
  • ticker (optional): Token ticker symbol (e.g., "USDC"). If omitted, transfers native SEI.

Example:

// Transfer native SEI
await transfer_tokens({
  amount: "1.5",
  recipient: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6"
});

// Transfer ERC-20 token
await transfer_tokens({
  amount: "100",
  recipient: "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
  ticker: "USDC"
});

get_token_address

Get the contract address for a token by its ticker symbol.

Parameters:

  • ticker (required): Token ticker symbol (e.g., "SEI", "USDC", "WETH")

Example:

await get_token_address({ ticker: "USDC" });

get_wallet_info

Get wallet address and native SEI balance.

Parameters:

  • None

Example:

await get_wallet_info({});

Using with AxiomKit Agent

Create an agent that uses the MCP server:

// mcp-sei-agent.ts
import { createAgent, Logger, LogLevel } from "@axiomkit/core";
import { createMcpProvider } from "@axiomkit/mcp";
import { groq } from "@ai-sdk/groq";
import path from "path";

const agent = createAgent({
  model: groq("qwen/qwen3-32b"),
  logger: new Logger({
    level: LogLevel.INFO,
  }),
  providers: [
    createMcpProvider([
      {
        id: "sei-blockchain-server",
        name: "SEI Blockchain Agent",
        transport: {
          type: "stdio",
          command: "tsx",
          args: [path.join(__dirname, "mcp-sei.ts")],
        },
      },
    ]),
  ],
});

// Start the agent
agent.start().then(() => {
  console.log("🚀 SEI MCP Agent started successfully!");
});

Claude Desktop Integration

MCP

1. Configure Claude Desktop

Update your Claude Desktop config file (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "sei-blockchain-server": {
      "command": "npx",
      "args": [
        "-y",
        "tsx",
        "/path/to/your/mcp-sei.ts"
      ],
      "env": {
        "SEI_RPC_URL": "https://evm-rpc.sei-apis.com/",
        "SEI_PRIVATE_KEY": "YOUR_PRIVATE_KEY_HERE"
      }
    }
  }
}

2. Usage Examples

Once configured, you can interact with SEI through Claude:

Get Wallet Information:

Get my wallet information

Check Balance:

What's my SEI balance?
What's my USDC balance?

Transfer Tokens:

Transfer 1.5 SEI to 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6
Transfer 100 USDC to 0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6

Token Discovery:

What's the contract address for USDC token?
Find information about WETH token

Advanced Features

Custom Tool Definitions

You can extend the MCP server with custom tools using the same server.tool() pattern:

// Add a custom portfolio summary tool
server.tool(
  "get_portfolio_summary",
  {
    includePrices: z
      .boolean()
      .optional()
      .describe("Include current USD prices"),
  },
  async ({ includePrices = false }) => {
    if (!seiWallet) {
      return {
        content: [
          {
            type: "text",
            text: "Error: SEI wallet not initialized.",
          },
        ],
      };
    }

    try {
      // Get common tokens
      const tokens = ["SEI", "USDC", "USDT", "WETH"];
      const holdings = [];

      for (const token of tokens) {
        try {
          const tokenAddress = await seiWallet.getTokenAddressFromTicker(token);
          if (tokenAddress) {
            const balance = await seiWallet.getERC20Balance(tokenAddress);
            if (parseFloat(balance) > 0) {
              holdings.push({ token, balance });
            }
          }
        } catch (error) {
          // Token not found or error, skip
        }
      }

      let summary = "Portfolio Summary:\n";
      holdings.forEach((holding) => {
        summary += `- ${holding.token}: ${holding.balance}\n`;
      });

      return {
        content: [
          {
            type: "text",
            text: summary,
          },
        ],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Error getting portfolio: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
      };
    }
  }
);

Error Handling

Each tool handler includes comprehensive error handling:

server.tool(
  "tool_name",
  { /* schema */ },
  async ({ /* params */ }) => {
    // Check wallet initialization
    if (!seiWallet) {
      return {
        content: [
          {
            type: "text",
            text: "Error: SEI wallet not initialized.",
          },
        ],
      };
    }

    try {
      // Tool logic here
      const result = await someOperation();
      
      return {
        content: [
          {
            type: "text",
            text: result,
          },
        ],
      };
    } catch (error) {
      // Handle errors gracefully
      return {
        content: [
          {
            type: "text",
            text: `Error: ${
              error instanceof Error ? error.message : String(error)
            }`,
          },
        ],
      };
    }
  }
);

Wallet Initialization Pattern

The wallet initialization follows a pattern that allows the server to start even if the wallet fails to initialize:

let seiWallet: AxiomSeiWallet | null = null;

function initializeSeiWallet() {
  const env = validateEnv(
    z.object({
      SEI_PRIVATE_KEY: z.string().min(1, "SEI_PRIVATE_KEY is required"),
      SEI_RPC_URL: z.string().min(1, "SEI_RPC_URL is required"),
    })
  );

  try {
    seiWallet = new AxiomSeiWallet({
      rpcUrl: env.SEI_RPC_URL,
      privateKey: env.SEI_PRIVATE_KEY as `0x${string}`,
      chain: seiTestnet,
    });
    return true;
  } catch (error) {
    console.error("Failed to initialize SEI wallet:", error);
    return false;
  }
}

async function main() {
  if (!initializeSeiWallet()) {
    console.error(
      "Failed to initialize SEI wallet. Server will start but tools will not work."
    );
  }

  await server.connect();
}

Best Practices

1. Security

  • Never expose private keys in logs or error messages
  • Validate all input parameters using Zod schemas
  • Use environment variables for sensitive data
  • Implement rate limiting for API calls

2. Error Handling

  • Always check wallet initialization before tool execution
  • Provide meaningful error messages
  • Handle network failures gracefully
  • Validate blockchain responses

3. Code Organization

  • Keep wallet initialization separate from tool definitions
  • Use descriptive tool names and parameter descriptions
  • Group related tools together
  • Add comments for complex logic

4. Testing

  • Test with testnet first (seiTestnet)
  • Use mock data for development
  • Test error scenarios
  • Verify tool responses match expected format

Troubleshooting

Common Issues

1. "SEI wallet not initialized"

  • Check your SEI_PRIVATE_KEY environment variable
  • Ensure the private key starts with 0x
  • Verify the key is 64 characters long (excluding 0x)
  • Check that SEI_RPC_URL is set correctly

2. "Invalid recipient address"

  • Ensure the address starts with 0x
  • Check the address is 42 characters long
  • Verify it's a valid Ethereum address format

3. "Insufficient balance"

  • Check your wallet balance before transfers
  • Ensure you have enough SEI for gas fees
  • Verify you're using the correct network (testnet vs mainnet)

4. "Token not found"

  • The token might not be available on SEI
  • Check the ticker symbol is correct
  • Some tokens might not be indexed by DexScreener

5. "MCP connection failed"

  • Verify the server is running
  • Check the transport configuration
  • Ensure all dependencies are installed
  • Check that server.connect() is called in main()

The MCP integration provides a powerful, standardized way to build SEI AI agents that can work across different platforms and clients. Start with the basic setup and gradually add more sophisticated tools and features as needed.