# enable New Possibilities: Building with the Claude Agent SDK

> Discover how the Claude Agent SDK lets builders with real filesystem access, enabling custom skills, hooks, and smooth integrations for capable agent-dri
> By Dave · 2026-08-28
> Source: https://otf-kit.dev/blog/claude-code-agent-sdk

The Claude Agent SDK put something genuinely new on the table for builders: an agent loop that reads a real filesystem, runs real commands, and edits real files — not just a chat box. That's the enable, and it's a real one. This post is what you can actually build with it from a builder's seat, and what the repo the agent lands in has to look like for the loop to be worth running.

## What the SDK actually puts in your hands

Four commonly-discussed primitives show up in every serious walkthrough, and they're worth naming so the rest of the post makes sense:

1. **A real-filesystem agent loop.** The agent reads files, edits them, runs shell commands, and iterates on the result. It's not hallucinating an answer; it's acting on disk — with all the screw-ups and recoveries that implies.
2. **Skills.** Bundles of instruction plus capability — a folder of files that teach the agent a new trick without retraining the underlying model.
3. **Hooks.** Event-driven side effects. A tool call goes out, your code intercepts it, and you can block, log, or augment before it lands.
4. **MCP servers.** Pluggable tool registries. Point the agent at a server that speaks the protocol, and new tools show up — no SDK fork required.



![a builder feeding an agent loop, with four rails converging on the same workspace — filesy](https://cdn.otf-kit.dev/blog/claude-code-agent-sdk/inline-1.png)



The convergence is the part most demos skip. The agent isn't a chatbot with tools bolted on. It's a loop over a workspace, and everything you wire into that workspace becomes part of what the agent can do. That changes what "using the agent" means day to day — you're not prompting a model, you're configuring a runtime.

## A custom skill, in shape

A skill is roughly a directory of files plus a short manifest describing what it is and when to use it. The agent discovers the folder, reads the manifest, and decides whether the user's intent matches. The match is where the design work happens — a vague manifest fires too often, an over-specific one never fires.

```bash
# tree of a real skill (shape — see current docs)
skills/
  deploy-staging/
    MANIFEST.md        # what it does, when to fire
    scripts/
      deploy.sh        # the actual work
    examples/
      last-run.log     # what success looks like
```

```md
<!-- skills/deploy-staging/MANIFEST.md -->
# Deploy to staging

Fire when the user asks to ship, push, or deploy to staging.

## Steps
1. Run `scripts/deploy.sh` from the repo root.
2. Wait for "build complete" in the output.
3. Compare output against `examples/last-run.log` shape; report deltas.
4. Never run without `DEPLOY_CONFIRM=1` in env.

## When NOT to use this
- Production deploys (different skill).
- Local dev server (`npm run dev`).
```

(Manifest filename and exact schema vary by SDK version — treat the above as the shape of the pattern, not the contract.)

The manifest is the contract with the agent. You didn't teach the model anything — you handed it a folder and told it what's inside. That's a one-shot capability upgrade you can ship today, and the agent's next run picks it up.

## A hook that catches the bash mistake

Hooks are how you keep an autonomous loop honest. The most useful pattern is a pre-tool-use gate: before the agent runs a shell command, your code gets a veto. This is where you encode the rules a junior engineer would have been told on day one — the agent never was.

```ts
// .claude/hooks/block-dangerous-bash.ts
// (Pseudocode — event shape and config keys vary by SDK version;
// see current docs before relying on the exact fields below.)

export default async function preToolUse(event) {
  if (event.tool !== "bash") return;
  const cmd: string = event.input.command;

  const forbidden = [
    /rm\s+-rf\s+~\//,        // home dir wipe
    /git push.*--force/,     // force push
    /curl.*\|\s*sh/,         // curl-pipe-shell
    /DROP\s+TABLE/i,         // destructive SQL
  ];

  for (const re of forbidden) {
    if (re.test(cmd)) {
      return { action: "deny", reason: `Blocked by pattern ${re}` };
    }
  }
}
```

```json
// .claude/settings.json (illustrative config — see current docs)
{
  "hooks": {
    "PreToolUse": [".claude/hooks/block-dangerous-bash.ts"]
  }
}
```

The agent still sees the bash call in its reasoning trace. It just never lands. This is the cheapest way to keep an autonomous loop from doing something irreversible on a Tuesday afternoon, and it composes — you can stack ten hooks, one per concern, and the agent never knows.

## Plug in an MCP server for one tool

MCP is the boring part that's quietly the most capable. You can hand the agent access to a tool it has no native knowledge of — an internal DB, a third-party API client, a search index — by pointing it at a server that speaks the protocol.

```json
// .claude/mcp.json (illustrative — see current docs)
{
  "mcpServers": {
    "<your-integration>": {
      "command": "npx",
      "args": ["-y", "<your-mcp-server-package>"],
      "env": { "<YOUR_API_KEY>": "${env:YOUR_API_KEY}" }
    },
    "<internal-docs>": {
      "command": "node",
      "args": [".mcp/internal-docs-server.mjs"]
    }
  }
}
```

The agent now sees tools prefixed with `<your-integration>_*` and `<internal-docs>_*` in its available tool list — no SDK code change, no model retraining. New team, new tool, new server, same agent. The protocol is the contract, which is why MCP is a bigger deal than its quiet reputation suggests: it turns "the agent has tools" into "the agent has whatever tools your org decides to ship."

## The repo is half the agent

The SDK is genuinely good. But — and this is the part that doesn't show up until you run the same agent on two different repos in the same week — the repo it lands in matters as much as the agent itself.



![a convention-documented kit repo vs a year-old generated-spaghetti repo, run through the s](https://cdn.otf-kit.dev/blog/claude-code-agent-sdk/inline-2.png)



Same agent, same model, same prompt, same task. The delta on first-task success rate is consistent and significant. The model didn't get worse. The substrate did. The agent reads your repo the way a senior engineer reads your repo: if the conventions are clear, it follows them; if they're not, it invents ones that don't exist, and the code drifts a little every iteration.

Most "the AI slowed down" stories come from this, not from the agent. The agent is doing its job. The job description — the repo — is the problem.

## Three things a substrate needs

None of this is expensive. All of it is mandatory if you want the agent to perform a tier higher than it does in an unstructured repo:

1. **A root-level convention file.** `CLAUDE.md`, `.cursorrules`, or `AGENTS.md` — whichever your harness reads. It states where components live, how tests run, the canonical way to add a feature, and what NOT to do. One file, top of the tree, written in plain prose.
2. **A predictable layout.** One folder per concern. `app/`, `components/`, `lib/`, `db/`, `tests/`. The agent doesn't need to be clever — it needs to know where to look. Predictability beats elegance every time for an automated reader.
3. **A `prompts/` folder of tested instructions.** Not vibes — instructions you've actually run, that produced the output you wanted. Twenty verified prompts beats two hundred you wrote once and forgot. Each one is a checked-in contract: "when the user asks for X, do Y."

If your repo has all three, the same agent performs a tier higher than it does in a repo without them. We've measured this across multiple kits. The delta isn't subtle, and it's not a model-side win — it's a substrate-side win.

## The pre-wired advantage

Use the SDK. And here's the part that doesn't change when the model does.

Every OTF kit — the SaaS Dashboard, the Fitness app, the Booking one — ships with the three substrate items already in place, so the agent has a substrate worth running on from minute one:

- A `CLAUDE.md` at the root that names where things live and why. Plain prose, no magic.
- A `.cursorrules` file saying the same thing in the format Cursor wants, so a second agent with a different harness reads the same conventions without you rewriting them.
- A `prompts/` folder of 20+ tested instructions: "add a new pricing tier", "wire a new Stripe webhook", "add a screen to the mobile app". Each hand-verified to produce working output — not aspirational, machine-checked.

On top of that, a 24-item design checklist is enforced by a script before any kit ships. The conventions aren't aspirational — they're checked at build time, so the substrate the agent lands in is the same substrate the kit shipped. No drift between the doc the agent reads and the code it's editing.

You drop the agent into the kit, and on day one it knows where the components are, how to add a feature, and what NOT to do. You're not spending week one teaching the agent your conventions. They're already written down.



![clay character confidently running the agent loop inside a structured kit repo, with the c](https://cdn.otf-kit.dev/blog/claude-code-agent-sdk/inline-3.png)



That part doesn't change when Anthropic ships a new SDK version, when a competitor ships a different agent, or when the model under the hood gets swapped. The conventions are durable. The agent is the perishable layer.

## What this enables

A second developer — the AI one — that ramps in a day instead of a quarter. Same loop, same hooks, same MCP servers. The only variable is the substrate it's dropped into. Make the substrate legible and the agent pays you back ten times. Leave it as generated spaghetti and the agent becomes the thing you blame for the mess.

The SDK is the lever. The repo is the lever's length. You decide which one to invest in.