lucidAGENTS
Packages

@lucid-agents/express

Express adapter for a completed Lucid HTTP runtime.

The Express adapter translates Node requests and streams to standard Web Request and Response objects, then binds the canonical route plan owned by @lucid-agents/http. It does not own a second entrypoint registry, manifest builder, task runtime, or paywall.

Installation

bun add @lucid-agents/core @lucid-agents/http @lucid-agents/express express
bun add -D @types/express

Add @lucid-agents/payments when the runtime receives x402 payments. No Express-specific payment package is required.

Basic usage

import { z } from 'zod';
import { createAgent } from '@lucid-agents/core';
import { http } from '@lucid-agents/http';
import { createAgentApp } from '@lucid-agents/express';

const runtime = await createAgent({
  name: 'my-agent',
  version: '1.0.0',
})
  .use(http())
  .addEntrypoint({
    key: 'greet',
    input: z.object({ name: z.string() }),
    async handler({ input }) {
      return { output: { message: `Hello, ${input.name}!` } };
    },
  })
  .build();

const { app } = await createAgentApp(runtime);
const server = app.listen(3000);

Always install http() before creating the app. During shutdown, close the Node server and call await runtime.close().

API reference

createAgentApp(runtime, options?)

Creates an Express app and mounts every route in runtime.http.routes.

const { app, runtime, agent, addEntrypoint } =
  await createAgentApp(runtime, options);
PropertyDescription
appConfigured Express application.
runtimeThe same completed runtime passed to the adapter.
agentThe runtime's protocol-agnostic agent core.
addEntrypointTyped delegate to runtime.entrypoints.add().

Adding an entrypoint through either API updates the canonical registry and invalidates the generated Agent Card.

CreateAgentAppOptions

type CreateAgentAppOptions = {
  beforeMount?: (app: Express) => void;
  afterMount?: (app: Express) => void;
};

Use beforeMount for middleware that must wrap agent routes. Use afterMount for additional routes or error handlers.

import cors from 'cors';
import helmet from 'helmet';

const { app } = await createAgentApp(runtime, {
  beforeMount(app) {
    app.set('trust proxy', true);
    app.use(helmet());
    app.use(cors());
  },
  afterMount(app) {
    app.get('/custom', (_request, response) => {
      response.json({ custom: true });
    });
  },
});

The adapter forwards unhandled route errors to Express next(error), so an error handler registered in afterMount can handle them.

Canonical routes

Paths below are relative to http({ basePath }). The default base path is empty.

MethodRouteDescription
GET/Landing page when enabled.
GET/healthHealth status.
GET/entrypointsDiscoverable entrypoints.
POST/entrypoints/:key/invokeInvoke an entrypoint.
POST/entrypoints/:key/streamStream SSE envelopes.
GET/.well-known/agent.jsonLegacy Agent Card path.
GET/.well-known/agent-card.jsonAgent Card.
GET/.well-known/oasf-record.jsonOASF record, or 404 when identity is not enabled.
GET/favicon.svgAgent favicon.

When the runtime includes a2a(), the same plan also mounts:

MethodRouteDescription
POST/tasksCreate and start a task.
GET/tasksList tasks owned by the access token.
GET/tasks/:taskIdRead an owned task.
POST/tasks/:taskId/cancelCancel an owned running task.
GET/tasks/:taskId/subscribeSubscribe to owned task updates over SSE.

Task creation accepts a 20–256 character Task-Access-Token header or generates a token and returns it with the task. All later task operations require that token. Task routes are omitted when a2a() is not installed.

Payments and authentication

Payments are installed on the runtime, before the adapter is created:

import { createAgent } from '@lucid-agents/core';
import { http } from '@lucid-agents/http';
import { payments, paymentsFromEnv } from '@lucid-agents/payments';
import { createAgentApp } from '@lucid-agents/express';

const runtime = await createAgent({
  name: 'paid-agent',
  version: '1.0.0',
})
  .use(payments({ config: paymentsFromEnv() }))
  .use(http())
  .addEntrypoint({
    key: 'premium',
    price: '0.01',
    async handler() {
      return { output: { result: 'premium content' } };
    },
  })
  .build();

const { app } = await createAgentApp(runtime);
app.listen(3000);

The HTTP runtime uses one authorization path for invoke, stream, and task creation. x402, MPP, SIWX entitlements, payment-policy admission, settlement, and idempotency therefore behave the same in Express, Hono, and TanStack. If both x402 and MPP are installed, each priced entrypoint must set paymentProtocol: 'x402' | 'mpp'.

Streaming and request bodies

The adapter passes request bodies through as Web streams and pipes response bodies back to Node without buffering. Do not add a JSON parser solely for Lucid routes; the canonical handlers parse their own request bodies. Application-specific middleware can still be registered in beforeMount.

Exports

export { createAgentApp } from '@lucid-agents/express';
export type { CreateAgentAppOptions } from '@lucid-agents/express';

On this page