AI agents call tools that change things in the real world: they refund orders, send e-mails, merge code, change records. A model that misreads a request, follows a prompt injection or retries in a loop can make such a call with real consequences, and afterwards it is often hard to say who allowed it, whether a person saw it first, and whether it ran once or twice.
K-Block is a control point between your agent and those tools. The agent sends its tool calls to the K-Block Gateway instead of straight to the tools. For every call, the Gateway applies your organization's policy, holds the call for a person's approval when the policy asks for one, executes it at most once, and records signed evidence of what happened.
You keep your agent, its framework and your MCP servers. What changes is where the agent's MCP client connects, and the key it presents there.
The problem K-Block solves
An agent framework turns model output into tool calls. Once a tool has a real effect, a few questions matter for every call:
- Should this call run at all? Some calls are fine automatically (look up an order), some need a person (a large refund), some must never happen (delete a customer).
- Did a person approve this exact call? An approval for a refund of 250 USD must not let the agent run a refund of 2,500 USD, or run the same refund twice.
- Did it run exactly once? Retries, timeouts and parallel workers must not execute an approved call a second time.
- Can you prove it afterwards? You want evidence of what was requested, who approved it and what the tool answered, which someone outside your organization can check.
What K-Block does on every tool call
Agent
calls a tool through the Gateway with its gateway API key
Gateway
applies the agent's policy
Approval
only when the policy requires one
MCP server
runs the call once
Receipt
signed evidence of the call
The Gateway decides each call with the policy of the agent that made it. A policy has three possible outcomes:
| Decision | What happens | What the agent gets |
|---|---|---|
| allow | The Gateway sends the call to your MCP server once and returns its answer. | completed, with the tool's result and the receipt's IDs (upstream_error when the tool fails) |
| require approval | Nothing runs yet. A person reviews the exact call in the dashboard and approves or rejects it. Once approved, the call runs once, when the agent repeats it or is still waiting for the decision. | approval_pending, then completed, rejected, expired or cancelled |
| deny | Nothing runs. | denied, with a code such as POLICY_DENIED and, if your organization allows it, the deciding rule |
Policy decision
A policy is a JSON document of rules. Rules refer to actions (permissions such as refund.issue that you map to each exposed tool), optionally to tool names and to amount ranges in one currency. When several rules match, the most restrictive one wins: deny before require approval before allow. When no rule matches, the policy's default applies, which is deny or require approval, never allow.
A call can also be denied before or after the policy is evaluated, each time with its own code: the agent has no policy (NO_POLICY), the call's amount cannot be read, or, for a tool with a currency pointer, the call names another currency than the tool's or none (INVALID_AMOUNT), no valid delegation grants the action to the agent (NO_DELEGATION), or no member is able to approve it (NO_APPROVERS). Error codes lists them all.
Human approval
When a call needs approval, K-Block creates an approval request for exactly that call: the tool, its arguments and its amount. Members with the owner, admin or approver role whom the rule admits can decide it, but only if they had set up a personal K-Block key in their browser before the call was made: the approvers are fixed when the call is decided. The approver checks the call in the dashboard and chooses Approve and sign or Reject and sign; the decision is signed with their personal key, in their browser.
An approval covers one call. If the agent changes the arguments, that is a new call and a new request. A request that nobody decides in time expires: the deciding rule's time-to-live applies, otherwise your organization's approval time-to-live (24 hours unless an owner or admin changes Default approval lifetime (seconds) in Settings), and never beyond the delegation's expiry. An approved call must also run before that time: if the agent does not repeat it in time, it expires without running.
Exactly-once execution
Each allowed or approved call is consumed by one execution only. K-Block guarantees this for every call: concurrent retries, parallel workers and repeated requests cannot execute the same decided call twice, even when they arrive at the same moment. A call repeated with the same idempotency key returns the first call's recorded outcome instead of running again (while the first is still executing, it is refused as a replay).
The key decides what counts as the same call. callTool in the TypeScript client and call_tool in the Python client send a fresh key with every call, so calling them again is a new call with its own decision; repeat a call with resume or waitForApproval (wait_for_approval in Python) instead. A call sent without a key continues the open action of the identical call, but once that action has finished, an identical call is decided and can run again.
The call to your MCP server is sent at most once, and K-Block never retries it. If the connection breaks after the call was sent, K-Block does not know whether the tool acted: it records the call as failed, with a receipt, and does not send it again. Execution and results explains the outcomes.
Signed receipts
Every executed call, successful or failed, ends with a receipt: a portable .kbundle file holding every signed object of the call and the public identity documents needed to check them. So do rejected and cancelled calls. A call denied when it was decided, and a call that expired (before or after its approval), are recorded as actions but have no receipt.
Three kinds of keys sign the evidence:
- the organization principal signs the delegation that lets the Gateway act for an agent (its private key stays in the browsers where it was generated or imported);
- an approver's personal key signs each approval or rejection (it stays in the approver's browser);
- the Gateway's identity signs the request, the execution, the result and the receipt on the organization's behalf.
The agent signs nothing. Anyone with the file can verify a receipt offline, without a K-Block account, and the dashboard can verify it in your browser.
The parts of K-Block
| Part | What it is |
|---|---|
| K-Block Cloud dashboard | The web application where members set everything up and run it day to day: MCP servers and tools, policies, agents, delegations, approvals, actions, receipts, keys and members. Keys that sign (the organization principal, personal keys) are generated and kept in the browser. |
| Cloud API | The REST API behind the dashboard. Scripts can use it with a management API key, for example to download receipts. |
| Gateway | The MCP server (Streamable HTTP) that agents connect to. It decides each call, waits for approvals, calls your MCP server and records the outcome and the receipt. See The Gateway. |
| Client SDKs | @kblockai/client for TypeScript and JavaScript (with LangChain.js tools under @kblockai/client/langchain) and the Python package kblock (with extras for LangChain, AutoGen and CrewAI). They connect to the Gateway and turn its answers into typed outcomes. Any MCP client can also call the Gateway directly. |
| Receipt verifier | @kblockai/sdk/verify checks a receipt file offline: every signature, identity chain, delegation and workflow step. The dashboard runs the same verifier in your browser. |
Architecture at a glance shows how these parts connect.
What K-Block does not do
Knowing the limits helps you decide what else you need.
- It only sees calls that go through the Gateway. K-Block does not run, watch or sandbox your agent or its model. A tool the agent can reach directly, around the Gateway, is not protected. Give the upstream credential to K-Block (the MCP server's Authorization header, stored encrypted) rather than to the agent.
- It decides on the action, the tool and the amount. Policies do not look at other arguments, at the content the tool returns, or at what the model intended. An approver sees the full arguments; the policy does not.
- It does not validate arguments against the tool's input schema. It rejects only arguments it cannot store or sign safely. Your MCP server must refuse input it does not expect.
- It does not retry calls to your tools. A call whose outcome is unknown is recorded as failed and never sent again; you decide what to do next.
- It forwards only to MCP servers over Streamable HTTP, at a public
httpsaddress. Plainhttpand private, loopback and internal network addresses are refused (URL_NOT_ALLOWEDwhen you register or change the server). - A receipt is not proof that the real-world effect happened. It proves what was requested, approved and executed, what result the tool reported, and which keys signed it, unaltered since. On its own it does not prove which Gateway issued it: compare the issuer with the Gateway KID. See What a receipt proves.
- The agent has no cryptographic identity of its own. The Gateway knows the agent by its gateway API key: whoever holds the key acts as that agent, within the agent's policy and delegation.
- K-Block Cloud cannot recover browser-held keys. It stores only public identities. If the only browser holding the organization principal's key is lost and nobody exported a key backup, an owner replaces the principal and every agent is authorized again.
- A browser-held key is only as safe as its browser profile. No passphrase protects keys stored in a browser: anything that runs code on the dashboard's pages in that profile, such as a malicious browser extension, can use and copy them. See How the browser stores keys.