Skip to main content

mcp-lens

Progressive disclosure for large MCP tool catalogs.

CI License Python Spec Status

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-toolssearch_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.registry is plain Python — testable, reusable, and swappable behind any transport. The FastMCP adapter in mcp_lens.server is a thin, optional layer on top.
  • Search is pluggable. The default matcher is keyword substring matching, fine for demos. Pass your own search_fn to CapabilityRegistry for 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.md defines 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

mcp_lens_py-0.1.0.tar.gz (16.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_lens_py-0.1.0-py3-none-any.whl (11.8 kB view details)

Uploaded Python 3

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

Hashes for mcp_lens_py-0.1.0.tar.gz
Algorithm Hash digest
SHA256 8386fab3230703db5b544834aa33d27e43cf4781faa14fd6df2dbfc086d9552c
MD5 e01f60c72c59a7504692791c7e3ee384
BLAKE2b-256 8643b7883fd260f51b545bc32a8d0db2d0c22d2cc6ec4bbf712050316c362120

See more details on using hashes here.

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

Hashes for mcp_lens_py-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 daeb4c06a5da9b3a224fc0eaa655d6b851bbaff7f5bab3a8625d1a78cfb566b0
MD5 9d5c32863f853189ae9c13f35637ffd5
BLAKE2b-256 b272f83a2adfbea7b274592dcee0def3d779cb45152a52fe045c1d6be4561676

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page