Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 39 additions & 19 deletions docs/config/aixyz-config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -26,37 +26,57 @@ const config: AixyzConfig = {
description: "Get current weather conditions for any city",
tags: ["weather"],
examples: ["What's the weather in Tokyo?"],
oasf: { name: "weather/forecast", id: 101 },
},
],
domains: [{ name: "technology/ai", id: 1 }],
services: [{ type: "openapi", url: "https://my-agent.vercel.app/openapi.json" }],
};

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 |
| 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 |
| `domains` | `Domain[]` | No | OASF domain categories (defaults to `[]`) |
| `services` | `Service[]` | No | External service locators (defaults to `[]`) |

### `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 |
| 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 |
| `oasf` | `object` | No | OASF catalog metadata (`{ name, id }`) — see [OASF](/protocols/oasf) |

### `Domain`

| Field | Type | Required | Description |
| ------ | -------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `name` | `string` | Yes | Domain name from the [OASF domain catalog](https://schema.oasf.outshift.com/1.0.0/domain_categories) |
| `id` | `number` | Yes | Domain ID from the OASF domain catalog |

### `Service`

| Field | Type | Required | Description |
| ------ | -------- | -------- | -------------------------------------------- |
| `type` | `string` | Yes | Service type (e.g. `"openapi"`, `"graphql"`) |
| `url` | `string` | Yes | Service endpoint URL |

## URL Resolution

Expand Down
2 changes: 1 addition & 1 deletion docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
},
{
"group": "Protocols",
"pages": ["protocols/a2a", "protocols/mcp", "protocols/x402", "protocols/erc-8004"]
"pages": ["protocols/a2a", "protocols/mcp", "protocols/x402", "protocols/erc-8004", "protocols/oasf"]
}
]
},
Expand Down
4 changes: 4 additions & 0 deletions docs/protocols/erc-8004.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ 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
- **OASF** — When both plugins are active, the registration file automatically includes an OASF service entry

<CardGroup cols={2}>
<Card title="A2A Protocol" icon="diagram-project" href="/protocols/a2a">
Expand All @@ -122,4 +123,7 @@ ERC-8004 identity complements the other protocols:
<Card title="x402 Payments" icon="credit-card" href="/protocols/x402">
Payment gating for agent endpoints.
</Card>
<Card title="OASF" icon="magnifying-glass" href="/protocols/oasf">
Standardized agent discovery with OASF.
</Card>
</CardGroup>
160 changes: 160 additions & 0 deletions docs/protocols/oasf.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
---
title: "OASF"
description: "Open Agent Service Framework for standardized agent discovery"
---

## Overview

[OASF (Open Agent Service Framework)](https://schema.oasf.outshift.com/1.0.0/) is a standardized format for describing AI agents, their capabilities, and integrations. It enables agent discovery across the Open Agent Service ecosystem.

aixyz automatically generates an OASF record from your `aixyz.config.ts` and registered plugins. The `OASFPlugin` is always included in the build pipeline — no setup required.

## Endpoint

| Endpoint | Method | Description |
| ------------------- | ------ | ------------------------------------------------ |
| `/_aixyz/oasf.json` | GET | OASF record with metadata, modules, and locators |

```bash
curl http://localhost:3000/_aixyz/oasf.json
```

Example response:

```json
{
"name": "Weather Agent",
"description": "Get current weather for any location worldwide.",
"version": "0.1.0",
"schema_version": "1.0.0",
"authors": [],
"created_at": "2026-03-23T12:00:00.000Z",
"domains": [{ "name": "technology/ai", "id": 1 }],
"skills": [{ "name": "weather/forecast", "id": 101 }],
"modules": [
{
"name": "integration/a2a",
"id": 203,
"data": {
"card_data": { "name": "Weather Agent", "...": "..." },
"card_schema_version": "0.3.0"
}
},
{
"name": "integration/mcp",
"id": 202,
"data": {
"name": "Weather Agent",
"connections": [{ "type": "streamable-http", "url": "https://my-agent.vercel.app/mcp" }],
"tools": [{ "name": "getWeather", "description": "Get weather for a city" }]
}
}
],
"locators": [
{ "type": "a2a", "urls": ["https://my-agent.vercel.app/.well-known/agent-card.json"] },
{ "type": "mcp", "urls": ["https://my-agent.vercel.app/mcp"] }
]
}
```

## Auto-Detection

The OASF record is assembled automatically from your registered plugins and config:

**Modules** — detected from registered plugins:

| Module | ID | Detected When | Data Included |
| ----------------- | --- | --------------------------- | -------------------------------------------- |
| `integration/a2a` | 203 | A2A agent card route exists | Full agent card data, schema version `0.3.0` |
| `integration/mcp` | 202 | MCP plugin registered | Connection info, registered tools |

**Locators** — service endpoints for agent discovery:

| Source | Type | URLs |
| --------------- | --------- | ----------------------------------------- |
| A2A plugin | `a2a` | All `/.well-known/agent-card.json` routes |
| MCP plugin | `mcp` | `/mcp` endpoint |
| Config services | Per entry | From `services` array in config |

## Configuration

OASF-specific fields in `aixyz.config.ts`:

```typescript title="aixyz.config.ts"
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?"],
// OASF catalog metadata — only skills with this field appear in the OASF record
oasf: { name: "weather/forecast", id: 101 },
},
],
// OASF domain categories
domains: [{ name: "technology/ai", id: 1 }],
// Additional service locators (e.g. OpenAPI, GraphQL)
services: [{ type: "openapi", url: "https://my-agent.vercel.app/openapi.json" }],
};

export default config;
```

### `domains`

OASF domain categories describing the agent's area of expertise. Each entry has a `name` and `id` from the [OASF domain catalog](https://schema.oasf.outshift.com/1.0.0/domain_categories). Defaults to `[]`.

### `services`

External service endpoints to advertise. Each entry becomes an OASF locator (and optionally an ERC-8004 service). Defaults to `[]`.
Comment thread
kevzzsk marked this conversation as resolved.

### `skills[].oasf`

Optional OASF catalog metadata on each skill. Skills without this field are excluded from the OASF record. The `name` and `id` come from the [OASF skill catalog](https://schema.oasf.outshift.com/1.0.0/skill_categories).

## Using the OASF Plugin

The `OASFPlugin` is auto-registered in all generated servers. For custom servers:

```typescript title="app/server.ts"
import { OASFPlugin } from "aixyz/app/plugins/oasf";

await server.withPlugin(new OASFPlugin());
```

The plugin uses a two-phase lifecycle:

1. **Register** — creates the `GET /_aixyz/oasf.json` route
2. **Initialize** — builds and caches the OASF record after all other plugins are registered, ensuring A2A and MCP integrations are detected

## Integration with ERC-8004

When both OASF and ERC-8004 plugins are active, the ERC-8004 agent registration file automatically includes an OASF service entry:

```json
{
"name": "OASF",
"endpoint": "https://my-agent.vercel.app/_aixyz/oasf.json",
"version": "1.0.0"
}
```

This links on-chain agent identity to the OASF discovery endpoint.

<CardGroup cols={2}>
<Card title="A2A Protocol" icon="diagram-project" href="/protocols/a2a">
Agent discovery with A2A agent cards.
</Card>
<Card title="ERC-8004 Identity" icon="id-card" href="/protocols/erc-8004">
On-chain agent identity standard.
</Card>
</CardGroup>
12 changes: 6 additions & 6 deletions packages/aixyz-cli/build/AixyzServerPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,9 @@ function generateServer(appDir: string, entrypointDir: string): string {
imports.push(`import * as erc8004 from "${importPrefix}/erc-8004";`);
}

// Always register OASF endpoint (derives from aixyz.config.ts)
imports.push('import { OASFPlugin } from "aixyz/app/plugins/oasf";');

body.push("const app = new AixyzApp({ facilitators: facilitator });");
body.push("await app.withPlugin(new IndexPagePlugin());");

Expand All @@ -189,14 +192,11 @@ function generateServer(appDir: string, entrypointDir: string): string {
}

if (hasErc8004) {
const a2aPaths: string[] = [];
if (rootAgent) a2aPaths.push("/.well-known/agent-card.json");
for (const subAgent of subAgents) a2aPaths.push(`/${subAgent.name}/.well-known/agent-card.json`);
body.push(
`await app.withPlugin(new ERC8004Plugin({ default: erc8004.default, options: { mcp: ${tools.length > 0}, a2a: ${JSON.stringify(a2aPaths)} } }));`,
);
body.push(`await app.withPlugin(new ERC8004Plugin({ default: erc8004.default }));`);
}

body.push(`await app.withPlugin(new OASFPlugin());`);

body.push("await app.initialize();");
body.push("export default app;");

Expand Down
39 changes: 39 additions & 0 deletions packages/aixyz-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ export type AixyzConfig = {
maxDuration?: number;
};
skills?: InferredAixyzConfig["skills"];
/**
* OASF domain categories for the agent.
* Uses the OASF domain catalog identifiers (e.g. `"finance_and_business"`, `"technology/blockchain"`).
* @see https://schema.oasf.outshift.com/1.0.0/domain_categories
*/
domains?: InferredAixyzConfig["domains"];
/**
* External services to advertise (e.g. OpenAPI, GraphQL).
* Each entry becomes an OASF locator and optionally an ERC-8004 service.
Comment thread
kevzzsk marked this conversation as resolved.
*/
services?: InferredAixyzConfig["services"];
};

const NetworkSchema = z.custom<Network>((val) => {
Expand All @@ -91,6 +102,8 @@ const defaultConfig = {
},
vercel: { maxDuration: 60 },
skills: [],
domains: [],
services: [],
};

const AixyzConfigSchema = z.object({
Expand Down Expand Up @@ -155,9 +168,31 @@ const AixyzConfigSchema = z.object({
inputModes: z.array(z.string()).optional(),
outputModes: z.array(z.string()).optional(),
security: z.array(z.record(z.string(), z.array(z.string()))).optional(),
oasf: z
.object({
name: z.string().nonempty(),
id: z.number().int().positive(),
})
.optional(),
}),
)
.default(defaultConfig.skills),
domains: z
.array(
z.object({
name: z.string().nonempty(),
id: z.number().int().positive(),
}),
)
.default(defaultConfig.domains),
services: z
.array(
z.object({
type: z.string().nonempty(),
url: z.string().url(),
}),
Comment thread
kevzzsk marked this conversation as resolved.
)
.default(defaultConfig.services),
});

type InferredAixyzConfig = z.infer<typeof AixyzConfigSchema>;
Expand All @@ -175,6 +210,8 @@ export type AixyzConfigRuntime = {
version: AixyzConfig["version"];
url: AixyzConfig["url"];
skills: NonNullable<AixyzConfig["skills"]>;
domains: NonNullable<AixyzConfig["domains"]>;
services: NonNullable<AixyzConfig["services"]>;
};

/**
Expand Down Expand Up @@ -225,5 +262,7 @@ export function getAixyzConfigRuntime(): AixyzConfigRuntime {
version: config.version,
url: config.url,
skills: config.skills,
domains: config.domains,
services: config.services,
};
}
Loading
Loading