MCP Server Guide
Overview
Section titled “Overview”@humanauth/mcp is a Model Context Protocol (MCP) server that exposes three HumanAuth tools to any MCP-compatible agent — Claude Code, Cursor, or any client that speaks stdio. No code changes: add the server to your client config and your agent can put a human in the loop before it acts.
- human_authorize — request a binary approve/deny from a human or group. Blocks until the request is decided, expires, or is cancelled, then returns the decision plus a receipt id.
- human_collect — request data from a human. Blocks until they respond. Schema-driven forms are not shipped yet — see Not yet supported.
- human_inform — send a fire-and-forget notification. Returns immediately without waiting for a response.
The server talks to the HumanAuth platform over HTTPS with your tenant API key, runs over stdio, and keeps no local state.
Prerequisites
Section titled “Prerequisites”- A HumanAuth tenant and API key (
ha_…) — see HumanAuth in 5 Minutes. - An MCP-compatible agent (Claude Code, Cursor, or any stdio MCP client).
Configuration
Section titled “Configuration”The server reads your API key from the HUMANAUTH_API_KEY environment variable and exits at startup if it is unset — the process prints a setup hint and returns a non-zero status. Provide the key through your client’s env block:
{ "mcpServers": { "humanauth": { "command": "npx", "args": ["@humanauth/mcp"], "env": { "HUMANAUTH_API_KEY": "ha_live_…" } } }}The three keys — command: "npx", args: ["@humanauth/mcp"], and the HUMANAUTH_API_KEY env — are the same for every client. Only the config file location differs:
- Claude Code —
~/.claude/settings.json - Cursor —
.cursor/mcp.jsonin your project root - Any other stdio MCP client — wherever it stores server definitions
To smoke-test it directly:
HUMANAUTH_API_KEY=ha_live_… npx @humanauth/mcpOn a healthy start it produces no output — it waits for an MCP client over stdin/stdout (JSON-RPC). With the key missing it prints the required-variable message and exits.
Each tool returns its result as a single text content block whose text is a JSON string; the agent reads the fields described below out of that JSON. Errors return the same way with the block flagged isError.
human_authorize
Section titled “human_authorize”Request binary authorization from a human or group. Blocks until the target approves or denies, the request expires, or it is cancelled.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
target | string | Yes | — | HumanID (hu_…) or GroupID (grp_…) to request authorization from |
action | string | Yes | — | Short action identifier (e.g. deploy-to-prod, send-email) |
description | string | Yes | — | Human-readable explanation of what you want to do and why |
severity | "low" | "medium" | "high" | "critical" | No | "medium" | How sensitive/risky the action is; higher severities show more prominently to the human |
ttl | number | No | 300 | Timeout in seconds (min 10, max 3600) |
Returns — a JSON object in the text block:
- Decided —
decision("approved"or"denied"),request_id,receipt_id,decisions(per-approver detail), anddecided_at. - Expired —
decision: "expired",request_id, and amessage. - Cancelled —
decision: "cancelled"andrequest_id. - Error —
error(code),status(HTTP status, when applicable), andmessage; timeouts returnerror: "timeout".
Example decided result:
{ "decision": "approved", "request_id": "req_a1b2c3d4e5f6a7b8", "receipt_id": "rct_a1b2c3d4e5f6a7b8", "decisions": [ { "human_id": "hu_karthick_7x2f", "decision": "approved", "device_id": "dev_a1b2c3d4e5f6a7b8", "decided_at": "2026-08-10T17:04:22Z", "reason": null } ], "decided_at": "2026-08-10T17:04:22Z"}receipt_id is the handle to the signed receipt. To actually gate the action, forward it (or the receipt JWS) to your resource server and verify it there with @humanauth/verifier.
human_collect
Section titled “human_collect”Request data from a human. Blocks until they respond, the request expires, or it is cancelled.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
human_id | string | Yes | — | HumanID (hu_…) to collect data from |
action | string | Yes | — | Short action identifier |
description | string | Yes | — | What data you need and why |
severity | "low" | "medium" | "high" | "critical" | No | "medium" | How sensitive the request is |
ttl | number | No | 600 | Timeout in seconds (min 10, max 3600) |
Returns — request_id, status ("decided" | "expired" | "cancelled"), result ("approved" | "denied" | "expired"), and receipt_id.
human_inform
Section titled “human_inform”Send a notification to a human. Fire-and-forget — returns as soon as the notification is dispatched, without waiting for acknowledgement.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
human_id | string | Yes | — | HumanID (hu_…) to notify |
action | string | Yes | — | Context identifier (e.g. job-complete, payment-processed) |
description | string | Yes | — | The notification message |
severity | "low" | "medium" | "high" | "critical" | No | "low" | Notification urgency |
There is no ttl parameter — inform does not wait for a response.
Returns — ok: true, request_id, and a message:
{ "ok": true, "request_id": "req_a1b2c3d4e5f6a7b8", "message": "Notification sent" }Long-running approvals (MCP Tasks)
Section titled “Long-running approvals (MCP Tasks)”A human takes seconds to respond — sometimes hours — and no LLM tool call can sit blocked that long. So in @humanauth/mcp@0.3.0, human_authorize and human_collect are task tools under the MCP Tasks extension (SEP-2663, io.modelcontextprotocol/tasks), declared with taskSupport: "optional":
- A Tasks-capable client gets back a task handle (
resultType: "task"plus ataskId) and pollstasks/getuntil the request reaches a terminal state — no single call is held open across the whole approval window. - A client without Tasks is served synchronously by the server: the call blocks until the request is decided, expires, or is cancelled, exactly as described above.
Either way the outcome is the same. When the task completes, its result carries the signed receipt (receipt_jws) — forward it to your resource server and verify it with @humanauth/verifier before anything irreversible runs.
Severity
Section titled “Severity”All three tools take a severity of low, medium, high, or critical; higher severities surface more prominently to the human. Reach for the higher end on production changes and irreversible or security-sensitive actions, and the lower end on routine or informational ones. The default is medium for human_authorize and human_collect, and low for human_inform.
Environment variables
Section titled “Environment variables”| Variable | Required | Default | Description |
|---|---|---|---|
HUMANAUTH_API_KEY | Yes | — | Tenant API key (ha_…). The server exits at startup if it is unset. |
HUMANAUTH_BASE_URL | No | https://api.humanauth.ai | Override the platform base URL (self-hosting or testing). |
Not yet supported
Section titled “Not yet supported”- Schema-driven collect.
human_collectis a stub over authorize (intent=collect); it accepts no field schema and returns an approve/deny-shaped result. There are noselect/number/checkboxform fields. - Groups on collect and inform. Only
human_authorizeaccepts a group target (grp_…).human_collectandhuman_informtake a singlehuman_id. - Notification categories.
human_informhas no category or type parameter; it sends the message with the given severity. - Enforcement. As noted above, these tools request and report — they do not gate. Pair them with
@humanauth/verifierat the resource server.
Troubleshooting
Section titled “Troubleshooting”Server exits immediately (“HUMANAUTH_API_KEY environment variable is required”)
Section titled “Server exits immediately (“HUMANAUTH_API_KEY environment variable is required”)”The key is not reaching the process. Confirm the env block in your MCP config, or export HUMANAUTH_API_KEY before launching the server manually.
Server not recognized by the client
Section titled “Server not recognized by the client”Check the config path for your client and that npx @humanauth/mcp resolves — the first run downloads the package. Launch it manually with the key set to confirm it starts cleanly; if it does, the problem is in the client configuration.
Requests never resolve or time out
Section titled “Requests never resolve or time out”human_authorize and human_collect block until the human responds or the TTL elapses. Make sure the target hu_… / grp_… has an enrolled approver device with notifications enabled, and raise ttl (up to 3600 seconds) for slower approvers.