# Extending kin — tools, kinds, and profiles

> How to add a new tool to the kin harness. This page is the user-facing mirror of the canonical walkthrough in src/kin/harness/AGENTS.md — the source-tree version stays the single source of truth; this page exists so a…

How to add a new tool to the kin harness. This page is the user-facing
mirror of the canonical walkthrough in
[`src/kin/harness/AGENTS.md`](https://github.com/kinra-ai/kin/blob/dev/src/kin/harness/AGENTS.md#tool-author-guide)
— the source-tree version stays the single source of truth; this page
exists so a contributor landing on `docs/` doesn't have to know to
navigate into `src/kin/harness/AGENTS.md` first.

<!-- SOURCE: src/kin/harness/AGENTS.md, src/kin/harness/tools/registry.py, src/kin/harness/tools/_util.py, src/kin/harness/permissions.py, src/kin/harness/modes.py, src/kin/harness/agents.py -->

## Adding a tool

Tools live in `src/kin/harness/tools/` (`kin.harness.tools`). The
`Tool` base class + `ToolContext` are in `tools/registry.py`.

**To add a tool:**

1. **Subclass `Tool` and set the class attrs:**

    - `name` — what the model calls (`"web_fetch"`, `"shell"`, etc.).
    - `description` — short, what the tool does. The model reads this;
      keep it tight.
    - `parameters` — JSON Schema for the args. Strict-decode opt-in via
      `KIN_STRICT_TOOLS` (see [Models & providers](/docs/kin/guide/models-and-providers/)).
    - `kind` — the static risk class the gate reads (`READ` / `EDIT` /
      `SHELL` / `META` / `MCP` / `NETWORK` / `RIG` / `OUTPOST` /
      `PUBLISH`; see [Permissions](/docs/kin/concepts/permissions/)). This
      drives the mode → permission decision. A NEW kind must land in
      both mode-policy dicts + the planning-freeze tuple in one change.
      An unmapped kind fails closed to `ASK`.

    A bounded multi-operation tool may override
    `permission_kind(args)` to resolve the concrete call's risk. It must
    fail closed for missing/unknown operations. Four further optional
    call-level seams exist:

    - `approval_scope(args, workdir)` — reusable signature + human label;
    - `approval_reusable(args)` — false removes the Always tier;
    - `requires_serial(args)` — preserve order for a call that may open a
      modal or race a sibling effect;
    - `snapshots_before_run(args)` — request the edit snapshot safety net
      when the risk kind alone does not imply it.

    `GitTool` is the reference implementation. These methods affect generic
    loop behavior; do not add a one-off dispatch path for a new tool.

2. **Implement `async run(args, ctx) → str`** (or a
   `list[ContentBlock]` for multimodal results — see
   `tools/_media.py`; the loop normalizes both via `coerce_content`).
   Use the `ctx` helpers:

    - `ctx.emit(event)` — post an `Event` to the UI / journal.
    - `ctx.progress(text)` — live-tail text in the UI (for streaming).
    - `ctx.parent_tool_call_id` — **MUST** be threaded onto any
      `tool_stream` event you emit, or the UI can't group the stream
      under the right tool call.

3. **Untrusted output** (web / file external content) must go through
   `frame_untrusted` in `tools/_util.py` before entering history. See
   [`REFERENCE.md`](https://github.com/kinra-ai/kin/blob/dev/REFERENCE.md)
   § "Untrusted content".

4. **Numeric args** should use `coerce_int` (in `tools/_util.py`) —
   don't bare-`int()` model args; junk → default, clamps. **Boolean
   args** use `coerce_bool` — never raw truthiness: a hallucinated
   `"false"` string is Python-truthy but a falsy token under the
   contract (see `REFERENCE.md` critical invariant — the contract is
   load-bearing anywhere permission scope keys off a boolean).

5. **Register the tool** in `tools/__init__.py::default_registry()`.
   Most tools register unconditionally; the default-on `search_workspace`
   tool keeps a conditional `search_enabled` kill-switch path. Ordinary
   `git` / `github` and Beam's internal Git trio are deliberately disjoint,
   and child registries remove publication tools. The `outpost` /
   `outpost-send` pair follows the same default-off-by-absence
   pattern, registering only once both `outpost_url` and
   `outpost_token` resolve.

6. **Add coverage under `tests/test_harness/`** (and `tests/test_web.py`
   if it's a web tool). The harness suite is the commit gate — a tool
   without a verify assertion is a silent regression waiting to
   happen.

**Process / subprocess tools** (currently just `shell.py`) need
`start_new_session=True` for process groups, and cleanup in `finally`.
See `REFERENCE.md` § "Process & subprocess".

## Adding a permission kind

The mode → permission gate lives in `src/kin/harness/permissions.py`
+ `src/kin/harness/modes.py`. There are nine built-in kinds: `READ`,
`EDIT`, `SHELL`, `META`, `MCP`, `NETWORK`, `RIG`, `OUTPOST`, `PUBLISH`. To add
another:

1. **Add the constant** to `permissions.py` (e.g. `EGRESS = "egress"`).
2. **Map it in BOTH mode-policy dicts** — `AUTO.policy` and
   `STRICT.policy` in `modes.py`. The default is fail-closed
   (`policy.get(kind, perm.ASK)` in `Mode.decide`), but an omission still
   changes the intended posture and fails the structural gate.
3. **Add it to `loop._planning_freezes`** if the kind should be
   denied while planning (`NETWORK`, `MCP`, `RIG`, and `PUBLISH` are).
   All three lists must change together so policy, UX, and planning
   semantics cannot drift.
4. **Add a kind-row entry to `docs/guide/modes-and-permissions.md`** with a
   one-line summary of the new risk class + mode behavior.
5. **Add a verify assertion** under `tests/test_harness/` that asserts the
   new kind is a key in BOTH `AUTO.policy` and `STRICT.policy`, and
   in `_planning_freezes` if applicable. The structural test is the
   load-bearing backstop.

## Adding a subagent profile

Subagent profiles (the `.md` files behind `task <profile>` and the
`/critique` / `/security-review` / `/deep-research` bundled
workflows) live in `src/kin/harness/defaults/agents/` (bundled) +
`.kin/agents/` + `.claude/agents/` (user + project, project wins).

1. **Create `<name>.md`** with frontmatter:

    ```markdown
    ---
    name: <name>
    description: <one-line, model-readable>
    max-turns: 0      # optional — 0/omitted = unlimited; positive values ≤1000
    tools:            # optional — omit entirely for "all tools available"
      - read_file
      - grep
      - shell
    ---

    <system-prompt body — same shape as the harness `compose_system`>
    ```

    Bundled profiles omit `max-turns`: registered children are supervised by
    the MCA and run until completion unless it pauses, interrupts, restarts, or
    kills them. Use a positive value only as an intentional operator leash;
    the root-shared token budget and doom-loop guard are the normal runaway
    boundaries.

    `tools:` is a YAML list of exact tool names (not a `read|write|edit`
    shorthand) — see the names registered in
    `tools/__init__.py::default_registry()`. There's no `model:`
    frontmatter key: a profile always inherits the parent's model unless
    the dispatcher passes a per-call `model` argument to `task(...)`.

2. **For a profile that ships with kin** (an exemplar), drop the file
   in `src/kin/harness/defaults/agents/`. For a user-only profile, drop
   it in `.kin/agents/<name>.md` or `.claude/agents/<name>.md`.

3. **For a critic-style profile** (read-only, rubric-based), follow the
   `critic-code.md` template — see
   `src/kin/harness/defaults/agents/critic-code.md` for the rubric +
   severity-sorted output contract.

4. **If the profile is dispatched by a bundled command** (like
   `/critique`), pair it with a `defaults/commands/<name>.md` that
   routes the body as `kind: workflow` and dispatches the agent
   subagent via the `agent()` primitive.

5. **Add coverage** — `tests/test_harness/test_skills_agents.py` /
   `test_subagents.py` for profile resolution + tool-allow-list
   enforcement; `tests/test_workflow.py` for the bundled-command wiring.

See `src/kin/harness/AGENTS.md` `agents.py` row for the discovery
mechanics (5-root walk + project-shadows-bundled).

## Where to read next

- [`src/kin/harness/AGENTS.md`](https://github.com/kinra-ai/kin/blob/dev/src/kin/harness/AGENTS.md)
  — the canonical per-file harness guide (the source of truth for
  everything on this page).
- [`docs/concepts/permissions.md`](/docs/kin/concepts/permissions/) — the
  conceptual frame for the mode → permission gate.
- [`docs/guide/tools.md`](/docs/kin/guide/tools/) — the tool catalog (what
  each existing tool does, the kind each carries).
- [`REFERENCE.md`](https://github.com/kinra-ai/kin/blob/dev/REFERENCE.md)
  — every gotcha, footgun, security invariant, and postmortem. Read
  before touching the permission gate, the planning freeze, or any
  tool that touches the filesystem / network.
