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

# Connecting agents

# Connecting Agents

Your agent does not work alone. This guide covers how to discover other agents, call their capabilities, and integrate MCP tool servers so your runtime can reach peers and external tools through one mental model.

## Discovering Agents

`AgentDiscovery` fetches and caches A2A agent cards from each peer’s `/.well-known/agent-card.json` endpoint. Use it to inspect names, skills, and metadata before you open a live connection.

```typescript theme={null}
import { AgentDiscovery } from '@svantic/sdk';

const discovery = new AgentDiscovery();
const card = await discovery.discover('http://billing-agent:4000');
console.log(card.name, card.skills);
```

**Notes**

* Cards are cached for **60 seconds** by default. Pass a different TTL in milliseconds (for example `new AgentDiscovery(30_000)` for 30s), or `0` to disable caching.
* **Batch:** `discover_all([url1, url2])` resolves many agents in parallel; failed URLs are skipped and only successful cards are returned.
* **Force refresh:** `discover(url, true)` bypasses the cache for that URL once.
* **Manual invalidation:** `invalidate(url)` or `invalidate_all()` when you know a peer redeployed.

`RemoteAgent.connect()` always fetches a fresh agent card for the connection itself; discovery caching is only for your own lookups.

## Calling Remote Agents

`RemoteAgent` sends tasks to any A2A agent: natural-language messages, streaming responses, or structured capability calls.

```typescript theme={null}
import { RemoteAgent } from '@svantic/sdk';

const billing = await RemoteAgent.connect('http://billing-agent:4000');

// Natural language
const reply = await billing.send('What is customer acme-123 billed this month?');

// Structured invocation
const result = await billing.invoke_capability('get_invoice', {
  customer_id: 'acme-123',
  month: '2026-04',
});
```

**Notes**

* **Auth:** `RemoteAgent.connect(url, jwt)` adds `Authorization: Bearer <jwt>` on requests when the peer requires it.
* **Streaming:** `for await (const event of billing.send_stream('...')) { ... }` yields A2A stream events (messages, tasks, status and artifact updates).
* **Session forwarding:** pass `{ session_id, tenant_id }` as the **third** argument to `invoke_capability` when the remote agent should run in an existing session context.
* **Advanced:** `send_stream_message` accepts a full A2A `Message` (see [`MessageBuilder`](../sdk/reference/message-builder)).

Trace context propagates across `RemoteAgent` calls via W3C `traceparent` when telemetry is enabled (see [Telemetry & tracing](../sdk/guides/telemetry)).

## MCP Integration

Register MCP tool servers as **agent capabilities** so the mesh and other agents can invoke them like any other skill. The SDK spawns the server as a child process, runs the MCP handshake, and maps each tool to a capability.

```typescript theme={null}
await agent.register_mcp('chrome-devtools', {
  command: 'npx',
  args: ['chrome-devtools-mcp@latest'],
});
```

**Notes**

* Call **`register_mcp` before `expose()` or `start()`** so discovered tools appear on the agent card.
* Each MCP tool becomes a capability named `{prefix}_{tool_name}`. By default, `prefix` is the server name with hyphens replaced by underscores (for example `chrome-devtools` → `chrome_devtools_navigate`).
* **Custom prefix:** third argument `{ tool_prefix: 'browser' }` → `browser_<tool>`.
* **Environment:** `{ command: '...', args: [...], env: { API_KEY: '...' } }` is merged with `process.env` for the child.
* **Cleanup:** `agent.close()` or `agent.stop()` tears down MCP child processes.

For more patterns (filesystem, GitHub, ordering with `define_capability`), see [MCP integration](../sdk/guides/mcp-integration).

## Full example: coordinator agent

A short coordinator discovers two peers, checks their cards, then invokes structured capabilities and returns a combined payload.

```typescript theme={null}
import express from 'express';
import { Agent, AgentDiscovery, RemoteAgent } from '@svantic/sdk';

const BILLING = 'http://billing-agent:4000';
const SUPPORT = 'http://support-agent:4001';

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

const discovery = new AgentDiscovery(30_000);

const agent = new Agent({
  name: 'coordinator',
  description: 'Orchestrates billing and support agents.',
  public_url: 'https://coordinator.example.com',
});

agent.define_capability({
  name: 'customer_snapshot',
  description: 'Latest invoice plus open tickets for a customer.',
  parameters: {
    type: 'object',
    properties: {
      customer_id: { type: 'string' },
    },
    required: ['customer_id'],
  },
  handler: async (args) => {
    const cards = await discovery.discover_all([BILLING, SUPPORT]);
    if (cards.length < 2) {
      throw new Error('One or more peer agents are unreachable');
    }

    const billing = await RemoteAgent.connect(BILLING);
    const support = await RemoteAgent.connect(SUPPORT);

    const invoice = await billing.invoke_capability('get_invoice', {
      customer_id: args.customer_id,
      month: '2026-04',
    });

    const tickets = await support.invoke_capability('list_open_tickets', {
      customer_id: args.customer_id,
    });

    return { invoice, tickets };
  },
});

agent.expose(app);
app.listen(5000, () => console.log('Coordinator ready on :5000'));
```

## See also

* [`AgentDiscovery`](../sdk/reference/agent-discovery)
* [`RemoteAgent`](../sdk/reference/remote-agent)
* [`Agent.register_mcp`](../sdk/reference/agent#register_mcpserver_name-config-options)
