Skip to main content

mcp-token-lint

CI PyPI Python

See what your MCP servers are costing you before you connect.

Three ordinary MCP servers — GitHub, Slack, Sentry, about 40 tools total — can burn 72% of a 200,000-token context window on tool schemas alone, before an agent reads a single message. Nothing in the MCP client stack warns you about this cost until your session is already thin.

mcp-token-lint tokenizes every tool schema across every server in your MCP config, reports the exact cost per server and per tool, and fails like a CI budget check if the total crosses a threshold you set — before you ever connect.

Install

pip install mcp-token-lint

Usage

Point it at a standard MCP client config (the mcpServers format used by Claude Desktop, Claude Code, Cursor, and similar tools):

mcp-token-lint check ~/.config/claude/mcp.json --budget 40000

Or check a pre-exported list of tool definitions without launching any servers — useful in CI, or for transports mcp-token-lint doesn't talk to yet:

mcp-token-lint check --tools-json tools.json --budget 40000

tools.json is a {server_name: [tool, ...]} map, where each tool has name, description, and inputSchema — the same shape returned by an MCP server's tools/list.

Example output:

SERVER                 TOOLS      TOKENS
----------------------------------------
github                     34      18,412
slack                       9       3,105
sentry                     12       6,880
----------------------------------------
TOTAL                      55      28,397

Budget: 40,000 tokens  (28,397 used, 71%)  [within budget]

FLAGGED TOOLS (waste detected)
------------------------------------------------------------
github/search_repositories        612 tok  verbose_description  (-140 est.)
github/create_issue               210 tok  boilerplate_prefix   (-18 est.)
------------------------------------------------------------
Run `mcp-token-lint fix` to review and apply suggested trims.

mcp-token-lint check exits non-zero when the total is over budget, so it works as a CI gate:

mcp-token-lint check .mcp.json --budget 40000 || exit 1

Add --json for machine-readable output, and --timeout to change how long mcp-token-lint waits for a slow server to start (default 20s).

Fixing what it finds

check tells you which tools are wasting tokens and why. fix walks through them and lets you actually trim the fat:

mcp-token-lint fix ~/.config/claude/mcp.json
github/search_repositories  (flags: verbose_description, est. savings: 140 tok)
  current:   This tool allows you to search for GitHub repositories matching a query...
  suggested: Search for GitHub repositories matching a query string, with optional...
  [a]ccept / [e]dit / [s]kip / [q]uit >
  • [a]ccept takes the suggested trim as-is.
  • [e]dit lets you type your own replacement instead.
  • [s]kip leaves that tool untouched.
  • [q]uit stops the session; anything already accepted is still saved.

Accepted trims are written to mcp-token-lint.overrides.json (--out to change the path) — a plain {server: {tool: {"description": "..."}}} map you (or a teammate) can use as the checklist for editing the server's actual source, or feed into your own MCP proxy to rewrite tools/list responses live.

Two non-interactive modes:

mcp-token-lint fix .mcp.json --yes        # accept every suggestion, for a first pass or CI
mcp-token-lint fix .mcp.json --dry-run    # preview suggestions, write nothing

Waste detection is rule-based, not model-based — deterministic and fast enough for CI:

  • boilerplate_prefix — description opens with filler like "This tool allows you to..." that a model doesn't need.
  • verbose_description — description runs well past what's needed to disambiguate the tool; suggests trimming to the first sentence.
  • schema_bloat — the input schema is disproportionately large relative to the description, usually from repeated per-property boilerplate (flagged for manual review — schema structure isn't auto-rewritten, since a bad automatic edit there can break validation).

verbose_description trims to the first sentence where there is one. When the whole description is a single run-on sentence — the shape bloated descriptions usually take — it cuts at the last clause boundary that fits under the limit instead, rather than flagging something it can't act on.

Exit codes

check is meant to be wired into CI, so the exit code is the contract:

Code Meaning
0 Within budget (or no budget set)
1 Over budget
2 One or more servers could not be measured — results are incomplete

Code 2 matters more than it looks. A budget gate that reports success because it silently failed to reach every server is worse than no gate at all, so an unreachable server fails the run by default. Pass --ignore-fetch-errors to downgrade that to a warning when you genuinely want a partial check.

Running offline

tiktoken downloads its BPE file the first time it encodes anything — a poor dependency for a tool built to run in CI, where the network is often locked down. mcp-token-lint degrades instead of crashing: if cl100k_base can't be loaded it falls back to a deterministic approximate tokenizer, says so once on stderr, and marks the report and JSON output as approximate.

NOTE: counts are approximate — the cl100k_base encoding could not be loaded,
so a fallback tokenizer was used.

Set MCP_TOKEN_LINT_TOKENIZER=approx to skip tiktoken entirely and silence the warning. Approximate counts are stable run-to-run, so they still catch regressions and compare servers against each other — they're just not exact.

Docker

docker build -t mcp-token-lint .
docker run --rm -v "$PWD:/work" mcp-token-lint check /work/.mcp.json --budget 40000

The image bakes the cl100k_base encoding into its tiktoken cache at build time, so the container gives exact counts even on a restricted network.

Note that check <config> launches the stdio servers in your config — inside a container those commands (npx, uvx, your own binaries) need to exist in the image or be mounted in. For CI, --tools-json is usually the better fit.

Compatibility

Python 3.10+. Works with both mcp 1.x and 2.x — the SDK renamed Tool.inputSchema to Tool.input_schema in 2.0, and mcp-token-lint reads either.

What it does and doesn't do

  • Launches each configured command-based (stdio) server, calls tools/list, and tokenizes the exact JSON a model would see: name, description, and input schema.
  • Token counts use tiktoken's cl100k_base encoding as a consistent, practical approximation — not an exact match for every model's tokenizer, but stable enough to catch regressions and to compare servers against each other.
  • URL-based (HTTP/SSE) servers are skipped when reading from a config file for now — export their tool list and use --tools-json instead.

Why this exists

MCP standardized how a model talks to a tool. It never standardized how much that conversation should cost. Server authors write schemas that mirror their REST API surface — the easy path for an SDK generator, the expensive path for your context window. mcp-token-lint is the linter that was missing.

Development

pip install -e ".[dev]"
pytest

The test suite installs its own deterministic tokenizer, so it runs fully offline and never depends on tiktoken's download.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_token_lint-0.2.0.tar.gz (19.7 kB view details)

Uploaded Source

Built Distribution

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

mcp_token_lint-0.2.0-py3-none-any.whl (18.2 kB view details)

Uploaded Python 3

File details

Details for the file mcp_token_lint-0.2.0.tar.gz.

File metadata

  • Download URL: mcp_token_lint-0.2.0.tar.gz
  • Upload date:
  • Size: 19.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_token_lint-0.2.0.tar.gz
Algorithm Hash digest
SHA256 6ddfcae1a4252f7b5b4ab9b65078ab04d5cbb761c9d8c37113c5f3e31641200d
MD5 c4e5ebc2d095551dbcab01624863f782
BLAKE2b-256 723f746faf6239d7de5b37c4bed6e0cdfd3a70b1b8bd4ad96732893d47a7760e

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_token_lint-0.2.0.tar.gz:

Publisher: publish.yml on yashdhingra0/mcp-token-lint

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mcp_token_lint-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_token_lint-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 18.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mcp_token_lint-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4fe06351617dbce87a6b499478a01d846318edbf6ef99a033e81e2a155a2a3a8
MD5 e45a4afda182b28038033efcf0c05b8d
BLAKE2b-256 915ccc1912bd0d73234499a4ecf8b2e1e612fd66df72274682e7d45c6fe63d58

See more details on using hashes here.

Provenance

The following attestation bundles were made for mcp_token_lint-0.2.0-py3-none-any.whl:

Publisher: publish.yml on yashdhingra0/mcp-token-lint

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page