NAH-JS / SERVER ADAPTER
Expose agent capabilities on your server.
Mount registered agents, tools, and workflows on any server that can pass a Web Request to NAH and return its Web Response. Your app keeps its own listener, middleware, authentication, and deployment.
Register capabilities
Use createNahServer to define the endpoints your app exposes. Agent IDs, explicitly callable tools, and workflow IDs become URL segments beneath the API prefix.
import { createNahServer } from "nah-js";
import { anthropic } from "@ai-sdk/anthropic";
const server = createNahServer({
agents: {
support: {
agent: ({ requestContext }) => ({
model: anthropic("claude-sonnet-4-5"),
system: `Help the signed-in user. User: ${(requestContext as { userId: string }).userId}`,
tools: supportTools,
maxSteps: 24,
}),
session: databaseSessionStore,
events: databaseEventLog,
},
},
tools: {
lookupOrder: {
description: "Look up an order owned by the authenticated user",
inputSchema: lookupOrderSchema,
execute: (input, { context, signal }) =>
lookupOrder(input, context, signal),
},
},
workflows: { refundReview },
workflowRunStore: databaseWorkflowRunStore,
workflowRunScope: (context) => (context as { tenantId: string }).tenantId,
authenticate: async (request) => {
const user = await verifySession(request);
if (!user) return new Response("Unauthorized", { status: 401 });
return { userId: user.id, tenantId: user.tenantId };
},
});When configured, the adapter calls authenticate for each agent, tool, workflow, and OpenAPI request. Return a Response to reject the request, or return trusted context for the capability. Agent factories receive it as requestContext, direct tools receive it as context, and workflow steps can read it at context.requestContext. Workflow request JSON cannot override that trusted value.
Mounted routes
POST /api/agents/:idStart a turn, answer an approval or suspended tool, steer, or abort; returns the NAH SSE stream.GET /api/agents/:id?sessionId=…&after=…Replay missed events and follow the session stream.POST /api/tools/:id/executeValidate input against the registered Zod schema and execute the explicitly exposed tool.POST /api/workflows/:id/runsStart a workflow; suspended runs are stored when workflowRunStore is configured.GET /api/workflows/:id/runsList suspended runs in the caller scope.GET /api/workflows/:id/runs/:runIdRead a suspended checkpoint.POST /api/workflows/:id/runs/:runId/resumeResume a suspended run with { resumeData }.GET /api/openapi.jsonReturn the OpenAPI 3.1 document for this registry.GET /api/healthBasic server health response.Pass prefix to change /api. Agent streams use the existing NAH frame protocol; tools and workflows currently return JSON responses. A tool request body is the tool input itself (validated with its Zod schema); a workflow request body is { input, context }.
// Start a turn; consume the response as an SSE stream.
const response = await fetch("/api/agents/support", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ sessionId, input: "Where is order 4821?" }),
});
// Run a directly exposed tool.
const order = await fetch("/api/tools/lookupOrder/execute", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ orderId: "4821" }),
});
// Run a workflow.
const review = await fetch("/api/workflows/refundReview/runs", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ input: { orderId: "4821" } }),
});Mount in Hono or Next.js
// Hono: context.req.raw is a Web Request
app.all("/api/*", (context) => server.handle(context.req.raw));
// Next.js App Router catch-all route
// app/api/[...nah]/route.ts
export const runtime = "nodejs";
export const GET = (request: Request) => server.handle(request);
export const POST = (request: Request) => server.handle(request);Remix, SvelteKit, and Astro can also call server.handle(request) in their endpoint/resource handlers. For Express, Fastify, or Koa, add a framework adapter that translates the framework request and response to Web API objects. The server adapter does not open a port or deploy your application.
Persist sessions and events
Agent handlers accept the same session and events stores as createNahHandler. Their defaults are in-memory, which is appropriate only for a single long-lived process. Use shared stores for serverless or multi-instance deployments. The optional workflowRunStore persists suspended workflow checkpoints; configure a shared database-backed implementation to resume after a restart.
import { createMemoryWorkflowRunStore } from "nah-js";
// Local development only. Implement WorkflowRunStore with your database in production.
const localRuns = createMemoryWorkflowRunStore();
const server = createNahServer({
workflows: { refundReview },
workflowRunStore: localRuns,
// Partition records by authenticated tenant.
workflowRunScope: (context) => (context as { tenantId: string }).tenantId,
authenticate: authenticateRequest,
});After restart, list suspended runs with GET /api/workflows/refundReview/runs, inspect one with GET /api/workflows/refundReview/runs/:runId, and resume it with POST /api/workflows/refundReview/runs/:runId/resume and a { "resumeData": ... } body. The resume request is authenticated again and its trusted context is passed to workflow steps. Recreate application services in your workflow closures; request context and runtime objects are not saved in checkpoints.