mcpreg
mcpreg
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:
pip install -e .Wrap a target server in your own code:
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 stdioRun as a CLI for any MCP target:
mcpreg --server-class mcp.server.mcpserver:MCPServer
# or
mcpreg my_server_module:appFrom an MCP client, the three mcpreg tools become available:
{"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 |
| 0.42 ms | 0.31 ms |
| 0.21 ms | 0.20 ms |
| 0.34 ms | n/a (no analog) |
| 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 and xenodeve/pal-mcp-server#17). Anything that wants to enumerate tools on a v2 server today must reach into the private_tool_managerattribute — 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_toolshandler, legacy_tool_cache, or v1_toolsdict) 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/queryOne 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 inputDynamic tool discovery: tools added to the target after
wrap()are visible to subsequentmcpreg/listcallsCLI for any module:object:
--server-class module:Symbolor positionalmodule:objectsyntaxUnicode-safe: tool names and descriptions with emoji, CJK, and combining marks pass through unchanged
Type hints throughout (PEP 561 compatible —
py.typedincluded)
Full API reference
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
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 exitInstall from source
Install from PyPI:
pip install mcpreg-cliOr clone and pip install -e . for development.
Running the test suite
pip install -e ".[dev]"
pytestThe 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.pyandtest_cli2.pyStdio MCP transport end-to-end in
test_stdio.pyExtended coverage in
test_extended.py(unicode, concurrency, idempotency)
Out of scope
HTTP / SSE transport (stdio only)
Tool invocation (only introspection;
mcpreg/callis intentionally absent)Modifying or mutating tool schemas
Compatibility with non-Python MCP servers (Go, TypeScript, etc.)
Authentication or access control
License
MIT — see LICENSE.