@isogonic/codeplane-server-inventory
v0.5.6
Published
Codeplane MCP server for SSH server inventory with encrypted secrets. Lets Codeplane agents answer 'audit all <group> servers' without manual credential input.
Maintainers
Readme
[!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 needexec_on.
What It Is
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 mutationUse 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.
Install
npm install -g @isogonic/codeplane-server-inventoryOr install locally in a project:
npm install @isogonic/codeplane-server-inventoryAdd to Codeplane
This server runs as a local stdio MCP server. There is no separate CLI or network service.
Via Codeplane UI
- 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.
Via codeplane.jsonc
Add this to your codeplane.jsonc (or codeplane.json):
{
"mcp": {
"server-inventory": {
"type": "local",
"command": [
"npx",
"-y",
"@isogonic/codeplane-server-inventory"
],
"environment": {
"SERVER_INVENTORY_ALLOW_EXEC": "0"
},
"enabled": true,
"timeout": 10000
}
}
}If you installed globally:
{
"mcp": {
"server-inventory": {
"type": "local",
"command": ["server-inventory-mcp"],
"environment": {
"SERVER_INVENTORY_ALLOW_EXEC": "0"
},
"enabled": true,
"timeout": 10000
}
}
}Permissions
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.
Core Features
- 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 Locations
| 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.
Tools
| 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. |
Example: audit all logicplanes servers
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 [email protected]", ... },
{ name: "lp-db-1", command: "ssh lp-db-1", ... },
{ name: "lp-app-1", command: "ssh -J ops@bastion [email protected]" },
...
] }
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 findingsThe 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.
Security Model
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.
Development
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.
Cross-Platform Support
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.
Repository Layout
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 outputLicense
MIT — see LICENSE.
