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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mcp_lens_py-0.1.0.tar.gz.
File metadata
- Download URL: mcp_lens_py-0.1.0.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8386fab3230703db5b544834aa33d27e43cf4781faa14fd6df2dbfc086d9552c
|
|
| MD5 |
e01f60c72c59a7504692791c7e3ee384
|
|
| BLAKE2b-256 |
8643b7883fd260f51b545bc32a8d0db2d0c22d2cc6ec4bbf712050316c362120
|
File details
Details for the file mcp_lens_py-0.1.0-py3-none-any.whl.
File metadata
- Download URL: mcp_lens_py-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
daeb4c06a5da9b3a224fc0eaa655d6b851bbaff7f5bab3a8625d1a78cfb566b0
|
|
| MD5 |
9d5c32863f853189ae9c13f35637ffd5
|
|
| BLAKE2b-256 |
b272f83a2adfbea7b274592dcee0def3d779cb45152a52fe045c1d6be4561676
|