Installation
npm install -g @thornguard/cli
Requires Node.js 18 or later. After installation, the thornguard command is available globally.
Authentication
thornguard auth login
Store your ThornGuard license key in the platform keyring.
You will be prompted to paste your license key (starts with THORN-). The key is stored securely in:
- macOS: Keychain (
security CLI)
- Linux: freedesktop.org Secret Service (
secret-tool)
- Windows: DPAPI-encrypted credential file
Options:
| Flag | Description |
|---|
--license-env NAME | Read the license from environment variable NAME instead of prompting |
thornguard auth logout
Remove the stored license key from the keyring.
Profile Management
Profiles store connection metadata locally. Secrets (license key, upstream tokens) are kept in the platform keyring, never in profile files.
thornguard profile add <name>
Create a new connection profile.
thornguard profile add my-server \
--display-name "My Server (Protected)" \
--target-url https://my-server.com/mcp \
--upstream-auth-mode bearer \
--upstream-token-env MY_TOKEN \
--vendor-name "My Vendor" \
--intel-enabled
Required:
| Flag | Description |
|---|
--target-url URL | The upstream MCP server URL (must be HTTPS) |
Optional:
| Flag | Description |
|---|
--display-name TEXT | Human-friendly label for client configs (supports spaces and special characters) |
--kind client|service | Profile type (default: client) |
--transport MODE | Transport preference: http-first (default), sse-first, http-only, sse-only |
--upstream-auth-mode none|bearer | Whether the upstream server requires auth (default: none) |
--upstream-token-env NAME | Read the upstream bearer token from environment variable NAME instead of prompting |
--vendor-name NAME | Vendor name for advisory intelligence |
--repo-url URL | Repository URL for dependency intelligence |
--status-page-url URL | Status page URL for availability intelligence |
--intel-enabled | Enable advisory intelligence for this profile |
--intel-disabled | Disable advisory intelligence |
--notes TEXT | Free-text notes |
thornguard profile update <name>
Update an existing profile. Only the provided flags are changed; everything else is preserved.
thornguard profile update my-server --display-name "New Display Name"
The upstream bearer token is only re-prompted if --upstream-token-env is explicitly provided or no token exists yet.
thornguard profile remove <name>
Delete a profile and its stored upstream token.
thornguard profile remove my-server
| Flag | Description |
|---|
--delete-remote | Also delete the connection from the ThornGuard management API |
thornguard profile list
List all saved profiles.
thornguard profile list
thornguard profile list --json
thornguard profile show <name>
Show full details of a profile.
thornguard profile show my-server
thornguard profile show my-server --json
Running & Config Generation
thornguard run <name>
Launch the MCP bridge for a profile. This is the command that MCP clients invoke — you typically don’t run it manually.
| Flag | Description |
|---|
--server-url URL | Override the ThornGuard proxy URL (default: https://thorns.qwady.app) |
thornguard print-config
Generate or apply client-specific MCP configuration.
Print to stdout:
thornguard print-config --client claude my-server
Apply directly to client config file:
thornguard print-config --client claude my-server --apply
The --apply flag merges the profile into the client’s config file, preserving existing settings. If a --display-name is set on the profile, it’s used as the server label in the config.
| Flag | Description |
|---|
--client NAME | Required. Target client: claude, vscode, cursor, zed |
--apply | Write directly to the client’s config file instead of printing |
Config file locations (macOS):
| Client | Path |
|---|
| Claude Desktop | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Cursor | ~/.cursor/mcp.json |
| VS Code | ~/Library/Application Support/Code/User/settings.json |
| Zed | ~/.config/zed/settings.json |
Discovery & Protection
thornguard scan
Discover all MCP servers configured across your MCP clients. Read-only — never modifies any config files.
thornguard scan
thornguard scan --client claude
thornguard scan --unprotected-only
thornguard scan --json
Example output:
Client Server Status Upstream
------ -------------------------- ------------------ ---------------------------------
claude GitHub Copilot (Protected) Protected (CLI) https://api.githubcopilot.com/mcp
claude Filesystem Local (not proxy.) --
cursor Slack MCP Unprotected https://mcp.slack.com/sse
| Flag | Description |
|---|
--client NAME | Scan only one client: claude, vscode, cursor, zed |
--protected-only | Show only ThornGuard-protected servers |
--unprotected-only | Show only unprotected servers |
--json | Output as JSON array |
Server statuses:
| Status | Meaning |
|---|
| Protected (CLI) | Uses thornguard run — fully managed by ThornGuard CLI |
| Protected (Bridge) | Uses mcp-remote through thorns.qwady.app — manual header config |
| Unprotected | Remote MCP server with no ThornGuard protection — can be protected |
| Local (not proxyable) | Local stdio server (e.g., python, node) — cannot be proxied |
thornguard protect
Interactive wizard that protects an unprotected remote MCP server with one command.
thornguard protect
thornguard protect --client claude
thornguard protect --yes
Flow:
- Scans all clients for unprotected remote MCP servers
- Presents a numbered list
- You select a server
- The CLI extracts its upstream URL, creates a ThornGuard profile, and rewrites the client config
- The original server name is preserved with “(Protected)” appended
| Flag | Description |
|---|
--client NAME | Only scan a specific client |
--yes | Skip confirmation prompt |
Local stdio servers (e.g., python -m my_server) cannot be proxied through
ThornGuard. These are shown during scan but skipped during protect.
Diagnostics
thornguard doctor <name>
Run connectivity and configuration checks for a profile.
thornguard doctor my-server
Checks performed:
- ThornGuard proxy health
- Target URL format validation
- CLI authentication status
- License key API validation
- Upstream bearer token presence (if required)
- Advisory intelligence refresh (if enabled)
| Flag | Description |
|---|
--server-url URL | Override the ThornGuard proxy URL |
thornguard status <name>
Show the current status and advisories for a profile.
thornguard status my-server
thornguard status my-server --refresh
| Flag | Description |
|---|
--refresh | Force an advisory refresh before showing status |
--server-url URL | Override the ThornGuard proxy URL |
Environment Variables
| Variable | Description |
|---|
THORNGUARD_CONFIG_DIR | Override the config directory (default: ~/.thornguard) |
THORNGUARD_SECRET_BACKEND | Force secret backend: keychain (default) or file |
THORNGUARD_ALLOW_INSECURE_PLAINTEXT | Set to 1 to allow the file backend (required for CI/testing) |
Examples
Protect GitHub Copilot MCP
# Store license
thornguard auth login
# Create profile with a pretty name
GITHUB_TOKEN=ghp_your_token_here \
thornguard profile add github-copilot \
--display-name "GitHub Copilot (Protected)" \
--target-url https://api.githubcopilot.com/mcp \
--upstream-auth-mode bearer \
--upstream-token-env GITHUB_TOKEN \
--vendor-name GitHub
# Apply to Claude Desktop
thornguard print-config --client claude github-copilot --apply
# Verify
thornguard doctor github-copilot
Protect a Custom MCP Server (No Upstream Auth)
thornguard profile add my-internal-tools \
--display-name "Internal Tools" \
--target-url https://tools.mycompany.com/mcp
thornguard print-config --client cursor my-internal-tools --apply
Discover and Protect Existing Servers
If you already have MCP servers configured, the fastest path is scan then protect:
# See what's configured across all your clients
thornguard scan
# Interactively protect an unprotected server
thornguard protect
The protect wizard handles profile creation, config rewriting, and name preservation automatically.