Skip to main content

mcp-servers-cli

Inspect, call and drive any MCP server from the command line. Point it at a local process, a remote HTTP endpoint, or an entry in a claude_desktop_config.json-style file, and it lists the tools, resources and prompts the server exposes — then lets you exercise them.

Installation

Run it without installing anything, with uv:

uvx mcp-servers-cli inspect --stdio "uvx mcp-server-fetch"

Or install it once, with the Anthropic backend if you need it:

uv tool install mcp-servers-cli
uv tool install 'mcp-servers-cli[anthropic]'

From a clone, for development:

uv sync --all-groups
uv run mcp-servers-cli --help

Commands

Every command takes exactly one target: --stdio, --http or --config with --server.

# What does this server expose?
mcp-servers-cli inspect --stdio "uv run server.py"
mcp-servers-cli inspect --http https://example.com/mcp
mcp-servers-cli inspect --config config.json --server fetch

# Call one tool
mcp-servers-cli call fetch '{"url": "https://example.com"}' --stdio "uvx mcp-server-fetch"

# Read one resource
mcp-servers-cli read "tasks://stats" --stdio "uv run server.py"

# Inspect, then stay interactive
mcp-servers-cli repl --stdio "uv run server.py"

# Let a model use the tools to answer
mcp-servers-cli agent "What is 2 + 3?" --model qwen3.5:4b --stdio "uv run server.py"

Inside the REPL: call <tool> <json>, read <uri>, list, quit.

Agent

agent hands the server's tools to a model and lets it call them until it answers in text. Each call is printed as it happens, then the answer:

-> add {"a": 2, "b": 3}
<- add ok (0.1s)
The sum is 5.
  • --model is required: small local models can mishandle nested arguments, and a silent default would hide that behind a plausible failure.
  • --backend ollama (default) talks to the server named by OLLAMA_HOST, localhost otherwise.
  • --backend anthropic needs the extra, uvx --from 'mcp-servers-cli[anthropic]' mcp-servers-cli, and reads ANTHROPIC_API_KEY from the environment.
  • A failing tool, an unknown tool name or an invented argument does not stop the run: the model reads the error or gets the cleaned call, and can correct itself.
  • --max-steps (default 10) bounds the model turns; running out is an error, not a silent stop.
  • --dry-run asks the model once and prints the calls it would make, running none: a safe first look at a server whose tools write or delete.
  • --trace run.jsonl writes one JSON object per executed call (time, tool, arguments, duration, error flag, a 500-character excerpt of the result) and a closing end line with the model turns, the number of calls and of failed calls, and the total duration.
  • When a tool call failed, a warning follows the answer on stderr: a model can answer as if its calls had worked (see below).

A trace reads with any JSON tool, for instance the slowest calls first:

jq -r 'select(.event == "tool") | "\(.duration_ms) ms  \(.tool)"' run.jsonl | sort -rn

Arguments and results are written as they are: keep traces out of version control when a server handles secrets.

Model requirements

Measured on 24 September 2026 against mcpserver-template (in-memory backend), three runs per model, with one prompt: add three tasks, complete one, list the pending ones. It takes a string argument, an integer read from an earlier result, and a nested object (filter_tasks).

Model Correct answers Tool calls Failed calls Model turns
qwen3.5:4b-mlx 3 / 3 5 0 4
llama3.2:3b 0 / 3 1 to 3 1 to 2 2

llama3.2:3b failed every run. Its first planned call was filter_tasks with invented fields, before any task existed. In the run examined in detail, that filter was rejected and complete_task targeted a task that did not exist; yet each of the three answers described the work as done. That is the failure to watch for with small models: not a crash, but a fluent and false answer. Read the <- lines, or the warning printed after the answer, before trusting it.

qwen3.5:4b-mlx sent the three add_task calls in one turn. Twice it filtered on the server ({"status": "pending"}); once it fetched every task and filtered the result itself. Both gave the right answer.

Unknown top-level arguments are dropped before a call; an invented field inside a nested object is left to fail. Dropping title from a mistaken filter would widen it to every task and return a wrong answer that looks right.

Client capabilities

MCP lets a server ask its client for three things during a call: a completion from the client's model (sampling), an answer from the user (elicitation), and the directories it may work in (roots). mcp-servers-cli declares none of them. A server that asks gets a one-line refusal, Sampling not supported, Elicitation not supported or List roots not supported: call prints it as an error, agent hands it to the model as a failed call.

FastMCP 4 negotiates the 2026-07-28 protocol revision by default. On such a connection a server no longer sends these requests itself: its tool returns an input-required result (SEP-2322) listing what it needs, and the client answers before the tool runs again. The refusals above are what such a server receives.

Handlers will come with the first server of this portfolio that needs one: sampling bridged to the agent backend, roots from the command line. Elicitation needs someone at the keyboard, which agent does not assume.

Configuring the server you launch

A stdio server runs as a subprocess, and the MCP SDK forwards only a whitelist of environment variables to it — HOME, LOGNAME, PATH, SHELL, TERM, USER. Anything else the server reads from its environment is silently absent, and it falls back to its defaults.

--env is how you pass the rest:

mcp-servers-cli call add_task '{"title": "buy milk"}' \
  --env TASK_BACKEND=sqlite --env DB_PATH=tasks.db \
  --stdio "uv run --directory ../mcpserver-template mcpserver-template"

~ and $VARS are expanded in the command and in every argument, including entries read from a config file.

For a remote server, MCP_TOKEN is sent as a bearer token when set.

Noisy servers

A stdio server writes its own logs to stderr, and they land in your terminal. Servers built on older MCP SDKs answer FastMCP's capability probe with a wall of validation errors before falling back to the legacy protocol — the inspection still succeeds, but the output is buried.

--quiet discards that stream. It is not the default on purpose: when a server fails to start, the reason is in its first stderr line, and hiding it turns a clear error into a bare "Connection closed".

Errors

A failure prints one line naming its cause and exits with status 1:

error: Client failed to connect: [Errno 2] No such file or directory: 'uvx'

When a stdio server dies while starting, its own stderr line comes first and the error line points to it. Set MCP_SERVERS_CLI_DEBUG=1 to get the full traceback instead.

Configuration file

The --config mode reads the mcpServers format used by Claude Desktop:

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "${HOME}/Developer"]
    },
    "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] },
    "remote": { "url": "https://example.com/mcp" }
  }
}

Project structure

src/mcp_servers_cli/
├── transports.py   one builder per transport, plus path expansion
├── inspection.py   reads a server into dataclasses
├── rendering.py    turns inspection data, results and agent progress into output
├── repl.py         interactive loop over a connected client
├── errors.py       turns a failure into one line
├── llm.py          provider-neutral conversation model and the LLMBackend protocol
├── backends/       one module per provider, translating to and from that model
├── agent.py        the tool loop: model turns and tool calls, printing nothing
├── trace.py        JSON Lines record of an agent run
└── cli.py          cyclopts commands

Inspection returns data and never prints; rendering never talks to a server. That is what lets the tests run an in-memory FastMCP server and assert on structures rather than on captured stdout.

Adding a transport means adding a builder in transports.py and a target option in cli.py, without touching the existing ones.

Only backends/ imports an LLM SDK; everything else works on the neutral types of llm.py. Adding a provider means adding one module there and one line in backends/__init__.py.

Tests

uv run pytest

No network and no LLM. The inspection and agent-loop tests run against an in-memory FastMCP server with a scripted model; the transport tests check expansion rules; the rendering tests capture a rich console; the backend tests translate real SDK objects through a stand-in client; the trace tests write to a temporary directory. Two kinds of test start a process: the error tests launch a command that does not exist, and the agent command is tested end to end against tests/fixtures/add_server.py over stdio.

Dependencies

Package Role
fastmcp MCP client and transports
cyclopts Commands and help, from type hints
rich Tables and JSON highlighting
ollama Local models, the default backend
anthropic Anthropic backend, optional: mcp-servers-cli[anthropic]

cyclopts and rich already ship in FastMCP's dependency tree; they are declared explicitly rather than relied on transitively.

Metadata

Release files for mcp-servers-cli 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-servers-cli 0.1.0
File Size Uploaded
mcp_servers_cli-0.1.0.tar.gz 110.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-servers-cli 0.1.0
File Interpreter ABI Platform
mcp_servers_cli-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 133.3 kB

Release files / mcp_servers_cli-0.1.0.tar.gz

Download URL mcp_servers_cli-0.1.0.tar.gz
Size 110.7 kB
Tags Source
SHA-256 checksum
How to use checksums
935a06f645bcac58e9df2604a2dfc966edffcbadc927a9e64a44fa9e8c68f90b
BLAKE2b-256 checksum
How to use checksums
2faf74f309ec5328685917596ac776df0dd25c9ff7f40fb12e8fca7b167bbdbd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / mcp_servers_cli-0.1.0-py3-none-any.whl

Download URL mcp_servers_cli-0.1.0-py3-none-any.whl
Size 22.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4b20dc66e15784a86cc87b12e8afa72fd924a39bc27028422cd26dd939f69a1b
BLAKE2b-256 checksum
How to use checksums
9319050999e6cb91e068c286d819e72d6af0cd447d2cd8929f38681851183997
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page