Docs

Gateway and tools

Fix agents that cannot reach the Gateway, keys refused with 401, failed MCP discovery, blocked or slow upstreams, and tools agents cannot see.

The Gateway is the MCP endpoint your agents call. It authenticates the agent's gateway API key, decides every tool call against the policy, forwards allowed calls to the upstream MCP server and records the outcome. This page covers what goes wrong between an agent, the Gateway and the upstream MCP servers.

Start with Check the Gateway first, then find the symptom below. For refusals that come from a policy or an approval (denied, approval_pending, POLICY_CHANGED and similar), see Policies and approvals.

Check the Gateway first

Agents connect to the Gateway's MCP endpoint: the Gateway's base URL followed by /mcp, for example https://gateway.example.com/mcp. The endpoint accepts JSON-RPC requests with POST only.

Your agent program needs two values, which K-Block's examples read from environment variables of the program's own. Check both first:

  • The Gateway URL. Use the Gateway endpoint the dashboard shows right after you create a gateway API key on the API keys page, and in the setup guide. If the dashboard does not show it, contact K-Block support.
  • The API key. It must be a gateway key of the calling agent, copied completely. See The API key is refused (401).

To test both from the host the agent runs on, connect with connectKBlock from @kblockai/client and call listTools(), or send tools/list from any MCP client. A list, even an empty one, means the URL and the key work. NETWORK_ERROR means that host cannot reach the URL; a 401 means the Gateway was reached but refused the key.

How the Gateway answers at the HTTP level, and what @kblockai/client makes of it:

Gateway answerMeaning@kblockai/client
401, JSON-RPC error -32001The API key was refused.KBlockAuthError, code UNAUTHORIZED
429, JSON-RPC error -32000, Retry-AfterThe key's rate limit is reached.KBlockRateLimitedError with retryAfterSeconds
503, code DATABASE_UNAVAILABLE, Retry-After: 5K-Block's database is temporarily unavailable to the Gateway. Nothing was processed.KBlockUnavailableError, code DATABASE_UNAVAILABLE
503, code GATEWAY_SHUTTING_DOWN, Retry-After: 5The Gateway is stopping. Nothing was processed.KBlockUnavailableError, code GATEWAY_SHUTTING_DOWN
405A method other than POST. The Gateway is stateless: it opens no MCP session and no server-to-client stream.—
413The request body is larger than 1 MiB.KBlockHttpError, status 413
400, JSON-RPC error -32700The body is not valid JSON.—
JSON-RPC error -32603 "internal gateway error (request …)", usually in an HTTP 200 answerAn unexpected failure while handling tools/list or tools/call.The MCP SDK's McpError (code -32603), passed on unchanged
500, JSON-RPC error -32603 "internal gateway error (request …)"An unexpected failure before the answer started.KBlockUnavailableError, code HTTP_ERROR, outcomeUnknown: true

Everything the Gateway decides about a tool call (allowed, denied, waiting for approval, failed upstream) is a normal MCP tool result, not an HTTP error. A refusal has isError: true and structuredContent.kblock with status, code and detail. See Execution and results.

The agent cannot connect

SymptomCauseFix
KBlockUnavailableError with code NETWORK_ERROR (connection refused, DNS failure)The URL's host or port is wrong, the agent's host cannot reach the Gateway (network, firewall or proxy), or the Gateway is down.Compare the URL with the one the dashboard shows, and test it from the agent's host as described in Check the Gateway first. If the URL is right and the agent still cannot connect, contact K-Block support with the error code and the time the failures began.
KBlockHttpError status 404 "has no MCP endpoint at …"The URL points at a path where the Gateway has no MCP endpoint.Use the Gateway's base URL (for example https://gateway.example.com) or its /mcp endpoint.
A redirect error ("answered with a redirect, which is not followed")Something in front of the Gateway redirects (for example http to https). The client never follows redirects, so the key goes only to the origin you configured.Use the final URL.
TypeError about gatewayUrl before anything is sentPlain http:// to a host that is not a loopback address (the key would travel in clear text), or a URL with user name, password, query or fragment.Use https://. The client accepts plain http only for a loopback host, and elsewhere only with its allowInsecureHttp option. See Connect to the Gateway.
Your MCP client reports 405The client tried to open an MCP session stream (GET) or end a session (DELETE).Use a Streamable HTTP MCP client that sends POST requests without a session.

The Gateway is unavailable

503 DATABASE_UNAVAILABLE. K-Block's database is temporarily unavailable to the Gateway, so it cannot check the key. Nothing was processed; retry after the Retry-After wait. The Gateway fails closed: it never decides or forwards a call while its database is unavailable. The same condition appears in other places:

  • a tool call refused with status: "unavailable", code DATABASE_UNAVAILABLE and retryable: true, when the database was lost before anything was consumed;
  • a tools/list JSON-RPC error whose data.code is DATABASE_UNAVAILABLE (with data.retryable: true), which @kblockai/client reports as KBlockUnavailableError with code RPC_UNAVAILABLE.

If the answer continues after several retries, contact K-Block support. Give them the code, the request ID from the response's X-Request-Id header if your program can read it, and the time the failures began.

503 GATEWAY_SHUTTING_DOWN. The Gateway is stopping and takes on no new work. The connection is closed with the answer; retry on a new connection after the Retry-After wait. A call that was waiting for an approval when the shutdown began returns approval_pending; resume it later.

Internal gateway error

A call that fails with JSON-RPC error -32603 "internal gateway error (request …)" is an unexpected failure in K-Block, not in your agent; the answer never carries its details. Contact K-Block support with the request ID from the message and the time of the failure. Before the agent repeats a tools/call, check under Actions in the dashboard whether the call was recorded.

When every call and every tools/list fails this way, one known cause is a problem with the Gateway's own identity, which only K-Block can fix. See Identities and KIDs for what the Gateway identity is.

The API key is refused (401)

Symptom. Every request fails with HTTP 401 and the JSON-RPC error -32001 "unauthorized: a valid gateway API key is required (Authorization: Bearer kb_…)". @kblockai/client throws KBlockAuthError with code UNAUTHORIZED.

Cause. The Gateway gives the same answer for every refused key and never tells the caller why; only K-Block can see the reason. A gateway key is refused when:

ReasonFix
The Authorization header is missing or not Bearer <key>.Send Authorization: Bearer kb_….
The key is malformed, unknown, or its secret does not match (for example a truncated copy).Copy the key again. It is shown only once; if it is lost, create a new one.
The key was revoked or has expired.Create a new gateway key for the agent on the API keys page.
It is a management key.Agents need a key of kind gateway, bound to the agent. Management keys are for the Cloud API.
The agent is disabled.Enable the agent on its page.
The member who created the key left the organization, was disabled, or now has a role that may not create gateway keys (owner, admin and developer may).Have a current owner, admin or developer create a new key.

The Gateway authenticates every request, so a key revoked while an agent is connected fails on the agent's next request.

Before anything is sent. @kblockai/client checks the key's format first and throws KBlockAuthError with code MALFORMED_API_KEY without sending it. The expected format is kb_<live|test>_<prefix>_<secret>. The most common causes are a trailing newline from a file or environment variable, quotes around the value, and an empty variable.

A key that stops working after a role change is expected: when a member is removed or demoted, the keys they issued that their new role could not create are revoked. See API keys.

If none of the reasons above applies, contact K-Block support. Give them the time of the refusal, the request ID from the response's X-Request-Id header if your program can read it, and the key's non-secret part (kb_<live|test>_<prefix>), never the full key.

Rate limited by the Gateway

Symptom. HTTP 429 with JSON-RPC error -32000 "rate limit exceeded" and a Retry-After header. @kblockai/client throws KBlockRateLimitedError; its retryAfterSeconds holds the wait.

Cause. Each gateway API key may send bursts of up to 120 requests, refilled at 20 requests per second. Each message of a JSON-RPC batch counts. Other keys are not affected. The limit is fixed in this version.

Fix. Wait for Retry-After and slow the agent down. Give separate workloads separate keys.

A related cap applies to approval waits (_meta["kblock/wait-seconds"]): at most 20 calls per key, and 1,000 in total on each Gateway, wait at the same time. A call over the cap is not refused: it returns approval_pending at once, with "(not waited: …)" in its detail. Resume it later.

MCP discovery fails

When you click Discover tools on an MCP server's page, the Cloud API (not the Gateway) connects to the upstream MCP server and lists its tools. A failure shows "Discovery failed" with one of these codes.

CodeHTTPCauseFix
URL_NOT_ALLOWED422When you register or change the server: the URL breaks the outbound rules. details.reason says which (see Private or plain-http upstream refused).Correct the URL.
UPSTREAM_REFUSED422While connecting: the address is not allowed, for example the host name now resolves to a private address. details.reason names the rule.Use a public https address: K-Block refuses private, loopback and internal addresses (see Private or plain-http upstream refused).
UPSTREAM_UNREACHABLE502DNS lookup failed or the connection failed.Check the URL, that the server is running, and that it accepts connections from the internet at that address. If it does and discovery still fails, contact K-Block support with the code and the request ID shown in the error.
UPSTREAM_TIMEOUT504No answer within the discovery timeout (15 seconds).Check the server's health and latency.
UPSTREAM_ERROR502The server answered with an HTTP error status (details.http_status), returned an MCP error, or needed more requests than a discovery may make.Check the server's logs. A 401 or 403 usually means the server needs credentials: enter the complete header value in New authorization header (optional) on the server's page.
DISCOVERY_TOO_LARGE502The server's answers add up to more than 8 MiB.Enter the tool's input schema yourself when you create the tool; then nothing is discovered.
DISCOVERY_BUSY429At most 2 discoveries per organization, and 8 for all organizations together, run at the same time; more are refused, not queued. Retry-After: 5.Try again after a few seconds.

Notes:

  • The Authorization header you store with an MCP server is write-only: it is encrypted, never shown again, sent with discovery and with every forwarded call, and never sent to another origin. To move a credentialed server to another origin, enter the header for the new upstream in the same save (otherwise VALIDATION_FAILED with issue AUTH_HEADER_REQUIRED).
  • An upstream token never belongs in the URL. A URL with a query is refused (URL_NOT_ALLOWED, reason URL_QUERY_NOT_ALLOWED) because the URL is signed into every approval and receipt of the server's calls.
  • You do not need discovery to expose a tool: you can create a tool with its input schema entered by hand. See Connect an MCP server.

Private or plain-http upstream refused

K-Block only talks to upstream MCP servers at public https addresses. This protects K-Block from being used to reach internal services (server-side request forgery). K-Block refuses:

  • plain http:// URLs;
  • loopback addresses (127.0.0.0/8, ::1), private networks (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00::/7), link-local addresses including the cloud metadata address 169.254.169.254, and other special-purpose ranges;
  • host names that resolve to any such address;
  • URLs with user name or password, a query or a fragment.
WhereWhat you see
Registering or editing a server (Cloud API)422 URL_NOT_ALLOWED, details.reason is URL_SCHEME_NOT_ALLOWED, ADDRESS_BLOCKED, URL_CREDENTIALS_NOT_ALLOWED, URL_QUERY_NOT_ALLOWED, URL_INVALID or DNS_LOOKUP_FAILED
Discovery (Cloud API)422 UPSTREAM_REFUSED with the rule in details.reason
A tool call (Gateway)status: "upstream_error", code UPSTREAM_BLOCKED, detail "the upstream URL is not allowed (…)" ending "nothing was executed and the approval was not consumed". A host name that does not resolve is refused the same way, with DNS_LOOKUP_FAILED in the detail.

Fix. Give the MCP server a public https address. An MCP server that has only a private address, or answers only on plain http, cannot be used with K-Block. If K-Block accepted the server and discovered its tools, but every call to it is refused with UPSTREAM_BLOCKED, check that its host name still resolves only to public addresses. If it does, contact K-Block support with the detail of the refusal and the time of the call.

The Gateway resolves the upstream's host name once per call, checks every address and connects only to those addresses, so a DNS change between the check and the call cannot redirect it.

Upstream timeout and outcome unknown

The upstream does not answer in time

Symptom. The agent receives status: "upstream_error" with code UPSTREAM_TIMEOUT. The detail may end with "the tools/call request was sent but no valid answer to it was received, so the upstream may have acted — check its effect before repeating the call".

Cause. The upstream did not answer within the Gateway's upstream timeout. The Gateway waits up to 30 seconds for your MCP server to answer. One deadline covers the MCP initialization and the tools/call.

What K-Block records. The approval or allow decision was consumed: the call was sent at most once and is never retried by the Gateway. The action ends failed, and a receipt is issued with the failed result. The same applies to the other upstream failures:

CodeMeaning
UPSTREAM_TIMEOUTNo answer within the timeout.
UPSTREAM_UNREACHABLEThe connection to the upstream failed. (A host name that does not resolve is refused earlier, before anything is consumed: see Private or plain-http upstream refused.)
UPSTREAM_HTTP_ERRORAn HTTP error status. A 4xx is the upstream refusing; a 5xx counts as a lost answer.
UPSTREAM_PROTOCOL_ERRORThe upstream answered with a JSON-RPC error, or with something that is not a valid MCP answer.
UPSTREAM_RESPONSE_TOO_LARGEThe answer was larger than 1 MiB.
UPSTREAM_ERRORAny other failure of the exchange with the upstream.
UPSTREAM_TOOL_ERRORThe upstream's tool itself returned an error result.
UPSTREAM_REFUSEDA K-Block-aware upstream (one with a server KID) refused the call's evidence.

A K-Block-aware upstream's result is also not accepted when its attestation is missing or does not verify: the call ends failed the same way, with code ATTESTATION_MISSING or ATTESTATION_INVALID.

Whenever the request was sent and no valid answer came back, the detail ends with the "may have acted" sentence above. Then the upstream may have carried out the call although K-Block records a failure.

Fix. Check the effect in the upstream system before the agent repeats the call. A repeated call is a new protected action with its own decision (and its own approval, if the policy requires one). If your tools regularly need longer than 30 seconds, contact K-Block support.

The agent's connection broke

If the agent's own request to the Gateway times out or its connection breaks after the call was sent, the agent does not know whether the Gateway executed the call. @kblockai/client throws KBlockUnavailableError with outcomeUnknown: true (code TIMEOUT or NETWORK_ERROR, or HTTP_ERROR for a 5xx answer that is not one of the Gateway's own 503s, for example from a proxy in front of it).

Fix. Repeat the call with the same idempotency key: the Gateway returns the stored outcome instead of running the call again. @kblockai/client sends a fresh idempotency key with every callTool unless you pass your own idempotencyKey option, so pass one for calls you may need to repeat, and repeat with resume using the same tool, arguments and key. Two limits:

  • A keyed repeat is answered only while the tool is still enabled and the agent and key are still usable. If the tool was disabled in the meantime, the repeat is refused UNKNOWN_TOOL; look up the action in the dashboard instead.
  • Without a key, repeating an allowed call that already finished is a new call.

The Gateway stopped during the call

SymptomMeaningWhat to do
status: "unavailable", code DATABASE_UNAVAILABLE, retryable: false, detail "…whether the approval was consumed is unknown; the call was NOT sent to the upstream…"K-Block's database became unavailable while the Gateway was consuming the approval. The call was not sent upstream.Check the action in the dashboard before repeating. If the approval was consumed, the call needs a new approval.
An action stuck in executingThe Gateway stopped after sending the call and before recording the result.Wait: about 11 to 15 minutes after the call started, K-Block marks the action failed with code INTERRUPTED and issues a receipt.
An action failed with INTERRUPTEDRecovered as above. The receipt says failed, but whether the upstream acted is unknown, unless the detail says "interrupted before the upstream was called".Check the upstream system before the agent retries.

The agent does not see a tool

tools/list returns only the tools the calling agent may use. A tool is listed when all of these hold:

  1. The organization has a principal.
  2. The tool is enabled and its MCP server is active.
  3. The agent has a usable delegation that grants the tool's action: signed by the organization's current principal, to the currently registered gateway identity, naming this agent, not revoked and not expired.
  4. The tool's upstream binding is the one an owner or admin authorized.
  5. The server's URL has no query, fragment or user info.
  6. The stored input schema is a valid MCP object schema.
Likely causeHow to tellFix
No principalThe setup guide's Set the organization principal step is open.An owner sets the principal under Settings.
Tool disabled (for example a developer's draft) or server disabledThe tool's row on Tools & MCP servers shows it disabled.An owner or admin enables the tool (and the server).
No delegation for the actionThe agent page lists no active delegation that covers the action.An owner or admin uses Sign a new delegation… on the agent page, in the browser that holds the principal key.
Delegation expired, revoked, or from an earlier principal or gateway identityThe delegation list shows it expired or revoked, or the principal or the gateway identity changed since it was signed.Sign a new delegation.
Binding not authorizedThe tools page names the tool as needing (re-)authorization. Any change to the tool's server, URL, server KID, credential, upstream tool, action, amount mapping or input schema requires it.An owner or admin clicks Review & authorize or Review & re-authorize on the tool's row.
Invalid input schemaEverything above is in order, but tools/list still leaves the tool out, and its input schema on Tools & MCP servers is not a valid MCP object schema.Edit the tool's input schema.

Calling a tool that is not listed fails differently by cause:

CauseResult
Disabled tool, inactive server or unknown namestatus: "invalid_request", code UNKNOWN_TOOL, "no enabled tool named '…'". Nothing is recorded.
Binding not authorizedstatus: "denied", code TOOL_REAUTHORIZATION_REQUIRED. Nothing is recorded.
No delegationA recorded denial with code NO_DELEGATION.
No principalA recorded denial with code NO_PRINCIPAL.

The Gateway does not notify connected MCP clients when the list changes (it declares listChanged: false). After you fix the cause, have the agent call tools/list again.

Next steps