infrabroker
infrabroker is an infrastructure access broker MCP server that lets AI agents securely execute SSH and Kubernetes operations using ephemeral, scope-limited credentials that never enter the model's context, with policy enforcement and audit logging.
SSH & Host Operations:
List accessible hosts (
ssh_list_servers): Discover all permitted hosts and their capabilities (sudo, PTY, file transfer, bastion info). Call this first before interacting with any host.Execute one-shot SSH commands (
ssh_execute): Run a single command with an ephemeral SSH certificate. Supports optional sudo elevation, PTY allocation, dry-run mode (previews policy without executing), and custom certificate TTL. Returns stdout, stderr, and exit code.Persistent SSH sessions (
ssh_session_open/ssh_session_exec/ssh_session_close): Open reusable connections for multi-step workflows in three modes —exec(stateless),shell(stateful:cdand env vars persist), orpty(interactive pseudo-terminal for editors,less, etc.). Policy is revalidated on each command.File transfer (
ssh_get_file/ssh_put_file): Read or write files over SSH with base64 support for binary data, size limits, SHA-256 integrity checks, and optionalchmod. Requiresallow_file_transfer=trueon the host.
Kubernetes Operations:
Supports
k8s_get,k8s_list,k8s_logs,k8s_apply,k8s_delete, andk8s_list_clustersusing short-lived bound ServiceAccount tokens.
Security & Governance:
Ephemeral credentials exist only in broker memory — never exposed to the AI model, preventing exfiltration even if the agent is compromised.
Every operation is validated against configurable allow/deny command policies, RBAC groups, and optional human-in-the-loop approval gates.
All actions are logged in an append-only, cryptographically chained audit trail with serial identifiers and SHA-256 digests for non-repudiation.
Sudo and PTY usage are gated by host-level policy flags. Session recording via ASCIIcast v2 is also supported.
Provides tools for managing Kubernetes clusters, including querying resources, applying manifests, and deleting resources, with per-operation bound ServiceAccount tokens and dry-run support.
infrabroker
Infrastructure access broker for AI agents — SSH & Kubernetes. The model
never touches a credential. (formerly ssh-broker)
The agent requests an action — run a command on a host, query or change a cluster. infrabroker checks it against policy, executes it with a credential minted for that single operation — an ephemeral, scope-limited SSH certificate from its own CA, or a short-lived bound ServiceAccount token — and returns only the output. Keys, certificates and tokens live in the broker's memory and are discarded after the call: nothing enters the model's context, so a prompt-injected agent has nothing to exfiltrate.
One binary — infrabroker — exposes the same engine (internal/broker) and tool
surface (internal/mcpserver) over three transports, chosen by subcommand. (The
legacy per-transport binaries broker / mcp-broker / mcp-broker-http remain as
thin deprecated wrappers over these subcommands, so existing configs keep
working.)
MCP stdio (local, recommended for personal use) —
infrabroker serve-mcp. Tools:ssh_execute,ssh_session_open/ssh_session_exec/ssh_session_close,ssh_list_servers,ssh_put_file/ssh_get_file; with clusters configured, alsok8s_get/k8s_list/k8s_logs/k8s_apply/k8s_delete/k8s_list_clusters. No transport auth — isolation comes from the process being launched by the user (as the MCP spec recommends for stdio).MCP HTTP + OAuth2/OIDC (remote, multi-user) —
infrabroker serve-mcp-http, Streamable HTTP. Same tools, but each client authenticates with an OIDC bearer token validated locally against the issuer's JWKS; the user identity (and groups, for per-user RBAC) is propagated to the signer.HTTP + mTLS —
infrabroker serve-http,POST /v1/ssh_run(one-shot), for network agents authenticated with a client certificate.
Documentation
This README is a landing page. The detail lives in focused, single-source docs:
Document | Contents |
First | |
Diagram, request flow, design decisions, sudo elevation, sessions, multi-CA | |
Actors, trust boundaries, security controls, and explicit non-goals/gaps | |
Runbook: startup, adding hosts, hot-reload, | |
Running infrabroker over a NetBird / Tailscale mesh — the session layer on top of the overlay path | |
Why it is single-instance today: state inventory, the blockers, and what degrades under replication | |
HTTP endpoint reference for all services | |
Guide to the MCP tools (SSH + Kubernetes), dry-run, and audit review (for the model / operator) | |
Vulnerability disclosure policy | |
Workflow, versioning, Go style |
Related MCP server: SSH Vault MCP
Why infrabroker
Anti-exfiltration (prompt injection): the ephemeral key/cert/token live only in the broker's memory; they never enter the model's context.
Kubernetes without kubeconfigs: the signer mints a short-lived bound ServiceAccount token (TokenRequest API) per operation; every cluster is default-deny with per-verb/resource/namespace policy and the same dry-run, approval and audit path as SSH.
Anti-reuse: each cert carries a TTL of minutes,
source-address(broker or bastion IP), and — for one-shot — aforce-command. Useless outside its host/time/IP.Controlled escalation:
allow_sudo/allowed_sudo_userslive in the signer; a compromised broker cannot escalate where policy forbids it.CA compromise bounded: one CA per host group (
ca_keys), each key optionally in Azure Key Vault or ssh-agent (YubiKey PIV / SoftHSM / TPM) — the private key never leaves the HSM.Audit / non-repudiation: append-only, Ed25519-chained log correlated by
serialacross signer, broker, andsshd.
The full threat model — including what the system deliberately does not defend — is in THREAT_MODEL.md.
How it works
AI model ──tool call──> broker ──mTLS──> [control-plane] ──mTLS──> signer
(no credential) (ephemeral key (approval + (CA key +
in RAM, never guardrails, policy + RBAC,
on disk) no CA key) signs the cert)
│
└── SSH with the ephemeral cert ──> bastion ──> target host
└─ stdout/stderr/exit_code ─> modelThe broker sends an intent ({host, role, purpose, command?, sudo?, pty?, pubkey, …}); the signer derives every certificate constraint from policy and
returns the signed cert. The ephemeral private key is generated in the broker
and never leaves it. See ARCHITECTURE.md for the request flow,
the design decisions, and the per-hop ProxyJump certificate diagrams.
Feature overview
Capability | One-liner | More |
Ephemeral certificates | Ed25519 pair in RAM per operation; minutes-long, scoped cert. No reusable secret. | |
External signer | A separate | |
Multi-CA + HSM | One CA key per host group via | |
AI-action firewall | Per-host or composable-by-group command policy (allow/deny/ | |
Human-in-the-loop approval | Optional control plane gates | |
Action budgets (behaviour guardrails) | Budget how much an agent can do: per-CN sign-rate cap plus per-subject rate limit and novelty escalation (a subsequent new host / novel command → approval); observe or enforce. Network tools budget what an agent can reach or spend; this budgets the actions themselves. | |
RBAC | Broker-CN groups (mTLS) + per-end-user OIDC groups; fail-closed. | |
sudo / PTY | Policy-gated elevation ( | |
Kubernetes broker |
| |
Session recording |
| |
Chained audit | Append-only, Ed25519-signed, SHA-256-chained; correlated by | |
Hot reload |
|
Comparison with existing solutions
Several tools address SSH access control or AI-agent credential security, but none cover the full combination that infrabroker targets in a lightweight, self-hosted package.
Feature | infrabroker | Teleport | Vault + SSH engine | StrongDM | ssh-mcp |
Ephemeral cert in memory (no disk) | ✅ | ✅ | ✅ | ❌ | ❌ |
Separate broker / signing service | ✅ | ✅ | Partial | ❌ | ❌ |
MCP-native (AI agents) | ✅ | ✅ (2025) | ✅ (2025) | ❌ | ✅ |
OAuth2/OIDC on MCP transport | ✅ | ✅ | ✅ | ❌ | ❌ |
Per-command policy + dry-run (AI-action firewall) | ✅ | ❌ | ❌ | ❌ | ❌ |
Human-in-the-loop approval for AI commands | ✅ | ❌ | ❌ | ❌ | ❌ |
Per-agent behavioral guardrails (anomaly/rate) | ✅ | ❌ | ❌ | ❌ | ❌ |
Session recording (ASCIIcast v2, stdin+stdout+stderr) | ✅ | ✅ | ❌ | Partial | ❌ |
Cryptographically chained audit log | ✅ | ❌ | ❌ | Partial | ❌ |
Single-binary / simple self-hosted | ✅ | ❌ | ❌ | ❌ | ✅ |
HSM/KMS for CA key | ✅ (AKV) | ✅ | ✅ | — | — |
Teleport is the closest commercial equivalent — short-lived SSH certs, RBAC, and since 2025 Secure MCP; its Jan-2026 Agentic Identity Framework targets the same threat model. The difference is operational weight: Teleport needs a dedicated control-plane cluster, recording proxy, and web UI — orders of magnitude heavier than a Go binary + signer.
HashiCorp Vault SSH secrets engine
is an SSH CA with full HSM/KMS support and (2025) its own MCP server, but it
provides only the signing piece — you still build the execution layer
(engine.go, session.go, the MCP tools) yourself.
StrongDM hides credentials but stores
long-lived secrets rather than generating ephemeral certs in memory, making it
weaker against exfiltration. Smallstep SSH CA is a
lightweight OIDC-integrated SSH CA (close to cmd/signer) with no execution
broker or MCP layer. ssh-mcp exposes
SSH to LLMs over MCP but uses a static SSH key — the exact vulnerability this
broker prevents. CyberArk PAM offers
comparable JIT cert access but is a closed enterprise platform for human
operators, not AI workloads.
Where it fits: MCP-native AI-agent access + in-memory ephemeral certs + separate signer + ASCIIcast recording + chained audit, as a small set of Go binaries without a cluster. Enterprise features (web UI, multi-region HA) are on the roadmap (see HANDOFF.md).
Install
Prebuilt binaries — each release ships
infrabroker_<ver>_{linux,darwin}_{amd64,arm64}.tar.gzwith all binaries, plus the installer tarball (infrabroker-v<ver>.tar.gz) thatdeploy/install.shconsumes for the systemd production path.go install —
go install github.com/luisgf/infrabroker/cmd/infrabroker@latest(pure Go, no CGO; same for the othercmd/binaries).Container —
ghcr.io/luisgf/infrabroker(docker or podman, multi-arch; entrypoint is the stdio MCP frontend). See CONTAINERS.md, including a compose demo that runs the full stack against a toy host:cd examples/compose && docker compose up --build -d(ormake demo).From source — the Quickstart below.
Register with Claude Code in one line — native binary or container:
claude mcp add infrabroker -- ~/bin/infrabroker serve-mcp -config /secure/path/config.json
claude mcp add infrabroker -- docker run -i --rm -v /secure/path:/config \
ghcr.io/luisgf/infrabroker -config /config/config.jsonQuickstart
Fastest path (local, single binary): QUICKSTART.md takes
you from git clone to your first ssh_execute in under 10 minutes with one
binary and one config.json — no signer service, no PKI. The steps below set up
the full remote stack (a separated signer); the containerised demo is under
Install.
# 1. Build (make injects the version from the git tag into every binary)
make install # → ~/bin/{infrabroker,signer,broker,broker-ctl,mcp-broker,...}
# or a single binary: make signer
# (plain `go build ./cmd/...` also works; it reports a dev-<commit> version)
# 2. Generate the local PKI + the two-service config (signer.json + config.json)
infrabroker init # writes pki/, signer.json, config.json; --force to redo
# add --import-ssh-config to import hosts from ~/.ssh/config, --register-mcp to
# run `claude mcp add` for you
# 3. Start the signing service (must be running before the broker)
./signer.sh start
# 4. Add a host and reload
broker-ctl host add --name web01 --addr web01.example.com:22 --user deploy --scan \
--groups prod-web --sudo
broker-ctl reload
# (--config is a global flag, before the subcommand; every binary takes --version)
broker-ctl --config /secure/path/signer.json host list
broker-ctl --version # short; add --verbose for build detailsRegister the stdio MCP with your client:
// Claude Code — ~/.claude.json
"infrabroker": { "type": "stdio", "command": "/Users/<you>/bin/infrabroker",
"args": ["serve-mcp", "-config", "/secure/path/config.json"] }
// OpenCode — ~/.config/opencode/opencode.json (note: type "local", command is an array)
"infrabroker": { "type": "local",
"command": ["/home/<you>/bin/infrabroker", "serve-mcp", "-config", "/secure/path/config.json"],
"enabled": true }Full setup — local vs external signing mode, the remote OAuth frontend, host
fields, sudoers, PKI, and broker-ctl — is in OPERATIONS.md.
Tool usage for the model is in USAGE.md.
API
Full reference: API.md.
Service | Endpoint | Auth | Description |
Signer |
| mTLS | Request an ephemeral SSH certificate |
Signer |
| mTLS | List accessible hosts (filtered by caller groups) |
Signer |
| mTLS | Hot-reload |
Control plane |
| mTLS | Forwarding + human approval |
Broker HTTP |
| mTLS | Execute a one-shot SSH command |
MCP HTTP |
| None | OAuth2 discovery (RFC 9728) |
MCP HTTP | Streamable HTTP | OIDC Bearer | MCP tools |
Security
The security posture — trust boundaries, the layered controls (RBAC, command
policy, approval gate, guardrails, source-address/TTL pinning, chained audit),
and the explicit non-goals (mode=exec sessions are broker-preflighted but
not host-enforced, no KRL, secrets logged verbatim, …) — is documented in
THREAT_MODEL.md.
To report a vulnerability, see SECURITY.md. CI enforces gofmt,
go vet, go test -race, and govulncheck on every push and PR.
Testing
make test # go test -race ./... (cert build, policy/RBAC/sudo/PTY, hops, …)
bash lab/run_signer_lab.sh # external signer: broker without ca_key + policy + denial
bash lab/run_mcp_lab.sh # bastion + target (ProxyJump) MCP scenario
bash lab/run_lab.sh # HTTP/mTLS frontendLicense
Copyright (C) 2026 Luis González Fernández.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3.0 as published by the Free Software Foundation. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. See LICENSE for the full text.
Available Tools
7 toolsssh_executeADestructive
Execute a single command on a Linux host via SSH with an ephemeral credential. Prefer this tool over ssh_session_open when you only need to run one command or independent commands. Returns stdout, stderr and exit_code. exit_code != 0 means remote command failure, NOT a tool error; treat it like a process that exits with an error. BEFORE calling: use ssh_list_servers to learn the host capabilities. sudo=true ONLY if allow_sudo=true; if allow_sudo=false, DO NOT retry with sudo and inform the user. pty=true ONLY if allow_pty=true and the command needs a TTY (with pty, stdout and stderr are merged). ttl_seconds is optional; omit to use the maximum allowed by the host policy.
| Name | Required | Description | Default |
|---|---|---|---|
| pty | No | if true, request a pseudo-terminal (stdout and stderr are merged). Requires allow_pty=true in ssh_list_servers. Use only for commands that need a TTY. If allow_pty=false DO NOT retry. | |
| sudo | No | if true, execute with sudo -n (NOPASSWD). Requires allow_sudo=true in ssh_list_servers. If allow_sudo=false DO NOT retry: inform the user that the host does not allow elevation. | |
| server | Yes | logical name of the target host (see ssh_list_servers) | |
| command | Yes | command to execute on the host | |
| dry_run | No | if true, SIMULATE: check whether the command would be allowed by the host policy (allow/deny and whether it requires approval) WITHOUT executing it. Does not connect to the host or produce stdout. Useful to preview before executing. | |
| sudo_user | No | target user for sudo (empty = root). Must be in the host's allowed_sudo_users list. | |
| ttl_seconds | No | ephemeral certificate validity in seconds; omit to use the maximum allowed by the host policy |
Output Schema
| Name | Required | Description |
|---|---|---|
| serial | Yes | audit identifier; ignore when reasoning about the result |
| stderr | Yes | error output of the remote command (empty when pty=true, since stdout and stderr are merged) |
| stdout | Yes | standard output of the remote command |
| decision | No | present only on a dry_run: the policy decision (allow/deny/approval) with a machine-readable reason_code, instead of executed output |
| warnings | No | advisory warnings; command_policy audit-mode warnings mean the command was allowed but would have been blocked or approval-gated in enforce mode |
| exit_code | Yes | exit code of the remote command: 0=success, non-zero=command failure (NOT a tool error) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond annotations (destructiveHint=true, readOnlyHint=false), description discloses ephemeral credential behavior, exit_code semantics, pty stdout/stderr merging, sudo/pty preconditions, and ttl_seconds default. No contradiction with annotations; significant added context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is well-structured and front-loaded with purpose. Every sentence earns its place—there is no filler. Short imperative warnings ('DO NOT retry') and clear separation of concerns make it easy to scan.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has 7 params and an output schema, but the description still covers key contextual aspects: purpose, preference over sibling, return values, exit_code interpretation, prerequisites, safety constraints, and parameter defaults. It is fully self-sufficient for an agent to invoke correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema covers all 7 parameters with descriptions, but the tool description adds vital operational semantics: sudo requires allow_sudo=true and 'DO NOT retry' if false, pty requires allow_pty=true, and ttl_seconds is optional with max-allowed default. These go beyond the schema's base descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Execute a single command on a Linux host via SSH with an ephemeral credential'—a specific verb, resource, and mode. It further distinguishes itself from sibling ssh_session_open by advising preference for single or independent commands.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells when to use this tool vs ssh_session_open, requires calling ssh_list_servers before use, and gives conditional rules for sudo and pty with clear 'do not retry if not allowed' instructions. It also clarifies that non-zero exit_code means remote failure not a tool error.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_get_fileARead-only
Read a file from a Linux host via SSH with an ephemeral credential. Returns the content as text, or base64 (base64=true in the result) when the file is not valid UTF-8. REQUIRES allow_file_transfer=true on the host (see ssh_list_servers); if false DO NOT retry, the signer will reject it. The read runs as the host's configured SSH user (no sudo); the file must be readable by that user. A file larger than max_bytes (default: the broker's file_transfer_max_bytes, 512 KiB) is an ERROR, not a truncation. The content's sha256 is recorded in the audit log.
| Name | Required | Description | Default |
|---|---|---|---|
| path | Yes | absolute path of the file to read on the host | |
| server | Yes | logical name of the target host (see ssh_list_servers) | |
| max_bytes | No | read at most this many bytes; a larger file is an error, not a truncation. Omit for the broker's configured limit. | |
| ttl_seconds | No | ephemeral certificate validity in seconds; omit to use the maximum allowed by the host policy |
Output Schema
| Name | Required | Description |
|---|---|---|
| size | Yes | file size in bytes (decoded) |
| base64 | Yes | true when content is base64-encoded because the file is not valid UTF-8 text |
| serial | Yes | audit identifier; ignore when reasoning about the result |
| sha256 | Yes | hex sha256 of the file content, recorded in the audit log |
| content | Yes | file content: text as-is, or base64 when base64=true (binary file) |
| warnings | No | advisory command-policy warnings |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description adds substantial behavioral context beyond the readOnlyHint: base64 fallback for non-UTF-8, max_bytes error (not truncation), ephemeral credential, sha256 audit log entry, and no sudo. This fully discloses edge cases and operational implications.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is information-dense and front-loaded with the primary purpose, followed by critical constraints and behavioral details. Every sentence adds value; there is no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (host prerequisites, output encoding, size limits, audit logging), the description covers all essential aspects: prerequisites, error conditions, output format, permissions, and audit. An output schema exists, so return-value details are unnecessary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already covers all parameters with descriptions (100% coverage), including the max_bytes error behavior. The description reiterates the default and error semantics but does not add new parameter-level meaning beyond what the schema provides, so the baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states 'Read a file from a Linux host via SSH with an ephemeral credential,' which is a specific verb+resource. It clearly distinguishes from sibling tools like ssh_put_file (write) and ssh_execute (execute commands) by focusing on reading file content.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It provides explicit usage conditions: REQUIRES allow_file_transfer=true and 'if false DO NOT retry.' It also mentions 'see ssh_list_servers' for checking and clarifies the read runs as the configured user with no sudo. However, it does not explicitly name alternative tools for file-reading vs command execution, so it stops short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_list_serversARead-only
List the hosts accessible to the caller with their capabilities (hosts outside the user's RBAC groups are not listed). ALWAYS call before ssh_execute or ssh_session_open. Fields per host: allow_sudo=true → the host accepts NOPASSWD sudo elevation (sudo=true may be used); allow_sudo=false → DO NOT attempt sudo, the signer will reject it. allow_pty=true → the host accepts PTY (pty=true or mode=pty may be used); allow_pty=false → DO NOT attempt PTY. allow_file_transfer=true → ssh_put_file and ssh_get_file may be used; allow_file_transfer=false → DO NOT attempt file transfers, the signer will reject them. jump → name of the bastion through which the host is reached (informational).
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| servers | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already declare readOnlyHint=true, and the description adds substantial behavioral context beyond that: RBAC filtering, per-host capability fields, and specific failure expectations ('the signer will reject it'). It could further explain response behavior when no hosts are listed, but the provided detail is strong.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately sized for the tool's complexity, is front-loaded with the primary purpose, and each field explanation earns its place by providing actionable guidance. No wasted words or vague filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Combined with the annotations and the presence of an output schema, the description fully covers the tool's role, its relationship to sibling tools, RBAC privacy implications, and capability-dependent behavior. An agent can safely invoke this tool and correctly interpret results without additional context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
This tool has zero parameters, so per the rubric the baseline is 4. The description does not need to add parameter meaning; instead, it thoroughly explains the output fields, which is the relevant semantic content for this zero-input tool.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb and resource: 'List the hosts accessible to the caller with their capabilities.' It also clarifies scope ('hosts outside the user's RBAC groups are not listed') and distinguishes itself from sibling tools by being a preflight discovery tool, not an execution or file-transfer tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly instructs 'ALWAYS call before ssh_execute or ssh_session_open,' establishing when to use this tool. It also provides conditional guidance on when to avoid certain operations based on capabilities (e.g., 'allow_sudo=false → DO NOT attempt sudo'), which helps the agent avoid errors when using sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_put_fileADestructive
Write a file on a Linux host via SSH with an ephemeral credential. Creates or OVERWRITES the destination file with the given content. Use content_base64=true for binary data (the content field is decoded before writing). REQUIRES allow_file_transfer=true on the host (see ssh_list_servers); if false DO NOT retry, the signer will reject it. The write runs as the host's configured SSH user (no sudo); the destination must be writable by that user. On hosts with a command policy the transfer command (cat > path) must also be allowed by the policy. Content is limited by the broker's file_transfer_max_bytes (default 512 KiB). The content's sha256 is recorded in the audit log.
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | optional octal permissions to chmod after writing, e.g. 0644 or 0755 | |
| path | Yes | absolute destination path on the host; the file is created or overwritten | |
| server | Yes | logical name of the target host (see ssh_list_servers) | |
| content | Yes | file content. Text as-is, or base64 with content_base64=true for binary data. | |
| ttl_seconds | No | ephemeral certificate validity in seconds; omit to use the maximum allowed by the host policy | |
| content_base64 | No | if true, content is base64-encoded and is decoded before writing (required for binary files) |
Output Schema
| Name | Required | Description |
|---|---|---|
| serial | Yes | audit identifier; ignore when reasoning about the result |
| sha256 | Yes | hex sha256 of the written content, recorded in the audit log |
| warnings | No | advisory command-policy warnings |
| bytes_written | Yes | number of bytes written to the remote file |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations indicate destructiveHint=true, and the description adds extensive context: overwrite behavior, no sudo, execution as configured user, command policy restrictions, size limit, and audit logging. It fully discloses side effects and constraints beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single dense paragraph with every sentence providing essential information: overwrite behavior, binary handling, host requirement, permission constraints, policy checks, size limit, and audit. Length is appropriate for the tool's complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers prerequisites, permission model, policy constraints, size limits, and audit side effects. It fully contextualizes the tool's behavior, and with an output schema present, return value details are not necessary.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, with each parameter already described clearly. The description reinforces the content_base64 semantics and mentions the size limit affecting content, but does not add substantial new meaning beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: writing a file to a Linux host via SSH with an ephemeral credential. It explicitly mentions creates/overwrites behavior and is distinct from siblings like ssh_get_file (reading) and ssh_execute (running commands).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit prerequisites (allow_file_transfer=true, command policy, writable destination, size limit) and instructs not to retry if the flag is false. It references ssh_list_servers for verification but does not explicitly contrast with alternatives like ssh_get_file for the reverse operation.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_session_closeA
Close a persistent SSH session and release the connection. Always call when done working with a session; an unclosed session keeps its SSH connection until it is reaped when the opening certificate expires, or by the idle or maximum-lifetime timeout (whichever comes first).
| Name | Required | Description | Default |
|---|---|---|---|
| session_id | Yes | id of the session to close |
Output Schema
| Name | Required | Description |
|---|---|---|
| ok | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description carries the full burden of behavioral disclosure. It explains that an unclosed session keeps its SSH connection until reaped by certificate expiry or idle/max-lifetime timeout, adding valuable lifecycle context. It does not detail idempotency or error handling, but the core behavior is well covered.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences: the first states the action directly, the second provides rationale and consequences. Every word earns its place with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple close operation with a well-documented parameter and an output schema, the description covers the purpose, when to use it, and the behavior of not using it. It is sufficiently complete for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema fully describes the only parameter (session_id with 'id of the session to close'), so the description adds no additional parameter detail. Schema coverage is 100%, so the baseline of 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource ('Close a persistent SSH session and release the connection'), clearly stating what the tool does. It distinguishes itself from sibling tools like ssh_session_open and ssh_execute by focusing on the close operation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly instructs 'Always call when done working with a session' and explains the consequences of not closing (connection remains until cert expiry or timeout), providing strong contextual guidance. It does not offer explicit exclusions or compare with alternatives, but the use case is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_session_execADestructive
Execute a command in a session opened with ssh_session_open. Returns stdout, stderr and exit_code. exit_code != 0 means remote command failure, NOT a tool error. The command is preflighted against the current signer policy before execution; target and bastion access, end-user groups, sudo, sudo_user, PTY, and the host's physical route are revalidated, and audit-mode policy warnings are returned in warnings. If a policy is enabled after a shell/pty session was opened, later commands in that session are rejected. Session state (current directory, environment variables) persists across calls when mode=shell or mode=pty.
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes | command to execute in the session | |
| session_id | Yes | id returned by ssh_session_open |
Output Schema
| Name | Required | Description |
|---|---|---|
| serial | Yes | audit identifier; ignore when reasoning about the result |
| stderr | Yes | error output of the remote command (empty when pty=true, since stdout and stderr are merged) |
| stdout | Yes | standard output of the remote command |
| decision | No | present only on a dry_run: the policy decision (allow/deny/approval) with a machine-readable reason_code, instead of executed output |
| warnings | No | advisory warnings; command_policy audit-mode warnings mean the command was allowed but would have been blocked or approval-gated in enforce mode |
| exit_code | Yes | exit code of the remote command: 0=success, non-zero=command failure (NOT a tool error) |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With annotations indicating destructiveHint=true and readOnlyHint=false, the description adds substantial context beyond this: it clarifies exit_code semantics (remote failure vs tool error), policy preflight behavior, audit-mode warnings, and that session state persists. It also warns that later commands are rejected if policy changes after session open. This fully discloses behavioral traits without contradicting annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise and front-loaded: the first sentence states the core purpose and output. Subsequent sentences pack essential behavioral details without fluff. Every sentence provides unique value, making it appropriately sized for the complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (policy preflight, state persistence, exit code semantics) and the existence of an output schema, the description is remarkably complete. It covers the purpose, return values, error semantics, policy interactions, and state behavior, leaving no major gaps for an agent to infer.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% (both parameters described: command and session_id). The description adds meaning by explaining that session_id comes from ssh_session_open, clarifying session state persistence, and mentioning mode implications. This goes beyond the schema descriptions, though not dramatically, so a 4 is appropriate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Execute a command in a session opened with ssh_session_open.' This specific verb+resource combination distinguishes it from siblings like ssh_execute (standalone execution) and ssh_session_open (session creation). The name itself reinforces this purpose.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description establishes clear usage context: it requires an existing session from ssh_session_open, implying it is not for standalone commands. It also mentions persistent session state for mode=shell or mode=pty, which helps differentiate from one-shot alternatives, though it does not explicitly name alternatives or state when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
ssh_session_openA
Open a persistent SSH session that reuses the connection across commands. Use when you need multiple commands with shared state (e.g. cd to a directory and then operate in it) or interactive programs. For isolated commands prefer ssh_execute (simpler, stronger isolation guarantee). Available modes: exec (default, independent commands), shell (stateful sh: cd and variables persist), pty (shell with TTY for interactive programs). sudo=true ONLY if allow_sudo=true (see ssh_list_servers); if allow_sudo=false DO NOT retry. mode=pty ONLY if allow_pty=true. Every ssh_session_exec is preflighted against the current signer policy, so policy reloads revalidate target and bastion access, end-user groups, sudo, sudo_user, PTY, and the host's physical route for already-open sessions. On command-policy hosts, mode=exec is allowed; mode=shell and mode=pty are rejected. Returns session_id for use with ssh_session_exec. IMPORTANT: always close the session with ssh_session_close when done; an open session holds an SSH connection and is otherwise closed when the certificate that opened it expires, or after an idle or maximum-lifetime timeout (whichever comes first).
| Name | Required | Description | Default |
|---|---|---|---|
| mode | No | exec (default): isolated commands with no shared state. shell: persistent sh, cd and environment variables survive across ssh_session_exec calls. pty: shell with pseudo-terminal for interactive programs (editors, less, etc.); requires allow_pty=true. If allow_pty=false DO NOT use pty. | |
| sudo | No | if true, start with sudo -n elevation (NOPASSWD). In mode=shell/pty elevates the whole shell process. In mode=exec prepends sudo to each individual command. Requires allow_sudo=true in ssh_list_servers. If allow_sudo=false DO NOT retry. | |
| server | Yes | logical name of the target host (see ssh_list_servers) | |
| sudo_user | No | target user for sudo (empty = root). Must be in the host's allowed_sudo_users list. | |
| ttl_seconds | No | connection certificate validity in seconds; omit to use the maximum allowed by the host policy |
Output Schema
| Name | Required | Description |
|---|---|---|
| serial | Yes | |
| session_id | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries full burden. It discloses session reuse, mode distinctions (exec/shell/pty), sudo behavior per mode, policy preflighting for open sessions, session closure and lifecycle timeouts, and the explicit need to close the session. This is comprehensive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is dense but every sentence contributes unique information. It front-loads the core purpose, then layered constraints, and ends with a critical closing instruction. No fluff; each clause earns its place given the complexity.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (persistent sessions, multiple modes, policy checks, lifecycle), the description covers all essential aspects: when to use, mode differences, sudo/pty prerequisites, policy revalidation, return value (session_id), and closing requirement. The output schema exists, so return value details are adequately handled.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has 100% coverage with detailed parameter descriptions, so baseline is 3. The description adds value by explaining mode semantics (state persistence, TTY for interactive programs) and sudo elevation behavior per mode, plus constraints tied to allow_sudo/allow_pty, which enriches the schema's meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool opens a persistent SSH session that reuses connections, with specific use cases for shared state or interactive programs. It explicitly distinguishes from the sibling ssh_execute by recommending it for isolated commands, providing clear differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit when-to-use guidance: multiple commands with shared state (e.g., cd then operate) or interactive programs. It names the alternative (ssh_execute) and explains why to prefer it for isolated commands, and also covers constraints like sudo and pty prerequisites.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
6 tool updates
v3.1.2- Added
ssh_execute - Added
ssh_list_servers - Added
ssh_put_file - Added
ssh_session_close - Added
ssh_session_exec - Added
ssh_session_open
6 tool updates
v3.0.1- Removed
ssh_execute - Removed
ssh_list_servers - Removed
ssh_put_file - Removed
ssh_session_close - Removed
ssh_session_exec - Removed
ssh_session_open
2 tool updates
v2.0.0- Changed
ssh_execute1 field changed- added
Output schema / properties / decisionAdded value: +{ + "additionalProperties": false, + "description": "present only on a dry_run: the policy decision (allow/deny/approval) with a machine-readable reason_code, instead of executed output", + "properties": { + "allowed": { + "description": "whether the command would be authorised", + "type": "boolean" + }, + "enforcement": { + "description": "effective command_policy enforcement mode (enforce or audit)", + "type": "string" + }, + "force_command": { + "description": "the force-command that would be baked into the certificate", + "type": "string" + }, + "matched_rule": { + "description": "the command_policy rule that drove the decision, e.g. 'deny:^rm ' or 'allowlist:no-match'", + "type": "string" + }, + "reason": { + "description": "human-readable explanation of a denial (empty when allowed)", + "type": "string" + }, + "reason_code": { + "description": "machine-readable outcome: allowed | needs_approval | command_denied | allowlist_no_match | shell_parse_error | denied", + "type": "string" + }, + "require_approval": { + "description": "true when the command is allowed but needs out-of-band human approval before it will execute", + "type": "boolean" + }, + "ttl_seconds": { + "description": "TTL the issued certificate would carry, in seconds", + "type": "integer" + }, + "warning": { + "description": "audit-mode observation: allowed now, but would be blocked or approval-gated in enforce mode", + "type": "string" + }, + "would_deny": { + "description": "audit mode only: the command would have been denied in enforce mode", + "type": "boolean" + } + }, + "required": [ + "allowed", + "reason_code" + ], + "type": [ + "null", + "object" + ] +}
- Changed
ssh_session_exec1 field changed- added
Output schema / properties / decisionAdded value: +{ + "additionalProperties": false, + "description": "present only on a dry_run: the policy decision (allow/deny/approval) with a machine-readable reason_code, instead of executed output", + "properties": { + "allowed": { + "description": "whether the command would be authorised", + "type": "boolean" + }, + "enforcement": { + "description": "effective command_policy enforcement mode (enforce or audit)", + "type": "string" + }, + "force_command": { + "description": "the force-command that would be baked into the certificate", + "type": "string" + }, + "matched_rule": { + "description": "the command_policy rule that drove the decision, e.g. 'deny:^rm ' or 'allowlist:no-match'", + "type": "string" + }, + "reason": { + "description": "human-readable explanation of a denial (empty when allowed)", + "type": "string" + }, + "reason_code": { + "description": "machine-readable outcome: allowed | needs_approval | command_denied | allowlist_no_match | shell_parse_error | denied", + "type": "string" + }, + "require_approval": { + "description": "true when the command is allowed but needs out-of-band human approval before it will execute", + "type": "boolean" + }, + "ttl_seconds": { + "description": "TTL the issued certificate would carry, in seconds", + "type": "integer" + }, + "warning": { + "description": "audit-mode observation: allowed now, but would be blocked or approval-gated in enforce mode", + "type": "string" + }, + "would_deny": { + "description": "audit mode only: the command would have been denied in enforce mode", + "type": "boolean" + } + }, + "required": [ + "allowed", + "reason_code" + ], + "type": [ + "null", + "object" + ] +}
7 tool updates
v1.0.0- First observed
ssh_execute - First observed
ssh_get_file - First observed
ssh_list_servers - First observed
ssh_put_file - First observed
ssh_session_close - First observed
ssh_session_exec - First observed
ssh_session_open
TDQS
Scored across 7 tools
Each tool targets a distinct operation: single command execution, session-based execution, session lifecycle, file transfer, and host discovery. The descriptions explicitly guide tool selection, e.g., preferring ssh_execute for isolated commands and ssh_session_open for stateful sessions, leaving no ambiguity.
Naming is mixed: ssh_get_file, ssh_put_file, and ssh_list_servers follow a verb_noun pattern, while ssh_session_open, ssh_session_exec, and ssh_session_close reverse the order to noun_verb. ssh_execute also lacks a noun. The prefix is consistent and readable, but the conventions are not uniform.
Seven tools is ideal for an SSH broker: host discovery, single command execution, file transfer (get/put), and persistent session management (open/exec/close). Each tool serves a clear purpose without unnecessary overlap or bloat.
The surface covers the full remote-administration lifecycle: list hosts, run commands directly or via sessions, transfer files in both directions, and manage session resources. No critical operations are missing for the stated domain.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
- FullmaktOAuthai.fullmakt
Credential broker for AI agents: scoped, revocable API access with policy enforcement and audit.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Fail-closed policy guardrails for AI agents running kubectl, terraform, helm, and argocd.
Security gateway for AI agents: policy, approval, and audited execution, no secrets shared.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceSelf-hostable control plane for managing the full lifecycle of non-human identities (AI agents), with short-lived credential issuance, attestation, and an MCP authorization gateway for per-tool access control.1MIT
- FlicenseNot gradedqualityDmaintenanceEnables AI agents to securely access encrypted secrets (SSH keys, API tokens, passwords) with real-time user approval via Passkey, and supports SSH remote execution through the MCP protocol.-
- FlicenseNot gradedqualityAmaintenanceGive AI agents Zero-Trust access to production infrastructure without the risks of granting them shell access. Actions are bounded by policy and an on-host runner.354-
- AlicenseNot gradedqualityAmaintenanceGive Claude Code, Cursor, and other AI agents safe access to your real infrastructure — without giving them raw SSH access.4MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/luisgf/infrabroker'
If you have feedback or need assistance with the MCP directory API, please join our Discord server