---
title: "Manifest"
description: "Each `surface.json` field with its type and default: identity, adapter, where the CLI runs, Binding, commands, triggers and the `hcp` metadata object."
canonical: https://harnesscontextprotocol.io/docs/packs/manifest
---

# 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)

## Sitemap

- [Documentation](https://harnesscontextprotocol.io/docs)
- [Packs](https://harnesscontextprotocol.io/packs)
- [Glossary](https://harnesscontextprotocol.io/docs/reference/glossary)
- [llms.txt](https://harnesscontextprotocol.io/llms.txt)
- [llms-full.txt](https://harnesscontextprotocol.io/llms-full.txt)
- [OpenAPI](https://harnesscontextprotocol.io/openapi.json)
- [MCP](https://harnesscontextprotocol.io/mcp)
