> ## Documentation Index
> Fetch the complete documentation index at: https://aixyz.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent and Tools

> Define agents with Vercel AI SDK and create tools for A2A and MCP

export const FileTree = ({structure = [], title, maxDepth = 10, showIcons = true, commentPrefix = "# "}) => {
  const lineColor = "rgba(156, 163, 175, 0.25)";
  const badgeColors = {
    blue: {
      bg: "rgba(59,130,246,0.1)",
      text: "#3b82f6",
      border: "rgba(59,130,246,0.2)"
    },
    green: {
      bg: "rgba(34,197,94,0.1)",
      text: "#22c55e",
      border: "rgba(34,197,94,0.2)"
    },
    purple: {
      bg: "rgba(168,85,247,0.1)",
      text: "#a855f7",
      border: "rgba(168,85,247,0.2)"
    },
    red: {
      bg: "rgba(239,68,68,0.1)",
      text: "#ef4444",
      border: "rgba(239,68,68,0.2)"
    },
    amber: {
      bg: "rgba(245,158,11,0.1)",
      text: "#f59e0b",
      border: "rgba(245,158,11,0.2)"
    }
  };
  const folderSvg = <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    flexShrink: 0,
    color: "#94a3b8"
  }} aria-hidden="true" focusable="false">
      <path d="m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2" />
    </svg>;
  const fileSvg = <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
    flexShrink: 0,
    color: "#94a3b8"
  }} aria-hidden="true" focusable="false">
      <path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" />
      <path d="M14 2v4a2 2 0 0 0 2 2h4" />
    </svg>;
  const charWidth = 8.4;
  const iconGap = showIcons ? 22 : 0;
  const connectorWidth = 20;
  const measure = (items, level) => {
    let max = 0;
    for (const item of items) {
      if (!item || !item.name) continue;
      const offset = level * connectorWidth + iconGap + item.name.length * charWidth;
      if (offset > max) max = offset;
      if (item.children) {
        const childMax = measure(item.children, level + 1);
        if (childMax > max) max = childMax;
      }
    }
    return max;
  };
  const descColumn = Math.ceil(measure(structure, 0)) + 16;
  const render = (item, level, isLast) => {
    if (!item || !item.name || level >= maxDepth) return null;
    const hasChildren = item.children != null && item.children.length > 0;
    const badge = item.badge ? (() => {
      const c = badgeColors[item.badge.color] || badgeColors.blue;
      return <span style={{
        fontSize: "0.625rem",
        fontWeight: 500,
        padding: "1px 6px",
        borderRadius: "4px",
        backgroundColor: c.bg,
        color: c.text,
        border: `1px solid ${c.border}`,
        lineHeight: "1.4",
        fontFamily: "ui-sans-serif, system-ui, sans-serif"
      }}>
              {item.badge.text}
            </span>;
    })() : null;
    const connector = level > 0 ? <div style={{
      position: "relative",
      width: `${connectorWidth}px`,
      flexShrink: 0,
      alignSelf: "stretch"
    }}>
          <div style={{
      position: "absolute",
      left: "0",
      top: "0",
      bottom: isLast ? "calc(100% - 12px)" : "0",
      width: "1px",
      backgroundColor: lineColor
    }} />
          <div style={{
      position: "absolute",
      left: "0",
      top: "12px",
      width: "12px",
      height: "1px",
      backgroundColor: lineColor
    }} />
        </div> : null;
    const nameAreaWidth = descColumn - level * connectorWidth;
    const label = <div style={{
      display: "flex",
      alignItems: "center",
      gap: "6px",
      paddingTop: "1px",
      paddingBottom: "1px",
      minHeight: "24px"
    }}>
        <div style={{
      display: "flex",
      alignItems: "center",
      gap: "6px",
      minWidth: `${nameAreaWidth}px`,
      flexShrink: 0
    }}>
          {showIcons && (hasChildren ? folderSvg : fileSvg)}
          <span style={hasChildren ? {
      fontWeight: 500
    } : undefined}>{item.name}</span>
        </div>
        {item.description && <span style={{
      fontSize: "0.75rem",
      color: "#94a3b8",
      whiteSpace: "nowrap"
    }}>
            {commentPrefix}
            {item.description}
          </span>}
        {badge}
      </div>;
    return <div key={`${item.name}-${level}`} style={{
      display: "flex",
      flexDirection: "row"
    }}>
        {connector}
        <div style={{
      flex: 1,
      minWidth: 0
    }}>
          {label}
          {hasChildren && item.children.map((child, idx) => render(child, level + 1, idx === item.children.length - 1))}
        </div>
      </div>;
  };
  return <div className="not-prose mt-5 mb-8 rounded-2xl relative text-gray-950 bg-gray-50 dark:bg-white/5 dark:text-gray-50 border border-gray-950/10 dark:border-white/10 p-0.5">
      {title && <div className="flex text-gray-400 text-xs rounded-t-[14px] leading-6 font-medium pl-4 pr-2.5 py-1">
          <div className="flex-none flex items-center gap-1.5 text-gray-700 dark:text-gray-300">{title}</div>
        </div>}
      <div className="w-0 min-w-full max-w-full py-3 px-4 bg-white dark:bg-[#0d1117] overflow-x-auto rounded-xl" style={{
    fontFamily: "ui-monospace, SFMono-Regular, monospace",
    fontSize: "0.875rem"
  }}>
        {structure.map((item, idx) => render(item, 0, idx === structure.length - 1))}
      </div>
    </div>;
};

## Agent (`app/agent.ts`)

Your agent is defined using [Vercel AI SDK](https://ai-sdk.dev) (`ai@^6`). The file exports a default `ToolLoopAgent` and an optional `accepts` for payment pricing.

```typescript title="app/agent.ts" theme={null}
import { openai } from "@ai-sdk/openai";
import { stepCountIs, ToolLoopAgent } from "ai";
import type { Accepts } from "aixyz/accepts";
import weather from "./tools/weather";

export const accepts: Accepts = {
  scheme: "exact",
  price: "$0.005",
};

export default new ToolLoopAgent({
  model: openai("gpt-4o-mini"),
  instructions: "You are a helpful weather assistant.",
  tools: { weather },
  stopWhen: stepCountIs(10),
});
```

### `ToolLoopAgent`

The `ToolLoopAgent` from Vercel AI SDK handles multi-step tool execution loops automatically. It:

* Takes a model, instructions, and a set of tools
* Executes tool calls in a loop until the stop condition is met
* Returns the final response

### `accepts` Export

The optional `accepts` named export controls [x402 payment](/getting-started/payments) gating on the A2A `/agent` endpoint:

* Agents **with** `accepts` are registered on payment-gated A2A endpoints
* Agents **without** `accepts` are not registered

You can also pass an array to accept payment on multiple networks:

```typescript theme={null}
export const accepts: Accepts = [
  { scheme: "exact", price: "$0.005", network: "eip155:8453" },
  { scheme: "exact", price: "$0.005", network: "eip155:84532" },
];
```

### `capabilities` Export

The optional `capabilities` named export configures the A2A agent card's capabilities and controls how the executor runs your agent:

```typescript title="app/agent.ts" theme={null}
import type { Capabilities } from "aixyz/app/plugins/a2a";

export const capabilities: Capabilities = {
  streaming: false,
  pushNotifications: false,
};
```

* `streaming` (default: `true`) — When `false`, the executor uses `generate()` instead of `stream()`, returning the full response as a single artifact
* `pushNotifications` (default: `false`) — Advertises push notification support in the agent card
* `stateTransitionHistory` — Advertises state transition history support in the agent card

If omitted, the agent defaults to `{ streaming: true, pushNotifications: false }`. See the [agent.ts reference](/api-reference/agent#capabilities) for details.

## Sub-Agents (`app/agents/*.ts`)

Place additional agent files in `app/agents/` to expose multiple A2A endpoints from a single deployment. Each file follows the same format as `app/agent.ts` and is automatically registered on its own path.

```typescript title="app/agents/research.ts" theme={null}
// → /research/agent
import { openai } from "@ai-sdk/openai";
import { stepCountIs, ToolLoopAgent } from "ai";
import type { Accepts } from "aixyz/accepts";

export const accepts: Accepts = {
  scheme: "exact",
  price: "$0.005",
};

export default new ToolLoopAgent({
  model: openai("gpt-4o-mini"),
  instructions: "You are a research assistant.",
  stopWhen: stepCountIs(10),
});
```

With the layout below, the build auto-generates three independent A2A endpoints:

<FileTree
  structure={[
{
  name: "app/",
  children: [
    { name: "agent.ts", description: "→ /agent" },
    {
      name: "agents/",
      children: [
        { name: "research.ts", description: "→ /research/agent" },
        { name: "implement.ts", description: "→ /implement/agent" },
      ],
    },
  ],
},
]}
/>

<Note>
  Files starting with `_` (e.g., `_helpers.ts`) are ignored by the build pipeline. Use this convention for shared
  utilities.
</Note>

## Tools (`app/tools/*.ts`)

Each `.ts` file in `app/tools/` exports a Vercel AI SDK `tool` and an optional `accepts` for MCP payment gating. Tools are automatically discovered and registered on both A2A and MCP endpoints.

```typescript title="app/tools/weather.ts" theme={null}
import { tool } from "ai";
import { z } from "zod";
import type { Accepts } from "aixyz/accepts";

export const accepts: Accepts = {
  scheme: "exact",
  price: "$0.0001",
};

export default tool({
  description: "Get current weather conditions for a city.",
  inputSchema: z.object({
    location: z.string().describe("City name"),
  }),
  execute: async ({ location }) => {
    // your implementation
  },
});
```

### Tool Discovery

The build pipeline automatically discovers all `.ts` files in `app/tools/`:

* Each file is registered as an MCP tool
* Tools with `accepts` are payment-gated on the `/mcp` endpoint
* Tools without `accepts` are not registered on MCP

<Note>
  Files starting with `_` (e.g., `_helpers.ts`) are ignored by the build pipeline. Use this convention for shared
  utilities.
</Note>

### Per-Tool Pricing

Each tool can declare its own price, enabling granular pricing across your agent:

```typescript theme={null}
// app/tools/basic-search.ts — free tool (no accepts)
export default tool({ ... });

// app/tools/premium-search.ts — paid tool
export const accepts: Accepts = { scheme: "exact", price: "$0.001" };
export default tool({ ... });
```

Tools also support [multiple payment options](/getting-started/payments#multiple-payment-options) via an array of accepts entries.

## Custom Server

For full control over endpoint registration and middleware, create `app/server.ts`. This overrides auto-generation entirely:

```typescript title="app/server.ts" theme={null}
import { AixyzApp } from "aixyz/app";
import { IndexPagePlugin } from "aixyz/app/plugins/index-page";
import { A2APlugin } from "aixyz/app/plugins/a2a";
import { MCPPlugin } from "aixyz/app/plugins/mcp";

import * as agent from "./agent";
import * as lookup from "./tools/lookup";

const server = new AixyzApp();

await server.withPlugin(new IndexPagePlugin());
await server.withPlugin(new A2APlugin([{ exports: agent }]));
await server.withPlugin(
  new MCPPlugin([
    {
      name: "lookup",
      exports: {
        default: lookup.default,
        accepts: { scheme: "exact", price: "$0.001" },
      },
    },
  ]),
);

await server.initialize();

export default server;
```

See the [Custom Server template](/templates/advanced/with-custom-server) for a working example.
