Skip to main content
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.
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.
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.
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>.

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

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.