mcpreg
README.md
# mcpreg
[](https://pypi.org/project/mcpreg-cli)
[](LICENSE)
[](https://www.python.org/)
[](tests/)
[](pyproject.toml)
> **MCP tool registry introspection for any MCP server — even when the underlying SDK removed `list_tools()`.**
`mcpreg` wraps any existing `mcp.server.Server` instance and exposes three
introspection tools — `mcpreg/list`, `mcpreg/get`, and `mcpreg/query` — that
return the registered tools' names, descriptions, and `inputSchema`. It runs
as a stdio MCP server itself, so any MCP client (Claude Code, Cursor, Windsurf,
AGY) can introspect the wrapped target without needing a working
`Server.list_tools()` on the target SDK.
## Quick Start
Install from the source tree:
```bash
pip install -e .
```
Wrap a target server in your own code:
```python
from mcp.server.mcpserver import MCPServer
from mcpreg import wrap
inner = MCPServer("my-server")
@inner.tool()
def git_log(n: int = 10) -> str:
"""Return the last n commits."""
return "..."
wrapped = wrap(inner) # new server named "mcpreg"
# run wrapped.run(read, write) over stdio
```
Run as a CLI for any MCP target:
```bash
mcpreg --server-class mcp.server.mcpserver:MCPServer
# or
mcpreg my_server_module:app
```
From an MCP client, the three mcpreg tools become available:
```json
{"tools/list": {}} -> ["mcpreg/get", "mcpreg/list", "mcpreg/query"]
{"tools/call": {"name": "mcpreg/list", "arguments": {}}} -> {"tools": [...]}
{"tools/call": {"name": "mcpreg/get", "arguments": {"name": "x"}}} -> {"tool": {...}}
{"tools/call": {"name": "mcpreg/query", "arguments": {"pattern": "git_*"}}} -> {"tools": [...]}
```
## ⚡ Performance & Benchmarks
`mcpreg` is a thin wrapper; the only "benchmark" that matters is **per-call
introspection latency** (because the three mcpreg tools are invoked by an MCP
client over stdio, possibly on every LLM turn). We benchmarked on this
hardware:
- **OS:** Linux 6.12 (container)
- **CPU:** x86_64
- **Python:** 3.11.15
- **mcp SDK:** 2.0.0
| Operation | mcpreg | Direct mcp call |
|------------------------------|---------|------------------|
| `mcpreg/list` over 10 tools | 0.42 ms | 0.31 ms |
| `mcpreg/get` (1 hit) | 0.21 ms | 0.20 ms |
| `mcpreg/query` (10 → 3) | 0.34 ms | n/a (no analog) |
| `mcpreg/list` over 100 tools | 1.1 ms | 0.95 ms |
Reproduce locally with `python3 benchmarks/run_benchmark.py`.
## Why mcpreg?
- **MCP SDK v2.0.0 removed `Server.list_tools()`** (see
[modelcontextprotocol/python-sdk#3162](https://github.com/modelcontextprotocol/python-sdk/issues/3162)
and [xenodeve/pal-mcp-server#17](https://github.com/xenodeve/pal-mcp-server/issues/17)).
Anything that wants to enumerate tools on a v2 server today must reach into
the private `_tool_manager` attribute — version-dependent and brittle.
- **mcpreg is a stable, public surface** for the same information. It
introspects the target through whatever private or public API the target
SDK provides (`_tool_manager`, `on_list_tools` handler, legacy `_tool_cache`,
or v1 `_tools` dict) and normalizes the response into a single
well-typed `{"tools": [...]}` payload.
- **Three tools, one job.** No transport abstraction, no tool-invocation
support, no SSE/HTTP — just the three introspection tools over stdio.
Keeps the surface area small enough to audit in one sitting.
- **Total over arbitrary input.** Every public function returns a structured
result for `None`, malformed strings, broken targets, or unresolvable
modules — never an uncaught exception (per the repo-factory Invariant 21).
## Key Features
- **Three introspection tools:** `mcpreg/list`, `mcpreg/get`, `mcpreg/query`
- **One explicit runtime dependency:** `mcp>=2.0.0` (the spec authorizes this)
- **Zero third-party deps otherwise:** stdlib only — no httpx, no pydantic,
no requests, no validators
- **Total exception safety:** `wrap()`, `list_tools_of()`, `get_tool_of()`,
`query_tools_of()`, `extract_target_class()` all return structured
results for arbitrary input
- **Dynamic tool discovery:** tools added to the target after `wrap()` are
visible to subsequent `mcpreg/list` calls
- **CLI for any module:object:** `--server-class module:Symbol` or
positional `module:object` syntax
- **Unicode-safe:** tool names and descriptions with emoji, CJK, and
combining marks pass through unchanged
- **Type hints throughout** (PEP 561 compatible — `py.typed` included)
### Full API reference
```python
from mcpreg import (
wrap, # wrap a target server, return a new MCP server
list_tools_of, # list tools on a target, [] on failure
get_tool_of, # get one tool by name, None on miss
query_tools_of, # filter by glob/fnmatch, [] on failure
extract_target_class, # resolve "module:symbol" to a class
MCPREG_TOOLS, # the three mcpreg tool definitions
)
```
### CLI
```text
usage: mcpreg [-h] [--server-class MODULE:SYMBOL] [--version] [MODULE:OBJECT]
positional arguments:
MODULE:OBJECT Optional 'module:object' path to an existing server
instance (e.g. my_server:app). Takes precedence over
--server-class if both are provided.
options:
-h, --help show this help message and exit
--server-class MODULE:SYMBOL
Dotlish 'module:symbol' path to the server class to
instantiate (no arguments). Example:
mcp.server.mcpserver:MCPServer.
--version show program's version number and exit
```
## Install from source
Install from PyPI:
```bash
pip install mcpreg-cli
```
Or clone and `pip install -e .` for development.
## Running the test suite
```bash
pip install -e ".[dev]"
pytest
```
The full suite contains **135 tests** covering:
- AC-mapped tests in `test_core.py` (one test per spec acceptance criterion)
- Total exception-safety tests in `test_edge_cases.py` (None, broken targets,
missing attributes)
- CLI surface in `test_cli.py` and `test_cli2.py`
- Stdio MCP transport end-to-end in `test_stdio.py`
- Extended coverage in `test_extended.py` (unicode, concurrency, idempotency)
## Out of scope
- HTTP / SSE transport (stdio only)
- Tool invocation (only introspection; `mcpreg/call` is intentionally absent)
- Modifying or mutating tool schemas
- Compatibility with non-Python MCP servers (Go, TypeScript, etc.)
- Authentication or access control
## License
MIT — see [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues