> ## 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.

# Agents

# Agents

An **agent** is a service that exposes capabilities to the Svantic mesh. Your ticket system, your billing API, your monitoring service — wrap it with the SDK and it becomes an agent that the mesh can discover, invoke, and orchestrate.

## How Agents Work

1. **Your agent registers** with the mesh, publishing its capabilities
2. **Users or triggers** send requests to the mesh
3. **The mesh plans** which agents and capabilities to call
4. **Your agent executes** the capability and returns the result
5. **The mesh returns** the final response

You write the business logic. The mesh handles routing, planning, and orchestration.

## Capabilities

A capability is a single thing your agent can do. Each has:

* **Name** — What the mesh calls it (`get_ticket`, `create_order`)
* **Description** — The LLM reads this to decide when to use it
* **Parameters** — JSON Schema defining the inputs
* **Handler** — Your async function that does the work

```typescript theme={null}
agent.define_capability({
  name: 'get_ticket',
  description: 'Look up a support ticket by ID. Returns subject, status, priority.',
  parameters: {
    type: 'object',
    properties: {
      ticket_id: { type: 'number', description: 'The ticket ID' },
    },
    required: ['ticket_id'],
  },
  handler: async (args) => {
    const ticket = await db.find(args.ticket_id);
    if (!ticket) throw new Error(`Ticket ${args.ticket_id} not found`);
    return ticket;
  },
});
```

Write descriptions that are specific about inputs, outputs, and when to use the capability. The mesh reads these to decide which capability to call.

<Info>
  For a comprehensive guide on defining capabilities, MCP servers, builtin integrations, and bidirectional execution, see **[Tools & Capabilities](/concepts/tools-and-capabilities)**.
</Info>

## Where Agents Run

Agents run on **your infrastructure** — your cloud, your VPC, your laptop. The mesh coordinates them but never sees your raw data. Your code, credentials, and databases stay with you.

This is what makes Svantic usable in regulated industries. The mesh sees capability names and results, not the underlying data or logic.

## Multiple Agents Together

The mesh can orchestrate multiple agents in a single workflow:

> "Check if customer acme-123 has overdue invoices and create a support ticket if they do."

The mesh might:

1. Call **billing-agent** → `get_overdue_invoices`
2. Call **ticket-agent** → `create_ticket`
3. Call **slack-agent** → `send_notification`

You don't write this orchestration logic. Each agent exposes its capabilities. The mesh figures out the plan.

***

## Connectivity

Every dispatch the mesh sends must reach your agent. Svantic supports two connectivity modes:

* **Connected** (default) — your agent dials Svantic over a persistent WebSocket. Works anywhere outbound HTTPS/WSS works — behind firewalls, NATs, corporate networks, laptops, CI runners, or ephemeral containers.
* **Hosted** — your agent exposes a public HTTPS surface the mesh calls into. Opt in when your agent already runs as an HTTP service that can accept inbound traffic.

You choose per `instance_id`. Two instances of the same agent type can use different modes.

### Connected mode (default)

<img src="https://mintcdn.com/svantic/DQEVJ_RnVW5_l6gu/images/diagrams/connected-mode.svg?fit=max&auto=format&n=DQEVJ_RnVW5_l6gu&q=85&s=d084e6a3692077a863b0119732013119" alt="Connected mode: agent dials Svantic via WebSocket, dispatches pushed bidirectionally" width="640" height="260" data-path="images/diagrams/connected-mode.svg" />

You get connected mode by calling `new MeshConnector(agent, { svantic_url, client_id, client_secret })` with no `public_url`. The SDK dials a WebSocket and keeps it open. The mesh pushes `dispatch` frames down the socket; your agent pushes `dispatch_result` frames back.

**When it's right (almost always):**

* Behind a firewall, NAT, or corporate proxy
* Developer laptop, CI runner, ephemeral container
* Zero inbound exposure — outbound-only is a compliance win
* Easiest onboarding: no ingress, no TLS cert, no reverse proxy

### Hosted mode

<img src="https://mintcdn.com/svantic/DQEVJ_RnVW5_l6gu/images/diagrams/hosted-mode.svg?fit=max&auto=format&n=DQEVJ_RnVW5_l6gu&q=85&s=f3cac9f22aac34d57a59fec494b62bbb" alt="Hosted mode: Svantic sends HTTPS dispatch to agent, agent returns response" width="640" height="180" data-path="images/diagrams/hosted-mode.svg" />

Opt in by passing a `public_url` at registration. Every dispatch is an HTTPS POST from the mesh to `{public_url}/send`.

**When to pick it:**

* Your agent already runs as a long-lived HTTP service with inbound traffic
* You have an existing ingress, LB, or API gateway
* You prefer request/response and control the infra on both sides

**Requirements:** Publicly reachable HTTPS URL, `/.well-known/agent-card.json` served at the root, TLS terminated by you.

### Quick guide

| You have…                             | Pick        |
| ------------------------------------- | ----------- |
| A public HTTPS service                | `hosted`    |
| Corporate firewall, egress-only       | `connected` |
| Ephemeral containers (Fly, Vercel CI) | `connected` |
| Kubernetes pod with Ingress           | `hosted`    |
| Developer laptop                      | `connected` |

***

## Health & Capacity

Svantic monitors two independent signals for every registered agent instance: **routing status** and **connection status**.

### Routing Status

Determines whether Svantic will send tasks to an agent instance:

| Status      | Routable? | Meaning                                       |
| ----------- | --------- | --------------------------------------------- |
| `available` | Yes       | Healthy and has capacity                      |
| `unknown`   | Yes       | Newly registered, not yet confirmed           |
| `busy`      | No        | Reached concurrent session limit              |
| `unhealthy` | No        | Missed heartbeats or failed recent dispatches |
| `offline`   | No        | Explicitly marked down                        |

Agents send periodic heartbeats. If an agent stops heartbeating, Svantic marks it `unhealthy` and stops routing. After repeated failures, it's automatically deregistered.

### Capacity

Each agent can declare a `max_concurrent_sessions` limit at registration:

* **0 (default)**: unlimited
* **N > 0**: Svantic tracks active sessions and stops routing new work once the limit is reached

When capacity frees up (sessions close), the agent becomes `available` again. Among available agents, Svantic prefers the least-loaded instance.

### Connection Status

Reflects the transport link health — drives dashboard indicators:

| Status     | Meaning                             |
| ---------- | ----------------------------------- |
| `unknown`  | Newly registered                    |
| `online`   | Live WebSocket connection open      |
| `healthy`  | Recent dispatch succeeded           |
| `degraded` | At least one recent dispatch failed |
| `offline`  | Agent is unreachable                |

### Health Events

| Event                  | When                             |
| ---------------------- | -------------------------------- |
| `agent.registered`     | New instance registered          |
| `agent.connected`      | WebSocket connection established |
| `agent.disconnected`   | WebSocket connection closed      |
| `agent.health_changed` | Routing status changed           |
| `agent.deregistered`   | Instance removed                 |

***

## Registration

Registration tells Svantic what your agent can do. Once registered, the mesh can discover your capabilities and route work to your agent.

### Quick Start (SDK)

```bash theme={null}
SVANTIC_CLIENT_ID=your-client-id \
SVANTIC_CLIENT_SECRET=your-client-secret \
node my-agent.js
```

```typescript theme={null}
import { Agent } from '@svantic/sdk';
import { MeshConnector } from '@svantic/sdk/mesh';

const agent = new Agent({
	name: 'my-service',
	description: 'Describe what your agent does — the AI reads this.',
});

agent.define_capability({
	name: 'analyze_data',
	description: 'Run statistical analysis on a dataset',
	parameters: {
		type: 'object',
		properties: {
			dataset_id: { type: 'string', description: 'Dataset identifier' },
			analysis_type: { type: 'string', description: 'mean, median, regression, or correlation' },
		},
		required: ['dataset_id', 'analysis_type'],
	},
	handler: async (args) => {
		return { result: 'analysis complete' };
	},
});

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();
```

The SDK handles authentication, registration, and connectivity automatically.

### Registration Policies

Configurable in the dashboard under **Settings → Agent Policy**:

| Mode               | Behavior                                                     |
| ------------------ | ------------------------------------------------------------ |
| **Open** (default) | Any agent type can register                                  |
| **Allow-list**     | Only pre-approved agent types can register                   |
| **Audit**          | All types register, but unknown types are flagged for review |

### Agent Card

Every agent publishes an **Agent Card** at `/.well-known/agent-card.json`. The SDK generates this from your `define_capability` calls.

```json theme={null}
{
  "name": "my-service",
  "description": "Run statistical analysis on datasets",
  "url": "https://my-agent.example.com/send",
  "version": "1.0.0",
  "capabilities": { "streaming": false },
  "skills": [
    {
      "id": "analyze_data",
      "name": "analyze_data",
      "description": "Run statistical analysis on a dataset",
      "parameters": {
        "type": "object",
        "properties": {
          "dataset_id": { "type": "string" },
          "analysis_type": { "type": "string" }
        },
        "required": ["dataset_id", "analysis_type"]
      }
    }
  ]
}
```

**Best practices:** kebab-case names, AI-readable descriptions, one skill per function, JSON Schema with descriptions on every property, semver versioning.

### Manual Registration (non-SDK)

If you're not using the SDK (e.g. Python, Go):

**1. Authenticate:**

```bash theme={null}
curl -X POST https://api.svantic.com/auth/get_token \
  -H "Content-Type: application/json" \
  -d '{ "client_id": "your-client-id", "client_secret": "your-client-secret" }'
```

**2. Register:**

```bash theme={null}
curl -X POST https://api.svantic.com/agents/register \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{ "agent_type": "my_agent", "instance_id": "pod-1" }'
```

Add `"public_url"` and `"deployment_mode": "hosted"` only if your agent is publicly reachable.

### Managing Agents

```bash theme={null}
# List registered agents
curl -X POST https://api.svantic.com/agents/list \
  -H "Authorization: Bearer $TOKEN"

# Deregister
curl -X POST https://api.svantic.com/agents/deregister \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{ "instance_id": "pod-1" }'
```

Deregistration removes the instance from routing and cleans up any active session bindings.

### Environment Variables

| Variable                | Purpose                                 | Required                       |
| ----------------------- | --------------------------------------- | ------------------------------ |
| `SVANTIC_CLIENT_ID`     | API client ID                           | Yes                            |
| `SVANTIC_CLIENT_SECRET` | API client secret                       | Yes                            |
| `SVANTIC_INSTANCE_ID`   | Stable instance ID (for containers/k8s) | No (defaults to hostname-port) |
