You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: book/src/advanced/a2a.md
+44Lines changed: 44 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -54,6 +54,50 @@ pub enum ProcessorEvent {
54
54
55
55
The processor sends events through an `mpsc::Sender<ProcessorEvent>`, enabling per-token SSE streaming to connected clients. In daemon mode, `AgentTaskProcessor` bridges A2A requests to the full agent loop (LLM, tools, memory, MCP) via `LoopbackChannel`, providing complete agent capabilities over the A2A protocol.
56
56
57
+
## Invocation-Bound Capability Tokens (IBCT)
58
+
59
+
IBCT are per-call security tokens that bind each A2A request to a specific task and endpoint. They prevent replayed or forwarded A2A requests from being accepted by other tasks or endpoints.
60
+
61
+
### Enabling IBCT
62
+
63
+
Gated on the `ibct` feature flag (enabled in the `full` feature set):
# Option B: vault reference (recommended for production)
75
+
ibct_signing_key_vault_ref = "ZEPH_A2A_IBCT_KEY"
76
+
```
77
+
78
+
When `ibct_keys` or `ibct_signing_key_vault_ref` is set, outgoing A2A client calls include an `X-Zeph-IBCT` header containing a base64-encoded JSON token.
79
+
80
+
### Token Structure
81
+
82
+
Each token is HMAC-SHA256 signed and contains:
83
+
84
+
| Field | Description |
85
+
|-------|-------------|
86
+
|`key_id`| Key identifier (for rotation without downtime) |
Copy file name to clipboardExpand all lines: book/src/advanced/tools.md
+36Lines changed: 36 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -252,6 +252,42 @@ action = "ask"
252
252
253
253
When `[tools.permissions]` is absent, legacy `blocked_commands` and `confirm_patterns` from `[tools.shell]` are automatically converted to equivalent permission rules (`deny` and `ask` respectively).
254
254
255
+
## Structured Shell Output Envelope
256
+
257
+
When `execute_bash` completes, stdout and stderr are captured as separate streams using a tagged channel. The result is stored as a `ShellOutputEnvelope` in `ToolOutput.raw_response`:
258
+
259
+
```json
260
+
{
261
+
"stdout": "...",
262
+
"stderr": "...",
263
+
"exit_code": 0,
264
+
"truncated": false
265
+
}
266
+
```
267
+
268
+
The LLM context continues to receive the interleaved combined output (in `summary`) — behavior for the agent is unchanged. ACP and audit consumers, however, can access the envelope directly via `raw_response` to distinguish stdout from stderr and inspect the exact exit code.
269
+
270
+
`AuditEntry` gains two optional fields populated from the envelope:
271
+
272
+
| Field | Description |
273
+
|-------|-------------|
274
+
|`exit_code`| Process exit code (`null` when the process was killed by a signal) |
275
+
|`truncated`|`true` when output was cut to the overflow threshold |
276
+
277
+
## File Read Sandbox
278
+
279
+
`FileExecutor` supports a per-path read sandbox via `[tools.file]`:
Evaluation order: deny-then-allow. Patterns are matched against canonicalized absolute paths, so symlinks pointing into a denied directory are still blocked after resolution.
288
+
289
+
See the [File Read Sandbox](../reference/security/file-sandbox.md) reference for the full configuration and glob syntax.
290
+
255
291
## Output Overflow
256
292
257
293
When tool output exceeds a configurable character threshold, the full response is stored in the SQLite memory database (table `tool_overflow`) and the LLM receives a truncated version (head + tail split) with an opaque reference (`overflow:<uuid>`). This prevents large outputs from consuming the entire context window while preserving access to the complete data.
Zeph can run shell commands automatically in response to environment changes. Two hook events are supported: working directory changes and file system changes.
4
+
5
+
## Hook Types
6
+
7
+
### `cwd_changed`
8
+
9
+
Fires when the agent's working directory changes — either via the `set_working_directory` tool or an explicit directory change detected after tool execution.
10
+
11
+
```toml
12
+
[[hooks.cwd_changed]]
13
+
command = "echo"
14
+
args = ["Changed to $ZEPH_NEW_CWD"]
15
+
16
+
[[hooks.cwd_changed]]
17
+
command = "git"
18
+
args = ["status", "--short"]
19
+
```
20
+
21
+
Environment variables available to the hook process:
22
+
23
+
| Variable | Description |
24
+
|----------|-------------|
25
+
|`ZEPH_OLD_CWD`| Previous working directory |
26
+
|`ZEPH_NEW_CWD`| New working directory |
27
+
28
+
### `file_changed`
29
+
30
+
Fires when a file under `watch_paths` is modified. Changes are detected via `notify-debouncer-mini` with a 500 ms debounce window — rapid successive modifications produce a single event.
31
+
32
+
```toml
33
+
[hooks.file_changed]
34
+
watch_paths = ["src/", "config.toml"]
35
+
36
+
[[hooks.file_changed.handlers]]
37
+
command = "cargo"
38
+
args = ["check", "--quiet"]
39
+
40
+
[[hooks.file_changed.handlers]]
41
+
command = "echo"
42
+
args = ["File changed: $ZEPH_CHANGED_PATH"]
43
+
```
44
+
45
+
Environment variable available to the hook process:
46
+
47
+
| Variable | Description |
48
+
|----------|-------------|
49
+
|`ZEPH_CHANGED_PATH`| Absolute path of the changed file |
50
+
51
+
## The `set_working_directory` Tool
52
+
53
+
The `set_working_directory` tool gives the LLM an explicit, persistent way to change the agent's working directory. Unlike `cd` in a `bash` tool call (which is ephemeral and scoped to one subprocess), `set_working_directory` updates the agent's global cwd and triggers any `cwd_changed` hooks.
54
+
55
+
```text
56
+
Use set_working_directory to switch into /path/to/project
57
+
```
58
+
59
+
After the tool executes, subsequent `bash` and file tool calls run relative to the new directory.
60
+
61
+
## TUI Indicator
62
+
63
+
When a hook fires, the TUI status bar shows a short spinner message:
64
+
65
+
-`cwd_changed` → `Working directory changed…`
66
+
-`file_changed` → `File changed: <path>…`
67
+
68
+
The indicator disappears once all hook commands for that event have completed.
69
+
70
+
## Configuration Reference
71
+
72
+
```toml
73
+
# cwd_changed hooks — run in order when the working directory changes
74
+
[[hooks.cwd_changed]]
75
+
command = "echo"
76
+
args = ["cwd is now $ZEPH_NEW_CWD"]
77
+
78
+
# file_changed hooks — watch_paths + handler list
79
+
[hooks.file_changed]
80
+
watch_paths = ["src/", "tests/"] # relative or absolute paths to watch
81
+
debounce_ms = 500# debounce window in milliseconds (default: 500)
82
+
83
+
[[hooks.file_changed.handlers]]
84
+
command = "cargo"
85
+
args = ["check", "--quiet"]
86
+
```
87
+
88
+
| Field | Type | Default | Description |
89
+
|-------|------|---------|-------------|
90
+
|`hooks.cwd_changed[].command`|`string`| — | Executable to run |
91
+
|`hooks.cwd_changed[].args`|`Vec<String>`|`[]`| Arguments (env vars expanded) |
92
+
|`hooks.file_changed.watch_paths`|`Vec<String>`|`[]`| Paths to monitor |
93
+
|`hooks.file_changed.debounce_ms`|`u64`|`500`| Debounce window in milliseconds |
94
+
|`hooks.file_changed.handlers[].command`|`string`| — | Executable to run |
95
+
|`hooks.file_changed.handlers[].args`|`Vec<String>`|`[]`| Arguments (env vars expanded) |
Copy file name to clipboardExpand all lines: book/src/guides/mcp.md
+38Lines changed: 38 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -117,6 +117,44 @@ min_tools_to_filter = 5 # Only apply filtering when the server exposes a
117
117
118
118
`min_tools_to_filter` prevents aggressive filtering on small servers. When a server exposes fewer tools than this value, all tools from that server are included unconditionally.
119
119
120
+
## MCP Elicitation
121
+
122
+
MCP servers can request structured user input mid-task via the `elicitation/create` protocol method. This allows a server to prompt for missing parameters, confirmations, or credentials without requiring a separate out-of-band channel.
123
+
124
+
### Enabling Elicitation
125
+
126
+
Elicitation is disabled by default. Enable it globally or per server:
127
+
128
+
```toml
129
+
[mcp]
130
+
elicitation_enabled = true# global default (default: false)
131
+
elicitation_timeout = 120# seconds to wait for user input (default: 120)
132
+
elicitation_queue_capacity = 16# max queued requests (default: 16)
133
+
elicitation_warn_sensitive_fields = true# warn before sensitive field prompts
134
+
135
+
[[mcp.servers]]
136
+
id = "my-server"
137
+
command = "npx"
138
+
args = ["-y", "@acme/mcp-server"]
139
+
elicitation_enabled = true# per-server override (overrides global default)
140
+
```
141
+
142
+
`Sandboxed` trust-level servers are never permitted to elicit regardless of config.
143
+
144
+
### How It Works
145
+
146
+
When a server sends `elicitation/create`:
147
+
148
+
-**CLI:** the user sees a phishing-prevention header showing the server name, followed by field prompts. Fields are typed (string, integer, number, boolean, enum).
149
+
-**Non-interactive channels** (Telegram, ACP without a connected client): the request is automatically declined.
150
+
- If the request queue is full (exceeds `elicitation_queue_capacity`), the request is auto-declined with a warning log instead of blocking or accumulating indefinitely.
151
+
152
+
### Security Notes
153
+
154
+
- Always review which servers have `elicitation_enabled = true`. A compromised server with elicitation access can prompt for arbitrary user input.
155
+
-`elicitation_warn_sensitive_fields = true` (default) logs a warning when field names match secret patterns before prompting.
156
+
- See [Elicitation Security](../reference/security/mcp.md#elicitation-security) for the full security model.
157
+
120
158
## How Matching Works
121
159
122
160
MCP tools are embedded in Qdrant (`zeph_mcp_tools` collection) with BLAKE3 content-hash delta sync. Unified matching injects both skills and MCP tools into the system prompt by relevance score — keeping prompt size O(K) instead of O(N) where N is total tools across all servers.
Copy file name to clipboardExpand all lines: book/src/reference/security/mcp.md
+71Lines changed: 71 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -109,6 +109,77 @@ MCP server child processes inherit a sanitized environment. The following 21 env
109
109
110
110
This prevents accidental secret leakage to untrusted MCP servers.
111
111
112
+
## Tool Collision Detection
113
+
114
+
When two connected MCP servers expose tools whose `sanitized_id` (server-prefix + normalized name) collide, Zeph logs a warning and the first-registered server's tool wins dispatch. This prevents a later server from silently shadowing an established tool.
115
+
116
+
Collision warnings appear at connection time and when a dynamic server is added via `/mcp add`. Check the log for `[WARN] mcp: tool id collision` lines if you suspect shadowing.
117
+
118
+
## Tool-List Snapshot Locking
119
+
120
+
By default, Zeph accepts `notifications/tools/list_changed` from connected servers and fetches an updated tool list. This creates a window for mid-session tool injection: a compromised or misbehaving server could swap in tools after the operator has reviewed the initial list.
121
+
122
+
Enable snapshot locking to prevent this:
123
+
124
+
```toml
125
+
[mcp]
126
+
lock_tool_list = true
127
+
```
128
+
129
+
When `lock_tool_list = true`, `tools/list_changed` notifications are rejected for all servers after the initial connection handshake. The tool set is frozen at connect time. The lock flag is applied atomically before the connection handshake to eliminate TOCTOU races.
130
+
131
+
## Per-Server Stdio Environment Isolation
132
+
133
+
By default, spawned MCP server processes inherit the full (already-sanitized) environment. For additional containment, enable per-server environment isolation:
134
+
135
+
```toml
136
+
# Apply to all stdio servers by default
137
+
[mcp]
138
+
default_env_isolation = true
139
+
140
+
# Override per server
141
+
[[mcp.servers]]
142
+
id = "sensitive-tools"
143
+
command = "npx"
144
+
args = ["-y", "@acme/sensitive"]
145
+
env_isolation = true
146
+
env = { TOOL_API_KEY = "vault:tool_key" }
147
+
```
148
+
149
+
With `env_isolation = true`, the child process receives only a minimal base environment (PATH, HOME, USER, TERM, TMPDIR, LANG, plus XDG dirs on Linux) plus the server-specific `env` map. All other inherited variables — including remaining secrets not caught by the blocklist — are stripped.
150
+
151
+
| Setting | Scope | Effect |
152
+
|---------|-------|--------|
153
+
|`default_env_isolation`| All stdio servers | Opt-in baseline for all servers |
154
+
|`env_isolation` per server | Single server | Override (can enable or disable the default) |
155
+
156
+
## Intent-Anchor Nonce Boundaries
157
+
158
+
Every MCP tool response is wrapped with a per-invocation nonce boundary:
The UUID is unique per call and generated inside Zeph, not from the server response. If tool output itself contains the string `[TOOL_OUTPUT::`, that prefix is escaped before wrapping, preventing injection attempts that mimic the boundary marker. This gives the injection-detection layer a reliable delimiter to trust.
167
+
168
+
## Elicitation Security
169
+
170
+
When a connected server uses the `elicitation/create` method to request user input, Zeph applies two safeguards:
171
+
172
+
1.**Phishing-prevention header** — the CLI always displays the requesting server's ID before showing any fields, so the user knows which server is asking.
173
+
174
+
2.**Sensitive field warning** — field names matching common secret patterns (password, token, secret, key, credential, auth, private, passphrase, pin) trigger an additional warning before the user is prompted. Configure with:
`Sandboxed` trust-level servers are never allowed to elicit regardless of `elicitation_enabled`. This is enforced unconditionally.
182
+
112
183
## Environment Variables
113
184
114
185
MCP servers inherit environment variables from their configuration. Never store secrets directly in `config.toml` — use the [Vault](../security.md#age-vault) integration instead:
0 commit comments