This guide is for running Plug as shared infrastructure: a local daemon for daily agent work, or a remote MCP gateway for trusted clients.
Plug has one configured upstream set and many downstream clients.
plug startstarts the shared daemon, IPC listener, and HTTP server.plug connectis the stdio adapter most local clients use./mcpis the Streamable HTTP endpoint for HTTP-capable clients.plug status,plug clients,plug servers,plug tools, andplug doctorare the primary operator surfaces.
Useful files:
- Config:
~/.config/plug/config.toml - macOS logs:
~/Library/Logs/plug/ - macOS runtime state:
~/Library/Application Support/plug/ - Linux logs/state:
~/.local/state/plug/
Prefer --output json for automation:
plug status --output json
plug clients --output json
plug servers --output json
plug tools --output jsonLoopback-only local use can keep the defaults:
[http]
bind_address = "127.0.0.1"
port = 3282
auth_mode = "auto"For remote clients, set a public URL and TLS material:
[http]
bind_address = "0.0.0.0"
port = 3282
public_base_url = "https://plug.example.com"
auth_mode = "oauth"
tls_cert_path = "/etc/plug/tls/fullchain.pem"
tls_key_path = "/etc/plug/tls/privkey.pem"
allowed_origins = ["https://claude.ai"]Rules enforced by Plug:
- Non-loopback binds require
tls_cert_pathandtls_key_path. auth_mode = "oauth"requirespublic_base_url.- Cert and key paths must be set together.
- Private keys must not be group/world readable on Unix.
Put Plug behind a reverse proxy only if the proxy preserves normal HTTP request headers and forwards the public /mcp URL consistently. public_base_url must be the URL clients actually use.
Downstream auth protects clients connecting to Plug.
Modes:
auto: default. Loopback is unauthenticated; non-loopback uses bearer auth.none: unauthenticated. Use only on loopback or tightly controlled private networks.bearer: protects/mcpwith a bearer token.oauth: local OAuth authorization server with PKCE and token refresh support.
Bearer mode:
[http]
auth_mode = "bearer"
public_base_url = "https://plug.example.com"OAuth mode:
[http]
auth_mode = "oauth"
public_base_url = "https://plug.example.com"
oauth_scopes = ["tools:read"]Clients connect with only the MCP URL, for example
https://plug.example.com/mcp. Plug supports public-client Dynamic Client
Registration and OAuth Client ID Metadata Documents; it does not require a
shared client secret or a per-client TOML entry. Each client must use PKCE
S256, an exact registered HTTPS or loopback redirect (or Cursor's exact
application-claimed native callback), the MCP resource parameter, and
explicit approval on Plug's consent page.
OAuth discovery endpoints:
/.well-known/oauth-authorization-server/.well-known/oauth-protected-resource/.well-known/oauth-protected-resource/mcp/oauth/register
Registrations that are never approved expire after one hour; active clients expire after 90 idle days. Plug retains at most 100 registrations and limits each source address to 10 new registrations per hour. Access tokens last one hour; refresh tokens last 30 days and rotate every time they are used. Registrations and tokens live in an issuer-specific owner-only state file and survive normal restarts.
List or revoke registered clients without revealing tokens:
plug auth clients list
plug auth clients revoke <client-id>Revoking a client immediately invalidates all of its codes, access tokens, and
refresh tokens. The former single-client oauth_client_id,
oauth_client_secret, and shared redirect allowlist settings are intentionally
unsupported; remove them and authorize each remote client again after upgrade.
Operator endpoints use a separate x-plug-operator-token and are not protected by downstream MCP bearer/OAuth tokens. Treat the operator token as an administrative secret.
Upstream OAuth protects Plug when it connects to remote MCP servers.
Example:
[servers.remote_docs]
transport = "http"
url = "https://docs.example.com/mcp"
auth = "oauth"
oauth_client_id = "plug"
oauth_scopes = ["mcp:read", "mcp:write"]Login and inspect state:
plug auth login --server remote_docs
plug auth statusPlug stores reusable OAuth credentials through the configured credential store and refreshes tokens in the background when possible. If a server registration cannot be reused safely, rerun plug auth login --server <name> or configure a stable oauth_client_id.
Start with:
plug status
plug doctor
plug clients
plug servers
plug toolsFor logs:
RUST_LOG=plug=debug,plug_core=debug plug startHTTP tracing:
- Plug accepts W3C
traceparentandx-plug-trace-idon downstream HTTP requests. - Plug propagates trace IDs across router calls, upstream retries, reconnects, and auth-refresh logs.
- Plug validates present SEP-2243
Mcp-MethodandMcp-Nameheaders against the JSON-RPC body. - Plug emits
Mcp-MethodandMcp-Namewhen proxying HTTP/SSE upstream requests.
Operator inventory:
plug tools --output jsonincludes source metadata, trust boundary, upstream-declared annotations, Plug-inferred annotations, and effective annotations.plug servers --output jsonincludes configured transport/auth/trust metadata without serializing secrets./_plug/live-sessionsexposes active session inventory for local operator tooling and requiresx-plug-operator-token.
Plug executes configured stdio commands. Only add upstream commands you trust, or enable sandboxing for third-party/local-risk servers.
Sandboxing is opt-in per stdio server:
[servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/Users/rob/projects"]
[servers.filesystem.sandbox]
enabled = true
allow_network = false
allow_read = ["/Users/rob/projects"]
allow_write = ["/Users/rob/projects/.cache"]On macOS, Plug uses /usr/bin/sandbox-exec with a generated deny-by-default profile, or a custom profile:
[servers.custom.sandbox]
enabled = true
profile_path = "/Users/rob/.config/plug/sandbox/custom.sb"Current limits:
- Sandboxing is implemented for stdio transports only.
- macOS enforcement is implemented; non-macOS sandbox config currently fails fast.
- CPU/memory/process limits are not implemented yet.
Current distribution names:
- GitHub repo:
cyberpapiii/plug - Homebrew tap:
cyberpapiii/tap/plug - crates.io package:
plug-mcp - Installed binary:
plug
Release checks before publishing:
cargo test --workspace
cargo clippy --workspace -- -D warnings
cargo deny check advisories
dist plan --no-local-paths
dist build --artifacts=global
dist build --artifacts=local --target aarch64-apple-darwinPublish order matters for crates.io: publish plug-core before plug-mcp, because the CLI package depends on the library package by version. The public install command is cargo install plug-mcp --locked; use cargo install --git https://github.com/cyberpapiii/plug plug-mcp --locked only when validating unreleased main builds.
Build, package, and install checks can leave large generated directories behind. Before or after a release pass, inspect cleanup candidates with:
scripts/clean-build-artifacts.shThen clean build/deploy artifacts with:
scripts/clean-build-artifacts.sh --yesUse --runtime-cache only when old plug://artifact/... result files are no longer needed. The cleanup script never removes Plug config, OAuth tokens, sockets, PID files, or installed binaries.
For local source reinstalls, the cleanup can be folded into the reinstall command:
./scripts/dev-reinstall.sh --quick --clean