Skip to main content
Glama

mcp-hub

One MCP connection. Every server. Loaded only when needed.

mcp-hub is a meta-MCP server that sits between your MCP host (Claude Code, Claude Desktop, Cursor, …) and all of your individual MCP servers. Instead of wiring a dozen servers directly into your client — each one spawning a process at launch and flooding the model's context with tool definitions — you connect to a single hub that exposes a handful of discovery tools and spawns child servers lazily, on first use.

Build & Test License: MIT Python Ruff semantic-release MCP


Table of contents

Related MCP server: programmatic-mcp

Quick start

# 1. Install (from Git — see Installation for options)
uv tool install git+https://github.com/igrybkov/mcp-hub.git

# 2. Describe your servers
mkdir -p ~/.config/mcp-hub
cat > ~/.config/mcp-hub/servers.yml <<'YAML'
everything:
  command: npx
  args: ["-y", "@modelcontextprotocol/server-everything"]
  description: "Reference MCP server for testing"
  tags: [example]
YAML

# 3. Verify from the shell
mcp-hub list
mcp-hub tools everything --summary

# 4. Register the hub with your client (writes .mcp.json by default)
mcp-hub install

That's it — your host now talks to one server (mcp-hub), and everything only starts when the model actually calls one of its tools.

Why mcp-hub?

Connecting many MCP servers directly to a host has two costs that grow with every server you add:

  • Context bloat. Every server's full tool schema is injected into the model's context up front. Twenty servers can burn tens of thousands of tokens before the user types a word.

  • Process bloat. Every server is spawned at startup, even the ones you won't touch this session — slow launches, idle Docker containers, wasted memory.

mcp-hub collapses all of that behind one connection:

Direct wiring

With mcp-hub

Connections in the host

one per server

one, total

Tools in context at startup

all tools, all servers

~8 meta-tools

Child process spawn

eager, at launch

lazy, on first use

Add/remove a server

edit + restart the host

edit config + reload

Secrets

per-client env plumbing

central OS keychain

The model discovers what it needs through cheap, progressive tool calls — and the hub only spawns the child servers a task actually touches.

Architecture

flowchart LR
    Host["MCP Host<br/>Claude Code · Desktop · Cursor"]

    subgraph HUB["mcp-hub (single connection)"]
        direction TB
        Meta["Discovery meta-tools<br/>list · search · get · call · recommend"]
        Cat["Catalog cache<br/>prompts + resources"]
        Auth["Keychain auth"]
    end

    Host <==>|"stdio · ~8 tools"| HUB

    HUB -.->|spawn on first use| G["github"]
    HUB -.->|spawn on first use| J["jira"]
    HUB -.->|lazy| S["slack"]
    HUB -.->|lazy| N["… N servers"]

Child servers stay dormant until a tool call (or an opt-in prompt/resource enumeration) reaches them. The hub also relays the full duplex of MCP capabilities — sampling, elicitation, roots, logging, and completions — between the host and each child, so wrapping a server in the hub doesn't take features away.

Features

  • Lazy proxying — child servers spawn on first use, each in its own supervised connection task.

  • Progressive discoverylist_servers, get_server_tools (summary or full schema), search, and call_tool let the model drill down without paying for every schema up front.

  • LLM-backed routingrecommend_servers asks the host's own model (via MCP sampling) which servers fit a task, with a graceful fallback when sampling is unavailable.

  • Keychain-native auth — secrets live in your OS keychain (via keyring), are injected into child environments on spawn, and are collected through MCP elicitation so they never enter the model's context.

  • Opt-in prompts & resources — surface a child's prompts/resources through the hub with a flat, namespaced view, backed by an on-disk catalog for instant warm starts and a self-healing recovery daemon for cold ones.

  • Full capability relay — bidirectional sampling, elicitation, roots, logging, and completions pass through transparently.

  • Three transportsstdio, streamable-http, and sse children.

  • Hot reload — add, remove, or edit servers and pick up the change with a single reload, no host restart.

  • First-class CLI — script everything (list, tools, call, search, auth, add, validate, install) with JSON output.

  • Bundled agent skill — ships a "managing MCP servers" skill so an assistant can add, configure, and troubleshoot servers from a vendor's docs; install it with mcp-hub skill install.

Installation

Note: mcp-hub is not yet published to PyPI. Install from Git for now.

Requires Python 3.11+.

# Recommended: install as an isolated tool with uv
uv tool install git+https://github.com/igrybkov/mcp-hub.git

# Run ephemerally without installing (great for trying it out)
uvx --from git+https://github.com/igrybkov/mcp-hub.git mcp-hub list

# Or with pip / pipx
pip install git+https://github.com/igrybkov/mcp-hub.git
pipx install git+https://github.com/igrybkov/mcp-hub.git

A single mcp-hub entry point is installed, with two roles:

  • mcp-hub <command> — the CLI (list, tools, call, auth, install, …).

  • mcp-hub server — the MCP server (stdio) your host launches.

From source

git clone https://github.com/igrybkov/mcp-hub.git
cd mcp-hub
uv sync --dev
uv run mcp-hub list

Use mcp-hub in your client

With the install command

install writes (or updates) an mcpServers entry and auto-detects the runner from how you launched it. If you ran via uvx --from <spec>, it reuses the same --from spec so the generated entry matches exactly; otherwise it writes a plain mcp-hub server.

# Claude Code — project-level (.mcp.json in CWD, checked into the repo)
mcp-hub install

# User-level / other clients — point at any config file
mcp-hub install --config ~/.mcp.json
mcp-hub install --config ~/Library/Application\ Support/Claude/claude_desktop_config.json
mcp-hub install --config ~/.cursor/mcp.json

# Preview without writing
mcp-hub install --config .mcp.json --dry-run

# Force a specific runner ("mcp-hub server" is appended automatically)
mcp-hub install --runner 'uvx --from git+https://github.com/igrybkov/mcp-hub.git'

Manual configuration

If you installed mcp-hub as a tool, the entry is simply:

{
  "mcpServers": {
    "mcp-hub": {
      "command": "mcp-hub",
      "args": ["server"]
    }
  }
}

To run straight from Git without a prior install:

{
  "mcpServers": {
    "mcp-hub": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/igrybkov/mcp-hub.git", "mcp-hub", "server"]
    }
  }
}

Agent skill

mcp-hub ships a bundled agent skill that teaches an assistant the full lifecycle of managing servers — discover/search, add & configure from a vendor's docs (secrets to the keychain, never inline), authenticate, reload, verify, and troubleshoot. With it installed, you can just say "add the MongoDB MCP to mcp-hub: " or "find me an MCP server for Postgres" and the agent knows the rest.

# Install for Claude (default) — writes ./.claude/skills/mcp-hub/
mcp-hub skill install

# Install for Cursor — writes ./.cursor/skills/mcp-hub/
mcp-hub skill install --client cursor

# Or an explicit directory
mcp-hub skill install --dir ~/.config/skills

# Print the guide to stdout (no install) — pull it on demand or pipe to a file
mcp-hub skill show
mcp-hub skill list

The skill travels inside the package, so every install has it. It's also surfaced to the model automatically: the hub's instructions always point at mcp-hub skill show.

Configuration

Servers are described in JSON or YAML. By default the hub merges these sources, in order, with later sources overriding earlier ones by server name:

  1. ~/.config/mcp-hub/servers.json

  2. ~/.config/mcp-hub/servers.yml

  3. ./.mcp.local.json (project-level, resolved from the working directory)

  4. ./.mcp.local.yml

Point the hub at different files with the CONFIG_FILE environment variable (comma-separated paths):

export CONFIG_FILE="~/.config/mcp-hub/servers.yml,./team-servers.yml"

Both the wrapped ({"mcpServers": {…}}) and unwrapped (top-level mapping) shapes are accepted, so you can reuse an existing .mcp.json-style file as-is.

Examples

# stdio child
github:
  command: gh-mcp
  args: ["--stdio"]
  env:
    GH_HOST: github.com
  description: "GitHub issues, PRs, and repos"
  tags: [dev, vcs]

# streamable-http child (default transport when `url` is set)
everything:
  url: https://everything.mcp.run/mcp
  headers:
    Authorization: "Bearer ${TOKEN}"

# sse child
metrics:
  url: https://metrics.example.com/sse
  transport: sse

# opt in to prompts/resources and give a slow (Docker) server more time
obsidian:
  command: docker
  args: ["run", "-i", "--rm", "obsidian-mcp"]
  expose_prompts: true
  expose_resources: true
  connect_timeout_seconds: 20

# temporarily turn a server off without deleting it
legacy:
  command: old-mcp
  disabled: true

Field reference

Field

Type

Applies to

Default

Description

command

string

stdio

Executable to launch.

args

string[]

stdio

[]

Arguments passed to command.

env

map

stdio

{}

Extra environment for the child (merged over the hub's own env).

url

string

http/sse

Endpoint URL. Presence selects an HTTP transport.

transport

string

http/sse

streamable-http

streamable-http or sse (only when url is set).

headers

map

http/sse

{}

Headers sent with each request.

description

string

all

Shown in discovery and used for search/recommendations.

tags

string[]

all

[]

Free-form labels, matched by list/search.

disabled

bool

all

false

Skip this server entirely.

expose_prompts

bool

all

false

Surface the child's prompts through the hub.

expose_resources

bool

all

false

Surface the child's resources/templates through the hub.

connect_timeout_seconds

number

exposed

5.0

Per-server connect + enumerate budget. Raise for slow/Docker cold starts.

auth.secrets

list

all

Secret schema for keychain injection (see Authentication).

Managing servers (agent workflow)

You rarely need to hand-edit config. mcp-hub add translates a vendor's docs snippet into the hub's shape and moves likely-secret env vars into the keychain schema automatically (the rule below), validate lints before you reload, and config path shows where things get written. The bundled agent skill drives the whole flow.

mcp-hub config path        # resolved sources + the file `add` writes to

# Translate a docs snippet (wrapped or single-entry). Secret env vars matching
# *TOKEN/KEY/SECRET/PASSWORD/CONNECTION_STRING* (with a real value) are moved to
# auth.secrets and their raw values dropped; --keep-env-secrets opts out.
mcp-hub add mongodb \
  --from-json '{"mcpServers":{"MongoDB":{"command":"npx","args":["-y","mongodb-mcp-server@latest"],"env":{"MDB_MCP_CONNECTION_STRING":"mongodb+srv://user:pass@host/db"}}}}' \
  --arg --readOnly --description "MongoDB / Atlas" --tag database

# Or build from flags (no snippet):
mcp-hub add linear --command npx --arg -y --arg linear-mcp-server \
  --secret 'LINEAR_API_KEY:Linear API key:https://linear.app/settings/api'

mcp-hub auth provision mongodb   # store the secret(s) in the keychain
mcp-hub validate                 # lint; flags any raw secrets left in env/headers
# …then call the `reload` tool (or restart the host) and verify with `list`/`tools`/`call`.

Secret-vs-env rule — API keys, tokens, passwords, client secrets, and connection strings with embedded credentials belong in auth.secrets (keychain). Base URLs, hostnames, account/team IDs, emails, regions, and flags stay in plaintext env/headers. Run mcp-hub skill show for the full guide.

Meta-tools

The hub exposes a small, fixed set of tools to the host. The model uses them to discover and reach everything else.

Tool

What it does

list_servers

List configured servers with descriptions, tags, transport, and auth status. Optional substring filter.

get_server_tools

List a server's tools. summary_only: true for cheap discovery (~100 tokens); tools: [names] for full schemas of specific tools. Connects lazily.

call_tool

Invoke tool on server with arguments. Spawns the child on first call.

search

Keyword-rank across server metadata and already-loaded tool descriptions.

recommend_servers

Ask the host LLM to rank servers for a task_description (via sampling). Falls back to a catalog dump if sampling is unsupported.

reload

Re-read config and reconcile the server set, or reload a single server. Drops cached schemas and refreshes exposed catalogs.

authenticate

Collect and store a server's secrets in the OS keychain via elicitation, then refresh the session.

auth_status

Report auth state (authenticated / partial / unauthenticated) for one or all servers.

The discovery funnel

The intended flow keeps token usage low by only loading detail when it's needed:

list_servers(filter?)              → which servers exist
        │
get_server_tools(server,           → which tools exist (names + descriptions only)
                 summary_only=true)
        │
get_server_tools(server,           → full input schema for the 1–2 tools you'll call
                 tools=[names])
        │
call_tool(server, tool, arguments) → run it (spawns the child if needed)

When you're unsure which server fits, recommend_servers("deploy the staging branch") returns a ranked shortlist with one-line rationales.

Authentication

Secrets are never stored in config files or passed through the model. Instead the hub uses a schema-as-source-of-truth model:

  1. A server declares which environment variables it needs in an auth.secrets block.

  2. Values are stored in your OS keychain (keyring; macOS Keychain, Windows Credential Locker, Secret Service, …) under the service name mcp-hub.

  3. On spawn, the hub injects only the declared secrets into the child's environment.

linear:
  command: linear-mcp
  auth:
    secrets:
      - env_var: LINEAR_API_KEY
        label: "Linear API key"
        create_url: "https://linear.app/settings/api"
        sensitive: true        # default; masks terminal input

Each secret supports env_var, label, create_url, sensitive (default true), and state (present or absent; absent reconciles the value out of the keychain).

Storing secrets

From the assistant (in-session, no terminal): call authenticate with the server name. The hub asks the host to prompt you via MCP elicitation, stores the answer in the keychain, and refreshes the session — the value never touches the model's context.

From the shell:

mcp-hub auth status                 # what's stored, what's missing
mcp-hub auth provision linear       # prompt for and store linear's secrets
mcp-hub auth provision linear --force  # overwrite stored secrets (rotated/expired keys)
mcp-hub auth provision --all        # provision every server with a schema
mcp-hub auth rm linear              # delete linear's stored secrets
mcp-hub auth rm linear LINEAR_API_KEY

By default provision skips secrets that are already in the keychain. Pass --force to re-prompt and overwrite them — use this to rotate an expired or revoked key. In-session, the authenticate tool takes an equivalent force: true.

Learned schemas

If you provision secrets for a server that has no declared schema, the hub records a learned schema at ~/.local/state/mcp-hub/learned-auth.json (honoring XDG_STATE_HOME). Promote it into your config to make it canonical:

mcp-hub auth promote linear   # prints the YAML auth block to paste into your config

Prompts & resources

By default, child prompts and resources stay hidden behind the meta-tools — the host's UI stays clean. Set expose_prompts: true and/or expose_resources: true on a server to surface them natively, where they appear in one flat, namespaced list:

  • Prompts: obsidian__daily-note (<server>__<prompt>)

  • Resources: mcphub://obsidian/<percent-encoded-original-uri>

The hub decodes these on get_prompt / read_resource and routes back to the right child. Resource templates and argument completions are proxied too.

To make this fast and resilient, exposed metadata is cached on disk at ~/.cache/mcp-hub/catalog.json:

  • Warm start (cache valid for the current config): prompts/resources are served instantly.

  • Cold start (no cache or config changed): the hub serves whatever has enumerated so far, then a background recovery daemon keeps trying — with exponential backoff (5s → 5min, jittered) for slow or flaky children — and emits list_changed as servers come online. Degraded servers keep serving their last-known-good entries.

The cache key is a hash of your config files, so editing config (or running reload) automatically invalidates stale entries.

Advanced MCP support

Wrapping a server in the hub keeps its full feature set. The hub relays every MCP capability in both directions:

Capability

Direction

Behaviour

Tools

host → child

Proxied on demand via call_tool; child spawned lazily.

Sampling

child → host

Forwarded to the host LLM (createMessage). Also powers recommend_servers.

Elicitation

child → host

Forwarded to the host (elicit). Also powers authenticate.

Roots

child → host & host → children

list_roots proxied to the host; roots/list_changed fanned out to connected children.

Logging

child → host & host → children

Child log messages forwarded (prefixed with the server name); setLevel fanned out to connected children.

Prompts

child → host

Opt-in (expose_prompts); namespaced and cached.

Resources

child → host

Opt-in (expose_resources); namespaced and cached, templates included.

Completions

host → child

Proxied for exposed prompts and resource templates.

Errors from children are mapped to clean JSON-RPC errors rather than crashing the hub, and capabilities a child doesn't implement degrade gracefully (e.g. "method not found" becomes "no suggestions").

CLI reference

Run any command with -h/--help for details. Add -v/--verbose for debug logging to stderr — this also raises mcp-hub server from INFO to DEBUG (e.g. mcp-hub -v server, or "args": ["-v", "server"] in a client config). Most commands print JSON to stdout, so they compose well with jq.

# Discover
mcp-hub list                          # all configured servers
mcp-hub list --filter monitoring      # substring filter on name/description/tags
mcp-hub list --names-only             # one name per line (scripting)

mcp-hub tools <server>                # full tool schemas for a server
mcp-hub tools <server> --summary      # names + descriptions only
mcp-hub tools <server> --tool <name>  # full schema for specific tool(s)

mcp-hub search "deploy"               # search metadata + loaded tools
mcp-hub search "deploy" --load        # load every server's tools first (slow)

# Invoke
mcp-hub call <server> <tool> --args '{"key": "value"}'
mcp-hub call <server> <tool> --args-file ./args.json

# Auth
mcp-hub auth status [--server <name>]
mcp-hub auth provision <server> | --all [--force]
mcp-hub auth rm <server> [<ENV_VAR>]
mcp-hub auth promote <server>

# Manage config (see "Managing servers" above)
mcp-hub config path                   # resolved sources + write target
mcp-hub add <name> [--from-json '<snippet>'] [flags] [--dry-run]
mcp-hub validate [--config PATH]      # lint specs; non-zero exit on error

# Bundled agent skill
mcp-hub skill show [name]             # print SKILL.md to stdout (default: mcp-hub)
mcp-hub skill list                    # list bundled skills
mcp-hub skill install [name] [--client claude|cursor] [--dir PATH] [--force]

# Install into a client config
mcp-hub install [--config PATH] [--name KEY] [--runner CMD] [--dry-run]

How it works

A few design choices worth knowing:

  • One task per connection. Each child connection is owned by a dedicated supervisor task that opens the transport, initializes the ClientSession, parks until shutdown, then tears everything down in the same task. This respects an anyio constraint (cancel scopes must be exited in the task that entered them) and prevents orphaned child processes.

  • Schema-driven secret injection. Only environment variables named in a server's (declared or learned) auth schema are pulled from the keychain and injected — nothing implicit leaks into a child.

  • Atomic, self-describing catalog. Exposed prompts/resources are serialized (with metadata) and written atomically (tempfile + os.replace), so the hub never needs to re-call a child to reconstruct a listing, and a crash can't corrupt the cache.

  • Buffered notifications. The host's ServerSession only exists once a request arrives, so background notifications produced during startup (e.g. a late server finishing enumeration) are buffered in a bounded queue and flushed the moment the host connects.

File locations

Path

Purpose

Override

~/.config/mcp-hub/servers.{json,yml}

Default global config

CONFIG_FILE

./.mcp.local.{json,yml}

Project-level config (CWD)

CONFIG_FILE

~/.cache/mcp-hub/catalog.json

Cached exposed prompts/resources

~/.local/state/mcp-hub/learned-auth.json

Learned auth schemas

XDG_STATE_HOME

~/Library/Logs/mcp-hub.log

Server log (created on start)

MCP_HUB_LOG_FILE

OS keychain, service mcp-hub

Stored secrets

keyring backend

Development

uv sync --dev          # install dev dependencies
uv run pytest          # run the test suite
uv run ruff check .    # lint
uv run ruff format .   # format

# optional: install git hooks (ruff lint + format on commit)
uv run pre-commit install

The Build & Test workflow (GitHub Actions) runs ruff check, ruff format --check, and pytest on every push and pull request to main.

Contributing

Contributions are welcome! Please:

  1. Open an issue to discuss substantial changes first.

  2. Keep PRs focused, and add or update tests where it makes sense.

  3. Use Conventional Commit messages (they drive the automated release).

  4. Make sure uv run ruff check ., uv run ruff format --check ., and uv run pytest pass before opening a PR.

Releasing

Releases are fully automated with python-semantic-release driven by Conventional Commits. When Build & Test passes on main, the Publish workflow computes the next version from commit messages, updates the changelog and version, tags the release, publishes a GitHub release, and attaches the built wheel and sdist to it as release assets.

Commit message prefixes that affect versioning:

  • fix: → patch release

  • feat: → minor release

  • feat!: / BREAKING CHANGE: → major release

  • chore:, docs:, refactor:, test:, … → no release

Troubleshooting

  • A child won't start / connection error. Run the child's command by hand to confirm it works, then mcp-hub tools <server> — the child's own stderr is surfaced. Detailed logs are at ~/Library/Logs/mcp-hub.log (or $MCP_HUB_LOG_FILE).

  • Edited config isn't picked up. Call the reload tool (or restart the host). Adding the first exposed server requires a host reconnect to register the prompts/resources capability.

  • A server needs more startup time. Raise its connect_timeout_seconds (slow Docker images especially).

  • Auth says "partial". One or more declared secrets aren't stored yet — run mcp-hub auth provision <server> or the authenticate tool.

  • A stored key expired or was rotated. provision skips secrets that already exist; re-store with mcp-hub auth provision <server> --force (or authenticate with force: true).

License

MIT © Illia Grybkov

Available Tools

8 tools
authenticateA

Authenticate a server by collecting and storing its required secrets in macOS Keychain. If the server has no auth schema, asks Claude to infer it. Uses MCP elicitation so secrets never enter the assistant's context. After storing, the server session is refreshed automatically. Set force=true to re-collect and overwrite secrets that are already stored (e.g. rotated or expired keys).

ParametersJSON Schema
NameRequiredDescriptionDefault
forceNoRe-collect and overwrite already-stored secrets (e.g. expired keys).
serverYesServer name to authenticate

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations to rely on, the description carries the full burden and does so admirably. It discloses that secrets are stored in macOS Keychain, never enter the assistant context via MCP elicitation, sessions auto-refresh, and force re-collects/overwrites existing secrets. These are meaningful behavioral traits beyond the bare action of 'authenticate'.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four compact sentences, each adding distinct value: core purpose, inference behavior, secret-handling guarantee, and force semantics. It is front-loaded and free of fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter tool with no output schema and no annotations, the description covers all essential call-time knowledge: what secrets are, where they are stored, how inference works, context safety, auto-refresh, and when to use force. An agent has enough to select and invoke the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the schema already documents both parameters. The description adds a concrete example for force ('rotated or expired keys') but mostly repeats the schema's own description, so it does not significantly extend parameter understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific action ('Authenticate a server') and adds concrete resource details: collecting and storing required secrets in macOS Keychain, with a distinct mechanism (MCP elicitation) and a force flag for re-collection. This clearly differentiates it from siblings like auth_status, search, and call_tool, which serve different purposes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear operational context: when to use force=true, how it handles missing auth schemas, and the fact that sessions refresh automatically. It does not explicitly name alternatives or state when not to use this tool, but the intended context is evident and actionable.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

auth_statusA

Show authentication status for one or all servers with auth schemas. Returns per-server status: authenticated, partial, or unauthenticated.

ParametersJSON Schema
NameRequiredDescriptionDefault
serverNoIf set, return status for this server only. Otherwise all servers.

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description must carry the behavioral burden. It states output values but does not explicitly say the tool has no side effects or that it does not trigger authentication. The word 'Show' implies read-only, and the three statuses are useful, but 'partial' is undefined and no side-effect guarantee is stated.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, front-loaded sentence without filler. It states the verb, resource, scope, and output format in 18 words, making it easy for an agent to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a low-complexity tool with one optional parameter, the description covers purpose, scope, and return values. Minor gaps remain—such as what 'partial' means and whether the tool modifies state—but these are not critical for an agent to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% because the only parameter, 'server', has a description ('If set, return status for this server only. Otherwise all servers.'). The tool description merely mirrors that behavior without adding new meaning, so a baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a clear verb ('Show') and a specific resource ('authentication status') with an explicit scope ('one or all servers'). It even enumerates the return states (authenticated, partial, unauthenticated), which fully differentiates it from siblings like authenticate (which modifies auth state) and list_servers (which lists servers).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly implies when to use the tool: whenever an agent needs to inspect which servers are already authenticated versus those that are not. It does not explicitly contrast with authenticate, but the verb 'Show' and the return-state list make the use case obvious.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

call_toolA

Call a tool on a specific server. The server is spawned on first call. The tool must exist on the server — use get_server_tools to discover first.

ParametersJSON Schema
NameRequiredDescriptionDefault
toolYesTool name
serverYesServer name
argumentsNoTool arguments as a JSON object.

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations are minimal (just readOnly and destructive hints), but the description adds behavioral context: the server is spawned on first call, and the tool must exist (implying failure if not). It doesn't disclose other behaviors like authentication requirements or side effects, but the annotations likely provide basic safety info. The description adds value but not exhaustive detail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, concise and front-loaded with the primary action. The second sentence provides important usage guidance without wasting words. It's efficient but could be slightly more structured with explicit 'when to use' formatting.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given that it's a dynamic call tool with a nested object parameter, the description is decently complete. It mentions the need to discover tools, but does not describe return values or error handling. However, the lack of an output schema means it could do more, but the combination of schema and description is sufficient for an agent to proceed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so both parameters are already documented in the schema. The description adds little beyond that, such as the implication of the 'server' parameter being a server reference, but it doesn't add significant meaning beyond the schema. Hence, baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool calls a tool on a specific server, which is more specific than a generic 'call tool'. It also mentions the need for the tool to exist on the server, but doesn't explicitly differentiate from siblings like get_server_tools. Overall, it provides a clear verb and resource.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives explicit usage guidance: use get_server_tools to discover tools before calling. It also implies that the server is spawned on first call, which is an important context. This is strong guidance for when to use this tool versus alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_server_toolsA

Get tools from a specific server. Lazily connects if not already connected. Use summary_only=true for cheap discovery (~100 tokens), then fetch full schemas for specific tools you plan to call.

ParametersJSON Schema
NameRequiredDescriptionDefault
toolsNoIf set, return full schemas only for the named tools.
serverYesServer name
summary_onlyNoIf true, return only tool names and descriptions (no input schemas).

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral disclosure burden. It reveals an important behavior: lazy connection to the server if not already connected. It also adds a token-cost hint (~100 tokens), which goes beyond the schema. It does not detail auth requirements or error behavior, but the read-only nature of 'Get' is apparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler, front-loaded with the core purpose and key behavioral note. The second sentence earns its place by giving strategic invocation guidance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the simple 3-parameter schema and no output schema, the description is largely complete: it covers purpose, lazy-connect behavior, and the optimal calling strategy. It could mention that server names likely come from list_servers or clarify auth prerequisites, but these are minor gaps for such a focused discovery tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaning beyond the schema by explaining summary_only's purpose ('cheap discovery') and the workflow of fetching full schemas for specific tools. This adds real semantic value over the structured parameter descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Get tools from a specific server.' It is clearly distinct from siblings like list_servers, call_tool, and search, so an agent can identify this as the tool-discovery operation without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives actionable usage guidance: use summary_only=true for cheap discovery, then fetch full schemas for specific tools. It does not explicitly contrast this tool with siblings, but the intended workflow is clear and appropriately scoped.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_serversA

List configured MCP servers with their descriptions and tags. Optionally filter by substring match on name/description/tags.

ParametersJSON Schema
NameRequiredDescriptionDefault
filterNoOptional substring to filter on name, description, or tags.

TDQS

A3.5/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It only states the function and does not mention whether the operation is read-only, requires authentication, returns paginated data, or has any side effects. For a tool that lists configuration, the agent is left without explicit safety or state-change context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no filler. The primary action is stated first, and the optional filter is added in the second sentence. Every word earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple list tool with one optional parameter, the description covers the core function but lacks context about return value shape (no output schema exists), whether authentication is needed, or whether this is a safe read-only operation. Given the absence of annotations, a complete description should at least hint at these aspects.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (filter is fully described as 'Optional substring to filter on name, description, or tags.'). The description essentially restates the parameter without adding extra semantics like default behavior, match case-sensitivity, or wildcard support. Baseline 3 is appropriate since the schema already does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses a specific verb ('List') and resource ('configured MCP servers'), and clarifies the fields returned (descriptions and tags). It clearly distinguishes this tool from siblings like 'search' or 'recommend_servers' by framing it as an enumeration of configured servers, not a search or recommendation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for listing servers and optionally filtering, but it does not explicitly state when to choose this tool over siblings (e.g., 'use search for semantic queries' or 'use get_server_tools to inspect a specific server'). There is no exclusion or conditional guidance, leaving some inference to the agent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

recommend_serversA

Given a natural-language task description, asks the host's LLM (via MCP sampling) to rank configured servers by relevance. Returns up to max_results recommendations with scores and rationale. Falls back to a raw catalog dump if the host doesn't support sampling. Use this when the user's request spans domains and you're not sure which server(s) to reach for.

ParametersJSON Schema
NameRequiredDescriptionDefault
max_resultsNoMax recommendations to return (default 5).
task_descriptionYesPlain-English description of what the user wants to do.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full transparency burden. It discloses the sampling mechanism, the fallback behavior when sampling is unsupported, and the return structure (recommendations with scores and rationale). This provides good insight into how the tool behaves and its limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each earning its place: the first defines the action, the second defines the output and fallback, the third gives usage guidance. No filler or redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a read-only recommendation tool with no output schema, the description covers the input, mechanism, output, fallback, and usage context. It doesn't detail error conditions or the shape of the fallback catalog, but these are minor given the tool's simplicity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema already documents both parameters (task_description and max_results) with clear descriptions, so the description adds no extra parameter context. It restates that the input is a natural-language task description without adding syntax or format details, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('recommend'), a resource (servers), and the input (a natural-language task description). It explains the mechanism (host LLM sampling) and output (ranked recommendations with scores and rationale), which clearly distinguishes it from siblings like search and list_servers that are direct retrieval operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use: when the user's request spans domains and you're unsure which server to use. It does not mention when not to use or name alternatives explicitly, but the use case is clear enough for an agent to decide without confusion.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

reloadA

Reload mcp-hub: re-reads config files and reconciles the server set (added/removed/changed), tears down stale child connections, and drops cached tool schemas. Use after editing the hub's config, or after a child server's tools have changed (code edits, new tool registered). If server is given, only that server is reloaded (faster — no config re-read). For exposed servers, the catalog is re-enumerated and prompts/list_changed + resources/list_changed notifications are emitted so the host re-fetches. Tool schema changes on non-exposed servers are picked up on the next get_server_tools call. Caveat: if the hub started with no exposed servers, the prompts/resources capabilities aren't registered for the session — adding an exposed server via reload won't surface until the host reconnects.

ParametersJSON Schema
NameRequiredDescriptionDefault
serverNoIf set, only reload this server. Otherwise, reconcile all servers against config files.

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses side effects: tears down stale child connections, drops cached tool schemas, re-enumerates catalog, emits notifications, and notes the caveat about capabilities not being registered if the hub started with no exposed servers. This is rich behavioral context beyond a simple 'reload' statement. It doesn't mention auth requirements or rate limits, but for a reload tool those are less critical.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is dense but well-organized: main effect, use cases, parameter behavior, exposed-server behavior, and caveat. It's longer than the typical one-liner but every sentence adds information. The caveat is a bit long but valuable. Slight deduction for the caveat being somewhat verbose and the sentence about non-exposed servers being a bit dense.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with one optional parameter and no output schema, the description covers the main action, side effects, when to use, parameter semantics, and edge cases. The caveat about exposed servers and host reconnection is exactly the kind of context an agent needs to avoid incorrect assumptions. Nothing critical is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and the single parameter `server` is described in the schema. The description adds meaning by explaining the behavioral difference when `server` is set: only that server is reloaded, faster, no config re-read. This goes beyond the schema's 'only reload this server' and gives the agent a decision-relevant semantic.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('reload'), a resource ('mcp-hub'), and enumerates concrete effects: re-reads config files, reconciles the server set, tears down stale child connections, and drops cached tool schemas. It clearly distinguishes itself from siblings like list_servers or get_server_tools by describing the reconciliation and cache-dropping behavior.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use: after editing the hub's config, or after a child server's tools have changed. It also gives the alternative path: if `server` is given, only that server is reloaded (faster, no config re-read). It even notes a caveat about exposed servers and host reconnection, which helps an agent decide when reload is appropriate.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 8 tool updatesv0.1.0
    • First observedauth_status
    • First observedauthenticate
    • First observedcall_tool
    • First observedget_server_tools
    • First observedlist_servers
    • First observedrecommend_servers
    • First observedreload
    • First observedsearch

TDQS

A4.1/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinct action: discovery (search/recommend_servers/list_servers), tool introspection (get_server_tools), execution (call_tool), lifecycle (reload), and auth (authenticate/auth_status). While search, recommend_servers, and list_servers all support discovery, their input modes and outputs are clearly differentiated. No two tools appear to do the same thing.

Naming Consistency4/5

Most tools follow a clear verb_noun pattern: recommend_servers, list_servers, get_server_tools, call_tool. Minor deviations exist: search, reload, and authenticate are bare verbs, and auth_status is a noun phrase rather than get_auth_status, but overall the names are predictable and readable.

Tool Count5/5

Eight tools is a well-scoped size for an MCP hub, covering discovery, introspection, execution, reload, and authentication. Every tool addresses a distinct operational need with no redundant helpers or filler.

Completeness4/5

The tool surface covers the core hub lifecycle: discover servers, inspect tools, call tools, reload configuration, and manage authentication. Minor gaps exist, such as no explicit deauthentication/credential removal or direct server config editing, but these are reasonably handled via the host's config files and OS keychain.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    A flexible proxy server that aggregates multiple backend MCP servers into a single interface using STDIO or SSE transports. It supports dynamic server management via an HTTP API and utilizes namespacing to prevent tool conflicts across connected services.
    3
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A meta MCP server that orchestrates other MCP servers by lazily connecting to them and exposing their tools as JavaScript libraries. It allows users to execute JavaScript code that programmatically interacts with multiple MCP servers within a unified environment.
    12 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    MetaMCP is a MCP proxy that dynamically aggregates MCP servers into a unified endpoint, with middlewares and namespaces.
    2,662
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Unifies multiple MCP servers behind a single endpoint with lazy loading, auto-cleanup, Python plugins, and role-based filtering.
    2
    MIT