> Source: https://wexa.ai/docs/agents/handshake-peer-agent

# handshake-peer-agent

Ask a candidate external agent who it is and what it says it can do, without creating anything. Use it to confirm an endpoint answers before you commit a credential with [register-peer-agent](/docs/agents/register-peer-agent). A failed handshake is a result, not an error: read `ok` first. Needs the `fabric:orchestrate.read` grant.

The shape of `result` for `handshake-peer-agent` is not recorded here: it was not captured against a live deployment. Call it once and read what comes back rather than assuming a shape.

**Handshake an external agent**

**Wexa MCP server**

```bash
curl -sS -X POST https://fabric.wexa.ai/mcp/$FABRIC_PROJECT_ID \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "Mcp-Session-Id: $MCP_SESSION_ID" \
  -H "content-type: application/json" -H "accept: application/json" \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"handshake-peer-agent","arguments":{"endpoint":"https://agents.example.com/a2a/invoice-reconciler","protocol":"a2a","secret":"<token>"}}}'
```

**REST API**

```bash
curl -sS https://fabric.wexa.ai/v1/handshake-peer-agent \
  -H "authorization: Bearer $FABRIC_API_KEY" \
  -H "content-type: application/json" \
  -d '{"endpoint":"https://agents.example.com/a2a/invoice-reconciler","protocol":"a2a","secret":"<token>"}'
```

**TypeScript SDK**

```ts
import { Fabric } from '@wexa-fabric/sdk'

const fabric = new Fabric()
const result = await fabric.handshakePeerAgent({
  endpoint: 'https://agents.example.com/a2a/invoice-reconciler',
  protocol: 'a2a',
  secret: '<token>'
})
```

**Python SDK**

```python
from wexa import Fabric

fabric = Fabric()
out = fabric.handshake_peer_agent(
    endpoint="https://agents.example.com/a2a/invoice-reconciler",
    protocol="a2a",
    secret="<token>",
)
```

## Parameters

| Parameter | Type | Required | Notes |
| --- | --- | --- | --- |
| `endpoint` | string | Required | The external agent's full `http://` or `https://` URL. Non-empty. |
| `protocol` | string | Required | `mcp` or `a2a`: which protocol the external agent speaks. Any other value is refused, and the refusal names both. |
| `secret` | string | Optional | The credential. Sent as `Authorization: Bearer <secret>`; no header when empty. Used for this call only; nothing is stored. |
| `platform` | string | Optional | A label for which platform this agent runs on, recorded on the audit event. |
| `auth` | string | Optional | Only `key` works today (the default). `oauth` and `mtls` are refused: they are not supported yet. |

**More details**

An endpoint that cannot be reached, or that does not complete the handshake, is not an error: it comes back as the `ok: false` result, with the `endpoint` and `protocol` you passed and a `reason`. Nothing was created, and the call can be repeated once the endpoint, the protocol or the credential is fixed. A handshake that failed leaves nothing behind.

**An MCP external agent must expose exactly one tool.** That one tool is the agent. A server with more tools, or none, fails the handshake. A2A has no such rule: the handshake reads the external agent's Agent Card.

### Result fields

| Field | What it is |
| --- | --- |
| `ok` | Whether the external agent answered the handshake. |
| `endpoint`, `protocol` | What you passed. |
| `external_agent_name`, `declared_capability` | The name and the description the external agent gave for itself. |
| `declared_abilities` | The abilities the external agent listed for itself. Absent when it listed none. |
| `protocol_version` | The protocol version the external agent reported, when it reported one. |
| `supports_cancel` | Whether the external agent says a delegated goal can be cancelled. |

Every tool call works the same way: [How every tool call works](/docs/sdks-and-apis/how-every-tool-call-works).
