Skip to main content
Glama

azeth_get_due_agreements

Find payment agreements ready for execution across specified accounts. Returns actionable data for keeper bots to process due payments or service providers to collect owed funds.

Instructions

Find all payment agreements that are due for execution across one or more accounts.

Use this when: You are a keeper bot looking for agreements to execute, or a service provider checking which of your customers' payments are collectible.

Returns: Array of due agreements with payer account, agreement ID, and expected payout. Each entry can be passed directly to azeth_execute_agreement.

Note: This scans all agreements for the specified accounts. For large-scale keeper operations, consider filtering by specific accounts rather than scanning all.

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").
accountsNoAccounts to scan: addresses, names, "me", or "#N". Defaults to ["me"].

Implementation Reference

  • The handler logic for 'azeth_get_due_agreements' that fetches agreement data, checks executability, calculates payouts, and returns a sorted list of due agreements.
    async (args) => {
      let client;
      try {
        client = await createClient(args.chain);
        const chain = resolveChain(args.chain);
    
        // Resolve all accounts
        const accountInputs = args.accounts ?? ['me'];
        const resolvedAccounts: Array<{ address: `0x${string}`; name?: string }> = [];
    
        for (const input of accountInputs) {
          try {
            const resolved = await resolveAddress(input, client);
            resolvedAccounts.push({
              address: resolved.address,
              name: resolved.name ?? resolved.resolvedFrom,
            });
          } catch (resolveErr) {
            return handleError(resolveErr);
          }
        }
    
        let scannedAgreements = 0;
        const dueAgreements: Array<Record<string, unknown>> = [];
    
        for (const acct of resolvedAccounts) {
          // Get count from first getAgreementData call (avoids separate getAgreementCount RPC)
          let count: bigint;
          try {
            const firstData = await client.getAgreementData(0n, acct.address);
            count = firstData.count;
          } catch {
            continue;
          }
    
          for (let i = 0; i < Number(count); i++) {
            scannedAgreements++;
            // Single RPC: agreement + executability + isDue + nextExecutionTime
            let data;
            try {
              data = await client.getAgreementData(BigInt(i), acct.address);
            } catch {
              continue;
            }
    
            const { agreement, executable, isDue, nextExecutionTime: nextExecTime } = data;
            if (!agreement.active) continue;
            if (!executable || !isDue) continue;
    
            const decimals = tokenDecimals(agreement.token, chain);
            const tokenSymbol = resolveTokenSymbol(agreement.token, chain);
    
            // Estimate payout (pro-rata based on elapsed time)
            const nowSecs = BigInt(Math.floor(Date.now() / 1000));
            const elapsed = agreement.lastExecuted === 0n
              ? agreement.interval // first execution: assume full interval
              : nowSecs - agreement.lastExecuted;
            const estimatedPayout = elapsed > 0n
              ? (agreement.amount * elapsed) / agreement.interval
              : agreement.amount;
            // Cap at 3x interval (max accrual multiplier)
            const cappedPayout = estimatedPayout > agreement.amount * 3n
              ? agreement.amount * 3n
              : estimatedPayout;
    
            // Calculate overdue from nextExecutionTime (already available, no extra RPC)
            let overdueBy: string | undefined;
            const diff = Math.floor(Date.now() / 1000) - Number(nextExecTime);
            if (diff > 0) {
              overdueBy = formatOverdue(diff);
            }
    
            // Payee name
            const payeeName = await lookupPayeeName(client, agreement.payee);
    
            dueAgreements.push({
              account: acct.address,
              ...(acct.name ? { accountName: acct.name } : {}),
              agreementId: i.toString(),
              payee: agreement.payee,
              ...(payeeName ? { payeeName } : {}),
              tokenSymbol,
              estimatedPayout: formatUnits(cappedPayout, decimals),
              ...(overdueBy ? { overdueBy } : {}),
            });
          }
        }
    
        // Sort by estimated payout descending (highest value first for keeper prioritization)
        dueAgreements.sort((a, b) => {
          const payoutA = parseFloat(a.estimatedPayout as string);
          const payoutB = parseFloat(b.estimatedPayout as string);
          return payoutB - payoutA;
        });
    
        return success({
          scannedAccounts: resolvedAccounts.length,
          scannedAgreements,
          dueAgreements,
        });
      } catch (err) {
        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`); }
  • Registration of 'azeth_get_due_agreements' tool with the MCP server, including the description and input schema.
    server.registerTool(
      'azeth_get_due_agreements',
      {
        description: [
          'Find all payment agreements that are due for execution across one or more accounts.',
          '',
          'Use this when: You are a keeper bot looking for agreements to execute,',
          'or a service provider checking which of your customers\' payments are collectible.',
          '',
          'Returns: Array of due agreements with payer account, agreement ID, and expected payout.',
          'Each entry can be passed directly to azeth_execute_agreement.',
          '',
          'Note: This scans all agreements for the specified accounts. For large-scale keeper operations,',
          'consider filtering by specific accounts rather than scanning all.',
        ].join('\n'),
        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").'),
          accounts: z.array(z.string()).min(1).max(20).optional()
            .describe('Accounts to scan: addresses, names, "me", or "#N". Defaults to ["me"].'),
        }),
      },
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It successfully discloses the scanning nature ('scans all agreements'), performance implications ('consider filtering... for large-scale'), return structure ('Array of due agreements with...'), and workflow integration ('can be passed directly to azeth_execute_agreement'). Lacks explicit confirmation of read-only safety.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

optimally structured with clear functional sections: purpose (sentence 1), usage context (sentence 2-3), return value (sentence 4), workflow integration (sentence 5), and performance note (sentence 6). No redundant text; every sentence advances agent understanding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite lacking an output schema, the description fully compensates by detailing the return structure (array with payer account, agreement ID, expected payout). It also explains the keeper/execution workflow context essential for blockchain payment agreement tools. Complete for a 2-parameter query tool with no annotations.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 100% description coverage for both 'chain' and 'accounts' parameters. The description references 'one or more accounts' which aligns with the accounts parameter, but does not add semantic detail beyond what the comprehensive schema already provides, warranting the baseline score of 3.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb ('Find') and resource ('payment agreements that are due for execution'), clearly distinguishing this from sibling tool 'azeth_list_agreements' by emphasizing the 'due' status filter. It also explicitly names 'azeth_execute_agreement' as the downstream consumer, establishing clear separation of concerns between querying and execution.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Contains an explicit 'Use this when:' section identifying two distinct personas (keeper bots and service providers) with concrete scenarios. It also provides implicit 'when-not' guidance via the Note recommending filtering for large-scale operations, helping agents optimize usage patterns.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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