plumb

One MCP config. Every client. All connected.
plumb manages MCP server configurations across AI coding tools from a single canonical YAML config. Define your servers once, and plumb applies them to Cursor, Claude Code, Gemini CLI, and other clients - resolving secrets from your OS keychain and handling each client's format differences.
Features
- Single source of truth - one YAML config for all your MCP servers across all AI clients
- Secret management - URI-based secret references (
op://, keychain://, env://, doppler://) resolved at apply time
- Declarative apply -
plumb apply shows a diff and asks for confirmation before writing
- Import from existing -
plumb import --all bootstraps your config from what you already have
- Profiles - named server groups assigned to different clients (
default, work, minimal)
- Hooks - run shell commands before/after sync (e.g. restart clients to pick up changes)
- Cross-machine portability - config file is secret-free and safe to commit to dotfiles
- JSON Schema - editor intellisense for the canonical config via yaml-language-server
- Doctor command - environment validation with actionable fix suggestions
- macOS (amd64, arm64) - full support
- Linux (amd64, arm64) - full support
Windows is not supported.
Installation
Homebrew (macOS, Linux)
brew install Drew-Daniels/tap/plumb
Go install
go install github.com/Drew-Daniels/plumb@latest
Nix flake
nix profile install github:Drew-Daniels/plumb
From source
git clone https://github.com/Drew-Daniels/plumb.git
cd plumb
go build -o plumb .
Quick Start
# Detect installed AI clients and create a config
plumb config
# Import servers from your existing client configs
plumb import --all
# See what plumb would write to each client
plumb diff
# Apply changes
plumb apply
# Check sync status
plumb status
Configuration
Config lives at ~/.config/plumb/config.yaml (XDG-aware):
servers:
firecrawl:
command: npx -y firecrawl-mcp
env:
FIRECRAWL_API_KEY: op://Personal/Firecrawl/API Key
context7:
transport: http
url: https://mcp.context7.com/mcp
github:
command: docker
args: [run, -i, --rm, -e, GITHUB_PERSONAL_ACCESS_TOKEN, ghcr.io/github/github-mcp-server]
env:
GITHUB_PERSONAL_ACCESS_TOKEN: keychain://github-pat
profiles:
default: [firecrawl, context7]
work: [firecrawl, context7, github]
clients:
cursor: default
claude-code: work
gemini: default
Commands
Apply
plumb apply # apply to all clients
plumb apply cursor # apply to one client
plumb diff # preview changes (secrets masked)
plumb diff --reveal # preview changes (secrets shown)
plumb status # sync overview
Server Management
plumb add <name> --command <cmd> # add a stdio server (auto-splits into command + args)
plumb add <name> --command npx --args "-y,pkg" # explicit args
plumb add <name> --url <url> # add an HTTP server
plumb add <name> --command <cmd> --env "KEY=op://vault/item/field" # env var with secret URI
plumb add <name> --command <cmd> --transport stdio # explicit transport (stdio or http)
plumb rm <name> # remove a server
plumb list # list all servers
plumb enable <name> # enable a server
plumb disable <name> # disable without removing
plumb config # open config in $EDITOR
plumb reset --yes # remove canonical config + lockfile (see --purge-backups)
Secrets
plumb secret check # verify all secret URIs resolvable
Profiles
plumb profile ls # list profiles
plumb profile create <name> <servers...> # create a profile
plumb profile rm <name> # delete a profile
Diagnostics
plumb doctor # runtime + which AI clients are detected (does not validate YAML)
plumb lint # validate config, includes, rules, and secret references
plumb version # print version info
Editor Intellisense
Add a modeline to your config file for autocomplete and validation:
# yaml-language-server: $schema=https://raw.githubusercontent.com/Drew-Daniels/plumb/main/schemas/config.schema.json
servers: ...
Shell Completions
plumb completions bash > /etc/bash_completion.d/plumb
plumb completions zsh > "${fpath[1]}/_plumb"
plumb completions fish > ~/.config/fish/completions/plumb.fish
License
MIT