Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
chainNoTarget chain. Defaults to AZETH_CHAIN env var or "baseSepolia". Accepts "base", "baseSepolia", "ethereumSepolia", "ethereum" (and aliases like "base-sepolia", "eth-sepolia", "sepolia", "eth", "mainnet").
nameYesDisplay name for this participant in the trust registry.
entityTypeYesParticipant type: "agent" (AI agent), "service" (API/oracle), or "infrastructure" (bridge/relay).
descriptionYesHuman-readable description of what this participant does.
capabilitiesYesList of capabilities this participant offers (e.g., ["swap", "price-feed", "translation"]).
endpointNoOptional HTTP endpoint (http:// or https://) where this participant can be reached.
maxTxAmountUSDNoMax USD per transaction (default: $100 testnet, $50 mainnet).
dailySpendLimitUSDNoMax USD per day (default: $1000 testnet, $500 mainnet).
guardianNoGuardian 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).
emergencyWithdrawToNoAddress 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

  • 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: `[![Azeth Trust Badge](${badgeUrl})](${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`); }
      }
    },
  • 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.',
      ),
    }),
  • Tool registration for 'azeth_create_account'.
    server.registerTool(
      'azeth_create_account',

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/azeth-protocol/mcp-azeth'

If you have feedback or need assistance with the MCP directory API, please join our Discord server