generate_aptos_component
Generate a new Aptos project component, such as a table or module, by specifying its type, name, and project directory path. Ideal for structured blockchain application development.
Instructions
Generate a new component for an Aptos project.
Args:
component_type: Type of component (table, module, etc.)
component_name: Name of the component
project_dir: Project directory path
options: Additional options as a string
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| component_name | Yes | ||
| component_type | Yes | ||
| options | No | ||
| project_dir | Yes |
Input Schema (JSON Schema)
{
"properties": {
"component_name": {
"title": "Component Name",
"type": "string"
},
"component_type": {
"title": "Component Type",
"type": "string"
},
"options": {
"default": "",
"title": "Options",
"type": "string"
},
"project_dir": {
"title": "Project Dir",
"type": "string"
}
},
"required": [
"component_type",
"component_name",
"project_dir"
],
"title": "generate_aptos_componentArguments",
"type": "object"
}
Implementation Reference
- aptos_mcp_server.py:204-274 (handler)The main async handler function decorated with @mcp.tool() that implements the generate_aptos_component tool. It dispatches to different helper generators based on component_type and writes code files to the project directory.@mcp.tool() async def generate_aptos_component(component_type: str, component_name: str, project_dir: str, options: str = "") -> str: """ Generate a new component for an Aptos project. Args: component_type: Type of component (table, module, etc.) component_name: Name of the component project_dir: Project directory path options: Additional options as a string """ supported_types = ["move-module", "react-component", "client-function", "table"] if component_type not in supported_types: return f"Unsupported component type. Choose from: {', '.join(supported_types)}" project_dir = os.path.expanduser(project_dir) # Expand ~ in paths if not os.path.exists(project_dir): return f"Project directory not found: {project_dir}" try: # Different command logic based on component type if component_type == "move-module": module_code = generate_move_module(component_name) module_path = os.path.join(project_dir, "move", "sources", f"{component_name}.move") os.makedirs(os.path.dirname(module_path), exist_ok=True) with open(module_path, "w") as f: f.write(module_code) return f"Generated Move module at {module_path}" elif component_type == "react-component": component_code = generate_react_component(component_name, options) component_path = os.path.join(project_dir, "client", "src", "components", f"{component_name}.tsx") os.makedirs(os.path.dirname(component_path), exist_ok=True) with open(component_path, "w") as f: f.write(component_code) return f"Generated React component at {component_path}" elif component_type == "client-function": function_code = generate_client_function(component_name, options) function_path = os.path.join(project_dir, "client", "src", "utils", f"{component_name}.ts") os.makedirs(os.path.dirname(function_path), exist_ok=True) with open(function_path, "w") as f: f.write(function_code) return f"Generated client function at {function_path}" elif component_type == "table": table_code = generate_move_table(component_name, options) table_path = os.path.join(project_dir, "move", "sources", f"{component_name}_table.move") os.makedirs(os.path.dirname(table_path), exist_ok=True) with open(table_path, "w") as f: f.write(table_code) return f"Generated Move table at {table_path}" return f"Unknown component type: {component_type}" except Exception as e: return f"Error generating component: {str(e)}"
- aptos_mcp_server.py:204-204 (registration)The @mcp.tool() decorator registers the generate_aptos_component function as an MCP tool.@mcp.tool()
- aptos_mcp_server.py:205-215 (schema)Function signature with type annotations and docstring defining the input schema/parameters for the tool.async def generate_aptos_component(component_type: str, component_name: str, project_dir: str, options: str = "") -> str: """ Generate a new component for an Aptos project. Args: component_type: Type of component (table, module, etc.) component_name: Name of the component project_dir: Project directory path options: Additional options as a string """
- aptos_mcp_server.py:274-742 (helper)One of the helper functions called by the handler to generate React component code.def generate_gas_station_server() -> str: """Generate the server code for a gas station""" return """import express, { Request, Response } from 'express'; import { Account, AccountAuthenticator, Aptos, AptosConfig, Deserializer, Network, NetworkToNetworkName, SimpleTransaction, } from '@aptos-labs/ts-sdk'; import dotenv from 'dotenv'; dotenv.config(); const app = express(); app.use(express.json()); const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000; const APTOS_NETWORK = NetworkToNetworkName[process.env.APTOS_NETWORK || ''] || Network.DEVNET; const config = new AptosConfig({network: APTOS_NETWORK}); const aptos = new Aptos(config); const feePayerAccount = Account.generate(); console.log(`Gas Station Account Address: ${feePayerAccount.accountAddress.toString()}`); // Fund the feePayerAccount account const fundFeePayerAccount = async () => { console.log('=== Funding Gas Station Account ==='); try { await aptos.fundAccount({ accountAddress: feePayerAccount.accountAddress, amount: 100_000_000, }); console.log('Gas Station Account funded successfully.'); } catch (error) { console.error('Error funding Gas Station Account:', error); console.log('Note: Automatic funding only works on DEVNET. On other networks, manually fund this address.'); } }; app.post('/signAndSubmit', async (req: Request, res: Response) => { try { const {transactionBytes, senderAuthenticator} = req.body; if (!transactionBytes) { return res.status(400).json({error: 'transactionBytes is required'}); } if (!senderAuthenticator) { return res.status(400).json({error: 'senderAuthenticator is required'}); } console.log('=== Received Transaction Request ==='); // Deserialize the raw transaction const deserializer = new Deserializer(Uint8Array.from(transactionBytes)); const transaction = SimpleTransaction.deserialize(deserializer); console.log('=== Signing Transaction as Fee Payer ==='); // Gas station signs the transaction as fee payer const feePayerAuthenticator = aptos.transaction.signAsFeePayer({ signer: feePayerAccount, transaction, }); const deserializedSenderAuth = AccountAuthenticator.deserialize(new Deserializer(Uint8Array.from(senderAuthenticator))); console.log('=== Submitting Sponsored Transaction ==='); const signedTxnInput = { transaction, senderAuthenticator: deserializedSenderAuth, feePayerAuthenticator, }; let response = await aptos.transaction.submit.simple(signedTxnInput); console.log(`Transaction submitted. Hash: ${response.hash}`); await aptos.waitForTransaction({transactionHash: response.hash}); console.log('Transaction executed successfully!'); return res.status(200).json({ transactionHash: response.hash, message: 'Transaction sponsored and executed successfully', feePayerAddress: feePayerAccount.accountAddress.toString() }); } catch (error) { console.error('Error processing transaction:', error); return res.status(500).json({error: String(error)}); } }); // Health check endpoint app.get('/health', (req: Request, res: Response) => { return res.status(200).json({ status: 'healthy', network: APTOS_NETWORK, feePayerAddress: feePayerAccount.accountAddress.toString() }); }); app.listen(PORT, async () => { console.log(`Gas Station Server running on http://localhost:${PORT}`); if (process.env.AUTO_FUND_ACCOUNT === 'true') { await fundFeePayerAccount(); } }); """ def generate_gas_station_client() -> str: """Generate the client code for a gas station""" return """import axios from 'axios'; import { Account, Aptos, AptosConfig, Network, } from '@aptos-labs/ts-sdk'; const GAS_STATION_URL = "http://localhost:3000"; const main = async () => { const config = new AptosConfig({ network: Network.DEVNET }); const aptos = new Aptos(config); console.log("=== Aptos Gas Station Client Demo ==="); // Create sender and recipient accounts const alice = Account.generate(); const bob = Account.generate(); console.log("Alice's address:", alice.accountAddress.toString()); console.log("Bob's address:", bob.accountAddress.toString()); // Fund Alice's account (the transaction sender, but not fee payer) console.log("\\n=== Funding Alice's account ==="); await aptos.fundAccount({ accountAddress: alice.accountAddress, amount: 100_000_000 }); console.log("Alice's account funded"); // Build a transaction (Alice sending APT to Bob) console.log("\\n=== Building transaction ==="); const transaction = await aptos.transaction.build.simple({ sender: alice.accountAddress, withFeePayer: true, // Important: Mark that this transaction will have a fee payer data: { function: "0x1::aptos_account::transfer", functionArguments: [bob.accountAddress, 100], // Sending 100 octas to Bob }, }); console.log("Transaction built successfully"); // Alice signs the transaction (but doesn't pay for gas) console.log("\\n=== Alice signing transaction ==="); const senderAuthenticator = aptos.transaction.sign({ signer: alice, transaction }); console.log("Transaction signed by Alice"); // Send the transaction to the gas station for fee sponsorship console.log("\\n=== Sending to Gas Station for sponsorship ==="); try { const response = await axios.post( `${GAS_STATION_URL}/signAndSubmit`, { transactionBytes: Array.from(transaction.bcsToBytes()), senderAuthenticator: Array.from(senderAuthenticator.bcsToBytes()), }, { headers: { "Content-Type": "application/json", }, } ); const { transactionHash, feePayerAddress } = response.data; console.log("Transaction sponsored by:", feePayerAddress); console.log("Transaction submitted. Hash:", transactionHash); // Wait for transaction to be executed console.log("\\n=== Waiting for transaction execution ==="); const executedTx = await aptos.waitForTransaction({ transactionHash }); console.log("Transaction executed successfully!"); // Verify Bob received the funds console.log("\\n=== Checking Bob's balance ==="); const bobBalance = await aptos.getAccountAPTAmount({ accountAddress: bob.accountAddress }); console.log("Bob's balance:", bobBalance, "octas"); } catch (error) { console.error("Error:", error.response?.data || error.message); } }; main(); """ def generate_transaction_processor(project_name: str) -> str: """Generate code for a transaction processor""" return f"""import {{ InputModels, Models, OutputModels, ProcessingResult, Transaction, TransactionModel, TransactionProcessor, UserTransactionInput, parseTransaction, }} from "@aptos-labs/indexer-sdk"; /** * {project_name.capitalize()} Transaction Processor * * This processor handles transactions and extracts relevant information. */ export class {project_name.capitalize()}TransactionProcessor extends TransactionProcessor {{ constructor() {{ super(); }} /** * Process a batch of transactions */ async process( transactionInputs: UserTransactionInput[], ): Promise<ProcessingResult> {{ const processingResult = new ProcessingResult(); console.log(`Processing ${{transactionInputs.length}} transactions`); for (const transactionInput of transactionInputs) {{ const transaction = parseTransaction(transactionInput); // Process each transaction try {{ await this.processTransaction(transaction, processingResult); }} catch (e) {{ console.error( `Error processing transaction ${{transaction.version}}: ${{e}}`, ); }} }} return processingResult; }} /** * Process a single transaction */ async processTransaction( transaction: Transaction, processingResult: ProcessingResult, ): Promise<void> {{ // Check if transaction is successful if (!transaction.success) {{ return; }} // Extract basic transaction data const txModel = new TransactionModel(); txModel.version = transaction.version; txModel.hash = transaction.hash; txModel.sender = transaction.sender; txModel.success = transaction.success; txModel.timestamp = new Date(Number(transaction.timestamp) / 1000); txModel.blockHeight = transaction.blockHeight; // Add to processing result processingResult.transactionModel = txModel; // Process specific entry functions if (transaction.payload?.type === "entry_function_payload") {{ const entryFunctionFullStr = transaction.payload.function; // Example: Process a specific entry function if (entryFunctionFullStr === "0x1::coin::transfer") {{ // Handle coin transfer function this.processCoinTransfer(transaction, processingResult); }} // TODO: Add more function handlers }} }} /** * Process a coin transfer transaction */ private processCoinTransfer( transaction: Transaction, processingResult: ProcessingResult, ): void {{ if ( transaction.payload?.type !== "entry_function_payload" || !transaction.payload.arguments ) {{ return; }} try {{ // Extract function arguments const [recipient, amount] = transaction.payload.arguments; // Create custom transaction model const transferModel = new Models.{project_name.capitalize()}TransferModel(); transferModel.version = transaction.version; transferModel.sender = transaction.sender; transferModel.recipient = recipient as string; transferModel.amount = BigInt(amount as string); transferModel.timestamp = new Date(Number(transaction.timestamp) / 1000); // Add to processing result processingResult.models.push(transferModel); console.log( `Processed transfer: ${{transaction.sender}} -> ${{recipient}} (${{amount}})`, ); }} catch (e) {{ console.error(`Error processing coin transfer: ${{e}}`); }} }} }} // Register processor new {project_name.capitalize()}TransactionProcessor().start(); """ def generate_event_processor(project_name: str) -> str: """Generate code for an event processor""" return f"""import {{ InputModels, Models, OutputModels, ProcessingResult, Event, EventProcessor, UserTransactionInput, parseEvent, }} from "@aptos-labs/indexer-sdk"; /** * {project_name.capitalize()} Event Processor * * This processor handles events and extracts relevant information. */ export class {project_name.capitalize()}EventProcessor extends EventProcessor {{ constructor() {{ super(); }} /** * Process a batch of events */ async process( eventInputs: InputModels.Event[], ): Promise<ProcessingResult> {{ const processingResult = new ProcessingResult(); console.log(`Processing ${{eventInputs.length}} events`); for (const eventInput of eventInputs) {{ const event = parseEvent(eventInput); // Process each event try {{ await this.processEvent(event, processingResult); }} catch (e) {{ console.error( `Error processing event ${{event.type}} (version: ${{event.version}}): ${{e}}`, ); }} }} return processingResult; }} /** * Process a single event */ async processEvent( event: Event, processingResult: ProcessingResult, ): Promise<void> {{ // Create base event model const eventModel = new Models.{project_name.capitalize()}EventModel(); eventModel.transactionVersion = event.version; eventModel.eventType = event.type; eventModel.data = event.data ? JSON.stringify(event.data) : null; eventModel.timestamp = new Date(Number(event.timestamp) / 1000); // Process specific event types if (event.type.includes("0x1::coin::DepositEvent")) {{ await this.processDepositEvent(event, processingResult); }} else if (event.type.includes("0x1::coin::WithdrawEvent")) {{ await this.processWithdrawEvent(event, processingResult); }} // Add base event to processing result processingResult.models.push(eventModel); }} /** * Process a deposit event */ private async processDepositEvent( event: Event, processingResult: ProcessingResult, ): Promise<void> {{ if (!event.data) {{ return; }} try {{ // Extract event data const {{ amount }} = event.data; // Create deposit model const depositModel = new Models.{project_name.capitalize()}DepositModel(); depositModel.transactionVersion = event.version; depositModel.address = event.accountAddress; depositModel.amount = BigInt(amount); depositModel.timestamp = new Date(Number(event.timestamp) / 1000); // Add to processing result processingResult.models.push(depositModel); console.log( `Processed deposit: ${{event.accountAddress}} (+${{amount}})`, ); }} catch (e) {{ console.error(`Error processing deposit event: ${{e}}`); }} }} /** * Process a withdraw event */ private async processWithdrawEvent( event: Event, processingResult: ProcessingResult, ): Promise<void> {{ if (!event.data) {{ return; }} try {{ // Extract event data const {{ amount }} = event.data; // Create withdraw model const withdrawModel = new Models.{project_name.capitalize()}WithdrawModel(); withdrawModel.transactionVersion = event.version; withdrawModel.address = event.accountAddress; withdrawModel.amount = BigInt(amount); withdrawModel.timestamp = new Date(Number(event.timestamp) / 1000); // Add to processing result processingResult.models.push(withdrawModel); console.log( `Processed withdraw: ${{event.accountAddress}} (-${{amount}})`, ); }} catch (e) {{ console.error(`Error processing withdraw event: ${{e}}`); }} }} }} // Register processor new {project_name.capitalize()}EventProcessor().start(); """ def generate_react_component(name: str, options: str) -> str:
- aptos_mcp_server.py:861-950 (helper)Helper function called by the handler to generate Move table module code.def generate_move_table(name: str, options: str) -> str: """Generate a Move table implementation""" return f""" module {name}::table {{ use std::signer; use aptos_framework::account; use aptos_framework::table::{{\n Table,\n new,\n add,\n borrow,\n borrow_mut,\n contains,\n remove\n }}; // Error codes const E_NOT_INITIALIZED: u64 = 1; const E_ALREADY_INITIALIZED: u64 = 2; const E_KEY_NOT_FOUND: u64 = 3; const E_KEY_ALREADY_EXISTS: u64 = 4; struct {name.capitalize()}Store has key {{ items: Table<KeyType, ValueType>, }} /// Key type for the table struct KeyType has copy, drop, store {{ // Define your key structure id: u64, }} /// Value type for the table struct ValueType has copy, drop, store {{ // Define your value structure data: u64, name: vector<u8>, }} /// Initialize the table public entry fun initialize(account: &signer) {{ let addr = signer::address_of(account); assert!(!exists<{name.capitalize()}Store>(addr), E_ALREADY_INITIALIZED); move_to(account, {name.capitalize()}Store {{ items: new<KeyType, ValueType>(), }}); }} /// Add an item to the table public entry fun add_item( account: &signer, id: u64, data: u64, name: vector<u8> ) acquires {name.capitalize()}Store {{ let addr = signer::address_of(account); assert!(exists<{name.capitalize()}Store>(addr), E_NOT_INITIALIZED); let store = borrow_mut<{name.capitalize()}Store>(addr); let key = KeyType {{ id }}; assert!(!contains(&store.items, key), E_KEY_ALREADY_EXISTS); add(&mut store.items, key, ValueType {{ data, name }}); }} /// Get an item from the table (public view function) #[view] public fun get_item(addr: address, id: u64): (u64, vector<u8>) acquires {name.capitalize()}Store {{ assert!(exists<{name.capitalize()}Store>(addr), E_NOT_INITIALIZED); let store = borrow<{name.capitalize()}Store>(addr); let key = KeyType {{ id }}; assert!(contains(&store.items, key), E_KEY_NOT_FOUND); let value = borrow(&store.items, key); (value.data, value.name) }} /// Remove an item from the table public entry fun remove_item(account: &signer, id: u64) acquires {name.capitalize()}Store {{ let addr = signer::address_of(account); assert!(exists<{name.capitalize()}Store>(addr), E_NOT_INITIALIZED); let store = borrow_mut<{name.capitalize()}Store>(addr); let key = KeyType {{ id }}; assert!(contains(&store.items, key), E_KEY_NOT_FOUND); remove(&mut store.items, key); }} }} """