Skip to content
This repository was archived by the owner on Jun 1, 2026. It is now read-only.

isogonic/codeplane-server-inventory

Repository files navigation

Codeplane Server Inventory

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.

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_alias that 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.

Install

npm install -g @isogonic/codeplane-server-inventory

Or install locally in a project:

npm install @isogonic/codeplane-server-inventory

Add to Codeplane

This server runs as a local stdio MCP server. There is no separate CLI or network service.

Via Codeplane UI

  1. Open CodeplaneSettingsMCP Servers (or Connected Apps / Plugins, depending on your UI version).
  2. Click Add Server / Connect.
  3. Choose Local command (or Custom command).
  4. Fill in:
    • Name: server-inventory
    • Command: npx -y @isogonic/codeplane-server-inventory
    • Environment: add SERVER_INVENTORY_ALLOW_EXEC = 0
  5. 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 defaultsexec_on defaults to dry_run: true, duplicate names are rejected with a hint to use update_server, secrets are never echoed back to the user.
  • SSH reachability probingssh_check classifies outcomes: ok / auth_failed / dns_failure / refused / timeout / host_key_mismatch / unreachable / unknown.
  • Remote command executionexec_on runs commands across a name / group / tag. Opt-in only: refuses unless SERVER_INVENTORY_ALLOW_EXEC=1.
  • Audit trail — append-only JSON-lines log records every mutation and exec_on call (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 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.

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_at is a reminder mechanism (surfaced through list_secrets and validate_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 output

License

MIT — see LICENSE.

About

MCP server that maintains a grouped, tagged inventory of SSH-reachable hosts so an agent can resolve queries like "audit all logicplanes servers" into concrete ssh targets.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors