> For the complete documentation index, see [llms.txt](https://docs.kangal.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kangal.dev/ai-gateway/protocols.md).

# Protocols

Kangal can publish tenant-scoped MCP upstreams and A2A agents through the same control plane used by AI Gateway. Protocol traffic uses a verified Kangal user JWT; tenant values in headers or JSON are not accepted as identity.

## Operator workflow

Open **AI Gateway > MCP / A2A**, select a gateway, and create an **MCP upstream** or **A2A agent**. The screen can:

* create resources;
* enable or pause them;
* delete them;
* show the newest 25 operations, refreshed every 10 seconds.

Use `PATCH` when changing URL, token, policy, limits, Agent Card, or labels on an existing resource.

Admin resources are available at:

```
POST   /admin/gateways/{gateway_id}/protocol-gateway/resources
GET    /admin/gateways/{gateway_id}/protocol-gateway/resources
GET    /admin/gateways/{gateway_id}/protocol-gateway/resources/{resource_id}
PATCH  /admin/gateways/{gateway_id}/protocol-gateway/resources/{resource_id}
DELETE /admin/gateways/{gateway_id}/protocol-gateway/resources/{resource_id}
GET    /admin/gateways/{gateway_id}/protocol-gateway/operations
```

The versioned `/api/v1/admin/...` aliases are also mounted. Admin reads permit read-only admin roles; writes require `admin` or `super_admin` and are scoped to the gateway tenant.

## Resource fields and limits

| Field                       | Default and validation                                          |
| --------------------------- | --------------------------------------------------------------- |
| `kind`                      | Required: `mcp_upstream` or `a2a_agent`                         |
| `name`                      | 1-120 characters; unique by tenant/gateway/name                 |
| `description`               | Up to 1,000 characters                                          |
| `enabled`                   | `true`; disabled resources return `404` at runtime              |
| `upstream_url`              | 8-2,048 characters; public HTTPS, no redirects/private targets  |
| `upstream_bearer_token`     | Optional, up to 8,192 characters; encrypted and redacted        |
| `required_scopes`           | Up to 50 exact JWT scopes; all configured scopes are required   |
| `allowed_origins`           | Up to 50 exact browser origins                                  |
| MCP allowlists              | Up to 500 tools, resources, and prompts per list                |
| `max_request_bytes`         | Default 262,144; 1,024-4,194,304                                |
| `max_response_bytes`        | Default 1,048,576; 1,024-16,777,216                             |
| `timeout_seconds`           | Default 30; 0.1-300                                             |
| `quota_requests_per_minute` | Default 120; 1-100,000 per principal/resource                   |
| `max_concurrency`           | Default 16; 1-1,000 per resource/process                        |
| `discovery_host`            | A2A only; DNS hostname without scheme, path, port, or user info |
| `agent_version`             | Default `1.0.0`, up to 80 characters                            |
| `skills`                    | A2A only, 1-100 skills                                          |

Each A2A skill requires `id`, `name`, and `description` (1-120, 1-120, and 1-1,000 characters). It can have up to 50 tags, 20 examples, 20 input modes, and 20 output modes.

Example MCP resource:

```bash
curl -sS -X POST \
  "$KANGAL_ADMIN_URL/admin/gateways/$GATEWAY_ID/protocol-gateway/resources" \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H 'Content-Type: application/json' \
  -d "{
    \"tenant_id\": \"$TENANT_ID\",
    \"kind\": \"mcp_upstream\",
    \"name\": \"support-tools\",
    \"upstream_url\": \"https://mcp.example.com/mcp\",
    \"upstream_bearer_token\": \"$UPSTREAM_TOKEN\",
    \"required_scopes\": [\"mcp:use\"],
    \"allowed_origins\": [\"https://app.example.com\"],
    \"tool_allowlist\": [\"tickets.*\", \"knowledge.search\"],
    \"resource_allowlist\": [\"kb://public/*\"],
    \"prompt_allowlist\": [\"support-*\"],
    \"quota_requests_per_minute\": 60,
    \"max_concurrency\": 8
  }"
```

Empty MCP allowlists deny calls/reads and filter discovery to an empty list. Patterns use case-sensitive shell-style matching.

## MCP runtime

| Method and path             | Use                                                             |
| --------------------------- | --------------------------------------------------------------- |
| `POST /mcp/{resource_id}`   | Streamable HTTP requests, notifications, and response envelopes |
| `GET /mcp/{resource_id}`    | Server-to-client SSE for an existing session                    |
| `DELETE /mcp/{resource_id}` | Terminate an existing session                                   |

Supported protocol versions are `2025-11-25`, `2025-06-18`, and `2025-03-26`. New clients should initialize with `2025-11-25`.

Supported request methods:

```
initialize
ping
tools/list
tools/call
resources/list
resources/templates/list
resources/read
prompts/list
prompts/get
```

Supported notifications are `notifications/initialized`, `notifications/cancelled`, and `notifications/progress`.

MCP `POST` requires:

```
Authorization: Bearer <Kangal-user-JWT>
Content-Type: application/json
Accept: application/json, text/event-stream
```

After initialization, send the returned `MCP-Session-Id` and `MCP-Protocol-Version`. Sessions are re-keyed by Kangal and bound to tenant, resource, principal, protocol version, and upstream session ID. They expire after eight hours. A session from another principal or scope returns `404`.

JSON responses are relayed as one validated JSON-RPC object. SSE is streamed after header/content-type preflight. Accepted notifications and response envelopes return `202` with no body. Server SSE is parsed incrementally; malformed, oversized, truncated, or invalid events terminate fail-closed.

Effective structure limits are 20 nesting levels, 10,000 JSON values, 512-character object keys, and 1,000,000-character string values, in addition to the resource byte limits. Send one JSON-RPC request per envelope; batches are rejected.

## A2A runtime

Create an `a2a_agent` with a globally unique `discovery_host` and at least one skill. Discovery is host-routed:

```
GET /.well-known/agent-card.json
```

The returned card advertises JSON-RPC binding `1.0`, streaming support, a Kangal JWT bearer scheme, and the configured skills. It is cached publicly for 300 seconds.

Send operations to:

```
POST /a2a/{resource_id}
Authorization: Bearer <Kangal-user-JWT>
Content-Type: application/json
A2A-Version: 1.0
```

Supported methods are `SendMessage`, `SendStreamingMessage`, `GetTask`, and `CancelTask`. An absent version header is treated as legacy `0.3` and returns `VersionNotSupportedError` (`-32009`). Streaming returns JSON-RPC response objects in SSE `data` fields.

Discovered task IDs are persisted with tenant, resource, gateway, and creating principal. Get/cancel requests must match that complete scope. Unknown and unauthorized IDs both return `TaskNotFoundError` (`-32001`).

## Security and operations

* Inbound bearer credentials are never forwarded. The separate encrypted upstream token is used instead.
* If a browser sends `Origin`, it must exactly match `allowed_origins`; no `Origin` header is accepted for non-browser clients.
* URL validation and pinned outbound dialing reject redirects, private and metadata addresses, and DNS rebinding before credential-bearing calls.
* Per-minute quota reservation is atomic. Exhaustion returns `429` with `Retry-After: 60`; exhausted concurrency returns `503` rather than queueing.
* Operations contain trace/request/principal/task IDs, method, outcome, status, latency, byte counts, bounded scalar usage, and error type. Payloads and credentials are not persisted.
* `GET .../operations` accepts optional `resource_id` and `limit` 1-200, default 50.
* `/metrics` exports low-cardinality protocol request, latency, byte, and rejection metrics without tenant, principal, resource, or trace labels.

Protocol clients must use the Kangal user JWT profile and the versions, methods, headers, and transport behavior documented above. Validate session handling, streaming, origin policy, and upstream timeouts in the target ingress environment before production rollout.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kangal.dev/ai-gateway/protocols.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
