- Agent SDK: an agent you write in TypeScript, holding its own key. It can also govern MCP servers you did not write.
- MCP: an MCP client, such as Claude Code, Claude Desktop or Cursor, acting as the agent. Humanos holds its key.
How a call is decided
Humanos decides each call in this order:- The agent. A suspended or retired agent, or a revoked key, is refused whatever the tool.
- The policy. The tool’s access, set on the dashboard’s Policy page:
A control is a set of rules over a call’s inputs, chosen for a tool on the Policy page. A control of type Mandate also declares the limits a person sets when granting a mandate under it, such as a refund cap.
The SDK steps need the Quick Start setup: the SDK installed, and an API key with its Signature Secret.With Humanos grades how the key is held and returns the grade as Declare again whenever the tools change. A tool left out of a service’s new declaration is kept and marked as no longer declared; For each call the guard:An upstream is a remote server (
npx humanos init and a framework adapter, or via(), registering, declaring and guarding are done for you: see Agent SDK. The steps below build each piece yourself, for an agent that needs to.1. Register the agent
The agent’s key is its identity.registerAgent proves the agent holds the key and returns its DID; every call after is signed with that key.granted. storedSoftwareKey creates the key in the file on the first run and loads it on every run after, so registering again returns the same DID: a new key is a new agent, and its mandates stay with the old one. Keep the file like the API key, out of the repository. Where no file persists, softwareKeyFromJson reads the file’s content from a variable; softwareKey() creates a key that lasts only as long as the process. Any key store works through the ViaAgentKey interface (publicJwk, sign, evidence), so a key that never leaves its KMS or enclave signs the same way.2. Declare its tools
Humanos governs the tools the agent declares. Declare each one as its server describes it: name, description,inputSchema, outputSchema and annotations, grouped by service.{ emptyServices: ["Files"] } marks every tool of a service the agent stopped using. A service takes at most 500 tools per declaration.3. Set the policy
Until you set the policy, nothing is enforced: every tool follows the default, which allows it.- In the dashboard, open Policy, under Agents in the sidebar. The tools the agent declared are listed there.
- Choose Allow, Restrict, Delegate or Deny for each tool.
- For a tool set to Restrict, choose the control whose rules it runs within. A tool set to Delegate runs under any mandate a person grants, a plain authorization included; choose a mandate control to require one under it.
- To refuse every tool you have not set, change the policy’s default to deny.
- Publish the policy. Nothing changes for your agents until you do. The dashboard asks you to confirm with your passkey or a code sent to your email; only a manager or an owner can publish.
getPolicy(client, did) returns the policy as the agent meets it: each declared tool’s access, its meaning in one sentence, and the controls behind it.4. Guard every call
- Picks the mandate the call presents, if any.
policyMandatesreads the policy and the agent’s mandates, and presents one only for a tool set to Delegate. - Gets a single-use challenge, signs the tool and its parameters with the agent’s key, and asks Humanos to verify.
- Runs the tool on allow, then reports the outcome:
completed, orfailedwith the error, which it rethrows. - Throws
ViaDeniedErroron deny, withreasonand the ruleevaluations. The tool never runs.
guard.interceptTools(tools) replaces each function in place; a guarded tool returns its own result and throws ViaDeniedError on a deny.mode: "observe" runs the whole check and records every decision, but runs the tool even when the decision is deny. Use it to see what a policy would block before enforcing it.5. When a tool needs a person’s mandate
A call to a tool set to Delegate with no mandate covering it is deniedmandate_required, and ViaDeniedError.request says what to ask for:- A guardrail (
kind: "guardrail"): a mandate under one of the controls inactionIds.getActionreturns the control’s limits; your agent fills in their values. - An authorization (
kind: "authorization"): with no control for the tool, a mandate in plain words. It allows one call and ends in an hour, unlessmaxExecutionsandvalidUntilsay otherwise.
waitForMandate returns once the person decides (ACTIVE, REJECTED or INACTIVE), or still PENDING at the earlier of the consent link’s expiry and ten minutes; until changes the deadline and signal stops the wait. The call after an approval presents the new mandate on its own. mandateStatus checks once, for an agent that waits its own way.6. Govern MCP servers you did not write
startViaMcpProxy puts the guard in front of MCP servers you connect to but did not write. It merges their tools, declares them to Humanos by service, and verifies every call before it reaches the server.url, headers) or a local one (command, args, env). Your MCP host connects to the proxy as it would to any MCP server. The proxy presents a mandate only for a tool set to Delegate: the one whose control lists the tool, or whose person allowed it. Every other call is decided on the policy alone.For a remote server, an allowed call also carries its call proof: the proxy sends it with the call, in _meta and in the Humanos-Call-Proof and Humanos-Agent-Record headers, so the server can check that Humanos verified the call.7. Test without a platform
createTestVerifier decides calls in process, with the same rules and policy the connector applies.Deny reasons
A deny is a decision, not an error: the agent asked for something it is not allowed to do.