` | Release connections/timers |
## MCP Integration
Sessions work automatically inside MCP tool handlers. The MCP plugin detects the session plugin and wraps tool execution with the payer context from x402 payment verification.
No additional configuration is needed.
## Constructor Options
```typescript theme={null}
new SessionPlugin(options?: SessionPluginOptions)
```
| Option | Type | Default | Description |
| ------- | -------------- | ---------------------- | ---------------------- |
| `store` | `SessionStore` | `InMemorySessionStore` | Custom storage backend |
Payment protocol that provides payer identity for sessions.
Working example with session-backed content storage.
# tools/[name].ts
Source: https://aixyz.sh/api-reference/tools
Auto-discovered tool definitions for A2A and MCP
Each `.ts` file in `app/tools/` is auto-discovered and registered as a tool 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
},
});
```
## Exports
| Export | Type | Required | Description |
| --------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `default` | `tool()` | Yes | The tool instance |
| `accepts` | `Accepts` | No | Payment config — gates the tool on MCP `/mcp`. Supports [array format](/getting-started/payments#multiple-payment-options) for multi-network |
## Conventions
* **Auto-discovered** — All `.ts` files in `app/tools/` are registered automatically
* **Ignored files** — Files starting with `_` (e.g., `_helpers.ts`) are skipped
* **Naming** — The tool is registered with the filename as its name (e.g., `weather.ts` → `weather`)
# IndexPagePlugin
Source: https://aixyz.sh/api-reference/unstable-with-index-page
Add a human-readable text page to your agent server
Adds a human-readable plain text page to your `AixyzApp` showing agent name, description, version, and skills.
```typescript theme={null}
import { IndexPagePlugin } from "aixyz/app/plugins/index-page";
await server.withPlugin(new IndexPagePlugin());
```
## Behavior
Registers a `GET` handler at the given path that responds with `text/plain` content:
```
My Agent
========
Description: A helpful travel agent
Version: 0.1.0
Skills:
1. Search Flights
ID: search-flights
Description: Search for flights between airports
Tags: travel, flights
Examples:
- Find flights from SFO to LAX
```
The output includes:
* Agent **name** and a separator line
* **Description** and **version** from config
* **Skills** list with IDs, descriptions, tags, and examples (if configured)
## Usage
The auto-generated server registers the `IndexPagePlugin` automatically at `"/"`. In a custom server, register it explicitly:
```typescript title="app/server.ts" theme={null}
import { AixyzApp } from "aixyz/app";
import { IndexPagePlugin } from "aixyz/app/plugins/index-page";
const server = new AixyzApp();
// Mount at root (default)
await server.withPlugin(new IndexPagePlugin());
await server.initialize();
export default server;
```
# aixyz.config.ts
Source: https://aixyz.sh/config/aixyz-config
Complete reference for the aixyz configuration file
Every aixyz project requires an `aixyz.config.ts` at the project root. This file defines your agent's identity, payment settings, and skills. It is validated at build time using Zod schemas.
## Full Example
```typescript title="aixyz.config.ts" theme={null}
import type { AixyzConfig } from "aixyz/config";
const config: AixyzConfig = {
name: "Weather Agent",
description: "Get current weather for any location worldwide.",
version: "0.1.0",
url: "https://my-agent.vercel.app",
x402: {
payTo: "0x...",
network: "eip155:8453",
},
skills: [
{
id: "get-weather",
name: "Get Weather",
description: "Get current weather conditions for any city",
tags: ["weather"],
examples: ["What's the weather in Tokyo?"],
},
],
};
export default config;
```
## Config Fields
| Field | Type | Required | Description |
| -------------- | -------------- | -------- | ---------------------------------------- |
| `name` | `string` | Yes | Agent display name |
| `description` | `string` | Yes | What the agent does |
| `version` | `string` | Yes | Semver version |
| `url` | `string` | No | Agent base URL (auto-detected on Vercel) |
| `x402` | `object` | Yes | Payment configuration |
| `x402.payTo` | `string` | Yes | EVM address to receive payments |
| `x402.network` | `string` | Yes | CAIP-2 chain ID (e.g., `eip155:8453`) |
| `skills` | `AgentSkill[]` | Yes | Skills exposed in the A2A agent card |
### `AgentSkill`
| Field | Type | Required | Description |
| ------------- | ---------- | -------- | ----------------------- |
| `id` | `string` | Yes | Unique skill identifier |
| `name` | `string` | Yes | Skill display name |
| `description` | `string` | Yes | What the skill does |
| `tags` | `string[]` | Yes | Categorization tags |
| `examples` | `string[]` | No | Example prompts |
| `inputModes` | `string[]` | No | Input MIME types |
| `outputModes` | `string[]` | No | Output MIME types |
## URL Resolution
If `url` is omitted, it is auto-detected in the following order:
1. `https://${VERCEL_PROJECT_PRODUCTION_URL}/` (when `VERCEL_ENV` is `"production"`)
2. `https://${VERCEL_URL}/` (Vercel preview/other environments)
3. `http://localhost:${PORT}/` (fallback, default port 3000)
## Payment Networks
| Network | CAIP-2 ID |
| ------------ | -------------- |
| Base | `eip155:8453` |
| Base Sepolia | `eip155:84532` |
| Ethereum | `eip155:1` |
Switch networks per environment:
```typescript title="aixyz.config.ts" theme={null}
x402: {
payTo: "0x...",
network: process.env.NODE_ENV === "production" ? "eip155:8453" : "eip155:84532",
},
```
## Build-Time vs Runtime
At build time, the `AixyzConfigPlugin` resolves the full config and materializes a runtime-safe subset into the bundle. This means:
* `import config from "aixyz/config"` works at runtime without the config file
* Environment variables referenced in the config are baked in at build time
* The config file itself is not included in the output bundle
# Environment Variables
Source: https://aixyz.sh/config/environment-variables
Configure your agent with .env files and environment variables
## `.env` Files
aixyz loads environment variables from `.env` files in the following order (later files take precedence):
1. `.env` — Shared defaults
2. `.env.local` — Local overrides (gitignored)
3. `.env.$(NODE_ENV)` — e.g., `.env.production`
4. `.env.$(NODE_ENV).local` — e.g., `.env.production.local`
This matches the loading order used by Next.js.
`.env.local` is always gitignored. Use it for API keys and secrets during development.
## Common Variables
| Variable | Description |
| ---------------------- | ------------------------------------------------------ |
| `OPENAI_API_KEY` | OpenAI API key |
| `X402_PAY_TO` | Default payment recipient address |
| `X402_NETWORK` | Default payment network |
| `X402_FACILITATOR_URL` | Custom x402 facilitator URL |
| `CDP_API_KEY_ID` | Coinbase CDP key ID (switches to Coinbase facilitator) |
| `CDP_API_KEY_SECRET` | Coinbase CDP key secret |
| `STRIPE_SECRET_KEY` | Experimental Stripe adapter |
| `STRIPE_PRICE_CENTS` | Stripe price in cents (default: 100) |
## Using in Config
Reference environment variables directly in `aixyz.config.ts`:
```typescript title="aixyz.config.ts" theme={null}
import type { AixyzConfig } from "aixyz/config";
const config: AixyzConfig = {
name: "My Agent",
description: "...",
version: "0.1.0",
x402: {
payTo: process.env.X402_PAY_TO!,
network: process.env.X402_NETWORK!,
},
skills: [],
};
export default config;
```
## Testing Environment
For tests, `loadEnv` from `aixyz/test` loads `.env.test.local` (where your `OPENAI_API_KEY` lives for tests). `.env.local` is ignored during testing:
```typescript theme={null}
import { loadEnv } from "aixyz/test";
describe("tests", () => {
loadEnv();
// process.env.OPENAI_API_KEY is now available
});
```
## Built-in Environment Variables
The CLI automatically sets these environment variables based on the command:
| Variable | `aixyz dev` | `aixyz build` | Description |
| ----------- | --------------- | -------------- | ---------------------------------------------------------------------------- |
| `NODE_ENV` | `"development"` | `"production"` | Standard Node.js environment indicator, for compatibility with node packages |
| `AIXYZ_ENV` | `"development"` | `"production"` | aixyz-specific environment indicator, mirrors `NODE_ENV` |
## Build-Time Resolution
Environment variables referenced in `aixyz.config.ts` are resolved and baked into the bundle at build time. The config file itself is not included in the output — only the resolved values.
# Agent and Tools
Source: https://aixyz.sh/getting-started/agent-and-tools
Define agents with Vercel AI SDK and create tools for A2A and MCP
## 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:
Files starting with `_` (e.g., `_helpers.ts`) are ignored by the build pipeline. Use this convention for shared
utilities.
## 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
Files starting with `_` (e.g., `_helpers.ts`) are ignored by the build pipeline. Use this convention for shared
utilities.
### 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.
# Deploying
Source: https://aixyz.sh/getting-started/deploying
Deploy your agent to Vercel, standalone Bun, or Docker
## Vercel
aixyz has first-class Vercel support using the [Build Output API v3](https://vercel.com/docs/build-output-api/v3).
```bash theme={null}
bun run build
vercel deploy
```
The build automatically detects the Vercel environment via `VERCEL=1` and outputs a Vercel serverless function using Bun Runtime. The agent URL is auto-detected from Vercel environment variables.
### `vercel.json`
Every aixyz project includes a `vercel.json`:
```json title="vercel.json" theme={null}
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"framework": null,
"buildCommand": "bun run build",
"bunVersion": "1.x"
}
```
No need to pass `--vercel` — the flag is automatically detected during Vercel builds.
Set `OPENAI_API_KEY` and any payment environment variables in the Vercel dashboard under **Settings → Environment Variables**.
## Standalone (Bun Runtime)
For direct deployment on Bun-compatible hosts (DigitalOcean, Railway, Fly.io, etc.):
```bash theme={null}
aixyz build
bun .aixyz/output/server.js
```
The build outputs a single bundled file at `.aixyz/output/server.js`.
### Environment Variables
Set these before running:
```bash theme={null}
export PORT=3000
export OPENAI_API_KEY=sk-...
bun .aixyz/output/server.js
```
## Docker
Using the standalone build:
```dockerfile title="Dockerfile" theme={null}
FROM oven/bun:1.3.9
WORKDIR /app
COPY . .
RUN bun install
RUN bun run build
CMD ["bun", ".aixyz/output/server.js"]
```
Build and run:
```bash theme={null}
docker build -t my-agent .
docker run -p 3000:3000 \
-e OPENAI_API_KEY=sk-... \
-e PORT=3000 \
my-agent
```
Never bake API keys into Docker images. Pass them as environment variables at runtime.
Set the `url` field in `aixyz.config.ts` manually for Docker deployments since Vercel environment variables won't be
available.
## Build Output Formats
| Format | Flag | Output Path | Description |
| ---------- | ------------------------------- | ------------------------- | --------------------------------------------------------------------------------- |
| Standalone | (default) | `.aixyz/output/server.js` | Single-file bundle, run with `bun` |
| Vercel | `VERCEL=1` or `--output vercel` | `.vercel/output/` | [Build Output API v3](https://vercel.com/docs/build-output-api/v3) for serverless |
| Executable | `--output executable` | `.aixyz/output/server` | Self-contained binary, no Bun runtime required |
## Verifying Your Deployment
After deploying, verify your agent is accessible:
```bash theme={null}
# Check the agent card
curl https://your-agent.example.com/.well-known/agent-card.json
# Test the A2A endpoint
curl -X POST https://your-agent.example.com/agent \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/send",
"id": "1",
"params": {
"id": "task-1",
"message": {
"role": "user",
"parts": [{"type": "text", "text": "Hello"}]
}
}
}'
```
# Installation
Source: https://aixyz.sh/getting-started/installation
Install Bun and create your first aixyz agent
## Prerequisites
* [Bun](https://bun.sh) 1.3.9 or later
* An API key for your preferred LLM provider (e.g., OpenAI)
Verify your Bun version:
```bash theme={null}
bun --version # Should be ≥ 1.3.9
```
## Create a New Project
Scaffold a new agent project with `create-aixyz-app`:
```bash theme={null}
bunx create-aixyz-app my-agent
cd my-agent
```
The CLI prompts for an agent name and optional OpenAI API key. Use `-y` to skip prompts:
```bash theme={null}
bunx create-aixyz-app my-agent -y
```
## Start the Dev Server
```bash theme={null}
bun run dev
```
This runs `aixyz dev`, which watches `app/` and `aixyz.config.ts` for changes and automatically restarts the server. Your agent is available at `http://localhost:3000`.
## Verify It Works
```bash theme={null}
# Check the agent card
curl http://localhost:3000/.well-known/agent-card.json
# Test the A2A endpoint
curl -X POST http://localhost:3000/agent \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tasks/send","id":"1","params":{"id":"task-1","message":{"role":"user","parts":[{"type":"text","text":"Hello"}]}}}'
```
The MCP endpoint is available at `http://localhost:3000/mcp` for MCP-compatible clients.
## Endpoints
Once running, your agent exposes:
| Endpoint | Protocol | Description |
| ------------------------------ | -------- | ---------------------------------------- |
| `/.well-known/agent-card.json` | A2A | Agent discovery card |
| `/agent` | A2A | JSON-RPC endpoint with x402 payment gate |
| `/mcp` | MCP | Tool sharing with MCP clients |
# Payments (x402)
Source: https://aixyz.sh/getting-started/payments
Add micropayments to your agent with x402
## Overview
[x402](https://www.x402.org/) is a payment protocol for HTTP resources. aixyz has built-in x402 support — when a client requests a payment-gated endpoint, the server responds with HTTP 402 and payment requirements. The client includes a payment proof in the `X-Payment` header, and the server verifies it via a facilitator.
## How It Works
1. Client requests a gated endpoint (e.g., `POST /agent`)
2. Server returns **HTTP 402** with payment requirements (price, network, payTo address)
3. Client constructs payment proof and retries with `X-Payment` header
4. Server verifies payment via a facilitator and grants access
## The `accepts` Export
Define payment requirements by exporting `accepts` from your agent or tool:
```typescript theme={null}
import type { Accepts } from "aixyz/accepts";
export const accepts: Accepts = {
scheme: "exact",
price: "$0.005",
};
```
This gates the endpoint behind a \$0.005 exact payment.
### With Overrides
```typescript theme={null}
export const accepts: Accepts = {
scheme: "exact",
price: "$0.005",
network: "eip155:8453", // override config network
payTo: "0x...", // override config payTo
};
```
Prices are USD strings: `"$0.005"`, `"$0.01"`, `"$0.0001"`.
## Per-Agent and Per-Tool Pricing
Agents and tools can each declare their own `accepts` export, enabling granular pricing:
```typescript theme={null}
// app/agent.ts — agent-level pricing (gates /agent)
export const accepts: Accepts = {
scheme: "exact",
price: "$0.01",
};
// app/tools/premium-search.ts — tool-level pricing (gates on /mcp)
export const accepts: Accepts = {
scheme: "exact",
price: "$0.001",
};
```
* Agent `accepts` gates the A2A `/agent` endpoint
* Tool `accepts` gates the tool on the MCP `/mcp` endpoint
* Agents and tools **without** `accepts` are not registered on protocol endpoints
## Multiple Payment Options
Accept payment across multiple networks by passing an array of payment entries. This lets clients pay on whichever network they prefer:
```typescript theme={null}
export const accepts: Accepts = [
{ scheme: "exact", price: "$0.005", network: "eip155:8453" }, // Base
{ scheme: "exact", price: "$0.005", network: "eip155:84532" }, // Base Sepolia
];
```
When using the array format, `network` is **required** on each entry so the server can register the correct payment scheme for each network.
All referenced networks are automatically discovered and registered during server initialization — no manual configuration needed beyond the `accepts` export.
This works for both agents and tools:
```typescript theme={null}
// app/agent.ts — multi-network agent pricing
export const accepts: Accepts = [
{ scheme: "exact", price: "$0.01", network: "eip155:8453" },
{ scheme: "exact", price: "$0.01", network: "eip155:1" },
];
// app/tools/premium-search.ts — multi-network tool pricing
export const accepts: Accepts = [
{ scheme: "exact", price: "$0.001", network: "eip155:8453" },
{ scheme: "exact", price: "$0.001", network: "eip155:1" },
];
```
## Payment Networks
Configure the payment network in `aixyz.config.ts`:
| Network | CAIP-2 ID |
| ------------ | -------------- |
| Base | `eip155:8453` |
| Base Sepolia | `eip155:84532` |
| Ethereum | `eip155:1` |
Switch networks per environment:
```typescript theme={null}
x402: {
payTo: "0x...",
network: process.env.NODE_ENV === "production" ? "eip155:8453" : "eip155:84532",
},
```
## Facilitators
Payment verification is handled by a facilitator service:
| Facilitator | Activation | URL |
| ------------ | ---------------------------- | ------------------------------------------ |
| Default | Always available | `https://x402.use-agently.com/facilitator` |
| Coinbase CDP | When `CDP_API_KEY_ID` is set | Auto-configured |
| Custom | Set `X402_FACILITATOR_URL` | Your custom URL |
### Custom Facilitator
Create `app/accepts.ts` to provide a custom facilitator:
```typescript title="app/accepts.ts" theme={null}
import { HTTPFacilitatorClient } from "aixyz/accepts";
export const facilitator = new HTTPFacilitatorClient({
url: process.env.X402_FACILITATOR_URL ?? "https://x402.use-agently.com/facilitator",
});
```
See the [BYO Facilitator template](/templates/advanced/with-custom-facilitator) for a working example.
## Payer Identity and Sessions
Every verified x402 payment identifies the payer by their wallet address. `SessionPlugin` (auto-registered by the build pipeline) gives each payer isolated key-value storage accessible via `getSession()`:
```typescript theme={null}
import { getSession } from "aixyz/app/plugins/session";
const session = getSession();
if (session) {
await session.set("preference", "dark-mode");
const payer = session.payer; // "0x1234..."
}
```
This works in both A2A agent handlers and MCP tool handlers. See [SessionPlugin](/api-reference/session-plugin) for the full API and custom store configuration.
# Project Structure
Source: https://aixyz.sh/getting-started/project-structure
Understand the app/ directory structure and file conventions
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
required
Must be provided
optional override
Provide to override auto-generated behavior
## 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.
# Testing
Source: https://aixyz.sh/getting-started/testing
Test your agent with bun:test and aixyz/test utilities
## Overview
aixyz provides a `loadEnv` utility from `aixyz/test` for loading environment variables during tests. Combined with Bun's built-in test runner, you can write both deterministic and non-deterministic agent tests.
## Test File Convention
Place your test file alongside your agent:
## Writing Tests
```typescript title="app/agent.test.ts" theme={null}
import { describe, expect, test } from "bun:test";
import { ToolLoopAgent } from "ai";
import { loadEnv } from "aixyz/test";
import agent, { accepts } from "./agent";
// Deterministic tests — no API calls, always run
test("default export is a ToolLoopAgent", () => {
expect(agent).toBeInstanceOf(ToolLoopAgent);
});
test("has convertTemperature tool registered", () => {
expect(agent.tools).toHaveProperty("convertTemperature");
});
test("accepts config uses exact scheme", () => {
expect(accepts.scheme).toBe("exact");
});
// Non-deterministic tests — require API key, skip gracefully in CI
describe("non deterministic agent test", () => {
loadEnv();
test.skipIf(!process.env.OPENAI_API_KEY)("agent can convert temperature", async () => {
const result = await agent.generate({
prompt: "convert 100 degrees celsius to fahrenheit",
});
expect(result.text).toContain("212");
});
});
```
## `loadEnv()`
The `loadEnv` function from `aixyz/test` loads environment variables for test runs. It loads `.env.test.local` (where your `OPENAI_API_KEY` lives for tests) while `.env.local` is ignored during testing.
```typescript theme={null}
import { loadEnv } from "aixyz/test";
describe("tests needing env vars", () => {
loadEnv();
// process.env is now populated
});
```
## Deterministic vs Non-Deterministic
| Type | Description | CI-Safe |
| ----------------- | -------------------------------------- | ------- |
| Deterministic | Validate agent type, tools, config | Yes |
| Non-deterministic | Call LLM and validate response content | No\* |
\* Use `test.skipIf(!process.env.OPENAI_API_KEY)` to skip gracefully when no API key is available.
## Fully Offline Tests with `fake()`
Use `fake()` from `aixyz/model` to replace the real LLM with a deterministic transform. It returns a `LanguageModelV3` that works with `ToolLoopAgent` — no API key, no network calls:
```typescript title="app/agent.ts" theme={null}
import { fake } from "aixyz/model";
import { ToolLoopAgent } from "ai";
export const model = fake((lastMessage) => `You said: ${lastMessage}`);
export default new ToolLoopAgent({ model, instructions: "..." });
```
Then test the model's output directly:
```typescript title="app/agent.test.ts" theme={null}
import type { Prompt } from "aixyz/model";
import { model } from "./agent";
test("echoes back the user message", async () => {
const prompt: Prompt = [{ role: "user", content: [{ type: "text", text: "hello" }] }];
const result = await model.doGenerate({ prompt });
expect(result.content).toEqual([{ type: "text", text: "You said: hello" }]);
});
```
See the [`aixyz/model` API reference](/api-reference/fake) for full details and the [Fake Model Agent template](/templates/advanced/fake-llm) for a complete working example.
## Running Tests
```bash theme={null}
# Run all tests
bun test
# Run a specific test file
bun test app/agent.test.ts
```
# Why Bun?
Source: https://aixyz.sh/getting-started/why-bun
Why aixyz uses Bun as its runtime
## Software is Being Rewritten — by Agents
We are entering a world where humans won't be writing most software. AI agents will. And agents will be creating other agents. When Claude Code scaffolds a project, installs dependencies, writes tests, and iterates on failures — all in a single session — the bottleneck isn't the model. It's the runtime.
In December 2025, Anthropic made their first-ever acquisition: [they bought Bun](https://www.anthropic.com/news/anthropic-acquires-bun-as-claude-code-reaches-usd1b-milestone). Not a model company. Not a data company. A JavaScript runtime. That tells you everything about where this is going.
We're making the same bet.
### The Runtime is the Agent's Operating System
Every tool an AI agent calls is a runtime operation — HTTP requests, file I/O, code execution, JSON parsing. An agent making dozens of tool calls per session doesn't experience latency the way a human does. For a human, 500ms is imperceptible. For an agent in a tight loop, [it's the difference between fast iteration and a stall](https://dev.to/inboryn_99399f96579fcd705/anthropic-acquires-bun-why-ai-agents-need-a-lightweight-runtime-228d).
Node.js was designed for a world where humans start a server and it runs for hours. Bun was designed for a world where processes spin up, execute, and terminate in milliseconds. That's the world agents live in — generate code, run it, check the output, fix the errors, repeat. The runtime needs to keep up.
Anthropic understood this before anyone else. Claude Code [ships as a Bun executable](https://betterstack.com/community/guides/scaling-nodejs/anthropic-acquires-bun/). So does [FactoryAI. So does OpenCode](https://betterstack.com/community/guides/scaling-nodejs/anthropic-acquires-bun/). The pattern is clear: [the runtime is becoming the agent's operating system](https://jimmysong.io/blog/bun-anthropic-runtime-shift/).
### One Binary, Zero Configuration
The Node.js ecosystem asks you to choose: which package manager, which bundler, which test runner, which TypeScript compiler. Five tools, five configurations, five places where things break.
Bun is one binary that does all of it:
```bash theme={null}
bun install # Package manager
bun run dev # Runtime
bun run build # Bundler
bun test # Test runner
```
This isn't just developer convenience. [For an AI agent that needs to spin up projects, test, bundle, run, and iterate at speed, that fragmentation is friction](https://dev.to/axrisi/anthropic-just-acquired-bun-and-it-signals-the-beginning-of-ai-native-software-engineering-3cd9). Every tool choice is a decision point. Every configuration file is a place where an automated workflow can break. Bun eliminates the entire category of problem.
Jarred Sumner, Bun's creator, said it directly: [Bun's job is to be the best place to build, run, and test AI-driven software](https://bun.com/blog/bun-joins-anthropic).
### TypeScript Without the Ceremony
Bun runs TypeScript natively. No compilation step. No `tsconfig.json`. No build pipeline between writing code and running it.
aixyz packages ship raw `.ts` files and Bun handles them directly. When an agent generates TypeScript, it can execute it immediately — the generate-run-verify loop has zero gap. This is the kind of simplicity that [makes agent-driven development practical at scale](https://dev.to/axrisi/anthropic-just-acquired-bun-and-it-signals-the-beginning-of-ai-native-software-engineering-3cd9).
### Compile Once, Run Anywhere
Bun compiles projects into [single-file executables](https://bun.com/docs/bundler/executables). No runtime installation required. No dependency management on the target machine. This is how Claude Code distributes to millions of developers — one binary, every platform.
For aixyz agents deploying to serverless platforms like Vercel, Bun's cold starts are fast enough that your agent responds quickly even on first invocation:
```json title="vercel.json" theme={null}
{
"buildCommand": "bun run build",
"bunVersion": "1.x"
}
```
### Built-in Testing
Bun includes a Jest-compatible test runner with near-instant startup. aixyz uses `bun:test` for agent testing:
```typescript title="app/agent.test.ts" theme={null}
import { describe, expect, test } from "bun:test";
import { ToolLoopAgent } from "ai";
import agent from "./agent";
test("default export is a ToolLoopAgent", () => {
expect(agent).toBeInstanceOf(ToolLoopAgent);
});
```
# Introduction
Source: https://aixyz.sh/index
Payment-native SDK for AI Agents. Build an agent, get paid today.
**aixyz** is the payment-native SDK for AI agents. Build agents and tools with your favorite framework, and get paid instantly through web3 payment rails — no payment integration work required.
> Build an agent and get paid today.
Every agent you deploy with aixyz comes with [x402 payments](/protocols/x402) embedded. When another agent or user calls yours, settlement happens on-chain automatically. You set a price, deploy, and start earning.
## How It Works
1. **Build your agent** — Use [Vercel AI SDK](https://ai-sdk.dev) (`ai@^6`) or any framework to create agents with tools
2. **Set your price** — Export an `accepts` object to gate endpoints with instant micropayments
3. **Deploy and earn** — Run `aixyz build` and deploy. Payments settle on-chain via [x402](https://www.x402.org/)
```typescript title="app/agent.ts" theme={null}
import type { Accepts } from "aixyz/accepts";
// set your price and start earning
export const accepts: Accepts = {
scheme: "exact",
price: "$0.01",
};
```
aixyz auto-generates a server that exposes:
| Endpoint | Protocol | Description |
| ------------------------------ | -------- | ---------------------------------------- |
| `/.well-known/agent-card.json` | A2A | Agent discovery card |
| `/agent` | A2A | JSON-RPC endpoint with x402 payment gate |
| `/mcp` | MCP | Tool sharing with MCP clients |
## Quick Start
```bash theme={null}
bunx create-aixyz-app my-agent
cd my-agent
bun run dev
```
Your agent is running at `http://localhost:3000` with payments, [A2A](/protocols/a2a), and [MCP](/protocols/mcp) out of the box.
Create and run your first agent in under a minute.
Set prices for your agent and tools with x402.
Define agents and tools with Vercel AI SDK.
Working agent templates to start from.
Deploy to Vercel, Docker, or standalone.
# aixyz
Source: https://aixyz.sh/packages/aixyz
CLI for building and running aixyz agents
The `aixyz` package includes the CLI for developing and building agents. Install it to get the `aixyz` command.
```bash theme={null}
bun add aixyz
```
## Commands
### `aixyz dev`
Starts a local development server with hot reload. Watches `app/` and `aixyz.config.ts` for changes and restarts automatically (100ms debounce).
```bash theme={null}
aixyz dev
aixyz dev --port 4000
```
| Flag | Default | Description |
| ------------ | ------- | ----------------- |
| `-p, --port` | `3000` | Port to listen on |
On startup, the dev server prints the A2A and MCP endpoints:
```
⟡ aixyz.sh v0.0.0
- A2A: http://localhost:3000/.well-known/agent-card.json
- MCP: http://localhost:3000/mcp
- Environments: .env, .env.local
```
If no `app/server.ts` exists, one is auto-generated from `app/agent.ts` and `app/tools/*.ts`.
### `aixyz build`
Bundles your agent for deployment.
```bash theme={null}
aixyz build # Standalone (default)
aixyz build --output standalone # Explicit standalone
aixyz build --output vercel # Vercel Build Output API v3
aixyz build --output executable # Self-contained binary
```
| Flag | Default | Description |
| ---------- | ------------ | ------------------------------------------------------ |
| `--output` | `standalone` | Output format: `standalone`, `vercel`, or `executable` |
**Standalone** output goes to `.aixyz/output/server.js` — run it with `bun .aixyz/output/server.js`.
**Vercel** output goes to `.vercel/output/` using Build Output API v3. Automatically detected when `VERCEL=1` is set or configured in `aixyz.config.ts`.
The build process:
1. Loads environment variables from `.env` files
2. Reads and validates `aixyz.config.ts`
3. Detects or auto-generates the server entrypoint
4. Bundles with `Bun.build()`, [materializing the config](/api-reference/aixyz-config) into the bundle
5. Copies static assets from `public/` and `app/icon.png`
### `aixyz erc-8004 register`
Registers a new agent on the ERC-8004 IdentityRegistry. Creates `app/erc-8004.ts` if it doesn't exist (prompting for supported trust mechanisms), asks for your agent's deployment URL, and derives the on-chain URI as `/_aixyz/erc-8004.json`. After a successful registration, the entry is written back to `app/erc-8004.ts`.
```bash theme={null}
aixyz erc-8004 register --url "https://my-agent.example.com" --chain sepolia --broadcast
```
| Flag | Description |
| ------------- | ------------------------------------------------- |
| `--url` | Agent deployment URL (prompted if omitted) |
| `--chain` | `mainnet`, `sepolia`, `base-sepolia`, `localhost` |
| `--rpc-url` | Custom RPC endpoint |
| `--keystore` | Encrypted keystore file |
| `--browser` | Use browser wallet (EIP-6963) |
| `--broadcast` | Send transaction (default is dry-run) |
| `--out-dir` | Write result JSON to directory |
### `aixyz erc-8004 update`
Updates the metadata URI of a registered agent. Reads existing registrations from `app/erc-8004.ts`, lets you select which one to update (if multiple), and derives the new URI from the provided URL.
```bash theme={null}
aixyz erc-8004 update --url "https://new-domain.example.com" --broadcast
```
| Flag | Description |
| ------------- | ---------------------------------------------- |
| `--url` | New agent deployment URL (prompted if omitted) |
| `--rpc-url` | Custom RPC endpoint |
| `--keystore` | Encrypted keystore file |
| `--browser` | Use browser wallet (EIP-6963) |
| `--broadcast` | Send transaction (default is dry-run) |
| `--out-dir` | Write result JSON to directory |
See [ERC-8004 Identity](/protocols/erc-8004) for the full protocol documentation.
# create-aixyz-app
Source: https://aixyz.sh/packages/create-aixyz-app
Scaffold new aixyz agent projects
## Overview
`create-aixyz-app` is a project scaffolding CLI that generates a new aixyz agent project with the standard directory structure, configuration, and example files.
## Usage
```bash theme={null}
bunx create-aixyz-app my-agent
```
The CLI prompts for:
* **Agent name** — Used in `aixyz.config.ts` and `package.json`
* **OpenAI API key** — Saved to `.env.local` (optional)
### Non-Interactive Mode
Skip all prompts with the `-y` flag:
```bash theme={null}
bunx create-aixyz-app my-agent -y
```
## What Gets Scaffolded
### Scripts
The generated `package.json` includes:
```json title="package.json" theme={null}
{
"scripts": {
"dev": "aixyz dev",
"build": "aixyz build"
}
}
```
## After Scaffolding
```bash theme={null}
cd my-agent
bun install
bun run dev
```
Your agent starts at `http://localhost:3000` with A2A, MCP, and x402 support ready to go.
## Next Steps
Full walkthrough of setting up your agent.
Understand the generated project layout.
# A2A Protocol
Source: https://aixyz.sh/protocols/a2a
Agent-to-Agent protocol for agent discovery and communication
## Overview
The [A2A (Agent-to-Agent)](https://google.github.io/A2A/) protocol enables standardized agent discovery and inter-agent communication. aixyz implements A2A protocol version **0.3.0** with an agent card for discovery and a JSON-RPC endpoint for task execution.
## Endpoints
| Endpoint | Method | Description |
| ------------------------------ | ------ | ------------------------------------------------------------ |
| `/.well-known/agent-card.json` | GET | Agent discovery card with metadata, skills, and capabilities |
| `/agent` | POST | JSON-RPC task handler, x402-gated if `accepts` is defined |
## Agent Card
The agent card is automatically generated from your `aixyz.config.ts`. Other agents fetch this card to discover your agent's capabilities.
```bash theme={null}
curl http://localhost:3000/.well-known/agent-card.json
```
Example response:
```json theme={null}
{
"name": "Weather Agent",
"description": "Get current weather for any location worldwide.",
"protocolVersion": "0.3.0",
"version": "0.1.0",
"url": "https://my-agent.vercel.app/agent",
"capabilities": {
"streaming": true,
"pushNotifications": false
},
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["text/plain"],
"skills": [
{
"id": "get-weather",
"name": "Get Weather",
"description": "Get current weather conditions for any city",
"tags": ["weather"],
"examples": ["What's the weather in Tokyo?"]
}
]
}
```
## JSON-RPC Endpoint
The `/agent` endpoint accepts JSON-RPC 2.0 requests. The primary method is `tasks/send`:
```bash theme={null}
curl -X POST http://localhost:3000/agent \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "tasks/send",
"id": "1",
"params": {
"id": "task-1",
"message": {
"role": "user",
"parts": [{"type": "text", "text": "What is the weather in NYC?"}]
}
}
}'
```
## Capabilities
The agent card's `capabilities` field is configured via the `capabilities` export from your agent file. If omitted, defaults to `{ streaming: true, pushNotifications: false }`.
```typescript title="app/agent.ts" theme={null}
import type { Capabilities } from "aixyz/app/plugins/a2a";
export const capabilities: Capabilities = {
streaming: false,
pushNotifications: false,
};
```
When `streaming` is `false`, the executor uses `agent.generate()` instead of `agent.stream()`, returning a single artifact with the full response. See [agent.ts — Capabilities](/api-reference/agent#capabilities) for the full reference.
## Using the A2A Plugin
The `A2APlugin` wires up both endpoints on your server:
```typescript theme={null}
import { A2APlugin } from "aixyz/app/plugins/a2a";
// agentExports is `import * as agent from "./agent"`
// reads `default` (the ToolLoopAgent), `accepts` (payment config), and `capabilities`
await server.withPlugin(new A2APlugin([{ exports: agent }]));
```
This registers:
* A **GET** handler at `/.well-known/agent-card.json` serving the agent card
* A **POST** handler at `/agent` routing JSON-RPC requests to `ToolLoopAgentExecutor`
### Sub-Agent Routing
Pass multiple entries with a `name` to mount sub-agents under their own paths:
```typescript title="app/server.ts" theme={null}
import * as research from "./agents/research";
import * as implement from "./agents/implement";
await server.withPlugin(
new A2APlugin([
{ exports: agent }, // → /agent
{ name: "research", exports: research }, // → /research/agent
{ name: "implement", exports: implement }, // → /implement/agent
]),
);
```
Each sub-agent gets its own agent card at `/{name}/.well-known/agent-card.json` and its own JSON-RPC endpoint at `/{name}/agent`. When using `app/agents/`, the build pipeline generates these calls automatically.
## Payment Integration
When an agent exports an `accepts` configuration with `scheme: "exact"`, the `/agent` endpoint is gated behind [x402 payment](/protocols/x402). Clients must include a valid `X-Payment` header. Agents without `accepts` are served without payment requirements.
How payment gating works on A2A endpoints.
Expose tools via MCP alongside A2A.
# ERC-8004 Identity
Source: https://aixyz.sh/protocols/erc-8004
On-chain agent identity using the ERC-8004 standard
## Overview
ERC-8004 defines an on-chain identity standard for AI agents. It allows agents to register their identity on a blockchain, providing verifiable, decentralized identity for agent-to-agent and agent-to-user interactions.
aixyz supports ERC-8004 through the `@aixyz/erc-8004` package (contract ABIs, deployed addresses, and Zod schemas) and built-in CLI commands for registry operations.
## Supported Networks
**Mainnets:** Ethereum, Base, Polygon, Scroll, Monad, BSC, Gnosis
**Testnets:** Sepolia, Base Sepolia, Polygon Amoy, Scroll Sepolia, Monad Testnet, BSC Testnet
## The `app/erc-8004.ts` File
Every agent that uses ERC-8004 identity declares an `app/erc-8004.ts` file. This file defines the agent's supported trust mechanisms and tracks on-chain registrations:
```typescript title="app/erc-8004.ts" theme={null}
import type { ERC8004Registration } from "aixyz/erc-8004";
const metadata: ERC8004Registration = {
registrations: [],
supportedTrust: ["reputation"],
};
export default metadata;
```
When this file exists, the build pipeline automatically exposes two endpoints:
* `GET /_aixyz/erc-8004.json`
These serve the full agent registration file, merging defaults from `aixyz.config.ts` (name, description, image, services).
## Register an Agent
Use `aixyz erc-8004 register` to register your agent on the IdentityRegistry:
```bash theme={null}
aixyz erc-8004 register --chain sepolia --broadcast
```
The command walks you through an interactive flow:
1. **Creates `app/erc-8004.ts`** if it doesn't exist — prompts you to select supported trust mechanisms
2. **Asks for your agent's deployment URL** (e.g., `https://my-agent.vercel.app`)
3. **Derives the on-chain URI** as `/_aixyz/erc-8004.json` and asks you to confirm
4. **Selects chain and wallet**, then signs and broadcasts the transaction
5. **Writes the registration** back into the `registrations` array in `app/erc-8004.ts`
After registration, your `app/erc-8004.ts` will contain the on-chain reference:
```typescript title="app/erc-8004.ts" theme={null}
const metadata: ERC8004Registration = {
registrations: [{ agentId: 42, agentRegistry: "eip155:84532:0x8004A818BFB912233c491871b3d84c89A494BD9e" }],
supportedTrust: ["reputation"],
};
```
| Flag | Description |
| ------------- | ------------------------------------------------- |
| `--url` | Agent deployment URL (prompted if omitted) |
| `--chain` | `mainnet`, `sepolia`, `base-sepolia`, `localhost` |
| `--rpc-url` | Custom RPC endpoint |
| `--keystore` | Encrypted keystore file |
| `--browser` | Use browser wallet (EIP-6963) |
| `--broadcast` | Send transaction (default is dry-run) |
## Update Agent URI
After redeploying to a new URL, update the on-chain URI with `aixyz erc-8004 update`:
```bash theme={null}
aixyz erc-8004 update --url "https://new-domain.example.com" --broadcast
```
The command reads existing registrations from `app/erc-8004.ts`:
* If there's one registration, it confirms and proceeds
* If there are multiple, it prompts you to select which one to update
The chain and registry address are derived automatically from the selected registration's `agentRegistry` field.
| Flag | Description |
| ------------- | ---------------------------------------------- |
| `--url` | New agent deployment URL (prompted if omitted) |
| `--rpc-url` | Custom RPC endpoint |
| `--keystore` | Encrypted keystore file |
| `--browser` | Use browser wallet (EIP-6963) |
| `--broadcast` | Send transaction (default is dry-run) |
## Programmatic Usage
Use the `@aixyz/erc-8004` package to interact with the registry from code:
```typescript theme={null}
import { IdentityRegistryAbi, getIdentityRegistryAddress } from "@aixyz/erc-8004";
```
The package provides:
* **Contract ABIs** — TypeScript-typed ABIs for use with viem, ethers, or wagmi
* **Deployed addresses** — Known contract addresses across supported networks
* **Zod schemas** — Validation schemas for registration data and agent URIs
## Integration with aixyz
ERC-8004 identity complements the other protocols:
* **A2A** — The agent card can reference an on-chain identity for verification
* **x402** — Payment gating tied to verified on-chain agent identities
* **MCP** — Tool discovery backed by verifiable identity
Agent discovery with A2A agent cards.
Payment gating for agent endpoints.
# MCP Protocol
Source: https://aixyz.sh/protocols/mcp
Model Context Protocol support for sharing tools with MCP clients
## Overview
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) allows AI clients to discover and invoke tools exposed by your agent. aixyz implements MCP via the `MCPPlugin`, serving tools at `/mcp` over a stateless `WebStandardStreamableHTTPServerTransport`.
## Endpoints
| Endpoint | Method | Description |
| -------- | ------ | ------------------------------------------------------------------ |
| `/mcp` | POST | JSON-RPC requests using `WebStandardStreamableHTTPServerTransport` |
| `/mcp` | GET | SSE stream for server-initiated messages |
| `/mcp` | DELETE | Session termination (stateless, returns 200) |
## Compatible Clients
Any MCP-compatible client can connect to your agent's `/mcp` endpoint:
* **Claude Desktop**
* **VS Code** (GitHub Copilot)
* **Cursor**
* Other MCP-enabled tools
Point the client at your agent's MCP URL:
```
http://localhost:3000/mcp
```
## Automatic Registration
When using the standard `app/` directory structure, all tools in `app/tools/*.ts` are automatically registered as MCP tools during the build. No additional configuration is needed.
Files starting with `_` (e.g., `app/tools/_helpers.ts`) are excluded from tool registration.
## Manual Setup
If you're using a custom server, wire up MCP manually with the `MCPPlugin`:
```typescript title="app/server.ts" theme={null}
import { MCPPlugin } from "aixyz/app/plugins/mcp";
import * as lookup from "./tools/lookup";
// Register tools and mount the /mcp endpoint
await server.withPlugin(
new MCPPlugin([
{
name: "lookup",
exports: {
default: lookup.default,
accepts: { scheme: "exact", price: "$0.001" },
},
},
]),
);
```
## Session Integration
Paid MCP tools automatically get session context. When a tool is invoked with x402 payment, `getSession()` returns the payer-scoped session inside the tool handler — no additional configuration needed.
```typescript theme={null}
import { getSession } from "aixyz/app/plugins/session";
// Inside an MCP tool handler:
const session = getSession();
await session?.set("key", "value"); // stored per-payer
```
See [SessionPlugin](/api-reference/session-plugin) for the full API.
## Payment-Gated Tools
Tools with `accepts.scheme === "exact"` require [x402 payment](/protocols/x402) via `@x402/mcp`. The payment wrapper is applied automatically when you provide an `accepts` configuration during registration.
```typescript theme={null}
// This tool requires $0.001 per invocation
await server.withPlugin(
new MCPPlugin([
{
name: "premiumSearch",
exports: {
default: searchTool,
accepts: { scheme: "exact", price: "$0.001" },
},
},
]),
);
```
How tools are defined in the app/ directory.
Payer-scoped storage for MCP tools.
Agent discovery and communication via A2A.
# x402 Payments
Source: https://aixyz.sh/protocols/x402
HTTP 402 micropayments for agent endpoints
## Overview
[x402](https://www.x402.org/) is a payment protocol for HTTP resources. When a client requests a payment-gated endpoint, the server responds with HTTP 402 and payment requirements. The client includes a payment proof in the `X-Payment` header, and the server verifies it via a facilitator before granting access.
In aixyz, `AixyzApp` uses a `PaymentGateway` to provide built-in payment gating for agent and tool endpoints.
## How It Works
1. Client requests a gated endpoint (e.g., `POST /agent`)
2. Server returns **HTTP 402** with payment requirements (price, network, payTo address)
3. Client constructs payment proof and retries with `X-Payment` header
4. Server verifies payment via a facilitator and grants access
## The `accepts` Export
Define payment requirements by exporting `accepts` from your agent or tool:
```typescript theme={null}
import type { Accepts } from "aixyz/accepts";
export const accepts: Accepts = {
scheme: "exact",
price: "$0.005",
};
```
This gates the endpoint behind a \$0.005 exact payment.
### With Overrides
```typescript theme={null}
export const accepts: Accepts = {
scheme: "exact",
price: "$0.005",
network: "eip155:8453", // override config network
payTo: "0x...", // override config payTo
};
```
Prices are USD strings: `"$0.005"`, `"$0.01"`, `"$0.0001"`.
## Per-Agent and Per-Tool Pricing
Agents and tools can each declare their own `accepts` export, enabling granular pricing:
```typescript theme={null}
// app/agent.ts — agent-level pricing
export const accepts: Accepts = {
scheme: "exact",
price: "$0.01",
};
// app/tools/premium-search.ts — tool-level pricing
export const accepts: Accepts = {
scheme: "exact",
price: "$0.001",
};
```
* Agent `accepts` gates the A2A `/agent` endpoint
* Tool `accepts` gates the tool on the MCP `/mcp` endpoint
* Agents and tools **without** `accepts` are not registered on protocol endpoints
## Facilitators
Payment verification is handled by a facilitator service:
| Facilitator | Activation | URL |
| ------------ | ---------------------------- | ------------------------------------------ |
| Default | Always available | `https://x402.use-agently.com/facilitator` |
| Coinbase CDP | When `CDP_API_KEY_ID` is set | Auto-configured |
| Custom | Set `X402_FACILITATOR_URL` | Your custom URL |
### Custom Facilitator
Create `app/accepts.ts` to provide a custom facilitator:
```typescript title="app/accepts.ts" theme={null}
import { HTTPFacilitatorClient } from "aixyz/accepts";
export const facilitator = new HTTPFacilitatorClient({
url: process.env.X402_FACILITATOR_URL ?? "https://x402.use-agently.com/facilitator",
});
```
## Server Integration
In a custom server, register payment-gated routes via `route()`:
```typescript theme={null}
server.route("POST", "/agent", handler, {
payment: { scheme: "exact", price: "$0.005" },
});
```
How to configure payments in your agent.
On-chain agent identity for verified payments.
# Fake Model Agent
Source: https://aixyz.sh/templates/advanced/fake-llm
Fully deterministic agent testing using the aixyz fake model — no API key required
**Source:** [`examples/fake-llm`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/fake-llm)
## Overview
This template demonstrates how to use `fake()` from `aixyz/model` to build and test an agent with **zero API calls and no API key**. The fake model's transform function deterministically maps each user message to a response, making every test fast, repeatable, and CI-safe.
The example agent checks whether the user's input is a palindrome, or reverses it if not — using both the `lastMessage` and the full `prompt` history from the transform signature.
## Project Structure
```
fake-llm/
├── aixyz.config.ts # Agent metadata
├── app/
│ ├── agent.ts # Agent using fake() model
│ └── agent.test.ts # Fully deterministic test suite
├── package.json
└── vercel.json
```
## The Fake Model
Import `fake` from `aixyz/model` and pass a transform function:
```typescript title="app/agent.ts" theme={null}
import { fake } from "aixyz/model";
import { ToolLoopAgent } from "ai";
import type { Accepts } from "aixyz/accepts";
import type { Capabilities } from "aixyz/app/plugins/a2a";
export const model = fake((lastMessage, prompt) => {
const reversed = [...lastMessage].reverse().join("");
const isPalindrome = lastMessage.toLowerCase() === reversed.toLowerCase();
const turn = prompt.filter((m) => m.role === "user").length;
if (isPalindrome) {
return `"${lastMessage}" is a palindrome! (turn ${turn})`;
}
return `"${lastMessage}" reversed is "${reversed}" (turn ${turn})`;
});
export const accepts: Accepts = {
scheme: "free",
};
export const capabilities: Capabilities = {
streaming: false,
pushNotifications: false,
};
export default new ToolLoopAgent({
model,
instructions: "You analyze text for palindromes and reverse it when asked.",
});
```
The transform receives:
* `lastMessage` — the text content of the last user turn
* `prompt` — the full conversation history, useful for tracking turn count or extracting prior context
## Test Pattern
Because the fake model is deterministic, every test runs without an API key:
```typescript title="app/agent.test.ts" theme={null}
import { describe, expect, test } from "bun:test";
import type { Prompt } from "aixyz/model";
import { model } from "./agent";
describe("palindrome checker (fake model)", () => {
test("detects a palindrome", async () => {
const prompt: Prompt = [{ role: "user", content: [{ type: "text", text: "racecar" }] }];
const result = await model.doGenerate({ prompt });
expect(result.content[0]).toEqual(
expect.objectContaining({ type: "text", text: expect.stringContaining("is a palindrome") }),
);
});
test("tracks turn number from prompt context", async () => {
const prompt: Prompt = [
{ role: "user", content: [{ type: "text", text: "hi" }] },
{ role: "assistant", content: [{ type: "text", text: "..." }] },
{ role: "user", content: [{ type: "text", text: "level" }] },
];
const result = await model.doGenerate({ prompt });
expect(result.content[0]).toEqual(
expect.objectContaining({ type: "text", text: expect.stringContaining("turn 2") }),
);
});
});
```
## Key Features
* **No API key** — `fake()` never makes network requests
* **Fully deterministic** — same input always produces the same output
* **CI-safe** — all tests run in any environment
* **Full prompt context** — the transform's second argument exposes the entire conversation history
## Running Tests
```bash theme={null}
cd examples/fake-llm
bun install
bun test
```
## Running the Agent
```bash theme={null}
cd examples/fake-llm
bun run dev
```
Endpoints available at `http://localhost:3000`:
| Endpoint | Protocol | Description |
| ------------------------------ | -------- | --------------------- |
| `/.well-known/agent-card.json` | A2A | Agent discovery card |
| `/agent` | A2A | JSON-RPC task handler |
# Agent with Sub-Agents
Source: https://aixyz.sh/templates/advanced/sub-agents
Template demonstrating multiple A2A endpoints from a single deployment using app/agents/[name].ts
**Source:** [`examples/sub-agents`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/sub-agents)
## Overview
This template shows how to deploy **multiple specialist agents** from a single service using the `app/agents/` directory. Each file in `app/agents/` becomes an independent A2A endpoint alongside the main `app/agent.ts` coordinator.
The example has three agents and two tools:
* **Coordinator** (`app/agent.ts`) — routes users to the right specialist
* **Math specialist** (`app/agents/math.ts`) — performs arithmetic using the `calculate` tool
* **Text specialist** (`app/agents/text.ts`) — analyzes text using the `word-count` tool
## Project Structure
```
sub-agents/
├── aixyz.config.ts # Agent metadata and skills
├── app/
│ ├── agent.ts # Coordinator agent → /agent
│ ├── agents/
│ │ ├── math.ts # Math sub-agent → /math/agent
│ │ └── text.ts # Text sub-agent → /text/agent
│ ├── tools/
│ │ ├── calculate.ts # Arithmetic tool (add/subtract/multiply/divide)
│ │ └── word-count.ts # Text analysis tool (words/characters/sentences)
│ └── icon.png
├── package.json
└── vercel.json
```
## Endpoints
A single deployment exposes three independent A2A endpoints:
| Endpoint | Agent | Description |
| ----------------------------------- | ----------- | --------------------------------- |
| `/agent` | Coordinator | Routes users to specialists |
| `/math/agent` | Math | Arithmetic calculations |
| `/text/agent` | Text | Word / character / sentence count |
| `/.well-known/agent-card.json` | Coordinator | A2A discovery card |
| `/math/.well-known/agent-card.json` | Math | A2A discovery card |
| `/text/.well-known/agent-card.json` | Text | A2A discovery card |
| `/mcp` | — | MCP tool endpoint |
## Sub-Agent Definition
Each file in `app/agents/` follows the same pattern as `app/agent.ts`:
```typescript title="app/agents/math.ts" theme={null}
import { openai } from "@ai-sdk/openai";
import { stepCountIs, ToolLoopAgent } from "ai";
import type { Accepts } from "aixyz/accepts";
import calculate from "../tools/calculate";
export const accepts: Accepts = { scheme: "exact", price: "$0.001" };
export default new ToolLoopAgent({
model: openai("gpt-4o-mini"),
instructions: "You are a math specialist...",
tools: { calculate },
stopWhen: stepCountIs(5),
});
```
The filename (`math.ts`) determines the URL prefix (`/math/agent`).
## Key Features
* **Auto-discovery** — All `.ts` files in `app/agents/` are automatically registered
* **Independent endpoints** — Each sub-agent gets its own A2A endpoint and agent card
* **Shared tools via MCP** — Tools in `app/tools/` are exposed on a single `/mcp` endpoint
* **Mixed deployment** — `app/agent.ts` and `app/agents/` can coexist in the same project
## Running
```bash theme={null}
cd examples/sub-agents
bun install
bun run dev
```
Agents available at `http://localhost:3000`:
| Endpoint | Protocol | Description |
| ----------------------------------- | -------- | ------------------------ |
| `/.well-known/agent-card.json` | A2A | Coordinator discovery |
| `/agent` | A2A | Coordinator JSON-RPC |
| `/math/.well-known/agent-card.json` | A2A | Math sub-agent discovery |
| `/math/agent` | A2A | Math sub-agent JSON-RPC |
| `/text/.well-known/agent-card.json` | A2A | Text sub-agent discovery |
| `/text/agent` | A2A | Text sub-agent JSON-RPC |
| `/mcp` | MCP | Shared tool endpoint |
# BYO Facilitator Agent
Source: https://aixyz.sh/templates/advanced/with-custom-facilitator
Template demonstrating a custom x402 payment facilitator
**Source:**
[`examples/with-custom-facilitator`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/with-custom-facilitator)
## Overview
This template demonstrates how to bring your own x402 payment facilitator by providing an `app/accepts.ts` file. Instead of using the default facilitator at `x402.org`, you can point to a custom facilitator URL for payment verification.
## Project Structure
```
with-custom-facilitator/
├── aixyz.config.ts # Agent metadata
├── app/
│ ├── agent.ts # Agent with temperature tool
│ ├── accepts.ts # Custom facilitator configuration
│ ├── tools/
│ │ └── temperature.ts # Temperature conversion tool
│ └── icon.png # Agent icon
├── package.json
└── vercel.json
```
## Custom Facilitator
The `app/accepts.ts` file configures a custom payment facilitator:
```typescript title="app/accepts.ts" theme={null}
import { HTTPFacilitatorClient } from "aixyz/accepts";
export const facilitator = new HTTPFacilitatorClient({
url: process.env.X402_FACILITATOR_URL ?? "https://www.x402.org/facilitator",
});
```
## Key Features
* **Custom facilitator URL** — Point to any x402-compatible facilitator service
* **Environment-driven** — Facilitator URL configurable via `X402_FACILITATOR_URL`
* **Fallback to default** — Uses the default x402.org facilitator if no env var is set
* **Auto-generated server** — No custom `server.ts` needed; the build pipeline picks up `app/accepts.ts` automatically
## When to Use
Use a custom facilitator when you need to:
* Run your own x402 facilitator service for payment verification
* Use a third-party facilitator other than the default
* Test payments against a local or staging facilitator
## Payment
The agent charges `$0.005` per request via x402 on Base Sepolia.
## Environment Variables
| Variable | Description |
| ---------------------- | --------------------------------------------- |
| `OPENAI_API_KEY` | OpenAI API key |
| `X402_FACILITATOR_URL` | Custom facilitator URL (defaults to x402.org) |
## Running
```bash theme={null}
cd examples/with-custom-facilitator
bun install
bun run dev
```
# Custom Server Agent
Source: https://aixyz.sh/templates/advanced/with-custom-server
Template demonstrating manual server control with app/server.ts
**Source:** [`examples/with-custom-server`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/with-custom-server)
## Overview
This template demonstrates how to take full control over endpoint registration by providing a custom `app/server.ts`. Instead of relying on the auto-generated server, you manually wire up A2A, MCP, and x402 — giving you complete control over pricing, middleware, and route configuration.
## Project Structure
```
with-custom-server/
├── aixyz.config.ts # Agent metadata
├── app/
│ ├── server.ts # Custom server setup
│ ├── agent.ts # Agent definition
│ ├── tools/
│ │ └── lookup.ts # Example tool
│ └── icon.png # Agent icon
├── package.json
└── vercel.json
```
## Custom Server
The `app/server.ts` file gives you full control:
```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 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: "latestData",
exports: {
default: lookup,
accepts: {
scheme: "exact",
price: "$0.001",
},
},
},
]),
);
await server.initialize();
export default server;
```
## Key Features
* **Manual A2A setup** — `A2APlugin` registers agent card and JSON-RPC endpoint
* **Manual MCP setup** — `MCPPlugin` registers tools with custom names and pricing
* **Per-tool pricing** — Set different x402 prices for each MCP tool
* **Index page** — `IndexPagePlugin` adds a human-readable landing page
## When to Use
Use a custom server when you need to:
* Register MCP tools with custom names or specific pricing
* Add custom Express middleware or routes
* Control the order of endpoint registration
* Integrate additional services (e.g., Stripe, webhooks)
## Running
```bash theme={null}
cd examples/with-custom-server
bun install
bun run dev
```
# Express Integration Agent
Source: https://aixyz.sh/templates/advanced/with-express
Template demonstrating mounting AixyzApp as Express middleware
**Source:** [`examples/with-express`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/with-express)
## Overview
This template demonstrates how to mount an `AixyzApp` on an Express server using the `toExpressMiddleware` adapter. Your own Express routes coexist alongside A2A, MCP, and x402 endpoints without conflict.
## Project Structure
```
with-express/
├── aixyz.config.ts # Agent metadata
├── app/
│ ├── server.ts # Express + AixyzApp setup
│ ├── server.test.ts # End-to-end tests with testcontainers
│ ├── agent.ts # Agent definition
│ ├── accepts.ts # Custom x402 facilitator
│ ├── tools/
│ │ ├── temperature.ts # Free tool
│ │ └── premium-temperature.ts # Paid tool
│ └── icon.png # Agent icon
├── package.json
└── vercel.json
```
## Custom Server
The `app/server.ts` creates an Express app and mounts AixyzApp as middleware:
```typescript title="app/server.ts" theme={null}
import express from "express";
import { AixyzApp } from "aixyz/app";
import { toExpressMiddleware } from "aixyz/app/adapters/express";
import { IndexPagePlugin } from "aixyz/app/plugins/index-page";
import { A2APlugin } from "aixyz/app/plugins/a2a";
import { MCPPlugin } from "aixyz/app/plugins/mcp";
import { facilitator } from "./accepts";
import * as agent from "./agent";
import * as convertTemperature from "./tools/temperature";
import * as premiumTemperature from "./tools/premium-temperature";
// 1. Create AixyzApp with plugins
const app = new AixyzApp(facilitator ? { facilitators: facilitator } : undefined);
await app.withPlugin(new IndexPagePlugin());
await app.withPlugin(new A2APlugin([{ exports: agent }]));
await app.withPlugin(
new MCPPlugin([
{ name: "convertTemperature", exports: convertTemperature },
{ name: "premiumTemperature", exports: premiumTemperature },
]),
);
await app.initialize();
// 2. Mount on Express
const expressApp = express();
// Custom Express routes
expressApp.get("/health", (_req, res) => res.json({ status: "ok" }));
expressApp.post("/echo", express.json(), (req, res) => res.json(req.body));
// Mount AixyzApp — do NOT use express.json() before this
expressApp.use(toExpressMiddleware(app));
// 3. Start server
const port = parseInt(process.env.PORT || "3000", 10);
expressApp.listen(port);
```
## Key Features
* **Express + AixyzApp coexistence** — Your own Express routes alongside A2A/MCP/x402
* **`toExpressMiddleware` adapter** — Bridges web-standard `Request`/`Response` to Express middleware
* **Mixed tool pricing** — Free and paid tools on the same MCP endpoint
* **Custom facilitator** — BYO x402 facilitator via `app/accepts.ts`
* **End-to-end tests** — Full payment flow tests using testcontainers
Do not apply `express.json()` or other body-parsing middleware before `toExpressMiddleware`, as it needs access to the
raw request body stream.
## When to Use
Use Express integration when you need to:
* Add AixyzApp to an existing Express application
* Mix custom REST endpoints with A2A/MCP protocol endpoints
* Use Express-specific middleware for some routes
## Running
```bash theme={null}
cd examples/with-express
bun install
bun run dev
```
# Agent with Tests
Source: https://aixyz.sh/templates/advanced/with-tests
Template demonstrating testing patterns for aixyz agents using bun:test
**Source:** [`examples/with-tests`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/with-tests)
## Overview
This template demonstrates how to write both deterministic and non-deterministic tests for aixyz agents using `bun:test`. It covers unit testing agent configuration, tool registration, and end-to-end agent execution.
## Project Structure
```
with-tests/
├── aixyz.config.ts # Agent metadata
├── app/
│ ├── agent.ts # Agent with temperature tool
│ ├── agent.test.ts # Test suite
│ ├── tools/
│ │ └── temperature.ts # Temperature conversion tool
│ └── icon.png # Agent icon
├── package.json
└── vercel.json
```
## Test Pattern
```typescript title="app/agent.test.ts" theme={null}
import { describe, expect, test } from "bun:test";
import { ToolLoopAgent } from "ai";
import { loadEnv } from "aixyz/test";
import agent, { accepts } from "./agent";
// Deterministic tests — no API calls needed
test("default export is a ToolLoopAgent", () => {
expect(agent).toBeInstanceOf(ToolLoopAgent);
});
test("has convertTemperature tool registered", () => {
expect(agent.tools).toHaveProperty("convertTemperature");
});
test("accepts config uses exact scheme", () => {
expect(accepts.scheme).toBe("exact");
});
// Non-deterministic tests — require OPENAI_API_KEY
describe("non deterministic agent test", () => {
loadEnv();
test.skipIf(!process.env.OPENAI_API_KEY)("agent can convert temperature", async () => {
const result = await agent.generate({
prompt: "convert 100 degrees celsius to fahrenheit",
});
expect(result.text).toContain("212");
});
});
```
## Key Features
* **Deterministic tests** — Validate agent type, tool registration, and config without API calls
* **Non-deterministic tests** — Test actual agent execution with `test.skipIf` for missing API keys
* **Environment loading** — `loadEnv()` from `aixyz/test` loads `.env` files for test runs
* **CI-friendly** — Deterministic tests always run; non-deterministic tests skip gracefully
## Running Tests
```bash theme={null}
cd examples/with-tests
bun install
bun test
```
## Running the Agent
```bash theme={null}
cd examples/with-tests
bun run dev
```
# x402 Sessions
Source: https://aixyz.sh/templates/advanced/x402-sessions
Template demonstrating payer-scoped session storage with x402 payment identity
**Source:** [`examples/x402-sessions`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/x402-sessions)
## Overview
This template demonstrates `SessionPlugin` -- payer-scoped key-value storage gated by x402 payment identity. Each x402 signer gets isolated storage that persists across requests. Two different payers never see each other's data.
## Project Structure
```
x402-sessions/
├── aixyz.config.ts # Agent metadata and skills
├── app/
│ ├── session.ts # Custom session store (optional)
│ ├── agent.ts # Agent definition
│ ├── accepts.ts # Custom x402 facilitator
│ └── tools/
│ ├── put-content.ts # Store content in session
│ └── get-content.ts # Retrieve content from session
├── package.json
└── vercel.json
```
## Session Store
SessionPlugin is auto-registered by the build pipeline. To customize the store, create `app/session.ts`:
```typescript title="app/session.ts" theme={null}
import { defineSessionStore, InMemorySessionStore } from "aixyz/app/plugins/session";
// Use the built-in in-memory store. Replace with Redis, DB, etc. for production.
export default defineSessionStore(new InMemorySessionStore());
```
If `app/session.ts` is not present, the default `InMemorySessionStore` is used automatically.
## Using Sessions in Tools
Tools access the session via `getSession()` -- no need to pass payer identity manually:
```typescript title="app/tools/put-content.ts" theme={null}
import { tool } from "ai";
import { z } from "zod";
import { getSession } from "aixyz/app/plugins/session";
import type { Accepts } from "aixyz/accepts";
export default tool({
description: "Store a key-value pair in the current user's session",
inputSchema: z.object({
key: z.string(),
value: z.string().nullable(),
}),
execute: async ({ key, value }) => {
const session = getSession();
if (!session) {
return { success: false, error: "No authenticated signer in context" };
}
if (value === null) {
await session.delete(key);
return { success: true, key, deleted: true };
}
await session.set(key, value);
return { success: true, key };
},
});
export const accepts: Accepts = { scheme: "exact", price: "$0.01" };
```
## Skills
| Skill | Description | Price |
| ------------- | ----------------------------------------------- | ------- |
| `put-content` | Store key-value content in payer-scoped session | \$0.01 |
| `get-content` | Retrieve stored content from session | \$0.001 |
## Running
```bash theme={null}
cd examples/x402-sessions
# create a .env file
bun install
bun run dev
```
Full API reference for SessionPlugin, Session, and SessionStore.
Payment protocol that provides payer identity for sessions.
# Boilerplate Agent
Source: https://aixyz.sh/templates/basic/boilerplate
A starter template with unit conversion tools for length, weight, and temperature
**Source:** [`examples/boilerplate`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/boilerplate)
## Overview
The boilerplate agent is the simplest starting point for building an aixyz agent. It demonstrates a multi-skill agent with three unit conversion tools — length, weight, and temperature — using the auto-generated server pattern.
## Project Structure
```
boilerplate/
├── aixyz.config.ts # Agent metadata and skills
├── app/
│ ├── agent.ts # Agent definition with tools
│ ├── tools/
│ │ ├── length.ts # Length conversion tool
│ │ ├── weight.ts # Weight conversion tool
│ │ └── temperature.ts # Temperature conversion tool
│ └── icon.png # Agent icon
├── package.json
└── vercel.json
```
## Skills
| Skill | Description |
| ------------------- | ------------------------------------------------------------------ |
| Convert Length | Convert length and distance values between metric and imperial |
| Convert Weight | Convert weight and mass values between metric and imperial |
| Convert Temperature | Convert temperature values between Celsius, Fahrenheit, and Kelvin |
## Configuration
```typescript title="aixyz.config.ts" theme={null}
const config: AixyzConfig = {
name: "Unit Conversion Agent",
description: "AI agent that converts values between metric, imperial, and other measurement systems.",
version: "0.1.0",
x402: {
payTo: "0x0799872E07EA7a63c79357694504FE66EDfE4a0A",
network: process.env.NODE_ENV === "production" ? "eip155:8453" : "eip155:84532",
},
skills: [
{ id: "convert-length", name: "Convert Length", ... },
{ id: "convert-weight", name: "Convert Weight", ... },
{ id: "convert-temperature", name: "Convert Temperature", ... },
],
};
```
## Payment
The agent charges `$0.001` per request via x402 on Base (mainnet in production, Base Sepolia in development).
## Running
```bash theme={null}
cd examples/boilerplate
bun install
bun run dev
```
Endpoints available at `http://localhost:3000`:
| Endpoint | Protocol | Description |
| ------------------------------ | -------- | --------------------- |
| `/.well-known/agent-card.json` | A2A | Agent discovery card |
| `/agent` | A2A | JSON-RPC task handler |
| `/mcp` | MCP | Tool endpoint |
# Chainlink Price Oracle
Source: https://aixyz.sh/templates/basic/chainlink
Real-time crypto price agent using Chainlink price feeds on Ethereum mainnet
**Source:** [`examples/chainlink`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/chainlink)
## Overview
The Chainlink Price Oracle agent provides real-time cryptocurrency price data by reading Chainlink price feeds directly from Ethereum mainnet. It demonstrates on-chain data integration with an aixyz agent, gated behind x402 payments.
## Project Structure
```
chainlink/
├── aixyz.config.ts # Agent metadata and skills
├── app/
│ ├── agent.ts # Agent with Chainlink lookup tool
│ ├── tools/
│ │ └── lookup.ts # Chainlink price feed reader
│ └── icon.png # Agent icon
├── package.json
└── vercel.json
```
## Skills
| Skill | Description |
| ---------------------- | -------------------------------------------------------------------- |
| Chainlink Price Lookup | Look up real-time cryptocurrency prices in USD using Chainlink feeds |
## Configuration
```typescript title="aixyz.config.ts" theme={null}
const config: AixyzConfig = {
name: "Chainlink Price Oracle",
description:
"AI agent that provides real-time cryptocurrency price data using Chainlink price feeds on Ethereum mainnet.",
version: "1.0.0",
x402: {
payTo: process.env.X402_PAY_TO!,
network: process.env.X402_NETWORK!,
},
};
```
## Key Features
* **On-chain data** — Reads directly from Chainlink price feeds on Ethereum mainnet
* **Symbol validation** — Does not assume token symbols; validates via the tool
* **Environment-driven config** — Payment address and network set via environment variables
* **Auto-generated server** — No custom `server.ts` needed
## Payment
The agent charges `$0.01` per request via x402. Payment destination is configured via environment variables.
## Environment Variables
| Variable | Description |
| ---------------- | ------------------------------------- |
| `OPENAI_API_KEY` | OpenAI API key |
| `X402_PAY_TO` | Payment destination address |
| `X402_NETWORK` | Payment network (e.g., `eip155:8453`) |
## Running
```bash theme={null}
cd examples/chainlink
bun install
bun run dev
```
# Local LLM Agent
Source: https://aixyz.sh/templates/basic/local-llm
An agent powered by a local LLM running entirely in-process via WebAssembly — no API key required
**Source:** [`examples/local-llm`](https://github.com/AgentlyHQ/aixyz/tree/main/examples/local-llm)
## Overview
The local LLM agent runs a quantized language model entirely in-process using [Transformers.js](https://huggingface.co/docs/transformers.js) (ONNX/WebAssembly). No external server, no base URL, and no API key are required. The model is downloaded from HuggingFace Hub on first run and cached locally.
## Project Structure
```
local-llm/
├── aixyz.config.ts # Agent metadata and skills (standalone output)
├── app/
│ ├── agent.ts # Agent definition with local LLM model
│ ├── agent.test.ts # Integration test
│ ├── tools/
│ │ └── temperature.ts # Temperature conversion tool
│ └── icon.png # Agent icon
├── prewarm.ts # Model prewarming script (used by Docker)
├── Dockerfile # Multi-stage Docker image with prewarmed model
├── .dockerignore
└── package.json
```
## Skills
| Skill | Description |
| ------------------- | ------------------------------------------------------------------ |
| Convert Temperature | Convert temperature values between Celsius, Fahrenheit, and Kelvin |
## Agent
```typescript title="app/agent.ts" theme={null}
import { transformersJS } from "@browser-ai/transformers-js";
import { stepCountIs, ToolLoopAgent } from "ai";
export default new ToolLoopAgent({
model: transformersJS("onnx-community/Qwen2.5-1.5B-Instruct", { dtype: "q4" }),
instructions: instructions,
tools: { convertTemperature },
stopWhen: stepCountIs(10),
});
```
The model (`onnx-community/Qwen2.5-1.5B-Instruct`, q4 quantized) is loaded via `@browser-ai/transformers-js`, which is an official [Vercel AI SDK community provider](https://ai-sdk.dev/providers/community-providers/built-in-ai) for Transformers.js.
## Payment
This agent is free (`scheme: "free"`) — no x402 payment is required to call it.
## Running locally
```bash theme={null}
cd examples/local-llm
bun install
bun run dev
```
## Docker deployment
Because the local LLM model (\~1 GB of weights) is downloaded and prewarmed **at image build time**, the container starts serving requests immediately with no cold-start delay.
### Build the image
```bash theme={null}
cd examples/local-llm
docker build -t local-llm .
```
Or use the npm script:
```bash theme={null}
bun run docker:build
```
### Run the container
```bash theme={null}
docker run -p 3000:3000 local-llm
```
Or:
```bash theme={null}
bun run docker:run
```
Endpoints available at `http://localhost:3000`:
| Endpoint | Protocol | Description |
| ------------------------------ | -------- | --------------------- |
| `/.well-known/agent-card.json` | A2A | Agent discovery card |
| `/agent` | A2A | JSON-RPC task handler |
| `/mcp` | MCP | Tool endpoint |
# Templates
Source: https://aixyz.sh/templates/overview
Working agent templates demonstrating common patterns
These templates are working examples in the [`examples/`](https://github.com/AgentlyHQ/aixyz/tree/main/examples) directory. Each follows the standard aixyz `app/` directory structure and can be run locally.
## Running a Template
```bash theme={null}
cd examples/boilerplate
bun install
bun run dev
```
The agent starts at `http://localhost:3000` with all protocol endpoints enabled.
## Basic
Standard agents using `app/agent.ts` + `app/tools/`. No custom server — the server is auto-generated by the build pipeline. Start here.
Unit conversion agent with length, weight, and temperature tools.
Flight search with Skyscanner API and Stripe payments.
Chainlink price feeds on Ethereum mainnet.
## Advanced
Custom server wiring, bring-your-own payment facilitator, and testing patterns. These templates demonstrate how to take full control of the aixyz server.
Multiple A2A endpoints from one deployment using app/agents/\[name].ts.
Manual server control with app/server.ts — wire A2A, MCP, and x402 yourself.
Bring-your-own x402 facilitator via app/accepts.ts.
Testing patterns with bun:test — deterministic and non-deterministic tests.
Fully deterministic testing with `fake()` — no API key required.
Mount AixyzApp as Express middleware alongside your own routes.
Payer-scoped session storage with x402 payment identity.