azeth_create_account
Deploy a smart account with spending limits and trust registry registration for AI agents and services, enabling on-chain identity without requiring ETH for gas.
Instructions
Deploy a new Azeth smart account with guardian guardrails and register it on the ERC-8004 trust registry.
Use this when: An AI agent or service needs its own on-chain identity with spending limits and trust registry presence. One EOA can own multiple smart accounts.
Single atomic transaction: deploys smart account proxy, installs all 4 modules (Guardian, TrustRegistry, PaymentAgreement, Reputation), registers on ERC-8004, and permanently revokes factory access.
Returns: The deployed smart account address, trust registry token ID, and transaction hash.
Gas is automatically sponsored — no ETH required. The SDK uses a gasless relay (EIP-712 signature) so the account can be created without any pre-funding. On testnet, USDC is also auto-funded.
Guardian: By default, the guardian is derived from AZETH_GUARDIAN_KEY env var. If not set, falls back to self-guardian (owner address). For production, always use a separate guardian key. Set AZETH_GUARDIAN_KEY in your .env file.
Example: { "name": "PriceFeedBot", "entityType": "service", "description": "Real-time crypto price data", "capabilities": ["price-feed", "market-data"] }
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chain | No | Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet"). | |
| name | Yes | Display name for this participant in the trust registry. | |
| entityType | Yes | Participant type: "agent" (AI agent), "service" (API/oracle), or "infrastructure" (bridge/relay). | |
| description | Yes | Human-readable description of what this participant does. | |
| capabilities | Yes | List of capabilities this participant offers (e.g., ["swap", "price-feed", "translation"]). | |
| endpoint | No | Optional HTTP endpoint (http:// or https://) where this participant can be reached. | |
| maxTxAmountUSD | No | Max USD per transaction (default: $100 testnet, $50 mainnet). | |
| dailySpendLimitUSD | No | Max USD per day (default: $1000 testnet, $500 mainnet). | |
| guardian | No | Guardian address for co-signing operations that exceed spending limits. If omitted, derived from AZETH_GUARDIAN_KEY env var. If neither is set, defaults to the owner address (self-guardian, NOT recommended for production). | |
| emergencyWithdrawTo | No | Address where funds are sent during emergency withdrawal. Defaults to the owner EOA address (derived from AZETH_PRIVATE_KEY). Must be a trusted address you control — this is your recovery destination. |
Implementation Reference
- src/tools/account.ts:94-214 (handler)The handler logic for 'azeth_create_account' tool, which handles address validation, guardian/emergency address resolution, guardrail setup, and account creation via the Azeth client.
async (args) => { // Runtime validation for endpoint protocol (replaces .refine() which breaks MCP JSON Schema) if (args.endpoint && !args.endpoint.startsWith('http://') && !args.endpoint.startsWith('https://')) { return error('INVALID_INPUT', 'Endpoint must use HTTP or HTTPS protocol.'); } let client; try { client = await createClient(args.chain); // Default guardrails: conservative limits const isTestnet = resolveChain(args.chain) === 'baseSepolia'; const maxTxUSD = args.maxTxAmountUSD ?? (isTestnet ? 100 : 50); const dailyUSD = args.dailySpendLimitUSD ?? (isTestnet ? 1000 : 500); // Resolve guardian address: explicit param > AZETH_GUARDIAN_KEY env > self-guardian (owner) let guardianAddress: `0x${string}` = client.address; // fallback: self-guardian let guardianSource = 'self (owner EOA)'; if (args.guardian) { // Explicit guardian address provided if (!validateAddress(args.guardian)) { return error('INVALID_INPUT', `Invalid guardian address: "${args.guardian}".`, 'Must be 0x-prefixed followed by 40 hex characters.'); } guardianAddress = args.guardian as `0x${string}`; guardianSource = 'explicit parameter'; } else { // Try deriving from AZETH_GUARDIAN_KEY env var const guardianKey = process.env['AZETH_GUARDIAN_KEY']; if (guardianKey && /^0x[0-9a-fA-F]{64}$/.test(guardianKey.trim())) { const { privateKeyToAccount } = await import('viem/accounts'); const guardianAccount = privateKeyToAccount(guardianKey.trim() as `0x${string}`); guardianAddress = guardianAccount.address; guardianSource = 'AZETH_GUARDIAN_KEY env var'; } } // Resolve emergencyWithdrawTo: explicit param > AZETH_EMERGENCY_ADDRESS env > owner EOA let emergencyAddress: `0x${string}` = client.address; if (args.emergencyWithdrawTo) { if (!validateAddress(args.emergencyWithdrawTo)) { return error('INVALID_INPUT', `Invalid emergencyWithdrawTo address: "${args.emergencyWithdrawTo}".`, 'Must be 0x-prefixed followed by 40 hex characters.'); } emergencyAddress = args.emergencyWithdrawTo as `0x${string}`; } else { const envEmergency = process.env['AZETH_EMERGENCY_ADDRESS']; if (envEmergency && validateAddress(envEmergency)) { emergencyAddress = envEmergency as `0x${string}`; } } // Default token whitelist: ETH + USDC + WETH so that payment agreements // and other executor-module operations work out of the box. const chain = resolveChain(args.chain); const defaultTokens: `0x${string}`[] = [ '0x0000000000000000000000000000000000000000', TOKENS[chain].USDC, TOKENS[chain].WETH, ]; const serverUrl = process.env['AZETH_SERVER_URL'] ?? 'https://api.azeth.ai'; const result = await client.createAccount({ owner: client.address, guardrails: { maxTxAmountUSD: BigInt(Math.round(maxTxUSD)) * 10n ** 18n, dailySpendLimitUSD: BigInt(Math.round(dailyUSD)) * 10n ** 18n, guardianMaxTxAmountUSD: BigInt(Math.round(maxTxUSD * 5)) * 10n ** 18n, guardianDailySpendLimitUSD: BigInt(Math.round(dailyUSD * 5)) * 10n ** 18n, guardian: guardianAddress, emergencyWithdrawTo: emergencyAddress, }, tokens: defaultTokens, registry: { name: args.name, description: args.description, entityType: args.entityType, capabilities: args.capabilities, endpoint: args.endpoint, }, }); const tokenIdStr = result.tokenId.toString(); const badgeUrl = `${serverUrl}/badge/${tokenIdStr}`; const profileUrl = `https://azeth.ai/agent/${result.account}`; return success( { account: result.account, tokenId: tokenIdStr, txHash: result.txHash, guardian: guardianAddress, guardianSource, emergencyWithdrawTo: emergencyAddress, badge: badgeUrl, profile: profileUrl, embedMarkdown: `[](${profileUrl})`, ...(guardianAddress === client.address ? { warning: 'Guardian is set to the owner address (self-guardian). This means guardian co-signatures use the same key as the owner, providing no additional security. For production, set AZETH_GUARDIAN_KEY in your environment or provide a separate guardian address.', } : {}), ...(wasAutoGenerated() ? { keySource: 'auto-generated', keyPath: '~/.azeth/key', keyWarning: 'This account uses an auto-generated demo key stored at ~/.azeth/key. For production, set AZETH_PRIVATE_KEY in your environment with a key you control.', } : {}), }, { txHash: result.txHash }, ); } catch (err) { if (err instanceof Error && err.message.includes('insufficient funds')) { return error( 'INSUFFICIENT_BALANCE', 'Account deployment failed: gasless relay unavailable and your EOA has no ETH for direct deployment.', 'The relay at api.azeth.ai may be down or rate-limited. Try again later, or send a small amount of ETH to your EOA address for direct deployment.', ); } return handleError(err); } finally { try { await client?.destroy(); } catch (e) { process.stderr.write(`[azeth-mcp] destroy error: ${e instanceof Error ? e.message : String(e)}\n`); } } }, - src/tools/account.ts:66-92 (schema)Input schema definition for the 'azeth_create_account' tool.
inputSchema: z.object({ chain: z.string().optional().describe('Target chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").'), name: z.string().min(1).max(256).describe('Display name for this participant in the trust registry.'), entityType: z.enum(['agent', 'service', 'infrastructure']).describe('Participant type: "agent" (AI agent), "service" (API/oracle), or "infrastructure" (bridge/relay).'), description: z.string().min(1).max(2048).describe('Human-readable description of what this participant does.'), capabilities: z.preprocess( (val) => typeof val === 'string' ? JSON.parse(val) : val, z.array(z.string().max(128)).min(1).max(50), ).describe('List of capabilities this participant offers (e.g., ["swap", "price-feed", "translation"]).'), endpoint: z.string().url().max(2048) .optional() .describe('Optional HTTP endpoint (http:// or https://) where this participant can be reached.'), maxTxAmountUSD: z.coerce.number().positive().optional() .describe('Max USD per transaction (default: $100 testnet, $50 mainnet).'), dailySpendLimitUSD: z.coerce.number().positive().optional() .describe('Max USD per day (default: $1000 testnet, $500 mainnet).'), guardian: z.string().optional().describe( 'Guardian address for co-signing operations that exceed spending limits. ' + 'If omitted, derived from AZETH_GUARDIAN_KEY env var. ' + 'If neither is set, defaults to the owner address (self-guardian, NOT recommended for production).', ), emergencyWithdrawTo: z.string().optional().describe( 'Address where funds are sent during emergency withdrawal. ' + 'Defaults to the owner EOA address (derived from AZETH_PRIVATE_KEY). ' + 'Must be a trusted address you control — this is your recovery destination.', ), }), - src/tools/account.ts:44-45 (registration)Tool registration for 'azeth_create_account'.
server.registerTool( 'azeth_create_account',