Usage Guide — infrabroker MCP Tools¶
This document covers practical usage of the seven MCP tools exposed by
cmd/mcp-broker (stdio) and cmd/mcp-broker-http (HTTP+OAuth2/OIDC).
Keep this file up to date whenever a tool is added, removed, renamed, or its parameters or behaviour change.
Table of Contents¶
- Before you start
- ssh_execute — one-shot command
- ssh_session_open / exec / close — persistent session
- Common patterns
- Error handling
- Quick reference
- Reviewing audit logs
- Session recording
- ssh_put_file / ssh_get_file — file transfer
1. Before you start¶
Always call ssh_list_servers first. It returns the hosts accessible to
you, together with the capabilities that determine which parameters are valid
for each host. In the HTTP+OAuth frontend the list is already filtered by your
RBAC groups — a host you cannot sign for is not listed.
Example response:
web01 (sudo=true pty=true file_transfer=true)
db01 (sudo=false pty=false file_transfer=false)
bastion (sudo=false pty=false file_transfer=false)
app02 (sudo=true pty=true file_transfer=false via=bastion)
What the fields mean:
| Field | Value | What it implies |
|---|---|---|
allow_sudo |
true |
You may use sudo=true in execute/session calls. |
allow_sudo |
false |
Do not pass sudo=true. The signer will reject it. Do not retry. |
allow_pty |
true |
You may use pty=true (execute) or mode=pty (session). |
allow_pty |
false |
Do not request a PTY. Do not retry. |
allow_file_transfer |
true |
You may use ssh_put_file / ssh_get_file (see §9). |
allow_file_transfer |
false |
Do not attempt file transfers. The signer will reject them. Do not retry. |
jump |
"bastion" |
The host is reached via a ProxyJump hop through bastion. Transparent — no extra action needed. |
Operator note — host groups, CA keys and broker-ctl (v1.11.1): each host in signer.json belongs to one or more groups. Those groups determine both RBAC visibility (which broker CN can reach the host) and, if the operator has configured ca_keys, which CA key signs its certificates. From the model's perspective this is transparent — the tools and their parameters are unchanged. Operators manage hosts, CA keys, callers, and command policy via broker-ctl; see OPERATIONS.md §4. Per-host command policy (allowlist, denylist, require-approval) is configured with broker-ctl host add --policy-mode flags and takes effect after the next broker-ctl reload.
2. ssh_execute — one-shot command¶
Use ssh_execute when you need to run one command (or several independent
commands) on a host. Each call issues a fresh ephemeral certificate scoped to
exactly that command (force-command in the cert). The connection is opened,
the command runs, and the connection is discarded.
2.1 Basic execution¶
Response:
exit=0 means success. serial is only useful for audit log correlation — the
model should not reason over it.
2.2 Capturing both stdout and stderr¶
Response:
exit_code != 0 is a remote command failure, not a tool error. Handle it
the same way you would a process that exits with a non-zero status.
2.3 With sudo (root)¶
Requires allow_sudo=true on the host (check ssh_list_servers first).
The signer bakes sudo -n -- /bin/sh -c 'systemctl restart nginx' into the
cert's force-command. sshd enforces it; the broker cannot modify it.
2.4 With sudo to a specific user¶
sudo_user must be in the host's allowed_sudo_users list. Empty sudo_user
defaults to root.
2.5 With PTY¶
Use pty=true only when the command explicitly requires a terminal (checks
isatty(), uses terminal control sequences, etc.). Requires allow_pty=true.
Important: with pty=true, stdout and stderr are merged in the PTY stream.
The stderr field in the response will be empty.
2.6 Overriding the certificate TTL¶
By default the broker requests the maximum TTL allowed by the host policy. Pass
ttl_seconds to request a shorter window.
2.7 Dry-run — preview without executing¶
Pass dry_run=true to check whether a command would be allowed by the host's
command policy (and whether it would require human approval) without connecting
or running anything. Nothing executes; no stdout is produced.
Response (allowed, but approval-gated):
[dry-run] ALLOWED (requires human approval before executing)
rule: require_approval:^systemctl restart
force-command: systemctl restart nginx
ttl: 120s
Response (denied by command policy):
Response (allowed because the host is in command-policy audit mode):
[dry-run] ALLOWED
rule: allowlist:no-match
warning: command_policy audit: would deny (allowlist:no-match)
force-command: rm -rf /tmp/example
ttl: 120s
Use dry-run to decide whether to proceed before committing an action. A host may
restrict commands via an allowlist or denylist (see the
AI-action firewall in ARCHITECTURE.md).
When a policy uses enforcement: "audit", the command is allowed but the
response and audit log carry would_deny / would_require_approval warnings.
Use this only to collect a baseline before switching to enforce.
2.8 Commands that require human approval¶
Some commands are configured to require human approval before they run
(require_approval in the host policy). When you call ssh_execute for such a
command, the tool blocks while a human is asked to approve out-of-band:
- If approved, the call returns normally (stdout / exit code) once the human approves — this may take seconds to minutes.
- If denied or it times out, the call returns an error explaining the command was not approved. Do not retry automatically; surface the outcome to the user.
You can check ahead of time whether a command needs approval with
dry_run: true (the response says "requires human approval"). This lets you
warn the user that the action will pause for approval before you run it.
Approve-and-learn. When a human approves, they may also learn the decision
(broker-ctl approval allow <id> --learn --ttl 2h). After that, the same command
on that host, from the same broker/end-user identity, runs without prompting again
until the waiver expires — so a follow-up ssh_execute (or a dry_run) of that exact
command returns immediately instead of blocking. This is decided entirely by the
human/operator; from your side nothing changes except that a previously-gated command
may stop pausing for a while. When the waiver expires the approval gate returns
automatically.
Note: approval can also be triggered dynamically by behavioral guardrails — e.g. the first time you touch a new host, or run an unusual command, the deployment may require a human to approve it even if the command itself isn't on the approval list. Additionally, a burst of commands may be rate-limited (the call returns a "rate limit" error); if that happens, slow down and inform the user rather than retrying in a tight loop.
The operator's notification channel (how the approval request reaches the human)
is configured server-side — options include a process log, a generic webhook, or
a Microsoft Teams card (notifier: "teams" in control-plane.json). This does
not affect how the tool behaves from the model's perspective.
3. ssh_session_open / exec / close — persistent session¶
Use sessions when you need:
- Stateful execution —
cdto a directory and run subsequent commands there, or set environment variables that persist across calls. - Reduced latency — the SSH connection is established once and reused.
- Interactive programs — editors, pagers, or programs that require a real
TTY (
mode=pty).
Always close a session when done with
ssh_session_close. Open sessions hold an SSH connection until they are closed or reaped. The reaper closes a session aftersession_idle_secondsof inactivity orsession_max_secondsof total lifetime — not when the certificate TTL elapses: OpenSSH validates the certificate only at authentication, so an already-established connection survives certificate expiry. Setsession_max_secondsto the maximum exposure window you are willing to accept.
3.1 mode=exec (default) — isolated commands, shared connection¶
Each ssh_session_exec call runs in a separate SSH channel. State (working
directory, variables) does not persist between calls. Use this when you want
connection reuse without state leakage.
The broker preflights every ssh_session_exec against the current signer policy
before sending it to SSH. This applies to sessions that were already open before
a signer reload: target and bastion access, end-user groups, sudo, sudo_user and
PTY are revalidated on the next call. The broker also checks whether the host's
physical SSH route (addr, user, host_key, or jump) changed since the
session was opened; if it did, the next command is rejected and you must open a
new session.
If a command_policy is added to the host, mode=exec commands are checked,
and mode=shell / mode=pty commands are rejected because stateful streams are
not independently verifiable. In enforcement: "enforce" a denied or
approval-gated command is not executed; in enforcement: "audit" it executes
and returns a warning.
Response: session_id=abc123 serial=1050
Response: web01\n[exit=0 serial=1050]
Audit-mode response example:
Response: closed
3.2 mode=shell — stateful shell¶
A single /bin/sh process is started. cd, variable assignments, and
environment changes persist across ssh_session_exec calls.
Hosts with a command_policy reject mode=shell; use mode=exec for
preflighted repeated commands, or ssh_execute for the strongest one-shot
force-command guarantee.
Response: session_id=def456 serial=1060
Response: /var/log\n[exit=0 serial=1061]
Response (still in /var/log):
total 48M
-rw-r--r-- 1 root root 12M Jun 5 11:00 access.log
-rw-r--r-- 1 root root 256K Jun 5 11:00 error.log
[exit=0 serial=1062]
3.3 mode=pty — interactive shell with PTY¶
Opens a shell with a pseudo-terminal. Use for programs that call isatty(),
check $TERM, or produce terminal-formatted output. Requires allow_pty=true.
Hosts with a command_policy reject mode=pty.
stdout and stderr are merged in the PTY stream.
Response: session_id=ghi789 serial=1070
3.4 Session with sudo (root escalation)¶
Requires allow_sudo=true (check ssh_list_servers).
mode=shell or mode=pty with sudo: the entire shell process is launched as
sudo -n -- /bin/sh. Every command in the session runs elevated.
mode=exec with sudo: each command is individually prefixed with the sudo
prefix returned by the signer (elevation_prefix).
3.5 Session with sudo to a specific user¶
4. Common patterns¶
4.1 Check service status across multiple hosts¶
# For each host in the list:
tool: ssh_execute
params:
server: "web01"
command: "systemctl is-active nginx"
tool: ssh_execute
params:
server: "app02"
command: "systemctl is-active myapp"
4.2 Deploy a config change and restart a service¶
tool: ssh_session_open
params:
server: "web01"
mode: "shell"
sudo: true
tool: ssh_session_exec
params:
session_id: "<id>"
command: "cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak"
tool: ssh_session_exec
params:
session_id: "<id>"
command: "printf 'server { listen 80; ... }' > /etc/nginx/conf.d/app.conf"
tool: ssh_session_exec
params:
session_id: "<id>"
command: "nginx -t && systemctl reload nginx"
tool: ssh_session_close
params:
session_id: "<id>"
4.3 Investigate a failing service¶
tool: ssh_session_open
params:
server: "web01"
mode: "shell"
sudo: true
tool: ssh_session_exec
params:
session_id: "<id>"
command: "systemctl status myapp --no-pager"
tool: ssh_session_exec
params:
session_id: "<id>"
command: "journalctl -u myapp --no-pager -n 50"
tool: ssh_session_exec
params:
session_id: "<id>"
command: "ss -tlnp | grep :8080"
tool: ssh_session_close
params:
session_id: "<id>"
4.4 Disk usage check¶
tool: ssh_execute
params:
server: "web01"
command: "df -h && du -sh /var/log/* 2>/dev/null | sort -rh | head -10"
4.5 Host reachable via bastion (ProxyJump)¶
No special parameters needed. The broker resolves the hop chain automatically.
ssh_list_servers will show jump=bastion on the host entry.
The broker signs two certificates (one for the bastion, one for app02) and
tunnels through automatically.
5. Error handling¶
5.1 Remote command failure vs tool error¶
exit_code != 0 means the remote command failed. It is not a tool error.
Treat it as you would any process that exits non-zero.
# exit_code=1 from grep when no match found — not an error:
tool: ssh_execute
params:
server: "web01"
command: "grep 'pattern' /var/log/app.log"
# Response: [exit=1 serial=...] → no match, not a failure worth alerting on
A tool error (IsError: true in the MCP response) means the broker itself
could not complete the request — e.g., the host is unknown, the signer rejected
the intent, or the SSH connection failed. These are actual errors that need
attention.
5.2 sudo rejected¶
If allow_sudo=false on a host and you pass sudo=true, the signer returns 403
and the tool returns an error. Do not retry with sudo. Inform the user that
the host does not permit privilege escalation.
# ssh_list_servers shows: db01 (sudo=false pty=false)
# Attempting sudo on db01 → tool error:
# host "db01" does not allow elevation (allow_sudo=false)
# Correct response: inform the user, do not retry
5.3 PTY rejected¶
Same rule: if allow_pty=false, do not pass pty=true or mode=pty. Do not
retry.
5.4 Session expired or closed¶
If ssh_session_exec returns an error for a session ID that was previously
valid, the session was most likely closed by the reaper — after
session_idle_seconds of inactivity or session_max_seconds of total lifetime.
Open a new session.
5.5 Newlines in commands¶
Commands must not contain \n or \r:
ssh_execute(one-shot): the signer rejects them — a newline would smuggle extra command lines past the host's command policy. Compose with;or&&instead.ssh_session_execinshell/ptysessions: the broker rejects them, since a newline would inject additional commands into the persistent shell.ssh_session_execinexecsessions: the command runs in an isolated channel, so the broker does not reject newlines by itself. If the current signer policy evaluates commands, the signer rejects newlines during preflight for the same command-policy reason as one-shot. Use multiplessh_session_execcalls instead.
# Wrong — will be rejected:
command: "echo foo\necho bar"
# Correct:
tool: ssh_session_exec command: "echo foo"
tool: ssh_session_exec command: "echo bar"
6. Quick reference¶
Tool selection¶
| Scenario | Tool |
|---|---|
| Single independent command | ssh_execute |
| Multiple commands, no shared state needed | ssh_execute (repeated) |
Multiple commands with cd or env state |
ssh_session_open (mode=shell) + ssh_session_exec |
| Program that requires a TTY | ssh_execute (pty=true) or ssh_session_open (mode=pty) |
| Lowest latency for many commands | ssh_session_open (mode=exec) + ssh_session_exec |
| Write a file (config, script) | ssh_put_file (requires allow_file_transfer=true) |
| Read a file (log, config) | ssh_get_file (requires allow_file_transfer=true) |
Parameter constraints¶
| Parameter | Constraint |
|---|---|
sudo=true |
Only when allow_sudo=true (from ssh_list_servers). Never retry if false. |
sudo_user |
Must be in allowed_sudo_users for the host. Empty = root. |
pty=true / mode=pty |
Only when allow_pty=true. Never retry if false. |
command |
Must not contain \n or \r for one-shot ssh_execute and shell/pty session exec; use ; or &&. Every session command is preflighted against the current signer policy: target and bastion access, end-user groups, sudo, sudo_user, PTY, and the physical host route are revalidated; when a host has command_policy, mode=exec must satisfy the allowlist/denylist and shell/pty commands are rejected. |
ttl_seconds |
Optional. Omit to use the host policy maximum. |
dry_run=true |
ssh_execute only. Simulates policy (allow/deny + approval) without executing. Nothing runs. |
ssh_put_file / ssh_get_file |
Only when allow_file_transfer=true (from ssh_list_servers). Never retry if false. Content capped by file_transfer_max_bytes (default 512 KiB). See §9. |
Recommended workflow¶
1. ssh_list_servers → discover hosts and their capabilities
2. ssh_execute → for simple, independent commands
— or —
ssh_session_open → for stateful / multi-step work
ssh_session_exec (×N)
ssh_session_close → always close when done
7. Reviewing audit logs¶
Both the broker and the signer write append-only, Ed25519-signed, SHA-256-chained
audit logs. Every entry carries a serial (issued by the signer) and a seq
(monotonic per log), allowing precise correlation across the three audit sources
(signer, broker, sshd).
Default log paths (from the active config):
| Service | Log file | Signing seed |
|---|---|---|
| Broker | audit.log |
pki/audit.seed |
| Signer | signer_audit.log |
pki/signer_audit.seed |
7.1 Live tail¶
Follow the broker log as commands execute:
broker-ctl audit tail --log audit.log
broker-ctl audit tail --log audit.log -n 50 # show last 50 lines before following
Follow the signer log (certificate issuances):
Output columns: TIME · SEQ · CALLER · HOST · OUTCOME · SERIAL · DETAIL
7.2 Searching and filtering¶
Filter by host (substring match):
Filter by outcome:
# Broker outcomes: executed, denied, error, session_open, session_exec,
# session_close, dry_run_allowed, dry_run_denied,
# file_put, file_get
broker-ctl audit show --log audit.log --outcome denied
# Signer outcomes: issued, denied, approval-required, dry_run_allowed,
# dry_run_denied, reloaded, reload-denied, reload-failed
broker-ctl audit show --log signer_audit.log --outcome issued
Filter by caller:
Filter by time window:
broker-ctl audit show --log audit.log --since 2026-06-05
broker-ctl audit show --log audit.log --since 2026-06-05T12:00:00Z
Combine filters and limit output:
7.3 jq pipelines¶
Raw JSON output (--json) pipes directly into jq:
# All denied entries as pretty JSON:
broker-ctl audit show --log audit.log --outcome denied --json | jq .
# Extract serial numbers from denied signer entries:
broker-ctl audit show --log signer_audit.log --outcome denied --json \
| jq -r .serial
# Correlate broker and signer by serial — find the cert that matches execution 1042:
broker-ctl audit show --log audit.log --json | jq 'select(.serial==1042)'
broker-ctl audit show --log signer_audit.log --json | jq 'select(.serial==1042)'
# Count executions per host in the last hour:
broker-ctl audit show --log audit.log \
--since "$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" \
--outcome executed --json \
| jq -r .host | sort | uniq -c | sort -rn
# Show all sudo (elevated) entries:
broker-ctl audit show --log audit.log --json \
| jq 'select(.elevation != null and .elevation != "")'
# Show all PTY sessions:
broker-ctl audit show --log audit.log --json | jq 'select(.pty == true)'
7.4 Verifying chain integrity¶
Verify hash chain only (no key needed):
broker-ctl audit verify --log audit.log
# OK: 1234 entries, chain intact (pass --key to also verify signatures)
broker-ctl audit verify --log signer_audit.log
Verify hash chain and Ed25519 signatures:
broker-ctl audit verify --log audit.log --key pki/audit.seed
broker-ctl audit verify --log signer_audit.log --key pki/signer_audit.seed
# OK: 1234 entries, chain intact, all signatures valid
If tampering is detected, verify exits with code 1 and prints the affected
sequence number(s):
ERROR: seq 42 — prev_hash mismatch
expected: 3a7f...
got: 000...
FAIL: 1234 entries checked, 1 error(s) found
7.5 Audit entry fields reference¶
| Field | Type | Description |
|---|---|---|
time |
RFC3339 | Timestamp (UTC) |
seq |
uint64 | Monotonic counter within this log file |
caller |
string | Broker CN (mTLS) or OIDC sub (HTTP) |
host |
string | Logical host name (broker) or FQDN/addr (signer) |
user |
string | Remote SSH account |
principal |
string | SSH certificate principal |
command |
string | Command executed (one-shot) or session mode |
ttl |
string | Certificate TTL granted |
serial |
uint64 | Certificate serial — correlates broker ↔ signer ↔ sshd |
session_id |
string | Session id — 24-char random hex token (session events only) |
outcome |
string | See table in §7.2 |
exit_code |
int | Remote exit code (execution events) |
err |
string | Error detail (on failure) |
elevation |
string | sudo:root or sudo:<user> if escalated |
pty |
bool | true if PTY was requested |
prev_hash |
string | SHA-256 hex of the previous raw JSON line |
sig |
string | Base64 Ed25519 signature over the entry (with sig="") |
8. Session recording¶
When session_recording_dir is set in config.json, the broker records
shell and pty sessions to ASCIIcast v2 files (.cast). Each file
captures stdin (what the agent typed), stdout, and stderr with millisecond
timestamps.
Enabling recording¶
The directory must exist and be writable by the broker process. File permissions
are set to 0600 (owner-read only).
File naming¶
One file per session: <session_id>.cast
The session_id matches the session_id field in the broker's audit log.
Use the audit log as an index to find recordings by agent, host, or time:
# Find all sessions opened by alice on web01 today
broker-ctl audit show --log audit.log --outcome session_open \
--host web01 --caller alice --since 2026-06-09 --json \
| jq -r '.session_id'
# List the corresponding recording files
broker-ctl audit show --log audit.log --outcome session_open --json \
| jq -r '.session_id' \
| xargs -I{} ls /var/log/infrabroker/recordings/{}.cast 2>/dev/null
Recording file format¶
The file is ASCIIcast v2 JSONL — one JSON line per event:
{"version":2,"width":220,"height":40,"timestamp":1749470400,
"title":"session a3f1b2c4d5e60718293a4b5c — alice@web01",
"env":{"TERM":"xterm-256color"},
"infrabroker":{"session_id":"a3f1b2c4d5e60718293a4b5c","caller":"alice","host":"web01",
"serial":1042,"started_at":"2026-06-09T14:00:01Z"}}
[0.000, "i", "df -h /\n"]
[0.012, "o", "Filesystem Size Used Avail Use% Mounted on\n"]
[0.013, "o", "/dev/sda1 50G 18G 30G 38% /\n"]
[1.244, "i", "uptime\n"]
[1.251, "o", " 14:00:02 up 3 days, 2:00\n"]
| Field | Description |
|---|---|
| Header line | Session metadata: session_id, caller, host, serial, timestamps |
[delta, "i", data] |
Input (command typed by the agent) |
[delta, "o", data] |
Output (stdout, or merged PTY output) |
[delta, "e", data] |
Stderr (non-PTY sessions only) |
delta |
Seconds since session start (float, millisecond precision) |
The infrabroker extension field in the header is a private extension within the
ASCIIcast v2 spec (extra fields are allowed). Standard tools ignore it.
Playback¶
Any tool that supports ASCIIcast v2 can replay the recording:
# Install asciinema if not present
pip install asciinema # or: brew install asciinema
# Play a session recording
asciinema play /var/log/infrabroker/recordings/a3f1b2c4d5e60718293a4b5c.cast
# Print the recording as plain text (no timing)
asciinema cat /var/log/infrabroker/recordings/a3f1b2c4d5e60718293a4b5c.cast
# Stream new events as they are written (live tail)
tail -f /var/log/infrabroker/recordings/a3f1b2c4d5e60718293a4b5c.cast \
| asciinema cat /dev/stdin
Scope¶
Only shell and pty sessions are recorded. One-shot ssh_execute commands
and exec-mode sessions are not recorded (those are fully captured in the audit
log including stdout/stderr content via ssh_execute responses).
Storage management¶
Recording files are not rotated automatically. Use standard tools to manage retention:
# Delete recordings older than 90 days
find /var/log/infrabroker/recordings -name "*.cast" -mtime +90 -delete
9. ssh_put_file / ssh_get_file — file transfer¶
Write and read files on a host without heredoc tricks. Both tools use the same
ephemeral-certificate machinery as ssh_execute: each transfer is a one-shot
command (cat > path for writes, a bounded read for downloads) locked into the
certificate's force-command, with the content streamed over stdin/stdout.
Requires allow_file_transfer=true on the host (check ssh_list_servers
first). If it is false, the signer rejects the transfer — do not retry;
inform the user. Transfers run as the host's configured SSH user (no sudo):
the path must be readable/writable by that user.
9.1 Write a file¶
tool: ssh_put_file
params:
server: "web01"
path: "/etc/myapp/config.yaml"
content: "listen: :8080\nworkers: 4\n"
mode: "0644"
Response:
- The destination is created or overwritten. Read it first if you need to preserve content.
modeis optional (octal, e.g.0644,0755); without it the file keeps the remote user's default umask.- For binary data pass
content_base64: trueand base64-encodecontent. - Content is capped by the broker's
file_transfer_max_bytes(default 512 KiB).
9.2 Read a file¶
- Text files come back as-is in
content; binary files come back base64-encoded withbase64: truein the result. - A file larger than
max_bytes(default: the broker cap) is an error, not a truncation — lower your expectations or fetch a slice withssh_execute(tail -c,sed -n).
9.3 Policy and audit interaction¶
- On hosts with a
command_policy, the generated transfer command (cat > 'path'/head -c N < 'path') is evaluated like any one-shot: an allowlist host denies transfers unless a pattern allows them, and ashell_parsepolicy in enforce mode rejects the stream redirects these commands use (deliberately conservative). - Every transfer writes two correlated audit entries (same
serial): the regularexecutedentry with the transfer command, and afile_put/file_getentry carryingpath=… bytes=… sha256=…for content integrity.
10. Kubernetes tools (k8s_*)¶
These tools appear only when the broker is configured with at least one Kubernetes cluster; an SSH-only deployment does not expose them. Like SSH, the model never sees a cluster credential: for an authorised action the signer mints a short-lived bound ServiceAccount token, the broker makes the one API call, and the token is discarded.
Every cluster is default-deny: an action runs only if the cluster policy has
an allow (or require_approval) rule matching its verb, resource,
namespace, and name. If a tool returns "not allowed", do not retry —
tell the user the cluster policy forbids it.
10.1 List the reachable clusters¶
Always call this first to learn the cluster names (filtered to the user's RBAC groups).
10.2 Read: get, list, logs¶
tool: k8s_get
params: { cluster: "prod-k8s", resource: "pods", namespace: "prod", name: "web-1" }
tool: k8s_list
params: { cluster: "prod-k8s", resource: "deployments", namespace: "prod", label_selector: "app=web" }
tool: k8s_logs
params: { cluster: "prod-k8s", namespace: "prod", pod: "web-1", container: "app", tail_lines: 100 }
- Omit
groupfor core resources (pods, services, configmaps, nodes…); the broker fills it in from the cluster's resource table. Omitnamespaceonly for cluster-scoped resources (nodes, namespaces, persistentvolumes) or tok8s_listacross all namespaces the ServiceAccount can read. k8s_get/k8s_list/k8s_logsreturn the API server's JSON (or plain-text logs) inoutput.
10.3 Mutate: apply, delete¶
tool: k8s_apply
params:
cluster: "prod-k8s"
resource: "deployments"
namespace: "staging"
name: "api"
manifest: '{"apiVersion":"apps/v1","kind":"Deployment","metadata":{"name":"api","namespace":"staging"},"spec":{...}}'
tool: k8s_delete
params: { cluster: "prod-k8s", resource: "pods", namespace: "prod", name: "web-1" }
k8s_applyis a server-side apply (create-or-update); the manifest'sapiVersion/kind/metadata.name/metadata.namespacemust match the arguments.- Mutating actions are frequently
require_approval: the tool then blocks until a human approves (or the request times out). This is the same approval flow as SSH — the approver sees the canonical action (e.g.delete pods prod/web-1).
10.4 Dry-run¶
Every k8s tool accepts dry_run: true to preview the broker policy decision
(allowed / denied / requires approval) without calling the API server. Use it
before a mutating action to check whether it will be allowed or gated.