# Start here

> One page per reader, whether you write a pack, build a runtime, adopt packs in a host or are weighing HCP, plus one-step setup for your agent.

<section className="docs-hero">
<h1>Documentation</h1>
<p className="docs-hero-lead">A Harness Pack is three files that describe a CLI you already ship, so a person, an agent and a product UI all run it as one governed command. Pick the page for what you do and start there.</p>
</section>

## Choose your path

<Cards>
  <Card title="Build a pack" href="/docs/packs">
    You ship a CLI and want agents, people, and product UIs to run it as one governed command.
  </Card>
  <Card title="Implement a runtime" href="/docs/runtimes">
    You build the product that loads packs, enforces grants, and runs them under your own auth.
  </Card>
  <Card title="Adopt in a host" href="/docs/hosts">
    You make an agent product and want it to install packs as easily as it adds an MCP server.
  </Card>
  <Card title="Evaluate HCP" href="/docs/introduction/what-is-hcp">
    You want to know what HCP is, which problem it solves, and where it sits beside Agent Skills and MCP.
  </Card>
</Cards>

## Set up your agent

Every host gets one Skill and one tool named `hcp` that reaches every pack you
activate, or your agent can do the whole setup from
[one prompt](/docs/quickstart#let-your-agent-do-it).

<HostSetup />

## Look something up

[Concepts](/docs/concepts) explains the objects every call passes through, and
[Design principles](/docs/introduction/design-principles) the twelve laws behind
them. For lookups, the [CLI reference](/docs/reference/cli) has every `hcpx`
command and exit code, [Manifest fields](/docs/packs/manifest) every
`surface.json` field, [Pack classes](/docs/packs/best-practices#pack-classes)
five shapes to copy, the [glossary](/docs/reference/glossary) every term, and
the [specification](/docs/specification) what packs, runtimes and hosts must do.

## For agents

Every page has a Markdown twin: append `.md` to the URL or send
`Accept: text/markdown`. The full corpus is at [`/llms.txt`](/llms.txt) and
[`/llms-full.txt`](/llms-full.txt), search at `/api/v1/search?q=`, and a
read-only MCP server at `/mcp` offers `list_pages`, `search`, and `get_page`.

Source: https://harnesscontextprotocol.io/docs

---

# What is HCP?

> The open standard for installing a capability into any agent harness as a portable pack, and the integration problems it removes.

Harness Context Protocol (HCP) is an open standard for installing a capability
into an agent harness, meaning whatever drives an agent: a coding assistant, a
chat product, an automation runner or a TypeScript tool loop. Teaching and
transport already had open standards while install was left to each product, so
HCP gives install one portable unit, the Harness Pack, and a short list of rules
for the runtime. The [Quickstart](/docs/quickstart) gets you running.

## Why HCP exists

An agent stack has three jobs, and most integration pain comes from the one
that had no standard.

| Job | What it needs | Standard |
|-----|---------------|----------|
| Teach the agent how to use a capability | Progressive disclosure, examples, anti-patterns | [Agent Skills](https://agentskills.io) |
| Connect the model to tools over a wire | Client and server transport, a tool catalog, sessions | [MCP](https://modelcontextprotocol.io) |
| Install a capability into the harness | Identity, authorisation, credential binding, policy, a sandbox, and an outbound loop | HCP |

Without an install layer, each harness adds a capability (planning a route,
reading a mailbox, sending a bank transfer) its own way: a config file, an MCP
server or setup steps pasted into an instructions file. Teams with more than a
handful of domain CLIs in front of agents then hit the same problems, which
better prompts or a faster transport cannot fix.

| Without an install layer | With HCP |
|--------------------------|----------|
| Every product invents a private connector schema, so one CLI is described three ways for three hosts | One folder that any conforming runtime activates |
| OAuth tokens and API keys sit in environment variables and Skill files, because nothing defines where a credential lives | The pack names a Binding mode and the runtime mints |
| A UI button and an agent's bash call are different APIs for one operation, with different auth and audit | Every caller, a UI included, runs one command under one Policy and one Binding |
| A CLI that already prints `--json` gets an MCP server wrapped around it, one more process and schema to keep in sync | The CLI is the harness and MCP an optional export |
| Each new host copies a 40-line setup table into `AGENTS.md`, and the copies drift | `hcpx adopt cursor` or `hcpx adopt opencode` writes one Skill and one bash tool, `hcp`, for every activated pack, so a pack added later adds no tools to the host |
| A write fires from a hallucinated turn because nothing forces a confirmation | A transfer without an explicit `confirm` is refused before any adapter runs, whoever asked |
| Changing transport or UI means rewriting the capability, because its identity is tied to the transport | Identity is the `pack_id` and command names, so packs survive a change of MCP or UI message format |

Wrapping a domain CLI this way takes an afternoon: describe the commands you
already ship and add a Policy row for each, and the same binary reaches a
terminal, an agent and a product UI. [Landscape](/docs/introduction/landscape)
shows how HCP composes with Agent Skills, MCP, Agent Plugins, A2UI and A2A.

## The Harness Pack

A pack is a directory of three files, and that is all an author writes.

| File | Role | What you write |
|------|------|----------------|
| `surface.json` | Manifest | Pack id, adapter kind, Binding mode, and 5 to 12 named commands |
| `policy.json` | Policy | One row per command and per trigger: `read` or `write`, which land it touches, and whether it needs confirmation |
| `HARNESS.md` | Skill | One screen that teaches the hot path and the anti-patterns |

A pack holds no secrets, allow-lists, tenant ids or server code. It describes an
existing CLI or API and what it may do, so an agent learns the pack without a
hand-built tool catalog, and a public pack with `find`, `plan`, `show`, `status`
and `follow` needs no login. Policy stays at two axes and a confirm flag,
because scale lives in the runtime's grants.

```json title="packs/transit/surface.json"
{
  "id": "transit",
  "kind": "wrap",
  "cliBin": "transit",
  "binding": { "mode": "public" },
  "verbs": {
    "plan": {
      "description": "Time-first options between two places.",
      "cli": ["transit", "plan", "{{from}}", "{{to}}"]
    }
  }
}
```

## Two loops on one pack

Execute is inbound. Someone runs one of the pack's named commands in a CLI form
(`transit plan A B --json`) or a JSON form
(`{ "pack_id": "transit", "verb_key": "plan", "args": {…} }`), and both are the
same command under the same Policy.

Sense is outbound and kept apart from Execute. A pack may announce a change in
the world by raising a trigger such as `new_item` or `binding.degraded`,
declared in the same Policy file and delivered to a runtime sink only under its
own grant. A trigger can never mint credentials or spend money, and an Invoker
that acts on one still goes through Execute and its gates.
[Concepts](/docs/concepts) covers both loops and the objects they share.

## What a runtime does with a pack

When a runtime installs a pack, it:

1. Finds the three files and validates them (lint).
2. Resolves the Binding the pack asks for and mints the credential.
3. Runs named commands through either face, under one Policy and one set of
   capability grants.
4. Receives Sense if the pack declares triggers, under a separate grant that can
   neither mint nor act.
5. Reports availability as four flags (`installed`, `activated`, `connected`,
   `executable`), with a reason for every denial, so an agent knows what it may
   run before it plans.

The protocol stops there. Catalog storage, the credential hub, RBAC tables,
inbox UI and trigger delivery belong to the runtime, which may keep them
private, and [Design principles](/docs/introduction/design-principles) gives the
reasoning behind each rule.

## Status

HCP/1.1 is a draft that includes the accepted wrap-hardening and exec-plane
RFCs. The reference TypeScript SDK, runtime and CLI implement it plus the draft
RFCs on the [Roadmap](/docs/community/roadmap#drafts-the-reference-sdk-implements),
as a private preview on GitHub Packages whose versions
[Implementations](/docs/community/implementations) lists. The
[Specification](/docs/specification) section is generated from the normative text in
the protocol repository, and [Versioning](/docs/introduction/versioning) explains how
versions move.

Source: https://harnesscontextprotocol.io/docs/introduction/what-is-hcp

---

# Design principles

> Twelve laws every conforming pack, runtime and host follows, the failure each one prevents, and what HCP/1.x leaves out on purpose.

HCP is small on purpose: twelve laws, each mapped to a lint check or a runtime
behaviour and each there because the alternative was tried and broke. The
[non-goals](#non-goals) list what HCP/1.x leaves out, and only an RFC changes
either list.

## L1: The CLI is the harness

A capability enters HCP as a CLI, or as an in-process SDK that projects to one,
and every command has a CLI face with structured output (`--json`, or JSON by
default). That is the interface people and CI already script, so agents share
it. MCP is an optional export from a pack and never the install path.

## L2: The twin face is identity

`transit plan A B --json` and `{ "pack_id": "transit", "verb_key": "plan", … }`
are the same command under the same Policy, Binding and Adapter, and `verb_key`
is the command name with no second ontology behind it. No Invoker gets a code
path of its own, which is where separate auth and audit would grow.

## L3: Named commands, 5 to 12, never a PATH wrap

A pack declares a closed set of commands, never a whole binary behind an
`allowedSubcommands` allow-list or a catch-all `run` or `exec -- <argv>`,
because Policy and Skill can only be written for things with names. A
pass-through wrap is a bootstrap step, and the pack conforms only once it is
promoted to named commands.

## L4: Binding mints; packs never hold secrets

A pack names a Binding mode (`public`, `user`, `local`, `mothership`, `mock`)
and the runtime's hub mints the credential. Exec fails closed when the mint
fails, with no silent fallback to environment tokens, and mock mode is explicit
in the envelope, never an environment flag. A mode is portable, while a secret
in a manifest, Skill or host config leaks, drifts and escapes audit.

## L5: Three lands; entity arguments are not tenants

`privacy.land` is `personal` (private to the actor), `brain` (a shared project)
or `local` (a workspace directory). Tenant and project come from the
authenticated Principal's session, so an argument such as `--entity` picks an
account inside the Binding's scope but never the tenant, and one user cannot
reach another user's data through a pack argument.

## L6: Writes are gated

A write that must not fire from a hallucinated turn declares `confirm: true`,
passed as `--confirm` or `--apply` on the CLI face and `confirm: true` on the
JSON face, and the runtime checks it before any Adapter runs, whoever invokes.
A dry run is still `operation: write` and must not persist. Health commands
(`doctor`, `status`) report and never mint, and exit `0` for ok, `2` for warn
and `1` for fail.

## L7: Execute and Sense are two loops on one pack

Inbound commands and outbound triggers share Policy, Binding and Principal but
never merge into one tool, because an event that could execute could also spend
money. A trigger cannot run a command, and an Invoker that reacts to one still
goes through Execute.

## L8: Sense must not mint, and polling is not Sense

A trigger cannot mint credentials or bypass Binding. Calling `status` or
`follow` repeatedly is Execute, and uptime monitors, UI refresh timers and
control-plane ticks are not pack triggers, otherwise every pack would claim to
sense by burning tokens in a loop.

## L9: Control and runtime are two packs

Admin, fleet and operator CLIs run in a second pack with `plane: control` and
Binding `mothership`, never silently available to an end-user Principal. That
closes the classic privilege-escalation bug in agent tooling, an end-user agent
inheriting operator credentials because a binary happened to be on `PATH`.

## L10: The Skill is one screen; Policy covers everything

`HARNESS.md` teaches the declared commands on one screen (a lead, a hot-path
table, copy-paste examples, anti-patterns, pointers), because a Skill that grows
into a manual stops being read. Every command and every trigger has a Policy
row or lint fails, since a runtime cannot reason about a command without one.

## L11: Invokers never redefine Policy or Binding

Agents, APIs, UIs and people run the same command, each carrying a Principal
whose grants the runtime evaluates, and none adds its own auth or policy for
it, because when one command has two policies the weaker one decides.

## L12: One living repo per pack; proof is a script

A pack id maps to one maintained repository, and the pack proves conformance
with a script CI can run: lint, one read, one gated write in mock or paper mode,
one live deny without confirm and, if it declares Sense, one announce with the
grant on and one deny with it off. A claim in Markdown does not count, because a
pack that cannot prove itself drifts away from its Skill.

## How the laws become checks

| Law | Enforced by |
|-----|-------------|
| L3 | Lint: `wrap requires named commands or pass_through` |
| L4 | Runtime: fail-closed mint; the sandbox denies env-flag mocks |
| L5 | Runtime: `brain` requires `project_id`; the Policy schema rejects unknown lands |
| L6 | Runtime: `confirm required` before the Adapter |
| L9 | Lint: `control plane packs default Binding mothership` |
| L10 | Lint: `missing HARNESS.md`, `verb … missing from policy.json`, `stimulus … missing from policy.json` |
| L12 | [Testing](/docs/packs/check#testing) and [Conformance](/docs/specification/conformance) |

## Non-goals

HCP/1.x leaves these out on purpose.

| HCP does not | Why |
|--------------|-----|
| Make MCP the install unit | MCP is a transport HCP can export to, and a transport carries no Policy, Binding, Sense or Skill |
| Define a verb ontology | Ontologies rot, so packs name commands after the CLI they wrap, and `read`/`write` is the only class a runtime needs |
| Put tenant, user or role identifiers in packs | A pack is shared across tenants, so anything tenant-specific in it is a leak or a lie; tenant comes from the session and grants from the runtime |
| Carry credentials | Packs are copied, published and read by agents, so they hold no tokens, key files or `.env` files ([L4](#l4-binding-mints-packs-never-hold-secrets)) |
| Mandate a network client/server split | A runtime may be in-process, a CLI or a hosted service, and the smallest useful one is a function call |
| Require UI or A2UI | Agents and CLIs invoke most capabilities first, so `transports` and host rendering stay optional |
| Wrap `PATH` | The sandbox does not resolve bare names on the host `PATH`, or an agent could run a binary nobody allow-listed |
| Merge teaching packs with capability packs | A Skill that only teaches is an Agent Skill, and Policy needs commands to govern |
| Bake product schemas into core types | Ledger, CRM and database record types belong to the packs that wrap those products |
| Name vendors in the specification | An open standard names roles, so hosts and runtimes appear as examples only in the Hosts section |
| Define a signed registry or pack versions (yet) | Packs are directories identified by `pack_id` while the pack model and the two loops come first. Signed fetch by `pack_id@version` is on the [roadmap](/docs/community/roadmap) for HCP/2.0, and the [community catalog](/docs/packs/share#catalog) indexes directories and CLI scaffolds without being a registry |
| Poll on behalf of packs | Sense is push, and polling for a pack would turn every read into a silent background write to the runtime's own store |

A pack author who needs one of these is probably building a runtime feature
([Build runtimes](/docs/runtimes)). A runtime builder may add any of them inside
the runtime but may not require packs to change for it, and moving the boundary
takes an [RFC](/docs/community/governance#rfc-process) that names the item and
the cost of keeping it out.

Source: https://harnesscontextprotocol.io/docs/introduction/design-principles

---

# Landscape

> Where HCP sits beside Agent Skills, MCP, Agent Plugins, A2UI and A2A, the question each one answers, and how they fit together in one stack.

HCP sits beside five other open agent standards, each of which answers one
question well, and trouble starts when one is stretched to answer the others.
HCP answers the install question
([why that needs a standard](/docs/introduction/what-is-hcp#why-hcp-exists)) and
composes with the rest, which the sections below lay out standard by standard.

## The map

| Standard | Question it answers | Unit | Governance |
|----------|--------------------|------|------------|
| [Agent Skills](https://agentskills.io) | How does an agent learn to use a capability? | `SKILL.md` directory | Open spec, multi-vendor clients |
| [MCP](https://modelcontextprotocol.io) | How does a model call a tool over a wire? | MCP server (stdio / Streamable HTTP) | Open spec, steering committee |
| [Agent Plugins](https://agent-plugins.org) | How are Skills and MCP servers packaged so clients can load them consistently? | Plugin directory (`plugin.json`, `skills/`, `mcp.json`) | Open spec, TSC |
| [A2UI](https://a2ui.org) | How does an agent render generative UI? | UI messages / catalogs | Open spec |
| A2A | How do agents talk to each other? | Agent cards, tasks | Open spec |
| HCP | How does a harness install, authorise, bind, sandbox, and execute a capability, and hear from it? | Harness Pack | Open spec, RFC process |

## How they compose

```text
Agent Skill (SKILL.md)              teach
        │
        ▼
Harness Pack (HCP)                  install
  commands · Policy · Binding · Sense · Skill · Adapter
        │                    │
        ▼                    ▼
   CLI / JSON face      optional MCP export        transport
        │
        ▼
   host agent loop · UI (A2UI) · other agents (A2A)
```

A pack's Skill (`HARNESS.md`) follows the Agent Skills principles (short lead,
examples, anti-patterns, pointers) and is narrower on purpose, because it
teaches how to invoke this pack's commands correctly. `hcpx activate` copies it
to `$HCP_HOME/skills/<id>/SKILL.md`, so hosts that already speak Agent Skills
get progressive disclosure with no extra work. A Skill without a pack still
teaches, but a pack without a Skill fails lint.

A runtime can expose packs to [MCP-only hosts](/docs/hosts/mcp) through the
reference export, a single MCP tool `hcp` that forwards to the runtime, so every
call still passes Policy and Binding and the pack stays the source of record. A
UI host speaking A2UI invokes pack commands the same way an agent does and may
subscribe to Sense for chrome, but it never owns mint or Policy.
[Projections](/docs/specification/projections) holds the normative rules for
these generated surfaces and exports.

## HCP and MCP

| | MCP | HCP |
|--|-----|-----|
| Category | Transport between model and tool server | Install unit for a harness |
| You ship | A server process (stdio or HTTP) | Three files describing an existing CLI or API |
| Identity of a capability | Tool name on a server | `pack_id` + `verb_key`, projected to CLI, JSON, Skill, and optionally MCP |
| Authorisation | OAuth 2.1 between client and server | Runtime capability grants per Principal; pack declares only effect class and land |
| Credentials | Server holds them | Runtime hub mints them; pack names a mode and never holds a secret |
| Writes | Tool annotations (hints) | Policy `operation: write` + `confirm` enforced by the runtime before any adapter runs |
| Outbound events | Notifications within a session | Sense: declared triggers, separate grant, cannot mint or act |
| Human at a terminal | Not a first-class path | The CLI face is the primary face |
| Sandbox | Client's concern | Default; bin allow-list and cwd jail are part of the profile |

The reverse does not hold: an MCP server cannot be the unit you install, because
it carries no Policy, Binding mode, Sense declaration or CLI face.

## HCP and Agent Plugins

Agent Plugins defines a portable directory for Skills and MCP servers, and HCP
defines one for an executable capability with rules. Both are vendor neutral,
and both say "put these files here and clients will find them".

| | Agent Plugins | HCP |
|--|---------------|-----|
| Manifest | `plugin.json` (name, version, metadata) | `surface.json` (id, adapter, Binding mode, commands) |
| Teaching | `skills/*/SKILL.md` | `HARNESS.md` (copied to a `SKILL.md` on activate) |
| Execution | `mcp.json` pointing at servers | Named commands run through an Adapter under Policy |
| Permissions | Left to each client | Policy rows + five capability grants, enforced by the runtime |
| Outbound | None | Sense |

A plugin's directory could carry an HCP pack next to its Skills and MCP
servers, with the pack as what the harness installs and the plugin as how a
client discovers it. That nesting is a proposal on the
[Roadmap](/docs/community/roadmap) and has not shipped.

## Name disambiguation

These names mean something narrower here than elsewhere, and the
[glossary](/docs/reference/glossary) defines the rest.

| Term | Meaning here |
|------|--------------|
| Harness | The thing driving an agent; in HCP, the runtime that installs packs |
| Skill | `HARNESS.md`, the pack's teaching file; or an Agent Skill in general |
| Surface | Legacy filename `surface.json` = the pack manifest. Not a UI surface |
| Agent teaching pack | An agent's identity and instructions. Invokes HCP; is not an HCP pack |
| Connector | Not an HCP type. Vendor SDK glue inside a product runtime |

Source: https://harnesscontextprotocol.io/docs/introduction/landscape

---

# Versioning

> How the protocol, each pack and the reference SDK are versioned apart, what each version promises about compatibility, and how deprecation works.

HCP versions the protocol, each pack and each implementation independently.
HCP/1.1 is the current draft and may still change by RFC, but every wire element
listed below stays stable across HCP/1.x, and packs that conformed to HCP/1
still conform.

## Protocol versions

The protocol is versioned as HCP/`<major>`.`<minor>`.

| Version | Status | Contents |
|---------|--------|----------|
| HCP/1 | Draft, implemented | Pack lattice (three files), Execute twin face, Policy (`operation`, `privacy.land`), Binding modes, five capability grants, sandbox default, CLI bootstrap pack (`hcp-cli`), Skill copy on activate |
| HCP/1.1 | Draft, implemented (current) | Wrap hardening ([RFC 0001](/docs/community/governance#rfc-process)): named-or-pass-through lint, installer command family (`setup.status` / `setup.apply` / `setup.update`), land `local`, Binding `hub` and `mock`, `binding.degraded` enum, `plane: control`, `transports[]`, four-faces naming rule. Wrap exec plane (RFC 0003): `wrap.workspace` |
| HCP/1.2 | Proposed | Pack `version` + `$schema`, extension namespaces, error code registry, path containment. See [Roadmap](/docs/community/roadmap) |
| HCP/2 | Planned | Registry install, capability-grant wire profile, optional MCP export profile, WASM adapters |

The protocol repository tagged `v0.1.0` (HCP/1 with RFC 0001), and HCP/1.x stays
a draft until the specification is declared stable. A minor version adds only
optional fields, lint rules that fail new constructs alone, and recommended
families, while a major version may change required fields or wire shapes and
comes with a migration note.

### Declaring protocol affinity

A pack declares the protocol version it targets in the `hcp` field of its
manifest metadata, and HCP/1.x packs use `1`.

```json
{ "hcp": 1, "pack_id": "transit", "adapter_kind": "wrap" }
```

A runtime rejects a pack whose `hcp` value it does not support and reports the
unsupported version. The reference SDK exposes the constants
`HCP_PROTOCOL_ID = 'hcp'` and `HCP_VERSION = 1`.

## Pack versions

A pack's identity is its `pack_id`, and in HCP/1.x its repository owns its
versions (one living repo per `pack_id`, per
[L12](/docs/introduction/design-principles#l12-one-living-repo-per-pack-proof-is-a-script)).
The runtime store keeps whatever was installed, and `hcpx install` from a newer
checkout replaces it.

Adding a command is backward compatible on the wire, because an Invoker that
does not know the new `verb_key` never calls it and availability reports it per
command. Removing or renaming a command breaks the Skills and host projections
that referenced it, so treat it as a breaking change. A semver `version` field
in the manifest is proposed for HCP/1.2, and a registry that resolves
`pack_id@version` for HCP/2.

## Wire compatibility

| Element | Stability promise |
|---------|-------------------|
| `{ pack_id, verb_key, args }` exec envelope | Stable across HCP/1.x. `surface_id` accepted as an alias of `pack_id` |
| `{ ok, result?, error? }` result envelope | Stable across HCP/1.x |
| Policy tokens `read` / `write`, `personal` / `brain` / `local` | Stable across HCP/1.x; new tokens only by RFC |
| Capability ids (`pack.install`, `pack.connect`, `verb.exec.read`, `verb.exec.write`, `sense.enable`) | Stable across HCP/1.x |
| Binding modes and hubs | Stable across HCP/1.x |
| Availability flags (`installed`, `activated`, `connected`, `executable`) | Stable across HCP/1.x |
| Host tool naming (`<pack_id>__<verb_key>`, dots as underscores) and the single `hcp` tool | Stable across HCP/1.x |
| Manifest filename `surface.json` and field name `verbs` | Retained for compatibility; a rename would be a major version |

## Implementation versions

The reference TypeScript SDK (`@harnesscontextprotocol/sdk`) and CLI follow
semver independently of the protocol, and each SDK release states which
protocol versions it implements. Conformance vectors for each protocol version
ship with the SDK, as [Conformance](/docs/specification/conformance) describes.

## Deprecation policy

A field, token or command family is deprecated by RFC and keeps working for at
least one minor version, and lint warns about a deprecated construct before any
version removes it. Wire aliases such as `surface_id` are accepted indefinitely
within a major version.

Source: https://harnesscontextprotocol.io/docs/introduction/versioning

---

# Quickstart

> Hand setup to your agent or install the CLI yourself, then follow the path for what you build until a read runs and an unconfirmed write is refused.

Every path starts with the HCP CLI, `hcpx`, and ends at the same proof: a read
that runs and a write that refuses without confirmation. Letting your agent do
the setup is fastest; otherwise install the CLI and take the path for what you
build.

## Let your agent do it

Two runbooks on this site are written for an agent to follow from start to
finish. Give Claude Code, Cursor or Codex the prompt and review what it reports.

<AgentPrompts />

| Runbook | Steps | Stops for you when |
|---------|-------|--------------------|
| [`/setup.md`](/setup.md) | Checks `hcpx doctor` and installs the CLI if needed, finds and activates packs, runs the right `hcpx adopt` for its own host, checks availability, runs one read | The CLI cannot be installed, a pack needs a login, or a config outside the repository has to change |
| [`/wrap.md`](/wrap.md) | Reads the CLI's help to check it is agent-ready, scaffolds with `hcpx wrap`, names 5 to 12 commands, writes Policy and the Skill, lints until clean, installs, proves a read and a refused write | The CLI has no JSON output or only an interactive wizard, or it administers infrastructure (a control-plane pack needs your decision) |

Both forbid putting a credential in any file, registering a tool per pack, and
running a write with `confirm=true` you did not approve in the conversation. The
runtime enforces those rules too, so an agent that misreads a runbook still
cannot spend, send or delete on its own.

Agents that look for skills find both without a prompt, as `hcp-setup` and
`hcp-wrap` in
[`/.well-known/agent-skills/index.json`](/.well-known/agent-skills/index.json)
and at the top of [`/llms.txt`](/llms.txt). To keep them in a project, save them
as Skills, under `.cursor/skills/` for Cursor, or reference the two files from
`AGENTS.md` for Codex:

```bash
HCP_DOCS=https://harnesscontextprotocol.io   # the origin you are reading
mkdir -p .claude/skills/hcp-setup .claude/skills/hcp-wrap
curl -s "$HCP_DOCS/setup.md" > .claude/skills/hcp-setup/SKILL.md
curl -s "$HCP_DOCS/wrap.md"  > .claude/skills/hcp-wrap/SKILL.md
```

## Install the CLI

`hcpx` is itself the first pack. The packages are a private preview on GitHub
Packages, so run it from the SDK repository:

```bash
git clone https://github.com/harnesscontextprotocol/hcp
cd hcp && bun install
alias hcpx="bun $PWD/packages/cli/src/hcp.ts"
hcpx doctor --json
```

A host starts the CLI from `PATH`, which an alias does not reach, so put the
package on `PATH` before you adopt a host; `hcpx adopt` says so when it cannot
find the CLI there. With preview access, install from GitHub Packages instead,
where `GITHUB_TOKEN` can read packages and a global install reads the `.npmrc`
in your home directory:

```text title="~/.npmrc"
@harnesscontextprotocol:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
```

```bash
bun add -g @harnesscontextprotocol/cli
hcpx doctor --json
```

The first run creates `$HCP_HOME` (default `~/.hcp`) and installs the `hcp-cli`
bootstrap pack, so there is always something to exec against. Set `HCP_HOME` to
keep a CI or project store apart from your personal one.

```json
{ "ok": true, "cli": "hcpx", "protocol": "hcp", "version": 1, "home": "~/.hcp", "sandbox_default": true, "installed_count": 1, "activated": [], "connected": [], "bootstrap": "hcp-cli" }
```

### One binary, two names

`@harnesscontextprotocol/cli` installs one binary as both `hcpx` and `hcp`, which
behave the same wherever `hcp` is this CLI. Everything the CLI prints uses
`hcpx`, because an unrelated tool also installs a command called `hcp` and may
start instead on a machine with both. Hosts still see `hcp`: one MCP server and
one tool of that name, started as `hcpx mcp`. `hcpx adopt` writes `hcpx` into
host config, or `hcp` for an older install that only answers to that name, and
allow lists and the sandbox accept both.

## Pick your path

Every path uses the two gold packs in `examples/packs/`, `transit` (public read)
and `ledger` (gated write), which are also the conformance suite's fixtures;
[Examples](/docs/packs/examples) shows what each proves.

### Build a pack

You ship a CLI or an API and want agents, people and UIs to run it as one
governed command. Scaffold from a template, or run `hcpx wrap <bin>` for a
binary already on your `PATH`, and continue in [Build packs](/docs/packs).

```bash
hcpx init weather --class public-read
```

### Implement a runtime

You build the product that loads packs, enforces grants, mints credentials and
executes. Add the reference packages with the `.npmrc` above and continue in
[Build runtimes](/docs/runtimes).

```bash
bun add @harnesscontextprotocol/runtime
bun add @harnesscontextprotocol/sdk
```

### Adopt in a host

You make an agent product and want it to install packs as easily as it adds an
MCP server. Activate packs, generate your host's config and continue in
[Adopt in hosts](/docs/hosts).

```bash
hcpx activate transit ledger
hcpx adopt claude --write        # or: codex, cursor, opencode, grok, mcp, agent
```

## What done looks like

Every path ends when this proof passes, and a pack runs the same proof in CI to
show conformance ([Testing](/docs/packs/check#testing)).

```text
lint                                  → no issues
one Execute read                      → { ok: true, result: … }
one gated write without confirm       → { ok: false, error: "confirm required" }
the same write with confirm (mock)    → { ok: true }
availability                          → installed · activated · connected · executable
```

Source: https://harnesscontextprotocol.io/docs/quickstart

---

# How HCP works

> The path every command and trigger takes through a pack, the runtime and a host, and the page that explains each HCP object.

HCP is one harness model: a pack declares what a capability can do, and the
runtime that already drives the agent decides whether each call runs. Commands
come in through Execute and announcements of change go out through Sense, and
both cross the same Policy, Binding and sandbox, so an observation becomes an
action only by passing every gate a typed command passes. Read the two loops
below, then open the object you need from the table at the end.

```text
                          HARNESS PACK
                     (three files, one id)
            ┌─────────────────┴─────────────────┐
            │                                   │
      EXECUTE (inbound)                   SENSE (outbound)
      named commands                      declared triggers
      CLI face + JSON face                stimuli[] + Policy rows
            │                                   │
            └─────────────────┬─────────────────┘
                              │
             Policy · Binding · Principal · sandbox
                              │
        runtime: catalog · grants · mint hub · sink · audit
```

The harness is the runtime: whatever already drives the agent loads packs,
resolves grants, mints Binding and runs Adapters. HCP therefore needs no
network "HCP server" and no daemon, and a company may keep its runtime
proprietary while the pack format and the rules stay open.

## Execute: the inbound loop

An Invoker, whether a person at a terminal, an agent in a tool loop, a product
API or a UI gesture, runs a command: a closed, named operation the pack
declared. Its CLI face and its JSON face are the same command under the same
Policy and Binding ([the twin face](/docs/concepts/command#the-twin-face)).
Before any Adapter runs, the request passes a fail-closed pipeline:

```text
Principal → scope (tenant · project · land) → capability grant
  → confirm (if Policy says so)
  → Binding mint (no ambient env fallback)
  → Adapter → sandbox → structured result + audit
```

Any step may deny with a reason (`denied: missing verb.exec.write …`,
`confirm required`), and then nothing downstream runs
([Execution](/docs/runtimes/execution),
[Execute specification](/docs/specification/execute)).

## Sense: the outbound loop

A pack announces that the world changed by declaring triggers (stimulus refs)
in its manifest, each with its own Policy row. The runtime delivers them by
whatever means it chooses, and only while the `sense.enable` grant is on.

```text
world changes ──► pack announces trigger ──► runtime sink records
                                                │
                                          (optional wake)
                                                ▼
                                  Invoker runs Execute (read → gated write)
```

Sense is emit-only, so a trigger must not mint credentials, bypass Binding or
turn into a command. Polling a `status` command is Execute, and a runtime's
uptime monitor is no pack trigger. The loops stay apart because an event that
could execute could also spend money, while an Execute that owned push would
have agents polling forever and calling it Sense
([Sense](/docs/concepts/sense),
[Sense specification](/docs/specification/sense)).

## What both loops share

Both loops sit on one Policy file with a row per command and per trigger, one
Binding that names the credential mode and the hub that mints it, the Principal
the runtime establishes, and the sandbox, which denies unknown binaries, jails
the working directory and inherits no ambient credentials. Secrets never live
in the pack.

## What a host sees

A host is the agent product that adopts HCP: it presents commands to its agent
loop, invokes Execute and may subscribe to Sense. Coding agents get one Skill
and the `hcpx` binary, so adding a pack adds no tools, while in-process loops
get one typed tool per command the Principal may run
([Adopt in hosts](/docs/hosts)).

## The objects

Each object has one home page. The normative definitions live in
[§1 Terminology](/docs/specification/terminology).

| Object | What it is |
|--------|------------|
| [Pack](/docs/concepts/pack) | The install unit: three files under one id, in one of five [classes](/docs/packs/best-practices#pack-classes) |
| [Command](/docs/concepts/command) | A named operation with a CLI face and a JSON face, identified by `verb_key` |
| [Policy](/docs/concepts/policy) | The pack's rules, one row per command and per trigger |
| [Access](/docs/concepts/access) | What runs without asking, set by the user or operator |
| [Binding](/docs/concepts/binding) | How a command gets credentials at exec time |
| [Principal](/docs/concepts/binding#principal) | Who acts, established by the runtime and never a pack field |
| [Sense](/docs/concepts/sense) | Triggers a pack may announce |
| [Skill](/docs/concepts/skill) | `HARNESS.md`, the one screen that teaches the commands |
| [Adapter](/docs/concepts/adapter) | How a command reaches the world: `wrap`, `composite`, `openapi` or `sense_only` |
| [Invoker](/docs/runtimes/execution#invoker) | Agent, API, UI or a person at the CLI; never redefines Policy or Binding |
| [Runtime](/docs/runtimes) | Loads, authorises, binds, executes and routes Sense |
| [Lifecycle](/docs/concepts/lifecycle) | Lint, install, activate, connect, availability, exec |

Source: https://harnesscontextprotocol.io/docs/concepts

---

# Pack

> What the three files of a pack hold, what never goes in them, where packs live, and the gold packs to copy when you start a new one.

A Harness Pack is what a runtime installs: one directory, one id and three
files that declare a capability's commands, rules and teaching. The pack
declares and the runtime executes, so a pack holds nothing that depends on who
runs it and moves unchanged between runtimes. Start by copying the
[gold pack](#gold-packs) closest to your capability and changing its id, bin,
commands and Binding mode.

## Layout

```text
packs/<id>/
  surface.json    manifest — id, adapter, Binding mode, named commands, optional triggers
  policy.json     rules — one row per command and per trigger
  HARNESS.md      Skill — one screen of teaching
```

The layout is flat, though a runtime may nest directories for its own
operations without the pack knowing. The three names are exact and
case-sensitive, and install fails when one is missing or when Policy does not
cover every command and trigger. `surface.json` is the manifest's legacy
filename, kept for wire stability, and has nothing to do with UI surfaces.

| File | Piece | You fill |
|------|-------|----------|
| `surface.json` | [Manifest](/docs/packs/manifest) | Identity (`id`), how it runs (`kind`, `cliBin`, `wrap`), the credentials it needs (`binding.mode`), the `verbs` map of named commands with their CLI forms, and triggers only for an upstream system that pushes, so most packs have none |
| `policy.json` | [Policy](/docs/concepts/policy) | For every command and trigger: `operation` (`read` \| `write`), `privacy.land`, and `confirm` on writes that must not auto-fire |
| `HARNESS.md` | [Skill](/docs/concepts/skill) | Lead, hot-path table, examples, anti-patterns |

### Optional fields

Add these to the manifest only when the capability needs them.

| Field | When |
|-------|------|
| `stimulusTriggers[]` | The upstream system pushes real events |
| `binding.family` | Related packs should share one login |
| `wrap.agent_env` | The wrapped bin needs environment defaults it already understands |
| `plane: "control"` | Admin or operator CLI, which makes it a second pack |
| `transports: [... "mcp"]` | A runtime will export MCP tools from this pack |
| `pass_through` | Bootstrap only; promote to named commands |
| `openapi` | `kind: openapi` |
| `skill_url` | A hosted copy of the Skill |

### What stays out

- Secrets, a `secrets/` directory or a `.env` file. The pack names a Binding
  mode and the runtime's hub mints the credential.
- User allow-lists, roles, tenant ids and connector types, which belong to
  runtime grants and Principal scope.
- A server, since a pack describes an existing CLI or API.
- A whole binary. A pack names 5 to 12 commands, and `allowedSubcommands`
  pass-through is a bootstrap to promote.
- UI widget catalogs or inbox chrome. A UI host invokes commands from outside.
- A `families/` directory, since family is a manifest field.
- Extra Markdown for scale. A capability scales by adding packs, never by
  growing one.

### Where packs live

| Location | Purpose |
|----------|---------|
| Your repository, `packs/<id>/` | Source of record; one living repo per `pack_id` |
| `$HCP_HOME/packs/<id>/` | Installed copy in the runtime store |
| `$HCP_HOME/skills/<id>/SKILL.md` | Skill copy written on activate |

### Gold packs

| Pack | Class | Location |
|------|-------|----------|
| `transit` | Public read | `hcp/examples/packs/transit` |
| `ledger` | Gated write | `hcp/examples/packs/ledger` |
| `echo` | Composite, sandbox-safe | `hcp/examples/packs/echo` |
| `hcp-cli` | The bootstrap pack (composite, `local` Binding) | `hcp/packs/hcp-cli` |

## Identity

A pack's identity is its manifest `id`, which is also `pack_id` on the wire and
`surface_id` in the Policy file, and lint checks that all three agree. One
`pack_id` maps to one maintained repository. A command is identified by
`pack_id` + `verb_key`, a pair that stays stable across the CLI face, the JSON
face, the Skill, the host tool catalog and any MCP export.

## Planes

| `plane` | Meaning | Binding |
|---------|---------|---------|
| `runtime` (default) | End-user capability | `public` · `user` · `local` |
| `control` | Admin, fleet, operator | `mothership`, in a second pack |

A runtime never silently activates a control-plane pack on an end-user
Principal, and a domain CLI with both an admin face and a user face is two
packs.

## Versioning

An HCP/1.x pack declares protocol affinity `hcp: 1` in its metadata and leaves
content versioning to its repository. A manifest `version` field with registry
resolution is on the [roadmap](/docs/community/roadmap).

## Example

The gold public-read pack, in full:

<Tabs items={['surface.json', 'policy.json', 'HARNESS.md']}>
<Tab value="surface.json">

```json
{
  "id": "transit",
  "name": "Transit",
  "kind": "wrap",
  "cliBin": "transit",
  "plane": "runtime",
  "transports": ["cli", "json_exec"],
  "binding": { "mode": "public", "hub": "public" },
  "wrap": { "bin": "../../bins/transit.mjs", "argv_template": ["{{verb}}"] },
  "verbs": {
    "find":   { "description": "Resolve a place.",                       "cli": ["transit", "find", "{{query}}"] },
    "plan":   { "description": "Time-first options between two places.", "cli": ["transit", "plan", "{{from}}", "{{to}}"] },
    "show":   { "description": "Expand one option.",                     "cli": ["transit", "show", "{{optionId}}"] },
    "status": { "description": "Live observation.",                      "cli": ["transit", "status"] },
    "follow": { "description": "Follow a run.",                          "cli": ["transit", "follow", "{{runId}}"] }
  }
}
```

</Tab>
<Tab value="policy.json">

```json
{
  "surface_id": "transit",
  "version": 1,
  "verbs": {
    "find":   { "operation": "read", "privacy": { "land": "personal" } },
    "plan":   { "operation": "read", "privacy": { "land": "personal" } },
    "show":   { "operation": "read", "privacy": { "land": "personal" } },
    "status": { "operation": "read", "privacy": { "land": "personal" } },
    "follow": { "operation": "read", "privacy": { "land": "personal" } }
  },
  "stimuli": {}
}
```

</Tab>
<Tab value="HARNESS.md">

```md
# HCP — Transit

Public five-command wrap sample. No login. No secrets. Copy this shape for
discover / plan / show / status / follow.

## Hot path

| Command | CLI |
|---------|-----|
| `find` | `transit find "Central" --json` |
| `plan` | `transit plan "A" "B" --json` |
| `show` | `transit show opt-1 --json` |
| `status` | `transit status --json` |
| `follow` | `transit follow run-1 --json` |

## Anti-patterns

- One mega-command `run`
- Polling `plan` in a tight loop
- Host-absolute paths in this Skill
```

</Tab>
</Tabs>

## Related

- [Build packs](/docs/packs): the authoring path
- [Specification: Pack model](/docs/specification/pack-model)

Source: https://harnesscontextprotocol.io/docs/concepts/pack

---

# Command

> What a command is made of, how to name it, how its CLI face renders from JSON args, and the rules that keep it safe for an agent to call.

A command is a named operation a pack declares and the unit of Execute. Its
CLI face and its JSON face are the same command under one Policy row, one
Binding and one Adapter, so the name you give it holds on every surface a host
shows. Give a pack 5 to 12 commands and write each CLI form to the
[agent-safe rules](#agent-safe-rules).

## Anatomy

The wire field `verb_key` and the manifest map `verbs` both mean command, since
HCP has no separate verb ontology.

| Part | Where | Example |
|------|-------|---------|
| Stable name | manifest `verbs` key; wire `verb_key` | `plan`, `mail.list`, `setup.apply` |
| Description | manifest | "Time-first options between two places." |
| CLI form | manifest `cli[]` | `["transit", "plan", "{{from}}", "{{to}}"]` |
| Argument placeholders | `{{name}}` in `cli[]` | map to JSON `args` |
| Title (optional) | manifest `title` | "Searched the Inbox for “Laura”" |
| Approval view (optional) | manifest `preview` | The card a person sees before a gated write |
| [Policy](/docs/concepts/policy) row | `policy.json` | `operation`, `privacy.land`, `confirm` |
| Adapter path | pack `kind` | spawn the wrap bin, or call in-process |

## The twin face

<Tabs items={['CLI face', 'JSON face']}>
<Tab value="CLI face">

```bash
transit plan "A" "B" --json
```

</Tab>
<Tab value="JSON face">

```json
{ "pack_id": "transit", "verb_key": "plan", "args": { "from": "A", "to": "B" } }
```

</Tab>
</Tabs>

Only the [Invoker](/docs/runtimes/execution#invoker) differs, which makes the
twin face the command's identity ([L2](/docs/introduction/design-principles)).
`hcpx exec` sends the JSON envelope from the command line for runtime operators
and tests, while Skills teach the domain bin. Its `k=v` pairs after `--` become
`args`, and `confirm=true` or `apply=true` satisfies the confirm gate.

```bash
hcpx exec transit plan --json -- from=A to=B
```

## Naming

Names are lower-case, dot-separated when a family is useful, and stable.

| Pattern | Use |
|---------|-----|
| `find` `plan` `show` `status` `follow` | Public read set |
| `setup.status` `setup.apply` `setup.update` | Installer family (HCP/1.1) |
| `connect` | Establish Binding; needs `pack.connect` |
| `doctor` `capabilities` `validate` | Health, discovery, and pre-flight checks; always `read` |
| `list` `get` `send` `create` | Domain ops on a linked channel |

Dots group names without creating a hierarchy, so `mail.list` is one command.
A pack that needs more than 12 commands is usually two packs.

Each name is identical in every projection, and lint fails on drift:

| Face | Name |
|------|------|
| CLI | `mail list` (space grammar from `cli[]`) |
| JSON | `"verb_key": "mail.list"` |
| Skill | `` `mail.list` `` in the hot-path table |
| Host tool | `hcpx exec mail mail.list` in a shell, `exec mail mail.list` through the one `hcp` tool, or `mail__mail_list` in an in-process loop (pack id, two underscores, `verb_key` with dots as underscores) |

The CLI face may carry aliases in other languages, declared as manifest
metadata and never as separate commands, while the JSON `verb_key` stays
English.

## Titles

A host shows a person each step of a run, and `title` gives the words: `live`
while the command runs and `done` once it ran. The pack declares them because a
command line shows only flags and ids, and model text uses words the pack never
said.

```json title="surface.json (fragment)"
"find": {
  "description": "Mail in the Inbox matching a query or a sender.",
  "cli": ["mail", "find", "--query", "{{q}}", "--from", "{{from}}"],
  "title": {
    "live": ["Searching the Inbox for “{{q}}”", "Searching the Inbox"],
    "done": ["Searched the Inbox for “{{q}}”", "Searched the Inbox for mail from {{from}}", "Searched the Inbox"]
  }
}
```

Each form is one template or a list tried in order. The first template whose
placeholders all have a value wins, so a title never shows an empty slot, and
the last one in each list should have no placeholders, so a title always shows.
`{{name}}` is a call argument, and `{{result.a.b}}` is a field of the JSON
result that works only in `done`, as in `"Read {{result.mail.subject}}"`.
Values are flattened to one line and cut to 60 characters, and a list of
strings is joined with commas.

| Call | `live` | `done` |
|------|--------|--------|
| `q=Laura` | Searching the Inbox for “Laura” | Searched the Inbox for “Laura” |
| `from=anna@acme.example` | Searching the Inbox | Searched the Inbox for mail from anna@acme.example |
| no arguments | Searching the Inbox | Searched the Inbox |

The SDK returns the words: `commandTitle(pack, verb_key, args)` while the
command runs and `commandTitle(pack, verb_key, args, result)` once it has a
result, or `null` when the pack declares no title and the host uses its own.
`hcpx check` reports a title that names an unknown argument, uses a result in
`live`, or has no plain fallback (M026).

[RFC 0007](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0007-host-projection.md)
proposes titles and the reference SDK implements them. It asks hosts to show a
declared title in place of the command line, keep the exact command one action
away, and never replace the title with text the model wrote.

## CLI projection

Every command projects to a CLI form an agent can run safely
([Projections](/docs/specification/projections)):

```text
<bin> <space…> [--flags] [--json]
```

<Tabs items={['CLI', 'JSON exec']}>
<Tab value="CLI">

```bash
mail list --folder=inbox --json
mail send --to user@example.com --subject Hello --confirm --json
```

</Tab>
<Tab value="JSON exec">

```json
{ "pack_id": "mail", "verb_key": "mail.list", "args": { "folder": "inbox" } }
{ "pack_id": "mail", "verb_key": "mail.send", "args": { "to": "user@example.com", "subject": "Hello", "confirm": true } }
```

</Tab>
</Tabs>

### How `cli[]` renders

The runtime substitutes placeholders from `args`, drops empty ones, and appends
the JSON flag.

| Declared | Args | Rendered |
|----------|------|----------|
| `["transit", "plan", "{{from}}", "{{to}}"]` | `{ from: "A", to: "B" }` | `transit plan A B --json` |
| `["mail", "list", "--folder={{folder}}"]` | `{ folder: "inbox" }` | `mail list --folder=inbox --json` |
| `["ledger", "transfer", "{{amount}}", "--to", "{{payee}}", "--memo", "{{memo}}", "--confirm"]` | `{ amount: "2.80", payee: "demo-transit", confirm: true }` | `ledger transfer 2.80 --to demo-transit --confirm --json` |
| `["ledger", "setup", "all", "-y"]` | `{}` | `ledger setup all -y --json` |

The first token is the bin, and the arguments go to `spawn` as an array with no
shell in between, so a value with spaces stays one argument. A missing optional
value drops its token and the flag right before it, so an empty `memo` leaves
no dangling `--memo`. Without `cli[]` the runtime falls back to
`wrap.argv_template`, where `{{verb}}` expands to the command tail (`mail.list`
becomes `list`).

### The JSON flag

The runtime appends `--json` unless the pack or the command selects JSON
another way.

| Declared | Appended |
|----------|----------|
| Nothing | `--json` |
| `wrap.json_flag: ["-o", "json"]` | `-o json` on every command |
| A command's own `json_flag` | That value instead, for that command only; `[]` appends nothing, for CLIs that print JSON by default |
| `cli[]` already selects JSON (`--json`, `--json=…`, `--json name,url`, or `-o`, `--output`, `--format`, `-f` followed by `json`) and the command has no `json_flag` | Nothing |

The JSON arguments always come last, and `hcpx check` probes render commands
the same way.
[RFC 0004](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0004-wrap-json-flag.md)
proposes the JSON flag and the reference runtime implements it.

### Exit codes

| Code | Meaning |
|------|---------|
| `0` | Success |
| `1` | Failure |
| `2` | Warning; used by `doctor` / `status` for degraded but usable |
| `127` | Bin not found or not allow-listed (runtime-side) |

A non-zero exit becomes `{ "ok": false, "error": <stderr or "exit N"> }` on the
JSON face.

### Agent-safe rules

| Rule | Why |
|------|-----|
| `--json` or JSON-by-default stdout | Agents parse; humans still read |
| Non-zero exit on failure | Orchestrators branch correctly |
| No interactive prompts on the agent path | Agents hang or invent answers |
| Idempotent reads | Retries are safe |
| Explicit writes: `operation: write` and clear names | No surprise side effects |
| `--confirm` / `--apply` on gated writes, mirroring Policy `confirm: true` | The gate is visible on both faces |
| Stable space grammar | Skills stay teachable across versions |
| `doctor` / `capabilities` for discovery | Beats hidden flags |

## What is not a command

A mega-command such as `run` or `exec -- <argv>` forwards arbitrary arguments
and behaves as a shell, which Execute does not cover. A PATH wrap over
`allowedSubcommands` is a [bootstrap](/docs/concepts/adapter#pass-through-is-a-bootstrap)
to promote to named commands. "New message arrived" is a
[Sense](/docs/concepts/sense) trigger such as `new_item`, while `mail.list` is a
command, and `follow` stays a read command however often an agent calls it.

## Related

- [Execution](/docs/runtimes/execution): the pipeline a command passes
- [Wrap a CLI](/docs/packs/wrap-a-cli)
- [Specification: Execute](/docs/specification/execute)

Source: https://harnesscontextprotocol.io/docs/concepts/command

---

# Policy

> Every policy.json field, how to choose each command's operation, land and confirm gate, and the coverage rules lint enforces against the manifest.

Policy states what each command and each trigger does: its side-effect class,
where its data may land, and whether a write must ask first. Who may cause it
is the runtime's business ([Authorization](/docs/concepts/access#authorization)),
and what runs without asking is the user's or operator's call
([Access](/docs/concepts/access)). Write one row per command and per trigger in
`policy.json`, choosing each value as below.

## Policy file

```json title="policy.json"
{
  "surface_id": "mail",
  "version": 1,
  "verbs": {
    "mail.list":    { "operation": "read",  "privacy": { "land": "personal" } },
    "mail.get":     { "operation": "read",  "privacy": { "land": "personal" } },
    "mail.send":    { "operation": "write", "privacy": { "land": "personal" }, "confirm": true },
    "mail.connect": { "operation": "write", "privacy": { "land": "personal" } },
    "notes.create": { "operation": "write", "privacy": { "land": "brain" } }
  },
  "stimuli": {
    "new_item":         { "operation": "read", "privacy": { "land": "personal" } },
    "binding.degraded": { "operation": "read", "privacy": { "land": "personal" } }
  }
}
```

<TypeTable
  type={{
    surface_id: { description: 'Must equal the manifest id. Legacy name for pack_id.', type: 'string', required: true },
    version: { description: 'Policy file version. 1 for HCP/1.x.', type: '1', default: '1' },
    verbs: { description: 'One entry per command key in the manifest. Coverage is checked both ways.', type: 'Record<string, PolicyEntry>', required: true },
    stimuli: { description: 'One entry per stimulus ref in the manifest. Empty object if none.', type: 'Record<string, PolicyEntry>', default: '{}' },
  }}
/>

Each entry, for a command or a trigger, has these fields:

<TypeTable
  type={{
    operation: { description: 'Side-effect class.', type: "'read' | 'write'", required: true },
    'privacy.land': { description: 'Where data or side effects may land.', type: "'personal' | 'brain' | 'local'", required: true },
    confirm: { description: 'Writes only. true: the command asks before it runs, unless the user\'s access mode is run. "always": it asks in every mode; only an exact allow rule written by the user pre-approves it. See Access.', type: 'boolean | "always"' },
  }}
/>

Unknown fields and unknown operation or land tokens fail parse. A trigger is
always `read`, since Sense cannot write, but its land still decides where the
sink records it.

## Operation

| Ask | If yes | Examples |
|-----|--------|----------|
| Would the caller need to undo anything afterwards? | `write` | `send`, `create`, `setup.apply` |
| Does it only inspect, list, validate, or report, even for a prospective write? | `read` | `list`, `status`, `validate`, `doctor` |
| Is it a dry-run of a write? | Still `write`, and it must not persist | |
| Does it establish or refresh a Binding? | `write`, and it needs `pack.connect` at runtime | `connect` |

## Land

| Land | Data belongs to | Runtime also checks |
|------|-----------------|---------------------|
| `personal` | One actor: a personal mailbox, the user's own artifacts | The Principal's own scope |
| `brain` | A shared project or team: team artifacts | Project membership, with `project_id` set; tenant membership alone is not enough |
| `local` | A local workspace directory (cwd jail), not a tenant. HCP/1.1 | The sandbox's working directory |

Products map `brain` to whatever they call a shared work context, and packs
never invent a fourth land. Choose by where the data belongs, never by adapter
or Binding mode, since a `local` Binding pack can have `personal` land and a
`user` Binding pack `brain` land. Arguments like `--entity` or `--workspace`
refine accounts within Binding scope and never select the tenant, which comes
from the authenticated Principal's session
([L5](/docs/introduction/design-principles)).

## Confirm

Set `confirm: true` on any write that must not fire from a hallucinated turn,
such as transfers, sends to external parties, deletes, publishes and anything
with cost. Leave it off idempotent writes an agent should retry, such as
`setup.apply`, `sync` and `categorize`. Use `confirm: "always"` for the few
commands that must never run unattended, even in access mode `run`: payments,
irreversible deletes, anything that leaves the company. Users can still
pre-approve those with an exact allow rule and limits
([Access](/docs/concepts/access#confirm-always)).

The runtime enforces the gate before any Adapter runs. The JSON envelope must
carry `confirm: true` (or `apply: true`), and the CLI face takes `--confirm` or
`--apply` in its `cli[]`.

```json
{
  "ok": false,
  "pack_id": "ledger",
  "verb_key": "transfer",
  "code": "confirm_required",
  "error": "confirm required",
  "preview": {
    "command": "transfer",
    "args": { "amount": "2.80", "payee": "demo-transit" },
    "cli": "ledger transfer 2.80 --to demo-transit --confirm"
  },
  "hint": "Nothing ran. Re-run with confirm=true only if the user has approved this exact action with these values …"
}
```

No Invoker can skip it: a UI shows an approval, an agent shows the user the
`preview` and waits, and a person at the terminal types the flag. `error` is
the conformance string, and the reference runtime adds `code`, `preview` and
`hint`. An agent may pass `confirm=true` without asking again only when the
user's own words approve that exact action with those values ("pay 2.80 to Demo
Transit"). "Pay the fare" names no amount, so the agent shows the preview
first.

## Coverage rules

Lint fails when a manifest command or stimulus ref has no Policy row, or when
Policy names a key the manifest lacks:

```text
verb "transfer" missing from policy.json          # manifest has it, Policy lacks it
policy verb "refund" not in pack manifest         # Policy has it, manifest lacks it
stimulus "new_item" missing from policy.json      # declared trigger without a row
policy stimulus "old_event" not in stimulusTriggers
policy surface_id "ledgr" !== "ledger"
```

## How grants scale Policy

Policy says what a command does, and capability grants say who may run it.

```text
Policy operation  ──►  required capability
read              ──►  verb.exec.read
write             ──►  verb.exec.write
(connect command) ──►  pack.connect
(trigger)         ──►  sense.enable
```

A runtime may grant per `pack_id` or per `verb_key`. A multi-tenant product must
not collapse read, write and connect into one boolean, because sending mail
must never imply re-linking the mailbox.

## What stays out

Roles, user lists, allow-lists, tenant ids, API keys, rate limits, schedules
and host paths stay out. Every runtime, UI and author must understand each knob
Policy carries, so it keeps two axes and one gate: a runtime with serious RBAC
layers its rules over these rows without the pack knowing, and a runtime with
none still enforces the confirm gate.

## Related

- [Specification: Policy](/docs/specification/policy)
- [Schemas](/docs/reference/schemas)

Source: https://harnesscontextprotocol.io/docs/concepts/policy

---

# Access

> Grants decide who may act at all, and access decides what runs without asking, through modes, rules, approvals, the approval card and an audit log.

Access is the user's or operator's setting for what a runtime runs without
asking, and capability grants decide before that who may act at all. Packs set
neither (their [Policy](/docs/concepts/policy) only says what a command is), so
they cannot grant themselves anything. Pick a mode and a few rules for daily
work, and give a multi-tenant product real [grants](#authorization).

## Modes

| Mode | Reads | Writes | Writes with `confirm: true` | Writes with `confirm: "always"` |
|------|-------|--------|-----------------------------|----------------------------------|
| `manual` | ask | ask | ask | ask |
| `auto` (default) | run | run | ask | ask |
| `run` | run | run | run | ask |

Use `manual` for a new pack or sensitive work, `auto` for daily work and `run`
for unattended jobs such as CI.
[RFC 0006](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0006-access-modes.md)
proposes access, and the reference runtime and `hcpx` implement it.

```bash
hcpx mode                       # show the current mode and where it comes from
hcpx mode manual                # for everything
hcpx mode run --pack transit    # for one pack
HCP_MODE=run hcpx exec …        # for one process (CI)
```

## Confirm "always"

A command marked `"confirm": "always"` in `policy.json` asks in every mode,
including `run`. Use it for anything that moves money, deletes data for good or
reaches outside the company; `hcpx check` suggests it (rule P007) for commands
that pay or transfer. Only an allow rule a person wrote naming the command
exactly lets it run without a question, and a wildcard allow rule never does.

## Rules

Rules refine the mode for one command, a whole pack or everything:

```bash
hcpx allow ledger.transfer --when payee=demo-transit --max amount=5 --note "fares"
hcpx ask gh.*                          # always ask for anything in the gh pack
hcpx deny notes.delete                 # never, whatever the mode
hcpx deny '*' --op write               # read-only everywhere
hcpx rules                             # list, with index and scope
hcpx rules rm 0
```

`--when key=value` requires an argument to equal a value and `--max key=n` caps
a numeric one, and a rule whose conditions fail does not apply. A matching deny
rule refuses the command (`denied_by_rule`). Otherwise the most specific
matching ask or allow rule decides: an exact command beats `pack.*`, which beats
`*`, a rule with conditions beats one without, and ask wins a tie. With no
matching rule, the mode decides.

## Approvals

When a command asks, nothing runs. The result carries `code: "confirm_required"`,
a `preview` of the exact command and an `approval_id`:

```json
{
  "ok": false,
  "code": "confirm_required",
  "preview": { "command": "transfer", "args": { "amount": "2.80", "payee": "demo-transit" },
               "cli": "ledger transfer 2.80 --to demo-transit --confirm" },
  "approval_id": "5ea7eb29",
  "access": { "mode": "auto", "reason": "Policy confirm \"always\": every mode asks", "approvals": "agent" }
}
```

| `approvals` | An ask is satisfied by |
|-------------|------------------------|
| `agent` (default) | The agent passing `confirm=true`, after the user agreed in the conversation, or a person running `hcpx approve <id>` |
| `human` | Only a person running `hcpx approve <id>`. `confirm=true` from an agent does not count |

```bash
hcpx approvals            # requests waiting for a person
hcpx approve 5ea7eb29     # yes, once
hcpx reject 5ea7eb29
HCP_APPROVALS=human …    # or "approvals": "human" in access.json
```

An approval covers one run of exactly that command with exactly those arguments
for 15 minutes, so the agent reruns it unchanged. Changing any argument, even
the amount by a cent, needs a new approval.

## Settings and who may change them

| Layer | File | May |
|-------|------|-----|
| User | `$HCP_HOME/access.json` (`~/.hcp/access.json`) | Loosen or tighten |
| Environment | `HCP_MODE`, `HCP_APPROVALS` | Loosen or tighten, for one process |
| Project | `.hcp/access.json` in the repository | Only tighten: a stricter mode, ask and deny rules, human approvals |

The project file only tightens because anyone who can open a pull request can
write it. `hcpx access` prints the effective settings, where each comes from,
and anything ignored because it would loosen access.

```json title="~/.hcp/access.json"
{
  "mode": "auto",
  "packs": { "transit": { "mode": "run" } },
  "approvals": "human",
  "rules": [
    { "match": "ledger.transfer", "decision": "allow",
      "when": { "args": { "payee": "demo-transit" }, "max": { "amount": 5 } }, "note": "fares" },
    { "match": "notes.delete", "decision": "deny" }
  ]
}
```

## Keeping agents out of access

The access commands (`hcpx approve`, `reject`, `mode`, `allow`, `ask`, `deny`,
`rules rm`) refuse with `human_only` inside an agent, meaning through `hcpx mcp`
or with `HCP_AGENT=1` set. `hcpx adopt claude` sets that variable for Claude
Code's shell and adds deny rules in `.claude/settings.json` for those commands
and for edits to the HCP store, and `hcpx adopt codex` prints it for Codex's
`shell_environment_policy`.

This stops mistakes and prompt injection that go through HCP. On a single-user
machine the agent runs as you, so one with unrestricted access to your files can
still get past it; for a hard boundary, run the runtime under its own account or
as a service, or use a host sandbox that cannot write to the HCP store.

## What the card shows

A host shows a person a small typed view of what will happen, with the exact
command under it. The runtime builds the view from the pack and the call's real
arguments, never from text the model wrote, so an agent cannot show one thing
and run another. `res.approval.view` is one of:

| `kind` | Fields | Drawn as |
|--------|--------|----------|
| `message` | `from`, `to[]`, `cc[]`, `channel`, `subject`, `body` | A letter |
| `payment` | `amount`, `currency`, `to`, `from`, `memo` | The figure first |
| `event` | `action` (create, update, cancel), `title`, `when`, `until`, `where`, `attendees[]` | An event slip; a cancel is struck through |
| `removal` | `what`, `id`, `count`, `irreversible` | What goes, and whether it can come back |
| `publish` | `target`, `what`, `version` | Where it goes |
| `record` | `action`, `entity`, `id`, other fields | The record |
| `fields` | any | Ruled rows |

Every view also has a `title` in words ("Pay 2.80 EUR to demo-transit"), `rest`
for the arguments it did not use, shown as rows so nothing stays hidden, and
`signals`, the facts in the values a person should weigh, such as "Cannot be
undone", "Goes to 7 people", "Tells 3 people it is cancelled" or "Goes live".
The request's `reason_code` (`confirm_always`, `confirm`, `mode_manual`,
`rule_ask`) lets a UI word the reason without parsing sentences. `hcpx preview`
shows the card before anything runs:

```bash
hcpx preview ledger transfer -- amount=2.80 payee=demo-transit
```

```text
Ledger · Pay a saved payee. Refused until a person confirms the amount and payee.

Pay demo-transit
· Moves money
  amount    2.80
  currency  EUR
  to        demo-transit
  from      Demo account DEMO-0001

  $ ledger transfer 2.80 --to demo-transit --confirm
```

A pack can declare the view for a command in `surface.json`:

```json
"transfer": {
  "description": "Pay a saved payee. Refused until a person confirms the amount and payee.",
  "cli": ["ledger", "transfer", "{{amount}}", "--to", "{{payee}}", "--memo", "{{memo}}", "--confirm"],
  "preview": {
    "kind": "payment",
    "title": "Pay {{payee}}",
    "fields": { "amount": "{{amount}}", "currency": "EUR", "to": "{{payee}}", "from": "Demo account DEMO-0001", "memo": "{{memo}}" }
  }
}
```

Without a `preview`, the runtime infers the kind from the command and argument
names: `send` with `to` and `subject` is a message, `transfer` with `amount` a
payment, `calendar cancel` an event, `remove` a removal. The inference and the
signals are data (`DEFAULT_VIEW_RULES`, `DEFAULT_SIGNAL_RULES`), and a host adds
its own ahead of them with `Hcp.create({ views: { rules, signals } })`. A
declared `preview` always wins, so declare one for the commands that matter.

### Command lines

Hosts that run commands as text, such as an agent's shell line, parse it with
`argsFromArgv(pack, command, argv)`, where `argv` is what follows the command's
own words. The declared arguments decide the parse: `--node-id=n-7` and
`--node-id n-7` both fill `node_id`, `{{placeholders}}` in `cli[]` take their
positions, an argument with `"x-position": 0` takes the first bare word, and
undeclared `--switches` stay switches.

Declaring `args` for every command that asks keeps the card exact. `hcpx check`
warns (M024) when a preview names an unknown kind or a placeholder that is not an
argument, and suggests the exact `preview` JSON (M025) for a confirm command
without one, which `hcpx wrap` writes into the draft for every command it marks
`confirm`. Because the card is built from their arguments, the HCP Skill tells
agents to pass the whole action in named arguments, one risky command per call.

## Answers a host can offer

Every approval request carries `options`, the answers a host may put next to the
card. The runtime computes them, and the host sends back only the kind a person
picked, never a rule of its own.
[RFC 0007](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0007-host-projection.md)
proposes them.

| `kind` | Means | Offered |
|--------|-------|---------|
| `allow_once` | Run this call, exactly as shown, once | Always |
| `reject_once` | Do not run it | Always |
| `allow_session` | Run it now, and let the same command run without asking for the rest of the session | Only where a lasting yes cannot widen what the person saw |

`allow_session` carries the `rule` it stands for, an allow rule with
`note: "session"`:

| The command asks because | The rule |
|--------------------------|----------|
| The mode or an ask rule (no `confirm` in its Policy) | `allow` on `<pack>.<command>` |
| Its Policy says `confirm`, and the card is a `message` | `allow` on `<pack>.<command>` only while the arguments the card took `to`, `cc`, and `channel` from keep the same values; `about` names them in words |

No `confirm: "always"` command gets `allow_session`, and neither does a removal,
payment, publish or event card, because each of those calls is its own decision.
The view's `sources` name the arguments behind each field, which lets a message
rule bind the audience the person saw. The SDK computes the options and the rule
but stores no session rules: a host that offers `allow_session` keeps the rule
in its access settings until the session ends, never lets the agent set one, and
still lets a deny rule win over it.

## In your own runtime

Any runtime gets access, with or without the CLI, from settings passed to
`Hcp.create` or by wrapping its driver:

```ts
import { Hcp, withAccess, gateCommand } from '@harnesscontextprotocol/runtime'

// A proprietary driver gets modes, rules, approvals, and audit in one line.
const hcp = await Hcp.create({
  runtime: myDriver,
  access: { mode: 'auto', approvals: 'human' },
  audit: (event) => log.write(event),
})

const res = await hcp.exec('ledger', 'transfer', { amount: '2.80', payee: 'demo-transit' })
if (res.code === 'confirm_required') {
  showApprovalCard(res.approval)          // pack, summary, arguments with descriptions, CLI line, why
}

// After the person presses Approve in your UI:
await hcp.exec('ledger', 'transfer', { amount: '2.80', payee: 'demo-transit' }, {
  approval: { by: 'human', id: uiApprovalId },
})
```

`res.approval` carries everything a host needs to ask a person: `pack_name`,
`command`, `summary`, `operation`, `confirm`, `args` (each with its
`description` from the pack), `cli`, `reason`, `reason_code`, `mode`,
`approvals`, the `view`, the `options` and, when the runtime keeps approvals,
`approval_id` and `expires_at`. A host that runs commands its own way, for
example from a command line an agent wrote, calls
`gateCommand({ pack, verb_key, args, access })` and gets allow, ask (with the
same request) or deny. `approval: { by: 'human' }` is the host's statement that
its own UI collected a person's yes for exactly this call, so agents never set
it.

`hcpx commands` shows each command's `access` (allow, ask, deny) under the
current settings, so an agent plans around an ask before it hits one. The Skill
tells agents to follow the `hint` of a `confirm_required` result, never to run
the access commands, and to stop on `denied_by_rule`.

## Authorization

Grants decide whether a Principal may cause an effect at all. The runtime grants
through its own RBAC, and packs never embed user allow-lists, role names or API
keys, so the same pack runs in a local demo and in a multi-tenant product. A
runtime grants at least these five capabilities, per `pack_id` or per
`verb_key`, and a multi-tenant runtime must not collapse read, write and connect
into one boolean:

| Capability | Gates | Typical grant scope |
|------------|-------|---------------------|
| `pack.install` | Enabling a pack for a tenant | Admin |
| `pack.connect` | Establishing or refreshing Binding ([apart from write](/docs/concepts/binding#connect)) | The account owner |
| `verb.exec.read` | Commands with `operation: read` | Broad |
| `verb.exec.write` | Commands with `operation: write` | Narrow; often per `verb_key` |
| `sense.enable` | Delivering declared triggers into a sink | Per pack |

The command's land decides which scope the runtime checks for the
[Principal](/docs/concepts/binding#principal):

| Land | Runtime must check |
|------|--------------------|
| `personal` | The Principal's own scope |
| `brain` | Project membership; tenant membership alone is not enough. `project_id` must be set |
| `local` | The sandbox working directory. Not a tenant |

The reference SDK's grant shape is what its default authoriser understands, and
HCP does not require it as a wire format. If you omit `grants`, `Hcp.create`
falls back to `permissiveGrants()`, which is fine for a local demo and wrong for
anything multi-tenant.

```ts
type HcpGrant = {
  capability: 'pack.install' | 'pack.connect' | 'verb.exec.read' | 'verb.exec.write' | 'sense.enable'
  pack_id?: string        // omit → any pack
  verb_keys?: string[]    // omit → any command in the pack
}
```

```json
[
  { "capability": "verb.exec.read",  "pack_id": "mail" },
  { "capability": "verb.exec.write", "pack_id": "mail", "verb_keys": ["mail.send"] },
  { "capability": "pack.connect",    "pack_id": "mail" }
]
```

The authoriser runs inside the [exec pipeline](/docs/runtimes/execution#the-pipeline),
after the Principal's scope is resolved and before the confirm step where modes,
rules and approvals apply. Pass `authorize` to the runtime driver to replace the
default:

```ts
import { defaultAuthorize } from '@harnesscontextprotocol/sdk'

defaultAuthorize(
  { principal: { id: 'u1', kind: 'human' }, tenant_id: 't1', project_id: null, grants },
  { pack_id: 'mail', verb_key: 'mail.send', operation: 'write', land: 'personal', kind: 'exec' },
)
// { allow: true } | { allow: false, reason: 'missing verb.exec.write for mail.mail.send' }
```

[Errors](/docs/reference/errors#authorization) lists every denial reason. Role
table schemas, row-level security policies, collection names, IdP vendor APIs
and host paths belong to adopter profiles, since the protocol fixes only the
five axes and the evaluation order.

## Audit

Every write is attributed with this tuple, and reads should be too:

```text
Principal + Tenant (+ Project if land = brain) + Binding identity + pack_id + verb_key + operation
```

The reference runtime writes each decision as one line in
`$HCP_HOME/audit.jsonl`: time, Principal, project, pack, command, read or write,
the decision and its reason, the mode, who approved (agent, human with approval
id, rule, or mode), whether it ran, and a SHA-256 of the arguments. The line
keeps argument names and never their values.

```bash
hcpx audit --limit 20
hcpx audit --pack ledger --json
```

## Related

- [Error codes](/docs/reference/errors#error-codes)
- [Specification: Authorization](/docs/specification/authorization)

Source: https://harnesscontextprotocol.io/docs/concepts/access

---

# Binding

> Who acts on each exec and which credentials the runtime mints for it, so a pack reaches a protected system without ever holding a secret.

A pack declares the kind of credential its commands need, and the runtime's hub
mints it at exec time for the Principal who asked. The pack never sees, stores
or ships a secret, and a failed mint refuses the command instead of falling back
to a token exported in the shell ([L4](/docs/introduction/design-principles)).
Pack authors choose a mode below; runtime builders wire the hub and the Principal.

```text
pack declares  binding.mode = user
                     │
runtime hub    mints delegated credential for this Principal
                     │
adapter        receives it as env or file the wrap bin already understands
```

## Modes

A pack declares one mode, so two faces of a CLI that need different modes are
two packs ([L9](/docs/introduction/design-principles)).

| Mode | Credential | Runtime expectation | Typical pack |
|------|------------|---------------------|--------------|
| `public` | None, and connect is skipped | The Principal is still attributed | Public read (`transit`) |
| `user` | Delegated user credential (OAuth, user token) | Minted for this Principal | Linked channel (mail, calendar) |
| `local` | Machine-local credential hub (dev, appliance, personal workspace) | The Principal is the machine's user | Gated write (`ledger`) |
| `mothership` | Operator or fleet identity | Elevation grant for any non-operator Principal | Control plane, as a second pack |
| `mock` | Explicit mock in the exec envelope | Never ambient; env-flag mocks fail closed in sandbox | Tests, paper mode |

## Hubs

The hub is where the credential lives, and the pack names only its kind:

| `hub` | Meaning |
|-------|---------|
| `keyring` | A product credential service |
| `local-credential` | A machine-local store |
| `public` | No credential |
| `none` | Pack needs none and declares so |

The protocol requires only that the hub mints fail-closed and that Invokers mint
through it. Its name, token storage and refresh are runtime details with no HCP
type.

## Families

Related packs share one login by declaring the same `family`, so a user who
connected `mail` is not asked again for `calendar` on the same account.

```json
"binding": { "mode": "user", "hub": "keyring", "family": "office" }
```

Connecting one pack connects the whole family for that Principal with one
consent, and linking several packs unions their delegated scopes. A family is a
manifest field, so packs stay flat under `packs/<id>/`.

## Connect

Establishing a Binding is itself a command (`connect`, or a `setup.*` step) with
`operation: write`. It is gated by `pack.connect`, separate from the
`verb.exec.write` grant for daily writes, because sending mail must not imply
re-linking the mailbox. Domain CLIs that split control-plane link from runtime
send should keep two grants as well.

## Health

A Binding degrades when a refresh token expires or a provider revokes consent.
Every pack with a `user`, `local` or `mothership` Binding should then announce
the [Sense](/docs/concepts/sense) trigger `binding.degraded`, which the runtime's
hub usually produces, with one state:

```text
linked | stale | needs_reauth | pending | error
```

A human then runs `connect`. Health commands (`doctor`, `status`) report the
state and never mint a long-lived secret as a side effect.

## Principal

The Principal is who acts when a command runs: `human` (an authenticated person
or profile), `service` (an API key or automation), `agent` (acting on behalf of
a human or service) or `mothership` (operator or fleet identity). The kinds are
informative. The runtime establishes exactly one Principal per exec from the
authenticated session, records it and evaluates grants for it, so the exec
envelope never carries one and packs never embed, name or allow-list one.

The tenant, the organisation or workspace boundary, comes from the same session
and never from a pack argument. A command whose `privacy.land` is `brain` also
needs a project, the shared work context ([lands](/docs/concepts/access#authorization)).
Arguments refine within that scope, so `--entity acme-eu` may select a linked
account under the tenant but never the tenant itself, or one user could read
another's data.

The Principal answers who asked for this exec, and Binding answers which
credentials may be minted for this pack, mode and family. They stay separate
columns in the [audit tuple](/docs/concepts/access#audit), since one string for
both hides the classic elevation bug: an end-user Principal reaches an operator
Binding because a `mothership` bin was on `PATH`. A runtime must refuse that
pairing without an explicit elevation grant.

The reference SDK takes the Principal and its scope at creation:

```ts
const hcp = await Hcp.create({
  packDirs,
  principal: { id: 'user_42', kind: 'human' },
  tenant_id: 'acme',
  project_id: 'proj_7',   // needed for any brain-land command
  grants,
})
```

Its default authoriser denies a `brain` command when `project_id` is null:

```json
{ "ok": false, "error": "denied: privacy.land brain requires project_id" }
```

## What stays out of the pack

- Tokens, refresh secrets, client ids, key files
- Tenant ids and account allow-lists
- The hub's table names, provider SDK calls, or refresh schedule

## Related

- [Specification: Binding](/docs/specification/binding)
- [Specification: Authorization](/docs/specification/authorization)

Source: https://harnesscontextprotocol.io/docs/concepts/binding

---

# Sense

> How a pack announces that the world changed and how the runtime delivers it, under Policy and a separate grant, with no power to mint or act.

Sense is the outbound loop of HCP: Execute says "do this", and Sense says "this
happened". A pack declares triggers, the runtime delivers them into a sink while
the `sense.enable` grant is on, and an Invoker decides what happens next, which
may include running Execute under its own grants. Pack authors start at
[Triggers](#triggers), runtime builders at [Sense delivery](#sense-delivery).

```text
world changes ──► pack announces `new_item` ──► runtime sink records
                                                     │
                                               (optional wake)
                                                     ▼
                                       Invoker runs Execute: list → get → gated write
```

## Isolation

Sense is emit-only. An event that could execute could also spend, so the path
from observation to action passes through every gate a typed command would.

| Sense must | Sense must not |
|------------|----------------|
| Share Policy, Binding, and land with Execute on the same pack | Mint credentials or bypass Binding |
| Fail closed on an unknown ref or when `sense.enable` is off | Treat polling as Sense |
| Be optional on most packs | Replace pull commands (`status`, `follow`) |
| Wake Execute only after the sink has recorded | Become a second pack catalog |

## Triggers

A trigger is a world change your pack may announce. Declare one only when the
upstream system really pushes, because what an Invoker has to pull is Execute.

| Ask | If yes |
|-----|--------|
| Does the upstream system push this to you (webhook, event stream, vendor notification)? | Trigger |
| Do you have to ask for it with `status`, `follow`, `show` or `list`? | Read command. Not a trigger |
| Is it your runtime's uptime check or a UI refresh timer? | Not a pack trigger at all |
| Did the pack's own credential go stale? | Trigger: `binding.degraded` |

Agents may poll observation commands, and that polling is never Sense. If the
source cannot push yet, ship pull commands and add the trigger once it can,
without overloading a command such as `plan` to fake one.

### Declare

Declare each trigger in two places that agree:

```json title="surface.json (fragment)"
"stimulusTriggers": [
  { "ref": "new_item" },
  { "ref": "binding.degraded" }
]
```

```json title="policy.json (fragment)"
"stimuli": {
  "new_item":         { "operation": "read", "privacy": { "land": "personal" } },
  "binding.degraded": { "operation": "read", "privacy": { "land": "personal" } }
}
```

Lint fails on a ref without a row or a row without a ref. Refs are lower case,
dot-separated and name the fact: `new_item`, never `please_list`. Land follows
the data, so a personal mailbox event is `personal`, a shared project stream
`brain` and a watched workspace directory `local`. The sink records each event
under its Principal, Binding and land, so a `brain` trigger still needs project
membership to be surfaced.

### Kinds

Sense has one loop and four kinds. Mail, calendar, chat and tickets are all
channel events on that loop, and only the Binding family and the payload differ.

| Kind | Refs | Then |
|------|------|------|
| Channel event | `new_item` (a new item on the linked stream), `item.updated` (an existing item changed) | Execute `list` / `get`; optional gated write |
| Binding health | `binding.degraded`, with a [state](/docs/concepts/binding#health) | A human runs `connect`. `doctor` reports; it does not mint |
| Domain observation | `run.completed` (an upstream job finished), a settlement posted | `show` / `follow`, or a gated write |
| Emit-only | Any ref on a `sense_only` pack | No user command required |

Design each trigger together with its reaction, which is Execute under its own
Policy row and grant:

```text
new_item        ──►  sink  ──►  Invoker: mail.get <id>  ──►  (maybe) mail.send --confirm
binding.degraded ──► sink  ──►  human: mail connect
run.completed   ──►  sink  ──►  Invoker: show <run>
```

### Composition

A linked channel carries Execute (`list`, `get`, `send`) and Sense (`new_item`,
`binding.degraded`) on one pack id, and a public read pack is Execute only until
a real push exists. An emit-only pack is a pure ingress source that declares
`kind: "sense_only"`, at least one trigger and no `verbs`, with a `connect` only
if the upstream needs a registration call. Control is Execute on a second pack,
because operator health is not a user inbox.

A pack that declares Sense adds two lines to its
[proof script](/docs/packs/check#the-proof-script):

```text
one announce with sense.enable on   → sink records
one announce with sense.enable off  → dropped, Execute unchanged
```

## Sense delivery

The runtime activates Sense per pack, independently of Execute, and turning it
off never touches the CLI wrap.

| Activation | Route | Effect on Execute |
|------------|-------|-------------------|
| on | `inbox` (surface to the user) or `silent` (record only) | None |
| off | `drop`: acknowledge the producer, do not deliver | None |

Route selection is runtime code, and the reference shape is:

```ts
function resolveSenseRoute(activation: 'on' | 'off', ref: string): 'inbox' | 'silent' | 'drop' {
  if (activation === 'off') return 'drop'
  return isUserFacing(ref) ? 'inbox' : 'silent'
}
```

Delivery requires `sense.enable` for the Principal and pack. Without the grant
the runtime drops the trigger, an unknown ref is dropped and reported, and with
both checks passed the runtime records under `Principal + Binding + land`:

```text
ingress → validate ref is declared → check sense.enable → resolve route → sink
```

A sink is wherever recorded triggers go: an inbox table, a queue, a webhook
fan-out or a UI badge counter. Sinks are runtime machinery with no HCP type. The
protocol requires only that each record is attributable (Principal, Binding,
land, pack, ref), that recording never mints, spends or runs a command, and that
an Invoker reading the sink later runs Execute under Execute's own grants. A
runtime must not shortcut that last step, since Sense recording and Execute
authorisation are separate code paths with separate grants.

How a trigger reaches the runtime has no HCP type either. Choose a mechanism per
pack family and document it in your runtime's own docs, because the pack does
not carry it.

| Mechanism | Example |
|-----------|---------|
| Signed ingress | HMAC-verified webhook from the upstream provider |
| Vendor push mapping | Provider change notification → pack ref |
| Queue | Internal event bus |
| Pack process | A `sense_only` pack emitting on stdout |

The [runtime checklist](/docs/runtimes/conformance#sense-if-implemented) lists
what conformance needs from delivery, including `binding.degraded` from the hub.

## What Sense is not

- A product ledger, database schema, or inbox UI
- A requirement that every pack emit events
- A way around Binding
- Polling with a nicer name, such as an agent polling `status`
- Uptime monitors, UI refresh timers or control-plane ticks, which are runtime
  machinery and never go through the sink as pack triggers

## Related

- [Specification: Sense](/docs/specification/sense)

Source: https://harnesscontextprotocol.io/docs/concepts/sense

---

# Skill

> How to write the one screen of HARNESS.md an agent gets right on the first try, what lint checks in it, and where hosts pick it up on activate.

The pack Skill is `HARNESS.md`, the one screen that teaches humans and agents
to call this pack's commands correctly. Like [Agent Skills](https://agentskills.io)
it is short, led by examples and aware of anti-patterns, though it teaches
invocation only and is never a product manual. A person deciding whether to use
the pack reads it too, but write it for the agent that will run the commands
unattended.

## Skill file

A pack without a Skill is machine-readable but not teachable, so lint requires
`HARNESS.md`:

```text
HCP pack "weather" missing HARNESS.md skill
```

A Skill fits on one screen in this order: title, lead, hot-path table,
examples, setup (only when Binding is not `public`), anti-patterns, and
pointers that link out instead of inlining vendor manuals. Start from the
template, or copy a filled one.

<Tabs items={['Template', 'Example: ledger']}>
<Tab value="Template">

````md title="HARNESS.md"
# HCP — <Pack name>

<One sentence: what the pack does.> <One sentence: Binding posture — "No login." or
"Run `connect` once; see Setup.">

## Hot path

| Command | CLI | Policy |
|---------|-----|--------|
| `<verb>` | `<bin> <verb> <args> --json` | read |
| … | … | … |

## Examples

```bash
<bin> <verb> "<arg>" --json
<bin> <write-verb> <arg> --confirm --json
```

## Setup (only if Binding is not public)

```bash
<bin> setup status --json
<bin> setup all -y --json
<bin> connect <provider> --json
```

## Anti-patterns

- <Wrong thing agents do, and the right thing instead>
- …

## See also

- <one or two links; never inlined vendor manuals>
````

</Tab>
<Tab value="Example: ledger">

```md title="HARNESS.md"
# HCP — Ledger

Banking-class wrap: onboard, connect, doctor, one domain read, one gated write.
Local workspace land. Secrets never in this Skill.

## Hot path

| Command | CLI | Policy |
|---------|-----|--------|
| `setup.status` | `ledger setup status --json` | read |
| `setup.apply` | `ledger setup all -y --json` | write |
| `connect` | `ledger connect demo --json` | write |
| `status` | `ledger status --json` | read |
| `list` | `ledger list --json` | read |
| `transfer` | `ledger transfer 2.80 --to demo-transit --confirm --json` | write + confirm |

## Anti-patterns

- Env-flag mocks (`LEDGER_MOCK`)
- Transfer without `--confirm`
- Mixing operator bins onto this pack
- MCP as the install path
```

</Tab>
</Tabs>

### The lead

Hosts do not paste the whole Skill into context. The reference `HarnessAgent`
puts the first ~480 characters of each activated pack's Skill in a stable
prefix and loads the rest on demand through `hcp__read_skill`
([Context optimization](/docs/hosts#context-optimization)). So the lead, two
sentences, must stand alone and say what the pack does, whether it needs login
("No login." or "Run `connect` once.") and which command to run first.

Bad: *"Ledger is a comprehensive financial capability pack that exposes a
range of operations…"*

Good: *"Gated-write wrap: onboard, connect, one domain read, one confirmed
transfer. Run `ledger setup status --json` first."*

### Hot path and examples

The hot-path table lists exactly the manifest's 5 to 12 commands with their CLI
form, and its Policy column shows the agent which ones write before it plans.
Examples use real commands with real-looking arguments and `--json`, and show
the confirm flag on gated writes so the agent copies it. Leave `hcpx exec` out:
the Skill teaches the domain bin, and the envelope Invoker belongs in runtime
docs. Give `connect` its own Setup heading, because agents that see it in the
hot-path table re-link on every turn.

### Anti-patterns

This is the most useful part of the file: three to six bullets, each naming a
concrete mistake, such as a wrong Binding mode, a wrong land, interactive flags
or a tight loop, and its fix.

- Env-flag mocks such as `LEDGER_MOCK=1`. Use `mode: mock` in the envelope instead.
- `transfer` without `--confirm`. The runtime refuses it anyway.
- Polling `plan` in a loop. Call `follow` once.
- Running `setup all` without `-y` on the agent path. It prompts and hangs.
- Mixing operator bins onto this pack. Operator commands belong in a second pack.

## On activate

`hcpx install` and `hcpx activate <id>` copy `HARNESS.md` to
`$HCP_HOME/skills/<id>/SKILL.md`, so hosts that speak Agent Skills discover it
without HCP-specific code. `hcpx adopt` copies no pack Skills into the host and
writes one protocol Skill instead, which tells the agent to read a pack's Skill
with `hcpx skill <id>`.

## Rules

- Only declared commands, since the Skill, the manifest, Policy and the CLI are
  four faces of one `verb_key`. Lint fails when a named command never appears
  in the Skill as a backticked `verb_key`, or when the first column of a table
  above `## Anti-patterns` names a command the manifest does not declare.
- Mention Sense only when the manifest declares stimuli.
- No secrets, tokens or client ids, even fake ones, because they get copied.
  Lint fails on key, password, secret or token assignments.
- No host-absolute paths, since Skills are portable. `~/.hcp/…` is fine; lint
  fails on `/Users/…`, `/home/…`, `/root/…`, `/opt/…` and `/srv/…`.
- MCP stays an export. Lint fails when the teaching part (everything above
  `## Anti-patterns`) presents MCP as the way to install or invoke the pack;
  naming it under Anti-patterns is fine.
- No UI widget catalogs, since hosts render, and no product strategy, roadmap
  or marketing.

The lint rules come from
[RFC 0002](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0002-skill-four-faces.md),
a draft the reference SDK implements. `hcpx check` adds quality rules on top,
such as the title line, the length of the lead and the anti-patterns section
([Skill rules](/docs/packs/check#skill)).

## Related

- [Landscape](/docs/introduction/landscape): Skills and Agent Skills
- [Specification: Skill](/docs/specification/skill)

Source: https://harnesscontextprotocol.io/docs/concepts/skill

---

# Adapter

> When to use wrap, composite, openapi or sense_only, the fields and safety rules of a wrap, and how a pack grows from pass-through to composite.

An Adapter is how a command runs, and it sits beneath the pack's identity. A
pack can move from a wrap over a demo bin to the real CLI and then to
composite while its id, commands, Policy, Skill, host projection and grants
stay the same, which is why commands deserve good names from the start.

| Kind | Meaning | Reach for it when |
|------|---------|-------------------|
| `wrap` | Spawn an existing CLI with a rendered argv | The capability already exists as a binary. This is the default |
| `composite` | Call in-process (HTTP/SDK) | You own the SDK, or spawn cost matters |
| `openapi` | Commands bootstrapped from an OpenAPI document | You want a large surface quickly and will review the Policy it infers |
| `sense_only` | Emit triggers; no user commands | The pack observes and never acts |

## Wrap

Most packs start as a wrap, which leaves the wrapped tool unchanged.

```text
verb_key ──► argv from cli[] / argv_template (no shell interpolation)
         ──► spawn allow-listed wrap.bin … --json
         ──► parse stdout · non-zero exit → { ok: false, error }
```

```json title="surface.json (fragment)"
"kind": "wrap",
"wrap": {
  "bin": "../../bins/ledger.mjs",
  "argv_template": ["{{verb}}"],
  "agent_env": { "LEDGER_OUTPUT": "json" }
}
```

| Field | Meaning |
|-------|---------|
| `bin` | The executable. Relative paths resolve against the pack directory |
| `argv_template` | Fallback argv when a command has no `cli[]`. `{{verb}}` expands to the command tail |
| `agent_env` | Environment defaults the bin already understands. Pack-owned; runtimes never special-case bin ids |

Arguments are templated into an argv and never into a shell string. The bin
must be on the sandbox allow-list, and an unknown bin is denied, never looked
up on `PATH`. The minted Binding reaches the bin as the env or files it already
reads, and tokens never appear in Skill logs. A wrap declares named commands or
an explicit `pass_through` bootstrap (HCP/1.1), and lint fails when it declares
neither.

### Pass-through is a bootstrap

```json
"pass_through": { "allowedSubcommands": ["status", "list"], "maxArgv": 6 }
```

Pass-through gets a binary under Policy in an hour. Then promote the hot path to
named commands with Policy rows and drop the allow-list. A mega-command `run`
does not conform.

## Composite

When the SDK is yours, calling in-process saves serialising across a process
boundary. The `hcp-cli` bootstrap pack is composite, and so are most
linked-channel packs once they settle, usually in steps:

```text
pass_through ──► named 5–12 commands + policy.json ──► composite for hot paths
```

## OpenAPI

An `openapi` pack points at a document and lets tooling propose commands, with
`GET` as `read` and mutating methods as `write`. Review the inferred Policy as a
draft and still name the commands; HCP/1 requires no full codegen UI.

```json
"kind": "openapi",
"openapi": { "url": "https://api.example.com/openapi.json" }
```

## Sense-only

A `sense_only` pack declares no user commands and at least one stimulus, and
lint fails without a trigger ref. It fits pure ingress such as a webhook source
or an event feed. Add `connect` or `setup.*` only when the upstream source
needs a registration call, never bash to "install a webhook".

## Related

- [Wrap a CLI](/docs/packs/wrap-a-cli)
- [Sandbox](/docs/runtimes/sandbox): allow-lists and the cwd jail
- [Specification: Manifest](/docs/specification/manifest): adapter fields

Source: https://harnesscontextprotocol.io/docs/concepts/adapter

---

# Lifecycle

> The six steps from lint to exec that every pack passes before a command runs, how to read availability, and where the store keeps its state.

Every step is a CLI command you can observe, and each one fails closed. When a
command will not run, `hcpx availability` gives the reason, so an agent reads it
before planning and never meets the denial mid-run.

```text
lint → install → activate → connect → availability → exec
```

| Step | Command | What changes | Proves |
|------|---------|--------------|--------|
| Lint | `hcpx lint <dir>` | Nothing | Policy covers every command and trigger; Skill present and naming the same commands; wrap is named or pass-through |
| Install | `hcpx install <dir>` | Pack copied to `$HCP_HOME/packs/<id>/` | The three files are valid and stored |
| Activate | `hcpx activate <id>` | Pack on this session; Skill copied to `$HCP_HOME/skills/<id>/SKILL.md` | The session may use it |
| Connect | `hcpx connect <id>` | Binding minted through the hub | Credentials exist for this Principal. Skipped when mode is `public` |
| Availability | `hcpx availability <id>` | Nothing | Which commands this Principal may run right now, and why not |
| Exec | `hcpx exec <id> <verb>` | The world | Grant → confirm → mint → Adapter → sandbox → result |

## Availability

Availability reports four flags per pack, with `executable` per command, and
every `false` carries a reason.

| Flag | Meaning | False when |
|------|---------|------------|
| installed | Present in the store | Never installed, or uninstalled |
| activated | On the harness session | Installed but not activated |
| connected | Binding minted | Non-public pack that has not run `connect` |
| executable | This command passes grants now | Any of the above, or `missing verb.exec.read` / `missing verb.exec.write` |

```bash
hcpx availability ledger --json
```

```json
{
  "pack_id": "ledger",
  "status": ["installed", "activated"],
  "installed": true,
  "activated": true,
  "connected": false,
  "adapter_kind": "wrap",
  "skill_present": true,
  "verbs": [
    { "verb_key": "setup.status", "operation": "read",  "land": "local", "executable": true },
    { "verb_key": "transfer",     "operation": "write", "land": "local", "executable": false, "reason": "missing verb.exec.write" }
  ],
  "capabilities": [
    { "capability": "pack.install",    "granted": true },
    { "capability": "pack.connect",    "granted": true },
    { "capability": "verb.exec.read",  "granted": true },
    { "capability": "verb.exec.write", "granted": false },
    { "capability": "sense.enable",    "granted": false }
  ]
}
```

## The store

The first run of `hcpx` creates the store and installs the `hcp-cli` bootstrap
pack, so a working pack is always there to exec against. Set `HCP_HOME` to move
the store, for example in CI or to keep a project store apart from a personal
one.

```text
$HCP_HOME/                 default ~/.hcp
  packs/<id>/              installed packs — the three files
  skills/<id>/SKILL.md     Skill copies, written on activate
  state.json               activated · connected · grants
```

## Activate and connect

Install puts a pack in the store and activate puts it on a session, so a
runtime can hold many packs and expose a few, and a host generates surfaces
from exactly the activated set. Host wiring with `hcpx adopt` is a separate,
optional step.

Connect applies to Binding modes `user`, `local` and `mothership`. It needs
`pack.connect`, a capability apart from daily writes, and is the only step that
mints a long-lived credential, while health commands report and never connect.

## Deactivate and uninstall

```bash
hcpx deactivate ledger     # leaves the pack in the store, removes it from the session
hcpx uninstall ledger      # removes the directory
```

A runtime can also turn off a pack's triggers alone without touching the CLI
wrap.

## Related

- [Build runtimes](/docs/runtimes)
- [CLI reference](/docs/reference/cli)
- [Specification: Lifecycle](/docs/specification/lifecycle)

Source: https://harnesscontextprotocol.io/docs/concepts/lifecycle

---

# Build a pack

> Wrap a CLI you already ship in about ten minutes, then prove it installs, answers on both faces and refuses a gated write until confirmed.

A pack is three files beside a binary you already have: `surface.json` names
the commands, `policy.json` gives each a rule and `HARNESS.md` teaches the
agent. This tutorial scaffolds them, lints, checks and runs the result, and adds
one gated write. Install `hcpx` first ([Quickstart](/docs/quickstart#install-the-cli))
and search the [catalog](/docs/packs/share#catalog), since someone may already
have wrapped your tool.

## Build it step by step

<Steps>
<Step>

### Scaffold the three files

Pick the starting point closest to what you have. The
[pack classes](/docs/packs/best-practices#pack-classes) explain each shape.

| You have | Run | You get |
|----------|-----|---------|
| A read-mostly tool with no login | `hcpx init weather --class public-read` | Public read template with a demo binary |
| A tool with a connect step or a risky write | `hcpx init payments --class gated-write` | Gated write template with installer commands and a confirm write |
| An existing binary on your `PATH` | `hcpx wrap weather` | The same files with `wrap.bin` pointing at that binary |
| A wish to read working code first | `hcpx init transit --template transit` | A copy of a gold pack (`transit` or `ledger`) |

Each writes `./<id>/` with the three files and a `package.json` that depends on
`@harnesscontextprotocol/cli`, so installing the pack also installs the CLI.
[Pack layout](/docs/concepts/pack#layout) lists the optional files and what
stays out.

</Step>
<Step>

### Name the commands

In `surface.json`, set the id, the binary, the Binding mode and the commands the
pack offers.

```json title="weather/surface.json"
{
  "id": "weather",
  "name": "Weather",
  "kind": "wrap",
  "cliBin": "weather",
  "plane": "runtime",
  "transports": ["cli", "json_exec"],
  "binding": { "mode": "public", "hub": "public" },
  "wrap": { "bin": "weather" },
  "verbs": {
    "find":     { "description": "Resolve a place name.", "cli": ["weather", "find", "{{query}}"] },
    "now":      { "description": "Current conditions.",   "cli": ["weather", "now", "{{place}}"] },
    "forecast": { "description": "Next 48 hours.",        "cli": ["weather", "forecast", "{{place}}"] },
    "alerts":   { "description": "Active warnings.",      "cli": ["weather", "alerts", "{{place}}"] },
    "status":   { "description": "Upstream health.",      "cli": ["weather", "status"] }
  }
}
```

Keep the set closed and small, five to twelve names. Each `cli` array is the
command's CLI form and every `{{name}}` placeholder maps to a JSON argument; the
runtime appends `--json` and never passes arguments through a shell.
[Manifest](/docs/packs/manifest) describes every field.

</Step>
<Step>

### Give every command a Policy row

Reads are `read` and anything that changes the world is `write`. A write that
must never fire unconfirmed also gets `confirm: true`.

```json title="weather/policy.json"
{
  "surface_id": "weather",
  "version": 1,
  "verbs": {
    "find":     { "operation": "read", "privacy": { "land": "personal" } },
    "now":      { "operation": "read", "privacy": { "land": "personal" } },
    "forecast": { "operation": "read", "privacy": { "land": "personal" } },
    "alerts":   { "operation": "read", "privacy": { "land": "personal" } },
    "status":   { "operation": "read", "privacy": { "land": "personal" } }
  },
  "stimuli": {}
}
```

`land` says where the data may end up (`personal`, `brain` or `local`). Roles,
user lists and keys stay out of the file because the runtime grants those, as
[Policy file](/docs/concepts/policy#policy-file) explains.

</Step>
<Step>

### Teach it on one screen

`HARNESS.md` teaches an agent, and a person, the hot path in a lead, a command
table, examples and the mistakes to avoid. It never holds essays, secrets or
host-absolute paths.

```md title="weather/HARNESS.md"
# HCP — Weather

Public read pack. No login. Resolve a place, then read conditions, forecast, or alerts.

## Hot path

| Command | CLI |
|---------|-----|
| `find` | `weather find "Berlin" --json` |
| `now` | `weather now berlin --json` |
| `forecast` | `weather forecast berlin --json` |
| `alerts` | `weather alerts berlin --json` |
| `status` | `weather status --json` |

## Anti-patterns

- Calling `now` in a tight loop. Read `forecast` once instead.
- Guessing a place id. Always `find` first.
```

[Skill file](/docs/concepts/skill#skill-file) is the writing guide.

</Step>
<Step>

### Lint until clean

```bash
hcpx lint ./weather --json
```

```json
{ "ok": true, "pack_id": "weather", "issues": [] }
```

Lint proves the files agree: every command has a Policy row, every row names a
real command, and the Skill exists and matches the manifest.
[Testing](/docs/packs/check#testing) lists each failure and its fix.

</Step>
<Step>

### Check it

`hcpx check` grades the pack as a reviewer would, and `--probe` runs the CLI as
an agent does.

```bash
hcpx check ./weather --init-evals    # write hcp.eval.json: one case per read, one refusal per confirm write
hcpx check ./weather --probe
```

Replace every `TODO` argument in `hcp.eval.json` with a real value and fix
findings until none is an error, using the rules in
[Check and evaluate](/docs/packs/check).

</Step>
<Step>

### Install, activate, run

```bash
hcpx install ./weather --json
hcpx activate weather --json
hcpx availability weather --json
hcpx exec weather now --json -- place=berlin
```

`hcpx exec` sent this envelope:

```json
{ "pack_id": "weather", "verb_key": "now", "args": { "place": "berlin" } }
```

and got back:

```json
{ "ok": true, "pack_id": "weather", "verb_key": "now", "result": { "temp_c": 17, "sky": "overcast" } }
```

Running `weather now berlin --json` directly returns the same result, because
the two forms are the command's [twin face](/docs/concepts/command#the-twin-face).

</Step>
<Step>

### Add a gated write

Declare any mutation that must never fire from a hallucinated turn as a confirm
write, in the shape the `ledger` gold pack uses:

```json title="surface.json (fragment)"
"transfer": {
  "description": "Move funds. Requires confirm.",
  "cli": ["ledger", "transfer", "{{amount}}", "--to", "{{payee}}", "--confirm"],
  "policy": "write"
}
```

```json title="policy.json (fragment)"
"transfer": { "operation": "write", "privacy": { "land": "local" }, "confirm": true }
```

Then prove the gate holds:

```bash
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit
# { "ok": false, "error": "confirm required" }

hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit confirm=true
# { "ok": true, … }
```

The runtime checks `confirm` before the adapter runs, so no Invoker can skip it.

</Step>
<Step>

### Share it

```bash
hcpx share ./weather --json
```

`share` lints the pack and prints a catalog row to add with a pull request, and
[Share a pack](/docs/packs/share) has the checklist. Because `hcpx init` writes
`"hcp": { "pack": "." }` into `package.json`, the pack can also ship as an npm
package:

```bash
hcpx install npm:@scope/hcp-pack-weather --json
```

HCP's own packages are a private preview on GitHub Packages for now, not on the
public npm registry.

</Step>
</Steps>

## What stays out

The pack has no connector code, server, host tool registration or token, and
it does not wrap the whole `weather` binary. Hosts pick it up through
`hcpx adopt` ([Hosts](/docs/hosts)), and a binary that needs credentials or a
setup step continues in [Wrap a CLI](/docs/packs/wrap-a-cli).

Source: https://harnesscontextprotocol.io/docs/packs

---

# Best practices

> Pick one of five pack shapes, then hold the pack to the habits for scope, naming, Policy, Skill, Binding, wrapping and Sense that review checks.

Lint decides whether a pack is valid, and its shape and these habits decide
whether agents use it correctly. Pick a class first, copy its sample, and run
the review checklist at the end before you share.

## Pack classes

Every CLI fits one of five classes, even in a domain that feels different. The
three files keep their shape in each, so copy the sample and change the fields.

| If the capability… | Class |
|--------------------|-------|
| Needs no login | [Public read](#public-read). It may still write; Policy `confirm` gates the risky commands |
| Needs a login, a token, or a connect step | [Gated write](#gated-write) |
| Is a linked personal or project stream with real push events | [Linked channel](#linked-channel) |
| Is ingress or webhook only, with no user commands | [Emit-only](#emit-only) |
| Runs under a different Binding than the user runtime | [Control](#control), which is a second pack |
| Is MCP-only with no CLI and no `doctor` | Do not wrap. Ship a CLI first |

| Class | Binding | Land | Commands | Sense |
|-------|---------|------|----------|-------|
| Public read | `public` | `personal` | `find` · `plan` · `show` · `status` · `follow` (or equivalent) | Optional. Pull observation until a real push exists |
| Gated write | `local` or `user`, with a credential hub | `local` (workspace directory) or `personal` | `setup.status` · `setup.apply` · `connect` · `status` · one read · one confirm write | `binding.degraded` when connect health moves |
| Linked channel | `user`, shared `family` | `personal` or `brain` | `list` / `get` / `send` (`send` is `write`, usually `confirm`) | Channel event (`new_item`) + `binding.degraded` |
| Emit-only | none or `public` | | none | Stimulus refs + Policy rows |
| Control | `mothership` | | | Optional health. Never a user inbox |

### Public read

A public read pack has five to twelve closed reads, such as a discover, plan
and show sequence, and its sample is `examples/packs/transit`. `follow` is a read an agent may call
repeatedly, so when the source starts to push, add a trigger instead of
overloading `plan`.

### Gated write

At least one write must not fire from a hallucinated turn (transfer, send,
apply, delete). The `examples/packs/ledger` sample runs the whole family, and
without `--confirm` its transfer returns
`{ "ok": false, "error": "confirm required" }`.

```bash
ledger setup status --json           # read, idempotent
ledger setup all -y --json           # write, no wizard on the agent path
ledger connect demo --json           # write, gated by pack.connect
ledger list --json                   # read
ledger transfer 2.80 --to demo-transit --confirm --json  # write + confirm
```

### Linked channel

Mail, calendar, chat and tickets differ only in Binding family and payload.
This is the one class that uses both loops, and its adapter is `composite` once
the SDK is stable, with `wrap` as a fine bootstrap.

### Emit-only

The pack only announces, with adapter `sense_only` and no user CLI. Never write
bash to "install a webhook"; add `connect` or `setup.*` only if the upstream
source needs a registration call.

### Control

Admin, fleet, operator and origin tools set `plane: "control"` and ship as a
second pack that never sits silently on an end-user Principal. A domain CLI with
an admin face and a user face becomes two packs that share only the bin.

## Scope

Name 5 to 12 commands. More means two packs, because a large set dilutes the
Skill and agents pick wrong, and a CLI with forty subcommands probably has five
that matter to an agent. Keep one class per pack: an operator face mixed into a
user pack is the most common reason review rejects one.

## Naming

Name the outcome: `plan`, `send` and `transfer`, never `doPlanning`, `sendMail`
or `execTransfer`. Use families for lifecycle steps (`setup.status`,
`setup.apply`, `setup.update`), never for hierarchy (`mail.folder.item.get`).
Renaming a command breaks the Skills, host projections and grants that name it,
so add a new command and deprecate the old one.

## Policy

Mark a command `read` only if an agent could safely retry it. Confirm the
irreversible (transfers, deletes, external sends, publishes) and leave confirm
off idempotent writes such as `setup.apply` and `sync`, so agents retry them
freely. Choose the land by where the data lives, never by adapter or Binding
mode, and never encode who may run a command: roles and users are runtime
grants, and a pack that embeds them is wrong for every other runtime.

## Skill

Hosts keep roughly the first 480 characters of the lead in stable context, so
the lead must stand alone and name the first command. Show the confirm flag in
examples, because agents copy them and an example without `--confirm` gets the
first call denied, after which the agent improvises. Separate setup from daily
ops, or agents will `connect` on every turn. Anti-patterns are the most useful
section, so write the ones you have seen.

## Binding

Use one mode per pack, because a CLI that needs two is two packs. Declare the
family when a sibling pack shares the login, so users consent once. Never fall
back to env: when the runtime cannot mint, the command fails. Mock is explicit
(`mode: mock` in the envelope, never `PACK_MOCK=1`).

## Wrap

Template with `cli[]` and `{{placeholders}}` and never interpolate. Set
`agent_env` for output mode if the bin needs it (`FOO_OUTPUT=json`), so the
runtime never special-cases your bin. Pass-through is only a bootstrap, so
promote it to named commands. A pack keeps its class while its adapter moves on,
and every step keeps `pack_id`, `verb_key` and Policy unchanged:

```text
pass_through  ──►  named 5–12 commands + policy.json  ──►  composite for hot paths
```

## Sense

Declare only real pushes, since polling is a read command. Always declare
`binding.degraded` on a pack with `user` or `local` Binding, because that is how
a person learns to reconnect.

## Testing

Ship the [proof script](/docs/packs/check#the-proof-script) and run it in CI,
with the [twin-face check](/docs/packs/check#the-twin-face-check). Never publish
a catalog of many packs without lint, a twin-face check and a gated-write proof.

## Review checklist

- [ ] 5 to 12 named commands, one class
- [ ] Every command and trigger has a Policy row; lint is clean
- [ ] Irreversible writes have `confirm: true` and `--confirm` in `cli[]`
- [ ] Land chosen by data ownership
- [ ] One Binding mode; family declared if shared; no env fallback
- [ ] Skill lead names the first command; setup separated; anti-patterns present
- [ ] No secrets, absolute paths, UI, or MCP-as-install anywhere
- [ ] Control-plane commands are in a second pack
- [ ] Proof script passes in CI

## Related

- [Design principles](/docs/introduction/design-principles)
- [Skill file](/docs/concepts/skill#skill-file)
- [Examples](/docs/packs/examples)

Source: https://harnesscontextprotocol.io/docs/packs/best-practices

---

# Examples

> What the four gold packs in the reference implementation prove (transit, ledger, echo and the hcp-cli bootstrap), with the commands to run each.

The reference implementation ships two conformance fixtures to copy, a
composite pack and the bootstrap pack. Copy `transit` for a discovery pack
without a login and `ledger` for anything with a login or an irreversible
write.

| Pack | Class | Adapter | Binding | Proves |
|------|-------|---------|---------|--------|
| [`transit`](#transit) | Public read | `wrap` | `public` | Five closed reads, no login |
| [`ledger`](#ledger) | Gated write | `wrap` | `local` | Installer family, connect, confirm gate |
| [`echo`](#echo) | Demo | `composite` | `public` | In-process exec, sandbox-safe |
| [`hcp-cli`](#hcp-cli) | Bootstrap | `composite` | `local` | The CLI is itself the first pack |

All four live in the SDK repository (`examples/packs/*` and `packs/hcp-cli`).
The wrap packs spawn demo binaries from their own `bin/`, so an installed copy
carries everything it runs.

## transit

```bash
hcpx install ./examples/packs/transit --json
hcpx activate transit --json
hcpx exec transit find --json -- query=Central
hcpx exec transit plan --json -- from=A to=B
hcpx exec transit show --json -- optionId=opt-1
hcpx exec transit status --json
hcpx exec transit follow --json -- runId=run-1
```

```json
{ "ok": true, "pack_id": "transit", "verb_key": "plan", "result": { "ok": true, "verb": "plan", "from": "A", "to": "B", "optionId": "opt-1" } }
```

It has five commands, all `read` and all `personal`, and no `stimuli`.
`follow` is a read an agent may poll rather than Sense. `binding.mode: public`
skips `connect`, though the Principal is still attributed, and `wrap.bin` is a
relative path resolved against the pack directory.

## ledger

```bash
hcpx install ./examples/packs/ledger --json
hcpx activate ledger --json
hcpx exec ledger setup.status --json
hcpx exec ledger setup.apply --json
hcpx exec ledger connect --json -- provider=demo
hcpx exec ledger list --json
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit confirm=true
```

```json
{ "ok": false, "pack_id": "ledger", "verb_key": "transfer", "code": "confirm_required", "error": "confirm required", … }
```

Its land is `local`, a workspace directory rather than a tenant.
`setup.status` (read) and `setup.apply` (write, `-y`) form the HCP/1.1 installer
family; both are idempotent and open no wizard on the agent path. `connect` is
`write` and needs `pack.connect`, separate from `verb.exec.write`.

`transfer` has `confirm: "always"` in Policy and `--confirm` in `cli[]`, so in
every access mode the runtime refuses it before the bin runs until the ask is
satisfied, by `confirm=true` after the user agreed or by a person's approval.
Its `payment` `preview` makes the approval card lead with the amount and payee
([Access](/docs/concepts/access#what-the-card-shows)). The pack's
`hcp.eval.json` proves this: three reads return what they should, and
`transfer` is refused without confirmation and runs once confirmed
(`hcpx eval ./examples/packs/ledger`). Its Skill's anti-patterns name real
mistakes: env-flag mocks, transfer without confirm and operator bins on this
pack.

## echo

This composite pack has no foreign binary, so it tests a runtime's JSON path
without a sandbox allow-list.

```json title="surface.json"
{
  "id": "echo",
  "kind": "composite",
  "cliBin": "echo-hcp",
  "binding": { "mode": "public" },
  "verbs": {
    "echo.ping": { "description": "Ping the echo pack.", "cli": ["echo-hcp", "ping"] },
    "echo.say":  { "description": "Echo a message back.", "cli": ["echo-hcp", "say", "{{message}}"] }
  }
}
```

```bash
hcpx exec echo echo.say --json -- message=hi
```

Its dotted command names (`echo.ping`) show how a family reads on the wire and
as an in-process tool name (`echo__echo_ping`).

## hcp-cli

The CLI is itself a composite pack with `local` Binding, installed into
`$HCP_HOME` on first run. Its 18 commands are the CLI's own lifecycle
operations, among them `hcp.doctor`, `hcp.availability`, `hcp.list`,
`hcp.search`, `hcp.install`, `hcp.activate`, `hcp.connect`, `hcp.exec`,
`hcp.lint`, `hcp.generate`, `hcp.adopt`, `hcp.skill`, and `hcp.share`, and
they keep the `hcp.` prefix whichever name you run the CLI by.

```bash
hcpx exec hcp-cli hcp.doctor --json
```

Because the bootstrap is a pack, there is always something to exec against,
and the CLI's own Skill is discoverable like any other. Store-mutating commands
(`install`, `activate`) are `write`, and the in-process composite defers them to
the CLI process that owns the store.

## hcp-example

`@harnesscontextprotocol/example` is the gold pack you can install as a package. Its CLI, `hcp-example`, is the protocol CLI's public read lattice (`doctor`, `list`, `lint`, `skill`, `catalog`) with `public` Binding, and the package ships conformance vectors and an eval file beside the three pack files. Like the other HCP packages it is a private preview on GitHub Packages, not on public npm.

```bash
hcpx install npm:@harnesscontextprotocol/example
hcp-example doctor --json
```

Use `transit` as the domain example and `hcp-cli` as the bootstrap; `hcp-example` shows how a pack ships as an npm package (`"hcp": { "pack": "." }` in `package.json`).

## Writing your own

Copy `transit` or `ledger`, change `id`, `cliBin`, `wrap.bin`, the `verbs` map
and `binding.mode`, update the Policy rows and the Skill, and lint.
[Build a pack](/docs/packs) walks through it from `hcpx init`, and
`examples/standalone/transit`, the copy with the demo binary inside the pack, an
MIT licence and a relative `wrap.bin`, is the one to
[extract into its own repository](/docs/packs/share#extract-a-gold-pack-into-its-own-repository).

## Related

- [Pack classes](/docs/packs/best-practices#pack-classes)
- [Testing](/docs/packs/check#testing)
- [Implementations](/docs/community/implementations): packs and runtimes in the wild

Source: https://harnesscontextprotocol.io/docs/packs/examples

---

# Manifest

> Each `surface.json` field with its type and default: identity, adapter, where the CLI runs, Binding, commands, triggers and the `hcp` metadata object.

`surface.json` declares who the pack is, how it runs, which credentials it
needs and the closed set of commands it exposes. Start from the complete
example, then look up each field below; `policy.json` and `HARNESS.md` have
their own references in [Policy file](/docs/concepts/policy#policy-file) and
[Skill file](/docs/concepts/skill#skill-file).

## Complete example

```json title="surface.json"
{
  "id": "ledger",
  "name": "Ledger",
  "kind": "wrap",
  "cliBin": "ledger",
  "plane": "runtime",
  "transports": ["cli", "json_exec"],
  "binding": { "mode": "local", "hub": "local-credential", "family": "ledger" },
  "wrap": {
    "bin": "./bin/ledger.mjs",
    "argv_template": ["{{verb}}"]
  },
  "verbs": {
    "setup.status": { "description": "Report whether the local ledger store is set up. Idempotent.", "cli": ["ledger", "setup", "status"] },
    "setup.apply":  { "description": "Create the local ledger store non-interactively.", "cli": ["ledger", "setup", "all", "-y"], "policy": "write" },
    "connect":      { "description": "Link a provider account through the local credential hub.", "cli": ["ledger", "connect", "{{provider}}"], "policy": "write" },
    "status":       { "description": "Balance, currency, and provider link health.", "cli": ["ledger", "status"] },
    "list":         { "description": "Recent entries (amount, payee id, memo), newest first.", "cli": ["ledger", "list"] },
    "transfer": {
      "description": "Pay a saved payee. Refused until a person confirms the amount and payee.",
      "cli": ["ledger", "transfer", "{{amount}}", "--to", "{{payee}}", "--memo", "{{memo}}", "--confirm"],
      "policy": "write",
      "args": {
        "type": "object",
        "properties": {
          "amount": { "type": "string", "description": "Amount in the account currency, as a number", "examples": ["2.80"] },
          "payee":  { "type": "string", "description": "Payee id from payees", "examples": ["demo-transit"] },
          "memo":   { "type": "string", "description": "Optional note shown on the entry" }
        },
        "required": ["amount", "payee"]
      },
      "title": { "live": ["Paying {{amount}} to {{payee}}", "Paying a payee"], "done": ["Paid {{amount}} to {{payee}}", "Paid a payee"] },
      "preview": { "kind": "payment", "title": "Pay {{payee}}", "fields": { "amount": "{{amount}}", "currency": "EUR", "to": "{{payee}}", "memo": "{{memo}}" } }
    }
  },
  "stimulusTriggers": [
    { "ref": "binding.degraded" }
  ]
}
```

## Identity

<TypeTable
  type={{
    id: { description: 'Pack id. Lower-case, stable. Equals policy.json surface_id and wire pack_id.', type: 'string', required: true },
    name: { description: 'Display name for hosts and catalogs.', type: 'string' },
    cliBin: { description: 'The CLI binary name the Skill teaches. The first token of every cli[] array.', type: 'string' },
    plane: { description: 'runtime (default) or control. Control packs default to Binding mothership and are a second pack.', type: "'runtime' | 'control'", default: 'runtime' },
    transports: { description: 'Faces the pack projects to. mcp is never required.', type: "('cli' | 'json_exec' | 'mcp')[]" },
    skill_url: { description: 'Hosted copy of HARNESS.md.', type: 'string (URL)' },
  }}
/>

## Adapter

<TypeTable
  type={{
    kind: { description: 'How commands run. Alias: adapter_kind.', type: "'wrap' | 'composite' | 'openapi' | 'sense_only'", default: 'composite' },
    wrap: { description: 'Required when kind is wrap.', type: 'WrapTarget' },
    'wrap.bin': { description: 'A bare name is a CLI on PATH. A path with a slash is relative to the pack directory and must stay inside it, because install copies only the pack. Must be allow-listed by the sandbox.', type: 'string', required: true },
    'wrap.json_flag': { description: 'Arguments that make the CLI print JSON, appended to every command. Skipped automatically when cli[] already selects JSON (--json fields, -o json, --format json). Use [] for CLIs that print JSON by default. A command can override it with its own json_flag.', type: 'string[]', default: "['--json']" },
    'wrap.argv_template': { description: 'Fallback argv when a command has no cli[]. {{verb}} expands to the command tail; {{name}} to args.name.', type: 'string[]', default: "['{{verb}}']" },
    'wrap.agent_env': { description: 'Environment defaults the bin already understands. Pack-owned.', type: 'Record<string, string>' },
    'wrap.workspace.mode': { description: 'Where the CLI runs. See Where the CLI runs below.', type: "'none' | 'project' | 'workspace' | 'ephemeral'", default: "'none'" },
    openapi: { description: 'Required when kind is openapi. One of url or path.', type: '{ url?: string; path?: string }' },
    pass_through: { description: 'HCP/1.1 bootstrap for wrap. Non-conforming as a destination.', type: '{ allowedSubcommands: string[]; maxArgv: number }' },
  }}
/>

## Where the CLI runs

`wrap.workspace.mode` sets the wrapped CLI's working directory.

| Mode | Working directory | Use it for |
|------|-------------------|-----------|
| `project` | The directory the caller is in (`HCP_PROJECT_DIR` overrides it) | CLIs that work on the project: `git`, `gh`, linters, notes and task tools that keep files in the repository. The usual choice for a CLI on `PATH`; `hcpx wrap` sets it |
| `none` | The installed pack's own directory | Packs that ship their own script in `./bin` and need nothing from the project |
| `workspace` | A private directory for this pack, with its own `HOME` | CLIs that must not touch the project, or keep state that belongs to the user, outside the repository |
| `ephemeral` | A fresh directory for each command | One-shot converters that read `workspace:` files and write an output |

[RFC 0005](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0005-wrap-project-mode.md)
proposes `project`, and the reference runtime implements it. A runtime with no
project, such as a hosted one, refuses these commands with
`code: "no_project"`, and `hcpx check` warns (M023) when a CLI on `PATH` would
run in the pack directory.

## Binding

<TypeTable
  type={{
    'binding.mode': { description: 'Credential mode. One per pack.', type: "'public' | 'user' | 'local' | 'mothership' | 'mock'" },
    'binding.hub': { description: 'Which kind of hub mints. Runtime detail; the pack names the expectation.', type: "'keyring' | 'local-credential' | 'public' | 'none'" },
    'binding.family': { description: 'Packs sharing a family share one consent and union their scopes.', type: 'string' },
    'binding.connect_provider_id': { description: 'Provider hint for the connect flow.', type: 'string' },
  }}
/>

## Commands

`verbs` maps each command name, the `verb_key` on the wire, to its declaration.

<TypeTable
  type={{
    description: { description: 'One sentence. Shown in host tool catalogs.', type: 'string' },
    cli: { description: 'Space-separated CLI form. First token is the bin. {{name}} placeholders map to args. --flag={{name}} is supported. A missing optional value drops its token and the flag right before it.', type: 'string[]' },
    args: { description: 'JSON Schema for args: properties with a description and examples, and required. The runtime refuses unknown and missing arguments before anything runs, hcpx commands shows the first example of each in a ready-to-run line, and "x-position": n lets a bare word of a command line fill the argument.', type: 'object' },
    json_flag: { description: 'Replaces wrap.json_flag for this command. [] appends nothing.', type: 'string[]' },
    title: { description: 'What a host calls a step of this command: live while it runs, done after. Each is a template or a list of templates with {{argument}} placeholders ({{result.…}} in done), tried in order. See Command, Titles.', type: '{ live?; done? }' },
    policy: { description: 'Legacy hint: read or write. policy.json is authoritative.', type: "'read' | 'write'" },
    preview: { description: 'How a person sees this command when it asks for approval: a kind (message, payment, event, removal, publish, record, fields) and field templates filled from args. See Access, What the card shows.', type: '{ kind; title?; fields? }' },
  }}
/>

Name 5 to 12 commands; fewer is fine for a public read pack, and more means
two packs. The set is closed, so every command is named and there is no `run`
and no `exec --`. Every command has a Policy row, and lint enforces coverage
both ways. The runtime substitutes `{{name}}` from `args`, appends the JSON flag
(`--json` unless `json_flag` says otherwise) and never builds a shell string, as
[CLI projection](/docs/concepts/command#how-cli-renders) describes.

## Triggers

<TypeTable
  type={{
    stimulusTriggers: { description: 'Sense declarations. Each ref must have a policy.json stimuli row. Only when the upstream system pushes.', type: '{ ref: string }[]' },
  }}
/>

## Protocol metadata

The manifest may carry an `hcp` metadata object, or the runtime derives one,
and either way it is strictly validated.

```json
{
  "hcp": 1,
  "pack_id": "ledger",
  "adapter_kind": "wrap",
  "binding": { "mode": "local", "hub": "local-credential" },
  "wrap": { "bin": "./bin/ledger.mjs" },
  "plane": "runtime",
  "transports": ["cli", "json_exec"]
}
```

`hcp` is the protocol affinity (`1` for HCP/1.x), `pack_id` must equal `id`,
and unknown fields fail validation.

## Lint checks

Lint rejects a manifest whose `id` differs from the `policy.json` `surface_id`,
a `kind: wrap` without `wrap.bin` or without named commands or `pass_through`,
a `plane: control` pack whose Binding is not `mothership`, a `kind: openapi`
without `openapi`, and a `kind: sense_only` without at least one trigger.
[Testing](/docs/packs/check#lint) lists the exact messages.

## Related

- [Adapter](/docs/concepts/adapter) · [Binding](/docs/concepts/binding)
- [Specification: Manifest](/docs/specification/manifest)

Source: https://harnesscontextprotocol.io/docs/packs/manifest

---

# Wrap a CLI

> Bring a CLI with a login, setup, several accounts or an admin face under HCP: the setup family, connect, doctor, gated writes, entities, the control split.

Most packs enter HCP as `adapter: wrap`, which names the commands a tool
already has and gives each a rule without changing the tool. This page covers
the CLI that has grown a login, a setup step, several accounts or an admin
face, starting from the draft `hcpx wrap` writes.

## Before you start

The CLI needs these habits first:

| Habit | Why HCP needs it |
|-------|------------------|
| `--json` (or JSON-default) output | Agents parse |
| Non-zero exit on failure | Orchestrators branch |
| A `doctor` or `status` command | Health without minting |
| Non-interactive flags (`-y`) on anything that would prompt | Agents hang |
| Named subcommands | Policy can only cover things with names |

A capability that exists only as an MCP server needs a CLI first.

## Start from a draft

```bash
hcpx wrap mail --class gated-write --json
```

`hcpx wrap` reads `mail --help` with no TTY and drafts the three files in
`packs/mail`: one command per usage line it can parse, a Policy row for each,
`confirm` and an approval `preview` on the ones that sound risky, and
`wrap.workspace.mode: project` so the CLI runs in the caller's project. Review
what its `next` list names, then run `hcpx check ./packs/mail --init-evals` and
`hcpx check ./packs/mail --probe --agent` until no error is left. The
[CLI reference](/docs/reference/cli) lists the options.

## Map the lifecycle to command families

HCP turns the habits production CLIs share into families with a default Policy.

### Setup: `read` and `write` (HCP/1.1)

```bash
mail setup status --json     # read  — idempotent; detected hosts, Skill merge state
mail setup all -y --json     # write — apply; -y default on the agent path
mail setup update --json     # write — refresh Skill after a pack upgrade
```

The pack keys are `setup.status`, `setup.apply` and `setup.update`. All three
are idempotent, and the agent path never opens a wizard.

### Health and discovery: `read`

```bash
mail doctor --json           # auth edge, binaries, config, capabilities
mail status --json           # narrower than doctor
mail capabilities --json     # feature discovery before send
```

Doctor reports Binding health, must not mint a long-lived secret as a side
effect, and exits `0` when healthy, `2` on a warning and `1` on failure.

### Validate before mutate: `read`

```bash
pulse validate --file ./envelope.json --json
```

Validating a prospective write is a `read`.

### Connect or link: `write`, gated by `pack.connect`

```bash
mail connect --json
finance connect bank --entity acme-eu --json
```

Connect establishes Binding. It is `operation: write` but needs the
`pack.connect` capability instead of `verb.exec.write`, and it belongs under
*Setup* in the Skill so agents run it once.

### Domain ops

| Shape | Policy |
|-------|--------|
| `list` `get` `recv` `brief` | `read` |
| `send` `create` `sync` `categorize` | `write` |
| `transfer` `delete` `publish` | `write` + `confirm` |

### Dry-run

Offer `--dry-run` on expensive or destructive writes only when the system can
preview without persisting anything. Policy stays `write`.

## Declare the wrap

```json title="surface.json (fragment)"
"kind": "wrap",
"cliBin": "mail",
"binding": { "mode": "user", "hub": "keyring", "family": "office" },
"wrap": { "bin": "mail", "workspace": { "mode": "project" }, "agent_env": { "MAIL_OUTPUT": "json" } },
"verbs": {
  "setup.status": { "cli": ["mail", "setup", "status"] },
  "setup.apply":  { "cli": ["mail", "setup", "all", "-y"], "policy": "write" },
  "connect":      { "cli": ["mail", "connect"], "policy": "write" },
  "doctor":       { "cli": ["mail", "doctor"] },
  "list":         { "cli": ["mail", "list", "--folder={{folder}}"] },
  "get":          { "cli": ["mail", "get", "{{id}}"] },
  "send":         { "cli": ["mail", "send", "--to", "{{to}}", "--subject", "{{subject}}", "--confirm"], "policy": "write" }
}
```

The runtime renders `cli[]` with `args`, appends the JSON flag and spawns the
allow-listed bin in the caller's project without a shell. A CLI that selects
JSON its own way (`-o json`, `--json name,url`, or JSON by default) sets
[`json_flag`](/docs/concepts/command#the-json-flag).

## Declare Binding mode explicitly

| Mode | When |
|------|------|
| `user` | The CLI acts with a delegated user credential |
| `local` | The CLI keeps credentials in a machine-local store |
| `mothership` | The CLI is an operator or fleet tool; see [Control](#control-plane-versus-runtime) |
| `public` | No credential |

Never infer the mode from whatever token sits in the environment. Mock is
explicit in the envelope, and an env-flag mock fails closed in sandbox.

## Multi-entity and multi-tenant arguments

CLIs often take `--entity`, `--workspace` or `--account`. Each maps to its own
home in HCP, and pack arguments alone never select the tenant.

| CLI concern | HCP home |
|-------------|----------|
| Which organisation | Runtime tenant, from the Principal's session, never a pack arg |
| Which shared project | Runtime project, when `privacy.land: brain` |
| Which local workspace directory | Land `local` (cwd jail), which is not a tenant |
| Which linked account or mailbox | Binding + command args (`--folder`, account id) |
| Which domain entity | Command args, still scoped by tenant. `--entity` only when Binding mints per entity |

## Control plane versus runtime

Keep the split many ecosystems make between an admin CLI (link, provision) and
a user CLI (send, receive).

| Approach | When |
|----------|------|
| Two packs | Different Binding modes, or very different RBAC |
| One pack, two namespaces | Same Binding family; mark `connect.*` clearly |

Different Binding modes always mean two packs, and an operator bin must never be
reachable from an end-user Principal just because it is on `PATH`.

## Bootstrap with pass-through, then promote

Pass-through gets a binary under Policy in an hour:

```json
"pass_through": { "allowedSubcommands": ["status", "list"], "maxArgv": 6 }
```

Then promote the hot path to named commands with Policy rows and delete the
allow-list, because a wrap that stays pass-through does not conform.

## Anti-patterns

| Anti-pattern | Do instead |
|--------------|------------|
| Secrets in `HARNESS.md` or the manifest | Binding mint |
| Interactive `--login` on the agent path | Separate human `connect`; Sense `binding.degraded` |
| One mega-command `run` | Named commands with Policy |
| Different auth for UI vs agent | Shared Binding |
| Teaching an MCP tool list as the pack | Pack commands; MCP is an optional export |
| Host-absolute paths in Skill steps | Portable bins; runtime documents env |

## Proof

```bash
hcpx check ./packs/mail --probe --strict   # quality rules, live probes, eval cases
hcpx lint ./packs/mail --json
hcpx install ./packs/mail --json && hcpx activate mail --json
hcpx exec mail list --json -- folder=inbox                 # one read
hcpx exec mail send --json -- to=a@b.c subject=Hi           # → confirm required
hcpx exec mail send --json -- to=a@b.c subject=Hi confirm=true   # mock/paper
```

[Testing](/docs/packs/check#testing) explains what each step proves.

## Related

- [CLI projection](/docs/concepts/command#cli-projection)
- [Pack classes](/docs/packs/best-practices#pack-classes)
- [Adapter](/docs/concepts/adapter)

Source: https://harnesscontextprotocol.io/docs/packs/wrap-a-cli

---

# Check and evaluate

> Lint, 61 review rules, live CLI probes, eval cases and the proof script every pack ships, with the exit codes and score that CI and review read.

`hcpx lint` proves a pack's three files exist and agree. `hcpx check` asks
whether an agent will pick the right command, fill its arguments, read its
output and be stopped before a risky write, with 61 rules that write down a
careful maintainer's review. Run it with `--probe` after every edit until no
error is left, then ship the [proof script](#the-proof-script) in CI.

```bash
hcpx check ./packs/gh                    # static rules: structure, manifest, Policy, Skill
hcpx check ./packs/gh --probe            # also run the CLI the way an agent does
hcpx check ./packs/gh --probe --agent    # the same, as a fix list for a coding agent
hcpx check ./packs/gh --init-evals       # write a starter hcp.eval.json
hcpx eval  ./packs/gh                    # replay eval cases through the runtime only
```

It exits `0` with nothing to fix, `2` on warnings only and `1` on any error.
Notes (`info`) never fail, and `--strict` fails warnings too, for CI.
`--timeout <ms>` limits each probe and eval case (default 10000), and
`--no-eval` skips the eval layer.

## The four layers

| Layer | Runs | Needs |
|-------|------|-------|
| Structure | [Lint](#lint): three files, Policy coverage, Skill drift | Nothing |
| Static | Quality rules over `surface.json`, `policy.json` and `HARNESS.md` | Nothing |
| Probe | The wrapped CLI with stdin closed, `NO_COLOR=1`, `TERM=dumb` and a timeout: `--help`, an unknown subcommand, `<bin> <subcommand> --help` per command, and every read it can call | `--probe`; the CLI on `PATH` or in the pack |
| Eval | [Eval cases](#eval-cases) through the reference runtime with grants, the confirm gate and the sandbox | An eval file and a pack that passes the structure layer |

Probes never run a write. For a pack with `wrap.workspace.mode: project`,
probes and eval cases run in the directory you started `hcpx check` from, so you
can check against a real project.

## Findings and score

Each finding has a stable id, a severity, the file and JSON pointer or heading,
what is wrong and a fix to apply as is.

```text
error  R014             surface.json /verbs/list/cli
       "gh list" is not a command of this CLI (unknown command "list" for "gh").
       fix: Point "list".cli at a subcommand that exists (see "gh --help"), or remove
            "list" from surface.json, policy.json, and HARNESS.md.
```

The score starts at 100. Each error costs 12 points and each warning 4, any
error caps the grade at D, and a pack is ready to share at 90 or more with no
errors. `--json` returns the full report: `score`, `grade`, per-category scores,
every finding with `fix` and `docs`, the probe records (exit code, milliseconds,
approximate tokens, whether stdout was JSON) and eval results with p50 and p95
latency.

## The coding-agent loop

`--agent` prints Markdown for a coding agent: errors, then warnings, each with a
"Do:" line and a link to the reason, then the eval table and the loop
instruction.

```md
# hcpx check: gh

Score 8/100, grade F. 7 error(s), 2 warning(s).

## Fix now (errors)

1. **surface.json `/verbs`** (M018): 6 command(s) still carry the scaffold template …
   Do: Replace the template commands with 5 to 12 real commands from "gh --help", …
```

The [`/wrap.md`](/wrap.md) runbook scaffolds with `hcpx wrap`, runs
`hcpx check --probe --agent`, fixes errors and then warnings, and stops at 90 or
more with no errors. Agents must never weaken an eval expectation to make a case
pass.

## Eval cases

`hcp.eval.json` sits next to the three pack files. Each case names a command
(`verb`), its `args` and what to `expect`.

```json title="packs/gh/hcp.eval.json"
{
  "cases": [
    { "name": "repo.view returns JSON", "verb": "repo.view",
      "args": { "repo": "cli/cli" },
      "expect": { "ok": true, "json": true, "contains": ["cli/cli"], "max_ms": 8000 } },
    { "name": "issue.create is refused without confirm", "verb": "issue.create",
      "args": { "repo": "cli/cli", "title": "probe", "body": "never created" },
      "expect": { "ok": false, "error": "confirm required" } }
  ]
}
```

| `expect` field | Passes when |
|----------------|-------------|
| `ok` | The runtime result's `ok` matches. A case without `expect` expects `ok: true` |
| `error` | The error message contains this text |
| `contains` / `not_contains` | Output includes / excludes each string |
| `json` | A successful call's output parsed as JSON |
| `max_ms` | The call finished within this many milliseconds |
| `max_tokens` | Output stayed under this many tokens (about four characters each) |

Cases run on the reference runtime's `hcpx exec` pipeline as an agent Principal
with every grant, in sandbox mode, with default access settings and the confirm
gate. A confirm-gated write is refused unless the case passes `confirm: true` in
its `args`, so a refusal case and a confirmed run can sit side by side. Output
is the command's stdout, or its JSON result when stdout is empty.

A case fails without running while an argument still starts with `TODO`, and
fails when it runs past the timeout. Results record duration and approximate
tokens, and the report adds the pass count and p50 and p95 latency. A failing
case is finding E005: fix the command, or the case if its expectation is wrong.
The layer runs whenever the file exists and the pack passes the structure layer,
and `hcpx eval` runs it alone, one line per case. The first case that expects
success also supplies a read's arguments to the probes, so probes reach reads
that need arguments.

`--init-evals` writes `hcp.eval.json` unless one exists, with one case per read
expecting `{ "ok": true, "json": true, "max_ms": 3000, "max_tokens": 5000 }` and
one refusal case per confirm-gated write expecting
`{ "ok": false, "error": "confirm required" }`; writes without `confirm` get no
case. Required arguments come from the manifest's first example or stay
`TODO <name>` until you replace them with real values from the CLI.

## Testing

A pack proves itself with a script ([L12](/docs/introduction/design-principles))
after lint passes, and one that passes its proof script against a conforming
runtime is a conforming pack ([Specification: Conformance](/docs/specification/conformance)).
The protocol's own vectors live in the SDK, use `transit` and `ledger` as
fixtures and test the runtime ([Runtime conformance](/docs/runtimes/conformance#vectors)).

### Lint

```bash
hcpx lint ./packs/ledger --json
```

```json
{ "ok": true, "pack_id": "ledger", "issues": [] }
```

A pack fails lint on any of these, and each message names its fix. In
`hcpx check` they appear with ids `L-<kind>`.

| Kind | Message |
|------|---------|
| `missing_harness` | `HCP pack "x" missing HARNESS.md skill` |
| `missing_policy` | `HCP pack "x" missing policy.json` |
| `missing_verb` | `verb "transfer" missing from policy.json` |
| `extra_verb` | `policy verb "refund" not in pack manifest` (remove the row or add the command) |
| `missing_stimulus` | `stimulus "new_item" missing from policy.json` |
| `extra_stimulus` | `policy stimulus "old" not in stimulusTriggers` (remove the row or declare the ref) |
| `write_stimulus` | `policy stimulus "new_item" is a write; a trigger announces a change and is always "read"` |
| `missing_verb` (id) | `policy surface_id "ledgr" !== "ledger"` |
| `wrap` | `wrap requires named commands or pass_through` |
| `binding` | `wrap adapter requires wrap.bin` |
| `binding` | `control plane packs default Binding mothership` (fix the mode or the plane) |
| `invalid_meta` | `openapi adapter requires openapi ref` · `sense_only adapter requires at least one stimulus trigger_ref` · schema errors |

In code:

```ts
import { loadPackFromDir, lintLoadedPack } from '@harnesscontextprotocol/sdk'

const pack = await loadPackFromDir('./packs/ledger')
const issues = lintLoadedPack(pack)   // [] when clean
```

### The proof script

Every pack ships one, and CI runs it against a mock or paper mode of the bin.

```bash title="proof.sh"
set -e
hcpx lint ./packs/ledger --json
hcpx install ./packs/ledger --json
hcpx activate ledger --json

# 1. one read
hcpx exec ledger list --json

# 2. gated write without confirm → live deny
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit | grep -q '"confirm required"'

# 3. the same write with confirm, in mock/paper mode
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit confirm=true

# 4. availability shows the four flags
hcpx availability ledger --json

# 5. if Sense is declared: announce with grant on, deny with grant off
#    (runtime-specific harness; see Sense delivery)
```

| Step | Proves |
|------|--------|
| lint | Policy coverage, Skill present, wrap shape |
| one read | Adapter path works; sandbox allow-list is right |
| deny without confirm | The gate is enforced before the adapter |
| write with confirm | The happy path, without spending |
| availability | installed · activated · connected · executable, with reasons |
| Sense on/off | Isolation; `sense.enable` is respected |

Steps 1 to 3 can also live in `hcp.eval.json` as [eval cases](#eval-cases).

### The twin-face check

Both faces must return the same result for the same arguments.

```bash
transit plan A B --json                              > cli.json
hcpx exec transit plan --json -- from=A to=B | jq .result > exec.json
diff cli.json exec.json
```

A mismatch means a `cli[]` template is wrong or the bin parses arguments
differently from what the manifest claims.

### Grant deny

A read with no grants must be denied, naming the missing capability.

```ts
const hcp = await Hcp.create({ packDirs: ['./packs/ledger'], grants: [] })
await hcp.activate(['ledger'])
const res = await hcp.exec('ledger', 'list')
// { ok: false, error: 'denied: missing verb.exec.read for ledger.list' }
```

### Sandbox deny

A wrap whose bin is not allow-listed must fail closed instead of falling back
to `PATH`.

```json
{ "ok": false, "error": "sandbox: bin \"ledger\" not allowlisted" }
```

## In CI

```yaml title=".github/workflows/pack.yml"
- run: npx @harnesscontextprotocol/cli check ./packs/gh --probe --strict --json > check.json
```

```yaml title=".github/workflows/pack.yml"
- run: bun install
- run: bun ./packages/cli/src/hcp.ts lint ./packs/ledger --json
- run: bun ./packages/cli/src/hcp.ts eval ./packs/ledger
- run: ./packs/ledger/proof.sh
```

Set `HCP_HOME` to a temp directory so CI never touches a real store, and keep
the `check` JSON report as a build artifact for reviewers.

## Rule catalogue

The rules encode HCP's [design laws](/docs/introduction/design-principles) and
Gate 0 for wrapped CLIs, the [Command Line Interface Guidelines](https://clig.dev)
(exit codes, stdout for data and stderr for messages, no prompts without a TTY,
`NO_COLOR`) and guidance on
[writing tools for agents](https://www.anthropic.com/engineering/writing-tools-for-agents)
(unambiguous parameter names, high-signal output, a 25,000-token budget,
actionable errors, evaluation against realistic tasks).

### Manifest

| Id | Severity | Rule |
|----|----------|------|
| M001 | warn, error above 12 | 5 to 12 commands |
| M002 | error | Command names are lower case with dot-separated families |
| M003 | error | No catch-all command (`run`, `exec`, `shell`, …) |
| M004 | error | Every command has a description |
| M005 | warn | The description names input and result, not "Run." |
| M006 | info | Descriptions stay one sentence |
| M007 | warn | Wrap commands declare `cli[]` |
| M008 | warn | `cli[0]` equals `cliBin` |
| M010 | error | No placeholder that forwards arbitrary arguments (`{{argv}}`) |
| M011 | warn | Argument names are unambiguous (`issue_number`, not `x`) |
| M012 | error | No `pass_through` left in a finished pack |
| M013 | error | A Binding mode is declared |
| M014 | warn | Public Binding uses a public hub |
| M015 | error | No credential in the manifest |
| M016 | warn | `wrap.bin` is not a host-absolute path |
| M018 | error | No commands left over from the `hcpx init` / `hcpx wrap` template |
| M019 | error | `wrap.bin` does not point outside the pack; install copies only the pack |
| M020 | info | Every argument has a description and an example |
| M021 | warn | No `TODO` left in a description or argument example |
| M022 | info | Argument descriptions say where the value comes from, not just its name |
| M023 | warn | A CLI on `PATH` runs in the project (`wrap.workspace.mode: project`), not the pack directory |
| M024 | warn | A declared `preview` uses a known kind and only the command's own arguments |
| M025 | info | A confirm command declares a `preview`; the fix is the suggested JSON |
| M026 | warn | A declared [`title`](/docs/concepts/command#titles) uses only the command's arguments, puts `{{result.…}}` only in `done`, and ends each form with a template without placeholders |

### Policy

| Id | Severity | Rule |
|----|----------|------|
| P001 | warn, error for risky names | A command named like a write is not `read` |
| P002 | error | Commands that delete, send, pay, publish, or deploy carry `confirm: true` |
| P003 | error | `confirm` only on writes |
| P004 | warn | A confirm-gated command also passes the CLI's own confirmation flag |
| P005 | warn | `connect` and `setup.*` changes are writes |
| P006 | warn | A pack that writes has a `doctor` or `status` command |
| P007 | info | Commands that pay or transfer use `confirm: "always"`, so access mode `run` still asks |

### Skill

| Id | Severity | Rule |
|----|----------|------|
| S001 | warn | Title is `# HCP — <Name>` |
| S002 | error | A lead follows the title |
| S003 | warn | The lead fits the ~480 characters hosts load |
| S004 | warn | The lead says whether login is needed |
| S005 | error | A hot-path table lists the commands |
| S006 | warn | Examples use `--json` |
| S007 | warn | An anti-patterns section exists |
| S008 | info | 3 to 6 anti-patterns |
| S009 | warn | The Skill fits on one screen |
| S010 | warn | Packs with credentials have a Setup section |
| S011 | info | Triggers are mentioned only when declared |

### Probe

| Id | Severity | Rule |
|----|----------|------|
| R001 | error | `wrap.bin` resolves (in the pack or on `PATH`) |
| R002 | warn | `--help` exits 0 with output |
| R003 | error | An unknown subcommand exits non-zero |
| R004 | warn | Failures explain themselves on stderr or as JSON |
| R005 | error | Reads finish with stdin closed (no prompts, no hangs) |
| R006 | error | JSON mode prints only JSON on stdout |
| R007 | warn | No ANSI colour when stdout is not a TTY or `NO_COLOR` is set |
| R008 | warn | Output stays under ~25,000 tokens |
| R009 | info | Output over ~5,000 tokens suggests a concise default |
| R010 | info | Reads over 3 seconds |
| R011 | warn | Health commands exit 0, 2, or 1 |
| R012 | info | At least one read could be probed |
| R013 | error | Read commands succeed |
| R014 | error | Every command's subcommand exists in the CLI |

### Eval

| Id | Severity | Rule |
|----|----------|------|
| E001 | warn | An eval file exists |
| E002 | error | Cases name real commands |
| E003 | warn | Every confirm-gated write has a refusal case |
| E004 | warn | At least one read succeeds |
| E005 | error | Every case passes |

## Related

- [Best practices](/docs/packs/best-practices)
- [Errors](/docs/reference/errors)

Source: https://harnesscontextprotocol.io/docs/packs/check

---

# Share a pack

> Find packs with `hcpx search`, add yours to the catalog by pull request, publish one as an npm package or extract a gold pack into its own repository.

A shared pack is a directory someone else can lint, install and exec. GitHub
holds the files and the community catalog indexes them by pull request, with an
npm package as a convenience install on top.

## Minimum to share

| File | Required |
|------|----------|
| `surface.json` | Yes |
| `policy.json` | Yes |
| `HARNESS.md` | Yes |
| `LICENSE` | Yes for a standalone repo |
| `README.md` | Yes for a standalone repo |
| Wrapped `bin` | Inside the pack directory, relative path |

Keep credentials out of the pack, host-absolute paths out of the Skill and
product brand names out of public gold packs.

## Catalog

[Packs](/packs) lists every pack you can install today, with its commands and
what each one asks before it writes. `hcpx search` reads the same index
(`/api/v1/packs`), and `HCP_CATALOG_URL` points it at a private one.

```bash
hcpx search transit --json
hcpx init transit --template transit
hcpx install ./transit --json
hcpx activate transit --json
hcpx exec transit plan --json -- from=A to=B
```

| Surface | Use |
|---------|-----|
| [`/api/v1/packs?query=`](/api/v1/packs?query=transit) | Search |
| [`/api/v1/packs/{id}`](/api/v1/packs/transit) | One entry |

To add your own, scaffold it (`--class public-read` or `gated-write`), lint it
and let `hcpx share` print the catalog row:

```bash
hcpx init weather --class public-read
# edit the three files
hcpx lint ./weather --json
hcpx share ./weather --json
```

Paste the row into
[`content/catalog/packs.json`](https://github.com/harnesscontextprotocol/hcp-docs)
in a pull request. Reviewers check lint, the class, and that the Skill names
every command.

Pack signing, provenance, integrity hashes and `pack_id@version` resolution
belong to the [HCP/2.0 registry](/docs/community/roadmap), and a catalog pull
request does not mint them. Until then you install from a directory or a
scaffold the CLI writes, and a runtime may install any directory that lints.

## npm package

A pack directory can also be an npm package, by this convention:

| Field | Value |
|-------|--------|
| `"hcp": { "pack": "." }` | Where `surface.json` lives inside the package |
| `dependencies["@harnesscontextprotocol/cli"]` | Installs the CLI (`hcpx`, also `hcp`) |
| `bin` | Optional wrap CLI (`transit`, `weather`, …). Never name it `hcpx` or `hcp` |
| `files` | The three protocol files plus wrap `bin/` |

```bash
hcpx install npm:@scope/hcp-pack-name --json
hcpx install npm:./path/to/pack --json
```

HCP's own packages stay a private preview on GitHub Packages by choice
(`access: restricted` on `@harnesscontextprotocol/sdk`,
`@harnesscontextprotocol/cli` and `@harnesscontextprotocol/example`), so they
are not on the public npm registry.

## Extract a gold pack into its own repository

`examples/standalone/transit` in the SDK repo is the gold pack built to
extract, with the demo binary next to the three files, an MIT licence and a
vendor-neutral README, so the extract is a plain copy. Work through this list
before `gh repo create harnesscontextprotocol/hcp-example-transit`, and keep
the repository private until every item holds:

1. `bun run verify:legal` passes on the docs site.
2. The pack lints clean from a fresh clone: `hcpx lint . --json`.
3. `wrap.bin` is a path inside the repo (`./bin/transit.mjs`), not `../../bins/`.
4. README shows install, activate, and one read exec.
5. No tokens, `.env`, or host paths.
6. Licence matches the SDK (MIT for executable examples).

## Related

- [Build a pack](/docs/packs)
- [Examples](/docs/packs/examples)

Source: https://harnesscontextprotocol.io/docs/packs/share

---

# Build a runtime

> Start a runtime on the reference SDK or in any language, meet the ten-point contract, and keep catalog, hub and RBAC private behind the open pack format.

In HCP the harness is the runtime. Whatever already drives the agent loads
packs, enforces grants, mints Binding and runs Adapters, so there is no separate
server to host and no daemon to keep alive. Start on the reference TypeScript
SDK below, or build in any language against [the contract](#the-contract) and
prove it with the [conformance vectors](/docs/runtimes/conformance).

## Start with the reference SDK

The reference runtime is `@harnesscontextprotocol/runtime`, the `Hcp` class
(also at `@harnesscontextprotocol/sdk/runtime`). It is file-backed, in-process
and local, takes pack directories, a Principal and grants with nothing else
implicit, and exists so the protocol runs from day one and the conformance
vectors have something to run against.

<Steps>
<Step>

### Install

The packages are in a private preview on GitHub Packages. With preview access,
point the scope there; `GITHUB_TOKEN` is a token that can read packages:

```text title=".npmrc"
@harnesscontextprotocol:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
```

```bash
bun add @harnesscontextprotocol/runtime
bun add @harnesscontextprotocol/sdk
bun add -g @harnesscontextprotocol/cli
```

A runtime that only parses and lints packs, with no agent loop, can import
`@harnesscontextprotocol/sdk/pack`, which carries no AI SDK dependency.

</Step>
<Step>

### Create a runtime over pack directories

```ts title="runtime.ts"
import { Hcp } from '@harnesscontextprotocol/runtime'

const hcp = await Hcp.create({
  packDirs: ['./packs/transit', './packs/ledger'],
  sandbox: true,                        // default posture
  allowBins: ['transit', 'ledger'],     // wrap bins the sandbox may spawn
  principal: { id: 'user_42', kind: 'human' },
  tenant_id: 'acme',
  project_id: null,                     // required only for land: brain
  grants: [
    { capability: 'verb.exec.read',  pack_id: 'transit' },
    { capability: 'verb.exec.read',  pack_id: 'ledger' },
    { capability: 'verb.exec.write', pack_id: 'ledger', verb_keys: ['transfer'] },
    { capability: 'pack.connect',    pack_id: 'ledger' },
  ],
})
```

Leave out `grants` only in a local demo ([Authorization](/docs/concepts/access#authorization)).

</Step>
<Step>

### Activate, connect, exec

```ts
await hcp.activate(['transit', 'ledger'])
await hcp.connect('ledger')                    // Binding mint; skipped for public packs

const read = await hcp.exec('transit', 'plan', { from: 'A', to: 'B' })
// { ok: true, pack_id: 'transit', verb_key: 'plan', result: {…} }

const denied = await hcp.exec('ledger', 'transfer', { amount: 10 })
// { ok: false, error: 'confirm required' }

const ok = await hcp.exec('ledger', 'transfer', { amount: 10, confirm: true })
// { ok: true, … }
```

Every result is an envelope, `{ ok, pack_id, verb_key, result?, error? }`, and
denials come back as values an agent can branch on, never as thrown exceptions.

</Step>
<Step>

### Report availability

```ts
import { buildPackAvailability } from '@harnesscontextprotocol/sdk'

for (const pack of await hcp.listPacks()) {
  const report = buildPackAvailability({
    pack,
    installed: true,
    activated: hcp.activatedPacks().includes(pack.pack_id),
    connected: false,
    grants: hcp.authz.grants,
  })
  // report.verbs[i] → { verb_key, operation, land, executable, reason? }
}
```

An agent reads [availability](/docs/concepts/lifecycle#availability) before it
plans, so it does not discover a denial mid-run.

</Step>
</Steps>

## The contract

A conforming runtime does these ten things, each in whatever way it chooses.
Every exec passes them in the order of the
[pipeline](/docs/runtimes/execution#the-pipeline), and any step may deny.

| # | Owns | Fail-closed rule | Page |
|---|------|------------------|------|
| 1 | Load `surface.json`, `policy.json` and `HARNESS.md` from a pack directory | Reject on parse error | [Execution](/docs/runtimes/execution) |
| 2 | Lint on install: Policy covers every command and trigger, Skill present, wrap has named commands or `pass_through` | Reject on any issue | [Testing](/docs/packs/check#testing) |
| 3 | Authorise each exec against five capability axes for a Principal | Deny with a reason; never default-allow | [Authorization](/docs/concepts/access#authorization) |
| 4 | Gate `confirm: true` writes | Refuse before any adapter runs | [Execution](/docs/runtimes/execution) |
| 5 | Mint Binding through a hub for `user`, `local` and `mothership` | No ambient env fallback | [Binding](/docs/concepts/binding) |
| 6 | Execute the Adapter: spawn the wrap bin with a rendered argv, or call in-process | Unknown bin denied | [Execution](/docs/runtimes/execution) |
| 7 | Sandbox by default: bin allow-list, cwd jail, no inherited credentials | `native-local` only by explicit opt-in | [Sandbox](/docs/runtimes/sandbox) |
| 8 | Audit writes | Full attribution tuple | [Audit](/docs/concepts/access#audit) |
| 9 | Report availability per command: installed, activated, connected, executable | Every `false` carries a reason | [Lifecycle](/docs/concepts/lifecycle) |
| 10 | Route Sense (optional): accept declared triggers and record them in a sink | `sense.enable` off → drop | [Sense delivery](/docs/concepts/sense#sense-delivery) |

A runtime implements the protocol, stores and runs packs, and enforces their
Policy. It must not:

- Add its own Policy rows, override a pack's, or rewrite a pack.
- Extend the protocol's types (a "Connector", another land, a new capability)
  without an RFC.
- Fall back to environment tokens when mint fails.
- Collapse read, write and connect into one boolean in a multi-tenant product.
- Let an end-user Principal reach a `mothership` Binding without an explicit
  elevation grant.
- Treat polling as Sense, or deliver undeclared triggers.

## Drivers

`Hcp` is a thin front over an `HcpRuntimeDriver`, a small interface with
`listPacks`, `getPack`, `exec` and an optional `connect`. Two drivers ship, and
your own implements the same interface:

| Driver | Purpose |
|--------|---------|
| `reference-local` | File-backed, in-process, sandbox allow-list, default authoriser. The conformance target |
| `proprietary-stub` | In-memory catalog and grants (`createProprietaryStubRuntime`). The shape of a company runtime, for tests |

## Proprietary runtimes

The pack format is open and a runtime does not have to be, so a company can put
HCP in a real product without publishing its internals while its packs stay
portable. HCP needs no public network "HCP server", because remote transport is
a runtime choice and the protocol defines no server role, and no registry, since
a local directory install is enough for HCP/1.x. Harnesses see only `activate`,
`connect`, `exec`, the Skill and optional Sense.

| You keep private | Freedom |
|------------------|---------|
| Pack catalog | Private, curated, versioned however you like: SQL tables, a directory, a private registry |
| Binding hub | Your keyring, OAuth flows and refresh schedule, a local file store, an OS keychain |
| RBAC | Your role tables, row-level security, IdP and tenancy model, mapped onto the five capabilities |
| Sandbox depth | Process allow-list up to containers and microVMs |
| Sense sink | An inbox, a queue, a webhook fan-out, UI badges |
| Principal mapping | Session → `{ id, kind, tenant, project }` |
| Transport | In-process, HTTP, stdio, or none |

None of these is an HCP type, and you never publish ACL tables, catalog schema
or hub internals. The protocol fixes what keeps packs portable:

| Fixed by the protocol | Why |
|-----------------------|-----|
| The three files and their schemas | So packs are portable |
| Command twin face and `verb_key` | So invokers are interchangeable |
| Policy tokens and the confirm gate | So authors write one Policy |
| Binding modes and hubs (as names) | So packs declare an expectation without knowing your hub |
| The five capability axes and evaluation order | So grants mean the same thing everywhere |
| Sense isolation rules | So an event never becomes an action silently |
| Availability flags | So agents plan the same way on every runtime |

Your runtime is an `HcpRuntimeDriver`:

```ts
import type { HcpRuntimeDriver, HcpAuthzContext } from '@harnesscontextprotocol/sdk'

export function createAcmeRuntime(deps: { catalog: Catalog; hub: Hub; rbac: Rbac; audit: Audit }): HcpRuntimeDriver {
  return {
    id: 'acme',
    kind: 'proprietary',

    listPacks: () => deps.catalog.list(),
    getPack:   (id) => deps.catalog.get(id),

    async connect(pack_id, authz) {
      if (!deps.rbac.has(authz, 'pack.connect', pack_id)) return { ok: false, error: 'denied: missing pack.connect' }
      return deps.hub.connect(pack_id, authz.principal)
    },

    async exec({ pack_id, verb_key, args = {}, authz, sandbox }) {
      const pack = await deps.catalog.get(pack_id)
      if (!pack) return { ok: false, pack_id, verb_key, error: `unknown pack ${pack_id}` }
      const policy = pack.policy.verbs[verb_key]
      if (!policy) return { ok: false, pack_id, verb_key, error: `no policy for ${verb_key}` }

      const cap = policy.operation === 'write' ? 'verb.exec.write' : 'verb.exec.read'
      if (!deps.rbac.has(authz, cap, pack_id, verb_key)) return { ok: false, pack_id, verb_key, error: `denied: missing ${cap} for ${pack_id}.${verb_key}` }
      if (policy.privacy.land === 'brain' && !authz.project_id) return { ok: false, pack_id, verb_key, error: 'denied: privacy.land brain requires project_id' }
      if (policy.confirm && args.confirm !== true && args.apply !== true) return { ok: false, pack_id, verb_key, error: 'confirm required' }

      const cred = await deps.hub.mint(pack, authz)          // fail closed inside
      const result = await runAdapter(pack, verb_key, args, cred, sandbox)
      if (policy.operation === 'write') await deps.audit.write({ authz, pack_id, verb_key, binding: cred.identity })
      return result
    },
  }
}
```

```ts
const hcp = await Hcp.create({ runtime: createAcmeRuntime(deps), grants: [] })
```

`createProprietaryStubRuntime` is an in-memory version of this shape, so you can
test multi-runtime behaviour before your real dependencies exist. A company
runtime meets the same [contract](#the-contract): a private catalog is fine, the
JSON face of the exec envelope is required and the CLI face only if you expose
one, and the five capability axes are the minimum.

Because packs never reference a runtime, the same `ledger` pack activates on the
reference-local runtime in CI, a developer's desktop in native-local, and a
company runtime in production, with the same Skill, Policy and commands. Only
the hub that mints and the RBAC that grants differ.

## Related

- [Implementations](/docs/community/implementations)
- [Specification: Conformance](/docs/specification/conformance)

Source: https://harnesscontextprotocol.io/docs/runtimes

---

# Execution

> Who may call a command and what the runtime does with the call, from the exec envelope through the fail-closed pipeline and wrap argv to the result.

Every Invoker reaches a command through one exec envelope and one fail-closed
pipeline, which ends in a result an agent can branch on. Build your runtime's
exec path against this page; [Specification: Execute](/docs/specification/execute)
holds the normative text.

## Invoker

An Invoker is whoever runs a command, and HCP treats all four the same:

| Invoker | Typical face | Example |
|---------|--------------|---------|
| Human | CLI | `ledger transfer 2.80 --to demo-transit --confirm --json` at a terminal |
| Agent | CLI or JSON | A tool loop calling `hcpx exec` or `Hcp.exec()` |
| Product API | JSON | A backend posting `{ pack_id, verb_key, args }` |
| UI | JSON | A button that fires the same envelope |

Invokers never redefine Policy or Binding
([L11](/docs/introduction/design-principles)). An Invoker carries a
[Principal](/docs/concepts/binding#principal) and adds no auth, confirm logic or
policy of its own, because with two policies on one command the weaker one wins.
Nor does its type pick the Binding mode: an agent acting for a human uses `user`
Binding for that human's mail, an operator job uses `mothership` Binding for
fleet wraps, and "it's an agent, give it the service token" is how elevation
bugs start.

```text
Agent harness ─┐
Product API   ─┼─► same command ─► Policy + grants ─► Binding mint ─► Adapter
UI gesture    ─┤
Human CLI     ─┘
```

Routing agent calls through bash and UI calls through HTTP is a runtime detail,
as long as both reach the same command, Policy row and Binding under a
Principal. An agent Invoker runs unattended, so it stays non-interactive (no
prompts, `-y` on installers), asks for `--json`, reads availability before
planning, surfaces writes as approvals and passes `confirm: true` only after
agreement, and loads a Skill's lead first and the full text on demand. A UI Invoker fires the JSON envelope, shows
the confirm gate as an approval dialog and never pre-sets `confirm: true`, takes
badges and "credential stale" banners from the runtime's Sense sink without
owning mint, and never embeds credentials in the client.

## The envelope

```json
{
  "pack_id": "ledger",
  "verb_key": "transfer",
  "args": { "amount": 10, "confirm": true }
}
```

| Field | Meaning |
|-------|---------|
| `pack_id` | The pack. `surface_id` is accepted as a legacy alias |
| `verb_key` | The command name |
| `args` | Command arguments. `confirm: true` or `apply: true` satisfies a confirm gate. `mode: "mock"` requests explicit mock Binding |

The CLI face (`ledger transfer 2.80 --to demo-transit --confirm --json`) is the
same command, and
`hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit confirm=true`
is how the CLI sends the envelope.

## The pipeline

The steps run in order, and nothing runs after a denial.

| # | Step | Denies with |
|---|------|-------------|
| 1 | Resolve the pack, its activation on this session and the command's Policy row, which a linted pack always has | `unknown pack <id>`, `pack <id> not activated`, `no policy for <verb_key>` |
| 2 | Authenticate the Principal and take the tenant from the session. Land `brain` needs a project in scope, and land `local` binds the sandbox cwd | `privacy.land brain requires project_id` |
| 3 | Assert the capability: `verb.exec.read` for `operation: read`, `verb.exec.write` for `operation: write`, `pack.connect` for a `connect` command, with grants scoped by `pack_id` and `verb_keys` | `denied: missing verb.exec.write for ledger.transfer` |
| 4 | Enforce confirm: a Policy row with `confirm: true` needs `confirm: true` or `apply: true` in the envelope | `confirm required` |
| 5 | Mint Binding from the hub for this Principal, pack and family (`user`, `local`, `mothership`). `public` skips the step and `mock` must be explicit in the envelope | The mint failure, never a fallback to an environment token |
| 6 | Run the Adapter in the sandbox: a wrap renders argv, checks the allow-list and spawns, and a composite calls in-process | `sandbox: bin "x" not allowlisted` (exit 127) |
| 7 | Return a structured result and audit writes with the [attribution tuple](/docs/concepts/access#audit) | None |

## Wrap rendering

For `kind: wrap`, the runtime builds argv from the command's `cli[]`:

```text
cli[]          ["ledger", "transfer", "{{amount}}", "--to", "{{payee}}", "--memo", "{{memo}}", "--confirm"]
args           { amount: "2.80", payee: "demo-transit", confirm: true }
argv           ["transfer", "2.80", "--to", "demo-transit", "--confirm", "--json"]
spawn          <resolved wrap.bin> transfer 2.80 --to demo-transit --confirm --json   (cwd = pack dir)
```

- The first `cli[]` token is the bin name and is dropped, since `wrap.bin` is
  what spawns; a relative `wrap.bin` resolves against the pack directory.
- `{{name}}` renders as `String(args.name)` and `--flag={{name}}` as
  `--flag=<value>`. A missing value renders empty and drops out with the flag in
  front of it (`--memo` above).
- Without `cli[]`, `wrap.argv_template` renders with `{{verb}}` set to the
  command tail (`setup.apply` becomes `apply`).
- The JSON flag (`--json` unless `json_flag` says otherwise) is appended, and
  the arguments go to the process as an array, with no shell.
- The working directory follows `wrap.workspace.mode`: the pack directory by
  default, the caller's project for `project`, or a private directory for
  `workspace` and `ephemeral` ([Where the CLI runs](/docs/packs/manifest#where-the-cli-runs)).
- `wrap.agent_env` is merged into the child environment, and `HCP_SANDBOX` is
  set to `sandbox` or `native-local`.

## Result envelope

```json
{
  "ok": true,
  "pack_id": "transit",
  "verb_key": "plan",
  "result": { "ok": true, "verb": "plan", "from": "A", "to": "B", "optionId": "opt-1" },
  "stdout": "…",
  "stderr": ""
}
```

| Field | Meaning |
|-------|---------|
| `ok` | `true` on exit 0 and no denial |
| `result` | Parsed JSON stdout if it parses, else the raw string |
| `error` | Present when `ok` is false: a denial reason, `confirm required`, stderr, or `exit N` |
| `stdout` / `stderr` | Raw streams (wrap only) |

A denial comes back as a value, never a thrown error, so agents can branch on
it. [Errors](/docs/reference/errors) has the full list.

## Composite and other adapters

`composite` returns an in-process result. `openapi` and `sense_only` are not
executable in the reference-local runtime, and a proprietary runtime implements
them behind the same `exec` signature. The bootstrap pack `hcp-cli` is composite:
its read commands (`hcp.doctor`, `hcp.list`, `hcp.availability`, `hcp.skill`)
return directly, and commands that change the store answer with
`deferred_to_cli: true` and a hint, because the CLI process owns the durable
store.

## Related

- [Command](/docs/concepts/command) · [Adapter](/docs/concepts/adapter)
- [Sandbox](/docs/runtimes/sandbox)

Source: https://harnesscontextprotocol.io/docs/runtimes/execution

---

# Sandbox

> The default execution posture, with a bin allow-list, a cwd jail and no ambient credentials, and why native-local is a trust decision you opt into.

Packs wrap real binaries, so a runtime sandboxes them unless someone opts out on
purpose. Leave the sandbox on everywhere except a personal machine whose owner
has chosen to trust the host `PATH`.

| Mode | When | Behaviour |
|------|------|-----------|
| `sandbox` (default) | Cloud, CI, demos, most harnesses | Allow-listed bins only; cwd jail; no host secret inheritance beyond Binding mint |
| `native-local` | Explicit desktop trust on a personal machine | Host `PATH` bins under user consent |

## What the sandbox enforces

### Bin allow-list

A pack may spawn only binaries the runtime allows, and an unknown bin fails
closed. There is no fallback to a `PATH` lookup, which would let a pack smuggle
in an executable the operator never approved.

```json
{ "ok": false, "error": "sandbox: bin \"transit\" not allowlisted" }
```

The reference runtime starts the allow-list with `hcpx` and `hcp`, or with the
`allowBins` you pass instead, and adds the `wrap.bin` of every pack it loaded.

### Working-directory jail

Execution stays in a known cwd, the pack directory in the reference runtime, so
a wrap cannot wander the filesystem through relative paths. Land `local` binds
to this directory.

### No ambient credentials

Binding mint never falls back to environment tokens, so an installed pack cannot
inherit whatever is exported in the shell. Env-flag mocks (`PACK_MOCK=1`) are
ignored, and a mock is `mode: mock` in the envelope.

### Marked environment

The child sees `HCP_SANDBOX=sandbox` (or `native-local`), so a wrapped bin can
refuse dangerous paths when sandboxed. The runtime also merges the manifest's
`agent_env` into the child environment and special-cases nothing else per bin.

## Configuring the reference runtime

```ts
const hcp = await Hcp.create({
  packDirs,
  sandbox: true,               // default
  allowBins: ['transit', '/opt/tools/ledger'],
})
```

`allowBins` accepts bare names or absolute paths, and a bare name matches a bin
whose path ends in `/<name>`.

## Native-local

```ts
const hcp = await Hcp.create({ packDirs, sandbox: false, nativeLocal: true })
```

Both flags are required, and `sandbox: false` alone still yields sandbox mode.
Native-local is a trust decision about the machine, meant for a personal desktop
where the user has consented to host `PATH` execution, so never turn it on to
make a demo work.

## Hardening beyond the reference

A process allow-list plus a cwd jail is the minimum. Containers, microVMs,
seccomp profiles and network egress policy per Binding are adopter upgrades that
slot in under the same profile, and packs stay the same because isolation lives
in the runtime.

| Layer | Reference | Upgrade |
|-------|-----------|---------|
| Process | Allow-list | Container / microVM |
| Filesystem | cwd jail | Read-only root, mounted pack dir |
| Network | Inherited | Egress per Binding family |
| Secrets | Mint only | Short-lived, scoped, injected as files |

## Related

- [Adapter](/docs/concepts/adapter) · [Execution](/docs/runtimes/execution)
- [Specification: Sandbox](/docs/specification/sandbox)

Source: https://harnesscontextprotocol.io/docs/runtimes/sandbox

---

# Runtime conformance

> The executable vectors a runtime must pass, the checklist behind them, what the suite does not cover yet, and how to run it against your driver.

A runtime conforms when it passes the vectors and meets the checklist, and both
are executable. Point the vectors at your own driver, work through the checklist
for what they do not test yet, then report the result.

## Vectors

The protocol's vectors ship in the SDK and use `transit` and `ledger` as
fixtures:

```bash
cd hcp/packages/sdk && bun test src/conformance.test.ts
```

| Vector | Passes when |
|--------|-------------|
| Lint | `transit`, `ledger`, and the composite `echo` lint clean |
| Read | `transit find` runs through the sandboxed runtime and returns the stop it resolved |
| Confirm | `transfer` without `confirm` → `{ ok: false, error: "confirm required" }`; with `confirm: true` → `ok: true` |
| Grant deny | With no grants, a read → `ok: false`, error contains `denied` |

The suite does not yet compare the twin faces, run a bin that is not
allow-listed, or lint a `wrap` with neither named commands nor `pass_through`.
The checklist still requires all three; the
[twin-face check](/docs/packs/check#the-twin-face-check) and the
[sandbox deny](/docs/packs/check#sandbox-deny) show how to test them.

## Running them against your driver

Pointing `Hcp` at your driver runs the same tests:

```ts
import { Hcp, loadPackFromDir } from '@harnesscontextprotocol/sdk'

const packs = await Promise.all([loadPackFromDir(transitDir), loadPackFromDir(ledgerDir)])
const hcp = await Hcp.create({ runtime: createAcmeRuntime({ packs, … }), grants })
await hcp.activate(['transit', 'ledger'])

expect((await hcp.exec('ledger', 'transfer', { amount: '2.80', payee: 'demo-transit' })).error).toBe('confirm required')
```

## Runtime checklist

### Loading

- [ ] Loads `surface.json`, `policy.json`, `HARNESS.md` from a directory
- [ ] Rejects a pack with any lint issue at install
- [ ] Checks `id` = `surface_id` = `pack_id`
- [ ] Rejects an unsupported `hcp` protocol affinity and reports it

### Authorisation

- [ ] Exactly one Principal per exec, from the session
- [ ] Five capability axes; none collapsed in multi-tenant deployments
- [ ] `brain` land requires project scope
- [ ] Tenant never taken from envelope args
- [ ] Denials carry a reason string
- [ ] No default-allow path

### Gates and Binding

- [ ] `confirm: true` enforced before any adapter; `confirm` / `apply` in args satisfies it
- [ ] `confirm: "always"` gated at least like `confirm: true` (draft RFC 0006)
- [ ] Mint fail-closed; no env-token fallback
- [ ] `mock` only when explicit in the envelope
- [ ] `mothership` Binding unreachable from a non-operator Principal without an elevation grant
- [ ] `connect` requires `pack.connect`, not `verb.exec.write`

### Execution

- [ ] Wrap argv rendered from `cli[]` / `argv_template`; no shell interpolation
- [ ] The JSON flag appended (`--json` unless `json_flag` says otherwise); result parsed when possible
- [ ] Non-zero exit → `ok: false` with stderr or `exit N`
- [ ] Sandbox default; unknown bin denied; cwd fixed
- [ ] `native-local` requires explicit opt-in

### Reporting

- [ ] Availability per pack: `installed`, `activated`, `connected`
- [ ] Availability per command: `executable` with `reason` when false
- [ ] Skill exposed on activate (copied to `skills/<id>/SKILL.md` or equivalent)

### Sense (if implemented)

- [ ] Activation independent of Execute
- [ ] `sense.enable` checked before any sink write; off → drop
- [ ] Undeclared refs dropped and reported
- [ ] Sink records inert and attributable
- [ ] Wake path goes through Execute grants
- [ ] `binding.degraded` produced by the hub

### Audit

- [ ] Writes attributed with `Principal + tenant (+ project) + Binding + pack_id + verb_key + operation`

## Reporting conformance

State the protocol version (`HCP/1.1`), the vector suite commit, and which
optional parts you implement (Sense, MCP export, the `openapi` adapter), then
follow [Implementations](/docs/community/implementations) to be listed.

## Related

- [Specification: Conformance](/docs/specification/conformance): the normative checklist for packs, runtimes, and hosts

Source: https://harnesscontextprotocol.io/docs/runtimes/conformance

---

# Adopt in a host

> One command gives a coding agent the HCP Skill, the `hcpx` binary and one MCP tool, and an in-process loop gets one typed tool per command.

A host is an agent product such as an IDE assistant, a coding agent or a
TypeScript tool loop, and adopting HCP takes about as much work as adding an MCP
server. For a supported host one command is the whole integration: it writes one
Skill that teaches the CLI and one tool, `hcp`, that runs it, both generated
from the activated packs, and a pack activated later changes the Skill's catalog
without adding a tool. Pick your agent below, or embed the runtime if you build
your own loop.

## Set up in one command

```bash
hcpx activate transit ledger
hcpx adopt claude --write        # or: codex, cursor, opencode, grok, mcp, agent
```

<HostSetup />

To inspect what adopt would write, run any target with `--json` and without
`--write` (`hcpx adopt cursor --json`), or run `hcpx generate --json`. What a
host gets depends on whether its agent has a terminal.

| Host | What it gets | Adding a pack later |
|------|--------------|---------------------|
| [Coding agent](/docs/hosts/coding-agents) with a terminal (Claude Code, Cursor, Codex, OpenCode, Grok Bot) | The agent plugin: one Skill and the `hcpx` binary, plus the one-tool MCP export | Changes the catalog the Skill points at; no new tools |
| [MCP-only client](/docs/hosts/mcp) | The same plugin exposed as one MCP tool, `hcp` | Same; `tools/list` stays `[hcp]` |
| In-process loop ([AI SDK](/docs/hosts/ai-sdk)) | `HarnessAgent`: one typed tool per executable command, named `<pack_id>__<verb_key>` | Adds that pack's executable commands as tools |

Both shapes come from one generator, under the normative rules in
[Projections](/docs/specification/projections), and run through the same
runtime, the open reference or a company's own, which holds Binding, grants and
the sandbox.

```text
activated packs ──► generateHarnessSurfaces() ──┬── .hcp/plugin.json         one bash wrap (`hcpx`)
                                                ├── .hcp/surface.json        catalog
                                                ├── Cursor                    Skill + .cursor/mcp.json
                                                ├── OpenCode                  one tool `hcp`
                                                ├── Grok Bot / Grok Build     terminal → hcpx · one MCP tool
                                                └── AI SDK                    HarnessAgent tool map (in-process)
```

## The agent plugin

Every coding agent adopts HCP through the plugin, and its three faces reach the
same runtime.

| Face | What it is |
|------|------------|
| Skill | One `SKILL.md` that teaches lint, install, and exec |
| Bash | One binary, `hcpx`, also installed as `hcp` ([two names](/docs/quickstart#one-binary-two-names)) |
| MCP | One tool named `hcp` (served by `hcpx mcp`) that runs the same commands |

```bash
bun add -g @harnesscontextprotocol/cli
bun add @harnesscontextprotocol/plugin
hcpx adopt agent --write
```

During the preview both packages are on GitHub Packages, where
`@harnesscontextprotocol/plugin` is restricted, and
[Install the CLI](/docs/quickstart#install-the-cli) shows the `.npmrc`.

```json title=".hcp/plugin.json"
{
  "id": "hcp",
  "wrap": { "bin": "hcpx", "kind": "bash" },
  "skill": ".hcp/skills/hcp/SKILL.md",
  "mcp": { "command": "hcpx", "args": ["mcp"], "tool": "hcp" },
  "home": "/home/you/.hcp",
  "activated": ["transit", "ledger"]
}
```

`mcp.command` and `wrap.bin` are `hcpx`, and adopt writes `hcp` instead only for
an older install that answers to no other name. The host targets (`claude`,
`codex`, `cursor`, `opencode`, `grok`, `mcp`) write this same plugin into the
paths each host reads, listed in [Coding agents](/docs/hosts/coding-agents).

## Embed the runtime in your own loop

If you build your own agent loop, `HarnessAgent` wraps the runtime as a
tool-loop agent with grant-filtered tools, progressive Skill disclosure and
approval on write commands.

```ts
import { Hcp, HarnessAgent } from '@harnesscontextprotocol/sdk'

const hcp = await Hcp.create({ packDirs: ['./packs/transit'], sandbox: true })
await hcp.activate(['transit'])

const agent = await HarnessAgent.create({
  hcp,
  harness: 'my-product-v1',
  runtime: 'ai-sdk',
  model,
  context: { skillLeadChars: 480 },
})

const run = await agent.send('Plan a route from A to B')
await run.wait()
```

Each executable command becomes one tool named `<pack_id>__<verb_key>`, with
dots turned into underscores (`transit__plan`), and long results are compacted
between steps ([Context optimization](/docs/hosts#context-optimization)).
[AI SDK](/docs/hosts/ai-sdk) has the options, approvals and events.

## Adding native support to a new host

A host with no adopt target can attach the plugin by hand: put the Skill where
the host reads Skills and `hcpx` on its `PATH`, or register MCP as
`{ "command": "hcpx", "args": ["mcp"] }`. Native intake takes little more work.
A host that meets the
[host checklist](/docs/specification/conformance#144-host-conformance):

1. Presents packs in one of the two shapes: the `hcpx` binary plus its Skill,
   for hosts whose agent has a terminal, or one tool per executable command
   named `hcpToolName(pack_id, verb_key)`, for in-process loops. Both come from
   `generateHarnessSurfaces()` or the catalog at `.hcp/surface.json`.
2. Executes through the runtime (`hcpx exec <pack> <verb>` on the CLI face,
   `Hcp.exec()` in-process) and never spawns the wrap binary itself.
3. Exposes the Skill (the copied `SKILL.md`) progressively, lead first and full
   text on demand.
4. Surfaces write commands as approvals and passes `confirm: true` only after
   the user has agreed.
5. Reads availability before offering a tool, so the agent never sees a command
   it cannot run.
6. Never embeds credentials, adds a second policy for the same command, or
   hand-registers tools that bypass the pack.

A host may also subscribe to Sense for chrome, such as an inbox badge or a
"credential stale" banner, through the runtime's sink, but it never owns mint.
New `hcpx adopt <host>` targets go through the
[RFC process](/docs/community/governance#rfc-process).

## Why MCP is not the install path

| | Adding an MCP server | Adopting HCP |
|--|----------------------|--------------|
| Setup | A config fragment per server | `hcpx install`, `hcpx activate`, then `hcpx adopt` once |
| `tools/list` | N tools per server | `[hcp]`, or the `hcpx` binary |
| Credentials | The server holds them | The runtime mints them; the pack names a mode |
| Writes | Tool annotations hint at side effects | Policy `confirm` is enforced before exec |

MCP-only hosts use the [single-tool export](/docs/hosts/mcp), `hcpx mcp`. It
runs every call through the runtime but installs no pack, because it only
forwards to a runtime that already has packs installed and activated. Native
adopt also keeps Policy, Binding, Sense and Skill visible to the host, so it can
show approvals and teaching correctly.

## Context optimization

Ten activated packs with ten commands and a one-screen Skill each are more text
than an agent should see every turn, so the generated context loads them in
layers.

```text
┌──────────────────────────────────────────────┐
│ STABLE PREFIX (cache-friendly, rarely changes)│
│  · doctrine: activate packs, never hand-wire  │
│  · pack / command index (executable only)     │
│  · Skill leads (~480 chars each)              │
│  · "call hcp__read_skill for full teaching"   │
├──────────────────────────────────────────────┤
│ TOOLS                                         │
│  · hcp__read_skill                            │
│  · <pack>__<verb> … grant-filtered            │
│  · write commands: needsApproval              │
├──────────────────────────────────────────────┤
│ CONVERSATION (compacted by prepareStep)       │
└──────────────────────────────────────────────┘
```

The prefix holds only what changes with the activated set, the pack index and
the Skill leads, so prompt caches stay warm across turns; your own
`instructions` follow it. Each lead is the first `skillLeadChars` characters of
the pack's `HARNESS.md` (default 480) and often the only part the agent sees
before it acts, which is why the [Skill file guide](/docs/concepts/skill#skill-file)
insists it stand alone. The always-present `hcp__read_skill` tool returns the
full Skill for a `pack_id` (the hot-path table, examples and anti-patterns),
which is progressive disclosure in the Agent Skills sense with no per-pack code
in the host.

Only commands that are `executable` for the current grants become tools
(`executableOnly: true` by default), so the agent never wastes steps on tools it
would be denied. Every `operation: write` command carries `needsApproval: true`,
which the host renders while the runtime still enforces `confirm`. Between
tool-loop steps, `prepareStep` shortens old tool outputs such as long `list`
results and verbose `doctor` reports, keeps `hcp__read_skill` results intact,
and can narrow `activeTools` to the packs the conversation is using.

```ts
type ContextOptimizationOptions = {
  skillLeadChars?: number        // default 480
  // further knobs: compaction thresholds, activeTools narrowing
}
```

The whole bundle comes from `buildHcpContextBundle(hcp, options)`, which is also
what `hcpx generate` inspects and `hcpx adopt` writes for Cursor and OpenCode. A
hand-rolled context kit drifts from the packs, so regenerate it instead.

```bash
hcpx generate --json
```

Source: https://harnesscontextprotocol.io/docs/hosts

---

# Coding agents

> The adopt command, the files and the quirks for Claude Code, Cursor, Codex, OpenCode and Grok Bot, so every activated pack reaches your agent.

A coding agent with a terminal adopts HCP in one step: `hcpx adopt <host>
--write` puts the protocol Skill, the `hcpx` binary and one tool named `hcp`
where that host looks for them. The agent then reaches every activated pack
through that tool or its shell, so new packs never touch the host's config. The
sections below give each host's command and files, and what sets it apart.

## What every host shares

Activate packs, then run your host's adopt target. Add `--json` and leave out
`--write` to preview the files first.

```bash
hcpx activate transit ledger
hcpx adopt <host> --write
```

Every target writes `.hcp/plugin.json`, the
[agent plugin](/docs/hosts#the-agent-plugin) manifest the other files come
from, and `.hcp/surface.json`, the catalog, which adds no tools. Claude Code,
Cursor, Codex and Grok Bot also get one MCP server, `hcp`, that wraps the same
bash (`hcpx mcp`) and starts the CLI by
[the name that works on your machine](/docs/quickstart#one-binary-two-names);
OpenCode gets one plugin tool instead. The tool and the shell reach the same
runtime with the same Policy, Binding and sandbox.

The protocol Skill teaches lint, install and exec, and points the agent at each
pack's own teaching with `hcpx skill <id>`. A session looks like this:

```bash
hcpx list --json
hcpx skill ledger --json
hcpx exec transit plan --json -- from=Central to=Harbour
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit
# { "ok": false, "error": "confirm required" }
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit confirm=true
```

The runtime enforces Policy before the adapter runs, and the Skill tells the
agent to ask you and retry with `confirm=true` only after you agree. The
runtime's hub mints credentials at exec time, so no token appears in the
conversation, the Skill or the MCP config. Claude Code and Codex also set
`HCP_AGENT=1` in the agent's shell, so the human-only access commands refuse
there ([Access](/docs/concepts/access#keeping-agents-out-of-access)).

## Claude Code

```bash
hcpx adopt claude --write
```

| File | What Claude Code does with it |
|------|-------------------------------|
| `.mcp.json` | Registers one MCP server, `hcp`, with one tool named `hcp`, and asks you to approve it on the next start. It carries `HCP_HOME` only if you set it, so the file works for every teammate who clones the repository |
| `.claude/skills/hcp/SKILL.md` | The protocol Skill: how to list, read, and exec packs, and when a write needs confirmation |
| `.claude/settings.json` | Sets `HCP_AGENT=1` for Claude's shell and adds deny rules for the access commands (`approve`, `reject`, `mode`, `allow`, `ask`, `deny`, `rules rm`, under both `hcp` and `hcpx`) and for edits to the HCP store. Existing settings are kept |

To register the server by hand instead of through `.mcp.json`:

```bash
claude mcp add hcp -- hcpx mcp
```

## Cursor

Cursor discovers the Skill in `.cursor/skills/hcp/` with no HCP-specific code.

```bash
hcpx adopt cursor --write
```

| Path | Content |
|------|---------|
| `.cursor/hcp.json` | Session pointer: `{ enabled, home, activated, sandbox, cli }` |
| `.cursor/skills/hcp/SKILL.md` | Protocol Skill: lint, install, exec |
| `.cursor/mcp.json` | One server, `hcp`: `command` is the CLI's name on this machine, `args: ["mcp"]` |
| `.cursor/rules/hcp.mdc` | Always-on: prefer `hcpx exec`, never hand-register tools |

```json title=".cursor/hcp.json"
{
  "hcp": {
    "enabled": true,
    "home": "/home/you/.hcp",
    "activated": ["transit", "ledger"],
    "sandbox": "sandbox",
    "cli": "hcpx"
  }
}
```

## Codex

```bash
hcpx adopt codex --write
```

This writes the protocol Skill to `.hcp/skills/hcp/SKILL.md`, adds one HCP
section to `AGENTS.md` (once, however often you run it) and prints the block for
your user config. The CLI never edits files outside the repository, so you add
the block yourself:

```toml title="~/.codex/config.toml"
[mcp_servers.hcp]
command = "hcpx"
args = ["mcp"]

[mcp_servers.hcp.env]
HCP_HOME = "/home/you/.hcp"

# Mark shell commands Codex runs, so hcpx refuses the human-only access commands.
[shell_environment_policy.set]
HCP_AGENT = "1"
```

`HCP_HOME` is your store. Because the runtime checks `confirm` before the adapter
runs, the gate holds in every Codex approval mode.

## OpenCode

OpenCode loads HCP as one plugin tool named `hcp` that runs the CLI without a
shell, so adding a pack never adds a tool file.

```bash
hcpx adopt opencode --write
```

| Path | Content |
|------|---------|
| `opencode.json` → `hcp` | `{ enabled, home, activated, sandbox, cli }` |
| `opencode.json` → `plugin` | `@harnesscontextprotocol/plugin-opencode` |
| `.opencode/skills/hcp/SKILL.md` | Protocol Skill |

```json title="opencode.json"
{
  "hcp": {
    "enabled": true,
    "home": "~/.hcp",
    "activated": ["transit", "ledger"],
    "sandbox": "sandbox",
    "cli": "hcpx"
  },
  "plugin": ["@harnesscontextprotocol/plugin-opencode"]
}
```

The plugin is a host shim over `wrapHcpArgv`. Its one tool takes `args` or
`argv` as you would type them after the binary (`exec transit plan -- from=A
to=B`) and starts the command in `hcp.cli` (`HCP_CLI` overrides it) directly,
never through a shell or as a nested `mcp`. It writes no
`.opencode/tools/<pack>__<verb>.ts` files.

To drive an OpenCode session from your own code, the [AI SDK](/docs/hosts/ai-sdk)
host's `HarnessAgent` takes `runtime: 'opencode'`. It maps grant-filtered
commands to tools in process, separate from this plugin:

```ts
import { Hcp, HarnessAgent } from '@harnesscontextprotocol/sdk'

const hcp = await Hcp.create({ packDirs, sandbox: true })
await hcp.activate(['transit', 'ledger'])

const agent = await HarnessAgent.create({
  hcp,
  runtime: 'opencode',
  cwd: process.cwd(),
  adoptOpenCode: true,
})
```

## Grok Bot

Grok Bot has a terminal on a persistent Linux computer and Grok Build has a bash
tool, so the agent runs `hcpx` like any other CLI, with MCP as the optional
fallback for hosts that only attach connectors.

```text
Grok Bot (cloud computer) / Grok Build (local TUI)
   │  terminal / bash   ← primary
   │  MCP connector     ← optional, one server named hcp
   ▼
hcpx search | install | skill | exec
   ▼
runtime (Policy · Binding · sandbox)
```

```bash
hcpx adopt grok --write      # alias: grokbot
```

| Path | Role |
|------|------|
| `.grok/hcp.json` | Session pointer (`home`, `activated`, `cli`) |
| `.grok/config.toml` | `[mcp_servers.hcp]` + allow `Bash(hcpx *)`, `Bash(hcp *)`, `Bash(bun *hcp*)`, `MCPTool(hcp__*)` (marked `BEGIN HCP` / `END HCP`) |
| `.grok/rules/hcp.md` | Prefer the terminal; MCP is one tool |
| `.grok/skills/hcp/SKILL.md` | How to drive the CLI from bash |
| `.mcp.json` | Same stdio server wrapping the CLI |

A project `.grok/config.toml` loads only MCP, plugins and permission, so other
Grok settings stay in the user config. Re-running adopt rewrites only the marked
block, which is how you change the MCP env or home.

Install the CLI on the Bot's computer, its VM, and put it on `PATH` like any
other CLI the Bot uses. The terminal and the MCP server both run there, so a Bot
working on its cloud computer never finds the CLI on your laptop. A first
session there runs:

```bash
hcpx doctor --json
hcpx search transit --json
hcpx install npm:@harnesscontextprotocol/pack-transit --json
hcpx activate transit --json
hcpx exec transit plan --json -- from=A to=B
```

Prefer bash, which keeps Policy in the runtime and the tool list the same size
as the catalog changes. Use MCP when the host will not let bash run the CLI (a
team connector allowlist, a headless attach), and point it at:

```toml
[mcp_servers.hcp]
command = "hcpx"
args = ["mcp"]
enabled = true
startup_timeout_sec = 30
env = { HCP_HOME = "~/.hcp" }
```

`tools/list` is still `[hcp]` ([MCP export](/docs/hosts/mcp)). Grok Bot also
follows Cursor MCP policy, with `.mcp.json` as the shared fragment.

## Adding packs later and what to avoid

```bash
hcpx install ./packs/weather --json
hcpx activate weather --json
```

The agent sees the new pack on its next call, through the `hcp` tool or `hcpx`
in its shell, while the MCP config, the tool list and the permission prompts
stay the same size however many packs you activate. A pack's teaching lives in
its `HARNESS.md`, which agents read with `hcpx skill <id>`, so re-run
`hcpx adopt <host> --write` only to refresh the protocol Skill and the session
or MCP pointer.

Avoid these in every host:

- Registering an MCP server or tool per pack or command. One `hcp` server
  reaches all of them.
- Treating MCP server install as pack install
  ([why](/docs/hosts#why-mcp-is-not-the-install-path)).
- Putting a token in the MCP config, `.cursor/hcp.json`, `.grok/config.toml`,
  `CLAUDE.md`, a rule, a Skill or pack files. Binding mints it.
- Exporting tokens into the agent's environment for packs to use. Binding never
  falls back to the environment.
- Copying pack setup steps into `AGENTS.md` or generating a Skill file per pack.
  `hcpx skill <id>` is the source.
- Pre-approving writes with `confirm=true` in a standing instruction, which
  defeats a gate that exists so you agree to each write.

Source: https://harnesscontextprotocol.io/docs/hosts/coding-agents

---

# MCP export

> A host that can only add an MCP server gets one tool, `hcp`, that runs `hcpx` commands in process while the pack stays the unit you install.

For a host that can only add an MCP server, the reference export is one tool
named `hcp` that wraps the CLI, instead of one tool per command. Search, install,
Skill, availability and exec all go through it, so the host takes new packs
without a config change, which a host given one tool per command never could.
Run `hcpx adopt mcp --write` and point the host at `hcpx mcp`.

## How a call runs

The tool's argument is the same argv you would type after `hcpx`. The server is
no shell, so it rejects pipes, substitution and chaining, and it runs each call
inside its own process the way `hcpx` runs it in a terminal: one runtime load
and no new CLI process. Every call still goes through Policy, Binding and the
sandbox, and the access commands refuse the agent there as they do in its
shell.

```text
Host (MCP only)
   │  tools/list → [hcp]
   │  tools/call { args: "exec transit plan -- from=A to=B" }
   ▼
hcpx mcp  (stdio)  →  run that argv in this process  (no shell, no new process)
   ▼
exec transit plan --json -- from=A to=B
   ▼
runtime (§6.3)  →  spawns the pack's CLI
```

## Run it

```bash
bun add -g @harnesscontextprotocol/cli   # GitHub Packages preview: /docs/quickstart#install-the-cli
hcpx adopt mcp --write
hcpx mcp                                  # stdio server
```

Adopt writes `.hcp/mcp.json`:

```json
{
  "mcpServers": {
    "hcp": {
      "command": "hcpx",
      "args": ["mcp"],
      "env": { "HCP_HOME": "~/.hcp" }
    }
  }
}
```

`command` is [the name that starts the CLI on your machine](/docs/quickstart#one-binary-two-names).
Point the host at that command, then call the one tool named `hcp`:

| `args` (string) | `argv` (array) | What happens |
|-----------------|----------------|----------------|
| `search transit` | `["search","transit"]` | Catalog search |
| `skill transit` | `["skill","transit"]` | Skill text |
| `availability transit` | `["availability","transit"]` | Four-flag availability |
| `exec transit plan -- from=A to=B` | `["exec","transit","plan","--","from=A","to=B"]` | Execute through the runtime |
| `install npm:@scope/pack` | `["install","npm:@scope/pack"]` | Install a pack package |

Prefer `argv` when the host can pass an array, and use `args`, the bash-like
form, when it only sends a string. Neither is a shell, and both reject
metacharacters.

For reading, `resources/list` exposes `hcp://catalog`, `hcp://help` and
`hcp://skill/<pack_id>` for installed packs, with no extra tools. Actions still
go through the single `hcp` tool, and writes still need `confirm=true` when
Policy says so, so MCP gives the model no way around the gate.

## What the export does not do

Installing the MCP server installs no pack, because `hcpx install` still copies
the three files. When the host can load HCP directly, native adopt
([coding agents](/docs/hosts/coding-agents)) is the better path, for the reasons
in [Adopt in a host](/docs/hosts#why-mcp-is-not-the-install-path). The normative
rules are in
[§12.5](/docs/specification/projections#125-export-to-other-protocols).

## Related

- [Agent plugin](/docs/hosts#the-agent-plugin)
- [CLI](/docs/reference/cli)
- [Catalog](/docs/packs/share#catalog)

Source: https://harnesscontextprotocol.io/docs/hosts/mcp

---

# AI SDK

> `HarnessAgent` builds a Vercel AI SDK ToolLoopAgent from activated packs, with grant-filtered tools, approvals on writes and Skills loaded on demand.

For a host built on the Vercel AI SDK or any TypeScript tool loop, the reference
SDK's `HarnessAgent` builds the agent from the activated packs, so its tools,
approvals and instructions all come from what the packs declare. Create it with
a runtime and a model, then render its approvals in your host.

## Create

```ts
import { Hcp, HarnessAgent } from '@harnesscontextprotocol/sdk'

const hcp = await Hcp.create({
  packDirs: ['./packs/transit', './packs/ledger'],
  sandbox: true,
  grants,
})
await hcp.activate(['transit', 'ledger'])

const agent = await HarnessAgent.create({
  hcp,
  harness: 'my-product-v1',     // teaching harness id
  runtime: 'ai-sdk',
  model,
  maxSteps: 12,
  context: { skillLeadChars: 480 },
})

const run = await agent.send('List the ledger and plan a route from A to B')
for await (const ev of run.stream()) {
  if (ev.type === 'tool') console.log(ev.name, ev.input)
}
await run.wait()
```

## Options

<TypeTable
  type={{
    hcp: { description: 'The runtime. Packs must be activated.', type: 'Hcp', required: true },
    runtime: { description: "'ai-sdk' builds a ToolLoopAgent; 'opencode' drives an OpenCode session over the same packs.", type: "'ai-sdk' | 'opencode'", default: 'ai-sdk' },
    model: { description: 'AI SDK language model (ai-sdk runtime).', type: 'LanguageModel' },
    harness: { description: 'Identifier for the teaching harness. It selects instructions, not capabilities.', type: 'string' },
    instructions: { description: 'Extra system instructions appended after the generated stable prefix.', type: 'string' },
    maxSteps: { description: 'Tool-loop step cap.', type: 'number' },
    cwd: { description: 'Working directory (opencode runtime).', type: 'string' },
    adoptOpenCode: { description: 'Write native OpenCode intake before the run.', type: 'boolean', default: 'true' },
    context: { description: 'Context optimisation knobs; see below.', type: 'ContextOptimizationOptions' },
  }}
/>

## What gets generated

| From | To |
|------|----|
| Activated packs + Policy + grants | Grant-filtered tool map: one tool per executable command, named `hcpToolName(pack_id, verb_key)` |
| `HARNESS.md` leads | A stable instruction prefix (prompt-cache friendly) |
| Full Skills | `hcp__read_skill` tool for on-demand loading |
| Commands with `operation: write` | `toolApproval` with `needsApproval: true` |
| Long tool results | `prepareStep` compaction between steps |

[Context optimization](/docs/hosts#context-optimization) explains each layer and
the `context` knobs. `buildHcpContextBundle(hcp, options)` builds the bundle, and
you can call it directly to assemble your own `ToolLoopAgent`:

```ts
import { buildHcpContextBundle } from '@harnesscontextprotocol/sdk'

const bundle = await buildHcpContextBundle(hcp, { skillLeadChars: 480 })
// bundle.tools · bundle.instructions · bundle.toolApproval · bundle.prepareStep · bundle.toolOrder
```

## Approvals

Every write command arrives with `needsApproval: true`. Your host shows the
approval, and when the user agrees the tool call proceeds with `confirm: true`
in its args. The runtime still enforces the gate, so the approval in the host
and `confirm` in the envelope are two views of one rule.

## Events

```ts
type HarnessAgentEvent =
  | { type: 'text'; text: string }
  | { type: 'tool'; name: string; input: unknown; output?: unknown }
  | { type: 'error'; error: string }
  | { type: 'context'; stablePrefixChars: number; tools: number; progressiveSkills: boolean }
  | { type: 'done' }
```

## Why not hand-registered tools

You can call `hcp.exec` from any tool you write yourself, for example with
`generateText`, but you lose everything the pack declared: grant filtering,
approvals from Policy, Skill leads and availability. `HarnessAgent` keeps the
pack as the integration identity, so no separate tool list can drift from it.

## Related

- [SDK reference](/docs/reference/sdk)
- [Adopt in a host](/docs/hosts)

Source: https://harnesscontextprotocol.io/docs/hosts/ai-sdk

---

# Specification

> Harness Context Protocol HCP/1.1, the normative requirements for packs, runtimes, and hosts. Status, conformance language, and table of contents.

This is the Harness Context Protocol specification, HCP/1.1, in the draft of
17 September 2026. Its text lives in the `spec/` directory of the
[protocol repository](https://github.com/harnesscontextprotocol/hcp-protocol),
and the Specification pages on harnesscontextprotocol.io are generated from it.
Changes land there by [RFC](/docs/community/governance#rfc-process).

## Status and version

This document specifies HCP/1.1. It consists of the HCP/1 core plus the
accepted extensions of RFC 0001 (wrap hardening) and RFC 0003 (wrap exec
plane), with the errata of RFC 0008. Packs, runtimes, and hosts claiming conformance to HCP/1.1 MUST
implement or follow the requirements in the sections below.

HCP/1 packs conform to HCP/1.1 without change. Proposed and planned changes are
tracked on the [Roadmap](/docs/community/roadmap) and are not part of this
specification.

## Conformance language

The key words MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT,
RECOMMENDED, MAY, and OPTIONAL in this specification are to be interpreted as
described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and
[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174) when, and only when, they
appear in all capitals.

Text marked *informative* explains intent and does not create requirements.

## Three conformance targets

| Target | Claims conformance by | Checklist |
|--------|----------------------|-----------|
| Pack | Passing lint on a conforming runtime | [Pack conformance](/docs/specification/conformance#142-pack-conformance) |
| Runtime | Passing the conformance vectors and satisfying the runtime checklist | [Runtime conformance](/docs/specification/conformance#143-runtime-conformance) |
| Host | Loading generated surfaces and satisfying the host checklist | [Host conformance](/docs/specification/conformance#144-host-conformance) |

## Table of contents

| § | Section | Defines |
|---|---------|---------|
| 1 | [Terminology](/docs/specification/terminology) | The objects and their exact meanings |
| 2 | [Pack model](/docs/specification/pack-model) | Directory, three files, identity, planes, invariants |
| 3 | [Manifest](/docs/specification/manifest) | `surface.json` schema and validation |
| 4 | [Policy](/docs/specification/policy) | `policy.json` schema, tokens, coverage, confirm |
| 5 | [Skill](/docs/specification/skill) | `HARNESS.md` requirements |
| 6 | [Execute](/docs/specification/execute) | Envelope, pipeline, wrap rendering, result |
| 7 | [Sense](/docs/specification/sense) | Triggers, isolation, activation |
| 8 | [Binding](/docs/specification/binding) | Modes, hubs, families, mint, health |
| 9 | [Authorization](/docs/specification/authorization) | Principal, scope, capabilities, evaluation order, audit |
| 10 | [Sandbox](/docs/specification/sandbox) | Default posture and native-local |
| 11 | [Lifecycle](/docs/specification/lifecycle) | States, store, availability |
| 12 | [Projections](/docs/specification/projections) | CLI face, host surfaces, MCP export naming |
| 13 | [Versioning](/docs/specification/versioning) | Protocol affinity, compatibility, deprecation |
| 14 | [Conformance](/docs/specification/conformance) | Checklists for packs, runtimes, hosts |

## Design goals (informative)

1. A capability installs into any harness as one portable unit.
2. Authorisation, credentials, sandboxing, and outbound events are part of the
   install layer; no host adds them separately.
3. The same command serves a human, an agent, an API, and a UI under one Policy.
4. Packs are small to write and cannot hold secrets.
5. Runtimes may be proprietary; the format and rules are open.
6. Transports are projections. A pack's identity does not depend on its
   transport.

## Non-goals

See [Non-goals](/docs/introduction/design-principles#non-goals).

## License

The specification is licensed under Apache-2.0. The reference SDK and CLI are
MIT.

Source: https://harnesscontextprotocol.io/docs/specification

---

# §1 Terminology

> Normative definitions of every object in the protocol, and what each is explicitly not.

The following terms have the meanings given here throughout this specification.

## 1.1 Participants

**Harness**: The system driving an agent. In HCP the harness is the runtime that
installs packs. An MCP server, an agent's instruction file, and a dashboard are
not harnesses.

**Runtime**: The component that loads packs, evaluates grants, mints Binding,
executes Adapters, routes Sense, and reports availability. MAY be proprietary. The
runtime is an implementation and is distinct from the protocol.

**Host**: An agent product that adopts HCP and presents pack commands to an agent
loop. Examples: an IDE assistant, a coding agent, a tool-loop library. A host
invokes the runtime and does not replace it.

**Invoker**: The party running a command: human, agent, API, or UI. An Invoker
carries a Principal. An Invoker MUST NOT redefine Policy or Binding.

**Principal**: The acting identity on an exec. A runtime concept. Kinds
(informative): `human`, `service`, `agent`, `mothership`. A Principal is never a
pack field.

## 1.2 The unit

**Harness Pack** (**pack**): A directory containing `surface.json`, `policy.json`,
and `HARNESS.md`, identified by one `pack_id`. The install unit. A pack is not a
connector, a UI surface, an MCP server, or an agent teaching pack.

**Manifest**: `surface.json`. Declares identity, adapter, Binding mode, commands,
and optional triggers. The filename is retained for compatibility and does not
refer to a UI surface.

**Policy**: `policy.json`. One entry per command and per trigger: `operation`,
`privacy.land`, and optional `confirm`.

**Skill**: `HARNESS.md`. The pack's teaching document. Copied to a `SKILL.md`
on activate.

**Pack id** (`pack_id`): The pack's stable identifier. Equal to manifest `id` and
Policy `surface_id`. `surface_id` is a legacy alias of `pack_id` on the wire.

**Plane**: `runtime` (end-user capability, default) or `control` (operator
capability). A control-plane pack is a distinct pack.

## 1.3 The loops

**Execute**: The inbound loop: an Invoker runs a command. Announcements that the
world changed belong to Sense.

**Command**: A closed, named operation the pack declares. Identified by
`verb_key`. Manifest map `verbs` and wire field `verb_key` are retained names and
mean *command*. A command is not a verb ontology, a PATH wrap, or a mega-command
`run`.

**Twin face**: The property that a command's CLI form and JSON envelope are the
same command under the same Policy, Binding, and Adapter.

**Sense**: The outbound loop: the pack announces a world change. Pull observation,
polling, and uptime monitoring are not Sense.

**Trigger** (**stimulus**): One Sense declaration, identified by a stimulus ref.
A trigger is not a command, a job table, or an inbox product.

**Sink**: Where a runtime records delivered triggers. A runtime concept.

## 1.4 Rules and credentials

**Operation**: Policy side-effect class: `read` or `write`.

**Land**: Policy privacy scope: `personal` (actor-private), `brain` (shared
project context), or `local` (a local workspace directory). Land is not a tenant
id.

**Confirm**: Policy gate on a write. The runtime MUST refuse the command unless
the envelope carries explicit confirmation.

**Binding**: The credential mode a pack requires and the path by which a runtime
mints it. Binding is neither a secret in the pack nor a Principal.

**Binding mode**: `public`, `user`, `local`, `mothership`, `mock`.

**Hub**: The runtime component that mints credentials. Named in the manifest as
`keyring`, `local-credential`, `public`, or `none`. Its implementation is not an
HCP type.

**Family**: A manifest field grouping packs that share one consent.

**Capability**: A runtime grant axis: `pack.install`, `pack.connect`,
`verb.exec.read`, `verb.exec.write`, `sense.enable`.

**Tenant** / **Project**: Runtime scope. Tenant comes from the Principal's
session; Project is required when land is `brain`.

## 1.5 Execution

**Adapter**: How a command reaches the world: `wrap`, `composite`, `openapi`, or
`sense_only`. An implementation detail beneath the pack's identity.

**Wrap**: An Adapter that spawns an existing CLI with a rendered argv.

**Pass-through**: An HCP/1.1 wrap bootstrap declaring `allowedSubcommands` and
`maxArgv`. Non-conforming as a destination.

**Sandbox**: The default execution posture: bin allow-list, cwd jail, no ambient
credentials. **Native-local** is the explicit opt-out.

**Envelope**: The JSON exec request `{ pack_id, verb_key, args }`.

**Result envelope**: `{ ok, pack_id, verb_key, result?, error? }`.

## 1.6 Lifecycle

**Install**: Placing a pack in the runtime store.

**Activate**: Placing an installed pack on a harness session.

**Connect**: Minting Binding for an activated pack.

**Availability**: The report of `installed`, `activated`, `connected`, and
per-command `executable`.

**Store**: The runtime's pack storage. In the reference runtime, `$HCP_HOME`.

## 1.7 Projections

**Projection**: Any surface generated from a pack: CLI face, JSON face, Skill
copy, host tool catalog, MCP export. Projections are derived; the pack is the
source of record.

**Four faces**: CLI, JSON, Skill, and optional MCP. They MUST share `verb_key`.

## 1.8 Terms that are not HCP types

*Informative.* The following appear in products built on HCP and are explicitly
outside the protocol: Connector, tenant id in the pack, inbox ledger, object-sync
job, catalog SQL schema, credential hub table names, UI widget catalog, role
table, IdP API.

Source: https://harnesscontextprotocol.io/docs/specification/terminology

---

# §2 Pack model

> Normative requirements for the pack directory, its three files, identity, planes, and the invariants every pack satisfies.

## 2.1 Package

1. A Harness Pack is a directory (the **pack root**).
2. A pack MUST contain these three files at the pack root: `surface.json`,
   `policy.json`, `HARNESS.md`. Filenames are exact and case-sensitive.
3. A pack MAY contain additional files. Runtimes MUST NOT assign protocol
   semantics to files other than the three above.
4. Runtimes MUST reject a pack root lacking any of the three files.
5. Paths declared in the manifest as relative (`wrap.bin` beginning with `.`,
   `openapi.path`) MUST be resolved against the pack root.

*Informative.* Path containment (rejecting relative paths that resolve outside
the pack root) is proposed for HCP/1.2. Runtimes SHOULD enforce it today.

## 2.2 Identity

1. A pack's identity is its `pack_id`, given by manifest field `id`.
2. `pack_id` MUST be 1 to 128 characters, lower-case, and stable across the pack's
   lifetime.
3. Policy `surface_id` MUST equal `pack_id`. Runtimes MUST reject a pack where
   they differ.
4. If the manifest carries protocol metadata (`hcp`, `pack_id`, …), its
   `pack_id` MUST equal `id`.
5. One `pack_id` SHOULD correspond to one maintained source repository.

## 2.3 Commands

1. A pack declares its commands in the manifest `verbs` map. Each key is a
   `verb_key`.
2. `verb_key` MUST be non-empty, lower-case, and MAY contain `.` as a family
   separator. It MUST NOT contain whitespace or `/`.
3. A pack SHOULD declare between 5 and 12 commands. A pack MAY declare fewer.
   A pack declaring more than 12 SHOULD be split.
4. A pack MUST NOT expose a generic pass-through command (a command whose
   arguments are forwarded as an arbitrary argv). Such a command is
   non-conforming.
5. Every `verb_key` MUST have a Policy entry (§4.4).

## 2.4 Triggers

1. A pack MAY declare triggers in the manifest as `stimulusTriggers[]`, each
   with a `ref`.
2. Every declared `ref` MUST have a Policy `stimuli` entry (§4.4).
3. A pack MUST NOT declare a trigger for a condition it can only observe by
   polling.

## 2.5 Planes

1. `plane` is `runtime` (default) or `control`.
2. A `control` pack MUST use Binding mode `mothership` (§8.2). Runtimes MUST
   fail lint otherwise.
3. Capabilities requiring different Binding modes MUST be separate packs.
4. Runtimes MUST NOT activate a `control` pack for a Principal lacking an
   explicit operator or elevation grant.

## 2.6 Protocol affinity

1. A pack targets a protocol version with metadata field `hcp`. For HCP/1.x the
   value MUST be `1`.
2. Runtimes MUST reject a pack whose `hcp` value they do not support and SHOULD
   report the unsupported version.
3. When the manifest does not carry explicit metadata, runtimes MUST derive it
   from the manifest fields (`id` → `pack_id`, `kind` → `adapter_kind`, …) and
   validate the derived object.

## 2.7 Invariants

A conforming pack satisfies all of the following.

| # | Invariant |
|---|-----------|
| P1 | Declares; never executes |
| P2 | Contains no secrets, tokens, or credential material |
| P3 | Contains no user allow-lists, roles, or tenant identifiers |
| P4 | Passes lint (§14.1) on a conforming runtime |
| P5 | Declares a closed, named command set |
| P6 | Has one Binding mode |
| P7 | One Policy file covers every command and every trigger |
| P8 | Adapter is replaceable without changing `verb_key` or Policy |

## 2.8 What is not part of the pack

*Informative.* Catalog metadata, runtime grants, Principal mappings, sink
configuration, UI catalogs, and delivery transports are runtime concerns. A pack
that embeds them is not portable and SHOULD be rejected in review even if it
passes lint.

Source: https://harnesscontextprotocol.io/docs/specification/pack-model

---

# §3 Manifest

> Normative schema for surface.json (identity, adapter, Binding, commands, triggers, protocol metadata) and the validation a runtime performs.

The manifest is `surface.json` at the pack root. It MUST be a JSON object.

## 3.1 Top-level fields

| Field | Type | Req | Constraint |
|-------|------|-----|------------|
| `id` | string | MUST | `pack_id`, 1 to 128 chars, lower-case |
| `name` | string | MAY | Display name |
| `cliBin` | string | SHOULD | The CLI binary the Skill teaches |
| `kind` | enum | SHOULD | `wrap` \| `composite` \| `openapi` \| `sense_only`. Alias `adapter_kind`. Default `composite` |
| `plane` | enum | MAY | `runtime` \| `control`. Default `runtime` |
| `transports` | array | MAY | Subset of `cli`, `json_exec`, `mcp`; at most 4 entries |
| `binding` | object | SHOULD | §3.3 |
| `wrap` | object | conditional | MUST when `kind` is `wrap` |
| `openapi` | object | conditional | MUST when `kind` is `openapi` |
| `pass_through` | object | MAY | §3.5; wrap only |
| `verbs` | object | MUST | Map `verb_key` → command (§3.4). MAY be empty only when `kind` is `sense_only` |
| `stimulusTriggers` | array | MAY | `{ ref: string }[]` |
| `skill_url` | string | MAY | URL to a hosted copy of the Skill |
| `harness` | string | MAY | Inline Skill text (deprecated in favour of `HARNESS.md`) |

Runtimes MUST ignore unknown top-level fields in `surface.json` for forward
compatibility, and SHOULD report them.

## 3.2 Adapter fields

### `wrap`

| Field | Type | Req | Constraint |
|-------|------|-----|------------|
| `bin` | string | MUST | 1 to 128 chars. Relative paths (`.`-prefixed) resolve against the pack root |
| `argv_template` | string[] | MAY | ≤ 64 entries. `{{verb}}` and `{{name}}` placeholders. Default `["{{verb}}"]` |
| `agent_env` | object | MAY | String → string. Merged into the child environment |

### `openapi`

| Field | Type | Constraint |
|-------|------|------------|
| `url` | string (URL) | One of `url` or `path` MUST be present |
| `path` | string | Relative to the pack root |

## 3.3 Binding fields

| Field | Type | Constraint |
|-------|------|------------|
| `mode` | enum | `user` \| `mothership` \| `local` \| `public` \| `mock` |
| `hub` | enum | `keyring` \| `local-credential` \| `public` \| `none` |
| `family` | string | 1 to 128 chars |
| `connect_provider_id` | string | 1 to 128 chars |

Unknown Binding fields MUST fail validation.

## 3.4 Command entry

Each value of `verbs` is an object.

| Field | Type | Req | Constraint |
|-------|------|-----|------------|
| `description` | string | SHOULD | One sentence |
| `cli` | string[] | SHOULD for `wrap` | First token is the bin. Placeholders `{{name}}` and `--flag={{name}}` |
| `args` | object | MAY | JSON Schema for the command's arguments |
| `policy` | enum | MAY | `read` \| `write`. Informative hint; `policy.json` is authoritative |

## 3.5 Pass-through (HCP/1.1)

| Field | Type | Req |
|-------|------|-----|
| `allowedSubcommands` | string[] | MUST, ≥ 1 |
| `maxArgv` | integer | MUST, > 0 |

1. A `wrap` pack MUST declare either a non-empty `verbs` map or `pass_through`.
2. A pack relying on `pass_through` MAY be installed as a bootstrap but is not a
   conforming HCP/1.1 pack until its hot path is promoted to named commands.

## 3.6 Protocol metadata

A manifest MAY carry, or a runtime MUST derive, the following object. It is
validated strictly: unknown fields MUST fail.

```ts
type HarnessPackMeta = {
  hcp: 1
  pack_id: string                         // = id
  adapter_kind: 'composite' | 'wrap' | 'openapi' | 'sense_only'
  binding?: HarnessBinding
  wrap?: HarnessWrapTarget
  openapi?: HarnessOpenApiRef
  pass_through?: HarnessPassThrough
  plane?: 'runtime' | 'control'
  transports?: ('cli' | 'json_exec' | 'mcp')[]
  skill_url?: string
}
```

## 3.7 Validation

A runtime MUST reject the manifest when any of the following holds.

| Condition | Issue |
|-----------|-------|
| `id` missing or ≠ Policy `surface_id` | `invalid_meta` / `missing_verb` (id) |
| `kind: wrap` without `wrap.bin` | `binding: wrap adapter requires wrap.bin` |
| `kind: wrap` with empty `verbs` and no `pass_through` | `wrap: wrap requires named commands or pass_through` |
| `plane: control` with `binding.mode` ≠ `mothership` | `binding: control plane packs default Binding mothership` |
| `kind: openapi` without `openapi` | `invalid_meta: openapi adapter requires openapi ref` |
| `kind: sense_only` with no `stimulusTriggers` | `invalid_meta: sense_only adapter requires at least one stimulus trigger_ref` |
| Metadata schema violation | `invalid_meta` |

## 3.8 Example (informative)

```json
{
  "id": "ledger",
  "name": "Ledger",
  "kind": "wrap",
  "cliBin": "ledger",
  "plane": "runtime",
  "transports": ["cli", "json_exec"],
  "binding": { "mode": "local", "hub": "local-credential", "family": "ledger" },
  "wrap": { "bin": "../../bins/ledger.mjs", "argv_template": ["{{verb}}"] },
  "verbs": {
    "setup.status": { "description": "Idempotent installer status.", "cli": ["ledger", "setup", "status"] },
    "setup.apply":  { "description": "Apply installer.", "cli": ["ledger", "setup", "all", "-y"], "policy": "write" },
    "connect":      { "description": "Connect Binding.", "cli": ["ledger", "connect", "{{provider}}"], "policy": "write" },
    "status":       { "description": "Connect health.", "cli": ["ledger", "status"] },
    "list":         { "description": "Domain read.", "cli": ["ledger", "list"] },
    "transfer":     { "description": "Gated write.", "cli": ["ledger", "transfer", "{{amount}}", "--to", "{{payee}}", "--confirm"], "policy": "write" }
  }
}
```

Source: https://harnesscontextprotocol.io/docs/specification/manifest

---

# §4 Policy

> Normative schema for policy.json (operation, land, confirm), with coverage rules and the semantics runtimes must enforce.

The Policy file is `policy.json` at the pack root. It MUST be a JSON object and
is validated strictly: unknown fields at any level MUST fail parse.

## 4.1 Schema

```ts
type Operation = 'read' | 'write'
type Land      = 'personal' | 'brain' | 'local'

type PolicyEntry = {
  operation: Operation
  privacy: { land: Land }
  confirm?: boolean          // writes only
}

type PolicyFile = {
  surface_id: string         // = pack_id
  version: 1
  verbs:    Record<string, PolicyEntry>
  stimuli:  Record<string, PolicyEntry>   // default {}
}
```

## 4.2 Operation

1. `read` denotes a command with no side effect an Invoker would need to undo.
2. `write` denotes a command that changes state.
3. Validating a prospective write MUST be `read`.
4. A dry-run or preview mode of a write MUST remain `write` and MUST NOT persist
   side effects. If persistence cannot be guaranteed the pack MUST NOT offer the
   flag.
5. A command that establishes or refreshes Binding MUST be `write` and is
   additionally gated by `pack.connect` (§9.4).
6. A trigger's `operation` MUST be `read`. Sense cannot write.

## 4.3 Land

| Token | Meaning | Runtime obligation |
|-------|---------|--------------------|
| `personal` | Actor-private data or effect | Restrict to the Principal's own scope |
| `brain` | Shared project context | Require project membership; `project_id` MUST be present |
| `local` | A local workspace directory | Bind to the sandbox working directory. Not a tenant |

1. Runtimes MUST reject a land token other than these three.
2. Runtimes MUST NOT let a command argument select the tenant. Tenant comes from
   the Principal's session (§9.2).
3. Products MAY map `brain` to their own shared-context object. They MUST NOT
   introduce additional land tokens without an RFC.

## 4.4 Coverage

1. Every `verb_key` in the manifest MUST appear in `verbs`.
2. Every key in `verbs` MUST be a `verb_key` in the manifest.
3. Every `ref` in manifest `stimulusTriggers` MUST appear in `stimuli`.
4. Every key in `stimuli` MUST be a declared `ref`.
5. `surface_id` MUST equal the manifest `id`.

Violations are lint issues of kind `missing_verb`, `extra_verb`,
`missing_stimulus`, `extra_stimulus`, and `write_stimulus` (a trigger whose
entry is not `read`, §4.2.6). Runtimes MUST reject a pack with any of them.

## 4.5 Confirm

1. `confirm` MAY be set only on entries with `operation: write`.
2. When `confirm` is `true`, a runtime MUST refuse the command unless the exec
   envelope's `args` contains `confirm: true` or `apply: true` (string `"true"`
   is accepted on the CLI face).
3. The refusal MUST occur before Binding mint and before any Adapter runs, and
   MUST return `error: "confirm required"`.
4. A pack SHOULD mirror `confirm` on the CLI face with a `--confirm` or `--apply`
   flag in the command's `cli[]`.
5. No Invoker MAY bypass the gate. Host approval UIs present the gate and do
   not replace it.

## 4.6 Grants (informative cross-reference)

Policy states effect class; runtime capabilities state who. The mapping is
normative in §9.4:

```text
operation read          → verb.exec.read
operation write         → verb.exec.write
connect command         → pack.connect
trigger delivery        → sense.enable
```

## 4.7 Example (informative)

```json
{
  "surface_id": "mail",
  "version": 1,
  "verbs": {
    "mail.list":    { "operation": "read",  "privacy": { "land": "personal" } },
    "mail.send":    { "operation": "write", "privacy": { "land": "personal" }, "confirm": true },
    "mail.connect": { "operation": "write", "privacy": { "land": "personal" } },
    "notes.create": { "operation": "write", "privacy": { "land": "brain" } }
  },
  "stimuli": {
    "new_item":         { "operation": "read", "privacy": { "land": "personal" } },
    "binding.degraded": { "operation": "read", "privacy": { "land": "personal" } }
  }
}
```

Source: https://harnesscontextprotocol.io/docs/specification/policy

---

# §5 Skill

> Normative requirements for HARNESS.md: presence, content constraints, the copy on activate, and drift with the manifest.

The Skill is `HARNESS.md` at the pack root.

## 5.1 Presence

1. A pack MUST contain `HARNESS.md`. Runtimes MUST fail lint otherwise
   (`missing_harness`).
2. `HARNESS.md` MUST be UTF-8 Markdown.
3. The manifest field `harness` (inline Skill text) is deprecated. When both are
   present, `HARNESS.md` takes precedence.

## 5.2 Content

1. The Skill MUST NOT contain credential material of any kind, including
   placeholders formatted as credentials.
2. The Skill MUST NOT contain host-absolute filesystem paths as normative steps.
3. The Skill MUST NOT present an MCP tool list as the way to invoke the pack.
4. Every command the Skill teaches MUST be a `verb_key` declared in the manifest.
5. The Skill MUST NOT describe triggers the manifest does not declare.
6. The Skill SHOULD fit on one screen and SHOULD follow this order: title, lead,
   hot-path table, examples, setup (if Binding is not `public`), anti-patterns,
   pointers.
7. The title SHOULD be `# HCP — <Name>`.
8. Examples SHOULD use the CLI face with `--json` and SHOULD show `--confirm` /
   `--apply` on gated writes.

## 5.3 Lead

1. The **lead** is the text between the title and the first `##` heading.
2. Runtimes and hosts MAY truncate the lead for stable context. The reference
   default is 480 characters.
3. Authors SHOULD write the lead so that, truncated, it still states what the
   pack does, whether it needs login, and the first command to run.

## 5.4 Copy on activate

1. On activate, a runtime SHOULD copy `HARNESS.md` to a Skill location hosts
   discover. The reference location is `$HCP_HOME/skills/<pack_id>/SKILL.md`.
2. The copy MUST be byte-identical to `HARNESS.md` or a faithful rendering; a
   runtime MUST NOT inject credentials, host paths, or policy into it.
3. Host adoption (`hcp adopt`) MAY copy the Skill again into a host-specific
   location.

## 5.5 Drift

*Informative.* Skill ↔ manifest ↔ Policy drift is a lint concern under the
four-faces rule (§12.4). Runtimes SHOULD warn when a Skill's hot-path table names
a command the manifest lacks or omits one it declares.

## 5.6 Relationship to Agent Skills

*Informative.* `HARNESS.md` follows the Agent Skills principles of progressive
disclosure, examples, and anti-patterns. It is intentionally scoped to invocation.
When copied to `SKILL.md` it is a valid Agent Skill body; frontmatter, if a host
requires it, is added by the runtime at copy time from manifest fields.

Source: https://harnesscontextprotocol.io/docs/specification/skill

---

# §6 Execute

> Normative definition of the inbound loop: the exec envelope, the fail-closed pipeline, wrap argv rendering, the result envelope, and the twin face.

Execute is the inbound loop: an Invoker runs a command.

## 6.1 Exec envelope

```json
{ "pack_id": "ledger", "verb_key": "transfer", "args": { "amount": 10, "confirm": true } }
```

| Field | Type | Req | Constraint |
|-------|------|-----|------------|
| `pack_id` | string | MUST | Runtimes MUST accept `surface_id` as an alias |
| `verb_key` | string | MUST | A declared command |
| `args` | object | MAY | Default `{}` |

Reserved `args` keys:

| Key | Meaning |
|-----|---------|
| `confirm` | `true` satisfies a `confirm` gate |
| `apply` | Synonym of `confirm` |
| `mode` | `"mock"` requests explicit mock Binding |

## 6.2 Result envelope

```json
{ "ok": true, "pack_id": "transit", "verb_key": "plan", "result": { … } }
{ "ok": false, "pack_id": "ledger", "verb_key": "transfer", "error": "confirm required" }
```

| Field | Type | Req |
|-------|------|-----|
| `ok` | boolean | MUST |
| `pack_id` | string | MUST |
| `verb_key` | string | MUST |
| `result` | any | SHOULD when `ok`. Parsed JSON stdout when parseable; else the raw string |
| `error` | string | MUST when `ok` is false |
| `stdout`, `stderr` | string | MAY (wrap) |

A denial MUST be returned as a result envelope with `ok: false`, not raised as a
transport error, so that Invokers can branch on it.

## 6.3 Pipeline

A runtime MUST evaluate the following in order and MUST stop at the first
failure. Nothing after a failing step MAY execute.

| # | Step | Failure → `error` |
|---|------|-------------------|
| 1 | Resolve pack | `unknown pack <id>` |
| 2 | Pack activated on this session | `pack <id> not activated` |
| 3 | Resolve Policy entry | `no policy for <verb_key>` |
| 4 | Authenticate Principal; resolve tenant and, if land is `brain`, project | `denied: privacy.land brain requires project_id` |
| 5 | Assert capability (§9.4) | `denied: missing <capability> for <pack>.<verb>` |
| 6 | Enforce `confirm` (§4.5) | `confirm required` |
| 7 | Mint Binding (§8.4) | runtime-defined; MUST fail closed |
| 8 | Run Adapter in sandbox (§10) | `sandbox: bin "<bin>" not allowlisted`, or stderr / `exit <n>` |
| 9 | Return result; audit (§9.6) | none |

## 6.4 Wrap rendering

For `kind: wrap`, the runtime MUST build the child argv as follows.

1. If the command declares `cli[]`: drop the first token (the bin name); for
   each remaining token, replace `{{name}}` with `String(args.name)` and
   `--flag={{name}}` with `--flag=<value>`; remove tokens that render empty.
2. Otherwise render `wrap.argv_template`, where `{{verb}}` expands to the command
   tail (`verb_key` after the first `.`, or the whole key; split on whitespace)
   and `{{name}}` as above.
3. Append `--json`.
4. Resolve `wrap.bin`: `.`-prefixed paths against the pack root; otherwise as
   given.
5. Spawn `wrap.bin` with the argv as an array. Runtimes MUST NOT construct a
   shell command string from untrusted values.
6. Set the working directory per §10.6 (`wrap.workspace.mode`: pack root,
   ephemeral jail, or local-land jail). Set `HCP_SANDBOX` to `sandbox` or
   `native-local` in the child environment. Merge `wrap.agent_env`. When the
   workspace mode is `workspace`, set `home_env` (default `HOME`) to that jail.
7. Rewrite `workspace:` file tokens and `file_flags` values into the jail.
   Host-absolute paths under `/etc`, `/home`, `/root`, and `/Users` MUST fail
   with `sandbox: host path not allowed`.
8. Exit code `0` → `ok: true`. Non-zero → `ok: false`, `error` = stderr if
   non-empty else `exit <code>`. Exit `127` denotes a missing or non-allow-listed
   bin.

## 6.5 Twin face

1. Every command SHOULD be invokable on the CLI face
   (`<bin> <space…> [--flags] --json`) and MUST be invokable on the JSON face.
2. For equal arguments the two faces MUST produce the same `result`.
3. The CLI face MUST emit structured output (`--json` or JSON by default) and
   MUST exit non-zero on failure.
4. The CLI face MUST NOT prompt interactively on the agent Invoker path.
5. `hcp exec <pack_id> <verb_key> [-- k=v …]` is the reference CLI's envelope
   Invoker; `k=v` pairs become `args`.

## 6.6 Composite and other adapters

1. `composite` execution is in-process. The result envelope is identical.
2. A runtime that does not implement an adapter kind MUST return
   `ok: false` with `error: "adapter <kind> not executable in <runtime>"`.

## 6.7 Prohibitions

- A runtime MUST NOT execute a command lacking a Policy entry.
- A runtime MUST NOT fall back to environment credentials when mint fails.
- A runtime MUST NOT accept a pack-level pass-through command as a substitute
  for named commands beyond bootstrap.
- Health commands (`doctor`, `status`) MUST NOT mint long-lived credentials as a
  side effect.

Source: https://harnesscontextprotocol.io/docs/specification/execute

---

# §7 Sense

> Normative definition of the outbound loop: trigger declaration, isolation from Execute, the sense.enable grant, activation, and what a runtime may and may not deliver.

Sense is the outbound loop: a pack announces a world change.

## 7.1 Declaration

1. A pack declares triggers in the manifest as `stimulusTriggers: [{ ref }]`.
2. `ref` MUST be non-empty, lower-case, and MAY contain `.` as a separator.
3. Every `ref` MUST have a Policy `stimuli` entry with `operation: read` and a
   land (§4.4). Runtimes MUST fail lint otherwise.
4. A pack MUST NOT declare a trigger for a condition it can only obtain by
   polling.

## 7.2 Isolation

1. A trigger MUST NOT cause a command to execute.
2. A trigger MUST NOT mint credentials or bypass Binding.
3. Delivery of a trigger MUST NOT spend, send, or mutate anything outside the
   runtime's sink.
4. Execute and Sense MUST share the pack's Policy, Binding, and Principal scope.
   They MUST NOT be merged into a single tool or command.
5. A recorded trigger MAY lead an Invoker to run Execute. That Execute MUST pass
   the full pipeline of §6.3 under its own grants.

## 7.3 Grant

1. A runtime MUST NOT deliver a trigger to a sink unless the Principal holds
   `sense.enable` for the pack.
2. Without the grant, the runtime MUST acknowledge the producer and drop.
3. A runtime MUST drop a trigger whose `ref` the pack does not declare and
   SHOULD report it.
4. A runtime MUST drop a trigger when the pack has no connected Binding for the
   Principal (any mode other than `public`), or when the trigger's land differs
   from its Policy entry. Sense fails closed, as Execute does (§9.4).

## 7.4 Activation

1. Sense activation MUST be independent of Execute activation. Deactivating
   Sense MUST NOT affect installed, activated, or connected state.
2. When activation is `on`, the runtime routes to a sink, either surfaced
   (`inbox`) or recorded (`silent`). When `off`, the route is `drop`.
3. Route selection and sink implementation are runtime concerns.

## 7.5 Recording

1. A sink record MUST be attributable to `Principal + Binding identity + land +
   pack_id + ref`.
2. When land is `brain`, the runtime MUST require project membership before
   surfacing the record.
3. Sink records are inert: reading one grants no capability.

## 7.6 Delivery mechanism

*Informative.* How a trigger reaches the runtime (signed ingress, vendor push
mapping, message queue, a `sense_only` pack's stdout) is not an HCP type.
Runtimes document it in their own profile.

## 7.7 Binding health

1. A pack with Binding mode `user`, `local`, or `mothership` SHOULD declare the
   trigger `binding.degraded`.
2. Its payload SHOULD carry a `state` from `linked`, `stale`, `needs_reauth`,
   `pending`, `error`.
3. The runtime's hub SHOULD be the producer of `binding.degraded`.

## 7.8 What is not Sense

*Informative.* Polling a read command; a runtime's uptime monitor; UI refresh
timers; control-plane scheduler ticks. None of these are pack triggers and a
runtime MUST NOT deliver them as such.

## 7.9 Sense-only packs

1. A pack with `kind: sense_only` MUST declare at least one trigger and MAY
   declare no commands.
2. Such a pack SHOULD have Binding mode `public`, or a Binding whose hub is
   `none` (§8.3), and MUST NOT require a
   user command to install a webhook unless the upstream requires a
   registration call.

Source: https://harnesscontextprotocol.io/docs/specification/sense

---

# §8 Binding

> Normative rules for credential binding: one mode per pack, hubs, families, fail-closed mint, explicit mock, connect, and health.

Binding is how a command obtains credentials without the pack holding any.

## 8.1 Declaration

1. A pack MAY declare `binding` in the manifest (§3.3). A pack that executes
   against any protected resource MUST declare a `mode`.
2. A pack MUST declare at most one `mode`.
3. A pack MUST NOT contain credential material. Runtimes SHOULD reject a pack
   whose files contain material that matches common credential patterns.

## 8.2 Modes

| Mode | Meaning | Connect |
|------|---------|---------|
| `public` | No credential | MUST be skipped; Principal still attributed |
| `user` | Delegated credential for the Principal | Required |
| `local` | Machine-local credential store | Required |
| `mothership` | Operator or fleet identity | Required; elevation grant for non-operator Principals |
| `mock` | Explicit test credential | MUST be requested in the envelope (`args.mode = "mock"`) |

1. A `control`-plane pack MUST use `mothership` (§2.5).
2. A runtime MUST NOT execute a `mothership` Binding for a Principal that lacks
   an explicit operator or elevation grant.
3. A runtime MUST NOT select a mode based on the Invoker type or on environment
   contents.

## 8.3 Hubs and families

1. `hub` names the kind of component expected to mint: `keyring`,
   `local-credential`, `public`, `none`. Its implementation is a runtime concern.
2. Packs sharing a `family` SHOULD share one consent per Principal. Connecting
   one pack in a family MAY connect the family.
3. When several packs in a family are linked, the runtime SHOULD union their
   delegated scopes.
4. Family MUST be expressed as a manifest field, not as a directory structure.

## 8.4 Mint

1. For modes other than `public`, a runtime MUST obtain the credential from its
   hub at exec time for the Principal, pack, and family.
2. If mint fails, the runtime MUST refuse the command. It MUST NOT fall back to
   an environment variable, a file the pack shipped, or any ambient source.
3. A runtime MUST ignore environment flags that request mock behaviour. Mock is
   explicit in the envelope only, and a runtime MAY refuse `mock` outside
   sandbox.
4. Minted material MUST be handed to the Adapter through a channel the wrapped
   tool already understands (environment, file) and MUST NOT be written to
   Skills, logs, or results.

## 8.5 Connect

1. Establishing or refreshing Binding is a command with `operation: write`.
2. It MUST be gated by capability `pack.connect` (§9.4), not by
   `verb.exec.write`.
3. Connect MUST NOT be required for `public` packs.
4. The reference lifecycle command is `hcp connect <pack_id>`; packs MAY also
   expose a domain `connect` or `setup.*` command that performs the same step.

## 8.6 Health

1. Health commands (`doctor`, `status`) MAY report Binding state and MUST NOT
   mint long-lived credentials.
2. Binding degradation SHOULD be announced as the trigger `binding.degraded`
   (§7.7) with a state from `linked`, `stale`, `needs_reauth`, `pending`, `error`.
3. Recovery is a human or elevated `connect`. The Sense path never re-mints
   automatically.

## 8.7 Binding and Principal

1. Binding identity and Principal MUST be recorded separately in audit (§9.6).
2. A runtime MUST NOT infer one from the other.

## 8.8 Prohibitions

- Secrets in `surface.json`, `policy.json`, or `HARNESS.md`.
- Two modes on one pack.
- Silent env-token fallback.
- Env-flag mocks.
- Operator Binding reachable from an end-user Principal without elevation.

Source: https://harnesscontextprotocol.io/docs/specification/binding

---

# §9 Authorization

> Normative authorisation model: Principal, scope, the five capabilities, evaluation order, denial reasons, and audit attribution.

Packs declare effect class (§4). Runtimes decide who may cause that effect.

## 9.1 Principal

1. Every exec, connect, install, and Sense delivery MUST be attributed to exactly
   one Principal.
2. The Principal MUST be established by the runtime from an authenticated
   session or credential. It MUST NOT be taken from the exec envelope.
3. Principal kinds are informative: `human`, `service`, `agent`, `mothership`.

## 9.2 Scope

1. Tenant MUST come from the Principal's session. A runtime MUST NOT select
   the tenant from `args`.
2. Project MUST be resolved when the command's or trigger's land is `brain`.
   Absent project scope, the runtime MUST deny with
   `privacy.land brain requires project_id`.
3. For land `brain`, the runtime MUST verify project membership; tenant
   membership alone is insufficient.
4. Arguments such as `--entity` MAY refine Binding-scoped accounts within the
   resolved scope.

## 9.3 Separation

| Layer | Declares | Owner |
|-------|----------|-------|
| Policy | `operation`, `land`, `confirm` | Pack author |
| Binding | Credential mode and path | Pack declares; runtime mints |
| Principal | Who acts | Runtime |
| Capability grants | Who may cause which effect | Runtime |
| Tenant / Project | Scope | Runtime |

Packs MUST NOT embed user allow-lists, roles, or credentials.

## 9.4 Capabilities

A runtime MUST implement, at minimum, these five axes.

| Capability | Gates |
|------------|-------|
| `pack.install` | Placing a pack in a tenant's catalog or store |
| `pack.connect` | Establishing or refreshing Binding |
| `verb.exec.read` | Executing a command with `operation: read` |
| `verb.exec.write` | Executing a command with `operation: write` |
| `sense.enable` | Delivering declared triggers to a sink |

1. A runtime MAY scope a grant to a `pack_id` and further to specific
   `verb_keys`.
2. A multi-tenant runtime MUST NOT collapse `verb.exec.read`,
   `verb.exec.write`, and `pack.connect` into a single boolean.
3. `pack.connect` MUST be evaluated for connect commands regardless of any
   `verb.exec.write` grant.
4. A runtime MUST fail closed: absence of a matching grant is a denial.

## 9.5 Evaluation order

Authorization runs inside the exec pipeline, in the order of §6.3:

```text
1. Resolve pack, activation, and Policy entry                       (§6.3 steps 1 to 3)
2. Authenticate Principal; resolve Tenant (+ Project if land = brain) (step 4)
3. Assert capability for pack_id (+ verb_key)                        (step 5)
4. Enforce confirm                                                   (step 6)
5. Mint Binding, fail closed                                         (step 7)
6. Execute Adapter                                                   (step 8)
7. Audit                                                             (step 9)
```

A runtime MUST NOT reorder these steps such that an Adapter could run before a
denial would have been produced. Every Invoker (an agent, an API, a UI, a
person at a terminal) reaches a command through this order, and a runtime MUST
NOT give an Invoker a path to a `verb_key` that skips a step.

*Informative.* RFC 0006 (draft) adds access modes, allow, ask and deny rules,
and approvals at step 4.

## 9.6 Audit

The reference runtime writes one line per decision to `audit.jsonl`; see
[Access](/docs/concepts/access#audit).


1. Every `write` MUST be recorded with:
   `Principal + Tenant (+ Project) + Binding identity + pack_id + verb_key + operation`.
2. Every `read` SHOULD be recorded with the same tuple.
3. Every denial SHOULD be recorded with its reason.
4. Binding identity and Principal MUST be distinct fields.

## 9.7 Denial reasons

Denials MUST be returned as `ok: false` result envelopes. The `error` string
SHOULD begin with `denied:` for grant and scope denials and SHOULD name the
missing capability.

| Reason | Condition |
|--------|-----------|
| `denied: missing verb.exec.read for <pack>.<verb>` | No read grant |
| `denied: missing verb.exec.write for <pack>.<verb>` | No write grant |
| `denied: missing pack.connect` | Connect without grant |
| `denied: missing pack.install` | Install without grant |
| `denied: missing sense.enable` | Sense delivery without grant |
| `denied: privacy.land brain requires project_id` | No project scope |
| `confirm required` | Gate not satisfied |

## 9.8 Cross-mode execution

A runtime MUST refuse a command whose Binding mode is `mothership` when the
Principal is not an operator, unless an explicit elevation grant is present. This
applies regardless of how the operator binary became reachable.

## 9.9 Out of scope

*Informative.* Role table schemas, row-level security, IdP integration, session
formats, and host paths are adopter profiles. The protocol fixes only the axes,
the order, and the tuple.

Source: https://harnesscontextprotocol.io/docs/specification/authorization

---

# §10 Sandbox

> Normative execution posture. Sandbox by default with bin allow-list, cwd jail, and no ambient credentials; native-local only by explicit opt-in.

## 10.1 Default

1. A runtime MUST execute Adapters in `sandbox` mode unless the harness has
   explicitly opted into `native-local`.
2. Opting into `native-local` MUST require an explicit, positive configuration
   action. Absence of configuration MUST yield `sandbox`.

## 10.2 Sandbox requirements

In `sandbox` mode a runtime MUST:

1. Allow-list binaries. Spawn only `wrap.bin` values that are on the
   runtime's allow-list. The allow-list SHOULD be seeded with the activated packs'
   declared `wrap.bin` values and the runtime's own CLI. A bin not on the list
   MUST be refused with exit `127` and `error: sandbox: bin "<bin>" not allowlisted`.
   The runtime MUST NOT resolve an unlisted bare name via `PATH`.
2. Jail the working directory. Set the child's working directory to the
   pack root or a per-pack jail. Land `local` binds to this directory.
3. Inherit no ambient credentials. Provide only credentials minted through
   Binding (§8.4) and the pack's `wrap.agent_env`. A runtime MUST NOT rely on
   credentials present in its own environment to satisfy a pack.
4. Mark the environment. Set `HCP_SANDBOX=sandbox` in the child.
5. Ignore env-flag mocks. Mock is envelope-explicit only (§8.2).

## 10.3 Native-local

1. In `native-local` mode a runtime MAY resolve `wrap.bin` on the host `PATH`
   and MAY relax the cwd jail.
2. The runtime MUST set `HCP_SANDBOX=native-local`.
3. All other requirements of this specification (grants, confirm, mint
   fail-closed, Policy coverage) apply unchanged.
4. *Informative.* Native-local expresses a trust decision about a personal
   machine. It is not a convenience mode.

## 10.4 Stronger isolation

1. Containers, microVMs, seccomp profiles, read-only filesystems, and per-family
   network egress policy are conforming implementations of `sandbox`.
2. A pack MUST NOT need to change to run under stronger isolation. Isolation is
   a runtime property.

## 10.5 Reference behaviour (informative)

The reference runtime implements §10.2 with a process allow-list (bare names or
absolute paths; a bare name matches a path ending in `/<name>`), a fixed cwd at
the pack root when `wrap.workspace` is omitted, and `HCP_SANDBOX` in the child
environment. It requires both `sandbox: false` and `nativeLocal: true` to enter
native-local.

## 10.6 Workspace / exec plane (RFC 0003)

Wrap names the bin. The exec plane names where that process may read and write.
Packs MUST NOT name a driver (object store, microVM, host path). Isolation is a
runtime property (§10.4).

### Pack field

Optional `wrap.workspace`:

| Field | Values | Default |
|-------|--------|---------|
| `mode` | `none` \| `ephemeral` \| `workspace` | `none` (pack root, HCP/1) |
| `home_env` | env key names | `["HOME"]` when `mode` is `workspace`, else none |
| `file_flags` | flags whose next argv token is a file | `--file`, `--json-file`, `--output`, `-o`, `--env-file` |

`ephemeral` is a fresh jail per exec. `workspace` binds the jail to
`privacy.land: local` for the Principal; the runtime persists it.

### File references

In `sandbox` mode a runtime MUST rewrite `workspace:` prefixes into the jail,
MUST reject host-absolute paths under `/etc`, `/home`, `/root`, and `/Users`
(`error: sandbox: host path not allowed`), MUST spawn argv as an array, and
MUST write back `--output` / `-o` (and new jail files when `mode` is
`workspace`) according to its driver. A runtime MAY accept more URI schemes in
file references and MUST rewrite them into the jail before spawn; those schemes
are not HCP types.

A runtime MUST implement at least a local driver. Additional drivers MUST use
prepare → rewriteArgv → spawn → writeback → cleanup. A pack MUST NOT change to
run under another conforming driver.

*Informative.* `hcp wrap <bin>` scaffolds `wrap.bin` from an existing CLI.

Source: https://harnesscontextprotocol.io/docs/specification/sandbox

---

# §11 Lifecycle

> Normative pack lifecycle (lint, install, activate, connect, exec), the four availability flags, and the store.

## 11.1 Stages

| Stage | Precondition | Effect | Capability |
|-------|-------------|--------|------------|
| lint | Directory readable | Validates §2 to §5; no state change | none |
| install | Lint passes | Pack present in the runtime's catalog or store | `pack.install` |
| activate | Installed | Pack's commands loadable for the session; Skill copied (§5.4) | none |
| connect | Activated; Binding not `public` | Binding established (§8.5) | `pack.connect` |
| exec | Activated; connected if Binding requires; grants | Command runs (§6) | `verb.exec.read` / `verb.exec.write` |
| deactivate | none | Removes activation and connection for the session | none |
| uninstall | none | Removes from catalog or store | `pack.install` |

1. A runtime MUST NOT execute a command of a pack that is not activated.
2. A runtime MUST NOT skip lint on install. A pack that fails lint MUST NOT be
   installed.
3. A runtime MAY treat `install` and `activate` as a single user action, but
   MUST expose both states in availability (§11.2).
4. Deactivate MUST also drop `connected` for the session.
5. A runtime MUST refuse to uninstall its own bootstrap pack, if it has one.
6. Activation MUST NOT require an MCP configuration or new host tools (§12.2.1).

## 11.2 Availability

For each pack a runtime MUST be able to report:

| Flag | Meaning |
|------|---------|
| `installed` | Present in the store |
| `activated` | Loaded for the current session |
| `connected` | Binding established (always `true` for `public`) |
| `executable` | Per command: `activated` ∧ (`connected` ∨ Binding `public`) ∧ Principal holds the required grant ∧ Adapter runnable in this runtime |

1. `executable` is computed per command, not per pack.
2. A pack's `status` MUST be one of `available`, `installed`, `activated`,
   `connected`, `executable`, or `unavailable`, reflecting the highest reached
   stage. Runtimes MAY report more than one.
3. A `sense_only` pack MUST report `executable: false` for Execute while still
   being valid; Sense activation is reported separately (§7.4).

## 11.3 Store

*Informative.* The reference store is `$HCP_HOME` (default `~/.hcp`):

```text
~/.hcp/
  packs/<pack_id>/       installed pack directories
  skills/<pack_id>/      SKILL.md copies (§5.4)
  state.json             { activated: [], connected: [], grants? }
```

A conforming runtime MAY use any store, including a database. It MUST preserve
the semantics of §11.1 and §11.2.

## 11.4 Bootstrap

1. A runtime MAY ship a bootstrap pack whose commands describe the runtime's own
   lifecycle (`doctor`, `list`, `install`, `activate`, `connect`,
   `availability`, `exec`, …).
2. A bootstrap pack follows every rule of this specification. One that manages
   the runtime's local store is a `runtime`-plane pack with Binding `local`; one
   that acts on an operator's fleet is a `control`-plane pack (§2.5).
3. The reference bootstrap pack is `hcp-cli`; it is installed automatically on
   first use and cannot be uninstalled.

## 11.5 Reference CLI (informative)

The reference binary is `hcpx`, also installed as `hcp`.

```text
hcpx lint <pack_dir>
hcpx install <pack_dir>
hcpx activate <pack_id> [pack_id…]
hcpx connect <pack_id>
hcpx exec <pack_id> <verb_key> [-- k=v …]
hcpx availability [pack_id]
hcpx deactivate <pack_id> [pack_id…]
hcpx uninstall <pack_id>
```

Every command accepts `--json`.

Source: https://harnesscontextprotocol.io/docs/specification/lifecycle

---

# §12 Projections

> Normative rules for how a pack appears on other surfaces: CLI grammar, the two host tool shapes, the four-faces rule, and export to MCP or A2UI.

A projection is a view of a pack on some surface. Projections MUST NOT add,
remove, or alter Policy, Binding, or Principal semantics, and MUST NOT add
commands the pack does not declare.

## 12.1 CLI

1. A pack's CLI face SHOULD follow `<bin> <space> [<sub…>] [--flags] [--json]`.
2. Every command SHOULD emit structured output when `--json` is present and MUST
   exit non-zero on failure.
3. Health and Binding commands (`doctor`, `status`, `login`, `connect`,
   `whoami`) SHOULD be first-class spaces, not flags on other commands.
4. Verbs SHOULD be `read` by default; `--apply` / `--confirm` SHOULD mark a
   write.
5. Selectors (`--entity`, `--account`, `--pool`) MUST refine within the
   Principal's resolved scope; they MUST NOT select tenant (§9.2).
6. A CLI face MUST NOT prompt interactively when invoked by an agent.
7. `doctor` and `status` SHOULD exit `0` when healthy, `2` on a warning, and `1`
   on failure.
8. A CLI with an installer SHOULD expose it as the family `setup.status`
   (`read`, idempotent: what is installed and linked), `setup.apply` (`write`;
   runs without prompts for an agent Invoker), and `setup.update` (`write`;
   refreshes the Skill after a pack upgrade).
9. Health commands are `read` and MUST NOT mint long-lived credentials.

## 12.2 Host tools

A host projects packs into a tool-calling model in one of two shapes.

1. A host whose agent can run shell commands SHOULD expose a single tool, or the
   runtime's binary itself (`hcpx` in the reference), that forwards argv to a
   runtime (`hcpx exec <pack_id> <verb_key> -- k=v …`), together with a Skill
   that teaches it. Activating a pack
   MUST NOT require the host to register additional tools.
2. A host that runs an in-process tool loop MAY instead expose one tool per
   executable command. Such tools SHOULD be named
   `<pack_id>__<verb_key_with_dots_as_underscores>` (for example
   `mail__mail_list`).
3. A host SHOULD expose `hcp__read_skill` (or equivalent) returning the full
   Skill for a pack, so that the system prompt may carry only leads (§5.3).
4. Tool descriptions SHOULD carry `operation`, `land`, and `confirm` so that the
   model can distinguish reads from writes.
5. A host MUST route every tool call through a runtime that implements §6.3. A
   host MUST NOT execute `wrap.bin` itself.
6. A host MUST filter the projected tool set by the Principal's grants.

## 12.3 Generated surfaces

*Informative.* The reference `hcpx generate` produces, from activated packs:
a stable system-prompt block, a tool list per §12.2, a set of Skill copies, and
each host's own configuration (an MCP server entry, a plugin file, or in-process
tool definitions). `hcpx adopt <host> --write` writes them where that host
reads its configuration.

## 12.4 Four faces

The same command MUST be nameable consistently on four faces:

| Face | Form |
|------|------|
| Wire | `verb_key` (`plan`) |
| CLI | `cli[]` (`transit plan {{from}} {{to}}`) |
| Host tool | `hcpx exec transit plan`, or `transit__plan` in-process |
| Skill | Hot-path table row `plan` |

1. Runtimes SHOULD lint drift among these faces.
2. Renaming a command on one face without the others is a breaking change (§13).

## 12.5 Export to other protocols

1. A runtime MAY export a pack's commands as MCP tools, A2UI components, OpenAPI
   operations, or any other surface. The reference MCP export is a single tool
   named `hcp` (§12.2.1).
2. An export MUST preserve the pack as the unit: one pack → one exported group.
3. An export MUST route invocations back through a runtime that implements
   §6.3; an export MUST NOT reimplement Policy or Binding.
4. An export MUST NOT be the install unit. Installing the export MUST NOT
   substitute for installing the pack.
5. Sense MUST NOT be exported as a tool the model can call to "poll".

## 12.6 What a projection may drop

A projection MAY omit commands the Principal cannot execute and MAY omit the
`hcp` protocol metadata. A projection MUST NOT omit `confirm` markers on
gated writes.

Source: https://harnesscontextprotocol.io/docs/specification/projections

---

# §13 Versioning

> Normative versioning: the protocol version, the manifest's protocol pin, compatibility promises, and deprecation.

## 13.1 Protocol version

1. The protocol is versioned as `HCP/<major>.<minor>`. This document is
   HCP/1.1.
2. The protocol field `hcp` (§2.6) carries the major only (`1`). A runtime MUST
   accept any `1.x` pack.
3. Minor versions add optional fields and tighten lints. They MUST NOT change
   the meaning of an existing field or remove one.
4. A major version MAY change field meaning or remove fields. A major bump
   requires an accepted RFC and a migration note.
5. A new adapter kind, Binding mode, land token, or envelope field requires an
   RFC. Implementations MUST NOT fork protocol names: an extension takes a new
   name, never a new meaning for an existing one.

## 13.2 Compatibility promise

Within a major:

1. A pack that passes lint on `1.n` MUST load on any `1.m` runtime with `m ≥ n`.
2. A runtime MUST ignore top-level manifest fields it does not recognise
   (§3.1) and MUST NOT fail lint on them, unless they collide with a reserved
   name. The closed objects (`binding` §3.3, protocol metadata §3.6, and
   `policy.json` §4) keep failing on unknown fields.
3. A runtime MUST reject a pack whose `hcp` major it does not implement,
   with `error: unsupported protocol version <n>`.
4. The exec and result envelopes (§6.1 and §6.2) are frozen for the major.

## 13.3 Deprecation

1. A deprecated field MUST remain accepted for at least one minor after the
   deprecation is published.
2. Runtimes SHOULD warn on deprecated fields in lint.
3. Currently deprecated: manifest `harness` (inline Skill); pack-level
   `pass_through` beyond bootstrap; unnamed wrap packs (§3.5).

## 13.4 Pack versioning

*Informative.* HCP/1.1 does not define a pack version field or a registry. Packs
are identified by `pack_id` and distributed as directories. A `version` field and
registry semantics are on the [roadmap](/docs/community/roadmap) and will
arrive by RFC.

## 13.5 Changes in 1.1

Relative to HCP/1.0, RFC 0001 ("wrap hardening") made these normative:

- Named commands are REQUIRED for wrap packs; pack-level pass-through is limited
  to bootstrap.
- `installer` is a Binding family.
- Land `local` is defined as the sandbox jail directory.
- `confirm` is defined for `write` entries; a write MAY require it (§4.5).
- Health commands are `read` and MUST NOT mint.

RFC 0008 (errata) carried the remaining text of RFC 0001 and RFC 0003 into this
specification, settled the sections that disagreed, and added the lint kind
`write_stimulus`.

## 13.6 Implementation versions

Implementations (SDKs, runtimes, CLIs) version independently under their own
schemes. An implementation MUST state which protocol version(s) it conforms to.

Source: https://harnesscontextprotocol.io/docs/specification/versioning

---

# §14 Conformance

> What it means to conform to HCP/1.1 as a pack, a runtime, or a host, and the executable vectors that back the claims.

## 14.1 Conformance targets

| Target | Claims | Sections |
|--------|--------|----------|
| Pack | Directory is a valid Harness Pack | §2, §3, §4, §5 |
| Runtime | Loads packs and executes both loops per this specification | §6 to §11 |
| Host | Projects packs through a conforming runtime | §12 |

An implementation MAY conform as more than one target.

## 14.2 Pack conformance

A pack conforms when all of the following hold:

1. The three required files are present (§2.1).
2. `surface.json` validates against the manifest schema (§3).
3. Every command and trigger has a Policy entry (§4.4).
4. `confirm` appears only on `write` entries (§4.5).
5. The Skill contains no credentials or host paths and teaches only declared
   commands (§5.2).
6. Binding declares at most one mode (§8.1).
7. Lint from a conforming runtime returns zero issues.

*Informative.* The reference implementation also runs a pack's commands live
and runs its eval cases (`hcpx check --probe`). A catalog entry carries that
result as evidence; a claim in the Skill or a README is not evidence.

## 14.3 Runtime conformance

A runtime conforms when it:

1. Fails lint on any of §14.2 violations.
2. Executes the pipeline of §6.3 in order and fails closed at each step.
3. Enforces the five capabilities (§9.4) and the evaluation order (§9.5).
4. Enforces `confirm` (§4.5) and resolves project scope for land `brain` (§9.2).
5. Mints Binding at exec time and never falls back (§8.4).
6. Defaults to sandbox with allow-list and jail (§10.2).
7. Isolates Sense from Execute and gates delivery on `sense.enable` (§7.2 and §7.3).
8. Reports the four availability flags (§11.2).
9. Returns the result envelope of §6.2 for success and denial.
10. Accepts any `1.x` pack (§13.2).

## 14.4 Host conformance

A host conforms when it:

1. Routes every projected tool call through a conforming runtime (§12.2.5).
2. Filters the projected tool set by grants (§12.2.6).
3. Preserves `confirm` markers (§12.6).
4. Does not merge Sense into a callable tool (§12.5.5).

## 14.5 Vectors

The reference implementation ships executable vectors in
`packages/sdk/src/conformance.test.ts` and pack fixtures under
`examples/packs/`. Each vector names the section it checks.

| Vector | Section |
|--------|---------|
| `pack.lint.transit` | §2, §3, §4 |
| `pack.lint.ledger` | §4.5 |
| `pack.lint.echo` | §3 (composite) |
| `exec.transit.plan` | §6.3, §6.4, §6.5 |
| `exec.ledger.transfer.confirm-required` | §4.5, §6.3 step 6 |
| `exec.denied.missing-write-grant` | §9.4, §9.7 |
| `exec.sandbox.bin-not-allowlisted` | §10.2 |
| `exec.brain.requires-project` | §9.2 |
| `availability.flags` | §11.2 |

```sh
cd hcp && bun test packages/sdk/src/conformance.test.ts
```

A runtime that passes every vector against the gold packs MAY claim
"HCP/1.1 runtime, reference vectors passing". Passing the vectors is necessary
but not sufficient; the text of this specification governs.

## 14.6 Claiming conformance

An implementation claiming conformance SHOULD publish:

- Target(s) and protocol version.
- Which optional behaviours are implemented (`native-local`, `openapi`,
  `composite`, Sense sinks).
- A link to its vector run or its own test suite.
- Its runtime profile: hub implementation, store, sink, isolation.

## 14.7 Non-conformance

*Informative.* Common ways to fail: executing an unactivated pack; a write without
`confirm`; env-token fallback on mint failure; a single `admin` boolean instead of
five capabilities; polling exposed as Sense; a host spawning `wrap.bin` directly.

Source: https://harnesscontextprotocol.io/docs/specification/conformance

---

# CLI

> Look up any `hcpx` command with its arguments, JSON output and exit codes, plus the access commands, the store layout and environment variables.

`hcpx` is the reference CLI and the bootstrap Invoker for the reference
runtime, described by its own pack, `hcp-cli`. With `--json` every command
prints a single JSON object, and agents should start with `hcpx commands`. The
binary also answers to `hcp`
([One binary, two names](/docs/quickstart#one-binary-two-names) says when to use
which).

```sh
bunx @harnesscontextprotocol/cli help
# or, from a checkout of the hcp repository
bun packages/cli/src/hcp.ts help
```

The package is a private preview on GitHub Packages, and
[Install the CLI](/docs/quickstart#install-the-cli) shows how to reach it.

## Conventions

- Exit `0` on success, `1` on any failure. `hcpx check` also exits `2` when it
  finds warnings only.
- Failures print `{ "ok": false, "error": "…" }`. Most also carry a stable
  `code`, a `hint`, and `next`, the commands to run instead. See
  [Error codes](/docs/reference/errors#error-codes).
- Without `--json`, objects are pretty-printed and strings (Skill text) print raw.
- The store is `$HCP_HOME` (default `~/.hcp`). See [Store](#store).
- The bootstrap pack `hcp-cli` is installed the first time a command opens the store.

## Commands

### `hcpx doctor`

Reports runtime health.

```json
{ "ok": true, "cli": "hcpx", "protocol": "hcp", "version": 1, "home": "~/.hcp",
  "sandbox_default": true, "installed_count": 3, "activated": ["hcp-cli", "transit"],
  "connected": [], "bootstrap": "hcp-cli" }
```

### `hcpx availability [pack_id]`

Prints the four availability flags for every installed pack, or for one. The
alias is `avail`.

```json
{ "ok": true, "home": "~/.hcp", "sandbox_default": true,
  "packs": [{ "pack_id": "transit", "status": ["activated", "executable"],
    "installed": true, "activated": true, "connected": true,
    "verbs": [{ "verb_key": "plan", "operation": "read", "land": "personal", "executable": true }],
    "capabilities": ["verb.exec.read"] }],
  "activated": ["hcp-cli", "transit"], "installed": ["hcp-cli", "transit"] }
```

An unknown `pack_id` yields one row with `status: ["unavailable"]`.

### `hcpx list`

Lists installed packs with their adapter kind, command count, activation and
directory.

### `hcpx lint <pack_dir>`

Loads the directory and runs the pack lints, exiting `1` on any issue.

```json
{ "ok": false, "pack_id": "ledger", "issues": [
  { "kind": "missing_verb", "key": "transfer", "message": "verb \"transfer\" missing from policy.json" } ] }
```

[Errors](/docs/reference/errors#lint-issues) lists the issue kinds.

### `hcpx check <pack_dir> [--probe] [--agent|--json] [--init-evals] [--strict] [--no-eval] [--timeout ms]`

Grades structure, the quality rules, live CLI probes (`--probe`) and the cases
in `hcp.eval.json` when it exists (`--no-eval` skips them). `--agent` prints a
prioritised fix list for coding agents and `--json` the full report, while
`--init-evals` writes a starter eval file (never over an existing one) and
exits. `--timeout` limits each probe and eval case (default 10000 ms). It exits
`0` when clean, `2` on warnings only and `1` on errors, and `--strict` makes
warnings exit `1`. See [Check and evaluate](/docs/packs/check).

```json
{ "ok": false, "pack_id": "gh", "score": 8, "grade": "F",
  "summary": { "errors": 7, "warnings": 2, "info": 0 },
  "findings": [ { "id": "R014", "severity": "error", "where": { "file": "surface.json", "pointer": "/verbs/list/cli" },
    "message": "\"gh list\" is not a command of this CLI …", "fix": "Point \"list\".cli at a subcommand that exists …" } ] }
```

### `hcpx eval <pack_dir> [--json] [--timeout ms]`

Runs the cases in `hcp.eval.json` through the reference runtime and prints pass
or fail per case with latency and approximate output tokens, then p50 and p95
for the run. Exit `1` when any case fails, the file is missing, or the pack
fails lint (then no case runs). `--json` returns `{ ok, pack_id, evals }`, with
`evals` as in `hcpx check --json`.

### `hcpx install <pack_dir|github:…|npm:…>`

Lints, then copies the pack directory into `$HCP_HOME/packs/<pack_id>/` and its
`HARNESS.md` to `$HCP_HOME/skills/<pack_id>/SKILL.md`. Any lint issue fails the
install, and a path without `surface.json` fails with `code: "no_pack"`.

From GitHub, `hcpx install github:owner/repo[@ref][//path]` or a `github.com`
URL clones the repository, falls back to its tarball, and sends `GITHUB_TOKEN`
when set. From npm, `hcpx install npm:@scope/name` runs `npm pack` and installs
the directory named by `"hcp": { "pack": "." }` in the package's `package.json`,
or the package root; `hcpx install npm:./path/to/pack` installs a local package.

When a GitHub or npm source cannot be fetched and the catalog lists a gold
template for it, the CLI installs the template and says so in `note`.

```json
{ "ok": true, "installed": "transit", "dest": "~/.hcp/packs/transit",
  "verbs": ["find", "plan", "show", "status", "follow"],
  "next": ["hcp activate transit", "hcp availability transit"] }
```

### `hcpx uninstall <pack_id>`

Removes the pack directory, and refuses to remove `hcp-cli`.

### `hcpx activate <pack_id> [pack_id…]`

Adds packs to `state.activated` and copies each `HARNESS.md` to
`$HCP_HOME/skills/<pack_id>/SKILL.md`.

```json
{ "ok": true, "activated": ["hcp-cli", "transit"], "skills": ["~/.hcp/skills/transit/SKILL.md"] }
```

An unknown id fails with `code: "not_installed"`, a "did you mean" when an
installed pack has a similar id, and `next` commands to search or install.

### `hcpx deactivate <pack_id> [pack_id…]`

Removes packs from `activated` and `connected`.

### `hcpx connect <pack_id>`

Runs the runtime's connect step for an activated pack and needs the
`pack.connect` grant. An inactive pack fails with `not activated: <id>`.

```json
{ "ok": true, "connected": "ledger", "all": ["ledger"] }
```

### `hcpx commands [pack_id]`

Lists every command of the activated packs, or of one pack, with its
description, `read` or `write`, whether it needs confirmation, what the current
access settings decide (`access`: allow, ask or deny), its arguments with
examples, and a line ready to run.

```json
{ "ok": true, "packs": [ { "pack_id": "transit", "about": "Plan public-transport journeys…",
  "commands": [ { "command": "plan", "description": "Route options between two places with duration, changes, and fare, fastest first.",
    "operation": "read", "access": "allow",
    "args": [ { "name": "from", "required": true, "description": "Origin place name or id from find", "example": "Central" } ],
    "run": "hcp exec transit plan --json -- from=Central to=Harbour" } ] } ] }
```

### `hcpx exec <pack_id> <verb_key> [--json] [-- k=v …] [--args '{…}']`

Runs a command through the full pipeline. Arguments after `--` become `args`
(`from=Central to=Harbour confirm=true`), and `--args` takes a JSON object for
typed values or text with spaces.

```sh
hcpx exec transit plan --json -- from=Central to=Harbour
hcpx exec transit plan --json --args '{"from":"Central","to":"Harbour"}'
hcpx exec ledger transfer --json -- amount=2.80 payee=demo-transit confirm=true
```

The runtime checks arguments first, so an unknown name or a missing required
argument fails with the valid names and a "did you mean". The command returns
the [result envelope](/docs/specification/execute#62-result-envelope) without
duplicating the raw output and exits `1` when `ok` is `false`. With no command,
`hcpx exec <pack_id>` returns that pack's command list.

### `hcpx search [query] [--class class]`

Searches the community catalog at
`https://harnesscontextprotocol.io/api/v1/packs`, or `HCP_CATALOG_URL` when set,
and caches the answer in `$HCP_HOME/catalog.json` for an hour. `source` is
`remote`, `cache`, or `bundled`: the snapshot shipped in the CLI, used when the
origin is unreachable (4 s timeout) and nothing is cached.

```json
{ "ok": true, "source": "bundled", "count": 1, "packs": [{ "id": "transit", "class": "public-read" }] }
```

### `hcpx catalog [pack_id]`

Lists catalog entries, or the entry for one `pack_id`.

### `hcpx init <id> [--class public-read|gated-write] [--template transit|ledger|echo] [--dir path]`

Writes a self-contained pack directory: the three files, a `package.json`
that depends on the CLI, and a demo `bin/` for wrap templates. Gold `--template`
values keep their gold `id`. `cli_bin` names the CLI that starts it on this
machine (see [One binary, two names](/docs/quickstart#one-binary-two-names)).

```json
{ "ok": true, "dir": "./weather", "pack_id": "weather", "template": "public-read",
  "files": ["package.json", "surface.json", "policy.json", "HARNESS.md", "bin/cli.mjs", "README.md"],
  "cli_bin": "hcpx", "next": ["hcpx check ./weather --probe --agent", "hcpx install ./weather --json", "hcpx activate weather --json"] }
```

### `hcpx wrap <bin> [--id pack_id] [--class public-read|gated-write] [--dir path]`

Scaffolds a pack whose `wrap.bin` is an existing CLI on `PATH`, in `packs/<id>`
unless `--dir` says otherwise. It reads `<bin> --help` as an agent would (no
TTY, no colour, stdin closed) and drafts one command per usage line it can
parse: the `cli[]` form, the arguments, a guessed Policy row with `land: local`,
and `confirm: true` plus a suggested `preview` for commands that sound risky
(delete, send, publish, pay, and similar) or take the CLI's own confirmation
flag. `wrap.workspace.mode` is `project`, so the CLI runs in the caller's
project; `gated-write` sets Binding `local` and `public-read` sets `public`.

```bash
hcpx wrap mycli --class gated-write --dir ./mycli-pack --json
```

The result lists the `drafted` commands, `candidates` (subcommand names seen
when no usage line parsed), `json_support`, `warnings`, and the `next` steps:
review the draft against `<bin> <command> --help`, check each Policy row, then
`hcpx check <dir> --init-evals` and `hcpx check <dir> --probe --agent`. A CLI
not on `PATH` fails with `code: "bin_not_found"`.

### `hcpx share [pack_dir|github:owner/repo] [--path subdir]`

Lints a directory and prints a catalog row for
`hcp-docs/content/catalog/packs.json`; exit `1` when lint fails. A `github:`
reference reads the pack from the public repository (`--path` for a
subdirectory) and reports the same row and issues.

[Share a pack](/docs/packs/share#catalog) covers the catalog and the PR.

### `hcpx skill <pack_id>`

Prints `HARNESS.md` for an activated pack. With `--json`: `{ ok, pack_id, skill }`.

### `hcpx generate`

Produces host-neutral output from activated packs (system-prompt block, tool
list, Skill copies, per-host configuration) without writing files.

### `hcpx mcp`

Starts a stdio MCP server whose `tools/list` returns one tool, named `hcp`.
`tools/call` takes `args` (string) or `argv` (string array) as CLI argv, never
through a shell, and runs it inside the server process with `HCP_MCP=1` set.
Every CLI a pack starts inherits that variable, so the access commands refuse.
A call starts no new `hcpx`, and `resources/list` offers only catalog, help and
skills. [MCP export](/docs/hosts/mcp) shows the host side.

### `hcpx adopt agent|claude|codex|cursor|opencode|mcp|grok [--write]`

Generates the host-neutral output and writes host configuration into the
current directory, or previews it without `--write`. `grokbot` is an alias of
`grok`.

| Target | Writes |
|--------|--------|
| `claude` | `.claude/skills/hcp/SKILL.md`, one `hcp` server in `.mcp.json`, and `.claude/settings.json` with `HCP_AGENT=1` and deny rules for the access commands |
| `codex` | The protocol Skill and an HCP section in `AGENTS.md`; prints the `~/.codex/config.toml` block (the server and `HCP_AGENT=1` for Codex's shell) and never edits files outside the repository |
| `cursor` | `.cursor/skills/hcp/SKILL.md`, `.cursor/mcp.json`, `.cursor/rules/hcp.mdc` |
| `opencode` | One plugin tool `hcp` in `opencode.json` |
| `grok` | `.grok/config.toml`, rule, Skill |
| `agent`, `mcp` | The generic plugin: `.hcp/plugin.json`, the Skill, and `.mcp.json` |

`HCP_HOME` goes into host config only when you set it explicitly, so committed
config works on every machine. A `.hcp/no-adopt` file opts a workspace out of
adopt, and [Adopt in hosts](/docs/hosts) covers each host.

### Access commands

The commands that change [access](/docs/concepts/access) refuse with
`human_only` inside an agent (`HCP_AGENT=1`, or through `hcpx mcp`).

| Command | Does |
|---------|------|
| `hcpx access` | Effective mode, rules, approval authority, where each comes from, and ignored project settings |
| `hcpx mode [manual\|auto\|run] [--pack id] [--project]` | Show or set the mode, globally or for one pack |
| `hcpx allow\|ask\|deny <pack.command\|pack.*\|*> [--op read\|write] [--when k=v]… [--max k=n]… [--note text] [--project]` | Add a rule. `--project` writes `.hcp/access.json` and cannot add allow rules |
| `hcpx rules [rm <n>] [--project]` | List rules with scope and index, or remove one |
| `hcpx approvals` | Requests waiting for a person |
| `hcpx preview <pack_id> <command> [-- k=v …]` | The approval card a person would see, and the exact command |
| `hcpx approve <id>` / `hcpx reject <id>` | Decide one request; an approval covers one run of exactly that command and those arguments for 15 minutes |
| `hcpx audit [--pack id] [--limit n]` | Recent decisions from `$HCP_HOME/audit.jsonl` |

`hcpx commands` adds `access` (allow, ask, deny) to every command.

### `hcpx help`

Usage text. With `--json`, `{ ok: true, help: string[] }`.

## Store

```text
$HCP_HOME/                 default ~/.hcp
  packs/<pack_id>/         installed pack directories (verbatim copies)
  skills/<pack_id>/SKILL.md
  state.json               { "activated": [], "connected": [], "grants"?: HcpGrant[] }
  access.json              user access settings (mode, packs, rules, approvals)
  approvals.json           pending and decided approvals
  audit.jsonl              one line per command decision
```

Without `grants` in `state.json`, the CLI uses permissive grants (every
capability, every pack). Set `grants` to test denials locally, as
[Testing](/docs/packs/check#testing) shows.

## Environment

`HCP_PROJECT_ID` sets the project scope for `brain`-land commands. Without it,
the enclosing git repository is the project.

| Variable | Effect |
|----------|--------|
| `HCP_HOME` | Store location |
| `HCP_CATALOG_URL` | Catalog search origin (default: the docs site's `/api/v1/packs`) |
| `HCP_MCP` | Set to `1` inside `hcpx mcp` and every CLI it starts; `hcpx mcp` refuses to recurse, and access commands refuse with `human_only` |
| `HCP_AGENT` | `1` marks an agent's shell; access commands refuse with `human_only`. `hcpx adopt claude` sets it |
| `HCP_MODE` | Access mode for this process: `manual`, `auto`, or `run` |
| `HCP_APPROVALS` | `human` or `agent`: who can satisfy an ask |
| `HCP_PROJECT_DIR` | Working directory for packs with `wrap.workspace.mode: project` |
| `HCP_CLI` | The command the host plugins start for each call, for example `hcpx` or `bun /path/to/packages/cli/src/hcp.ts` |
| `GITHUB_TOKEN` | Sent when `hcpx install github:…` falls back to downloading a tarball |
| `HCP_SANDBOX` | Set by the runtime in the child process: `sandbox` or `native-local`. Not read by the CLI |

The CLI always runs the reference runtime in sandbox mode, with `allowBins`
holding every installed pack's `wrap.bin`, the names `hcpx` and `hcp`, and the
executable running the CLI.

Source: https://harnesscontextprotocol.io/docs/reference/cli

---

# SDK

> Every export of the TypeScript reference SDK, from the `Hcp` facade and runtime drivers to lints, grants, access, check, host adoption and HarnessAgent.

`@harnesscontextprotocol/sdk` (TypeScript, ESM, Zod schemas) is the reference
implementation of the [Specification](/docs/specification), and the spec
governs wherever the two differ. Most hosts need only the
[`Hcp` facade](#hcp-facade) to load, activate and run packs, and the sections
after it list the lower-level exports.

```sh
bun add @harnesscontextprotocol/sdk
```

The package is a private preview on GitHub Packages, and
[Install the CLI](/docs/quickstart#install-the-cli) shows the `.npmrc` it needs.

## `Hcp` facade

```ts
import { Hcp } from '@harnesscontextprotocol/sdk'

const hcp = await Hcp.create({
  packDirs: ['./packs/transit', './packs/ledger'],
  sandbox: true,
  grants: [{ capability: 'verb.exec.read' }],
  principal: { id: 'u_1', kind: 'human' },
  tenant_id: 't_1',
  project_id: null,
})

await hcp.activate(['transit'])
const res = await hcp.exec('transit', 'plan', { from: 'Zurich', to: 'Bern' })
```

### `Hcp.create(opts): Promise<Hcp>`

| Option | Type | Default | Notes |
|--------|------|---------|-------|
| `runtime` | `HcpRuntimeDriver \| 'local' \| 'proprietary-stub'` | `'local'` | Pass your own driver to embed |
| `packDirs` | `string[]` | none | Required for `'local'` |
| `packs` | `LoadedHcpPack[]` | none | Required for `'proprietary-stub'` |
| `sandbox` | `boolean` | `true` | |
| `nativeLocal` | `boolean` | `false` | `native-local` only when `sandbox: false && nativeLocal: true` |
| `grants` | `HcpGrant[]` | `permissiveGrants()` | |
| `principal` | `HcpPrincipal` | `{ id: 'local-principal', kind: 'human' }` | |
| `tenant_id` | `string` | `'local-tenant'` | |
| `project_id` | `string \| null` | `null` | Required for land `brain` |
| `allowBins` | `string[]` | `['hcpx', 'hcp']` plus every pack's `wrap.bin` | Sandbox allow-list |
| `projectDir` | `string` | none | Working directory for packs with `wrap.workspace.mode: project`; without it those commands fail with `no_project` |
| `access` | `AccessConfig` | `{ mode: 'auto', approvals: 'agent' }` | Mode, per-pack modes, rules, approval authority. See [Access](/docs/concepts/access) |
| `approvals` | `ApprovalStore` | none | Where approval requests wait for a person |
| `audit` | `AuditSink` | none | Receives one `AuditEvent` per decision |
| `views` | `ApprovalViewOptions` | none | Extra approval-view inference and signal rules, tried before the defaults |

### Methods

| Method | Returns | Behaviour |
|--------|---------|-----------|
| `activate(ids)` | `Promise<void>` | Throws `cannot activate unknown pack: <id>` |
| `activatedPacks()` | `string[]` | |
| `connect(id)` | `Promise<void>` | Throws if not activated or the driver's connect fails |
| `exec(id, verb, args?, opts?)` | `Promise<HcpExecResult>` | Returns `ok: false` for unactivated packs; delegates to the driver. `opts.approval: { by: 'human', id? }` is the host's statement that its own UI collected a person's yes for exactly this call |
| `listVerbs(id)` | `Promise<string[]>` | |
| `skill(id)` | `Promise<string>` | `HARNESS.md` text or `''` |
| `listPacks()` | `Promise<LoadedHcpPack[]>` | |
| `setGrants(grants)` | `void` | Replaces the authz grants |
| `sandboxMode` | `HcpSandboxMode` | `'sandbox' \| 'native-local'` |
| `authz` | `HcpAuthzContext` | Principal, tenant, project, grants |

## Runtime drivers

```ts
type HcpRuntimeDriver = {
  id: string
  kind: 'reference-local' | 'proprietary-stub' | string
  listPacks(): Promise<LoadedHcpPack[]>
  getPack(pack_id: string): Promise<LoadedHcpPack | null>
  connect?(pack_id: string, authz: HcpAuthzContext): Promise<{ ok: boolean; error?: string }>
  exec(args: {
    pack_id: string; verb_key: string; args?: Record<string, unknown>
    authz: HcpAuthzContext; sandbox: HcpSandboxMode
    approval?: HcpHostApproval
  }): Promise<HcpExecResult>
}
```

- `createReferenceLocalRuntime({ packDirs, allowBins?, projectDir?, access?, approvals?, audit?, views? })`
  runs wrap and composite adapters with a process allow-list, a cwd jail, and
  `HCP_SANDBOX` set in the child, implementing the pipeline in
  [§6.3](/docs/specification/execute#63-pipeline).
- `createProprietaryStubRuntime({ packs, grants, principal, access?, approvals?, audit?, views? })`
  runs the same authorization pipeline with a stubbed adapter step, as the
  starting point for a hosted runtime ([Build runtimes](/docs/runtimes)).

Both come wrapped in access control. `Hcp.create({ runtime: myDriver })` wraps
your own driver the same way.

```ts
type HcpExecResult = {
  ok: boolean; pack_id: string; verb_key: string
  result?: unknown; error?: string; stdout?: string; stderr?: string
  code?: string                // stable reason when ok is false: unknown_argument, confirm_required, …
  hint?: string                // what to do next, written for an agent
  preview?: { command: string; args: Record<string, unknown>; cli?: string }   // on confirm_required
  approval?: HcpApprovalRequest                                                 // on confirm_required
  approval_id?: string
  access?: { mode: string; reason: string; approvals: string }
}
```

## Loading and linting

| Export | Purpose |
|--------|---------|
| `loadPackFromDir(dir)` | Reads the three files, parses `hcp` meta, returns `LoadedHcpPack` |
| `lintLoadedPack(pack)` | Runs `lintHarnessPack` + `lintHcpPolicy`; returns `HarnessPackLintIssue[]` |
| `lintHarnessPack({...})` | Manifest-level lints (presence, meta, wrap, plane, openapi, sense_only) |
| `lintHcpPolicy({...})` | Coverage lints (missing/extra verbs and stimuli, `surface_id` match) |
| `parseHarnessPackMeta(raw)` | Zod parse of the `hcp` block |
| `parseHcpPolicyFile(raw)` | Zod parse of `policy.json` |
| `getVerbPolicy(file, key)`, `getStimulusPolicy(file, ref)` | Lookups |
| `applyHarnessPackMeta(meta)` | Runtime projection (bin, cwd, env) from meta |
| `wrapAgentEnvFromMeta(meta, env?)` | Merged child environment |

```ts
type LoadedHcpPack = {
  pack_id: string; dir: string
  manifest: HcpPackManifest; policy: HcpPolicyFile
  harness_md: string; harness_present: boolean
  adapter_kind: HarnessAdapterKind; binding?: HarnessBinding; wrap?: HarnessWrapTarget
  verb_keys: string[]
}
```

## Schemas

These Zod schemas validate input and generate JSON Schema:

`HarnessPackMetaSchema`, `HarnessBindingSchema`, `HarnessAdapterKindSchema`,
`HarnessWrapTargetSchema`, `HarnessPassThroughSchema`, `HarnessOpenApiRefSchema`,
`HcpPolicyFileSchema`, `HcpPolicyEntrySchema`, `HcpPolicyOperationSchema`,
`HcpPrivacyLandSchema`.

The constants are `HCP_PROTOCOL_ID = 'hcp'` and `HCP_VERSION = 1`, and
[Schemas](/docs/reference/schemas) prints the JSON Schema generated from them.

## Grants

```ts
const HCP_CAPABILITIES = ['pack.install', 'pack.connect', 'verb.exec.read', 'verb.exec.write', 'sense.enable'] as const

type HcpGrant = { capability: HcpCapability; pack_id?: string; verb_keys?: string[] }
type HcpPrincipal = { id: string; kind: 'human' | 'service' | 'agent' | 'mothership' }
type HcpAuthzContext = { principal: HcpPrincipal; tenant_id: string; project_id: string | null; grants: HcpGrant[] }
type HcpAuthorize = (ctx: HcpAuthzContext, req: HcpExecRequest) => HcpAuthzDecision
```

`defaultAuthorize` implements [§9.4 and §9.5](/docs/specification/authorization):
land `brain` requires `project_id`, the Policy `operation` picks the capability,
and grant matching narrows by `pack_id` and then by `verb_keys`.
`permissiveGrants()` returns one unscoped grant per capability.

## Access and approvals

These exports implement the access layer of draft
[RFC 0006](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0006-access-modes.md)
and the approval options of draft RFC 0007, and [Access](/docs/concepts/access)
explains the behaviour.

| Export | Purpose |
|--------|---------|
| `withAccess(driver, opts)` | Puts argument checks, grants (with `opts.authorize`), the access decision, and audit in front of any driver. Wrapping twice is a no-op |
| `gateCommand({ pack, verb_key, args, access?, approvals?, approval?, views? })` | The decision for one call: `allow` (with who satisfied it), `ask` (with the `HcpApprovalRequest` and the result to return), or `deny` |
| `decideAccess(req, config?)` | Mode and rules only: allow, ask, or deny, with `code` and `reason` |
| `describeCommand(pack, verb_key, args)` | Pack name, summary, arguments with descriptions, and the exact CLI line |
| `mergeAccess({ user, project, env })`, `loadEffectiveAccess(files, env?)` | Layered settings; project settings only tighten |
| `accessFiles(home, projectRoot)`, `readAccessFile(path)`, `writeAccessFile(path, config)`, `envAccess(env?)` | The `access.json` files and `HCP_MODE` / `HCP_APPROVALS` |
| `requestApproval`, `consumeApproval`, `decideApproval`, `memoryApprovalStore()`, `fileApprovalStore(path)` | Approval requests: one run, exact arguments, 15 minutes |
| `fileAuditSink(path)`, `readAudit(path, filter?)`, `argsHash(args)` | The audit log and the argument hash it records |
| `approvalView(pack, verb_key, args, opts?)` | The typed card: `kind`, `title`, `fields`, `rest`, `signals`, `sources`, `inferred` |
| `suggestPreview(pack, verb_key, argNames)`, `lintApprovalPreview(pack, verb_key, argNames)` | The `preview` an author should declare, and the problems with a declared one |
| `formatApprovalView(view, cli?)` | The card as plain lines, as `hcpx preview` prints it |
| `DEFAULT_VIEW_RULES`, `DEFAULT_SIGNAL_RULES`, `APPROVAL_VIEW_KINDS`, `APPROVAL_VIEW_FIELDS` | The inference data and the kinds hosts draw |
| `approvalOptions({ pack_id, verb_key, policyConfirm, view, args })` | `allow_once`, `reject_once`, and, where allowed, `allow_session` with its rule |
| `argsFromArgv(pack, verb_key, argv)` | Arguments from a command line an agent wrote |
| `verbArguments(pack, verb_key)` | A command's arguments: name, required, description, example |

## Command titles

| Export | Purpose |
|--------|---------|
| `commandTitle(pack, verb_key, args, result?)` | `{ live?, done? }` for this call, or `null` when the command declares no title. Pass the JSON result to fill `{{result.…}}` in `done` |
| `lintCommandTitle(pack, verb_key, argNames)` | The problems `hcpx check` reports as M026 |

[Titles](/docs/concepts/command#titles) explains how a pack declares them.

## Check and eval

The `check` entry point exposes `hcpx check` and `hcpx eval` as functions:

```ts
import { checkPack, formatCheckForAgent, initEvalFile } from '@harnesscontextprotocol/sdk/check'

await initEvalFile('./packs/gh')                    // { written, file, cases }; never overwrites
const report = await checkPack('./packs/gh', { probe: true })
if (!report.ok) console.log(formatCheckForAgent(report))
```

| Export | Purpose |
|--------|---------|
| `checkPack(dir, opts?)` | The full report: `ok`, `score`, `grade`, `summary`, `categories`, `findings`, `probes`, `evals`, `layers`, `next`. Options: `probe`, `eval` (default: when the file exists), `timeoutMs` (default 10000), `docsBase`, `displayDir` |
| `readEvalCases(dir)` | The cases of `hcp.eval.json`, or `null` when there is no file |
| `scaffoldEvalCases(pack)`, `initEvalFile(dir)` | The starter cases `--init-evals` writes |
| `formatCheckReport(report)`, `formatCheckForAgent(report)` | The terminal report and the Markdown fix list |
| `resolveWrapBin(pack)` | `wrap.bin` resolved the way the runtime does: in the pack, else on `PATH` |
| `EVAL_FILE` | `'hcp.eval.json'` |

```ts
type EvalCase = {
  name: string
  verb: string
  args?: Record<string, unknown>
  expect?: { ok?: boolean; error?: string; contains?: string[]; not_contains?: string[]
             json?: boolean; max_ms?: number; max_tokens?: number }
}
```

[Check and evaluate](/docs/packs/check) lists the rules and how cases are graded.

## Availability

`buildPackAvailability({ pack, installed, activated, connected, grants, source_available })`
returns `HcpPackAvailability` with a per-command `executable` and a `reason`
(`not installed`, `not activated`, `missing <capability>`). `hcpx availability`
is built on it.

## Generation and adoption

| Export | Purpose |
|--------|---------|
| `generateHarnessSurfaces(hcp, opts?)` | Prompt block, tools, skills, per-host configs from activated packs. `opts.cliBin` (default `'hcp'`) is the command written into host config |
| `hcpToolName(pack_id, verb_key)` | `<pack_id>__<verb_key>`, dots as underscores |
| `adoptOpenCode(surfaces, cwd, { write, pluginEntry })` | Writes `opencode.json` + plugin wiring |
| `adoptCursor(surfaces, cwd, { write })` | Writes `.cursor/` rules and skills |
| `adoptAgent`, `adoptMcp`, `adoptGrok` `(surfaces, cwd, { write })` | The generic plugin, the MCP export, and the Grok files |
| `refuseProductRuntimeAdopt(cwd)` | Throws when the workspace has a `.hcp/no-adopt` file |
| `buildHcpContextBundle(hcp, opts?)` | Stable prefix + leads + `hcp__read_skill` tool. See [Context optimization](/docs/hosts#context-optimization) |
| `hcpToToolLoopAgent(hcp, opts)` | AI SDK tool set from an `Hcp` |

## Host intake

A host that consumes a generated surface manifest, and does not embed the
runtime, uses `loadSurfaceManifest(cwd)`, `loadHostIntake(cwd, opts?)`,
`resolveSurfaceTool(manifest, toolName)` and `execVerbViaCli(pack_id, verb_key, { args, cli, home, cwd })`.
The last runs `hcpx exec`, or the command in `cli`, and parses the JSON, failing
with `non-json cli output` when stdout is not JSON.

## `HarnessAgent`

```ts
import { HarnessAgent } from '@harnesscontextprotocol/sdk'

const agent = await HarnessAgent.create({
  runtime: 'ai-sdk', model, packDirs: ['./packs/transit'],
  grants: [{ capability: 'verb.exec.read' }],
})
for await (const ev of agent.run('Plan Zurich → Bern at 9')) { … }
```

[AI SDK](/docs/hosts/ai-sdk) covers the options, the events and the approval flow.

## Store helpers

`resolveHcpHome(env?)`, `ensureHcpHome()`, `ensureBootstrapPack(dir, layout)`,
`installPackFromDir(src, layout)`, `uninstallPack(id, layout)`,
`listInstalledPackDirs(layout)`, `readCliState(layout)`, `writeCliState(state, layout)`
and `installPackSkill(pack, layout)` back the [CLI store](/docs/reference/cli#store).

## Deprecated aliases

`Systems*` policy schema names and `parseSystemsSurfacePolicyFile` /
`lintSystemsSurfacePolicy` remain exported and map to their `Hcp*` equivalents.

Source: https://harnesscontextprotocol.io/docs/reference/sdk

---

# API docs

> How agents read this site by machine: the `/api/v1` HTTP API with its OpenAPI file, Markdown twins of every page and a read-only MCP server.

These interfaces serve the documentation and the pack catalog, and they are
separate from any pack runtime. They are read-only and need no key, so an agent
that has to read the docs should start here.

## HTTP (`/api/v1`)

| Operation | Method | What it returns |
|-----------|--------|-----------------|
| `/api/v1/search?query=` | GET | Full-text hits over docs |
| `/api/v1/pages/{slug}` | GET | Markdown for one docs page |
| `/api/v1/packs?query=` | GET | Community pack catalog search (`class`, `tag`, `status` filters) |
| `/api/v1/packs/{id}` | GET | One catalog entry |

The current major version is v1, and breaking changes will go to `/api/v2`.
The older aliases `/api/search` and `/api/md` send `Deprecation` and `Sunset`
headers.

Errors come as RFC 9457 `application/problem+json` with a stable `code`, a
`detail` and a `hint`. The rate limit is 120 requests per 60 seconds, reported
in the `RateLimit` and `RateLimit-Policy` headers, with `Retry-After` on a 429.
[OpenAPI](/openapi.json) describes the whole API.

## Markdown twins

Every docs URL has a Markdown version: send `Accept: text/markdown` or append
`.md`. The docs hub is [`/docs.md`](/docs.md).

## JSON Schemas

The seven schemas live under `/schemas/` at their `$id` URLs, for example
`/schemas/1/surface.json`, served as `application/schema+json` to any origin.
[Schemas](/docs/reference/schemas) lists them all.

## MCP

The MCP server speaks Streamable HTTP at [`/mcp`](/mcp) and offers the tools
`list_pages`, `search` and `get_page`, and its server card is at
[`/mcp/server-card`](/mcp/server-card). The pack catalog stays on HTTP at
[`/api/v1/packs`](/api/v1/packs).

## Authentication

None of these interfaces takes an API key. Agents should use the HTTP and MCP
interfaces documented here whenever they can reach the origin.

Source: https://harnesscontextprotocol.io/docs/reference/http-api

---

# Schemas

> Seven JSON Schemas, each live at its `$id`, for validating `surface.json`, `policy.json`, the envelopes, grants, availability and `hcp.eval.json`.

These schemas are the machine-readable form of the
[Specification](/docs/specification), derived from the Zod definitions in the
[SDK](/docs/reference/sdk#schemas). They use JSON Schema draft 2020-12, and
each `$id` is a live URL that serves the schema as `application/schema+json`
to any origin, so an editor or validator can fetch it directly. The `$id`s stay
stable within HCP/1.x, and the files are the blocks on this page, so the two
cannot drift. Every gold pack's `surface.json`, `policy.json` and
`hcp.eval.json` validates against them. Where a schema and the specification
text disagree, the text governs, so open an issue.

| Schema | URL |
|--------|-----|
| Manifest | [`/schemas/1/surface.json`](https://harnesscontextprotocol.io/schemas/1/surface.json) |
| Policy | [`/schemas/1/policy.json`](https://harnesscontextprotocol.io/schemas/1/policy.json) |
| Exec envelope | [`/schemas/1/exec.json`](https://harnesscontextprotocol.io/schemas/1/exec.json) |
| Result envelope | [`/schemas/1/result.json`](https://harnesscontextprotocol.io/schemas/1/result.json) |
| Grant | [`/schemas/1/grant.json`](https://harnesscontextprotocol.io/schemas/1/grant.json) |
| Availability | [`/schemas/1/availability.json`](https://harnesscontextprotocol.io/schemas/1/availability.json) |
| Eval cases | [`/schemas/hcp.eval.json`](https://harnesscontextprotocol.io/schemas/hcp.eval.json) |

## `surface.json`

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://harnesscontextprotocol.io/schemas/1/surface.json",
  "type": "object",
  "required": ["id", "verbs"],
  "properties": {
    "id":      { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[a-z0-9][a-z0-9-]*$" },
    "name":    { "type": "string" },
    "cliBin":  { "type": "string" },
    "kind":    { "$ref": "#/$defs/adapterKind" },
    "adapter_kind": { "$ref": "#/$defs/adapterKind" },
    "binding": { "$ref": "#/$defs/binding" },
    "wrap":    { "$ref": "#/$defs/wrap" },
    "openapi": { "$ref": "#/$defs/openapi" },
    "pass_through": { "$ref": "#/$defs/passThrough" },
    "plane":   { "enum": ["runtime", "control"] },
    "transports": { "type": "array", "maxItems": 4, "items": { "enum": ["cli", "json_exec", "mcp"] } },
    "skill_url": { "type": "string", "format": "uri" },
    "harness": { "type": "string", "deprecated": true },
    "verbs": {
      "type": "object",
      "propertyNames": { "pattern": "^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)*$" },
      "additionalProperties": { "$ref": "#/$defs/command" }
    },
    "stimulusTriggers": {
      "type": "array",
      "items": { "type": "object", "required": ["ref"], "properties": { "ref": { "type": "string", "minLength": 1 } }, "additionalProperties": false }
    },
    "hcp": { "$ref": "#/$defs/meta" }
  },
  "$defs": {
    "adapterKind": { "enum": ["composite", "wrap", "openapi", "sense_only"] },
    "binding": {
      "type": "object", "additionalProperties": false,
      "properties": {
        "family": { "type": "string", "minLength": 1, "maxLength": 128 },
        "mode":   { "enum": ["user", "mothership", "local", "public", "mock"] },
        "hub":    { "enum": ["keyring", "local-credential", "public", "none"] },
        "connect_provider_id": { "type": "string", "minLength": 1, "maxLength": 128 }
      }
    },
    "wrap": {
      "type": "object", "additionalProperties": false, "required": ["bin"],
      "properties": {
        "bin": { "type": "string", "minLength": 1, "maxLength": 128 },
        "argv_template": { "type": "array", "maxItems": 64, "items": { "type": "string" } },
        "agent_env": { "type": "object", "additionalProperties": { "type": "string" } },
        "workspace": { "$ref": "#/$defs/workspace" },
        "json_flag": { "type": "array", "maxItems": 8, "items": { "type": "string", "maxLength": 64 } }
      }
    },
    "workspace": {
      "type": "object", "additionalProperties": false,
      "properties": {
        "mode": { "enum": ["none", "ephemeral", "workspace", "project"] },
        "home_env": { "type": "array", "maxItems": 8, "items": { "type": "string", "minLength": 1, "maxLength": 64 } },
        "file_flags": { "type": "array", "maxItems": 16, "items": { "type": "string", "minLength": 1, "maxLength": 32 } }
      }
    },
    "openapi": {
      "type": "object", "additionalProperties": false,
      "properties": { "url": { "type": "string", "format": "uri" }, "path": { "type": "string", "minLength": 1, "maxLength": 512 } },
      "anyOf": [{ "required": ["url"] }, { "required": ["path"] }]
    },
    "passThrough": {
      "type": "object", "additionalProperties": false, "required": ["allowedSubcommands", "maxArgv"],
      "properties": {
        "allowedSubcommands": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } },
        "maxArgv": { "type": "integer", "minimum": 1 }
      }
    },
    "command": {
      "type": "object",
      "properties": {
        "description": { "type": "string" },
        "args": { "type": "object", "description": "JSON Schema: properties (description, examples, x-position) and required" },
        "cli": { "type": "array", "items": { "type": "string" } },
        "policy": { "type": "string" },
        "json_flag": { "type": "array", "items": { "type": "string" } },
        "title": {
          "type": "object",
          "properties": {
            "live": { "$ref": "#/$defs/templates" },
            "done": { "$ref": "#/$defs/templates" }
          }
        },
        "preview": {
          "type": "object", "required": ["kind"],
          "properties": {
            "kind": { "enum": ["message", "payment", "event", "removal", "publish", "record", "fields"] },
            "title": { "type": "string" },
            "fields": { "type": "object", "additionalProperties": { "type": "string" } }
          }
        }
      }
    },
    "templates": {
      "oneOf": [
        { "type": "string", "minLength": 1 },
        { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }
      ]
    },
    "meta": {
      "type": "object", "additionalProperties": false,
      "required": ["hcp", "pack_id", "adapter_kind"],
      "properties": {
        "hcp": { "const": 1 },
        "pack_id": { "type": "string", "minLength": 1, "maxLength": 128 },
        "adapter_kind": { "$ref": "#/$defs/adapterKind" },
        "binding": { "$ref": "#/$defs/binding" },
        "wrap": { "$ref": "#/$defs/wrap" },
        "openapi": { "$ref": "#/$defs/openapi" },
        "pass_through": { "$ref": "#/$defs/passThrough" },
        "plane": { "enum": ["runtime", "control"] },
        "transports": { "type": "array", "maxItems": 4, "items": { "enum": ["cli", "json_exec", "mcp"] } },
        "skill_url": { "type": "string", "format": "uri" }
      }
    }
  }
}
```

Lint enforces what the schema cannot express ([§3.6](/docs/specification/manifest)):
`hcp.pack_id === id`, the `wrap` adapter requires `wrap.bin`, the `openapi`
adapter requires the `openapi` block, `sense_only` requires one trigger, wrap
requires named commands or `pass_through`, and the `control` plane requires
Binding `mothership`. `hcpx check` adds two more: `preview` and `title`
placeholders must be arguments of the command, and `{{result.…}}` belongs in
`title.done` only.

## `policy.json`

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://harnesscontextprotocol.io/schemas/1/policy.json",
  "type": "object", "additionalProperties": false,
  "required": ["surface_id", "verbs"],
  "properties": {
    "surface_id": { "type": "string", "minLength": 1, "maxLength": 128 },
    "version": { "const": 1, "default": 1 },
    "verbs":   { "type": "object", "additionalProperties": { "$ref": "#/$defs/entry" } },
    "stimuli": { "type": "object", "additionalProperties": { "$ref": "#/$defs/entry" }, "default": {} }
  },
  "$defs": {
    "entry": {
      "type": "object", "additionalProperties": false,
      "required": ["operation", "privacy"],
      "properties": {
        "operation": { "enum": ["read", "write"] },
        "privacy": {
          "type": "object", "additionalProperties": false, "required": ["land"],
          "properties": { "land": { "enum": ["personal", "brain", "local"] } }
        },
        "confirm": { "oneOf": [{ "type": "boolean" }, { "const": "always" }] }
      }
    }
  }
}
```

Lint enforces the coverage rules of [§4.6](/docs/specification/policy): the
`verbs` keys are exactly the manifest's commands, the `stimuli` keys are exactly
its triggers, and `surface_id === id`.

## Exec envelope

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://harnesscontextprotocol.io/schemas/1/exec.json",
  "type": "object", "required": ["verb_key"],
  "properties": {
    "pack_id":    { "type": "string" },
    "surface_id": { "type": "string", "description": "Alias of pack_id" },
    "verb_key":   { "type": "string" },
    "args": {
      "type": "object", "default": {},
      "properties": {
        "confirm": { "type": "boolean" },
        "apply":   { "type": "boolean" },
        "mode":    { "enum": ["mock"] }
      }
    }
  },
  "anyOf": [{ "required": ["pack_id"] }, { "required": ["surface_id"] }]
}
```

## Result envelope

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://harnesscontextprotocol.io/schemas/1/result.json",
  "type": "object", "required": ["ok", "pack_id", "verb_key"],
  "properties": {
    "ok":       { "type": "boolean" },
    "pack_id":  { "type": "string" },
    "verb_key": { "type": "string" },
    "result":   {},
    "error":    { "type": "string" },
    "code":     { "type": "string", "description": "Stable reason when ok is false" },
    "hint":     { "type": "string", "description": "What to do next, written for an agent" },
    "preview":  { "type": "object", "description": "On confirm_required: command, args, cli" },
    "approval": { "type": "object", "description": "On confirm_required: the request a host shows a person" },
    "approval_id": { "type": "string" },
    "access":   { "type": "object", "description": "On confirm_required: mode, reason, approvals" },
    "stdout":   { "type": "string" },
    "stderr":   { "type": "string" }
  },
  "if":   { "properties": { "ok": { "const": false } } },
  "then": { "required": ["error"] }
}
```

[Errors](/docs/reference/errors#error-codes) lists the `code` values, and
[Access](/docs/concepts/access#in-your-own-runtime) describes the approval
request.

## Grant

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://harnesscontextprotocol.io/schemas/1/grant.json",
  "type": "object", "required": ["capability"],
  "properties": {
    "capability": { "enum": ["pack.install", "pack.connect", "verb.exec.read", "verb.exec.write", "sense.enable"] },
    "pack_id":    { "type": "string" },
    "verb_keys":  { "type": "array", "items": { "type": "string" } }
  }
}
```

## Availability

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://harnesscontextprotocol.io/schemas/1/availability.json",
  "type": "object",
  "required": ["pack_id", "status", "installed", "activated", "connected", "verbs", "capabilities"],
  "properties": {
    "pack_id": { "type": "string" },
    "status": { "type": "array", "items": { "enum": ["available", "installed", "activated", "connected", "executable", "unavailable"] } },
    "installed": { "type": "boolean" }, "activated": { "type": "boolean" }, "connected": { "type": "boolean" },
    "verbs": { "type": "array", "items": {
      "type": "object", "required": ["verb_key", "operation", "land", "executable"],
      "properties": {
        "verb_key": { "type": "string" }, "operation": { "enum": ["read", "write"] },
        "land": { "enum": ["personal", "brain", "local"] }, "executable": { "type": "boolean" },
        "reason": { "type": "string" }
      } } },
    "capabilities": { "type": "array", "items": {
      "type": "object", "required": ["capability", "granted"],
      "properties": {
        "capability": { "enum": ["pack.install", "pack.connect", "verb.exec.read", "verb.exec.write", "sense.enable"] },
        "granted": { "type": "boolean" }
      } } },
    "adapter_kind": { "enum": ["composite", "wrap", "openapi", "sense_only"] },
    "binding_mode": { "enum": ["user", "mothership", "local", "public", "mock"] },
    "skill_present": { "type": "boolean" },
    "connect_needed": { "type": "boolean", "description": "Commands fail until someone runs hcpx connect" },
    "ready": { "type": "boolean", "description": "Activated and, if it needs a login, connected" }
  }
}
```

## `hcp.eval.json`

This file holds the cases that
[`hcpx check` and `hcpx eval`](/docs/packs/check#eval-cases) run, and
`hcpx check --init-evals` writes this `$id` as its `$schema`.

```json
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://harnesscontextprotocol.io/schemas/hcp.eval.json",
  "type": "object",
  "properties": {
    "$schema": { "type": "string" },
    "cases": {
      "type": "array",
      "items": {
        "type": "object", "required": ["name", "verb"],
        "properties": {
          "name": { "type": "string" },
          "verb": { "type": "string", "description": "A command in surface.json" },
          "args": { "type": "object" },
          "expect": {
            "type": "object",
            "properties": {
              "ok": { "type": "boolean" },
              "error": { "type": "string" },
              "contains": { "type": "array", "items": { "type": "string" } },
              "not_contains": { "type": "array", "items": { "type": "string" } },
              "json": { "type": "boolean" },
              "max_ms": { "type": "number" },
              "max_tokens": { "type": "number" }
            }
          }
        }
      }
    }
  }
}
```

## Generating from source

Zod generates each schema from the SDK's definitions:

```ts
import { z } from 'zod'
import { HarnessPackMetaSchema, HcpPolicyFileSchema } from '@harnesscontextprotocol/sdk'

z.toJSONSchema(HarnessPackMetaSchema)
z.toJSONSchema(HcpPolicyFileSchema)
```

Source: https://harnesscontextprotocol.io/docs/reference/schemas

---

# Errors

> The lint issue kinds, error codes and runtime error strings the reference implementation emits, each with its cause and the fix.

Lint issues are structured objects from `hcpx lint`, and runtime errors are
the `error` string of a result envelope with `ok: false`. Both stay stable
within HCP/1.x, so hosts may match on them. Code that handles a failure should
start from [Error codes](#error-codes), which most failures carry.

## Lint issues

Each issue is `{ kind, key, message }`.

| `kind` | `key` | Cause | Fix |
|--------|-------|-------|-----|
| `missing_surface` | `surface.json` | No manifest | Add `surface.json` |
| `missing_harness` | `HARNESS.md` | No Skill file | Add `HARNESS.md` |
| `missing_policy` | `policy.json` | No Policy file | Add `policy.json` |
| `missing_verb` | `<verb_key>` | Manifest command without a Policy entry | Add the entry |
| `missing_verb` | `<pack_id>` | `policy.surface_id` ≠ `id` | Make them equal |
| `extra_verb` | `<verb_key>` | Policy entry with no manifest command | Remove it or declare the command |
| `missing_stimulus` | `<ref>` | Trigger without `stimuli` entry | Add the entry |
| `extra_stimulus` | `<ref>` | `stimuli` entry without a trigger | Remove or declare |
| `write_stimulus` | `<ref>` | Trigger whose Policy `operation` is not `read` | Set it to `read`; a trigger never writes |
| `invalid_meta` | `hcp` | `hcp` block fails schema | Read the Zod message |
| `invalid_meta` | `pack_id` | `hcp.pack_id` ≠ `id` | Make them equal |
| `invalid_meta` | `openapi` | `openapi` adapter without ref | Add `openapi.url` or `.path` |
| `invalid_meta` | `sense_only` | `sense_only` with no triggers | Declare a trigger |
| `binding` | `wrap` | `wrap` adapter without `wrap.bin` | Add `wrap.bin` |
| `binding` | `plane` | `control` plane with non-`mothership` Binding | Set `binding.mode: mothership` |
| `wrap` | `verbs` | Wrap pack with neither named commands nor `pass_through` | Declare commands |
| `skill` | `<verb_key>` | The Skill never names a command, or its hot-path table names one the manifest does not declare | Make the Skill and the manifest agree |
| `skill` | `path` | A host-absolute path in `HARNESS.md` | Use a relative path or `~/.hcp/…` |
| `skill` | `secret` | Something that looks like a credential in `HARNESS.md` | Remove it and rotate it |
| `skill` | `mcp` | The teaching part presents MCP as the install or invoke path | Teach the CLI; mention MCP under Anti-patterns only |

The `skill` rules are the four-face lint that [Skill](/docs/concepts/skill#rules)
describes. `hcpx install` fails with `lint failed: <messages joined by "; ">`
when any issue exists.

## Error codes

Most failures from the runtime and the CLI carry a stable `code`, a readable
`error`, a `hint` written for an agent and often `next`, the exact commands to
run. Agents should branch on `code`, never on the message text.

| `code` | Meaning | What an agent should do |
|--------|---------|------------------------|
| `confirm_required` | The command asks: Policy `confirm`, access mode `manual`, or an ask rule, and nothing satisfied it. Nothing ran; `preview` holds the command, its arguments, and the rendered CLI line, and `approval` the card and the answers a host may offer | With approvals `agent`, re-run with `confirm=true` only if the user approved this exact action with these values, in their request or after seeing `preview`. With approvals `human`, ask the user to approve it (`approval_id`) and run the same command again. Otherwise show `preview` and wait |
| `missing_argument` | A required argument has no value | Supply it; the hint lists every argument |
| `unknown_argument` | An argument name the command does not take | Use the suggested name ("did you mean") |
| `unknown_command` | The pack has no such command | Pick from the listed commands |
| `missing_command` | `hcpx exec <pack>` without a command | The response includes the pack's commands |
| `missing_pack` | `hcpx exec` without a pack | Run `hcpx commands` |
| `unknown_pack` | No such pack is installed | Run a `next` command: the suggested pack, or `hcpx search` |
| `not_activated` | Installed but not activated | `hcpx activate <pack_id>` |
| `not_installed` | `hcpx activate` on a pack that is not installed | `hcpx install` first |
| `no_pack` | `hcpx install` on a path without `surface.json` | Point at the pack directory, or scaffold one |
| `denied` | The Principal lacks a grant | Stop and tell the user |
| `denied_by_rule` | The user's [access](/docs/concepts/access) rules forbid this command | Tell the user; do not reach the same result another way |
| `human_only` | An access command (`approve`, `reject`, `mode`, `allow`, `ask`, `deny`, `rules rm`) ran inside an agent | Show the user the command; they run it |
| `bad_approval` | `hcpx approve` or `reject` on an unknown, used, or expired id | See `hcpx approvals`; ask the agent to run the command again |
| `bad_mode` / `bad_pattern` / `project_cannot_allow` | Invalid access settings | Follow the hint |
| `usage` | A CLI command is missing a required argument | Follow the hint |
| `bad_argument` / `bad_args_json` | An argument is not `key=value`, or `--args` is not JSON | Fix the syntax as the hint shows |
| `no_project` | A pack with `wrap.workspace.mode: project` was called by a runtime that has no project directory | Run it through a local runtime started in the project |
| `bin_not_found` | `hcpx wrap` could not find the CLI on `PATH` | Install it, or pass its path |
| `wrap_failed` | `hcpx wrap` could not write the pack | Pick another `--id` or `--dir` |

## Runtime errors

### Lifecycle

| `error` | Cause |
|---------|-------|
| `unknown pack <id>` | `pack_id` not in the runtime's catalog |
| `pack <id> not activated` | Exec or connect before activate |
| `cannot activate unknown pack: <id>` | Activate an id not in the catalog (thrown) |
| `pack <id> is not installed` | CLI activate of an uninstalled pack (`not_installed`) |
| `pack <id> is installed but not activated` | CLI exec of an installed, inactive pack (`not_activated`) |
| `not activated: <id> — hcpx activate <id>` | CLI connect of an unactivated pack |
| `pack not installed: <id>` | Uninstall of an unknown pack (thrown) |
| `cannot uninstall bootstrap pack hcp-cli` | Uninstall of the bootstrap pack `hcp-cli` |
| `connect failed` | Driver connect returned `ok: false` without a message |

### Policy and confirm

| `error` | Cause |
|---------|-------|
| `no policy for <verb_key>` | Command lacks a Policy entry at exec time |
| `confirm required` | The command asks and nothing satisfied the ask (`confirm_required`; see [Access](/docs/concepts/access#approvals)) |

### Authorization

Each message begins with `denied: `.

| `error` | Cause |
|---------|-------|
| `denied: privacy.land brain requires project_id` | Land `brain` with no project scope |
| `denied: missing verb.exec.read for <pack>.<verb>` | No matching read grant |
| `denied: missing verb.exec.write for <pack>.<verb>` | No matching write grant |
| `denied: missing pack.connect` | Connect without grant |
| `denied: missing pack.install` | Install without grant |
| `denied: missing sense.enable` | Sense delivery without grant |

### Sandbox and adapter

| `error` | Cause |
|---------|-------|
| `sandbox: bin "<bin>" not allowlisted` | `wrap.bin` not in the allow-list (exit `127`) |
| `exit <n>` | Child exited non-zero with empty stderr |
| `<stderr>` | Child exited non-zero; stderr returned verbatim |
| `adapter <kind> not executable in reference runtime` | `openapi` or `sense_only` exec on the reference runtime |

### SDK construction (thrown)

| Message | Cause |
|---------|-------|
| `local runtime requires packDirs` | `Hcp.create()` with no `packDirs` |
| `proprietary-stub requires packs[]` | Stub runtime without loaded packs |
| `HarnessAgent ai-sdk runtime requires model` | `HarnessAgent` with the `ai-sdk` runtime and no `model` |

### Host intake

| `error` | Cause |
|---------|-------|
| `non-json cli output` | `execVerbViaCli` could not parse stdout; `raw` carries it |
| `no skill for <pack_id>` | `hcp__read_skill` for an unknown or unactivated pack |

## Availability reasons

When a command's `executable` is `false`, its `reason` is `not installed`,
`not activated` or `missing <capability>`.

## Roadmap

The `code` values above belong to the reference implementation. The
specification has no registry of error codes yet, and the
[Roadmap](/docs/community/roadmap#proposed-for-hcp12) proposes one for HCP/1.2.

Source: https://harnesscontextprotocol.io/docs/reference/errors

---

# Glossary

> Each HCP term in one line, from Harness Pack and Binding to Sense, land and the four faces, with a link to the page that explains it.

Use this table for quick lookup. The normative definitions live in
[§1 Terminology](/docs/specification/terminology), and each row links to the
page that covers the term in full.

| Term | One line | More |
|------|----------|------|
| HCP | Harness Context Protocol: installs a capability into an agent harness as one pack with two loops | [What is HCP](/docs/introduction/what-is-hcp) |
| Harness | The environment an agent runs in: model, tools, memory, policy, runtime | [Concepts](/docs/concepts) |
| Harness Pack | Directory of `surface.json`, `policy.json`, `HARNESS.md` | [Pack](/docs/concepts/pack) |
| Manifest | `surface.json`: identity, adapter, binding, commands, triggers | [Manifest](/docs/packs/manifest) |
| Command | Named operation a pack exposes; wire name `verb_key` | [Command](/docs/concepts/command) |
| `verb_key` | Wire identifier of a command (`plan`, `setup.connect`) | [§3](/docs/specification/manifest) |
| Twin face | A command's CLI form and JSON form produce the same result | [§6.5](/docs/specification/execute) |
| Execute | Inbound loop: an Invoker runs a command | [§6](/docs/specification/execute) |
| Sense | Outbound loop: a pack announces a world change | [§7](/docs/specification/sense) |
| Trigger | Declared Sense event, `stimulusTriggers[].ref` | [Triggers](/docs/concepts/sense#triggers) |
| Policy | Per command or trigger: `operation`, `privacy.land`, `confirm` | [Policy](/docs/concepts/policy) |
| Operation | The effect class of a command, `read` or `write` | [§4.3](/docs/specification/policy) |
| Land | The data domain a command touches: `personal` \| `brain` \| `local` | [§4.4](/docs/specification/policy) |
| Confirm | Gate on writes, satisfied by `args.confirm` or `args.apply` | [§4.5](/docs/specification/policy) |
| Access | The user's or operator's setting for which commands run without asking: mode, rules, approvals | [Access](/docs/concepts/access) |
| Access mode | `manual` \| `auto` \| `run`, how much runs without asking | [Access](/docs/concepts/access#modes) |
| Approval view | The typed card a person sees when a command asks, built from the pack and the call's arguments | [Access](/docs/concepts/access#what-the-card-shows) |
| Command title | The pack's words for a step, `live` while it runs and `done` after | [Command](/docs/concepts/command#titles) |
| Binding | How a command gets credentials at exec time: mode and hub, never a secret | [Binding](/docs/concepts/binding) |
| Mode | `public` \| `user` \| `local` \| `mothership` \| `mock` | [§8.2](/docs/specification/binding) |
| Hub | Component kind that mints: `keyring`, `local-credential`, `public`, `none` | [§8.3](/docs/specification/binding) |
| Family | Group of packs sharing one consent | [§8.3](/docs/specification/binding) |
| Mint | The runtime gets a credential from the hub for one exec | [§8.4](/docs/specification/binding) |
| Connect | Lifecycle step that establishes Binding; capability `pack.connect` | [§8.5](/docs/specification/binding) |
| Principal | The acting identity: human, service, agent or mothership | [Principal](/docs/concepts/binding#principal) |
| Mothership | Operator or fleet identity; the Binding mode for control-plane packs | [§8.2](/docs/specification/binding) |
| Tenant | Organisational scope from the session, never from args | [§9.2](/docs/specification/authorization) |
| Project | Sub-scope required for land `brain` | [§9.2](/docs/specification/authorization) |
| Capability | One of five grant axes | [§9.4](/docs/specification/authorization) |
| Grant | `{ capability, pack_id?, verb_keys? }` held by a Principal | [Authorization](/docs/concepts/access#authorization) |
| Invoker | Who calls Execute: agent tool, API, UI, human CLI | [Invoker](/docs/runtimes/execution#invoker) |
| Adapter | How a command runs: `wrap`, `composite`, `openapi`, `sense_only` | [Adapter](/docs/concepts/adapter) |
| Wrap | Adapter that spawns `wrap.bin` with rendered argv | [Wrap a CLI](/docs/packs/wrap-a-cli) |
| Composite | In-process adapter | [Adapter](/docs/concepts/adapter) |
| Pass-through | Bootstrap-only whole-CLI wrap with allowed subcommands | [§3.3](/docs/specification/manifest) |
| Plane | `runtime` (user work) or `control` (operator lifecycle) | [§2.5](/docs/specification/pack-model) |
| Runtime | Loads, authorises, binds, executes, routes Sense | [Build runtimes](/docs/runtimes) |
| Reference runtime | `createReferenceLocalRuntime` in the SDK | [SDK](/docs/reference/sdk#runtime-drivers) |
| Host | An agent product that adopts HCP | [Adopt in hosts](/docs/hosts) |
| Projection | A pack's view on a surface: CLI, host tool, Skill, MCP export | [§12](/docs/specification/projections) |
| Four faces | Wire, CLI, host tool and Skill must name a command consistently | [§12.4](/docs/specification/projections) |
| Skill | `HARNESS.md`, the teaching document for humans and agents | [Skill](/docs/concepts/skill) |
| Lead | Text between the Skill title and first `##`; the stable-context excerpt | [§5.3](/docs/specification/skill) |
| Sandbox | Default exec posture: bin allow-list, cwd jail, no ambient credentials | [§10](/docs/specification/sandbox) |
| Native-local | Explicit opt-out of the jail on a trusted machine | [§10.3](/docs/specification/sandbox) |
| Availability | `installed` / `activated` / `connected` / `executable` | [§11.2](/docs/specification/lifecycle) |
| Store | `$HCP_HOME`, which holds packs, skills and state | [CLI](/docs/reference/cli#store) |
| `hcpx` | The reference CLI's binary, also installed as `hcp` | [Quickstart](/docs/quickstart#one-binary-two-names) |
| Eval case | One expectation about a command in `hcp.eval.json`, run through the runtime by `hcpx eval` | [Check and evaluate](/docs/packs/check#eval-cases) |
| Bootstrap pack | `hcp-cli`, which describes the runtime's own lifecycle | [§11.4](/docs/specification/lifecycle) |
| Gold pack | A reference pack that ships with the SDK (`transit`, `ledger`, `echo`) | [Examples](/docs/packs/examples) |
| Sink | Where Sense records land: `inbox` or `silent` | [Sense delivery](/docs/concepts/sense#sense-delivery) |
| Conformance vector | Executable test tied to a spec section | [§14](/docs/specification/conformance) |
| RFC | Change proposal to the protocol | [RFC process](/docs/community/governance#rfc-process) |
| Agent Skills | External standard for teaching documents; `HARNESS.md` is compatible | [Landscape](/docs/introduction/landscape) |
| MCP | Model Context Protocol, a transport HCP can export to | [Landscape](/docs/introduction/landscape) |
| Connector | Not an HCP type; a vendor SDK inside a product runtime | [Landscape](/docs/introduction/landscape) |

Source: https://harnesscontextprotocol.io/docs/reference/glossary

---

# FAQ

> Short answers to the first questions about HCP, from MCP and Skills to secrets, runtimes, Sense and hosts, each linking to the page that settles it.

Each answer here is short and links to the page that owns the topic. Where an
answer simplifies, the [Specification](/docs/specification) governs.

## Positioning

### Is HCP a replacement for MCP?
No. MCP is a transport between a model and tools, and HCP is a harness's
install unit: one pack carrying Execute, Sense, Policy and Binding
([Landscape](/docs/introduction/landscape)). A runtime can
[export](/docs/hosts/mcp) a single MCP tool, `hcp`, that forwards CLI argv, but
that tool is a projection ([§12.5](/docs/specification/projections)) and the
pack stays the unit you install and authorise.

### Is HCP a replacement for Agent Skills?
No. `HARNESS.md` follows Agent Skills principles and is copied to `SKILL.md` on
activate. HCP adds what a Skill alone cannot carry: the commands a runtime can
execute, the effect class of each and the credential path
([Skill](/docs/concepts/skill)).

### Why not just ship a CLI and a README?
A README does not tell a runtime which commands write, which need a credential
or which need confirmation. `policy.json` does, and the runtime enforces it
before anything runs ([What is HCP](/docs/introduction/what-is-hcp)).

### Is HCP vendor-neutral?
Yes. The specification names no products
([Governance](/docs/community/governance#vendor-neutrality)), and hosts and
runtimes appear as examples only in [Adopt in hosts](/docs/hosts).

## Packs

### What is the smallest valid pack?
Three files: `surface.json` with one command, `policy.json` with one entry for
it, and `HARNESS.md`. The `echo` gold pack in [Examples](/docs/packs/examples)
is exactly that.

### Can a pack have no commands?
Only a `sense_only` pack can. It must declare at least one
[trigger](/docs/concepts/sense#triggers) ([§7.9](/docs/specification/sense)).

### Where do secrets go?
Never in the pack. A pack declares a [Binding](/docs/concepts/binding) `mode`,
and the runtime's hub mints a credential at exec time
([§8](/docs/specification/binding)).

### Why is it called `surface.json` and `verb_key` if the concept is "command"?
Wire names are frozen for HCP/1.x compatibility, so the docs say "command"
while the wire field stays `verb_key` ([Command](/docs/concepts/command)). The
[Roadmap](/docs/community/roadmap#reserved-for-hcp20) reserves the rename for
HCP/2.0.

### Do I need `--json`?
By default the runtime appends `--json` to every wrap invocation, so emit JSON
when you see it. A CLI that already emits JSON can ignore the flag or set
`json_flag` ([CLI projection](/docs/concepts/command#the-json-flag)).

### What is a "gated write"?
It is a command with `operation: write` and `confirm: true`, which runs only
when `args.confirm` or `args.apply` is `true`.
[Pack classes](/docs/packs/best-practices#pack-classes) shows where it fits.

### How do I find existing packs?
Search the [catalog](/docs/packs/share#catalog) or run `hcpx search`. Gold
packs also scaffold from the CLI (`hcpx init transit --template transit`).

### How do I publish a pack?
Lint it, then add a row to `content/catalog/packs.json` or let `hcpx share`
print one ([Share a pack](/docs/packs/share)). A signed registry belongs to
HCP/2.0.

## Runtimes

### Must I use the reference SDK?
No. Any implementation that satisfies
[§14.3](/docs/specification/conformance#143-runtime-conformance) conforms, and
the TypeScript SDK is one ([Build runtimes](/docs/runtimes)).

### What does the runtime enforce that the pack cannot?
The runtime decides who the Principal is, which tenant and project they act
in, which of the five capabilities they hold, whether the confirm gate was
satisfied, which credential is minted and whether the binary is allow-listed.
[Authorization](/docs/concepts/access#authorization) covers the checks.

### Can a runtime be proprietary?
Yes. The pack format is open, and the runtime's hub, store, sink, RBAC tables
and isolation are its own
([Proprietary runtimes](/docs/runtimes#proprietary-runtimes)).

### What is `native-local`?
It is an explicit opt-out of the sandbox jail for a trusted personal machine,
set with two flags, `sandbox: false` and `nativeLocal: true`. Everything else
still applies ([§10.3](/docs/specification/sandbox)).

## Sense

### Is Sense just webhooks?
No. Sense is the declaration that a pack can announce events, and webhooks,
queues and push mappings are only how a runtime
[receives them](/docs/concepts/sense#sense-delivery)
([§7.6](/docs/specification/sense)).

### Can a trigger run a command?
No. A trigger only records, and an Invoker may then choose to Execute under its
own grants ([§7.2](/docs/specification/sense)).

### Can I poll instead?
Yes, with a read command. Polling is Execute, so do not declare a trigger for
it ([Design principles](/docs/introduction/design-principles)).

## Hosts

### How does an agent see a pack?
A coding agent sees one tool, `hcp`, and a Skill that teaches it, and
`hcpx skill <id>` prints any pack's `HARNESS.md`. An in-process loop sees one
tool per executable command, named `<pack_id>__<verb_key>`, a short lead per
pack in the system prompt, and an `hcp__read_skill` tool for the full Skill
([Context optimization](/docs/hosts#context-optimization)).

### Does the host spawn the binary?
No. The host calls a runtime, which runs the
[pipeline](/docs/runtimes/execution) and spawns the binary
([§12.2](/docs/specification/projections)).

## Project

### What version is this?
HCP/1.1, still a draft. [Versioning](/docs/introduction/versioning) explains
the scheme.

### How do I propose a change?
Open an issue titled `RFC idea: <title>` on `hcp-protocol` to test interest.
The [RFC process](/docs/community/governance#rfc-process) takes it from there.

### Where is the source?
`hcp-protocol` holds the spec, RFCs and conformance, `hcp` the SDK, CLI and
gold packs, and `hcp-docs` this site.
[Governance](/docs/community/governance#repositories) lists them with their
licences.

Source: https://harnesscontextprotocol.io/docs/reference/faq

---

# Governance

> Who owns each HCP repository, who decides, how a normative change moves from RFC idea to release, and when you may say you implement HCP.

HCP is an open specification kept by its maintainers in three repositories.
Editorial fixes merge directly, and every normative change goes through an RFC
so the reason for each rule stays on record; to propose one, open an issue
titled `RFC idea: <title>` on `hcp-protocol`.

## Repositories

| Repository | Holds | Licence |
|------------|-------|---------|
| `hcp-protocol` | Normative specification, RFCs, conformance vectors | Apache-2.0 |
| `hcp` | Reference SDK, the `hcpx` CLI, gold packs, executable conformance | MIT |
| `hcp-docs` | This site | Apache-2.0 |

The specification in `hcp-protocol/spec` is the source of truth. This site's
[Specification](/docs/specification) section is regenerated from it, so where
the two differ, the repository wins. Security reports follow
[Security](/docs/community/security), and everything else goes in an issue on
the repository it concerns.

## Roles

| Role | Does | How you become one |
|------|------|--------------------|
| Contributor | Opens issues, PRs and RFCs | Open one |
| Maintainer | Reviews and merges in one repository; triages RFCs | Sustained contribution; nominated by maintainers |
| Spec editor | Owns the text of `spec/`; lands RFC decisions verbatim | Appointed by maintainers |
| Steering | Resolves disputes; approves major versions; owns the roadmap | The founding maintainers at first; expands with implementers |

Once two conforming runtimes exist, the plan is for steering to include at least
one independent runtime implementer.

## Decision making

1. Any maintainer may merge an editorial change: a typo fix or a clarification
   that keeps the meaning.
2. A normative change needs an accepted RFC.
   [When you need an RFC](#when-you-need-an-rfc) lists what counts.
3. A minor version is an accepted RFC that adds optional fields or tightens
   lints, with two maintainer approvals.
4. A major version is an accepted RFC that changes meaning or removes fields,
   with steering approval and a migration note.
5. Disputes run on lazy consensus for seven days and then go to steering, which
   decides by simple majority and records its reasoning in the RFC.

These principles from [Design principles](/docs/introduction/design-principles)
are constitutional, so changing one takes a major version:

- The pack is the unit, and a pack is three files.
- Packs declare; runtimes decide.
- No credentials in packs.
- Sense never executes.
- Sandbox by default.
- Vendor-neutral text.

## RFC process

### When you need an RFC

You need one to add, remove or change the meaning of a field in
`surface.json` or `policy.json`, to add or change a MUST, SHOULD or MAY in the
[Specification](/docs/specification), to add a capability, Binding mode,
adapter kind or land, or to change the exec or result envelope, a lifecycle
stage or an availability flag. Typo fixes, clearer prose, new examples, new
informative sections and SDK-only changes that leave conformance alone need
none.

### Before writing

1. Search the existing RFCs in `hcp-protocol/rfcs/`.
2. Check the [non-goals](/docs/introduction/design-principles#non-goals). If
   your proposal touches one, say so and argue the cost of that boundary.
3. Open an issue titled `RFC idea: <title>` to test interest. Two thumbs from
   maintainers, or one from an implementer, is enough to go ahead.

### Template

Copy `rfcs/0000-template.md` to `rfcs/NNNN-<kebab-title>.md`:

```md
# RFC NNNN — <Title>

- **Status:** Draft
- **Authors:**
- **Replaces:**            <spec section or prior RFC, if any>
- **Tracking:**            <issue link>

## Summary
One paragraph.

## Motivation
What breaks today. Evidence: a pack, a runtime, or a corpus.

## Specification
Normative text, using RFC 2119 keywords, written so it can be pasted into spec/.

## Conformance
Which vectors change or are added (§14.5).

## Compatibility
Minor or major? What happens to 1.x packs and runtimes?

## Drawbacks

## Alternatives
```

The Specification section must be spec text you can paste as it stands, every
new MUST needs at least one conformance vector, and the RFC states its version
impact.

### Lifecycle

```text
Draft ──▶ Review ──▶ Final comment (7 days) ──▶ Accepted ──▶ Implemented ──▶ Active
   │          │                                     │
   └──────────┴───────────── Rejected / Withdrawn ──┘
```

| Status | Meaning |
|--------|---------|
| Draft | PR open; author iterating |
| Review | A maintainer is sponsoring; discussion on the PR |
| Final comment | Seven days; any maintainer may object with reasons |
| Accepted | Merged into `rfcs/`; spec editor lands text in `spec/`; docs regenerate |
| Implemented | Reference SDK ships it; vectors pass |
| Active | Released in a protocol version |
| Rejected | Closed with reasons recorded in the RFC |
| Withdrawn | Closed by the author |

An RFC is accepted when it leaves Final comment with no unresolved objection
and the approvals [Decision making](#decision-making) sets for its version.
Its conformance vectors then go into `packages/sdk/src/conformance.test.ts`,
[Versioning §13.5](/docs/specification/versioning) lists the change, and the
RFC's status is updated in place.

### Accepted RFCs

| RFC | Title | Version | Status |
|-----|-------|---------|--------|
| 0001 | Wrap hardening: named commands, installer family, land `local`, confirm | HCP/1.1 | Accepted, Implemented |
| 0003 | Wrap exec plane: `wrap.workspace` and the local driver | HCP/1.1 | Accepted, Implemented |
| 0008 | HCP/1.1 errata and the `write_stimulus` lint | HCP/1.1 | Accepted, Implemented |

The [Roadmap](/docs/community/roadmap) tracks the drafts and open proposals.

## Vendor neutrality

The specification names roles (host, runtime, hub), never products, and no
company holds a special position in its text. Host guides under
[Adopt in hosts](/docs/hosts) document integration, so they may name products
as examples.

## Trademark

"Harness Context Protocol" and "HCP" as applied to this protocol, the `hcpx_`
wordmark and the `h_` symbol identify the project and conforming
implementations. You may say your implementation "implements HCP/1.1" once it
passes [§14](/docs/specification/conformance), and you may not imply
endorsement.

Source: https://harnesscontextprotocol.io/docs/community/governance

---

# Roadmap

> Which RFCs HCP/1.1 holds, which drafts the reference SDK already runs on trial, what is proposed for 1.2 and reserved for 2.0, and which gaps remain.

HCP/1.1 is the current draft, and the reference SDK also implements five draft
RFCs on trial, one of them in part. Everything after that is a proposal that
binds no one until it passes the [RFC process](/docs/community/governance#rfc-process),
so open an issue titled `RFC idea: <title>` on `hcp-protocol` to move an item
forward.

## In the current draft

The reference SDK and runtime (0.3.0) and the CLI (0.2.0) implement both
versions below. The protocol repository is tagged `v0.1.0` with HCP/1 and RFC
0001, and the next tag adds RFC 0003, the RFC 0001 erratum and the RFC 0008
errata.

| Version | Contents |
|---------|----------|
| HCP/1.0 | Pack model (three files), Execute + isolated Sense, Policy (`operation`, `land`), Binding modes and hubs, five capability grants, sandbox default, CLI bootstrap pack (`hcp-cli`), availability flags, host projections (tool naming, `hcp__read_skill`) |
| HCP/1.1 | RFC 0001: named commands required for wrap; installer family; land `local`; `confirm` defined for writes; health commands never mint; control vs runtime plane. RFC 0003: `wrap.workspace` exec plane (local driver in the reference runtime). RFC 0008: errata where the specification disagreed with itself and with its working notes, and the `write_stimulus` lint. |

## Drafts the reference SDK implements

These RFCs are drafts in `hcp-protocol`. The reference SDK, runtime and CLI
implement them on trial before acceptance, and the
[Specification](/docs/specification) section does not contain them yet.

| RFC | Adds | Explained in |
|-----|------|--------------|
| [0002](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0002-skill-four-faces.md) Skill four faces | Lint fails on drift between the Skill and the commands, host paths, credentials, and MCP as the install path | [Skill](/docs/concepts/skill#rules) |
| [0004](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0004-wrap-json-flag.md) Wrap JSON flag | `wrap.json_flag`, a per-command `json_flag`, and no flag when `cli[]` already selects JSON | [CLI projection](/docs/concepts/command#the-json-flag) |
| [0005](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0005-wrap-project-mode.md) Wrap project mode | `wrap.workspace.mode: project`, and `no_project` where a runtime has none | [Manifest](/docs/packs/manifest#where-the-cli-runs) |
| [0006](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0006-access-modes.md) Access | Modes, allow/ask/deny rules, human approvals, `confirm: "always"`, approval views, audit | [Access](/docs/concepts/access) |
| [0007](https://github.com/harnesscontextprotocol/hcp-protocol/blob/master/rfcs/0007-host-projection.md) Host projection | Command titles, approval options | [Titles](/docs/concepts/command#titles), [Answers a host can offer](/docs/concepts/access#answers-a-host-can-offer) |

The SDK does not implement the rest of RFC 0007 yet: a call record a host shows
for each exec (`call_id`, `title`, `effect`, `status`, `locations`), rules for
cancelling a run (open approvals answered as cancelled, no call started after a
cancel), and an access mode a person picks per session.

## Proposed for HCP/1.2

Each item is a candidate RFC for a minor version, which may only add.

| Item | Why | Shape |
|------|-----|-------|
| Pack `version` | Upgrades and pinning need it | `version: "1.4.0"` in manifest; semver; runtimes report it in availability |
| Argument schema | Hosts generate tool parameters by hand today | `verbs.<k>.args` as JSON Schema; lint validates; hosts project directly |
| Result schema | Agents cannot rely on result shape | `verbs.<k>.result` as JSON Schema, optional |
| Error codes | The specification has only `error` strings, matched by regex | Normative `code` alongside `error` in the result envelope, with a registry; the reference implementation already returns the [codes in Errors](/docs/reference/errors#error-codes) |
| Extension namespace | Runtimes need private fields without lint noise | `x-*` keys ignored by lint, reserved for extensions |
| Trigger payload schema | Sinks cannot validate | `stimulusTriggers[].payload` as JSON Schema |
| Manifest `$schema` | Editor validation | Optional `$schema` pointing at the published schema |
| Deprecation markers | Renaming commands is breaking | `verbs.<k>.deprecated: "<replacement>"` |

## Reserved for HCP/2.0

These change meaning, which only a major version may do.

| Item | Why it is major |
|------|-----------------|
| Registry protocol | Discovery, publish, fetch by `pack_id@version`; integrity hashes; provenance. The [community catalog](/docs/packs/share#catalog) is a PR-based index and stays separate from it. |
| Pack signing | Signature over the three files; runtimes verify before install |
| Capability wire profile | Grants as a portable token so a runtime can accept an upstream decision |
| Transport adapters | Normative MCP export and A2UI projection with round-trip guarantees |
| Rename wire fields | `surface.json` → `pack.json`, `verb_key` → `command`, with a compatibility shim |
| Streaming results | Long-running commands, progress events |

## What HCP still lacks

Set against MCP, Agent Plugins, OpenAPI and package ecosystems, HCP still has
these gaps.

| They have | HCP status | Plan |
|-----------|-----------|------|
| Multi-language SDKs | TypeScript only | Python next; the pipeline is small |
| A registry protocol (signed `pack_id@version`) | None | HCP/2.0 |
| A community catalog (search + share) | [Catalog](/docs/packs/share#catalog), `/api/v1/packs`, `hcpx search` | Grow entries by PR |
| A conformance badge | Vectors exist, no badge | Publish a checklist and self-attestation format |
| A public implementations list | Starting | [Implementations](/docs/community/implementations) |
| A security disclosure process | Starting | [Security](/docs/community/security) |
| Versioned spec permalinks | Single draft | Freeze `/docs/specification` per `hcp-protocol` tag |
| Changelog | In §13.5 | Dedicated page per version |
| Interactive examples / playground | None | Browser sandbox running the reference runtime on gold packs |
| Editor tooling | The [schemas](/docs/reference/schemas) are live; no editor maps them yet | VS Code validation through the published schemas |
| A test-suite runner for third parties | Bun test in the monorepo | Extract vectors to a language-neutral fixture format |
| Governance charter | [Governance](/docs/community/governance) | Ratify once a second implementer joins steering |

## Not planned

The protocol will not add a verb ontology, tenant identifiers or credentials in
packs, a mandatory UI, or polling as Sense. The
[non-goals](/docs/introduction/design-principles#non-goals) give the reasons.

Source: https://harnesscontextprotocol.io/docs/community/roadmap

---

# Implementations

> The runtimes, hosts, SDKs and packs known to implement HCP, with the protocol version and conformance target each one claims.

Every row here is a self-attestation against
[§14 Conformance](/docs/specification/conformance), and the reference SDK,
runtime and CLI are a private preview on GitHub Packages today. To add your
implementation, open a PR to `hcp-docs` with a row that links your vector run
or test suite.

## Runtimes

| Name | Target | Version | Adapters | Sandbox | Notes |
|------|--------|---------|----------|---------|-------|
| Reference local runtime (`@harnesscontextprotocol/sdk`) | Runtime | HCP/1.1 | wrap, composite | allow-list + jail; native-local opt-in | Passes all reference vectors |
| Proprietary stub (`createProprietaryStubRuntime`) | Runtime (pipeline only) | HCP/1.1 | none | none | Authorization pipeline for hosted runtimes to extend |
| First hosted runtime | Runtime | HCP/1.1 | wrap | container | Private; first production deployment of a gated-write pack family with a local credential hub |

## Hosts

| Name | Target | Version | Integration | Notes |
|------|--------|---------|-------------|-------|
| Claude Code | Host | HCP/1.1 | `hcpx adopt claude` (Skill, one MCP tool, settings) | [Guide](/docs/hosts/coding-agents#claude-code) |
| Cursor | Host | HCP/1.1 | `hcpx adopt cursor` (rules and skills) | [Guide](/docs/hosts/coding-agents#cursor) |
| Codex | Host | HCP/1.1 | `hcpx adopt codex` (Skill, `AGENTS.md`, one MCP tool) | [Guide](/docs/hosts/coding-agents#codex) |
| OpenCode | Host | HCP/1.1 | `hcpx adopt opencode` (plugin and config) | [Guide](/docs/hosts/coding-agents#opencode) |
| Grok Bot / Grok Build | Host | HCP/1.1 | `hcpx adopt grok` (bash + one MCP tool) | [Guide](/docs/hosts/coding-agents#grok-bot) |
| AI SDK (`HarnessAgent`) | Host | HCP/1.1 | In-process tool loop | [Guide](/docs/hosts/ai-sdk) |

## SDKs and tools

| Name | Language | Version | Notes |
|------|----------|---------|-------|
| `@harnesscontextprotocol/sdk` | TypeScript | 0.3.0 | Reference SDK |
| `@harnesscontextprotocol/runtime` | TypeScript | 0.3.0 | The embeddable runtime surface of the SDK |
| `@harnesscontextprotocol/cli` (`hcpx`, also `hcp`) | TypeScript | 0.2.0 | Reference CLI and bootstrap Invoker |

## Packs

Gold packs ship with the SDK, and [Examples](/docs/packs/examples) walks
through them.

| Pack | Class | Adapter | Binding | Proves |
|------|-------|---------|---------|--------|
| `transit` | Public read | wrap | public | Five commands, twin face, `cli[]` rendering |
| `ledger` | Gated write | wrap | local | `confirm: "always"`, write grant, denial envelope, a declared approval `preview` |
| `echo` | Minimal | composite | public | Smallest valid pack |
| `hcp-cli` | Bootstrap | composite | local | The CLI's own commands as a pack |

They also scaffold from the CLI (`hcpx init transit --template transit`) and
appear in the [catalog](/docs/packs/share#catalog).

## Claiming conformance

Your row names the targets, the protocol version, the supported adapters, the
sandbox posture and a link to evidence, as
[§14.6](/docs/specification/conformance#146-claiming-conformance) describes.

Source: https://harnesscontextprotocol.io/docs/community/implementations

---

# Security

> Where to report an HCP vulnerability, which threats the protocol defends against, what it guarantees, what it leaves to runtimes and each role's rules.

Report a vulnerability privately to `security@harnesscontextprotocol.io`, never
in a public issue. The protocol fixes the guarantees below, which every
conforming runtime must implement, and leaves the rest, such as isolation
strength and credential storage, to the runtime. Pack authors, runtime builders
and hosts each have a short list of rules at the end.

## Reporting

The private address covers the specification, the reference SDK, the CLI and
the gold packs. Send the affected repository and version, a reproduction, the
impact, and whether you believe it is a specification flaw or an implementation
bug.

We acknowledge within three business days and aim to publish a fix or an
advisory within ninety days. Reporters are credited unless they ask otherwise.

Vulnerabilities in third-party runtimes and packs go to their maintainers, and
if the root cause is in the protocol, copy us.

## Threat model

HCP sits between an agent, whose input is untrusted and possibly
prompt-injected, and real-world effects such as money, messages, files and
credentials.

### Adversaries

| Adversary | Goal |
|-----------|------|
| Injected agent | Make the harness run a write it should not, or run it without confirmation |
| Malicious pack | Exfiltrate credentials; run arbitrary binaries; escape the working directory |
| Compromised host | Spawn binaries directly, bypassing the runtime |
| Tenant confusion | Act in another tenant or project by argument |
| Sense abuse | Use a trigger to cause execution or to leak data |

### Guarantees the protocol makes

Each one maps to a normative section that a conforming runtime must implement.

| Guarantee | Section |
|-----------|---------|
| No command runs without a Policy entry | [§6.3](/docs/specification/execute) |
| Writes are classified and gated; `confirm` is enforced by the runtime, not the model | [§4.5](/docs/specification/policy) |
| Grants are checked before anything executes; fail closed | [§9.4 and §9.5](/docs/specification/authorization) |
| Tenant from session, never from args; `brain` requires project membership | [§9.2](/docs/specification/authorization) |
| Packs carry no credentials; mint at exec; no ambient fallback | [§8](/docs/specification/binding) |
| Binaries are allow-listed; cwd is jailed; no `PATH` resolution | [§10.2](/docs/specification/sandbox) |
| Sense cannot execute, mint, or mutate | [§7.2](/docs/specification/sense) |
| Hosts route through a runtime; they do not spawn | [§12.2](/docs/specification/projections) |
| Every write is attributed: Principal + tenant + Binding identity + command | [§9.6](/docs/specification/authorization) |

### What the protocol does not guarantee

| Left to runtimes | Notes |
|------------------|-------|
| Process isolation strength | Allow-list + jail is the floor; containers and microVMs are conforming and encouraged |
| Network egress control | Runtime profile |
| Credential storage | The hub's problem; the protocol only says "not in the pack" |
| Session authentication | How a Principal is established |
| Rate limiting and quotas | Runtime |
| Wrapped CLI behaviour | A pack cannot make a hostile binary safe; the allow-list decides what may run |
| Prompt injection in Skills | `HARNESS.md` is text the model reads. Runtimes should lint for instruction-like content that contradicts Policy; hosts should present Skills as data |

## Guidance for pack authors

- Keep writes few, and put each one behind `confirm`.
- Never put a credential, or a placeholder that looks like one, in any of the
  three files.
- Do not teach the agent to bypass gates ("always pass `--confirm`").
- Health commands must not mint.

## Guidance for runtime builders

- Implement the pipeline order exactly, because reordering is the most common bug.
- Seed the allow-list from activated packs only, and never add `/usr/bin`.
- Log the audit tuple for every write before returning the result.
- Treat `native-local` as a per-machine trust decision that needs explicit
  configuration.
- Run the [conformance vectors](/docs/specification/conformance#145-vectors)
  in CI.

## Guidance for hosts

- Never execute `wrap.bin` yourself.
- Show `confirm` markers to the user, and do not let the model satisfy them on
  its own unless a policy permits it.
- Filter tools by grants before the model sees them.

## Advisories

No advisories have been published yet.

Source: https://harnesscontextprotocol.io/docs/community/security
