diff --git a/reference/configuration/options.md b/reference/configuration/options.md
index cbd7ce5d0..3226a84ef 100644
--- a/reference/configuration/options.md
+++ b/reference/configuration/options.md
@@ -460,7 +460,7 @@ rootPath: /var/lib/harper
Added in: v5.2.0
-Built-in Harper agent — an LLM loop that operates this instance through Harper's own operations, scoped filesystem access, followup scheduling, the V8 inspector, and outbound HTTP. Disabled by default, so it never incurs LLM cost unless you turn it on. Driven through the [Agent operations](../operations-api/operations.md#agent), which also describe the privilege boundary before you enable it.
+Built-in Harper agent — an LLM loop that operates this instance through Harper's own operations, scoped filesystem access, followup scheduling, the V8 inspector, and outbound HTTP. Disabled by default, so it never incurs LLM cost unless you turn it on. Driven through the [Agent operations](../operations-api/operations.md#agent), which also describe the privilege boundary before you enable it. Each of its own tool groups can be [turned off](#turning-off-tool-groups).
```yaml
agent:
@@ -488,9 +488,41 @@ agent:
- `componentsScope` — The agent's `components` filesystem scope, which it reads and, with `allowDestructive`, writes; absolute or relative to `rootPath`. Read at startup only; _Default_: the full `componentsRoot`
- `configScope` — The agent's read-only `config` filesystem scope: a file or a directory, absolute or relative to `rootPath`. Read at startup only. See [Filesystem scopes](#filesystem-scopes); _Default_: the Harper config file only
- `httpFetch` — Whether the agent has its `http_fetch` tool, and which hosts it may reach: `true`, `false`, or `{ allow: [...] }`. Read at startup only. See [Restricting `http_fetch`](#restricting-http_fetch); _Default_: `true`
+- `tools` — Which of the agent's own tool groups are in its toolset, one boolean each: `filesystem`, `schedule`, `inspector`, and `bestPractices`. `false` removes the group. Read at startup only. See [Turning off tool groups](#turning-off-tool-groups); _Default_: every group on
- `systemPromptAppend` — Operator text appended to the agent's system prompt
-`enabled`, `provider`, `model`, `maxTurns`, `maxTokens`, `maxToolResultBytes`, `maxSessionTokens`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend` can also be changed at runtime with [`set_agent_config`](../operations-api/operations.md#set_agent_config), which applies in memory only. `enabled` is the exception worth knowing: it cannot switch the agent on, because with the agent disabled at startup no agent operation is registered at all. `httpFetch`, `componentsScope`, and `configScope` are read at startup only, and `set_agent_config` rejects them.
+`enabled`, `provider`, `model`, `maxTurns`, `maxTokens`, `maxToolResultBytes`, `maxSessionTokens`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend` can also be changed at runtime with [`set_agent_config`](../operations-api/operations.md#set_agent_config), which applies in memory only. `enabled` is the exception worth knowing: it cannot switch the agent on, because with the agent disabled at startup no agent operation is registered at all. `httpFetch`, `tools`, `componentsScope`, and `configScope` are read at startup only, and `set_agent_config` rejects them. [`get_agent_config`](../operations-api/operations.md#get_agent_config) returns the configuration in effect, the startup-only keys included.
+
+### Turning off tool groups
+
+
+
+Besides the operations tools, which run as `agent.user`, the agent has tools of its own that run at the Harper process's privilege: the filesystem tools, `schedule_followup`, the V8 inspector tools, `http_fetch`, and the best-practices lookup. `allowDestructive: false` removes only the ones marked destructive, and `maxTurns: 1` still lets the model call tools within that turn. To run the agent without a group, turn it off under `tools`:
+
+```yaml
+agent:
+ enabled: true
+ httpFetch: false
+ tools:
+ filesystem: false
+ schedule: false
+ inspector: false
+ bestPractices: false
+```
+
+| Key | Tools removed when `false` |
+| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
+| `filesystem` | `read_file`, `write_file`, `list_dir`, `grep_files`, `tail_file` |
+| `schedule` | `schedule_followup` |
+| `inspector` | `inspector_attach`, `inspector_evaluate`, `inspector_set_breakpoint`, `inspector_set_logpoint`, `inspector_profile_cpu` |
+| `bestPractices` | `harper_best_practice`, and the best-practices overview leaves the system prompt |
+
+Every key defaults to `true`, so an existing configuration is unchanged. The behavior worth knowing:
+
+- **A group that is off is gone, not gated.** The model is sent no schema for its tools, the system prompt does not describe them, and a call to one by name is answered as an unknown tool and runs nothing. With every group and `httpFetch` off, the toolset holds only the operations tools `agent.user` may call, which is the shape for an application that sends the agent a prepared brief and wants an answer in one turn.
+- **A disabled group's tool names stay reserved.** No operations tool or injected tool can take the name of a tool that is off, so turning a group off never changes what a call to one of its names does. This was already so for `http_fetch`.
+- **Read at startup only.** Set it in the configuration file, through `set_configuration`, through `HARPER_SET_CONFIG`, or with the `AGENT_TOOLS_FILESYSTEM`, `AGENT_TOOLS_SCHEDULE`, `AGENT_TOOLS_INSPECTOR` and `AGENT_TOOLS_BESTPRACTICES` environment variables or CLI flags, then restart. `set_agent_config` rejects a request that includes `tools` with a 400 and applies nothing from it; a later `set_agent_config` that changes `allowDestructive` rebuilds the toolset with the same switches. `get_agent_config` reports the switches in effect, so an application can check at startup that the groups it needs off are off. As with `httpFetch`, a switch holds against the agent itself only while `set_configuration` and `restart` stay out of its toolset (they are not in the default operations allow list) or behind [approval](../operations-api/operations.md#approve_agent_action): with both, a prompt-steered run could write the switch back on and restart.
+- **A malformed value turns groups off, never on.** A value other than `true` or `false` turns its group off, and an unknown key under `tools`, or a `tools` that is not a map, turns every group off, since a misspelled key may have been meant to turn a group off. The agent logs an error naming what to correct and starts with the rest of its configuration.
### Filesystem scopes
diff --git a/reference/operations-api/operations.md b/reference/operations-api/operations.md
index d1ebc7f0f..23b627d69 100644
--- a/reference/operations-api/operations.md
+++ b/reference/operations-api/operations.md
@@ -2039,13 +2039,13 @@ Operations for driving Harper's built-in agent — an LLM loop that operates the
The agent component is **disabled by default**. Enable it with `agent.enabled: true` in `harper-config.yaml` (see [`agent`](../configuration/options.md#agent)) and configure a generative model under [`models`](../models/overview.md#configuration). With the component disabled at startup none of these operations are registered, so calling one is an unknown-operation error rather than a permission or state error.
-All six operations are `super_user` by default. They participate in the role [`operations` allowlist](../users-and-roles/overview.md#operation-permissions), so a non-`super_user` role can be granted a scoped subset without granting full `super_user` — either individual names (`operations: ['agent_prompt', 'get_agent_session']`) or the built-in [`agent` permission group](../users-and-roles/overview.md#operation-permissions), which covers all of these except `set_agent_config`. Note that the read operations are not caller-scoped: a role granted `get_agent_session` or `list_agent_sessions` reads every session on the instance, including transcripts of runs it did not start. Because a transcript records the _arguments and output_ of every tool call, and those calls ran as `agent.user`, delegating a read operation hands that role the results of work done at the agent's privilege — table contents, log excerpts, configuration — regardless of its own permissions. Delegate the read operations only to roles you would trust with the agent itself.
+All seven operations are `super_user` by default. They participate in the role [`operations` allowlist](../users-and-roles/overview.md#operation-permissions), so a non-`super_user` role can be granted a scoped subset without granting full `super_user` — either individual names (`operations: ['agent_prompt', 'get_agent_session']`) or the built-in [`agent` permission group](../users-and-roles/overview.md#operation-permissions), which covers all of these except `set_agent_config` and `get_agent_config`. Note that the read operations are not caller-scoped: a role granted `get_agent_session` or `list_agent_sessions` reads every session on the instance, including transcripts of runs it did not start. Because a transcript records the _arguments and output_ of every tool call, and those calls ran as `agent.user`, delegating a read operation hands that role the results of work done at the agent's privilege — table contents, log excerpts, configuration — regardless of its own permissions. Delegate the read operations only to roles you would trust with the agent itself.
:::warning
Anyone who can call `agent_prompt` can direct whatever the agent does. Understand the boundary before enabling it:
- `agent.user` (default: a `super_user` bootstrap identity) governs only the **operations** tools. Setting it to a restricted user narrows those, and nothing else.
-- The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, and the V8 inspector — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.)
+- The agent's other tools — scoped filesystem access, outbound `http_fetch`, followup scheduling, the V8 inspector, and the best-practices lookup — run at the Harper process's own privilege, whatever `agent.user` is. (The inspector tools additionally need `threads.debug`, and fail with an explanatory error without it.) Since v5.4.0, each of these groups can be removed from the toolset at startup with [`agent.tools`](../configuration/options.md#turning-off-tool-groups) and [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch); with all of them off, the agent has only the operations tools `agent.user` may call.
- By default, `http_fetch` blocks only cloud-metadata hosts and the IPv4 link-local range `169.254.0.0/16`. It checks host names, not resolved addresses, so a name that resolves to a blocked address is not caught. Since v5.4.0, it also checks every redirect hop before following it. Every other host is reachable, including anything private or internal the server can route to. Reading is ungated too. By default, `read_file` covers the component tree, the log directory, and the Harper config file. Before v5.4.0, it covered the whole directory holding the config file. `componentsScope` and `configScope` can point it elsewhere, including somewhere wider. Every scope refuses Harper's key directories, key file names, and PEM private keys. See [Filesystem scopes](../configuration/options.md#filesystem-scopes). An enabled agent therefore has a read path and an egress path in the same toolset. Unless the agent needs the network, set [`agent.httpFetch`](../configuration/options.md#restricting-http_fetch) to `false` to remove the tool, or to an allow-list of the hosts it needs. Otherwise, treat it as an outbound network client and apply egress policy to the host.
- With the default `agent.allowDestructive: false`, destructive tools (including filesystem writes) are removed from the toolset entirely. Turning it on admits component writes, and component code is executed by the Harper process — a write is effectively code execution at process privilege.
- Leave `agent.autoApprove` off so any destructive call that is admitted still pauses for [approval](#approve_agent_action). The gate covers only the tools marked destructive — filesystem writes, the inspector's code-evaluation tools, and the operations on MCP's [curated destructive set](../mcp/tool-metadata.md) (`drop_table`, `delete`, `restart`, `set_configuration`, ...). `http_fetch` and followup scheduling are not gated, so an outbound POST and a self-rescheduling run proceed without an approval prompt.
@@ -2060,6 +2060,7 @@ Anyone who can call `agent_prompt` can direct whatever the agent does. Understan
| `approve_agent_action` | Approves or denies a gated tool call and resumes the run | super_user |
| `cancel_agent_run` | Cancels a run and marks the session aborted | super_user |
| `set_agent_config` | Updates agent settings in memory for the life of the process | super_user |
+| `get_agent_config` | Returns the agent settings in effect | super_user |
### Sessions and run status
@@ -2220,7 +2221,7 @@ One gap is worth knowing: changing `allowDestructive` with [`set_agent_config`](
-Updates agent settings and returns the resulting configuration. Accepts any of `enabled`, `provider`, `model`, `maxTurns`, `maxTokens`, `maxToolResultBytes`, `maxSessionTokens`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend`; keys not supplied are left unchanged. Set `maxSessionTokens` or `maxCostUsd` to `null` to remove that cap. Each field is described under [`agent`](../configuration/options.md#agent). A request that includes `httpFetch`, `componentsScope`, or `configScope` is rejected with a 400 and nothing in it is applied: the [`http_fetch` policy](../configuration/options.md#restricting-http_fetch) and the [filesystem scopes](../configuration/options.md#filesystem-scopes) are read at startup only. A `maxTurns` or `maxTokens` that is not a positive integer, or a cap that is not a usable number, is rejected the same way. A `maxToolResultBytes` that is not an integer from `1024` to `1048576` is rejected the same way.
+Updates agent settings and returns the resulting configuration. Accepts any of `enabled`, `provider`, `model`, `maxTurns`, `maxTokens`, `maxToolResultBytes`, `maxSessionTokens`, `maxCostUsd`, `autoApprove`, `allowDestructive`, and `systemPromptAppend`; keys not supplied are left unchanged. Set `maxSessionTokens` or `maxCostUsd` to `null` to remove that cap. Each field is described under [`agent`](../configuration/options.md#agent). A request that includes `httpFetch`, `tools`, `componentsScope`, or `configScope` is rejected with a 400 and nothing in it is applied: the [`http_fetch` policy](../configuration/options.md#restricting-http_fetch), the [tool groups](../configuration/options.md#turning-off-tool-groups) and the [filesystem scopes](../configuration/options.md#filesystem-scopes) are read at startup only. A `maxTurns` or `maxTokens` that is not a positive integer, or a cap that is not a usable number, is rejected the same way. A `maxToolResultBytes` that is not an integer from `1024` to `1048576` is rejected the same way.
```json
{ "operation": "set_agent_config", "autoApprove": false, "maxTurns": 20 }
@@ -2232,9 +2233,35 @@ Three limits are worth knowing:
- **A run already in flight keeps the settings it started with** — its toolset, `autoApprove`, `model`, `maxTurns`, `maxTokens`, `maxToolResultBytes`, `systemPromptAppend` and spend caps are all captured at start. Changes take effect on the next run, and a paused run that `approve_agent_action` resumes counts as a new one: it picks up the current settings. To stop a run immediately, use `cancel_agent_run`.
- **`enabled` is not a kill switch.** It cannot turn the agent on — if it was off at startup, this operation does not exist. Setting it to `false` only makes subsequent `agent_prompt` calls return 409; a run already in flight continues, and `approve_agent_action` still resumes a paused one. Use `cancel_agent_run` to stop a run.
+### `get_agent_config`
+
+
+
+Returns the agent configuration in effect: the startup-only keys as resolved at startup (`httpFetch`, `tools`, `componentsScope`, `configScope`, `user`), and the runtime-tunable keys as last set by `set_agent_config`. It takes no parameters and changes nothing. An application that embeds the agent can call it at startup to confirm that the [tool groups](../configuration/options.md#turning-off-tool-groups) it needs off are off. Like `set_agent_config`, it is outside the `agent` permission group and is not exposed over MCP; grant it to a role by name if a delegated role needs it.
+
+```json
+{ "operation": "get_agent_config" }
+```
+
+```json
+{
+ "enabled": true,
+ "maxTurns": 50,
+ "maxTokens": 16384,
+ "maxToolResultBytes": 65536,
+ "autoApprove": false,
+ "allowDestructive": false,
+ "user": "hdb_agent",
+ "httpFetch": false,
+ "tools": { "filesystem": false, "schedule": false, "inspector": false, "bestPractices": false }
+}
+```
+
+A `tools` value reflects what the agent resolved, so a malformed setting shows as the groups it turned off. `provider`, `model`, `componentsScope`, `configScope`, `maxSessionTokens`, `maxCostUsd` and `systemPromptAppend` appear only when they are set.
+
### MCP access
-When the [MCP server](../mcp/overview.md) is enabled with the operations profile, `agent_prompt`, `get_agent_session`, `list_agent_sessions`, `approve_agent_action`, and `cancel_agent_run` are also exposed as MCP tools, with no allow-list entry required. They dispatch through the same authorization path as the operations above, and are listed only for users whose role could call them. `set_agent_config` is deliberately not exposed over MCP — it is an operator action.
+When the [MCP server](../mcp/overview.md) is enabled with the operations profile, `agent_prompt`, `get_agent_session`, `list_agent_sessions`, `approve_agent_action`, and `cancel_agent_run` are also exposed as MCP tools, with no allow-list entry required. They dispatch through the same authorization path as the operations above, and are listed only for users whose role could call them. `set_agent_config` and `get_agent_config` are deliberately not exposed over MCP — they are operator actions.
---
diff --git a/reference/users-and-roles/overview.md b/reference/users-and-roles/overview.md
index b7ac8d189..df6d2aafe 100644
--- a/reference/users-and-roles/overview.md
+++ b/reference/users-and-roles/overview.md
@@ -127,7 +127,7 @@ Groups expand to a predefined set of operations and can be mixed with individual
- `standard_user` — Everything in `read_only` plus full data manipulation and bulk load. Does not include any `super_user`-restricted operations, schema DDL (`create_attribute`), or token management. Additional operations beyond `read_only`: `insert`, `update`, `upsert`, `delete`, `csv_data_load`, `csv_file_load`, `csv_url_load`, `import_from_s3`
-- `agent` — Drive the [built-in agent](../operations-api/operations.md#agent) without full `super_user`. Operations: `agent_prompt`, `get_agent_session`, `list_agent_sessions`, `cancel_agent_run`, `approve_agent_action`. `set_agent_config` is deliberately excluded, so a delegated role cannot change the agent's model or approval policy. Read the agent section's security warning before granting this: the session reads are not caller-scoped, and a prompt directs tools that run at process privilege
+- `agent` — Drive the [built-in agent](../operations-api/operations.md#agent) without full `super_user`. Operations: `agent_prompt`, `get_agent_session`, `list_agent_sessions`, `cancel_agent_run`, `approve_agent_action`. `set_agent_config` and `get_agent_config` are deliberately excluded, so a delegated role cannot change the agent's model or approval policy and does not get the operator's view of its startup-only settings; the session reads still show the `model` and `provider` a session ran with. Read the agent section's security warning before granting this: the session reads are not caller-scoped, and a prompt directs tools that run at process privilege
**Example: read-only role**
diff --git a/release-notes/v5-lincoln/5.4.md b/release-notes/v5-lincoln/5.4.md
index c86de87c2..d3756d119 100644
--- a/release-notes/v5-lincoln/5.4.md
+++ b/release-notes/v5-lincoln/5.4.md
@@ -76,6 +76,10 @@ A static `mcpResources` entry can now name a `subscribe` method, so MCP clients
`agent.httpFetch` sets whether the built-in agent has its `http_fetch` tool: `false` removes it, and `{ allow: [...] }` refuses any request whose host is not on the list. It is read at startup only, and `set_agent_config` rejects it. `http_fetch` now also checks every redirect hop before following it, in every mode. See [Restricting `http_fetch`](/reference/v5/configuration/options#restricting-http_fetch).
+### Turning Off the Agent's Own Tool Groups
+
+The built-in agent's filesystem tools, `schedule_followup`, the V8 inspector tools and the best-practices lookup can each be removed from its toolset at startup with `agent.tools.filesystem`, `agent.tools.schedule`, `agent.tools.inspector` and `agent.tools.bestPractices` (default `true`). A group that is off is not described to the model, and a call to one of its tools runs nothing; its tool names stay reserved, so no operations or injected tool can take them. With every group and `httpFetch` off, the agent has only the operations tools `agent.user` may call, so an application can send it a prepared brief and take a one-turn answer. The switches are read at startup only, and `set_agent_config` rejects them. The new `get_agent_config` operation returns the configuration in effect, the startup-only keys included. See [Turning off tool groups](/reference/v5/configuration/options#turning-off-tool-groups) and [`get_agent_config`](/reference/v5/operations-api/operations#get_agent_config) ([harper#3993](https://github.com/HarperFast/harper/issues/3993)).
+
### Cut-Off Agent Replies End the Run `error`
A model reply that did not finish now ends the agent session `error`, with the reason in `lastError`, instead of `completed`. That covers a reply cut off at the output-token cap or at the model's context window, one stopped by a content filter or a refusal, one that asks for tool calls of which none could be parsed, and an empty reply. Nothing from that reply reaches the transcript, and its tool calls are neither run nor queued for approval. Its usage is still kept on the session, and counts toward the agent's spend caps. The new `agent.maxTokens` (default `16384`) is sent with every model request the agent makes; lower it for a model whose own output limit is smaller, or the provider rejects each request. See [`agent`](/reference/v5/configuration/options#agent) and [Agent](/reference/v5/operations-api/operations#agent).