> For the complete documentation index, see [llms.txt](https://docs.perception.cx/perception/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.perception.cx/perception/enma-ai/extending.md).

# Extending

### MCP servers

Connect MCP servers over stdio, streamable HTTP or legacy SSE. Their tools become available to every agent, image results render as real images, and each MCP tool can be toggled off like a built-in. Tool names are sanitized automatically so strict providers accept them.

### Hooks

Run your own command before or after any tool call (Settings, Tools tab):

* A **pre-hook** that exits non-zero blocks the call, and the agent reads your hook's output as the reason. Use it for policy: "no installs", "never touch this folder".
* A **post-hook**'s output is appended to the tool result. Use it for linting, logging or reminders.

Hooks receive the tool name, arguments, result tail and project dir via `ENMA_*` environment variables and run in your project folder through the system shell. A hook that crashes or times out never blocks the run (default timeout 10 s, configurable per hook). Agents cannot create or edit hooks.

#### A hook rule has three parts

* **Event**: before (preToolUse) or after (postToolUse) the tool call.
* **Tool match**: empty matches every tool, plain text is a substring match (`edit` matches `edit_file` and `edit_and_verify`), and `*` makes it a glob (`*_file` matches `write_file`, `edit_file`, `append_file`).
* **Command**: any shell command. It sees `ENMA_TOOL`, `ENMA_ARGS_JSON`, `ENMA_PROJECT_DIR`, and on post-hooks `ENMA_RESULT_TAIL`.

#### Example: block package installs (pre-hook)

Event `preToolUse`, tool match `run_shell`, command:

```
node -e "const a=process.env.ENMA_ARGS_JSON||''; if(/npm +i(nstall)?\b|yarn add|pnpm add/i.test(a)){console.log('Installs are blocked in this project. Ask the user to run installs themselves.');process.exit(1)}"
```

When the agent tries `npm install`, the hook exits 1, the call is blocked, and the agent reads your printed line as the reason. Everything else passes through untouched.

#### Example: typecheck after every edit (post-hook)

Event `postToolUse`, tool match `edit`, command:

```
npx tsc --noEmit
```

Whatever the command prints is appended to the tool result, so the agent sees fresh type errors immediately after its own edit and fixes them in the same run. On a large repo, raise this hook's timeout.

{% hint style="info" %}
Print nothing and exit 0 for "all good": silent hooks add zero noise to the transcript.
{% endhint %}

### Custom API references

Store your own API docs in Settings and agents pull them on demand while coding, instead of guessing at your internal endpoints.

### Web research

Web search is built in and keyless by default, with Tavily, Brave, Serper or SearXNG by key. URL fetch extracts readable content and renders source cards.

### Datasets

Scan a repo into a training dataset with chat, alpaca, completion and fim exports.
