The @radaros/transport package provides createAgentRouter() to generate a fully-featured Express router with endpoints for all your agents, teams, and workflows.
Installation
npm install @radaros/transport express
pnpm add @radaros/transport express
yarn add @radaros/transport express
Quick Start
Explicit wiring
Pass agents, teams, and workflows by name:
import express from "express";
import { Agent, openai } from "@radaros/core";
import { createAgentRouter } from "@radaros/transport";
const assistant = new Agent({
name: "assistant",
model: openai("gpt-4o"),
instructions: "You are a helpful assistant.",
});
const app = express();
app.use(express.json());
const router = createAgentRouter({
agents: { assistant },
});
app.use("/api", router);
app.listen(3000);
Auto-discovery (zero-wiring)
Agents, teams, and workflows auto-register into a global registry when instantiated. The transport layer reads from this registry dynamically — entities created after the server starts are immediately available.
import express from "express";
import { Agent, openai } from "@radaros/core";
import { createAgentRouter } from "@radaros/transport";
const app = express();
app.use(express.json());
app.use("/api", createAgentRouter()); // no agents/teams/workflows needed
// Agents created anywhere auto-register and become available
new Agent({ name: "assistant", model: openai("gpt-4o") });
new Agent({ name: "analyst", model: openai("gpt-4o-mini") });
app.listen(3000);
// POST /api/agents/assistant/run ✓
// POST /api/agents/analyst/run ✓
You can also pass a mixed array via serve:
const router = createAgentRouter({
serve: [assistant, analyst, researchTeam, pipeline],
});
This creates the following endpoints:
| Method | Path | Description |
|---|
| POST | /api/agents/:name/run | Run agent, return JSON response |
| POST | /api/agents/:name/stream | Stream response via Server-Sent Events |
| POST | /api/teams/:name/run | Run team |
| POST | /api/teams/:name/stream | Stream team via SSE |
| POST | /api/workflows/:name/run | Run workflow |
| GET | /api/agents | List registered agents with metadata |
| GET | /api/teams | List registered teams |
| GET | /api/workflows | List registered workflows |
| GET | /api/registry | List all registered names |
| GET | /api/tools | List available tools (when toolkits/toolLibrary configured) |
| GET | /api/tools/:name | Get single tool detail |
| GET | /api/admin/mcp | List MCP servers (when admin enabled) |
| POST | /api/admin/mcp | Add + connect an MCP server |
| GET | /api/admin/toolkits | List toolkit catalog |
RouterOptions
Map of named agents to expose. Each agent gets /agents/:name/run and /agents/:name/stream endpoints.
Map of named teams. Each gets /teams/:name/run and /teams/:name/stream endpoints.
Map of named workflows. Each gets /workflows/:name/run endpoint.
Mixed array of Agent, Team, and Workflow instances. Automatically classified and registered. An alternative to passing agents, teams, and workflows separately.
Controls live auto-discovery. Defaults to the global registry (all auto-registered entities are available). Pass a custom Registry instance, or false to disable auto-discovery and only serve explicitly passed entities.
Express middleware applied to all routes (e.g., auth, rate limiting).
fileUpload
boolean | FileUploadOptions
Toolkit instances whose tools are exposed via GET /tools. Useful for UI tool discovery.
Named tools exposed via GET /tools. Merged with toolkit tools (explicit entries take precedence).
admin
boolean | { mcpManager?: MCPManager }
Enable admin routes under /admin for managing MCP servers and the toolkit catalog at runtime.
Pass true to use defaults, or provide a shared MCPManager instance. See MCP Admin.
JSON Request
curl -X POST http://localhost:3000/api/agents/assistant/run \
-H "Content-Type: application/json" \
-d '{"input": "What is TypeScript?"}'
With Session
curl -X POST http://localhost:3000/api/agents/assistant/run \
-H "Content-Type: application/json" \
-d '{"input": "What was my last question?", "sessionId": "user-123"}'
With API Key
curl -X POST http://localhost:3000/api/agents/assistant/run \
-H "Content-Type: application/json" \
-H "x-openai-api-key: sk-your-key" \
-d '{"input": "Hello"}'
Run Response
{
"text": "TypeScript is a typed superset of JavaScript...",
"toolCalls": [],
"usage": {
"promptTokens": 25,
"completionTokens": 150,
"totalTokens": 175
},
"structured": null,
"durationMs": 1234
}
Structured Output Response
When an agent has structuredOutput configured, the structured field contains the parsed and validated JSON object:
{
"text": "{\"summary\":\"AI agents have advanced...\",\"keyPoints\":[...],\"confidence\":0.85}",
"toolCalls": [],
"usage": {
"promptTokens": 82,
"completionTokens": 129,
"totalTokens": 211
},
"structured": {
"summary": "AI agents have advanced significantly in 2026...",
"keyPoints": [
"Diagnostic accuracy improved by 20% with AI imaging tools.",
"Customer service bots handle 70% of inquiries."
],
"confidence": 0.85
},
"durationMs": 3026
}
Stream Response (SSE)
curl -X POST http://localhost:3000/api/agents/assistant/stream \
-H "Content-Type: application/json" \
-d '{"input": "Tell me a joke"}'
data: {"type":"text","text":"Why"}
data: {"type":"text","text":" did"}
data: {"type":"text","text":" the"}
...
data: {"type":"finish","finishReason":"stop"}
List Endpoints
When auto-discovery is enabled (default), the router exposes list endpoints with rich metadata:
# List all agents with model, provider, tools
curl http://localhost:3000/api/agents
[
{
"name": "assistant",
"model": "gpt-4o",
"provider": "openai",
"tools": ["calculate"],
"hasStructuredOutput": false
}
]
# List all registered names
curl http://localhost:3000/api/registry
{
"agents": ["assistant", "analyst"],
"teams": ["research"],
"workflows": ["pipeline"]
}
Full Example with Multiple Agents
import express from "express";
import { Agent, openai, google, defineTool } from "@radaros/core";
import { createAgentRouter } from "@radaros/transport";
import { z } from "zod";
const calculator = defineTool({
name: "calculate",
description: "Evaluate a math expression",
parameters: z.object({ expression: z.string() }),
execute: async ({ expression }) => String(eval(expression)),
});
// Agents auto-register into the global registry
const assistant = new Agent({
name: "assistant",
model: openai("gpt-4o"),
instructions: "You are a helpful assistant.",
tools: [calculator],
});
const analyst = new Agent({
name: "analyst",
model: openai("gpt-4o-mini"),
instructions: "You analyze data and provide insights.",
structuredOutput: z.object({
summary: z.string(),
sentiment: z.enum(["positive", "negative", "neutral"]),
confidence: z.number(),
}),
});
const vision = new Agent({
name: "vision",
model: google("gemini-2.5-flash"),
instructions: "You analyze images and describe what you see.",
});
const app = express();
app.use(express.json());
// Auto-discovery: no need to pass agents explicitly
const router = createAgentRouter({
swagger: {
enabled: true,
title: "My AI API",
description: "Multi-agent API powered by RadarOS",
},
fileUpload: true,
});
app.use("/api", router);
app.listen(3000, () => {
console.log("API running on http://localhost:3000");
console.log("Swagger UI at http://localhost:3000/api/docs");
});
Adding Custom Middleware
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization;
if (!token) return res.status(401).json({ error: "Unauthorized" });
next();
};
const router = createAgentRouter({
agents: { assistant },
middleware: [authMiddleware],
});
Error Handling
import { errorHandler } from "@radaros/transport";
app.use("/api", router);
app.use(errorHandler);
The errorHandler middleware catches errors and returns structured JSON responses:
{
"error": "Agent \"unknown\" not found"
}