Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.svantic.com/llms.txt

Use this file to discover all available pages before exploring further.

Attach to Express

Svantic agents don’t have to live in their own process. When you already have an Express service, you can mount an agent into it with a single call. This guide covers the attach() helper, which wires the A2A endpoints, the Svantic connection, and any declared triggers in one go.

When to use this

  • You have an existing HTTP service and you want it to be callable from Svantic in addition to its normal duties.
  • You want to share middleware, logging, and process lifecycle with that service.
  • You want Svantic traffic to go through your own ingress for auditing.
If you’re starting fresh, new Agent({...}).start() is simpler. Use attach when there’s already an app to embed into.

Minimal example

import express from 'express';
import { Agent, attach } from '@svantic/sdk';

const app = express();
app.use(express.json());

const agent = new Agent({
  name: 'orders',
  description: 'Order-management agent.',
  public_url: 'https://api.acme.com',
});

agent.define_capability({
  name: 'fetch_order',
  description: 'Fetch an order by id.',
  parameters: {
    type: 'object',
    properties: { order_id: { type: 'string' } },
    required: ['order_id'],
  },
  handler: async ({ order_id }) => {
    return db.orders.find(order_id);
  },
});

await attach(app, {
  agent,
  mesh: {
    client_id: process.env.SVANTIC_CLIENT_ID!,
    client_secret: process.env.SVANTIC_CLIENT_SECRET!,
  },
});

app.listen(4000);
attach returns an AgentHandle you can use to emit events or detach cleanly:
const handle = await attach(app, { /* … */ });

// from any request handler elsewhere in the app:
handle.emit('order_shipped', { order_id });

// on process teardown (attach also installs its own SIGTERM / SIGINT handlers):
await handle.detach();

What attach does

  1. Calls agent.expose(app) — mounts /.well-known/agent-card.json and /send on your Express app.
  2. Mounts each declared trigger (see Triggers).
  3. If mesh is provided, creates a MeshConnector, authenticates, registers the agent, and opens the WebSocket (connected mode) or leaves dispatches to hit public_url/send (hosted mode).
  4. Installs SIGTERM / SIGINT handlers that gracefully deregister.
  5. Returns the handle.

Skipping the mesh

Omit mesh during local development to mount the agent without talking to Svantic at all — useful when running A2A clients directly against your local app:
await attach(app, { agent }); // no mesh registration

Adding triggers

Register triggers on the agent with agent.add_triggers() before calling attach(). Hand-written or Forge-generated — each trigger carries a prompt template with {{placeholders}}:
agent.add_triggers([
  {
    type: 'webhook',
    path: '/hooks/stripe',
    prompt: 'A payment of {{amount}} just settled; update the order.',
    payload_map: { amount: 'data.object.amount_total' },
  },
  {
    type: 'schedule',
    cron: '0 9 * * MON-FRI',
    prompt: 'Email ops the overnight order summary.',
  },
]);

await attach(app, { agent, mesh: { /* … */ } });
The Triggers guide walks through every kind with worked examples.

Customizing how triggers dispatch

By default, triggers create a Svantic session and send the interpolated prompt to the agent. Override with agent.on_trigger:
agent.on_trigger = async (ctx) => {
  console.log('Custom handler:', ctx.prompt);
  // ctx has: prompt, trigger, payload, mesh
  // return { skip: true } to block delivery
  // return { prompt: 'override' } to change the prompt
  // return { metadata: {...} } to attach metadata
};

Custom endpoint path

agent.expose() defaults to /send. To mount on a different path, use expose() directly instead of attach():
agent.expose(app, '/v2/invoke');
// then use MeshConnector separately for the Svantic connection

See also