AI SDK

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

On this page

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#

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

hcprequired
Hcp

The runtime. Packs must be activated.

runtime
'ai-sdk' | 'opencode'default ai-sdk

'ai-sdk' builds a ToolLoopAgent; 'opencode' drives an OpenCode session over the same packs.

model
LanguageModel

AI SDK language model (ai-sdk runtime).

harness
string

Identifier for the teaching harness. It selects instructions, not capabilities.

instructions
string

Extra system instructions appended after the generated stable prefix.

maxSteps
number

Tool-loop step cap.

cwd
string

Working directory (opencode runtime).

adoptOpenCode
booleandefault true

Write native OpenCode intake before the run.

context
ContextOptimizationOptions

Context optimisation knobs; see below.

What gets generated#

FromTo
Activated packs + Policy + grantsGrant-filtered tool map: one tool per executable command, named hcpToolName(pack_id, verb_key)
HARNESS.md leadsA stable instruction prefix (prompt-cache friendly)
Full Skillshcp__read_skill tool for on-demand loading
Commands with operation: writetoolApproval with needsApproval: true
Long tool resultsprepareStep compaction between steps

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:

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

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