The Gateway is the MCP server your agents connect to instead of connecting to your tools directly. It speaks MCP over Streamable HTTP. For every tool call it checks the organization's policy, records a protected action, holds the call for an approval when the policy requires one, forwards approved and allowed calls to the upstream MCP server, and signs the evidence that becomes a receipt.
The Gateway is the only component that executes tool calls. The dashboard and the Cloud API manage configuration, decisions and receipts, but they never call an upstream tool.
Agent
tools/call over MCP
Gateway
authenticate, decide, record
Approval
only if the policy requires it
Upstream MCP server
called once
Receipt
signed RESULT and bundle
What the Gateway is
| Property | Value |
|---|---|
| Protocol | MCP over Streamable HTTP, at the path /mcp (POST only) |
| Server name and version reported to agents | kblock-gateway 0.1.0 |
| Sessions | None: the Gateway is stateless and issues no Mcp-Session-Id. GET and DELETE on /mcp are answered 405 (after authentication) |
| Capabilities | tools only |
| Request body | At most 1 MiB; larger requests get HTTP 413 |
At initialization the Gateway sends agents short instructions that explain the optional _meta
keys and the shape of refusals, including the optional retryable and policy members
(retryable: false means: check the protected action before repeating the call).
You get the Gateway's URL from the dashboard, which shows the Gateway's MCP endpoint right after you create a gateway API key. If it does not show it, contact K-Block support.
Authentication with gateway API keys
Every HTTP request to /mcp must carry a gateway API key:
POST /mcp HTTP/1.1
Host: gateway.example.com
Authorization: Bearer kb_live_…
Content-Type: application/json
Accept: application/json, text/event-streamA gateway API key (kb_live_… or kb_test_…) belongs to one agent of one
organization. Owners, admins and developers create them in the dashboard; the secret is shown once.
See API keys.
Because the Gateway is stateless, every request is authenticated again. A key that is revoked or expires, or whose agent is disabled, fails on the next request even if the agent's MCP client is still connected. These all get the same answer, so the response does not reveal why:
- HTTP 401 with
WWW-Authenticate: Bearer realm="kblock-gateway"and a JSON-RPC error-32001; - for a missing, malformed, unknown, revoked or expired key, a management API key (management keys only work with the Cloud API), a disabled agent, or a key whose creator left the organization or lost the role needed to create it.
Each key may send a burst of 120 requests, refilled at 20 per second, to each Gateway. Beyond that
the Gateway answers HTTP 429 with Retry-After; wait that many seconds before you retry.
What an agent sees in tools/list
tools/list returns only the tools this agent can call through the Gateway right now; the policy
still decides each call. A tool is listed when all of these hold:
- the tool is enabled, and its MCP server is active;
- the organization has a principal, and a usable delegation from that principal to the Gateway names this agent and grants the tool's action;
- the tool's binding is the one an owner or admin authorized (a tool whose configuration changed since is left out until it is re-authorized);
- the server URL has no query, fragment or user information, so it can be signed;
- the stored input schema is a valid MCP object schema.
Each tool is listed with its exposed name, its input schema and its description. The Gateway never
declares an outputSchema. Tools of other organizations, and tools no delegation grants, are never
shown. See Tools and MCP servers.
The tools/call flow
Read the K-Block _meta keys
Two optional keys are accepted in the request's
_meta:kblock/idempotency-key— 1 to 200 characters, scoped to the calling agent. A repeat with the same key returns the first call's stored outcome; the same key with another tool or other arguments is refusedIDEMPOTENCY_KEY_REUSED.kblock/wait-seconds— a whole number of seconds to hold the call while it waits for an approval.
Any other key that starts with
kblock/is refusedinvalid_request/INVALID_META.Resolve the tool
The name must be an enabled tool on an active server of the caller's organization. Otherwise the call is refused
invalid_request/UNKNOWN_TOOLand nothing is recorded.Decide and record
The Gateway looks for an earlier outcome first: the action of the same idempotency key, or, without a key, the open action of the identical call. If there is none, it decides: it checks the tool's authorized binding, reads the amount, evaluates the agent's active policy version, selects the delegation, resolves the approvers, signs a TASK and a PROPOSAL, and records the protected action, all or nothing. Denials are recorded too, except two refusals that record nothing: a tool whose binding is not the authorized one (
TOOL_REAUTHORIZATION_REQUIRED) and a call over the plan limit (PLAN_LIMIT_EXCEEDED).Settle
Depending on the action's state, the Gateway executes it (allowed, or approved), waits for a decision (pending, with
kblock/wait-seconds), or answers with a refusal.Execute and record
For an allowed or approved action, the Gateway re-checks everything, consumes the action exactly once, calls the upstream, signs the RESULT and issues the receipt. See Execution and results.
What the agent receives
A completed call returns the upstream's result with _meta.kblock. Every other outcome is an MCP
error result (isError: true) whose structuredContent.kblock has status, code and detail,
and, when the call was recorded, protected_action_id and, where they apply, approval_request_id
and expires_at. Some refusals add retryable and the policy's explanation (policy, and a second
text line starting Why:), depending on what your organization discloses to agents (see
What agents are told).
status | Typical codes | Meaning |
|---|---|---|
| — (success) | — | The upstream result, with _meta.kblock IDs of the action and receipt |
denied | POLICY_DENIED, NO_POLICY, NO_DELEGATION, NO_APPROVERS, TOOL_REAUTHORIZATION_REQUIRED, and at execution UPSTREAM_CHANGED, POLICY_CHANGED | Refused; nothing ran |
approval_pending | APPROVAL_PENDING | Held for approval; repeat the identical call later |
rejected | REJECTED | An approver rejected it (receipt IDs included) |
expired | APPROVAL_EXPIRED, TASK_EXPIRED, SUPERSEDED | The action ended without running |
cancelled | CANCELLED | The organization cancelled it (receipt IDs included) |
replay | EXECUTION_IN_PROGRESS, REPLAY, DUPLICATE_IN_FLIGHT | Another request is running, or already ran, this action or an identical call |
upstream_error | UPSTREAM_TOOL_ERROR, UPSTREAM_TIMEOUT, UPSTREAM_BLOCKED, … | The upstream failed or could not be called |
unavailable | DATABASE_UNAVAILABLE, GATEWAY_SHUTTING_DOWN | The Gateway cannot process the call right now |
invalid_request | UNKNOWN_TOOL, INVALID_META, INVALID_ARGUMENTS, IDEMPOTENCY_KEY_REUSED | The request itself is wrong |
plan_limit | PLAN_LIMIT_EXCEEDED | The organization's monthly limit is reached; nothing recorded |
unauthorized | UNAUTHORIZED | The key or agent became unusable during the call |
All codes: Error codes and Action statuses.
Waiting for an approval
Without kblock/wait-seconds, a call that needs approval is answered approval_pending at once. With
it, the Gateway holds the request until the approver decides or the time is up:
- The wait is capped at 60 seconds. A longer
kblock/wait-secondsis lowered to the cap. - The Gateway learns of a decision as soon as it is recorded, with periodic checks as a fallback, so a waiting call reacts within moments, whichever Gateway it waits on.
- If the agent sent an MCP
progressToken, the Gateway sends a progress notification every 10 seconds while it waits. - On each Gateway, at most 20 calls per API key, and at most 1,000 calls in all, wait at the same
time. Beyond that the call is answered
approval_pendingat once, with "(not waited: …)" in the detail; repeat the call later to resume it.
When the wait ends, the Gateway acts on the current state: it executes an approved call in the same
request, returns the rejection, expiry or cancellation, or answers approval_pending again if nothing
was decided. If the agent disconnects at the moment the approval lands, the call may still execute;
its outcome is recorded and a repeat with the same idempotency key returns it.
Resuming a call
To continue a pending call, the agent repeats the identical call: same tool, same arguments, and the same idempotency key if it used one. The Gateway finds the existing protected action instead of deciding again:
- with a key, only the action recorded with that key;
- without a key, the open action of the exact call (the keyless one first, else the oldest keyed one).
A pending request can be resumed through any Gateway, not only the one that created it. In
@kblockai/client, use client.resume(result) or client.waitForApproval(result); see
TypeScript client.
Upstream forwarding
The Gateway calls the upstream MCP server as an MCP client over Streamable HTTP, with the
Authorization header stored for that server. The stored credential is encrypted at rest and never
shown back. The Gateway:
- resolves the upstream's host once, checks every address, and connects only to those addresses;
- connects only to public
httpsaddresses: plainhttpURLs and private, loopback and internal addresses are refused withupstream_error/UPSTREAM_BLOCKED(see URL rules); - follows no redirects, and accepts responses of at most 1 MiB;
- gives the whole exchange one deadline of 30 seconds;
- sends the
tools/callonce and never retries it; - ignores requests and notifications the upstream sends back.
For a K-Block-aware upstream (registered with a server KID), the Gateway also sends the evidence in
_meta.kblock and requires a signed attestation back. See
Identities and KIDs.
Evidence generation
The Gateway signs evidence with its service identity, whose KID is the Gateway KID (see Identities and KIDs):
| When | Objects signed |
|---|---|
| A call is decided (allow or require approval) | TASK and PROPOSAL (nothing is signed for a denial) |
| The action is consumed | EXECUTION |
| The upstream call ends | RESULT, and the RECEIPT with its .kbundle |
Approvals and rejections are signed in the approver's browser, and cancellations with the organization principal. The Cloud API verifies them when they are submitted and issues the receipts of rejections and cancellations; the Gateway verifies the approval again before it executes.
If the Gateway's identity no longer matches the one K-Block has on record, new decisions and
executions fail with a JSON-RPC internal error (-32603, "internal gateway error (request …)"). Your
agent cannot fix this: contact K-Block support with the error code and the request ID from the error.
Several Gateways
K-Block may run several Gateways, for availability or load. They share the state of every call: decisions, approvals, executions and idempotency keys are kept by K-Block, not by one Gateway, so any Gateway can serve any request.
- All Gateways sign with the same identity, so receipts carry the same Gateway KID.
- An approved call retried on several Gateways at once still executes exactly once.
- A call waiting on one Gateway learns of the decision wherever the approval was given.
- If one Gateway stops, the others keep serving; work it left unfinished is recovered (see Execution and results).
- Rate limits and limits on waiting calls are counted by each Gateway separately, so with several Gateways the totals add up.
Effect of an emergency stop
An emergency stop (Emergency stop… on a policy) activates a stop version of the policy at once:
- The very next call decided on any Gateway uses it; no observe window relaxes it.
- Calls that were allowed before the stop but have not run yet are refused at consumption with
denied/POLICY_CHANGED; nothing runs. - With the default for "deny all", pending approvals and approved calls that have not run are closed
(
expired/SUPERSEDED). A keyless repeat of such a call gets a new decision under the stop; a repeat with the same idempotency key is answeredexpired/SUPERSEDED. - An execution that already started finishes and is recorded.
See Emergency stop and Emergency controls.
When the Gateway cannot serve
| Situation | What agents receive |
|---|---|
| K-Block is temporarily unavailable and cannot check or record the call in time | HTTP 503 with Retry-After: 5, or a refusal unavailable / DATABASE_UNAVAILABLE with retryable: true (for tools/list, a JSON-RPC error that names DATABASE_UNAVAILABLE). Nothing is executed or consumed by that request; the exception is a failure at the moment an approval is being consumed (retryable: false, see Execution and results) |
| The Gateway is shutting down | HTTP 503 or a refusal unavailable / GATEWAY_SHUTTING_DOWN, retryable. Calls waiting for approval end with approval_pending |
| The upstream is down or slow | upstream_error with the precise code, after the approval was consumed. See Execution and results |
The Gateway fails closed: if it cannot check or record a call, it does not run it.
For a retryable answer, wait for Retry-After (or a few seconds) and repeat the call, with the same
idempotency key if you used one. If the Gateway stays unavailable, contact K-Block support with the
error code, the request ID (every Gateway answer carries it in the x-request-id header) and the time
it happened. See also
Gateway and tools.