MCP server for Codeplane that keeps an inventory of SSH-reachable servers (grouped + tagged) with an encrypted secrets store so an agent can answer "audit all servers" without manual credential input.
Website · MCP docs · Issues · npm · Versioning
Warning
This server exposes SSH and optional remote command execution to AI agents. Treat it like a privileged credential-aware tool: do not expose it over a network, and use SERVER_INVENTORY_ALLOW_EXEC=0 (the default) unless you explicitly need exec_on.
An MCP server for Codeplane that stores:
- A grouped, tagged inventory of SSH hosts — hostname, user, port, identity file, jump host, or just an
ssh_aliasthat resolves via~/.ssh/config. - An encrypted secrets store for passwords, sudo passwords, key passphrases, DB credentials, and API tokens — retrieved right before the call that needs them, never written to disk in plaintext.
Everything writable lives in your Codeplane data directory:
~/.config/server-inventory/
├── servers.json inventory (no secrets, 0600)
├── secrets.enc AES-256-GCM blob, master key in your macOS Keychain
└── audit.log append-only JSON-lines, every mutation
Use the paths_report tool to get every file location, including resolved identity file paths and ssh aliases that don't resolve in your ~/.ssh/config.
npm install -g @isogonic/codeplane-server-inventoryOr install locally in a project:
npm install @isogonic/codeplane-server-inventoryThis server runs as a local stdio MCP server. There is no separate CLI or network service.
- Open Codeplane → Settings → MCP Servers (or Connected Apps / Plugins, depending on your UI version).
- Click Add Server / Connect.
- Choose Local command (or Custom command).
- Fill in:
- Name:
server-inventory - Command:
npx -y @isogonic/codeplane-server-inventory - Environment: add
SERVER_INVENTORY_ALLOW_EXEC=0
- Name:
- Save / Connect. Codeplane will spawn the server on demand.
Add this to your codeplane.jsonc (or codeplane.json):
If you installed globally:
{
"mcp": {
"server-inventory": {
"type": "local",
"command": ["server-inventory-mcp"],
"environment": {
"SERVER_INVENTORY_ALLOW_EXEC": "0"
},
"enabled": true,
"timeout": 10000
}
}
}Allow or prompt for specific tools in codeplane.jsonc:
{
"permission": {
"tools": {
"mcp__server-inventory__list_servers": "allow",
"mcp__server-inventory__ssh_target_for": "allow",
"mcp__server-inventory__get_secret": "ask",
"mcp__server-inventory__exec_on": "deny"
}
}
}MCP tool IDs are generated from the server name and tool name. Keep the server name (server-inventory) stable if you persist permission rules.
- Grouped + tagged inventory — organize servers by environment, role, team, or any tag.
- Encrypted secrets store — AES-256-GCM with master key from macOS Keychain or scrypt-derived passphrase.
- Agent-safe defaults —
exec_ondefaults todry_run: true, duplicate names are rejected with a hint to useupdate_server, secrets are never echoed back to the user. - SSH reachability probing —
ssh_checkclassifies outcomes:ok/auth_failed/dns_failure/refused/timeout/host_key_mismatch/unreachable/unknown. - Remote command execution —
exec_onruns commands across a name / group / tag. Opt-in only: refuses unlessSERVER_INVENTORY_ALLOW_EXEC=1. - Audit trail — append-only JSON-lines log records every mutation and
exec_oncall (server + exit code only, never command body or output).
| File | Default | Override |
|---|---|---|
| Inventory | ~/.config/server-inventory/servers.json |
SERVER_INVENTORY_PATH |
| Secrets | ~/.config/server-inventory/secrets.enc |
SERVER_INVENTORY_SECRETS_PATH |
| Audit log | ~/.config/server-inventory/audit.log |
SERVER_INVENTORY_AUDIT_LOG |
| Master key | macOS Keychain (security) on darwin; scrypt-derived from SERVER_INVENTORY_PASSPHRASE elsewhere |
— |
exec_on gate |
off by default | SERVER_INVENTORY_ALLOW_EXEC |
All files except ~/.ssh/config are created with mode 0600.
| Tool | What it does |
|---|---|
inventory_info |
Where the inventory + secrets + audit log live; counts. |
paths_report |
Detailed report of every file location, including resolved identity files (with chmod warnings) and ssh aliases (with whether ~/.ssh/config defines them). |
validate_inventory |
Sanity-check every server: identity files exist, ssh aliases resolve, every entry is reachable. |
list_servers |
Filter by group / tag / environment / role / free-text search. |
get_server |
Full record + ssh command + which secret keys are available. |
list_groups |
Every distinct group with member names. |
list_tags |
Every distinct tag with usage counts. |
ssh_target_for |
Resolve a name OR group OR tag to ssh commands. |
add_server / update_server / remove_server |
CRUD. remove_server cascades to delete secrets; update_server with rename_to migrates them. |
secrets_info |
Backend + master-key provider + secrets file path. |
set_secret |
Encrypt + store one value. Accepts expires_at (ISO) or expires_in (30d, 12h, 2w); preserves created_at across updates. Returns metadata, never the value. |
get_secret |
Decrypt + return one value. Call this right before the command that needs it. |
list_secrets |
Keys for one server with metadata (created_at, updated_at, optional expires_at, expired). Values are never returned. |
list_all_secrets |
Every server's keys with metadata + an expired_count. |
delete_secret |
Remove one key. |
audit_tail |
Last N entries from the audit log. |
ssh_check |
Probe reachability per host with structured outcomes. Bounded parallelism, ConnectTimeout, hard kill timer. |
exec_on |
Run a non-interactive command across name / group / tag. Opt-in: refuses unless SERVER_INVENTORY_ALLOW_EXEC=1. Defaults to dry_run: true — first call returns reachability + the plan; pass dry_run: false to actually run. Output truncated; audit log records server + exit code only. |
A realistic agent flow once the inventory is populated:
user → agent → Codeplane → MCP
agent: list_groups
← { groups: [{ name: "logicplanes", count: 4, members: [...] }, ...] }
agent: ssh_target_for { group: "logicplanes" }
← { count: 4, targets: [
{ name: "lp-web-1", command: "ssh ubuntu@10.0.0.5", ... },
{ name: "lp-db-1", command: "ssh lp-db-1", ... },
{ name: "lp-app-1", command: "ssh -J ops@bastion deploy@10.0.1.20" },
...
] }
for each target:
agent: get_server { name }
← { ..., secrets: { keys: ["sudo_password"], hint: "..." } }
agent: get_secret { server, key: "sudo_password" }
← { value: "..." } # used immediately, not persisted
agent (via its own ssh tool): ssh ... 'echo "$sudo_pw" | sudo -S lynis audit system --quick'
agent: collects findings
The agent never had to ask you which servers count, what credentials to use, or where the keys live. paths_report would have told it everything in one call if it ever needed to debug.
What this server is good for:
- Personal / small-team inventory that lives on your laptop or a single workstation. The encrypted secrets file is unreadable without the master key, which never leaves your keychain (or env var).
- Replacing "credentials pasted into chat" with "credentials retrieved from an encrypted local store the moment the agent needs them".
- An audit trail of what was changed and when.
What it is not:
- A team password manager. There's no sharing, no rotation policy. Per-key
expires_atis a reminder mechanism (surfaced throughlist_secretsandvalidate_inventory); it does NOT delete or rotate the value. Use 1Password / Bitwarden / Vault for actual team credential management. - An access-control system. Anything that can talk to this MCP server can read all the secrets. Don't expose it over the network.
- Tamper-proof. The audit log is on disk and can be deleted. If you need cryptographically verifiable history, ship the audit log to a WORM store.
git clone https://github.com/isogonic/codeplane-server-inventory.git
cd codeplane-server-inventory
npm install
npm run build # compile TypeScript -> dist/
npm run test:unit # node --test on the unit suites
npm run smoke # spawn the built MCP server, exercise every tool
npm test # build + unit + smoke chained
npm run dev # run src/index.ts directly via tsx (no build)Set SERVER_INVENTORY_TRACE=1 for stderr breadcrumbs from the lock / load / save paths when debugging.
Works on:
- macOS (uses Keychain for master key by default)
- Linux (uses env-passphrase fallback)
- Windows (uses env-passphrase fallback; file permissions are ignored by the OS)
All files use Node.js built-ins (path, fs, os, crypto) and are tested on ubuntu-latest and macos-latest with Node 20 and 22.
src/
index.ts MCP server entry point (stdio)
inventory.ts inventory store + SSH resolution
secrets.ts encrypted secrets store
audit.ts append-only audit log
schema.ts Zod validation
paths.ts paths_report helper
ssh.ts ssh_check / exec_on implementation
tests/ Node test suites
scripts/ smoke test
dist/ compiled output
MIT — see LICENSE.
{ "mcp": { "server-inventory": { "type": "local", "command": [ "npx", "-y", "@isogonic/codeplane-server-inventory" ], "environment": { "SERVER_INVENTORY_ALLOW_EXEC": "0" }, "enabled": true, "timeout": 10000 } } }