Hen
Run API requests as files, from the command line or through MCP.
Hen keeps request definitions, assertions, captures, dependencies, and protocol-specific behavior in a single .hen file. It works well for local exploration, CI, and editor or agent integrations.
Table of Contents
Quick Example
name = Test Collection File
description = A collection of mock requests for testing this syntax.
$ API_KEY = $(./get_secret.sh)
$ USERNAME = $(echo $USER)
$ API_ORIGIN = https://lorem-api.com/api
$ ROLE = [admin, user, guest]
---
Some descriptive title for the prompt.
POST {{ API_ORIGIN }}/echo
* Authorization = {{ API_KEY }}
? query_param_1 = value
~~~ application/json
{
"username": "{{ USERNAME }}",
"password": "[[ password ]]",
"role": "{{ ROLE }}"
}
~~~
^ & status == 200
! sh ./callback.sh
This single file can define variables, prompt inputs, mapped requests, assertions, and callbacks. Add > requires: when later requests depend on earlier ones, and use response captures to thread values through a collection.
Installation
This installs both hen and hen-mcp.
Container Image
The repository also ships a container image that includes both binaries.
Build it locally with Docker:
Quick Start
Verify a collection without executing shell commands or network requests:
Run every request in a collection non-interactively:
Emit machine-readable output for scripts or CI:
Run the same collection against a selected named environment:
Verify a collection that references local secret providers without loading the secrets:
CLI
Hen has two primary commands:
hen runexecutes a collection or request.hen verifyparses and validates a collection without making requests.
The default hen [PATH] [SELECTOR] form still works for interactive terminal use, but hen run ... is the clearer choice for CI and automation.
Selection and prompts
- If a directory contains one
.henfile, Hen selects it automatically. - If a collection contains multiple requests, provide an index or
allto bypass the picker. - The text CLI prompts for unresolved
[[ prompt ]]placeholders. --non-interactivedisables selection prompts and fails when required prompt values are missing.- Use repeated
--input key=valueflags to provide prompt values up front. - Use
--env <name>to apply a named collection environment before request execution.
Named environments
Collections can declare named environment overlays in the preamble to swap scalar variable values without editing requests.
name = Example Collection
$ API_ORIGIN = https://api.example.com
$ CLIENT_ID = [[ client_id ]]
env local
$ API_ORIGIN = http://localhost:3000
$ CLIENT_ID = hen-local
---
Get profile
GET {{ API_ORIGIN }}/profile
* X-Client-Id = {{ CLIENT_ID }}
- Environment blocks are only valid in the preamble before the first
---. - Environment overrides can only target previously declared scalar variables in this milestone.
- If no environment is selected, Hen keeps the collection-level defaults.
hen verifyreports available environment names without selecting one.- Structured run output reports the selected environment so CI artifacts show which overlay was used.
Resolution order is:
- Collection preamble scalar assignments.
- The selected named environment.
- Explicit
--input key=valueor MCPinputsvalues for prompt placeholders. - Prompt defaults declared with
[[ name = default ]]. - Runtime dependency captures and callback exports for downstream requests.
Local secret providers
Hen now supports local secret references inside scalar assignments so collections can stop shelling out for simple secret lookup.
$ API_TOKEN = secret.env("HEN_API_TOKEN")
$ CLIENT_ID = secret.file("./secrets/client_id.txt")
secret.env("NAME")reads one environment variable at run time.secret.file("PATH")reads one UTF-8 text file relative to the collection working directory and strips one trailing line ending.- Repeated secret references are cached once per run after the first lookup.
- Secret references are only resolved during runs, not during
hen verify. - Secret reference arguments are string literals in this slice; interpolation inside
secret.env(...)orsecret.file(...)is intentionally out of scope. - Supported providers in this slice are
envandfile. OS keychain remains a stretch goal and is not implemented.
Safe output redaction
Hen automatically redacts values loaded through secret.*(...) plus built-in sensitive header names such as Authorization, Proxy-Authorization, Cookie, Set-Cookie, and API-key style headers across text, JSON, NDJSON, JUnit, transcripts, and retained artifacts.
Use preamble redaction rules to broaden that default policy without changing request execution:
redact_header = X-Session-Token
redact_capture = SESSION_ID
redact_body = body.session.accessToken
redact_header = NAMEadds one header name to the built-in masked header set.redact_capture = NAMEtreats captured or exported values under that name as sensitive in the current request and in downstream dependent requests that reuse them.redact_body = body.pathorredact_body = json(body.payload).tokenmasks a specific response-body value even when you do not export it.- Redaction rules are additive, only valid in the collection preamble before the first
---, and are validated structurally byhen verify. - See examples/redaction_rules.hen for a concrete collection.
Useful options
--body none|selected|failed|allcontrols how much of the request and response bodies appear in text output and retained artifacts.--output text|json|ndjson|junitselects human or machine-readable output.--parallelruns independent requests concurrently.--max-concurrency Nthrottles parallel execution.--continue-on-errorkeeps unaffected dependency branches running.--benchmark Nbenchmarks a request instead of running it once.--exportrenders the request as a curl command.--verboseincludes more detail in text output and enables debug logging.
Schema validation
Hen supports collection-local scalar and schema declarations plus built-in targets such as UUID, EMAIL, NUMBER, DATE, DATE_TIME, TIME, and URI.
scalar HANDLE = string & len(3..24) & pattern(/^[a-z][a-z0-9_]*$/)
schema User {
id: UUID
email: EMAIL
handle: HANDLE
}
For the full declaration grammar and more examples, see syntax-reference.md and the schema examples under examples/.
MCP Server
Hen also ships with hen-mcp, a stdio MCP server for editors and agents. It is intentionally non-interactive:
- use
henfor terminal-first, prompt-driven workflows - use
hen-mcpwhen an MCP client needs structured access - provide any
[[ prompt ]]values explicitly through tool inputs
Running the server
If hen-mcp is on your PATH:
From a checkout of this repository:
For normal MCP client usage, prefer a compiled binary:
Example configuration
VS Code uses .vscode/mcp.json:
Claude Code uses .mcp.json:
If hen-mcp is installed globally, command can simply be hen-mcp.
Exposed MCP surface
run_hen: run a collection or request non-interactivelyverify_hen_syntax: validate a file or inline source without executionget_hen_authoring_guide: return built-in usage or syntax docshen://authoring-guideandhen://readme: built-in resources for clients that read docs directly
run_hen accepts the same explicit environment selection used by the CLI:
Authoring Model
Hen files are plain text collections made of a preamble and one or more requests separated by ---.
Core concepts
- Variables:
$ NAME = value, shell substitutions with$(...), local secret references such assecret.env("NAME")andsecret.file("./path"), prompt placeholders with[[ name ]], and simple arrays for mapped requests. - Named environments:
env <name>blocks in the preamble override previously declared scalar variables for a selected run. - Redaction rules: preamble-level
redact_header,redact_capture, andredact_bodyextend the default safe-output masking policy without changing request behavior. - Requests: HTTP by default, plus explicit
protocol = graphql,protocol = mcp,protocol = sse, andprotocol = ws. - Captures:
& body.token -> $TOKENstores response data for later requests, assertions, and callbacks. - Assertions:
^lines validate status, headers, body fields, structural JSON matches, and schema targets. - Dependencies:
> requires: Request Namecreates a DAG so setup requests run before dependents. - Fragments:
<< file.henreuses shared request snippets or declarations. - Callbacks:
!lines run shell commands after request execution. - Guards:
[predicate]can gate assertions or fragment imports.
Protocol support
- HTTP: ordinary request and response workflows.
- GraphQL: GraphQL-over-HTTP with
operation,variables, and~~~graphqldocuments. - MCP: MCP-over-HTTP authoring with generated JSON-RPC envelopes and reusable sessions.
- SSE: named streaming sessions with
receivesteps and timeout windows. - WebSocket:
open,send,exchange, andreceiveflows over a named session.
Full syntax guide
The complete authoring grammar lives in syntax-reference.md. That file covers:
- variables and prompts
- headers, query parameters, form data, and body blocks
- protocol-specific directives
- declarations, captures, assertions, callbacks, and dependencies
Examples
The fastest way to learn the format is to run the included examples:
- examples/lorem.hen: basic HTTP requests
- examples/environment_overrides.hen: named environment overlays
- examples/local_secrets.hen: local env and file secret providers
- examples/redaction_rules.hen: additive safe-output redaction rules
- examples/json_response_captures.hen: captures and JSON paths
- examples/filtered_selector_variables.hen: selector variables in captures and assertions
- examples/schema_scalar_checks.hen: scalar validation
- examples/schema_object_validation.hen: object schema validation
- examples/schema_root_array_validation.hen: root-array schemas
- examples/schema_fragment_reuse.hen: declaration reuse through fragments
- examples/structural_json_matching.hen: structural JSON assertions
- examples/graphql_protocol.hen: GraphQL authoring
- examples/mcp_protocol.hen: MCP-over-HTTP sessions
- examples/sse_protocol.hen: server-sent events
- examples/ws_protocol.hen: WebSocket sessions