Skip to main content
Glama

t2000_withdraw

Withdraw assets from NAVI lending directly to your wallet. Supports any deposited asset; specify amount or use 'all' for full withdrawal. Preview with dry run mode.

Instructions

Withdraw from NAVI lending back to wallet. Supports any deposited asset. Amount is in token units. Use "all" to withdraw everything. Set dryRun: true to preview.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
amountYesAmount to withdraw, or "all"
assetNoAsset to withdraw (default: auto-selects largest position)
dryRunNoPreview without signing (default: false)

Implementation Reference

  • Registers the 't2000_withdraw' tool on the MCP server using server.tool(), with Zod schema for amount (number or 'all'), asset (optional string), and dryRun (optional boolean). The handler calls agent.withdraw() via a mutex.
    server.tool(
      't2000_withdraw',
      'Withdraw from NAVI lending back to wallet. Supports any deposited asset. Amount is in token units. Use "all" to withdraw everything. Set dryRun: true to preview.',
      {
        amount: z.union([z.number(), z.literal('all')]).describe('Amount to withdraw, or "all"'),
        asset: z.string().optional().describe('Asset to withdraw (default: auto-selects largest position)'),
        dryRun: z.boolean().optional().describe('Preview without signing (default: false)'),
      },
      async ({ amount, asset, dryRun }) => {
        try {
          if (dryRun) {
            agent.enforcer.assertNotLocked();
            const positions = await agent.positions();
            const health = await agent.healthFactor();
            const savings = positions.positions
              .filter(p => p.type === 'save' && (!asset || p.asset === asset))
              .reduce((sum, p) => sum + p.amount, 0);
    
            return {
              content: [{
                type: 'text',
                text: JSON.stringify({
                  preview: true,
                  amount: amount === 'all' ? savings : amount,
                  asset: asset ?? 'auto',
                  currentSavings: savings,
                  currentHealthFactor: health.healthFactor,
                }),
              }],
            };
          }
    
          const result = await mutex.run(() => agent.withdraw({ amount, asset }));
          return { content: [{ type: 'text', text: JSON.stringify(result) }] };
        } catch (err) {
          return errorResult(err);
        }
      },
    );
  • The async handler for t2000_withdraw. If dryRun is true, fetches positions and health factor to preview. Otherwise, calls agent.withdraw({ amount, asset }) via mutex.run() and returns the result as JSON text.
    async ({ amount, asset, dryRun }) => {
      try {
        if (dryRun) {
          agent.enforcer.assertNotLocked();
          const positions = await agent.positions();
          const health = await agent.healthFactor();
          const savings = positions.positions
            .filter(p => p.type === 'save' && (!asset || p.asset === asset))
            .reduce((sum, p) => sum + p.amount, 0);
    
          return {
            content: [{
              type: 'text',
              text: JSON.stringify({
                preview: true,
                amount: amount === 'all' ? savings : amount,
                asset: asset ?? 'auto',
                currentSavings: savings,
                currentHealthFactor: health.healthFactor,
              }),
            }],
          };
        }
    
        const result = await mutex.run(() => agent.withdraw({ amount, asset }));
        return { content: [{ type: 'text', text: JSON.stringify(result) }] };
      } catch (err) {
        return errorResult(err);
      }
    },
  • Zod input schema for t2000_withdraw: amount (z.number() | z.literal('all')), asset (z.string().optional()), dryRun (z.boolean().optional()).
    {
      amount: z.union([z.number(), z.literal('all')]).describe('Amount to withdraw, or "all"'),
      asset: z.string().optional().describe('Asset to withdraw (default: auto-selects largest position)'),
      dryRun: z.boolean().optional().describe('Preview without signing (default: false)'),
    },
  • Calls registerWriteTools(server, agent) which registers t2000_withdraw among other write tools.
    registerWriteTools(server, agent);
  • Engine-level withdraw tool definition used by the CLI ('serve' command) — separate from the MCP tool but shows the underlying agent.withdraw() call pattern.
    export const withdrawTool = buildTool({
      name: 'withdraw',
      description:
        'Withdraw USDC or USDsui from NAVI lending back to wallet. Defaults to USDC. ' +
        'Audric supports ONLY USDC and USDsui — these are the same two stables save_deposit accepts. ' +
        'NAVI may also surface legacy positions (USDe, SUI, etc.) in savings_info / balance_check; those are READ-ONLY through Audric. ' +
        'For non-canonical positions, direct the user to NAVI\'s app (https://app.naviprotocol.io) — Audric will not withdraw them. ' +
        'Payment Intent: composable — when paired with another composable write in the same request (e.g. "withdraw and send to Mom"), emit all calls in the same assistant turn so the engine compiles them into one atomic Payment Intent the user signs once.',
      inputSchema: z.object({
        amount: z.number().positive(),
        asset: z.string().optional().describe('Asset to withdraw — must be USDC (default) or USDsui. Other assets surfaced in savings_info are read-only via Audric.'),
      }),
      jsonSchema: {
        type: 'object',
        properties: {
          amount: {
            description: 'Exact amount to withdraw in token units',
          },
          asset: {
            type: 'string',
            description: 'Asset to withdraw — USDC (default) or USDsui only. Other assets surfaced in savings_info are read-only via Audric; direct the user to https://app.naviprotocol.io for those.',
          },
        },
        required: ['amount'],
      },
      isReadOnly: false,
      permissionLevel: 'confirm',
      flags: { mutating: true, affectsHealth: true },
    
      async call(input, context) {
        const agent = requireAgent(context);
        const result = await agent.withdraw({
          amount: input.amount,
          asset: input.asset,
        });
    
        const withdrawnAsset = (result as { asset?: string }).asset ?? input.asset ?? 'USDC';
        return {
          data: {
            success: result.success,
            tx: result.tx,
            amount: result.amount,
            asset: withdrawnAsset,
            gasCost: result.gasCost,
          },
          displayText: `Withdrew ${result.amount.toFixed(result.amount < 1 ? 6 : 2)} ${withdrawnAsset} (tx: ${result.tx.slice(0, 8)}…)`,
        };
      },
    });
Behavior3/5

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

No annotations are provided, so the description carries the full burden. It discloses key behaviors: withdrawal from lending, support for any deposited asset, amount in token units, the 'all' option, and dryRun preview. However, it does not mention that withdrawals are irreversible or require signing, nor does it specify auth requirements. The description is adequate but lacks depth for a write operation.

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?

The description is three concise sentences, each adding useful information without redundancy. It is front-loaded with the main action and efficiently covers the purpose, special values, and preview option.

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

Completeness4/5

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

Given no output schema and no annotations, the description is fairly complete. It explains the amount format, the 'all' option, and dryRun preview. It could mention what happens on success (e.g., transaction created), but the dryRun feature implies signing is needed. The description is sufficient for an agent to use the tool correctly.

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

Parameters5/5

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

Schema coverage is 100% with descriptions for all three parameters. The tool description adds additional clarity: 'Amount is in token units', 'Use "all" to withdraw everything', and 'Supports any deposited asset', which goes beyond the schema descriptions.

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 uses a specific verb ('Withdraw') and resource ('NAVI lending back to wallet'), clearly distinguishing it from siblings like 't2000_borrow', 't2000_repay', and 't2000_deposit_info'. It also states it supports any deposited asset.

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

Usage Guidelines4/5

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

The description provides guidance on using special values like 'all' and the dryRun parameter for preview. However, it does not explicitly state when to avoid this tool or mention alternatives like using 't2000_borrow' for borrowing actions, which is implied by context but not explicit.

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/mission69b/t2000'

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