aixyz 0.28.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/app/adapters/express.ts +1 -1
- package/app/index.ts +22 -4
- package/app/plugin.ts +78 -3
- package/app/plugins/a2a.ts +37 -27
- package/app/plugins/erc-8004.ts +4 -6
- package/app/plugins/index-page/index.ts +6 -7
- package/app/plugins/mcp.ts +8 -9
- package/docs/api-reference/aixyz-server.mdx +3 -3
- package/docs/api-reference/experimental-stripe-payment-intent-plugin.mdx +1 -1
- package/docs/getting-started/agent-and-tools.mdx +1 -1
- package/docs/protocols/a2a.mdx +10 -6
- package/docs/protocols/erc-8004.mdx +0 -1
- package/docs/templates/advanced/with-custom-server.mdx +1 -1
- package/docs/templates/advanced/with-express.mdx +1 -1
- package/examples/with-express/app/server.ts +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -137,7 +137,7 @@ const server = new AixyzApp();
|
|
|
137
137
|
await server.withPlugin(new IndexPagePlugin());
|
|
138
138
|
|
|
139
139
|
// A2A: agent discovery + JSON-RPC endpoint
|
|
140
|
-
await server.withPlugin(new A2APlugin(agent));
|
|
140
|
+
await server.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
141
141
|
|
|
142
142
|
// MCP: expose tools to MCP clients
|
|
143
143
|
await server.withPlugin(
|
package/app/adapters/express.ts
CHANGED
|
@@ -47,7 +47,7 @@ interface NodeResponse {
|
|
|
47
47
|
* import * as myTool from "./tools/my-tool";
|
|
48
48
|
*
|
|
49
49
|
* const app = new AixyzApp();
|
|
50
|
-
* await app.withPlugin(new A2APlugin(agent));
|
|
50
|
+
* await app.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
51
51
|
* await app.withPlugin(new MCPPlugin([{ name: "myTool", exports: myTool }]));
|
|
52
52
|
* await app.initialize();
|
|
53
53
|
*
|
package/app/index.ts
CHANGED
|
@@ -5,12 +5,13 @@ import { PaymentGateway } from "./payment/payment";
|
|
|
5
5
|
import { Network } from "@x402/core/types";
|
|
6
6
|
import { getAixyzConfig } from "@aixyz/config";
|
|
7
7
|
import { loadEnvConfig } from "@next/env";
|
|
8
|
-
import { BasePlugin } from "./plugin";
|
|
8
|
+
import { BasePlugin, type RegisterContext, type InitializeContext } from "./plugin";
|
|
9
9
|
|
|
10
10
|
// Load .env and .env.production files at runtime (local files are excluded at build time).
|
|
11
11
|
loadEnvConfig(process.cwd());
|
|
12
12
|
|
|
13
13
|
export { BasePlugin };
|
|
14
|
+
export type { RegisterContext, InitializeContext };
|
|
14
15
|
export type { HttpMethod, RouteHandler, Middleware, RouteEntry };
|
|
15
16
|
|
|
16
17
|
export interface AixyzAppOptions {
|
|
@@ -52,15 +53,32 @@ export class AixyzApp {
|
|
|
52
53
|
await this.payment.initialize();
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
const ctx: InitializeContext = {
|
|
57
|
+
routes: this.routes,
|
|
58
|
+
getPlugin: <T extends BasePlugin>(name: string) => this.getPlugin<T>(name),
|
|
59
|
+
payment: this.payment,
|
|
60
|
+
};
|
|
55
61
|
for (const plugin of this.plugins) {
|
|
56
|
-
await plugin.initialize?.(
|
|
62
|
+
await plugin.initialize?.(ctx);
|
|
57
63
|
}
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
/** Register a plugin
|
|
66
|
+
/** Register a plugin with a scoped RegisterContext that auto-tracks registered routes. */
|
|
61
67
|
async withPlugin<B extends BasePlugin>(plugin: B): Promise<this> {
|
|
62
68
|
this.plugins.push(plugin);
|
|
63
|
-
|
|
69
|
+
// clear registered routes for this plugin before registration, in case of hot reload or multiple registrations
|
|
70
|
+
plugin.registeredRoutes.clear();
|
|
71
|
+
|
|
72
|
+
const ctx: RegisterContext = {
|
|
73
|
+
route: (method, path, handler, options) => {
|
|
74
|
+
this.route(method, path, handler, options);
|
|
75
|
+
const key = this.getRouteKey(method, path);
|
|
76
|
+
const entry = this.routes.get(key);
|
|
77
|
+
if (entry) plugin.registeredRoutes.set(key, entry);
|
|
78
|
+
},
|
|
79
|
+
use: (mw) => this.use(mw),
|
|
80
|
+
};
|
|
81
|
+
await plugin.register?.(ctx);
|
|
64
82
|
return this;
|
|
65
83
|
}
|
|
66
84
|
|
package/app/plugin.ts
CHANGED
|
@@ -1,7 +1,82 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { HttpMethod, RouteHandler, RouteEntry, Middleware } from "./types";
|
|
2
|
+
import type { AcceptsX402 } from "../accepts";
|
|
3
|
+
import type { PaymentGateway } from "./payment/payment";
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Scoped context passed to {@link BasePlugin.register}.
|
|
7
|
+
*
|
|
8
|
+
* Intentionally narrow — plugins can only register routes and middleware during
|
|
9
|
+
* the registration phase. Every `route()` call is automatically tracked in
|
|
10
|
+
* {@link BasePlugin.registeredRoutes}, so plugins never need to track their own routes.
|
|
11
|
+
*
|
|
12
|
+
* This follows the Rollup/Vite plugin context pattern: plugins receive a scoped
|
|
13
|
+
* interface rather than the full application instance.
|
|
14
|
+
*/
|
|
15
|
+
export interface RegisterContext {
|
|
16
|
+
/** Register a route on the application. Automatically tracked in `plugin.registeredRoutes`. */
|
|
17
|
+
route(method: HttpMethod, path: string, handler: RouteHandler, options?: { payment?: AcceptsX402 }): void;
|
|
18
|
+
/** Append a middleware to the application's middleware chain. */
|
|
19
|
+
use(middleware: Middleware): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Context passed to {@link BasePlugin.initialize}.
|
|
24
|
+
*
|
|
25
|
+
* Available after all plugins have registered their routes. Provides read access
|
|
26
|
+
* to the full route table, other plugins, and the payment gateway — enabling
|
|
27
|
+
* cross-plugin discovery and late-binding concerns like payment wrapper setup.
|
|
28
|
+
*
|
|
29
|
+
* Route registration should happen in `register()`, not `initialize()`.
|
|
30
|
+
*/
|
|
31
|
+
export interface InitializeContext {
|
|
32
|
+
/** Read-only view of all registered routes across all plugins. */
|
|
33
|
+
readonly routes: ReadonlyMap<string, RouteEntry>;
|
|
34
|
+
/** Find a registered plugin by name. Returns `undefined` if not found. */
|
|
35
|
+
getPlugin<T extends BasePlugin>(name: string): Readonly<T> | undefined;
|
|
36
|
+
/** Payment gateway instance, if x402 payment is configured. */
|
|
37
|
+
readonly payment?: PaymentGateway;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Base class for all aixyz plugins.
|
|
42
|
+
*
|
|
43
|
+
* Plugins extend `BasePlugin` and implement one or both lifecycle hooks:
|
|
44
|
+
*
|
|
45
|
+
* - **`register(ctx)`** — Called by {@link AixyzApp.withPlugin}. Use `ctx.route()` and
|
|
46
|
+
* `ctx.use()` to register routes and middleware. Routes registered here are
|
|
47
|
+
* automatically tracked in {@link registeredRoutes}.
|
|
48
|
+
*
|
|
49
|
+
* - **`initialize(ctx)`** — Called by {@link AixyzApp.initialize} after all plugins
|
|
50
|
+
* have registered. Use `ctx.routes`, `ctx.getPlugin()`, and `ctx.payment` for
|
|
51
|
+
* cross-plugin discovery and late-binding (e.g., payment wrappers, UI generation).
|
|
52
|
+
*
|
|
53
|
+
* Both hooks are optional — a plugin may implement only `register` (most common),
|
|
54
|
+
* only `initialize`, or both.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* class MyPlugin extends BasePlugin {
|
|
59
|
+
* readonly name = "my-plugin";
|
|
60
|
+
*
|
|
61
|
+
* register(ctx: RegisterContext) {
|
|
62
|
+
* ctx.route("GET", "/health", () => Response.json({ ok: true }));
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
3
67
|
export abstract class BasePlugin {
|
|
68
|
+
/** Unique identifier for this plugin. Used by {@link InitializeContext.getPlugin} for discovery. */
|
|
4
69
|
abstract readonly name: string;
|
|
5
|
-
|
|
6
|
-
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Routes registered by this plugin during {@link register}.
|
|
73
|
+
* Populated automatically by the framework — plugins do not need to manage this.
|
|
74
|
+
*/
|
|
75
|
+
readonly registeredRoutes = new Map<string, RouteEntry>();
|
|
76
|
+
|
|
77
|
+
/** Register routes and middleware. Called once by {@link AixyzApp.withPlugin}. */
|
|
78
|
+
register?(ctx: RegisterContext): void | Promise<void>;
|
|
79
|
+
|
|
80
|
+
/** Late initialization after all plugins have registered. Called once by {@link AixyzApp.initialize}. */
|
|
81
|
+
initialize?(ctx: InitializeContext): void | Promise<void>;
|
|
7
82
|
}
|
package/app/plugins/a2a.ts
CHANGED
|
@@ -12,8 +12,7 @@ import { AgentCard, Message, Task, TaskArtifactUpdateEvent, TaskStatusUpdateEven
|
|
|
12
12
|
import type { ToolLoopAgent, ToolSet } from "ai";
|
|
13
13
|
import { z } from "zod";
|
|
14
14
|
import { getAixyzConfigRuntime } from "../../config";
|
|
15
|
-
import { BasePlugin } from "../plugin";
|
|
16
|
-
import type { AixyzApp } from "../index";
|
|
15
|
+
import { BasePlugin, type RegisterContext } from "../plugin";
|
|
17
16
|
import { Accepts, AcceptsScheme } from "../../accepts";
|
|
18
17
|
|
|
19
18
|
export const CapabilitiesSchema = z.object({
|
|
@@ -24,6 +23,13 @@ export const CapabilitiesSchema = z.object({
|
|
|
24
23
|
|
|
25
24
|
export type Capabilities = z.infer<typeof CapabilitiesSchema>;
|
|
26
25
|
|
|
26
|
+
export interface A2AAgentEntry {
|
|
27
|
+
name?: string;
|
|
28
|
+
exports: { default: ToolLoopAgent; accepts?: Accepts; capabilities?: Capabilities };
|
|
29
|
+
/** Optional task store override. Defaults to a per-agent InMemoryTaskStore for isolation. */
|
|
30
|
+
taskStore?: TaskStore;
|
|
31
|
+
}
|
|
32
|
+
|
|
27
33
|
const DEFAULT_CAPABILITIES: Capabilities = { streaming: true, pushNotifications: false };
|
|
28
34
|
|
|
29
35
|
/**
|
|
@@ -148,52 +154,56 @@ export function getAgentCard(agentPath = "/agent", capabilities?: Capabilities):
|
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
/**
|
|
151
|
-
* A2A protocol plugin. Registers
|
|
152
|
-
* and
|
|
153
|
-
* Routes are only registered
|
|
157
|
+
* A2A protocol plugin. Registers well-known agent card endpoints
|
|
158
|
+
* and JSON-RPC endpoints for one or more agents.
|
|
159
|
+
* Routes are only registered for agents that export a valid `accepts` payment config.
|
|
154
160
|
*/
|
|
155
|
-
export class A2APlugin
|
|
161
|
+
export class A2APlugin extends BasePlugin {
|
|
156
162
|
readonly name = "a2a";
|
|
157
163
|
|
|
158
|
-
constructor(
|
|
159
|
-
private exports: { default: ToolLoopAgent<never, TOOLS>; accepts?: Accepts; capabilities?: Capabilities },
|
|
160
|
-
private prefix?: string,
|
|
161
|
-
private taskStore: TaskStore = new InMemoryTaskStore(),
|
|
162
|
-
) {
|
|
164
|
+
constructor(private agents: A2AAgentEntry[]) {
|
|
163
165
|
super();
|
|
164
166
|
}
|
|
165
167
|
|
|
166
|
-
register(
|
|
167
|
-
|
|
168
|
-
|
|
168
|
+
register(ctx: RegisterContext): void {
|
|
169
|
+
for (const entry of this.agents) {
|
|
170
|
+
this.registerAgent(ctx, entry);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
private registerAgent(ctx: RegisterContext, entry: A2AAgentEntry): void {
|
|
175
|
+
if (entry.exports.accepts) {
|
|
176
|
+
const result = AcceptsScheme.safeParse(entry.exports.accepts);
|
|
177
|
+
if (!result.success) {
|
|
178
|
+
const id = entry.name ?? "root";
|
|
179
|
+
throw new Error(`Invalid accepts config for agent "${id}": ${result.error.message}`);
|
|
180
|
+
}
|
|
169
181
|
} else {
|
|
170
182
|
return;
|
|
171
183
|
}
|
|
172
184
|
|
|
173
|
-
const parsed =
|
|
185
|
+
const parsed = entry.exports.capabilities ? CapabilitiesSchema.safeParse(entry.exports.capabilities) : undefined;
|
|
174
186
|
const capabilities = parsed?.success ? { ...DEFAULT_CAPABILITIES, ...parsed.data } : DEFAULT_CAPABILITIES;
|
|
175
187
|
|
|
176
|
-
const
|
|
177
|
-
const
|
|
178
|
-
|
|
188
|
+
const prefix = entry.name;
|
|
189
|
+
const agentPath: `/${string}` = prefix ? `/${prefix}/agent` : "/agent";
|
|
190
|
+
const wellKnownPath: `/${string}` = prefix
|
|
191
|
+
? `/${prefix}/.well-known/agent-card.json`
|
|
179
192
|
: "/.well-known/agent-card.json";
|
|
180
193
|
|
|
181
|
-
const
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
this.taskStore,
|
|
185
|
-
agentExecutor,
|
|
186
|
-
);
|
|
194
|
+
const taskStore = entry.taskStore ?? new InMemoryTaskStore();
|
|
195
|
+
const agentExecutor = new ToolLoopAgentExecutor(entry.exports.default, capabilities.streaming ?? true);
|
|
196
|
+
const requestHandler = new DefaultRequestHandler(getAgentCard(agentPath, capabilities), taskStore, agentExecutor);
|
|
187
197
|
const jsonRpcTransport = new JsonRpcTransportHandler(requestHandler);
|
|
188
198
|
|
|
189
199
|
// Agent card — pure web-standard handler
|
|
190
|
-
|
|
200
|
+
ctx.route("GET", wellKnownPath, async () => {
|
|
191
201
|
const card = await requestHandler.getAgentCard();
|
|
192
202
|
return Response.json(card);
|
|
193
203
|
});
|
|
194
204
|
|
|
195
205
|
// JSON-RPC endpoint — pure web-standard handler using JsonRpcTransportHandler
|
|
196
|
-
|
|
206
|
+
ctx.route(
|
|
197
207
|
"POST",
|
|
198
208
|
agentPath,
|
|
199
209
|
async (request: Request) => {
|
|
@@ -240,7 +250,7 @@ export class A2APlugin<TOOLS extends ToolSet = ToolSet> extends BasePlugin {
|
|
|
240
250
|
return Response.json(result);
|
|
241
251
|
},
|
|
242
252
|
{
|
|
243
|
-
payment:
|
|
253
|
+
payment: entry.exports.accepts.scheme === "exact" ? entry.exports.accepts : undefined,
|
|
244
254
|
},
|
|
245
255
|
);
|
|
246
256
|
}
|
package/app/plugins/erc-8004.ts
CHANGED
|
@@ -6,8 +6,7 @@ import {
|
|
|
6
6
|
ERC8004_REGISTRATION_TYPE,
|
|
7
7
|
ServiceSchema,
|
|
8
8
|
} from "@aixyz/erc-8004/schemas/registration";
|
|
9
|
-
import { BasePlugin } from "../plugin";
|
|
10
|
-
import type { AixyzApp } from "../index";
|
|
9
|
+
import { BasePlugin, type RegisterContext } from "../plugin";
|
|
11
10
|
|
|
12
11
|
/**
|
|
13
12
|
* Build an ERC-8004 agent registration file by merging user-provided data with
|
|
@@ -49,7 +48,7 @@ export function getAgentRegistrationFile(
|
|
|
49
48
|
return withDefault.parse(data);
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
/** ERC-8004 identity plugin. Registers
|
|
51
|
+
/** ERC-8004 identity plugin. Registers the `/_aixyz/erc-8004.json` route. */
|
|
53
52
|
export class ERC8004Plugin extends BasePlugin {
|
|
54
53
|
readonly name = "erc-8004";
|
|
55
54
|
|
|
@@ -57,10 +56,9 @@ export class ERC8004Plugin extends BasePlugin {
|
|
|
57
56
|
super();
|
|
58
57
|
}
|
|
59
58
|
|
|
60
|
-
register(
|
|
59
|
+
register(ctx: RegisterContext): void {
|
|
61
60
|
const file = getAgentRegistrationFile(this.exports.default, this.exports.options);
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
app.route("GET", "/_aixyz/erc-8004.json", () => Response.json(file));
|
|
62
|
+
ctx.route("GET", "/_aixyz/erc-8004.json", () => Response.json(file));
|
|
65
63
|
}
|
|
66
64
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getAixyzConfigRuntime } from "@aixyz/config";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
-
import { BasePlugin } from "../../plugin";
|
|
4
|
-
import type { AixyzApp } from "../../index";
|
|
3
|
+
import { BasePlugin, type RegisterContext, type InitializeContext } from "../../plugin";
|
|
5
4
|
import type { MCPPlugin } from "../mcp";
|
|
6
5
|
import { renderHtml } from "./html";
|
|
7
6
|
|
|
@@ -111,14 +110,14 @@ export class IndexPagePlugin extends BasePlugin {
|
|
|
111
110
|
super();
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
register(
|
|
113
|
+
register(ctx: RegisterContext): void {
|
|
115
114
|
const config = getAixyzConfigRuntime();
|
|
116
115
|
if (!this.path.startsWith("/")) {
|
|
117
116
|
throw new Error(`Invalid path: ${this.path}. Path must start with "/"`);
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
// Default to serve markdown, else explicitly asked for HTML (which browsers do by default)
|
|
121
|
-
|
|
120
|
+
ctx.route("GET", this.path, (request: Request) => {
|
|
122
121
|
if (prefersHtml(request)) {
|
|
123
122
|
return new Response(renderHtml(config, this.protocols), {
|
|
124
123
|
headers: { "Content-Type": "text/html; charset=utf-8", Vary: "Accept" },
|
|
@@ -130,11 +129,11 @@ export class IndexPagePlugin extends BasePlugin {
|
|
|
130
129
|
});
|
|
131
130
|
}
|
|
132
131
|
|
|
133
|
-
initialize(
|
|
132
|
+
initialize(ctx: InitializeContext): void {
|
|
134
133
|
const entrypoints: Entrypoint[] = [];
|
|
135
134
|
|
|
136
135
|
// Detect A2A agents from routes (POST */agent pattern)
|
|
137
|
-
for (const [key, entry] of
|
|
136
|
+
for (const [key, entry] of ctx.routes) {
|
|
138
137
|
if (key.startsWith("POST ") && entry.path.endsWith("/agent")) {
|
|
139
138
|
const prefix = entry.path.slice(1, -"/agent".length); // e.g. "" or "foo"
|
|
140
139
|
const name = prefix || "agent";
|
|
@@ -148,7 +147,7 @@ export class IndexPagePlugin extends BasePlugin {
|
|
|
148
147
|
}
|
|
149
148
|
|
|
150
149
|
// Detect MCP tools from MCPPlugin
|
|
151
|
-
const mcpPlugin =
|
|
150
|
+
const mcpPlugin = ctx.getPlugin<MCPPlugin>("mcp");
|
|
152
151
|
if (mcpPlugin?.registeredTools) {
|
|
153
152
|
for (const tool of mcpPlugin.registeredTools) {
|
|
154
153
|
let inputSchema: Record<string, unknown> | undefined;
|
package/app/plugins/mcp.ts
CHANGED
|
@@ -2,8 +2,7 @@ import { type Tool } from "ai";
|
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
3
|
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
4
4
|
import { createPaymentWrapper } from "@x402/mcp";
|
|
5
|
-
import { BasePlugin } from "../plugin";
|
|
6
|
-
import type { AixyzApp } from "../index";
|
|
5
|
+
import { BasePlugin, type RegisterContext, type InitializeContext } from "../plugin";
|
|
7
6
|
import type { Accepts } from "../../accepts";
|
|
8
7
|
import { AcceptsScheme } from "../../accepts";
|
|
9
8
|
import { getAixyzConfig, getAixyzConfigRuntime } from "../../config";
|
|
@@ -53,7 +52,7 @@ export class MCPPlugin extends BasePlugin {
|
|
|
53
52
|
return mcpServer;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
async register(
|
|
55
|
+
async register(ctx: RegisterContext): Promise<void> {
|
|
57
56
|
for (const t of this.tools) {
|
|
58
57
|
if (t.exports.accepts) {
|
|
59
58
|
AcceptsScheme.parse(t.exports.accepts);
|
|
@@ -74,16 +73,16 @@ export class MCPPlugin extends BasePlugin {
|
|
|
74
73
|
return transport.handleRequest(request);
|
|
75
74
|
};
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
ctx.route("POST", "/mcp", mcpHandler);
|
|
77
|
+
ctx.route("GET", "/mcp", mcpHandler);
|
|
78
|
+
ctx.route("DELETE", "/mcp", mcpHandler);
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
async initialize(
|
|
83
|
-
if (!
|
|
81
|
+
async initialize(ctx: InitializeContext): Promise<void> {
|
|
82
|
+
if (!ctx.payment) return;
|
|
84
83
|
|
|
85
84
|
const config = getAixyzConfig();
|
|
86
|
-
const resourceServer =
|
|
85
|
+
const resourceServer = ctx.payment.resourceServer;
|
|
87
86
|
|
|
88
87
|
for (const { name, accepts } of this.registeredTools) {
|
|
89
88
|
if (accepts?.scheme !== "exact") continue;
|
|
@@ -73,7 +73,7 @@ server.use(async (request, next) => {
|
|
|
73
73
|
|
|
74
74
|
### `withPlugin(plugin)`
|
|
75
75
|
|
|
76
|
-
Register a plugin.
|
|
76
|
+
Register a plugin. Creates a scoped `RegisterContext` that auto-tracks routes in `plugin.registeredRoutes`, calls `plugin.register(ctx)`, and returns `this` for chaining:
|
|
77
77
|
|
|
78
78
|
```typescript
|
|
79
79
|
await server.withPlugin(new IndexPagePlugin());
|
|
@@ -81,7 +81,7 @@ await server.withPlugin(new IndexPagePlugin());
|
|
|
81
81
|
|
|
82
82
|
### `initialize()`
|
|
83
83
|
|
|
84
|
-
Initialize payment gateway. Must be called after all
|
|
84
|
+
Initialize payment gateway and plugins. Passes an `InitializeContext` with read access to all routes, registered plugins, and payment gateway. Must be called after all plugins are registered:
|
|
85
85
|
|
|
86
86
|
```typescript
|
|
87
87
|
await server.initialize();
|
|
@@ -98,7 +98,7 @@ import * as agent from "./agent";
|
|
|
98
98
|
const server = new AixyzApp();
|
|
99
99
|
|
|
100
100
|
await server.withPlugin(new IndexPagePlugin());
|
|
101
|
-
await server.withPlugin(new A2APlugin(agent));
|
|
101
|
+
await server.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
102
102
|
|
|
103
103
|
await server.initialize();
|
|
104
104
|
|
|
@@ -77,7 +77,7 @@ await server.withPlugin(new IndexPagePlugin());
|
|
|
77
77
|
// Register Stripe before other plugins so the Stripe middleware runs first
|
|
78
78
|
await server.withPlugin(new experimental_StripePaymentIntentPlugin());
|
|
79
79
|
|
|
80
|
-
await server.withPlugin(new A2APlugin(agent));
|
|
80
|
+
await server.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
81
81
|
|
|
82
82
|
await server.initialize();
|
|
83
83
|
|
|
@@ -176,7 +176,7 @@ import * as lookup from "./tools/lookup";
|
|
|
176
176
|
const server = new AixyzApp();
|
|
177
177
|
|
|
178
178
|
await server.withPlugin(new IndexPagePlugin());
|
|
179
|
-
await server.withPlugin(new A2APlugin(agent));
|
|
179
|
+
await server.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
180
180
|
await server.withPlugin(
|
|
181
181
|
new MCPPlugin([
|
|
182
182
|
{
|
package/docs/protocols/a2a.mdx
CHANGED
|
@@ -94,7 +94,7 @@ import { A2APlugin } from "aixyz/app/plugins/a2a";
|
|
|
94
94
|
|
|
95
95
|
// agentExports is `import * as agent from "./agent"`
|
|
96
96
|
// reads `default` (the ToolLoopAgent), `accepts` (payment config), and `capabilities`
|
|
97
|
-
await server.withPlugin(new A2APlugin(agent));
|
|
97
|
+
await server.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
This registers:
|
|
@@ -104,18 +104,22 @@ This registers:
|
|
|
104
104
|
|
|
105
105
|
### Sub-Agent Routing
|
|
106
106
|
|
|
107
|
-
Pass
|
|
107
|
+
Pass multiple entries with a `name` to mount sub-agents under their own paths:
|
|
108
108
|
|
|
109
109
|
```typescript title="app/server.ts"
|
|
110
110
|
import * as research from "./agents/research";
|
|
111
111
|
import * as implement from "./agents/implement";
|
|
112
112
|
|
|
113
|
-
await server.withPlugin(
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
await server.withPlugin(
|
|
114
|
+
new A2APlugin([
|
|
115
|
+
{ exports: agent }, // → /agent
|
|
116
|
+
{ name: "research", exports: research }, // → /research/agent
|
|
117
|
+
{ name: "implement", exports: implement }, // → /implement/agent
|
|
118
|
+
]),
|
|
119
|
+
);
|
|
116
120
|
```
|
|
117
121
|
|
|
118
|
-
Each sub-agent gets its own agent card at `/{
|
|
122
|
+
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.
|
|
119
123
|
|
|
120
124
|
## Payment Integration
|
|
121
125
|
|
|
@@ -33,7 +33,6 @@ export default metadata;
|
|
|
33
33
|
When this file exists, the build pipeline automatically exposes two endpoints:
|
|
34
34
|
|
|
35
35
|
- `GET /_aixyz/erc-8004.json`
|
|
36
|
-
- `GET /.well-known/erc-8004.json`
|
|
37
36
|
|
|
38
37
|
These serve the full agent registration file, merging defaults from `aixyz.config.ts` (name, description, image, services).
|
|
39
38
|
|
|
@@ -42,7 +42,7 @@ import lookup from "./tools/lookup";
|
|
|
42
42
|
const server = new AixyzApp();
|
|
43
43
|
|
|
44
44
|
await server.withPlugin(new IndexPagePlugin());
|
|
45
|
-
await server.withPlugin(new A2APlugin(agent));
|
|
45
|
+
await server.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
46
46
|
await server.withPlugin(
|
|
47
47
|
new MCPPlugin([
|
|
48
48
|
{
|
|
@@ -47,7 +47,7 @@ import * as premiumTemperature from "./tools/premium-temperature";
|
|
|
47
47
|
// 1. Create AixyzApp with plugins
|
|
48
48
|
const app = new AixyzApp(facilitator ? { facilitators: facilitator } : undefined);
|
|
49
49
|
await app.withPlugin(new IndexPagePlugin());
|
|
50
|
-
await app.withPlugin(new A2APlugin(agent));
|
|
50
|
+
await app.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
51
51
|
await app.withPlugin(
|
|
52
52
|
new MCPPlugin([
|
|
53
53
|
{ name: "convertTemperature", exports: convertTemperature },
|
|
@@ -13,7 +13,7 @@ import * as premiumTemperature from "./tools/premium-temperature";
|
|
|
13
13
|
// 1. Create AixyzApp with plugins
|
|
14
14
|
const app = new AixyzApp(facilitator ? { facilitators: facilitator } : undefined);
|
|
15
15
|
await app.withPlugin(new IndexPagePlugin());
|
|
16
|
-
await app.withPlugin(new A2APlugin(agent));
|
|
16
|
+
await app.withPlugin(new A2APlugin([{ exports: agent }]));
|
|
17
17
|
await app.withPlugin(
|
|
18
18
|
new MCPPlugin([
|
|
19
19
|
{ name: "convertTemperature", exports: convertTemperature },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aixyz",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "Payment-native SDK for AI Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@a2a-js/sdk": "^0.3.10",
|
|
43
|
-
"@aixyz/cli": "0.
|
|
44
|
-
"@aixyz/config": "0.
|
|
45
|
-
"@aixyz/erc-8004": "0.
|
|
43
|
+
"@aixyz/cli": "0.29.0",
|
|
44
|
+
"@aixyz/config": "0.29.0",
|
|
45
|
+
"@aixyz/erc-8004": "0.29.0",
|
|
46
46
|
"@kitajs/html": "^4.2.13",
|
|
47
47
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
48
48
|
"@next/env": "^16.1.6",
|