.md

SDK

Every export of the TypeScript reference SDK, from the Hcp facade and runtime drivers to lints, grants, access, check, host adoption and HarnessAgent.

On this page

@harnesscontextprotocol/sdk (TypeScript, ESM, Zod schemas) is the reference implementation of the Specification, and the spec governs wherever the two differ. Most hosts need only the Hcp facade to load, activate and run packs, and the sections after it list the lower-level exports.

Terminal
bun add @harnesscontextprotocol/sdk

The package is a private preview on GitHub Packages, and Install the CLI shows the .npmrc it needs.

Hcp facade#

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

OptionTypeDefaultNotes
runtimeHcpRuntimeDriver | 'local' | 'proprietary-stub''local'Pass your own driver to embed
packDirsstring[]noneRequired for 'local'
packsLoadedHcpPack[]noneRequired for 'proprietary-stub'
sandboxbooleantrue
nativeLocalbooleanfalsenative-local only when sandbox: false && nativeLocal: true
grantsHcpGrant[]permissiveGrants()
principalHcpPrincipal{ id: 'local-principal', kind: 'human' }
tenant_idstring'local-tenant'
project_idstring | nullnullRequired for land brain
allowBinsstring[]['hcpx', 'hcp'] plus every pack's wrap.binSandbox allow-list
projectDirstringnoneWorking directory for packs with wrap.workspace.mode: project; without it those commands fail with no_project
accessAccessConfig{ mode: 'auto', approvals: 'agent' }Mode, per-pack modes, rules, approval authority. See Access
approvalsApprovalStorenoneWhere approval requests wait for a person
auditAuditSinknoneReceives one AuditEvent per decision
viewsApprovalViewOptionsnoneExtra approval-view inference and signal rules, tried before the defaults

Methods#

MethodReturnsBehaviour
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)voidReplaces the authz grants
sandboxModeHcpSandboxMode'sandbox' | 'native-local'
authzHcpAuthzContextPrincipal, tenant, project, grants

Runtime drivers#

TypeScript
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.
  • 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).

Both come wrapped in access control. Hcp.create({ runtime: myDriver }) wraps your own driver the same way.

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

ExportPurpose
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
TypeScript
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 prints the JSON Schema generated from them.

Grants#

TypeScript
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: 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 and the approval options of draft RFC 0007, and Access explains the behaviour.

ExportPurpose
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_FIELDSThe 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#

ExportPurpose
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 explains how a pack declares them.

Check and eval#

The check entry point exposes hcpx check and hcpx eval as functions:

TypeScript
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))
ExportPurpose
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'
TypeScript
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 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#

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

TypeScript
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 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.

Deprecated aliases#

Systems* policy schema names and parseSystemsSurfacePolicyFile / lintSystemsSurfacePolicy remain exported and map to their Hcp* equivalents.