> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Extending Narya

> Add tool servers, OpenAPI tool trees, confined scripts, agents, skills, commands and hooks, or drive Narya over its local API.

Narya has two public extension surfaces: the host API over a Unix socket, and confined scripts. Around them sit the resources you declare in files.

## MCP tool servers

***

Declare a server as `[tool_servers.<name>]` in `config.toml`. Give it exactly one transport. `command` spawns a process and speaks stdio. `url` speaks streamable HTTP over https, or over http on loopback. A declaration with both, or with neither, refuses to load.

```toml theme={null}
[tool_servers.filesystem]
command = ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/repo"]
env = { PATH = "/usr/local/bin:/usr/bin:/bin", HOME = "/home/you" }
```

<Warning>
  `env` replaces the child environment. It does not add to it. The child inherits nothing, not even `PATH` and `HOME`. Declare both for any `npx` or `uvx` server, or the child exits before it serves a tool.
</Warning>

The other keys are `dir`, `headers`, `oauth`, `timeout_ms` and `enabled`. Every declared server connects at startup unless you set `enabled = false`.

Narya keeps one long-lived session per server and reconnects with backoff. When a server disconnects, Narya withdraws its tools from the registry, so the model never sees a departed server's tools.

Set `oauth = true` for a remote server that signs in. Narya holds the connection open, prints the authorization URL, and listens on `127.0.0.1:7666` for the callback while the sign-in is open. A sign-in nobody answers costs that one server and nothing else. A one-shot run cannot complete a sign-in, and it says so.

<Warning>
  A declared tool server runs under the `tool` rule, which the shipped set allows. Narya's write and secrets rules do not apply to what the server itself reads or writes. Narrow it with `ask tool <name>` or `deny tool <name>`.
</Warning>

## OpenAPI tool trees

***

Declare `[tool_trees.<name>]` to turn an OpenAPI document into a callable tool tree. The keys are `spec`, `base_url`, `headers`, `timeout_ms` and `enabled`. A relative `spec` path resolves against your Narya home. Set `base_url` when the document declares no server of its own. Narya derives the tools once at startup. No server runs, and no session reconnects.

## Confined scripts

***

Narya runs ECMAScript in a confined engine. The engine reaches no filesystem, no network, no environment and no module loader. It has no dynamic evaluation. Call depth stops at 4,096, and source stops at 256 KiB. Three callers use it: the model's `run_program` tool, workflows, and tool discovery.

## The host API

***

The host serves one HTTP API over a Unix socket, and every client drives it. [The host API](/en/narya/the-host-api) describes the transport, the event stream and the envelopes.

## Agents, skills, commands and hooks

***

Narya reads Claude Code's own formats without any change.

**Agents** are markdown files with YAML frontmatter. `name` is the only field a file must carry. The others are `description`, `prompt`, `tools`, `model`, `mode` and `hidden`. `tools` is an allowlist and never a grant: Narya intersects a child's list with its parent's, so a child agent can lose tools, never gain them.

Narya reads agents from your Narya home, then `~/.claude/agents`, then the `.claude/agents` and `.narya/agents` of a repository you trust. Four agents ship with the binary: `explore`, `general`, `plan` and `review`.

**Skills** follow the Agent Skills standard: a directory that holds a `SKILL.md`. Narya reads the same ladder, `~/.claude/skills` included, and re-reads it on each request. A skill you add mid-session reaches the next turn.

**Commands** come from your Narya home, from `~/.claude/commands`, and from the `.claude/commands` and `.narya/commands` of a repository you trust.

**Hooks** come from the `hooks` table of `~/.claude/settings.json`, and from the same file in a repository you trust. Narya reads them with the stdin keys and exit codes those scripts already use. Four events map: `PreToolUse`, `PostToolUse`, `UserPromptSubmit` and `SessionStart`. Five have no equivalent, so Narya skips them and writes a line on standard error: `Stop`, `SubagentStop`, `Notification`, `PreCompact` and `SessionEnd`.

<Warning>
  Three differences have no workaround. Narya reads hook output for a verdict only, so `additionalContext` never reaches the model's context. `systemMessage` goes nowhere. A hook read from `settings.json` always fails open: a hook that times out, exits non-zero, or prints output Narya cannot read lets the call through, and this format has no field to change that. Its `tool_response` arrives as a string, not an object.
</Warning>

## Narya's own hooks

***

Declare `[[hooks]]` in `config.toml` with `point`, `command`, `timeout_ms`, `fail` and `match`. `fail` carries no default, so state it. `match` filters a `before-tool-call` hook to one tool name.

Four points decide, and a hook there can stop what it fronts:

`before-tool-call`, `before-model-request`, `after-tool-call`, `at-permission-ask`.

Eight points observe, and a hook there learns what happened without changing it:

`session-started`, `lane-started`, `lane-finished`, `delegation-created`, `child-asked`, `skill-invoked`, `permission-denied`, `session-compacted`.

A repository's own file cannot declare `at-permission-ask`.

## Delegation

***

Delegation is a tool call. Set its bounds under `[delegation]` in `config.toml`. The `max_depth` key counts the levels below your conversation, and `0` turns delegation off. Set `max_concurrent` to bound how many lanes one conversation runs at any instant.
