> ## Documentation Index
> Fetch the complete documentation index at: https://inkbox.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Voice AI external connections

> Connect external MCP tools, prompts, resources, and skills to your voice identity.

Attach up to three MCP servers to one identity. Each connection has its own
authentication and optional tool allowlist. Check `mcp_supported` in the
[voice catalog](https://inkbox.ai/docs/capabilities/phone/hosted-call-agent.md#choosing-a-voice) before
selecting a voice. Saved connections remain when you choose an unsupported voice,
but that voice cannot use their tools or context.

Connection credentials are write-only. The voice service receives access tokens
for tool execution. OAuth refresh tokens and client secrets stay with Inkbox.
External tools run automatically under those credentials in either
data-access mode. Changes apply to future calls. Deletion, interruption, and call
termination cannot undo an external action that has already started.

## Endpoints

All paths below start with `/api/v1/phone/hosted-agent-config/mcp-servers`.

| Method | Path | Purpose |
| - | - | - |
| GET | collection | List connections |
| POST | collection | Create a connection; returns 201 |
| GET | `/{connection_id}` | Read configuration and preparation status |
| PATCH | `/{connection_id}` | Update selected settings |
| DELETE | `/{connection_id}` | Delete a connection; returns 204 |
| POST | `/{connection_id}/test` | Test discovery without running tools |
| POST | `/{connection_id}/refresh` | Queue a refresh; returns 202 |
| GET | `/{connection_id}/catalog` | Browse tools, prompts, resources, and skills |
| POST | `/{connection_id}/oauth/authorizations` | Request browser approval; returns 201 |
| GET | `/{connection_id}/oauth/authorizations/{transaction_id}` | Read approval status |
| DELETE | `/{connection_id}/oauth/authorizations/{transaction_id}` | Cancel unfinished approval; returns 204 |
| POST | `/{connection_id}/oauth/disconnect` | Disconnect the external account and retain settings |

Authenticate with a claimed agent API key, an admin API key, or identity OAuth.
Agent credentials target their own identity. Admin callers provide
`agent_identity_id` when listing or creating connections. Create and refresh
requests require `Idempotency-Key`; reuse the same key when retrying the same request.
Starting OAuth authorization and disconnecting also require this header and a JSON
body containing `expected_version`.

## Configuration

| Field | Meaning |
| - | - |
| `label` | Unique display label within the identity, up to 64 characters |
| `url` | Public HTTPS endpoint without embedded credentials or fragments |
| `transport` | `streamable_http` (default) or `sse` |
| `enabled` | Defaults to true; disabled connections still count toward the limit |
| `auth` | No auth, bearer token, one custom authentication header, or OAuth |
| `allowed_tools` | `null` allows all tools, including later additions; otherwise a nonempty list of unique names |
| `context` | `{"mode":"all"}` enables automatic access to discovered prompts, resources, and supported skills; otherwise explicit selections apply |

Authentication forms:

```json theme={null}
{"type": "none"}
```

```json theme={null}
{"type": "bearer", "token": "YOUR_TOKEN"}
```

```json theme={null}
{"type": "header", "header_name": "X-API-Key", "value_prefix": "", "token": "YOUR_TOKEN"}
```

```json theme={null}
{"type": "oauth", "scopes": ["read"]}
```

Read responses include `auth.type`, `auth.configured`, `auth.header_name`, and
`auth.value_prefix`. They never include tokens or client secrets. A manually
supplied bearer token works until it expires; automatic renewal requires OAuth.

PATCH requires `expected_version` from the last read. Omitted fields are preserved.
Changing an authenticated endpoint requires a complete replacement `auth` object,
even for a different path on the same host. Use `auth: {"type": "none"}` to remove
authentication explicitly. Only `allowed_tools` accepts null.

Preparation is asynchronous. Read `preparation_status` (`pending`, `ready`, or
`error`) and `health_code`. Only ready connections are available to new calls.
`capabilities`, `catalog_version`, and `catalog_fetched_at` describe discovery.
Refresh does not change tools already admitted to an active call.

Conflicts such as a fourth connection, duplicate label, or stale version return
409\. Invalid settings return 422. An inaccessible connection returns 404.

## SDK and CLI

Requires version **0.7.24** or later.

```python Python theme={null}
import getpass
from inkbox import Inkbox

client = Inkbox()
identity = client.get_identity("my-agent")
connection = identity.create_mcp_server(
    {
        "label": "Calendar",
        "url": "https://tools.example.com/mcp",
        "auth": {"type": "bearer", "token": getpass.getpass("MCP token: ")},
        "allowed_tools": ["find_availability"],
    },
    idempotency_key="calendar-setup-1",
)
print(connection.id, connection.preparation_status)
```

```typescript TypeScript theme={null}
import { Inkbox } from "@inkbox/sdk";

const client = new Inkbox();
const identity = await client.getIdentity("my-agent");
const connection = await identity.createMcpServer({
  label: "Calendar",
  url: "https://tools.example.com/mcp",
  auth: { type: "bearer", token: process.env.MCP_TOKEN! },
  allowedTools: ["find_availability"],
}, { idempotencyKey: "calendar-setup-1" });
console.log(connection.id, connection.preparationStatus);
```

```bash CLI theme={null}
# connection.json uses SDK names such as allowedTools and expectedVersion.
inkbox phone hosted-agent mcp create -i my-agent \
  --file connection.json --idempotency-key calendar-setup-1
inkbox phone hosted-agent mcp list -i my-agent
inkbox phone hosted-agent mcp test CONNECTION_ID
inkbox phone hosted-agent mcp catalog CONNECTION_ID
inkbox phone hosted-agent mcp refresh CONNECTION_ID --idempotency-key refresh-1
```

The CLI accepts `--file -` to read JSON from stdin. Put tokens in the input file or
stdin, not command arguments. Python uses `client.hosted_agent` and TypeScript uses
`client.hostedAgent` for item methods: get, update, delete, test, refresh, catalog,
and activity. Rust exposes the corresponding methods on `client.hosted_agent()`.

## Connect an external account with OAuth

1. Choose **OAuth** when adding or editing an external connection in the
   [Inkbox Console](https://inkbox.ai/console).
2. Save the connection and wait for authentication setup to finish.
3. Select **Connect**, review the identity, server, and requested permissions,
   then continue to the external service to approve access.
4. Return to the connection settings and check both authorization and catalog
   preparation. The connection becomes usable when both are ready.

Approval grants access to the selected identity's voice agent. It does not grant
access separately to each phone caller. An API key can request an approval URL,
but a signed-in person must approve the connection in their browser.

OAuth setup supports the following optional fields:

| Field | Meaning |
| - | - |
| `issuer` | Exact authorization-server URL; required when discovery offers several choices |
| `scopes` | Requested permission names; omitted scopes use the service's advertised guidance |
| `client_id` | Client ID from an application registered with the external service |
| `client_secret` | Write-only secret for a confidential client |
| `token_endpoint_auth_method` | `none` (default), `client_secret_basic`, or `client_secret_post` |
| `profile` | `standard` (default), or `offline_consent` for a service that requires this supported profile |

Inkbox uses a supplied client ID first, then advertised client metadata registration,
then advertised dynamic registration. If setup reports `client_configuration_required`,
register an application with the external service using the exact `auth.callback_uri`
shown on the connection. Save the resulting client ID and, when needed, client secret.
Application approval and account permissions depend on that external service.

Supported flows use authorization codes with S256 PKCE and bearer access tokens.
Access tokens must report a lifetime greater than 150 seconds.
Implicit, password, device, client-credentials, request-signing, and proof-bound
token flows are unsupported. A service must support the required discovery and
resource parameters; OAuth support alone does not establish compatibility.

### SDK and CLI authorization

After creating an OAuth connection and waiting for setup, request a browser URL:

```python Python theme={null}
connection = client.hosted_agent.get_mcp_server(connection.id)
approval = client.hosted_agent.start_mcp_authorization(
    connection.id,
    expected_version=connection.config_version,
    idempotency_key="calendar-connect-1",
)
print(approval["connect_url"])
status = client.hosted_agent.get_mcp_authorization(connection.id, approval["id"])
print(status["status"])
```

```typescript TypeScript theme={null}
const current = await client.hostedAgent.getMcpServer(connection.id);
const approval = await client.hostedAgent.startMcpAuthorization(connection.id, {
  expectedVersion: current.configVersion,
  idempotencyKey: "calendar-connect-1",
});
console.log(approval.connectUrl);
const status = await client.hostedAgent.getMcpAuthorization(connection.id, approval.id);
console.log(status.status);
```

```bash CLI theme={null}
inkbox phone hosted-agent mcp oauth connect CONNECTION_ID \
  --expected-version 1 --idempotency-key calendar-connect-1 --wait 120
inkbox phone hosted-agent mcp oauth status CONNECTION_ID TRANSACTION_ID
inkbox phone hosted-agent mcp oauth cancel CONNECTION_ID TRANSACTION_ID
inkbox phone hosted-agent mcp oauth disconnect CONNECTION_ID \
  --expected-version 2 --idempotency-key calendar-disconnect-1
```

Rust exposes `start_mcp_authorization`, `get_mcp_authorization`,
`cancel_mcp_authorization`, and `disconnect_mcp_server` on `client.hosted_agent()`.
TypeScript and CLI configuration use `clientId`, `clientSecret`, and
`tokenEndpointAuthMethod`; Python and the API use snake\_case.
Supply client secrets through the configuration file or stdin, never CLI arguments.

Authorization responses include `status`, `expires_at`, `requested_scopes`,
`error_code`, and the resulting `config_version` after completion. A new intent
expires after ten minutes. A connection edit during approval requires starting again.
The CLI can wait for up to 600 seconds; it never completes browser consent for you.

### Expiry, reconnect, and disconnect

OAuth connection views expose `auth.status`, `auth.expires_at`,
`auth.refresh_supported`, `auth.requested_scopes`, `auth.granted_scopes`, and
`auth.error_code`. `auth.configured` means an account was connected, not that its
authorization is currently usable. Catalog preparation has a separate status.

Inkbox renews refreshable credentials when needed. When renewal is unavailable,
select **Reconnect** and approve access again. A reconnect applies to future calls;
an active call never switches to a different external account or expanded permissions.
Missing authorization makes the connection unavailable for new tool requests.
If safe credential replacement fails during a call, the affected voice session
may stop. Failed or uncertain external actions are not automatically repeated.

Disabling excludes future calls but retains authorization. Disconnecting or deleting
an OAuth connection stops new credential issuance and renewal. It cannot retract
credentials already sent to the voice service or undo an action already started.
You can also revoke access directly at the external service.

## Automatic capabilities and selected context

Tools, prompts, resources, and skills are separate capabilities. List cached
metadata through `/catalog`; pass its `next_cursor` as `cursor` for the next page.
The page limit is 1–100, default 50.

The Console enables all tools, prompts, resources, and supported skills when you
save a connection. You do not need to enter tool names or context JSON. Existing
custom selections remain until you edit and save that connection.

For the same behavior through the API, send:

```json theme={null}
{
  "allowed_tools": null,
  "context": {"mode": "all"}
}
```

The voice agent discovers available context and loads it when needed. It can
retrieve prompts with arguments supplied during the conversation, read text
resources, and load instruction-only skills without individual approvals.
New capabilities become available to future calls after the catalog refreshes.
Each call retains its admitted context catalog. Skill files are verified against
the manifest loaded for that call; automatic access does not execute local code.

Omitting `context.mode`, or setting it to `selected`, keeps explicit selections.
Do not combine `mode: "all"` with prompt, resource, or skill selections.

Set `context.prompts` to `[{"name":"briefing","arguments":{"team":"support"}}]`
and `context.resources` to explicitly selected resource URIs. Prompt roles are
preserved. Selected context supplements the call's instructions without expanding
permissions.

In selected mode, inspect a skill with `/catalog?skill_uri=...`. Approve it by placing its `uri` and
returned `manifest_digest` in `context.skills`. Instructions and supporting files
load only when needed and must match the approved manifest. Changes require a new
approval. Only instruction workflows are supported; dynamic content, local code
execution, and executable skill environments are unavailable.

Once a skill loads, file reads stay on that skill's server for the rest of the
call. Tools from all connected servers remain available. Reading a nested skill as
a supporting file does not activate it.

## External call activity

Use [`GET /phone/calls/{call_id}/tool-invocations`](https://inkbox.ai/docs/api/phone/calls#list-voice-ai-tool-activity) for a shared history of built-in and remote MCP tools, including source, connection label, and server URL. The existing `GET /api/v1/phone/calls/{call_id}/mcp-activity` endpoint remains available as an MCP-only projection of that same history and returns a page with `items`,
`limit`, `offset`, `has_more`, and `incomplete`. Each item contains the connection
label, tool name, status, safe error code, and available timestamps. Status is
`started`, `succeeded`, `failed`, or `unknown`. Arguments and result bodies are not
included. Limit is 1–200, default 50.

`incomplete: true` means additional external activity may have occurred. An empty
page does not prove that no tools ran. Confirm an unknown operation with your
external service before repeating it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.