CLI Reference
The CLI ships with 35 commands covering search, install, publish, runtime, daemon management, version visibility, OAuth, config, audit, and health. This reference documents pharos-cli v2.0.0.
Installation
# Unix / macOS / WSL / Git Bash
curl -fsSL https://getpharos.dev/install | sh
# Windows PowerShell (not cmd.exe)
irm https://getpharos.dev/install.ps1 | iex
# Or install with Go 1.25+
go install github.com/Wpnx330/pharos-cli@latest
# Print the CLI version
pharos versionSearch & Discovery
# Search the registry for a keyword
pharos search "flight tracker"
# Limit the number of results
pharos search "database" --limit 20
# Paginate results
pharos search "database" --page 2
# Output search results as JSON
pharos search "database" --json
# Filter by transport and source registry
pharos search echo --transport http-sse --registry pharos
# Quote package IDs that contain spaces or (
pharos info "MCP Test Servers"
# Show full package details from the registry
pharos info my-test-mcp-server
# Use the alias
pharos show my-test-mcp-server
# List packages installed locally
pharos list
# List only currently running servers
pharos list --running
# Sort installed packages by a field
pharos list --sort memorySearch trust signals
The search table shows the trust signals the registry provides: OWNER (publisher namespace) and CATEGORY columns alongside DOWNLOADS, with download counts humanized (1234 renders as 1.2k). A version that is not active is flagged inline, as in 1.2.3 (stale); active versions stay bare.
pharos info shows the same Owner and Category fields, and --json carries publisher, category, tools_count, and version_status per result.
Sponsored results and security scorecard
Sponsored placements: when the registry has active boosts matching your query, a SPONSORED block prints above the organic table (same columns, max 2 rows, each name suffixed [boosted]). Sponsored entries never reorder organic results, and with no active boosts the output is unchanged.
--json exposes sponsored entries as a separate boosted array (each entry "sponsored": true); organic results are never mixed in.
Security scorecard: search results and pharos info carry the registry's scorecard grade.
Install & Manage
# Install an MCP server into your client
pharos install my-test-mcp-server --client claude-desktop
# Install a pinned version
pharos install my-test-mcp-server --version 2.0.7
# Install system-wide
pharos install my-test-mcp-server --global
# Interactively pick which MCP clients to configure
pharos install my-test-mcp-server --select-clients
# Install with a frozen lockfile (CI-friendly)
pharos install my-test-mcp-server --frozen
# Install without writing MCP client configs for dependencies
pharos install my-test-mcp-server --no-dep-config
# Auto-unload this HTTP/SSE server after 30 minutes idle
pharos install my-test-mcp-server --idle-timeout 30
# Keep this server always on (never unload)
pharos install my-test-mcp-server --idle-timeout 0
# Install into Cursor
pharos install my-test-mcp-server --client cursor
# Remove an installed package
pharos remove my-test-mcp-server
# Remove even if other packages depend on it
pharos remove my-test-mcp-server --force
# Update one installed server
pharos update my-test-mcp-server
# Dry-run an update to see what would change
pharos update my-test-mcp-server --dry-run
# Update all installed packages (installs the new version and
# rewrites affected client configs, same as install)
pharos update
# Resolve dependencies and write pharos.lock
pharos lock
# Import existing MCP servers from a client config
pharos import --client claude-desktop
# Import without managing the servers (read-only)
pharos import --as-unmanaged
# Preview a full adopt (full adopt report, writes nothing)
pharos import --dry-run --adopt
# Adopt every detected client config as the managed baseline
# (writes pharos.lock and ~/.pharos/mcp.json; conflicts prompt
# unless --yes, --json, or PHAROS_NON_INTERACTIVE is set)
pharos import --adopt
# Machine-readable import output
pharos import --jsonInstall receipts
Every install emits a machine-readable receipt with per-client results, dependency rows, and a status of ok or partial.
Partial status means non-fatal failures occurred (client config write failures, lockfile save failures); they are listed in errors[].
The human summary prints the same outcome as “completed with N warnings”.
Publishing
# Scaffold a new pharos.json manifest (interactive)
pharos init
# Non-interactive with defaults
pharos init --yes
# Transport choices when prompted: stdio (default), http-sse, streamable-http
# Create a .tgz tarball without publishing (like npm pack)
pharos package
# Publish a package to the registry
pharos publish
# Publish from a specific directory
pharos publish ./my-server
# Publish with an explicit auth token
pharos publish --token phs_live_xxx
# Dry-run a publish to validate without uploading
pharos publish --dry-runVersion Visibility
# Hide a version from search and direct lookup
pharos unpublish my-test-mcp-server --version 0.2.0
# Hide ALL versions
pharos unpublish my-test-mcp-server --all
# Skip the confirmation prompt
pharos unpublish my-test-mcp-server --all --yes
# Re-activate a previously unpublished version
pharos republish my-test-mcp-server --version 0.2.0
# Permanently remove a version from the registry (irreversible)
pharos purge my-test-mcp-server --version 0.2.0
# Purge ALL versions
pharos purge my-test-mcp-server --all --yesRuntime
# Start a locally installed MCP server (background)
pharos start my-test-mcp-server
# Start in the foreground (blocks terminal, Ctrl+C to stop)
pharos start my-test-mcp-server --foreground
# Override the declared port (http-sse only)
pharos start my-test-mcp-server --port 3100
# Pass environment variables to the server
pharos start my-test-mcp-server --env API_KEY=secret
# Stop a running server
pharos stop my-test-mcp-server
# Stop all running servers
pharos stop --all
# Force-stop with SIGKILL after timeout
pharos stop my-test-mcp-server --force --timeout 10OAuth Configuration
# Register OAuth settings for a published MCP server
pharos oauth configure my-salesforce-server \
--auth-url https://login.salesforce.com/services/oauth2/authorize \
--client-id 3MVG9...your-client-id \
--scopes api,refresh_token \
--pkce
# With a server-side secret (confidential clients)
pharos oauth configure my-google-drive-server \
--auth-url https://accounts.google.com/o/oauth2/v2/auth \
--client-id your-client-id.apps.googleusercontent.com \
--scopes openid,email,profile,drive.file \
--pkce \
--client-secret your-client-secretDaemon (Process Supervisor)
# Start the daemon in the background
pharos daemon start
# Run in the foreground (for debugging)
pharos daemon start --foreground
# Show daemon status and managed server details
pharos daemon status
# Show recent daemon log output (last 50 lines)
pharos daemon log
# Show more log lines
pharos daemon log --lines 100
# Restart the daemon
pharos daemon restart
# Stop the daemon and all managed servers
pharos daemon stop
# Enable autostart on system boot
pharos daemon autostart --on
# Disable autostart on boot
pharos daemon autostart --off
# Check current autostart status
pharos daemon autostartAutostart on boot
pharos daemon autostart --on configures the daemon to start automatically when you log in, --off disables it, and bare pharos daemon autostart shows the current status.
The underlying mechanism is platform-specific: a systemd user unit on Linux (~/.config/systemd/user/pharos-daemon.service; enable lingering with loginctl enable-linger $USER if you want the daemon to survive when no session is active), a LaunchAgent plist on macOS (~/Library/LaunchAgents/dev.getpharos.daemon.plist, runs on login), and a Task Scheduler task on Windows (schtasks /create /tn PharosDaemon /sc onlogon, triggers at user logon).
Version Pinning
# Lock a server at its currently installed version
pharos pin my-test-mcp-server
# Install a specific version first, then pin it
pharos pin my-test-mcp-server 1.2.3
# Release the pin
pharos unpin my-test-mcp-server
# Origin-aware dry-run that still probes pinned servers
pharos update --checkPinned-skip behavior
Pinned servers are skipped by pharos update and pharos update --all: no registry call, no change. The summary table lists them with the pinned action and the pharos unpin hint.
pharos update --check (and --dry-run) still probe pinned servers and report what's available, marked pinned, so you can see what you're missing without applying anything. For git-hosted origins, --check also shows repo and changelog links.
Installing a different version of a pinned server (directly or through dependency resolution) moves the pin to that installed version, most-recent-install-wins.
Try Before You Wire
# Probe a server's live capabilities without wiring it into any client
pharos try echo-server
# Raise the total probe budget (default 10s)
pharos try echo-server --timeout 30s
# Machine-readable capabilities document
pharos try echo-server --json
# Print and launch the npx MCP Inspector command instead of probing
pharos try echo-server --inspectWhat try reports
pharos try spawns the server exactly as it is configured in ~/.pharos/mcp.json, speaks real MCP to it (initialize handshake over stdio JSON-RPC), and prints its live capabilities in about five seconds. No client config is touched.
Honest failures: if the server won't start, fails the handshake, or hangs, try exits 1 and shows the server's actual stderr (last 10 lines), not just an exit status. An unknown server exits 2 with an install hint. try probes stdio servers only.
--json emits a single stdout document {server, caps: {protocolVersion, serverInfo, tools[], resources[], prompts[]}}; probe failures emit {server, errors, stderr_tail}. With --inspect --json only the inspector command is reported, without spawning.
Idle-Cost Budget
# Human report: daemon, resident processes, per-server idle budget
pharos budget
# Full structured report
pharos budget --jsonReading the report
pharos budget aggregates the standing cost of daemon-managed resident servers only: plain stdio servers are launched on demand by your MCP client and never appear here. When the daemon is not running that is a valid report: 0 resident processes, exit 0.
Each server row shows idle time against its own idle timeout with a BUDGET flag: active (inside its budget), over (past its budget and still resident), idle (past its budget, unloaded), always-on (idleTimeout=0, no auto-unload budget), never-used (no recorded request).
The report ends with advisory suggestions naming the real commands (pharos stop <name>, pharos daemon stop) and never applies anything. budget is strictly read-only; memory figures are OS RSS estimates.
Secure Tunnel Sharing
# Expose requires the daemon
pharos daemon start
# Publish a daemon-managed server on an explicit public bind
pharos expose web --addr :9500
# Detach after showing the token (PID confirmed ~5s later)
pharos expose web --addr :9500 --background
# List active exposes (NAME/ADDR/TARGET/PID/STATUS/EXPIRES)
pharos expose list
# Graceful stop (10s grace), or stale-entry cleanup
pharos expose stop webSecurity model
pharos expose publishes one daemon-managed HTTP/SSE server through a token-authed reverse proxy. The token comes from crypto/rand (256-bit, base64url) and is printed once; it is stored only as a SHA-256 hash in ~/.pharos/expose.json. Lose the printed token and you re-run expose.
Every request must present Authorization: Bearer <token>; the comparison is constant-time, and anything else gets a 401 JSON error and never reaches the backing server. The validated header is stripped before proxying, so the public token never reaches the local server's logs.
--addr is required: pharos never silently binds 0.0.0.0 (loopback-only sharing with --addr 127.0.0.1:9500 is valid). --ttl defaults to 8h with a hard cap of 24h; on expiry the listener closes and the process exits cleanly.
Plaintext HTTP on the wire: a 127.0.0.1 bind keeps the token on-host, but a LAN bind puts it on the wire where anyone who can observe the traffic can read it. For remote sharing, front the listener with TLS or reach it through an SSH tunnel. There is no third-party tunnel in the path.
MCP stdio Server
# Run Pharos as an MCP stdio server (JSON-RPC 2.0 on stdin/stdout)
pharos serve
# Expose the install tool to MCP clients (off by default)
pharos serve --allow-installTools and framing
pharos serve speaks newline-delimited JSON-RPC 2.0: initialize (protocolVersion 2024-11-05, serverInfo pharos), tools/list, tools/call, ping. stdout carries only protocol frames; diagnostics go to stderr. Tool failures are isError:true tool results, never a crash; unknown methods answer -32601, bad params -32602.
Tools: search(query, limit?, transport?), info(name), list_installed(). install(name, version?) is opt-in via --allow-install and runs the same pipeline as pharos install; when the flag is absent the install tool is omitted from tools/list.
{
"mcpServers": {
"pharos": {
"command": "pharos",
"args": ["serve"]
}
}
}Profiles (Contexts)
# Create a profile and map it to a client
pharos profile create work --client cursor
# Create one that inherits another profile's servers
pharos profile create personal --client claude-desktop --inherit base
# Attach already-installed servers
pharos profile add work Context7 github
# Preview the reconciliation plan (writes nothing)
pharos profile use work --dry-run
# Apply the plan (prompts Apply? [y/N]; --yes skips the prompt)
pharos profile use work
# Delete the profile; servers stay installed
pharos profile rm work
# Daemon: load this set, idle the rest
pharos profile run workHow profiles work
A profile is a named context that maps a set of installed servers to clients. pharos.lock and ~/.pharos/mcp.json are untouched until pharos profile use, which reconciles the mapped clients to contain exactly the profile's target set. pharos profile ls --json shows the context map.
Every profile implicitly inherits base: servers attached to base stay in every context, so common servers are listed once. --inherit adds one explicit parent on top (single parent, cycles rejected).
profile use is safe by default: it computes the plan first and prompts Apply? [y/N]. --yes applies without prompting, --dry-run is a pure preview that writes nothing, and --strict refuses to apply while unprofiled servers would be removed.
Security & Health
# Scan installed servers for known vulnerabilities
pharos audit
# Output audit results as JSON
pharos audit --json
# Diagnose CLI health — registry, installed servers, configs
# Validates client configs across JSON, YAML, and TOML formats
pharos doctor
# Output doctor results as JSON
pharos doctor --json
# Report drift between client configs and the pharos.lock baseline
pharos doctor --diff
# Check the PHAROS registry health
pharos health
# Output health as JSON
pharos health --jsonConfig drift detection (doctor --diff)
Safe writes keep pharos from corrupting a client config, but a hand edit can still drift a config out from under pharos. pharos doctor --diff is a read-only report that compares every pharos-managed server entry in each client config against the pharos.lock baseline recorded at install time, and reports missing, modified (naming the field), or extra findings. Extra entries are unmanaged, hand-added servers; the report lists them as informational.
Only command, args, env, url, and type are compared. Reformatting (key order, whitespace, empty containers) never counts as drift, and unknown user keys inside an entry are left alone.
Run with --json (or PHAROS_JSON=1) and the findings appear on the drift checks in the doctor JSON report.
✗ Config drift: Generic MCP 2 drift finding(s) in this config
• server 'context7' modified: env.API_KEY: expected "abc", got "xyz"
• server 'hand-added' is not managed by pharos (not in pharos.lock) — unmanaged (hand-added?)Shell Completion
# Generate shell tab-completion (bash, zsh, fish, or powershell)
pharos completion bash
# Load it in your current shell (bash)
source <(pharos completion bash)Account & Configuration
# Authenticate with the registry via GitHub OAuth
pharos login
# Use a pre-issued token (paste instead of browser flow)
pharos login --manual
# Show the currently authenticated user
pharos whoami
# Show authenticated user as JSON
pharos whoami --json
# Read a config value
pharos config registry
# Set a config value
pharos config registry https://api.getpharos.dev
# List registered MCP clients
pharos config list-clients
# Add a custom MCP client for auto-detection
pharos config add-client cursor --path ~/.cursor/mcp.json
# Add with a specific config format
pharos config add-client my-app --path ~/my-app/mcp.json --format array
# Remove a registered MCP client
pharos config remove-client cursorAutomation & Agents
# Non-interactive agent run: JSON receipt, no prompts, default answers
PHAROS_JSON=1 PHAROS_NON_INTERACTIVE=1 PHAROS_ASSUME_YES=1 \
pharos install echo-mcp-server-for-testingAgent environment contract
Pharos reads three environment variables for non-interactive use: PHAROS_JSON=1 for JSON output, PHAROS_NON_INTERACTIVE=1 to disable prompts, and PHAROS_ASSUME_YES=1 to accept the default answers.
The machine-readable command surface for agents is served live at https://discoverpharos.dev/llm.txt, generated from the CLI repo's docs/llm.txt.
Exit Codes
# 0 — success
# 1 — user error or audit found vulnerabilities
#
# Example: check the exit code in a shell script
pharos audit && echo "clean" || echo "exit code: $?"