@agentick/express
Express middleware for Agentick Gateway.
This is a thin adapter (~50 lines) that delegates all business logic to @agentick/gateway. Use this when you want to integrate Agentick into an existing Express application.
Installation
npm install @agentick/express
pnpm add @agentick/express
Quick Start
import express from "express";
import { createAgentickMiddleware } from "@agentick/express";
import { createApp, Model, System, Timeline } from "@agentick/core";
const app = express();
app.use(express.json());
const AssistantAgent = () => (
<>
<Model model={gpt4} />
<System>You are a helpful assistant.</System>
<Timeline />
</>
);
const agentickApp = createApp(<AssistantAgent />);
const agentick = createAgentickMiddleware({
apps: { assistant: agentickApp },
defaultApp: "assistant",
});
app.use("/api", agentick);
const server = app.listen(3000);
process.on("SIGTERM", async () => {
await agentick.gateway.close();
server.close();
});
API
createAgentickMiddleware(config, options?)
Creates an Express Router that delegates to an embedded Gateway.
Returns a AgentickRouter - an Express Router with an attached .gateway property for lifecycle management.
import { createAgentickMiddleware, method } from "@agentick/express";
import { z } from "zod";
const middleware = createAgentickMiddleware({
apps: {
assistant: agentickApp,
researcher: researchApp,
},
defaultApp: "assistant",
auth: {
type: "custom",
validate: async (token) => {
const user = await verifyToken(token);
return user ? { valid: true, user } : { valid: false };
},
},
methods: {
tasks: {
list: method({
schema: z.object({ sessionId: z.string() }),
handler: async (params) => todoService.list(params.sessionId),
}),
create: method({
schema: z.object({
sessionId: z.string(),
title: z.string().min(1),
}),
handler: async (params) => todoService.create(params),
}),
},
health: async () => ({
status: "ok",
timestamp: new Date().toISOString(),
}),
},
});
AgentickRouter
The middleware returns a AgentickRouter which extends Express Router with:
interface AgentickRouter extends Router {
gateway: Gateway;
}
Use .gateway for:
- Graceful shutdown:
await middleware.gateway.close()
- Event subscriptions:
middleware.gateway.on('session:created', ...)
- Direct method invocation (advanced)
Options
interface AgentickMiddlewareOptions {
getToken?: (req: Request) => string | undefined;
}
Endpoints
The middleware exposes these HTTP endpoints:
| GET | /events | SSE stream for session events |
| POST | /send | Send message to session |
| POST | /invoke | Invoke custom method |
SSE Events Stream
const events = new EventSource("/api/events?sessionId=main&token=xxx");
events.addEventListener("content_delta", (e) => {
const data = JSON.parse(e.data);
console.log("Content:", data.delta);
});
events.addEventListener("tool_use", (e) => {
const data = JSON.parse(e.data);
console.log("Tool call:", data.name);
});
events.addEventListener("message_end", () => {
console.log("Response complete");
});
Send Message
const response = await fetch("/api/send", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
sessionId: "main",
message: "Hello!",
}),
});
Invoke Custom Method
const response = await fetch("/api/invoke", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
method: "tasks:list",
params: { sessionId: "main" },
}),
});
Custom Methods
Define methods using the method() helper for schema validation and guards:
import { method } from "@agentick/express";
import { z } from "zod";
import { Context } from "@agentick/kernel";
const middleware = createAgentickMiddleware({
apps: { assistant: agentickApp },
defaultApp: "assistant",
methods: {
ping: async () => ({ pong: true }),
tasks: {
create: method({
schema: z.object({
title: z.string().min(1),
priority: z.enum(["low", "medium", "high"]).optional(),
}),
handler: async (params) => {
const ctx = Context.get();
return todoService.create({
title: params.title,
priority: params.priority,
userId: ctx.user?.id,
});
},
}),
admin: {
delete: method({
roles: ["admin"],
schema: z.object({ id: z.number() }),
handler: async (params) => todoService.delete(params.id),
}),
},
},
},
});
Authentication
auth: {
type: "token",
token: process.env.API_TOKEN,
}
auth: {
type: "jwt",
secret: process.env.JWT_SECRET,
}
auth: {
type: "custom",
validate: async (token) => {
const decoded = await verifyJWT(token);
return { valid: true, user: { id: decoded.sub } };
},
hydrateUser: async (authResult) => {
const dbUser = await db.users.findById(authResult.user.id);
return {
id: dbUser.id,
tenantId: dbUser.tenantId,
roles: dbUser.roles,
};
},
}
Graceful Shutdown
The middleware exposes the underlying Gateway for lifecycle management:
const agentick = createAgentickMiddleware({ ... });
app.use("/api", agentick);
const server = app.listen(3000);
const shutdown = async (signal: string) => {
console.log(`${signal} received, shutting down...`);
await agentick.gateway.close();
server.close(() => {
console.log("Server closed");
process.exit(0);
});
};
process.once("SIGTERM", () => shutdown("SIGTERM"));
process.once("SIGINT", () => shutdown("SIGINT"));
Re-exports
For convenience, the package re-exports common types from @agentick/gateway:
export {
Gateway,
method,
type GatewayConfig,
type MethodDefinition,
type AuthConfig,
} from "@agentick/gateway";
Related Packages
License
MIT