Skip to content

spranab/mcpier

Repository files navigation

Pier

ci npm version license

MCPs, simpler. Self-hosted control plane for Model Context Protocol servers.

One YAML manifest on your homelab, pier sync on every client, zero API keys in ~/.claude.json. Install MCPs from the official MCP Registry, community catalogs, or any git repo. Spawn them centrally so clients just point at a URL — no npx or uvx on your laptop.

pier quickstart

The problem

You run MCPs across five machines. Each ~/.claude.json has your OpenAI key, Gemini key, GitHub token — in plaintext. Adding a new MCP means editing five files. Rotating a key means editing five files. Pick up a new laptop? Copy-paste five configs. Hit the 8-server / 60-second timeout wall and everything falls over.

Public directories (mcp.so, Smithery) don't help — your secrets aren't in them. Client-side tools (mcp-linker) sync configs on one machine but charge for cross-device and still leave keys on disk in cleartext. Anthropic's MCP Registry is read-only discovery; it can't actually install for you.

What Pier does

  • One manifest, one source of truth. YAML on your homelab describes every MCP you use — transport, runtime, which secrets it needs.
  • Secrets stay on the server. AES-256-GCM encrypted at rest. CLI fetches them only when writing a client config, over an authenticated channel. Optional file-backed master key (PIER_MASTER_KEY_FILE) for Docker/K8s secret mounts.
  • Multi-client output. Writes ~/.claude.json, Cursor's mcp.json, Codex's config.toml. More on the way.
  • Gateway for remote-eligible MCPs. Pier spawns the MCP subprocess on your homelab and fronts it over SSE. Clients put ONE URL entry in their config; secrets never touch the client. Dodges the 8-server timeout by design.
  • Built-in marketplace. Subscribed to the official MCP Registry out of the box — namespace-authenticated entries (com.stripe/mcp, io.github.*). Plus curated feeds for community servers. Plus pier install-git <url> for anything else.
  • Self-hosted, open source, MIT. Your homelab, your keys, your network. No SaaS tier.

Architecture

┌─────────────────┐            ┌──────────────────────────┐
│  Homelab        │            │  Your laptop / desktop   │
│                 │            │                          │
│  ┌───────────┐  │            │  ┌────────────────────┐  │
│  │  Pier     │◄─┼──HTTP(S)───┤  │  pier CLI          │  │
│  │  server   │  │            │  │  (writes configs)  │  │
│  │           │  │            │  └────────────────────┘  │
│  │  API      │  │            │                          │
│  │  UI       │◄─┼──browser───┤  ~/.claude.json          │
│  │  Gateway  │  │            │  ~/.cursor/mcp.json      │
│  │  ┌─────┐  │  │            │  ~/.codex/config.toml    │
│  │  │MCP-1│  │  │            │                          │
│  │  │MCP-2│◄─┼──┼──SSE───────┤  Claude Code / Cursor    │
│  │  │MCP-N│  │  │            │  (one URL per remote MCP)│
│  │  └─────┘  │  │            │                          │
│  └───────────┘  │            └──────────────────────────┘
│       │         │
│       ▼         │
│   SQLite        │
│   (encrypted    │
│    secrets,     │
│    manifest,    │
│    audit log)   │
└─────────────────┘

Install

Server (pick one)

Runtime One command
Docker Compose docker compose -f https://raw.githubusercontent.com/spranab/mcpier/main/deploy/compose/install.yml up -d
Docker run docker run -d --name pier -p 8420:8420 -v pier-data:/data -e PIER_MASTER_KEY=$(openssl rand -hex 32) -e PIER_TOKENS=$(openssl rand -hex 24) ghcr.io/spranab/mcpier:latest
Kubernetes kubectl apply -f https://raw.githubusercontent.com/spranab/mcpier/main/deploy/kubernetes/install.yaml

All three pull ghcr.io/spranab/mcpier:latest, bind to port 8420, persist state to a named volume / PVC. The Docker Compose and Kubernetes install files ship with placeholder credentials so you get a working instance immediately — rotate before storing real API keys (see deploy/compose/README.md / deploy/kubernetes/README.md).

CLI (on every client machine)

npm i -g mcpier

Requires Node 20+. That puts the pier binary on your PATH.

Quickstart

Once the server is up and the CLI is installed:

# 1. Point the CLI at your Pier server (once per machine)
pier login http://<your-host>:8420 --token <one-of-your-PIER_TOKENS>

# 2. Install an MCP — interactive prompts for secrets
pier install brainstorm-mcp --location remote --sync claude-code
#   ↑ finds it in the official catalog
#   ↑ prompts for OpenAI / Gemini / DeepSeek keys
#   ↑ stores them encrypted on Pier
#   ↑ writes the URL entry into ~/.claude.json

# 3. Restart Claude Code → brainstorm_* tools live

Or script it:

pier install brainstorm-mcp --location remote --non-interactive \
  --set openai_key=sk-... \
  --set gemini_key=... \
  --sync claude-code

Or from any repo with a pier.yaml at its root:

pier install-git github.com/spranab/some-mcp --sync claude-code

CLI reference

Command What it does
pier login <server> --token <t> Save server URL + device token to ~/.config/pier/config.json
pier status Show server health, manifest summary, secrets count
pier install <name> Install from a subscribed catalog (interactive prompts)
pier install-git <url> Install from a git repo or raw pier.yaml URL
pier sync Pull manifest + secrets and write client configs (default: claude-code)
pier sync --clients claude-code,cursor,codex Sync to multiple clients at once
pier secrets list List secret keys stored on the server (not values)
pier secrets set <key> <value> Store a secret (encrypted at rest)
pier backup -o <file> Download JSON bundle (encrypted DB + manifest)
pier restore <file> Restore from a bundle (same master key required)

Run pier <command> --help for full flags.

Runtime support

Pier installs MCPs written in any language:

runtime: in formula Command Pier generates Prereq for location: local
node (default) npx -y <package> Node.js 20+
python uvx <package> uv
binary <package> <args> The binary on PATH

For location: remote (Pier spawns the subprocess itself), the prereq lives only on the Pier host. On Linux, spawned subprocesses run under a prlimit --as memory cap (default 512 MB, PIER_SPAWN_MEMORY_MB) so a runaway MCP can't OOM your homelab.

Catalogs

Pier subscribes out of the box to:

Add any catalog.json URL via the UI's Sources panel, or by setting PIER_CATALOG_URLS. Anything not in your subscribed catalogs is one pier install-git <url> away.

Trust model

Three tiers, clearly labeled in the UI:

Badge Source Authority
registry (green) Official MCP Registry Namespace verified via GitHub OAuth or DNS TXT — unforgeable per namespace
curated (amber) Subscribed catalog.json feeds Soft — you trust the catalog maintainer
(no badge) pier install-git <url> Explicit user confirmation — you trust the URL

Deploy docs

Development

git clone https://github.com/spranab/mcpier && cd mcpier
npm install
npm run dev        # shared (watch) + server + UI
npm test           # crypto + manifest + config tests
npm run build      # production build of all workspaces

Repo layout

Releases

Published on npm as mcpier. Images on GHCR as ghcr.io/spranab/mcpier. See the releases page for changelogs.

License

MIT

About

Self-hosted MCP control plane — deploy on your homelab, install from the official MCP Registry, keep API keys off your clients. Docker / K8s / npm.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors