import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { Connection, PublicKey, LAMPORTS_PER_TRZ, clusterApiUrl } from "@trezoa/web3.js";
// Create an MCP server
const server = new McpServer({
name: "Trezoa RPC Tools",
version: "1.0.0",
});
// Initialize Trezoa connection
const connection = new Connection(clusterApiUrl("mainnet-beta"), "confirmed");
// Trezoa 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",
"Used to look up balance by public key (32 byte base58 encoded address)",
{ publicKey: z.string() },
async ({ publicKey }) => {
try {
const pubkey = new PublicKey(publicKey);
const balance = await connection.getBalance(pubkey);
return {
content: [{ type: "text", text: `${balance / LAMPORTS_PER_TRZ} TRZ (${balance} lamports)` }]
};
} 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_TRZ} TRZ (${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}` }]
};
}
}
);
// Add a dynamic account info resource
// Setup specific resources to read from trezoa.com/docs pages
server.resource(
"trezoaDocsInstallation",
new ResourceTemplate("trezoa://docs/intro/installation", { list: undefined }),
async (uri) => {
try {
const response = await fetch(`https://raw.githubusercontent.com/trzledgerfoundation/trezoa-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(
"trezoaDocsClusters",
new ResourceTemplate("trezoa://docs/references/clusters", { list: undefined }),
async (uri) => {
try {
const response = await fetch(`https://raw.githubusercontent.com/trzledgerfoundation/trezoa-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}`
}]
};
}
}
);
server.prompt(
'calculate-storage-deposit',
'Calculate storage deposit for a specified number of bytes',
{ bytes: z.string() },
({ bytes }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Calculate the TRZ amount needed to store ${bytes} bytes of data on Trezoa using getMinimumBalanceForRentExemption.`
}
}]
})
);
server.prompt(
'minimum-amount-of-sol-for-storage',
'Calculate the minimum amount of TRZ needed for storing 0 bytes on-chain',
() => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Calculate the amount of TRZ needed to store 0 bytes of data on Trezoa using getMinimumBalanceForRentExemption & present it to the user as the minimum cost for storing any data on Trezoa.`
}
}]
})
);
server.prompt(
'why-did-my-transaction-fail',
'Look up the given transaction and inspect its logs to figure out why it failed',
{ signature: z.string() },
({ signature }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Look up the transaction with signature ${signature} and inspect its logs to figure out why it failed.`
}
}]
})
);
server.prompt(
'how-much-did-this-transaction-cost',
'Fetch the transaction by signature, and break down cost & priority fees',
{ signature: z.string() },
({ signature }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Calculate the network fee for the transaction with signature ${signature} by fetching it and inspecting the 'fee' field in 'meta'. Base fee is 0.000005 trz per signature (also provided as array at the end). So priority fee is fee - (numSignatures * 0.000005). Please provide the base fee and the priority fee.`
}
}]
})
);
server.prompt('what-happened-in-transaction',
'Look up the given transaction and inspect its logs & instructions to figure out what happened',
{ signature: z.string() },
({ signature }) => ({
messages: [{
role: 'user',
content: {
type: 'text',
text: `Look up the transaction with signature ${signature} and inspect its logs & instructions to figure out what happened.`
}
}]
})
);
// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
server.connect(transport);