---
title: 'CLI'
description: 'Manage a running mcphub instance and call MCP tools from the terminal'
---

## Overview

The `mcphub` binary doubles as a CLI for the running hub. Invoke `mcphub` with no arguments to start the server (the original behavior); pass a subcommand to drive a remote (or local) hub over its HTTP API.

```bash
mcphub --help                    # list every subcommand
mcphub <command> [options]       # run a CLI command
mcphub                            # legacy: start the server (unchanged)
```

The CLI ships with the same npm package as the server — there's nothing extra to install.

## Logging in

```bash
mcphub login --url http://localhost:3000 --username admin --password ********
```

A JWT is cached in an XDG-compliant credentials file with `0600` permissions:

- macOS/Linux: `$XDG_DATA_HOME/mcphub/credentials.json` (default `~/.local/share/mcphub/credentials.json`)
- Legacy: `~/.mcphub/credentials.json` is still honored when it already exists

Multiple **profiles** are supported, so a single shell can target `staging`, `prod`, etc. side-by-side:

```bash
mcphub login --profile staging --url https://staging.hub.example
mcphub config use prod
mcphub config list
```

You can also bypass the credentials file entirely:

| Override | Where it comes from |
| -------- | ------------------- |
| `--url`, `--token`, `--bearer` flags | per-command |
| `MCPHUB_URL`, `MCPHUB_TOKEN`, `MCPHUB_TOKEN_KIND` env | shell session / CI |
| Active profile in `credentials.json` | default |

Resolution order: flag → environment variable → active profile.

## Global options

| Option | Purpose |
| ------ | ------- |
| `--url <url>` | Override the active profile's base URL |
| `--token <token>` | Override the active profile's token |
| `--bearer` | Treat `--token` as a bearer key (default is JWT via `x-auth-token`) |
| `--profile <name>` | Pick a saved profile other than `current` |
| `--json` | Print JSON instead of human-friendly tables |
| `--debug` | Print stack traces on error |

## Commands

### `servers`

```bash
mcphub servers list
mcphub servers get <name>

# Add from a JSON file:
mcphub servers add fetch --from-file ./fetch.json

# Or inline (--arg / --env can repeat):
mcphub servers add fetch \
  --type stdio --command uvx \
  --arg mcp-server-fetch \
  --env USER_AGENT=mcphub

mcphub servers remove fetch
mcphub servers toggle fetch --on
mcphub servers reload fetch
```

### `groups`

```bash
mcphub groups list
mcphub groups add dev --description "developer tools"
mcphub groups add-server dev fetch
mcphub groups remove-server dev fetch
mcphub groups remove dev
```

`<group>` accepts either the UUID or the human-readable name; the CLI resolves the name to an id by listing groups first.

### `keys`

Manage bearer keys. Admins manage system-level and user-level keys; ordinary users manage their own
user-level keys.

```bash
mcphub keys list
mcphub keys create --name ci --access-type all
mcphub keys create --name dev-only --access-type groups --groups dev,staging
mcphub keys delete <id>
```

The hub-generated token is printed once on `create` — copy it before navigating away.

### `tools` — discover what to call

`tools` is the agent-friendly index for `call`: it answers **what tools exist, what params each one takes, which server hosts it** without having to hand-parse `servers list` JSON.

```bash
# Flat list of every tool across every connected server.
mcphub tools list
mcphub tools list --json                       # machine-readable, no schemas
mcphub tools list --schema --json              # include each tool's inputSchema
mcphub tools list --server fetch               # scope to one server
mcphub tools list --enabled-only

# Inspect one tool: description, parameter table, full input schema, sample command.
mcphub tools get fetch_url
mcphub tools get fetch_url --server fetch      # disambiguate when two servers expose the same name
mcphub tools schema fetch_url                  # alias for `get`
```

The `tools get` text view prints a parameter table marking which keys are `required`, followed by the full JSON schema and a ready-to-paste `mcphub call ...` example with the required params filled in.

### `call`

Call an MCP tool against the active profile's hub. By default `/mcp/$smart` is used so smart routing picks the right tool. The recommended agent workflow is **`tools list` → `tools get` → `call`**.

```bash
# 1. Find a tool
mcphub tools list --json | jq '.[] | select(.name | contains("fetch"))'

# 2. Check its schema
mcphub tools get fetch_url --json | jq '.inputSchema'

# 3. Call it
mcphub call fetch_url url=https://example.com timeout=30
mcphub call fetch_url url=https://example.com --server fetch        # pin to a specific server
mcphub call my_tool --group dev nested='{"a":1}'
mcphub call my_tool --params-json '{"deep":{"v":1}}'
```

Routing precedence inside `call`: `--smart` > `--server <name>` > `--group <name>` > default (`$smart`). All three resolve to the same `/mcp/<slug>` endpoint; `--server` is the natural pair for `tools list`/`tools get` output.

`key=value` argument coercion:

| Form | Result |
| ---- | ------ |
| `n=42`, `f=1.5` | number |
| `b=true`, `b=false` | boolean |
| `x=null` | `null` |
| `obj={"a":1}`, `arr=[1,2]` | parsed JSON |
| `payload=@./body.json` | JSON loaded from file |
| `phone=0123` | preserved as the string `"0123"` (leading-zero protection) |
| anything else | string |

Pass `--no-coerce` to treat every value as a string.

### `export`

Download `mcp_settings.json` from the running hub.

```bash
mcphub export --out backup.json
mcphub export > backup.json
```

### `discover` / `install` (marketplace)

When the hub has `systemConfig.discovery.enabled = true`, the CLI can browse the public marketplace and install servers into your own hub or directly into a client-side config file.

```bash
# Browse the active profile's hub
mcphub discover --search github --limit 10
mcphub discover info amap-maps
mcphub discover categories
mcphub discover tags

# Install into the active hub (POST /api/servers)
mcphub install amap-maps --env AMAP_MAPS_API_KEY=...

# Install from a different hub into the active one
mcphub install some-server --remote https://hub.example.com --yes

# Dry-run: just print the mcpServers snippet
mcphub install amap-maps --dry-run

# Install into a Claude Desktop / OpenClaw-style config file
mcphub install amap-maps --to file --out ~/Library/Application\ Support/Claude/claude_desktop_config.json
```

Notes:

- `--type` picks the installation method (`npm`, `docker`, `uvx`, `pip`, `binary`). If omitted, the hub picks the first available; if the requested type doesn't exist, the CLI lists the alternatives.
- `--env KEY=VAL` (repeatable) injects environment values — both predeclared and additional keys are merged into the resulting `env` block.
- `--to file` writes atomically (temp file + rename), so a crashed write can't corrupt your client config.

## Exit codes

| Code | Meaning |
| ---- | ------- |
| `0` | Success |
| `1` | Usage error (bad flag, missing argument, unauthorized state) |
| `2` | API error (non-2xx response from the hub) |

## Autonomous-agent recipe

Every command supports `--json` and reads `MCPHUB_URL` / `MCPHUB_TOKEN`, so an agent can drive the entire discover → call loop from JSON:

```bash
export MCPHUB_URL=http://localhost:3000
export MCPHUB_TOKEN=$JWT

# 1. enumerate tools
mcphub tools list --json > tools.json

# 2. pick one and read its schema
mcphub tools get fetch_url --json > schema.json

# 3. build a payload from the schema and invoke
mcphub call fetch_url --params-json "$(jq -n '{url:"https://example.com"}')" --json
```

The `tools list --json` response is intentionally flat (`{server, serverStatus, name, description, enabled}`) so an agent can filter in one pass without traversing nested `servers[*].tools[*]`.

## Scripting and CI

The CLI is JSON-friendly: `--json` is honored by every command, and `MCPHUB_URL` / `MCPHUB_TOKEN` make it easy to skip the credentials file in CI.

```bash
export MCPHUB_URL=https://hub.example.com
export MCPHUB_TOKEN=$CI_HUB_TOKEN

mcphub servers list --json | jq '.[] | select(.status != "connected") | .name'
```

For bearer-key auth in CI, add `MCPHUB_TOKEN_KIND=bearer` (or `--bearer`). Note that only system-level bearer keys with `accessType=all` authorize the `/api/*` management surface. Scoped system keys and user-level keys are for MCP transports only.
