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.

Composing Agents

You have tool specs from multiple sources — an OpenAPI spec for Zendesk, a natural-language-generated Datadog set — and you want to combine them into a single deployable agent. The compose step merges tool specs and generates everything needed to run.

Prerequisites

  • One or more Tool Spec YAML files (generated by any Forge path)
  • The Svantic CLI installed (npm install -g @svantic/cli)

Standalone Mode: Full Project Generation

Generate a complete, self-contained agent project:
svantic forge agent --name support-bot \
  --tools zendesk.tool-spec.yaml,slack.tool-spec.yaml,datadog.tool-spec.yaml \
  --standalone
This creates a support-bot/ directory with:
FilePurpose
server.tsExpress server with all capabilities attached and triggers wired
.env.templateAll required environment variables collected from every tool spec
package.jsonDependencies (@svantic/sdk, express, etc.) with a start script
tsconfig.jsonTypeScript configuration
DockerfileMulti-stage build for production deployment
capabilities/One .ts file per domain (zendesk, slack, datadog)
triggers/Trigger handlers (webhooks, schedules) if any tool spec defines them

The generated server

import { Agent } from '@svantic/sdk';
import { MeshConnector } from '@svantic/sdk/mesh';
import { attach } from '@svantic/sdk/forge';
import { zendesk_tools } from './capabilities/zendesk';
import { slack_tools } from './capabilities/slack';
import { datadog_tools } from './capabilities/datadog';

const agent = new Agent({
  name: 'support-bot',
  description: 'Manages Zendesk tickets, Slack messages, and Datadog incidents.',
});

attach(agent, [
  ...zendesk_tools,
  ...slack_tools,
  ...datadog_tools,
]);

const mesh = new MeshConnector(agent, {
  svantic_url: process.env.SVANTIC_URL ?? 'https://api.svantic.com',
  client_id: process.env.SVANTIC_CLIENT_ID!,
  client_secret: process.env.SVANTIC_CLIENT_SECRET!,
});
await mesh.connect();
console.log('support-bot ready');
The attach() function registers all capabilities on the agent. MeshConnector dials the Svantic edge — one URL, no ingress needed. The agent runs in connected mode by default.

Run it

cd support-bot
cp .env.template .env    # fill in your API keys
npm install
npm start

Embedded Mode: Capabilities Only

Omit --standalone to generate just the capability and trigger files, without the server scaffold:
svantic forge agent --name support-bot \
  --tools zendesk.tool-spec.yaml,slack.tool-spec.yaml \
  --out ./src/generated/
This writes capabilities and triggers into ./src/generated/ so you can import them into an existing agent:
import { Agent } from '@svantic/sdk';
import { attach } from '@svantic/sdk/forge';
import { zendesk_tools } from './src/generated/zendesk';
import { slack_tools } from './src/generated/slack';

const agent = new Agent({
  name: 'my-existing-agent',
  description: 'Already running agent with new Forge-generated tools.',
});

attach(agent, [...zendesk_tools, ...slack_tools]);

Programmatic Usage

import { compose, write_composed_agent } from '@svantic/sdk/forge';

const composed = await compose({
  name: 'support-bot',
  tool_specs: [
    './tools/zendesk.tool-spec.yaml',
    './tools/slack.tool-spec.yaml',
    './tools/datadog.tool-spec.yaml',
  ],
});

console.log(composed.tools);       // Merged array of all tools
console.log(composed.env_vars);    // Collected env vars from all specs
console.log(composed.triggers);    // Merged triggers

// Write standalone project
await write_composed_agent(composed, {
  output_dir: './support-bot/',
  standalone: true,
});

// Or write embedded capabilities only
await write_composed_agent(composed, {
  output_dir: './src/generated/',
  standalone: false,
});

Collision Detection

When composing tools from multiple domains, Forge checks for duplicate tool names. If two specs define a tool with the same name, the CLI reports an error:
Error: Tool name collision detected:
  "create_ticket" exists in both zendesk.tool-spec.yaml and jira.tool-spec.yaml

Options:
  --prefix <domain>     Prefix tool names with domain (e.g. zendesk_create_ticket)
  --rename <old>=<new>  Rename a specific tool

Resolving collisions

Option 1: Auto-prefix by domain
svantic forge agent --name support-bot \
  --tools zendesk.tool-spec.yaml,jira.tool-spec.yaml \
  --prefix domain \
  --standalone
This produces zendesk_create_ticket and jira_create_ticket. Option 2: Rename specific tools
svantic forge agent --name support-bot \
  --tools zendesk.tool-spec.yaml,jira.tool-spec.yaml \
  --rename jira:create_ticket=create_jira_ticket \
  --standalone
Option 3: Edit the Tool Spec YAML Open one of the .tool-spec.yaml files and change the tool name directly, then re-run compose.

Adding a Description

Forge auto-generates an agent description from the combined tool set. Override it:
svantic forge agent --name support-bot \
  --tools zendesk.tool-spec.yaml,slack.tool-spec.yaml \
  --description "Handles customer support tickets and notifies the team via Slack" \
  --standalone

The .env.template

Forge collects every env field from every tool spec’s auth section and every trigger’s configuration, then writes a single .env.template:
# Auth — zendesk
ZENDESK_API_TOKEN=
ZENDESK_BASE_URL=https://your-domain.zendesk.com

# Auth — slack
SLACK_BOT_TOKEN=

# Auth — datadog
DATADOG_API_KEY=
DATADOG_APP_KEY=

# Svantic platform
SVANTIC_URL=https://api.svantic.com
SVANTIC_CLIENT_ID=
SVANTIC_CLIENT_SECRET=

Composing with Mixed Sources

You can compose tool specs generated from different paths in a single command. The YAML format is the same regardless of how it was produced:
svantic forge agent --name ops-bot \
  --tools \
    tools/zendesk.tool-spec.yaml,\
    tools/billing.tool-spec.yaml,\
    tools/datadog.tool-spec.yaml \
  --standalone