---
title: "MCP compatibility, call contract, and Tool catalog API"
description: "MCP compatibility, the synchronous call contract, fixed platform limits, and the Tool catalog API define how Duale AI connects to and invokes remote Tools."
lang: en
status: public-preview
lastUpdated: 2026-09-26
url: https://duale.ai/en/docs/tools/reference
---

## AI-generated summary

Reference for MCP server compatibility, synchronous call behavior, fixed platform limits, and the organization Tool catalog API for SDK and MCP Tool Sources.

- MCP Connections require Streamable HTTP at one public HTTPS URL with specific MCP protocol versions.
- This release supports only synchronous MCP calls returning complete text or structured results.
- After three failures for one Tool, Duale AI pauses that Connection-and-Tool pair for 30 seconds.
- The Tool catalog API uses cursor pagination and requires user tokens bound via DPoP.
- Platform limits cap remote responses at 4 MiB and accepted Tool Results at 2 MiB.

Summaries were generated by AI.

---

Use this page to check MCP server compatibility, synchronous call behavior, and fixed limits. It also documents the
organization Tool catalog API for SDK and MCP Tool Sources. For setup, [connect an MCP
server](https://duale.ai/en/docs/tools/mcp-connections.md). For status, naming, Analytics, and recovery, [operate
Tools](https://duale.ai/en/docs/tools/operations.md).

## Confirm MCP server compatibility

An MCP Connection supports Streamable HTTP at one exact public HTTPS URL. The server must negotiate MCP `2025-06-18`,
`2025-11-25`, or `2026-07-28` and answer catalog and Tool requests at that URL. Duale AI uses HTTP/1.1, does not
follow redirects, does not retain server cookies, requests an uncompressed response, and rejects a compressed
response. A server that negotiates an older MCP revision, or that depends on a redirect, cookie state, compression,
or HTTP/2, is not compatible with this release.

Choose one authentication mode: no authentication, one bearer token, or one API key in an allowed custom header. The
same credential applies to catalog checks and Tool Calls for that named Connection.

## Understand the MCP call contract

This release supports synchronous MCP calls that return a complete text or structured result. It does not continue
interactive `input_required` responses, queued MCP Tasks, or a task handle returned for later retrieval. Image, audio,
embedded resource, and resource-link result blocks are not supported.

Duale AI cannot guarantee exactly-once execution for a remote MCP Tool. When Duale AI cannot rule out that the server
received a request, it does not intentionally retry that logical call. If it also cannot prove the response, the call
ends as **Outcome unknown**. Make state-changing remote operations idempotent where possible. Check the destination
system before any manual retry or compensation.

After three transport, timeout, or invalid-result failures for one Tool, Duale AI pauses that Connection-and-Tool pair
for 30 seconds. Catalog checks and other Tools on the Connection continue. The next call after the pause can test the
Tool again.

## Check MCP Connection limits

These Platform limits are not configurable per Connection.

| Boundary               | Limit                                                                               |
| ---------------------- | ----------------------------------------------------------------------------------- |
| Connection             | 100-character name and 1,000 assigned Agent Identities                              |
| Complete catalog check | 30 seconds, 128 pages, 4,096 Tools, and 4 MiB of Tool definitions                   |
| One Tool definition    | 512 KiB; 128-character native name; 4,096-character description                     |
| Tool JSON Schemas      | 64 levels and 100,000 total JSON nodes across the input and optional output schemas |
| Tool Call arguments    | 256 top-level properties                                                            |
| Remote HTTP response   | 4 MiB before protocol projection                                                    |
| Accepted Tool Result   | 2 MiB; at most 32 text blocks, each no more than 1 MiB                              |

If a catalog check exceeds a limit, the whole check fails and does not replace the active catalog. If a Tool Result
exceeds a limit, Duale AI rejects that result.

[Tool limits](https://duale.ai/en/docs/tools/limits.md) covers the same boundaries for an SDK Tool Host, and how many Tools one Agent
Identity can hold across every Source.

## Read the organization Tool catalog API

The API reads the same retained repository shown in the Dashboard. These user-authenticated routes require a user
access token bound with Demonstrating Proof of Possession (DPoP), an `acr` claim of `2`, and the permission shown below.
They do not accept an Agent Identity API token. The Dashboard obtains the user credential; this preview does not
document a standalone command-line sign-in flow. [Tenant access policies](https://duale.ai/en/docs/identity/policies.md) explains
how permissions combine.

For managed beta, use `https://api.beta.duale.ai/orgraph` as the base URL. For another deployment, use the Orgraph base
URL supplied with that deployment.

| Request                                            | Permission                         | Result and filters                                                                                                                                                                                       |
| -------------------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `GET /tenants/{tenant_id}/tool-sources`            | `tools:read_catalog` on the Tenant | Source states; optional `source_kind=sdk\|mcp`                                                                                                                                                           |
| `GET /tenants/{tenant_id}/tool-sources/tools`      | `tools:read_catalog` on the Tenant | Retained Tools; optional `source_kind`, `source_id`, and `freshness=active\|expired\|all`; freshness defaults to `all`                                                                                   |
| `GET /tenants/{tenant_id}/agents/{agent_id}/tools` | `agent:read` on the Agent Identity | Assigned Tools, offered first then recently lost; required `order_by_name=native\|model`; optional `freshness=active\|expired\|all` (default `active`) and `expired_within_days` (default 7, maximum 30) |

This illustrative request asks for one active Tool assigned to an Agent Identity. Replace the user token and DPoP proof
with values issued for your signed-in session.

```http
GET /orgraph/tenants/tenant_a/agents/agent_research/tools?order_by_name=native&freshness=active&limit=1 HTTP/1.1
Host: api.beta.duale.ai
Authorization: DPoP <user-access-token>
DPoP: <proof>
```

A successful response is a JSON object:

```json
{
  "agent_id": "agent_research",
  "tools": [
    {
      "source_kind": "mcp",
      "source_id": "0199d7df-40f8-7000-8000-000000000001",
      "source_name": "CRM Production",
      "native_tool_name": "search_contacts",
      "model_tool_name": "mcp_search_contacts",
      "description": "Search contacts",
      "reasons": [],
      "freshness": "active",
      "last_observed_at": "2026-09-14T10:00:00Z",
      "expires_at": "2026-09-14T10:07:00Z"
    }
  ],
  "next_cursor": null
}
```

Each collection uses opaque cursor pagination. `limit` defaults to 100 and accepts 1 through 500. Send the returned
`next_cursor` unchanged with the same Tenant and filters. A `null` value marks the final page. These are live reads, not
a cross-page snapshot. If the catalog changes while you paginate and you need one consistent current traversal, restart
from the first page. The Agent Identity read pins the instant its first page was evaluated against and carries it in
the cursor, so every page of one traversal reports freshness against that instant. A different `order_by_name`,
`freshness`, or `expired_within_days` is a different traversal, and the earlier cursor is refused.

The response shapes are:

| Request                              | Response body                                                                                   |
| ------------------------------------ | ----------------------------------------------------------------------------------------------- |
| Tool Source list                     | `sources`: Source state objects; `next_cursor`: string or `null`                                |
| Retained Tool list                   | `tools`: retained Tool objects; `next_cursor`: string or `null`                                 |
| Tools assigned to one Agent Identity | `agent_id`: requested identity; `tools`: assigned Tool objects; `next_cursor`: string or `null` |

The item fields are:

| Item         | Fields                                                                                                                                                             |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Source state | `source_kind`, `source_id`, `source_name`, `freshness`, `last_observed_at`, `expires_at`, `assigned_agent_count`, `retained_tool_count`                            |
| Tool         | `source_kind`, `source_id`, `source_name`, `native_tool_name`, `model_tool_name`, optional `description`, `reasons`, `freshness`, `last_observed_at`, `expires_at` |

The retained Tool list and the Agent Identity list return the same Tool object. `reasons` states compatibility: it is
empty for a Tool the Platform can expose to a model, and an incompatible Tool has `model_tool_name: null` and one or more
entries in it. `description` is present only when the Source reports one. The Agent Identity response includes only
compatible Tools under the selected freshness filter, so its `reasons` is always empty.

Errors use `application/problem+json`. A malformed cursor, or a cursor reused with another Tenant or filter set, returns
`400`. Missing or insufficient user authentication returns `401`. A caller without the required permission receives
`403`. An invalid path or query value returns `422`.

## Related content

- [Tools: named capabilities supplied by SDK and MCP sources](https://duale.ai/en/docs/tools.md)
- [Operate and troubleshoot MCP Connections](https://duale.ai/en/docs/tools/operations.md)
- [Connect an MCP client to search documentation](https://duale.ai/en/docs/mcp.md)
- [Tool publication and presentation limits](https://duale.ai/en/docs/tools/limits.md)
- [Shared responsibility for agent security controls](https://duale.ai/en/docs/security/shared-responsibility.md)
- [Secure integrations against redelivery and uncertainty](https://duale.ai/en/docs/security/secure-integration.md)

---

## Sitemap

See the full [Markdown sitemap](https://duale.ai/sitemap.md) for all pages.
