mcp-lens
Progressive disclosure for large MCP tool catalogs.
Quick Start · Spec · Examples · Contributing
Most MCP servers expose one tool per endpoint. That works until your catalog grows — 20 endpoints becomes 20 tool definitions loaded into every context window, 200 becomes 200, and the model starts guessing wrong between similarly-named tools long before you get there.
mcp-lens is a small, dependency-light pattern (and a Python reference
implementation) for the alternative: expose exactly 3 stable meta-tools —
search_capabilities, get_capability_schema, execute_capability — no
matter how many capabilities sit behind them. The tool-definition cost the
model pays is O(1) in catalog size; only what search_capabilities actually
returns grows with your catalog.
from mcp_lens import Capability, CapabilityRegistry, build_server
registry = CapabilityRegistry()
registry.register(
Capability(
key="billing.create_invoice",
description="Create an invoice for a customer",
input_schema={
"type": "object",
"properties": {"customer_id": {"type": "string"}, "amount": {"type": "number"}},
"required": ["customer_id", "amount"],
},
executor=lambda customer_id, amount: {"invoice_id": "INV-001", "amount": amount},
)
)
# ...register 5, 50, or 5,000 more capabilities the same way...
mcp = build_server(registry, name="my-server")
mcp.run()
Whether registry holds 1 capability or 5,000, the MCP client always sees
the same 3 tools.
Why this, specifically
- The registry has no MCP dependency.
mcp_lens.registryis plain Python — testable, reusable, and swappable behind any transport. The FastMCP adapter inmcp_lens.serveris a thin, optional layer on top. - Search is pluggable. The default matcher is keyword substring
matching, fine for demos. Pass your own
search_fntoCapabilityRegistryfor Postgres full-text, embeddings, or whatever search backend you already run — the 3-tool contract doesn't change. - It's a spec, not just a library.
SPEC.mddefines the contract (tool names, schemas, semantics) independently of this implementation, so it can be implemented in other languages and still interoperate conceptually. - Validated with real traffic, not just a thought experiment — this pattern (search → schema → execute) has been running in production MCP servers before this repository existed; this is the extracted, product-agnostic version of that mechanism.
Installation
Add mcp-lens to a new or existing Python project with uv:
uv add mcp-lens
Or with pip:
pip install mcp-lens
Installing from source — track main:
uv add "mcp-lens @ git+https://github.com/helygp/mcp-lens.git@main"
Quick Start
1. Define your capabilities
A Capability is a key, a description, a JSON Schema for its input, and a
callable (sync or async) that runs it:
from mcp_lens import Capability
def get_weather(city: str) -> str:
return f"Sunny in {city}"
weather = Capability(
key="weather.get_weather",
description="Get current weather for a city",
input_schema={
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
executor=get_weather,
tags=("weather", "forecast"),
)
2. Register them
from mcp_lens import CapabilityRegistry
registry = CapabilityRegistry()
registry.register(weather)
# or: registry.register_many([weather, other_capability, ...])
3. Serve them
from mcp_lens import build_server
mcp = build_server(registry, name="my-server")
if __name__ == "__main__":
mcp.run()
Run the checked-in example instead of writing your own from scratch:
uv run examples/basic/server.py
See examples/README.md for the full walkthrough, including
the token-cost comparison in examples/benchmark/.
Learn more
- SPEC.md — the formal contract: tool names, schemas, semantics, and what's deliberately left out of scope (auth, persistence, discovery UI — those are yours to build).
- examples/ — a runnable 5-capability example server and a before/after token-cost comparison as the catalog grows.
- CONTRIBUTING.md — how to propose changes, including changes to the spec itself.
Related work
This pattern isn't new — Twenty CRM uses
a similar fixed-meta-tool approach for its MCP server, and "don't load every
tool definition up front" is an increasingly common idea across the MCP
ecosystem as catalogs grow. What mcp-lens adds is not the idea itself but:
a formal, implementation-agnostic contract for it (SPEC.md), a tested
reference implementation with the MCP-specific parts cleanly separated from
the reusable core, and measured numbers for the tradeoff instead of just the
claim. If you know of other implementations of this pattern, a PR adding
them here is welcome.
Non-goals
mcp-lens is deliberately narrow. It does not provide: authentication, capability persistence/storage, an approval or review UI, or AI-assisted onboarding of new capabilities. Those are real, useful things to build on top of this — but they're product decisions, not part of the protocol pattern this repo exists to document and implement.
Contributing
git clone https://github.com/helygp/mcp-lens.git
cd mcp-lens
uv sync --group dev
uv run pytest
uv run ruff check
See CONTRIBUTING.md for the full workflow.
Citation
If mcp-lens or the pattern in SPEC.md is useful in your work, you can cite
the repository:
@software{mcp_lens,
title = {mcp-lens: Progressive disclosure for large MCP tool catalogs},
author = {Pasqual, Hely},
year = {2026},
url = {https://github.com/helygp/mcp-lens}
}
License
Apache 2.0. See LICENSE.
Metadata
Release files for mcp-lens-py 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_lens_py-0.1.0.tar.gz | 16.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_lens_py-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.1 kB
Release files / mcp_lens_py-0.1.0.tar.gz
| Download URL | mcp_lens_py-0.1.0.tar.gz |
|---|---|
| Size | 16.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8386fab3230703db5b544834aa33d27e43cf4781faa14fd6df2dbfc086d9552c
|
|
BLAKE2b-256 checksum How to use checksums |
8643b7883fd260f51b545bc32a8d0db2d0c22d2cc6ec4bbf712050316c362120
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / mcp_lens_py-0.1.0-py3-none-any.whl
| Download URL | mcp_lens_py-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
daeb4c06a5da9b3a224fc0eaa655d6b851bbaff7f5bab3a8625d1a78cfb566b0
|
|
BLAKE2b-256 checksum How to use checksums |
b272f83a2adfbea7b274592dcee0def3d779cb45152a52fe045c1d6be4561676
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|