Algorand MCP
by GoPlausible
import algosdk, { Transaction } from 'algosdk';
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import { algodClient } from '../algorand-client.js';
// Tool schemas
export const accountToolSchemas = {
createAccount: {
type: 'object',
properties: {},
required: []
},
rekeyAccount: {
type: 'object',
properties: {
sourceAddress: { type: 'string' },
targetAddress: { type: 'string' }
},
required: ['sourceAddress', 'targetAddress']
},
mnemonicToMdk: {
type: 'object',
properties: {
mnemonic: { type: 'string' }
},
required: ['mnemonic']
},
mdkToMnemonic: {
type: 'object',
properties: {
mdk: { type: 'string' }
},
required: ['mdk']
},
secretKeyToMnemonic: {
type: 'object',
properties: {
secretKey: { type: 'string' }
},
required: ['secretKey']
},
mnemonicToSecretKey: {
type: 'object',
properties: {
mnemonic: { type: 'string' }
},
required: ['mnemonic']
},
seedFromMnemonic: {
type: 'object',
properties: {
mnemonic: { type: 'string' }
},
required: ['mnemonic']
},
mnemonicFromSeed: {
type: 'object',
properties: {
seed: { type: 'string' }
},
required: ['seed']
}
};
export class AccountManager {
static readonly accountTools = [
{
name: 'create_account',
description: 'Create a new Algorand account',
inputSchema: accountToolSchemas.createAccount,
},
{
name: 'rekey_account',
description: 'Rekey an Algorand account to a new address',
inputSchema: accountToolSchemas.rekeyAccount,
},
{
name: 'mnemonic_to_mdk',
description: 'Convert a mnemonic to a master derivation key',
inputSchema: accountToolSchemas.mnemonicToMdk,
},
{
name: 'mdk_to_mnemonic',
description: 'Convert a master derivation key to a mnemonic',
inputSchema: accountToolSchemas.mdkToMnemonic,
},
{
name: 'secret_key_to_mnemonic',
description: 'Convert a secret key to a mnemonic',
inputSchema: accountToolSchemas.secretKeyToMnemonic,
},
{
name: 'mnemonic_to_secret_key',
description: 'Convert a mnemonic to a secret key',
inputSchema: accountToolSchemas.mnemonicToSecretKey,
},
{
name: 'seed_from_mnemonic',
description: 'Generate a seed from a mnemonic',
inputSchema: accountToolSchemas.seedFromMnemonic,
},
{
name: 'mnemonic_from_seed',
description: 'Generate a mnemonic from a seed',
inputSchema: accountToolSchemas.mnemonicFromSeed,
}
];
// Tool handlers
static async handleTool(name: string, args: Record<string, unknown>) {
try {
switch (name) {
case 'create_account':
return {
content: [{
type: 'text',
text: JSON.stringify(AccountManager.createAccount(), null, 2),
}],
};
case 'rekey_account':
if (!args.sourceAddress || !args.targetAddress ||
typeof args.sourceAddress !== 'string' ||
typeof args.targetAddress !== 'string') {
throw new McpError(
ErrorCode.InvalidParams,
'Invalid rekey account parameters'
);
}
const rekeyTxn = await AccountManager.createRekeyTransaction(
args.sourceAddress,
args.targetAddress
);
return {
content: [{
type: 'text',
text: JSON.stringify(rekeyTxn, null, 2),
}],
};
case 'mnemonic_to_mdk':
if (!args.mnemonic || typeof args.mnemonic !== 'string') {
throw new McpError(ErrorCode.InvalidParams, 'Mnemonic is required');
}
const mdk = AccountManager.mnemonicToMasterDerivationKey(args.mnemonic);
return {
content: [{
type: 'text',
text: JSON.stringify({ mdk: Buffer.from(mdk).toString('hex') }, null, 2),
}],
};
case 'mdk_to_mnemonic':
if (!args.mdk || typeof args.mdk !== 'string') {
throw new McpError(ErrorCode.InvalidParams, 'Master derivation key is required');
}
const mdkMnemonic = AccountManager.masterDerivationKeyToMnemonic(Buffer.from(args.mdk, 'hex'));
return {
content: [{
type: 'text',
text: JSON.stringify({ mnemonic: mdkMnemonic }, null, 2),
}],
};
case 'secret_key_to_mnemonic':
if (!args.secretKey || typeof args.secretKey !== 'string') {
throw new McpError(ErrorCode.InvalidParams, 'Secret key is required');
}
const skMnemonic = AccountManager.secretKeyToMnemonic(Buffer.from(args.secretKey, 'hex'));
return {
content: [{
type: 'text',
text: JSON.stringify({ mnemonic: skMnemonic }, null, 2),
}],
};
case 'mnemonic_to_secret_key':
if (!args.mnemonic || typeof args.mnemonic !== 'string') {
throw new McpError(ErrorCode.InvalidParams, 'Mnemonic is required');
}
const sk = AccountManager.mnemonicToSecretKey(args.mnemonic);
return {
content: [{
type: 'text',
text: JSON.stringify({
secretKey: Buffer.from(sk.sk).toString('hex'),
address: sk.addr
}, null, 2),
}],
};
case 'seed_from_mnemonic':
if (!args.mnemonic || typeof args.mnemonic !== 'string') {
throw new McpError(ErrorCode.InvalidParams, 'Mnemonic is required');
}
const seed = AccountManager.seedFromMnemonic(args.mnemonic);
return {
content: [{
type: 'text',
text: JSON.stringify({ seed: Buffer.from(seed).toString('hex') }, null, 2),
}],
};
case 'mnemonic_from_seed':
if (!args.seed || typeof args.seed !== 'string') {
throw new McpError(ErrorCode.InvalidParams, 'Seed is required');
}
const seedMnemonic = AccountManager.mnemonicFromSeed(Buffer.from(args.seed, 'hex'));
return {
content: [{
type: 'text',
text: JSON.stringify({ mnemonic: seedMnemonic }, null, 2),
}],
};
default:
console.error(`[MCP Error] Unknown tool requested: ${name}`);
throw new McpError(
ErrorCode.MethodNotFound,
`Unknown tool: ${name}`
);
}
} catch (error) {
if (error instanceof McpError) {
console.error(`[MCP Error] ${error.code}: ${error.message}`);
throw error;
}
console.error('[MCP Error] Unexpected error:', error);
throw new McpError(
ErrorCode.InternalError,
`Operation failed: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Creates a new account
* @returns Object containing the address and mnemonic
*/
static createAccount(): { address: string; mnemonic: string } {
try {
const account = algosdk.generateAccount();
const mnemonic = algosdk.secretKeyToMnemonic(account.sk);
return {
address: account.addr,
mnemonic,
};
} catch (error) {
console.error('[MCP Error] Failed to create account:', error);
throw new McpError(
ErrorCode.InternalError,
`Failed to create account: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Rekeys an account to a new address
* @param fromAddress The address to rekey from
* @param toAddress The address to rekey to
* @returns The rekey transaction object
*/
static async createRekeyTransaction(fromAddress: string, toAddress: string): Promise<algosdk.Transaction> {
try {
const params = await algodClient.getTransactionParams().do();
const suggestedParams = { ...params, flatFee: true, fee: params.minFee };
return algosdk.makePaymentTxnWithSuggestedParamsFromObject({
from: fromAddress,
to: fromAddress,
amount: 0,
rekeyTo: toAddress,
suggestedParams,
});
} catch (error) {
console.error('[MCP Error] Failed to create rekey transaction:', error);
throw new McpError(
ErrorCode.InternalError,
`Failed to create rekey transaction: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Converts a mnemonic to a master derivation key
* @param mnemonic The mnemonic to convert
* @returns The master derivation key
*/
static mnemonicToMasterDerivationKey(mnemonic: string): Uint8Array {
try {
return algosdk.mnemonicToMasterDerivationKey(mnemonic);
} catch (error) {
console.error('[MCP Error] Failed to convert mnemonic to MDK:', error);
throw new McpError(
ErrorCode.InvalidParams,
`Invalid mnemonic provided: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Converts a master derivation key to a mnemonic
* @param mdk The master derivation key to convert
* @returns The mnemonic
*/
static masterDerivationKeyToMnemonic(mdk: Uint8Array): string {
try {
return algosdk.masterDerivationKeyToMnemonic(mdk);
} catch (error) {
console.error('[MCP Error] Failed to convert MDK to mnemonic:', error);
throw new McpError(
ErrorCode.InvalidParams,
`Invalid master derivation key provided: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Converts a secret key to a mnemonic
* @param secretKey The secret key to convert
* @returns The mnemonic
*/
static secretKeyToMnemonic(secretKey: Uint8Array): string {
try {
return algosdk.secretKeyToMnemonic(secretKey);
} catch (error) {
console.error('[MCP Error] Failed to convert secret key to mnemonic:', error);
throw new McpError(
ErrorCode.InvalidParams,
`Invalid secret key provided: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Converts a mnemonic to a secret key
* @param mnemonic The mnemonic to convert
* @returns The secret key
*/
static mnemonicToSecretKey(mnemonic: string): { sk: Uint8Array; addr: string } {
try {
return algosdk.mnemonicToSecretKey(mnemonic);
} catch (error) {
console.error('[MCP Error] Failed to convert mnemonic to secret key:', error);
throw new McpError(
ErrorCode.InvalidParams,
`Invalid mnemonic provided: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Generates a seed from a mnemonic
* @param mnemonic The mnemonic to generate the seed from
* @returns The seed
*/
static seedFromMnemonic(mnemonic: string): Uint8Array {
try {
return algosdk.seedFromMnemonic(mnemonic);
} catch (error) {
console.error('[MCP Error] Failed to generate seed from mnemonic:', error);
throw new McpError(
ErrorCode.InvalidParams,
`Invalid mnemonic provided: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
/**
* Generates a mnemonic from a seed
* @param seed The seed to generate the mnemonic from
* @returns The mnemonic
*/
static mnemonicFromSeed(seed: Uint8Array): string {
try {
return algosdk.mnemonicFromSeed(seed);
} catch (error) {
console.error('[MCP Error] Failed to generate mnemonic from seed:', error);
throw new McpError(
ErrorCode.InvalidParams,
`Invalid seed provided: ${error instanceof Error ? error.message : 'Unknown error'}`
);
}
}
}