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

# Project Structure

> Understand the app/ directory structure and file conventions

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>;
};

An aixyz agent is defined by a small set of files in a standard layout. Run `aixyz dev` and the framework auto-generates a server from them.

## Directory Layout

<FileTree
  title="Project Structure"
  structure={[
{
  name: "my-agent/",
  children: [
    {
      name: "aixyz.config.ts",
      description: "Agent identity, payment config, and skills",
      badge: { text: "required", color: "green" },
    },
    {
      name: "app/",
      children: [
        { name: "agent.ts", description: "Main agent definition" },
        { name: "agent.test.ts", description: "Agent tests" },
        {
          name: "server.ts",
          description: "Custom server override",
          badge: { text: "optional override", color: "purple" },
        },
        {
          name: "accepts.ts",
          description: "Custom x402 facilitator",
          badge: { text: "optional override", color: "purple" },
        },
        {
          name: "session.ts",
          description: "Custom session store",
          badge: { text: "optional override", color: "purple" },
        },
        {
          name: "erc-8004.ts",
          description: "ERC-8004 identity registration",
        },
        {
          name: "agents/",
          children: [
            { name: "research.ts", description: "Sub-agent → /research/agent" },
            { name: "implement.ts", description: "Sub-agent → /implement/agent" },
            { name: "[...].ts", description: "Any sub-agents declaration" },
          ],
        },
        {
          name: "tools/",
          children: [
            { name: "weather.ts", description: "Tool implementation" },
            { name: "search.ts", description: "Tool implementation" },
            { name: "_helpers.ts", description: "Ignored — files starting with _ are skipped" },
            { name: "[...].ts", description: "Any tools declaration" },
          ],
        },
        {
          name: "icon.svg",
          description: "Agent icon (served as static asset)",
        },
      ],
    },
    { name: "package.json", badge: { text: "required", color: "green" } },
    { name: "tsconfig.json", badge: { text: "required", color: "green" } },
    { name: "vercel.json", description: "Vercel deployment config" },
    { name: ".env.local", description: "API keys and environment variables" },
  ],
},
]}
/>

<div
  style={{
display: "flex",
flexDirection: "column",
gap: "4px",
fontSize: "0.75rem",
color: "#94a3b8",
marginTop: "-24px",
paddingLeft: "4px",
}}
>
  <span style={{ display: "inline-flex", alignItems: "center", gap: "6px" }}>
    <code
      style={{
    fontSize: "0.625rem",
    fontWeight: 500,
    padding: "1px 6px",
    borderRadius: "4px",
    backgroundColor: "rgba(34,197,94,0.1)",
    color: "#22c55e",
    border: "1px solid rgba(34,197,94,0.2)",
  }}
    >
      required
    </code>

    Must be provided
  </span>

  <span style={{ display: "inline-flex", alignItems: "center", gap: "6px" }}>
    <code
      style={{
    fontSize: "0.625rem",
    fontWeight: 500,
    padding: "1px 6px",
    borderRadius: "4px",
    backgroundColor: "rgba(168,85,247,0.1)",
    color: "#a855f7",
    border: "1px solid rgba(168,85,247,0.2)",
  }}
    >
      optional override
    </code>

    Provide to override auto-generated behavior
  </span>
</div>

## Key Files

| File                | Required | Description                                                                       |
| ------------------- | -------- | --------------------------------------------------------------------------------- |
| `aixyz.config.ts`   | Yes      | [Agent metadata, payment config, and skills](/config/aixyz-config)                |
| `app/agent.ts`      | No\*     | Agent definition using Vercel AI SDK                                              |
| `app/agents/*.ts`   | No       | Sub-agent definitions, each gets its own endpoint                                 |
| `app/tools/*.ts`    | No       | Tool implementations, auto-discovered by the build                                |
| `app/server.ts`     | No       | [Custom server](/api-reference/aixyz-server) — overrides auto-generation entirely |
| `app/accepts.ts`    | No       | [Custom x402 facilitator](/api-reference/accepts) for payment verification        |
| `app/session.ts`    | No       | [Custom session store](/api-reference/session-plugin) override for SessionPlugin  |
| `app/erc-8004.ts`   | No       | ERC-8004 identity registration and trust config                                   |
| `app/agent.test.ts` | No       | Agent tests using `bun:test`                                                      |
| `app/icon.svg`      | No       | Agent icon served as a static asset                                               |
| `vercel.json`       | No       | Vercel deployment configuration                                                   |

\* If omitted, the build skips A2A endpoints and only serves tools via MCP.

## How It Works

The build pipeline scans the `app/` directory to auto-generate a server:

1. Reads `aixyz.config.ts` for agent metadata and skills
2. Imports `app/agent.ts` for the main agent definition (if present)
3. Discovers all `.ts` files in `app/agents/` for sub-agents (each gets its own A2A endpoint)
4. Discovers all `.ts` files in `app/tools/` (excluding `_` prefixed files)
5. Registers `SessionPlugin` (with custom store from `app/session.ts` if present)
6. Wires up A2A (when agents exist), MCP (when tools exist), and x402 endpoints automatically

The result is a server exposing:

* **A2A** at `/agent` and `/.well-known/agent-card.json` (main agent)
* **A2A** at `/{name}/agent` and `/{name}/.well-known/agent-card.json` (per sub-agent in `app/agents/`)
* **MCP** at `/mcp`
* **x402** payment verification on gated endpoints

For full control, provide `app/server.ts` to override auto-generation. See the [Custom Server template](/templates/advanced/with-custom-server) for an example.
