---
title: "AI SDK"
description: "`HarnessAgent` builds a Vercel AI SDK ToolLoopAgent from activated packs, with grant-filtered tools, approvals on writes and Skills loaded on demand."
canonical: https://harnesscontextprotocol.io/docs/hosts/ai-sdk
---

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

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