# Harn ACP/MCP extensions v1

> Canonical URL: https://harnlang.com/spec/harn-extensions/v1

Website: https://harnlang.com/spec/harn-extensions/v1.html

This page documents Harn, which is pre-1.0. Language, standard library, and CLI APIs may change. If the intended version is unclear, clarify before using this page.

---

Canonical URL: <https://harnlang.com/spec/harn-extensions/v1>

This page defines Harn-owned protocol extension fields for clients that consume
Harn over Agent Client Protocol (ACP) or Model Context Protocol (MCP). Harn uses
`_meta.harn` for fields that extend an upstream protocol object. Standard ACP
and MCP fields stay at their standard locations.

## Compatibility

The ACP adapter advertises this contract during `initialize`:

```json
{
  "agentCapabilities": {
    "_meta": {
      "harn": {
        "schemaCompatibility": "agentclientprotocol/agent-client-protocol schema v0.12.2",
        "extensionContract": "https://harnlang.com/spec/harn-extensions/v1",
        "sessionUpdateExtensions": [
          "artifact",
          "available_commands_update",
          "fs_watch",
          "handoff",
          "hitl_request",
          "hitl_resolved",
          "log",
          "progress",
          "reminder_emitted",
          "skill_activated",
          "skill_deactivated",
          "skill_narrow",
          "skill_scope_tools",
          "stance_transition",
          "tool_search_query",
          "tool_search_result",
          "transcript_compacted",
          "transcript_projected",
          "worker_update"
        ],
        "toolLifecycleExtensionFields": [
          "audit",
          "changedPaths",
          "data",
          "durationMs",
          "error",
          "errorCategory",
          "executionDurationMs",
          "executor",
          "mutationStatus",
          "parsing",
          "rawInputPartial"
        ],
        "contentExtensionFields": [
          "permission_preview",
          "visible_delta",
          "visible_text"
        ],
        "promptResultExtensionFields": [
          "terminal"
        ],
        "stagedWritesPendingFields": [
          "pendingCount",
          "totalBytes",
          "pendingWrites"
        ],
        "stagedWriteFields": [
          "path",
          "kind",
          "byteDelta",
          "snapshotId"
        ],
        "extensionMethods": {
          "_harn/agentEvent": {
            "description": "Pipeline-loop milestones that have no canonical ACP session/update mapping.",
            "schema": "https://harnlang.com/spec/harn-extensions/agent-event/v1"
          },
          "_harn/providerCatalog": {
            "description": "Return the normalized provider/model catalog used by this runtime.",
            "schema": "https://harnlang.com/schemas/provider-catalog.v10.json",
            "schemaVersion": 10
          },
          "harn.session_view.query": {
            "description": "Return the harn.session_view.v1 projection for a live or persisted session.",
            "schema": "harn.session_view.v1",
            "schemaVersion": 1
          }
        },
        "hostCapabilityOperations": {
          "hypothesis": ["attest_event"],
          "process": [
            "exec",
            "list_shells",
            "get_default_shell",
            "set_default_shell",
            "shell_invocation"
          ]
        }
      }
    }
  }
}
```

Field stability means the field name and location are part of this v1 contract.
Consumers must still ignore unknown sibling fields and tolerate missing optional
fields.

| Field | Location | Stability | Meaning |
| --- | --- | --- | --- |
| `schemaCompatibility` | `agentCapabilities._meta.harn` | Stable v1 | Upstream ACP schema version Harn pins its ACP wire shape against. |
| `extensionContract` | `agentCapabilities._meta.harn` | Stable v1 | Public spec URL for Harn-owned extension fields. |
| `sessionUpdateExtensions` | `agentCapabilities._meta.harn` | Stable v1 | Harn `session/update` discriminators a client may receive in addition to upstream ACP values. |
| `toolLifecycleExtensionFields` | `agentCapabilities._meta.harn` | Stable v1 | Harn fields that can appear below `tool_call._meta.harn` or `tool_call_update._meta.harn`. |
| `contentExtensionFields` | `agentCapabilities._meta.harn` | Stable v1 | Harn fields that can appear below ACP content-block `_meta.harn`. |
| `promptResultExtensionFields` | `agentCapabilities._meta.harn` | Stable v1 | Harn fields that can appear below `session/prompt` result `_meta.harn`. |
| `stagedWritesPendingFields` | `agentCapabilities._meta.harn` | Stable v1 | Fields emitted for `_meta.harn.kind = "staged_writes_pending"`. |
| `stagedWriteFields` | `agentCapabilities._meta.harn` | Stable v1 | Fields in each `pendingWrites` item. |
| `extensionMethods` | `agentCapabilities._meta.harn` | Stable v1 | Harn-owned JSON-RPC methods beyond canonical ACP methods. Unknown keys must be ignored. |
| `hostCapabilityOperations` | `agentCapabilities._meta.harn` | Experimental | Host bridge operation names Harn may request through ACP host calls. |

`hypothesis.attest_event` is a trusted native-operation completion boundary.
It is present in a session's `host/capabilities` result only when the host has
installed the corresponding adapter; its appearance in
`hostCapabilityOperations` means Harn understands the operation, not that every
host implements it.

## ACP extension methods

### `_harn/providerCatalog`

Request:

```json
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "_harn/providerCatalog",
  "params": {}
}
```

Response `result` is the provider catalog v10 artifact identified by
`https://harnlang.com/schemas/provider-catalog.v10.json`. It is the same shape
as Harn's checked-in provider catalog, with the serving runtime's effective
provider and capability overlays already applied.

Clients should use this method for provider/model pickers and avoid maintaining
parallel hard-coded provider lists. The catalog includes provider auth
requirements and env var names, extra headers, healthcheck probes, aliases,
context windows, tool-support hints, local/cloud classification, availability,
pricing where known, model-selection families/presets, and `routing_routes` for host-trusted route-decision rows
that carry provider/model/family/capability/timeout metadata without resolved
secret values.

## ACP prompt results

When a pipeline runs `agent_loop`, the successful `session/prompt` result
includes the loop's producer-owned terminal outcome under
`result._meta.harn.terminal`:

```json
{
  "stopReason": "max_turn_requests",
  "_meta": {
    "harn": {
      "terminal": {
        "kind": "policy_budget",
        "reason": "max_iterations",
        "owner": "policy"
      }
    }
  }
}
```

`stopReason` remains the canonical ACP field and may collapse distinct Harn
outcomes. A host reports verified completion only when `terminal.kind` is
`natural`; policy, cancellation, error, suspension, and `unknown` kinds are
incomplete even when ACP reports `end_turn`. Pipelines without `agent_loop`
omit `terminal`, so clients must tolerate its absence.

## ACP session updates

Harn extension session updates keep the `sessionUpdate` discriminator at
`params.update.sessionUpdate`; Harn-owned payload fields are under
`params.update._meta.harn`. ACP-shaped compatibility updates that Harn
advertises, such as `available_commands_update`, keep their canonical root
fields.

```json
{
  "method": "session/update",
  "params": {
    "sessionId": "session_123",
    "update": {
      "sessionUpdate": "progress",
      "_meta": {
        "harn": {
          "phase": "ingest",
          "message": "loading",
          "progress": 3,
          "total": 10
        }
      }
    }
  }
}
```

| `sessionUpdate` value | `_meta.harn` fields | Stability |
| --- | --- | --- |
| `artifact` | `artifactId`, `kind`, `title`, `mimeType`, `spec`, `fallback`, `sizeBytes`, `provenance`, `metadata` | Stable v1 |
| `available_commands_update` | none; root `availableCommands` follows ACP | Stable v1 |
| `progress` | `phase`, `message`, `progress`, `total`, `data` | Stable v1 |
| `log` | `level`, `message`, `fields` | Stable v1 |
| `fs_watch` | `subscriptionId`, `events` | Stable v1 |
| `worker_update` | `workerId`, `workerName`, `workerTask`, `workerMode`, `event`, `status`, `terminal`, `metadata`, `audit` | Stable v1 |
| `transcript_compacted` | `receiptId`, `schemaVersion`, `mode`, `reason`, `strategy`, `engineStrategy`, `archivedMessages`, `estimatedTokensBefore`, `estimatedTokensAfter`, `snapshotAssetId`, `instructionMode`, `instructionSource`, `compactionPolicy`, `recap` | Stable v1 |
| `transcript_projected` | `policy`, `reason`, `prefixHash`, `keptCount`, `droppedCount`, `providerSafetyBlocked`, `redactedCount`, `reclaimedTokens`, `rootsConsulted`, `redactionPointers` | Stable v1 |
| `handoff` | `handoffId`, `artifactId`, `handoff` | Stable v1 |
| `skill_activated` | `skillName`, `iteration`, `reason` | Stable v1 |
| `skill_deactivated` | `skillName`, `iteration` | Stable v1 |
| `skill_narrow` | `reason`, `removedTools`, `remainingTools`, `policy`, `removedToolDetails`, `keptToolDetails` | Stable v1 |
| `skill_scope_tools` | `skillName`, `allowedTools` | Stable v1 |
| `stance_transition` | `phase`, `escapeTool`, `allowedTools`, `justification`, `consent`, `reason` | Stable v1 |
| `tool_search_query` | `toolUseId`, `name`, `query`, `strategy`, `mode` | Stable v1 |
| `tool_search_result` | `toolUseId`, `promoted`, `strategy`, `mode` | Stable v1 |
| `reminder_emitted` | `reminder` | Stable v1 |
| `hitl_request` | `requestId`, `kind`, `payload` | Experimental |
| `hitl_resolved` | `requestId`, `kind`, `outcome` | Experimental |

`artifact` updates carry renderable chart/table specs or external file
references. File-reference `spec` objects are intentionally references, not
inline payloads: the stable manifest/file-ref schema is
`https://harnlang.com/schemas/artifact-manifest.v1.schema.json`
(`schema_version: "harn.artifacts.v1"`).
`harness.agent.artifact_emit("file", spec, options)` accepts the same safe
reference subset used by document/media packages such as `@harn/documents`.
`harness.agent.artifact_emit("artifact_manifest", manifest, options)` emits
the bundle-level manifest itself and validates that every nested
`manifest.artifacts[]` entry is still an external `file://`, `artifact://`,
`harn-artifact://`, or `urn:` reference with no inline bytes or network fetch.

The `audit` field on `worker_update` is the same mutation-session record shape
used by tool lifecycle `audit`.

`agent_progress` task-list entries are not encoded as the Harn `progress`
extension. ACP adapters emit them as canonical `sessionUpdate: "plan"` updates
with full-replacement `entries`; message-only `agent_progress` reports use the
Harn `progress` extension with `phase: "narration"`.

Standard ACP updates such as `agent_message_chunk`, `tool_call`,
`tool_call_update`, and `plan` keep their standard payload fields at the root
of `params.update`.

## ACP tool lifecycle

`tool_call` and `tool_call_update` use canonical ACP root fields for
`toolCallId`, `title`, `kind`, `status`, `rawInput`, `rawOutput`, and `content`.
Harn-owned lifecycle fields appear under `params.update._meta.harn`.

```json
{
  "sessionUpdate": "tool_call_update",
  "toolCallId": "call_123",
  "title": "edit_file",
  "status": "completed",
  "rawOutput": "ok",
  "_meta": {
    "harn": {
      "executor": "host_bridge",
      "durationMs": 25,
      "executionDurationMs": 18,
      "audit": {
        "session_id": "session_123",
        "run_id": "run_123",
        "mutation_scope": "apply_workspace"
      }
    }
  }
}
```

| Field | Applies to | Stability | Meaning |
| --- | --- | --- | --- |
| `audit` | `tool_call`, `tool_call_update` | Stable v1 | Mutation-session record for grouping writes, approvals, undo/redo, and audit logs. |
| `changedPaths` | `tool_call_update` | Stable v1 | Workspace-relative paths changed by the tool when the producer can report them precisely. |
| `data` | `tool_call_update` | Stable v1 | Complete producer-owned facts from a declared `harn.agent_tool_handler_result.v1` envelope. |
| `executor` | `tool_call_update` | Stable v1 | Execution backend: `"harn_builtin"`, `"host_bridge"`, `"provider_native"`, or `{"kind":"mcp_server","serverName":"..."}`. |
| `durationMs` | `tool_call_update` | Stable v1 | End-to-end duration from Harn's lifecycle observation. |
| `executionDurationMs` | `tool_call_update` | Stable v1 | Backend execution duration when Harn can separate it from parsing or routing overhead. |
| `error` | `tool_call_update` | Stable v1 | Human-readable execution or validation error. |
| `errorCategory` | `tool_call_update` | Stable v1 | Machine-readable error category such as `schema_validation` or `parse_aborted`. |
| `mutationStatus` | `tool_call_update` | Stable v1 | Structured workspace mutation outcome: `"applied"`, `"unchanged"` (the requested state already existed), `"not_applied"`, or `"unknown"`. |
| `parsing` | `tool_call`, `tool_call_update` | Experimental | Whether the event represents an in-progress structured input parsing state. |
| `rawInputPartial` | `tool_call_update` | Experimental | Unparseable partial tool input bytes captured during streaming tool-call parsing. |

`audit` is omitted when no mutation session is installed.

## ACP content extensions

Harn can add metadata to ACP content blocks. These fields live under the
content block's `_meta.harn`, not under the surrounding update.

```json
{
  "sessionUpdate": "agent_message_chunk",
  "content": {
    "type": "text",
    "text": "hello",
    "_meta": {
      "harn": {
        "visible_text": "hello",
        "visible_delta": "hello"
      }
    }
  }
}
```

| Field | Location | Stability | Meaning |
| --- | --- | --- | --- |
| `permission_preview` | `toolCall.content[]._meta.harn` | Experimental | Pre-approval provenance for a canonical diff, including source, pre-image SHA-256, and byte count. |
| `visible_text` | `content._meta.harn` | Experimental | Assistant-visible text after Harn visibility filtering. |
| `visible_delta` | `content._meta.harn` | Experimental | Newly visible text in this content chunk. |

## MCP extension boundary

Harn emits its typed-tool MCP extensions below the reverse-DNS key
`_meta["com.harnlang/toolContract"]`. This key is distinct from Harn's ACP
`_meta.harn` namespace because MCP reserves reverse-DNS names for vendor
extensions. Other listed MCP fields remain standard protocol fields:

| MCP surface | Fields Harn emits or consumes | Namespace |
| --- | --- | --- |
| Tool list entries | `annotations.*`; optional `_meta["com.harnlang/toolContract"].errorSchema` | MCP standard annotations; Harn error-schema extension |
| Tool-call results | optional `_meta["com.harnlang/toolContract"].applicationError` when `isError: true` | Harn typed application-error extension |
| Server capabilities | `tools.listChanged`, `resources.listChanged`, `prompts.listChanged`, `logging`, `elicitation`, `extensions["io.modelcontextprotocol/tasks"]` | MCP standard or stable MCP extension |
| Tool-call requests | `_meta.progressToken` | MCP standard |
| Notifications | `notifications/progress` with `progressToken`, `progress`, `message` | MCP standard |
| Task lifecycle | `tasks/get`, `tasks/update`, `tasks/cancel`, `notifications/tasks`; optional `_meta["com.harnlang/toolContract"].immediateResponse` on task creation | Stable MCP tasks extension; Harn display note |

Unknown fields below `_meta["com.harnlang/toolContract"]` must be ignored so
Harn can extend the envelope without changing the upstream MCP object.

Task IDs are unguessable bearer handles. A client can reconnect and retrieve a
task by presenting its ID; Harn does not treat self-reported MCP `clientInfo`
or a transport connection as an authorization identity. Cancelling a known
terminal task returns the extension's empty acknowledgement because completion
and cancellation can race. Only an unknown task ID is rejected.

---

## Read next

- [Remote session control](https://harnlang.com/remote-session-control.md)
- [MCP Apps UI resources](https://harnlang.com/interop/ui-resource.md)
