import algosdk from 'algosdk';
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import { getAlgodClient, extractNetwork, type NetworkId } from '../algorand-client.js';
import { withCommonParams } from './commonParams.js';
// Tool schemas
export const accountToolSchemas = {
createAccount: {
type: 'object',
properties: {},
required: []
},
rekeyAccount: {
type: 'object',
properties: {
sourceAddress: { type: 'string', description: 'Sender address in standard Algorand format (58 characters)' },
targetAddress: { type: 'string', description: 'Address to rekey the sender account to' }
},
required: ['sourceAddress', 'targetAddress']
},
mnemonicToMdk: {
type: 'object',
properties: {
mnemonic: { type: 'string', description: 'The mnemonic phrase to convert to a master derivation key' }
},
required: ['mnemonic']
},
mdkToMnemonic: {
type: 'object',
properties: {
mdk: { type: 'string', description: 'The master derivation key in hexadecimal format' }
},
required: ['mdk']
},
secretKeyToMnemonic: {
type: 'object',
properties: {
secretKey: { type: 'string', description: 'The secret key in hexadecimal format' }
},
required: ['secretKey']
},
mnemonicToSecretKey: {
type: 'object',
properties: {
mnemonic: { type: 'string', description: 'The mnemonic phrase to convert to a secret key' }
},
required: ['mnemonic']
},
seedFromMnemonic: {
type: 'object',
properties: {
mnemonic: { type: 'string', description: 'The mnemonic phrase to generate a seed from' }
},
required: ['mnemonic']
},
mnemonicFromSeed: {
type: 'object',
properties: {
seed: { type: 'string', description: 'The seed in hexadecimal format to generate a mnemonic from' }
},
required: ['seed']
}
};
export class AccountManager {
static readonly accountTools = [
{
name: 'create_account',
description: 'Create a new Algorand account',
inputSchema: withCommonParams(accountToolSchemas.createAccount),
},
{
name: 'rekey_account',
description: 'Rekey an Algorand account to a new address',
inputSchema: withCommonParams(accountToolSchemas.rekeyAccount),
},
{
name: 'mnemonic_to_mdk',
description: 'Convert a mnemonic to a master derivation key',
inputSchema: withCommonParams(accountToolSchemas.mnemonicToMdk),
},
{
name: 'mdk_to_mnemonic',
description: 'Convert a master derivation key to a mnemonic',
inputSchema: withCommonParams(accountToolSchemas.mdkToMnemonic),
},
{
name: 'secret_key_to_mnemonic',
description: 'Convert a secret key to a mnemonic',
inputSchema: withCommonParams(accountToolSchemas.secretKeyToMnemonic),
},
{
name: 'mnemonic_to_secret_key',
description: 'Convert a mnemonic to a secret key',
inputSchema: withCommonParams(accountToolSchemas.mnemonicToSecretKey),
},
{
name: 'seed_from_mnemonic',
description: 'Generate a seed from a mnemonic',
inputSchema: withCommonParams(accountToolSchemas.seedFromMnemonic),
},
{
name: 'mnemonic_from_seed',
description: 'Generate a mnemonic from a seed',
inputSchema: withCommonParams(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 network = extractNetwork(args);
const rekeyTxn = await AccountManager.createRekeyTransaction(
args.sourceAddress,
args.targetAddress,
network
);
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: algosdk.bytesToHex(mdk) }, 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(algosdk.hexToBytes(args.mdk));
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(algosdk.hexToBytes(args.secretKey));
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: algosdk.bytesToHex(sk.sk),
address: sk.addr.toString()
}, 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: algosdk.bytesToHex(seed) }, 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(algosdk.hexToBytes(args.seed));
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.toString(),
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, network?: NetworkId): Promise<algosdk.Transaction> {
try {
const algodClient = getAlgodClient(network);
const suggestedParams = await algodClient.getTransactionParams().do();
return algosdk.makePaymentTxnWithSuggestedParamsFromObject({
sender: fromAddress,
receiver: 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: algosdk.Address } {
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'}`
);
}
}
}