tare-mcp
v0.3.0
Published
Local-first CLI for analyzing MCP context weight and tool ambiguity.
Maintainers
Readme
tare-mcp
Measure the MCP tool surface your agent is about to send to a model.
npm install tare-mcpimport { measureTools } from "tare-mcp";
const tools = await mcpClient.listTools();
const report = await measureTools(tools, { budget: 40_000 });
console.log(
`MCP tool surface: ${report.summary.tools} tools, ~${report.summary.estimatedTokens.claude} Claude tokens`
);
if (report.metadata.budgetExceeded) {
throw new Error("MCP tool surface exceeds budget");
}MCP made tools easy to connect. It did not make them cheap to carry.
tare-mcp shows, from inside your agent or from the CLI:
- how many tools your agent sees
- how much context those tools consume, estimated for Claude and OpenAI cl100k
- which servers dominate the budget
- which tools overlap and compete for model attention
- whether your setup exceeds a context budget
Use the CLI when you want to inspect config-discovered MCP servers locally:
npx tare-mcpThink of it as du -sh node_modules, but for agent tool context.
Table of Contents
- Why This Matters
- Why Token Count Is Not the Whole Problem
- Using tare-mcp in your agent
- CLI Quickstart
- Hosted MCP Quickstart
- Scenario Examples
- Example Output
- Supported Transports
- Static vs Live Inspection
- Accuracy
- Security Model
- Config Discovery
- JSON Usage
- PR Regression Checks
- CI Usage
- Publishing to npm
- CLI
- Roadmap
- License
Why this matters
Every MCP tool is context the model has to carry before it can act. Large tool lists and verbose schemas reduce the room left for the actual task, and they can make routing decisions noisier.
tare-mcp makes that weight visible locally, before a coding agent or assistant spends any of its working context on it.
Why token count is not the whole problem
A bloated MCP setup hurts in two ways.
First, it consumes context.
Second, it creates ambiguity.
If three servers all expose tools that look like "search", the model has to choose between them before it can do useful work.
tare-mcp shows both:
- what your tools weigh
- where your tools overlap
Using tare-mcp in your agent
Production agents often do not have a stable .mcp.json file to inspect. They connect to MCP servers, call tools/list, and pass those tool definitions to the model on each request.
Use measureTools() when you already have the tool definitions in memory:
import { measureTools } from "tare-mcp";
const tools = await mcpClient.listTools();
const report = await measureTools(tools);
console.log(
`MCP tool surface: ${report.summary.tools} tools, ~${report.summary.estimatedTokens.claude} Claude tokens`
);For multiple MCP servers, add server per tool so overlap warnings and per-server totals remain useful:
import { measureTools } from "tare-mcp";
const last9Tools = await last9Client.listTools();
const githubTools = await githubClient.listTools();
const report = await measureTools([
...last9Tools.map((tool) => ({ ...tool, server: "last9" })),
...githubTools.map((tool) => ({ ...tool, server: "github" }))
]);For unattributed tools, pass a fallback server name:
const report = await measureTools(tools, {
serverName: "agent"
});Budget checks are metadata, not exceptions. That keeps the library easy to use in request paths, logs, and CI:
const report = await measureTools(tools, { budget: 40_000 });
if (report.metadata.budgetExceeded) {
throw new Error(
`MCP tool surface exceeds budget: ~${report.summary.estimatedTokens.claude} Claude tokens`
);
}Structured logging example:
logger.info("mcp.tool_surface", {
tools: report.summary.tools,
servers: report.summary.servers,
tokens_claude: report.summary.estimatedTokens.claude,
tokens_openai_cl100k: report.summary.estimatedTokens.openaiCl100k,
overlap_clusters: report.overlapClusters.length,
budget_exceeded: report.metadata.budgetExceeded ?? false
});The programmatic API is local-first. It does not read config files, spawn MCP servers, or call cloud tokenization APIs by default. API-backed Claude token counting is opt-in:
const report = await measureTools(tools, {
claudeTokenizerMode: "api",
anthropicApiKey: process.env.ANTHROPIC_API_KEY
});CLI Quickstart
Run it without installing:
npx tare-mcpOn the first run you should see something like:
tare-mcp — MCP context weight
Config files found: 1
Servers analyzed: 2
Inspection mode: live default
Tools exposed: 47
Estimated context weight:
- Claude estimate: ~18,400 tokens
- OpenAI cl100k estimate: ~17,800 tokens
Context window usage:
- 200k window: 9%
- 128k window: 14%
- 64k window: 29%If the output is empty or shows "Config files found: 0", see Config discovery.
Install it in a project:
npm install --save-dev tare-mcp
npx tare-mcpInstall it globally:
npm install --global tare-mcp
tare-mcpStatic-only mode parses config without starting servers or calling hosted endpoints:
npx tare-mcp --no-execSet a budget:
npx tare-mcp --budget 40000
npx tare-mcp --budget 40000 --tokenizer openaiEmit JSON for CI or other tools:
npx tare-mcp --jsonFor local development from this repository:
pnpm install
pnpm build
pnpm devHosted MCP Quickstart
Use this when you want to inspect a real hosted MCP endpoint.
mkdir -p /tmp/tare-mcp-hosted
cd /tmp/tare-mcp-hosted
cat > .mcp.json <<'JSON'
{
"mcpServers": {
"last9": {
"type": "http",
"url": "https://mcp.last9.io/mcp",
"headers": {
"Authorization": "Bearer ${LAST9_MCP_TOKEN}"
}
}
}
}
JSON
export LAST9_MCP_TOKEN="..."
npx tare-mcp --timeout 10000The hosted example config points at Last9's observability MCP endpoint:
https://mcp.last9.io/mcpTo use a different hosted MCP server, edit the url and headers in .mcp.json. Any Streamable HTTP MCP server works here.
If the token is missing or invalid, tare-mcp reports a 401 Unauthorized fallback instead of crashing.
Expected shape with valid credentials:
Inspecting last9 via streamable-http...
tare-mcp — MCP context weight
Config files found: 1
Servers analyzed: 1
Inspection mode: live default
Tools exposed: ...Scenario Examples
Scenario files live in examples/scenarios:
hosted-streamable-http.mcp.json: hosted Streamable HTTP MCP with bearer-token auth.local-stdio.mcp.json: packaged stdio MCP server.examples/scenarios/README.md: copy-paste commands for each scenario.
For a no-credentials local Streamable HTTP smoke test, use examples/live-streamable-http:
pnpm install
pnpm build
cd examples/live-streamable-http
node server.mjsThen in another terminal:
cd examples/live-streamable-http
mkdir -p .home
HOME="$PWD/.home" node ../../dist/cli.jsThe temporary HOME keeps local smoke tests focused on the example .mcp.json instead of mixing in your real Claude or editor MCP configs.
Expected local smoke-test shape:
Inspecting tare-live-http-example via streamable-http...
tare-mcp — MCP context weight
Config files found: 1
Servers analyzed: 1
Inspection mode: live default
Tools exposed: 2
Worst servers:
1. tare-live-http-example ...
Worst tools:
1. tare-live-http-example.search_docs ...
2. tare-live-http-example.read_doc ...Example output
tare-mcp — MCP context weight
MCP made tools easy to connect. It did not make them cheap to carry.
Config files found: 2
Servers analyzed: 3
Inspection mode: live default
Tools exposed: 418
Estimated context weight:
- Claude estimate: ~143,200 tokens
- OpenAI cl100k estimate: ~138,400 tokens
Context window usage:
- 200k window: 72%
- 128k window: 112%
- 64k window: 224%
Worst servers:
1. github ~67,410 Claude tokens 188 tools
2. notion ~41,800 Claude tokens 96 tools
3. linear ~34,010 Claude tokens 134 tools
Worst tools:
1. github.create_pull_request ~3,912 Claude tokens
2. notion.query_database ~3,110 Claude tokens
3. linear.create_issue ~2,440 Claude tokens
Overlap warnings: 3 clusters
1. search intent
github.search_code
filesystem.grep
linear.search_issues
→ Prefer one search surface per workflow.
2. file write
filesystem.write_file
github.create_or_update_file
→ Disable duplicate write paths unless explicitly needed.
3. issue creation
github.create_issue
linear.create_issue
jira.create_issue
→ Create task-specific profiles.
Recommendations:
- Split large MCP servers into task-specific profiles.
- Prefer read-only profiles for common workflows.
- Avoid exposing multiple tools for the same intent unless needed.
- Disable rarely used write/admin tools.
- Use `tare-mcp --budget 40000` to enforce a context budget.
- Use `tare-mcp --json` to track this in CI.Supported transports
v0.3 supports:
- stdio MCP servers
- Streamable HTTP MCP servers
- programmatic tool definitions through
measureTools()
SSE may be supported best-effort later.
If a server cannot be inspected because credentials are missing, the endpoint is wrong, or the transport is unsupported, tare-mcp falls back to static-insufficient mode and says so clearly.
Static vs live inspection
Live inspection is the default because it asks MCP servers for the tool definitions they actually expose through tools/list.
npx tare-mcpStatic-only mode does not spawn stdio MCP servers and does not call hosted MCP URLs:
npx tare-mcp --no-execStatic-only mode is insufficient for packaged or hosted MCP servers because config files usually contain only commands, args, URLs, and headers. They do not contain the tool schemas the model receives.
Accuracy
tare-mcp reports estimates, not exact truth.
Live inspection shows the actual tool definitions exposed by your MCP servers at inspection time.
Token counts are still model-dependent. tare-mcp shows both Claude and OpenAI cl100k estimates where possible.
By default, Claude token counts are local approximations. API-backed Claude token counting is optional and must be explicitly enabled:
npx tare-mcp --claude-tokenizer apiThat mode requires ANTHROPIC_API_KEY and uses Anthropic's POST /v1/messages/count_tokens endpoint.
Environment variables that control tokenization:
| Variable | Values | Default | Description |
| ---------------------------------- | -------------- | ------------------- | ----------------------------------------------- |
| ANTHROPIC_API_KEY | string | — | Required for --claude-tokenizer api |
| TARE_CLAUDE_TOKENIZER | local, api | local | Override --claude-tokenizer via env |
| TARE_ANTHROPIC_MODEL | model ID | claude-sonnet-4-6 | Model used for API-backed token counting |
| TARE_DISABLE_ANTHROPIC_TOKEN_API | 1 | unset | Disable API-backed counting even when requested |
Security model
tare-mcp is local-first.
By default, it does not call cloud tokenization APIs.
Live inspection does execute configured stdio MCP server commands and calls configured hosted MCP URLs so it can ask them for their actual tool definitions.
Use --no-exec for static-only mode, but note that static-only mode is insufficient for packaged or hosted MCP servers because it cannot see exposed tool schemas.
tare-mcp redacts environment variable values and header values in logs, warnings, JSON, and errors.
Config discovery
tare-mcp discovers MCP configs from common locations:
./.mcp.json
./mcp.json
./.cursor/mcp.json
./.vscode/mcp.json
~/.claude/mcp.json
~/Library/Application Support/Claude/claude_desktop_config.json
~/.config/Claude/claude_desktop_config.json
~/.config/claude/claude_desktop_config.json
~/.config/tare/mcp.jsonSupported server maps:
{
"mcpServers": {}
}{
"servers": {}
}{
"mcp": {
"servers": {}
}
}See examples/stdio.mcp.json and examples/streamable-http.mcp.json.
JSON usage
npx tare-mcp --json > tare-report.jsonThe JSON report includes:
- version and generation time
- summary counts
- per-server inspection mode and confidence
- both Claude and OpenAI cl100k estimates
- per-tool estimates
- overlap clusters
- recommendations
- warnings
Secrets from env vars and headers are redacted.
PR regression checks
tare-mcp diff compares two tare-mcp --json reports. It is built for pull requests: generate a fresh report in CI, compare it to a committed baseline, and fail when the MCP surface grows beyond a threshold.
Diff mode only reads JSON files. It does not spawn stdio servers, call hosted MCP URLs, or re-run live inspection.
Create an auditable baseline and commit it:
mkdir -p .tare
npx tare-mcp --json > .tare/baseline.json
git add .tare/baseline.jsonCompare the current branch against that baseline:
npx tare-mcp --json > tare-report.json
npx tare-mcp diff --base .tare/baseline.json --head tare-report.jsonExample output:
tare-mcp diff - MCP context regression
Base: .tare/baseline.json (0.2.0)
Head: tare-report.json (0.2.0)
Summary:
- Servers: 3 -> 4 (+1)
- Tools: 418 -> 443 (+25)
- Claude tokens: ~143,200 -> ~151,620 (~+8,420)
- OpenAI cl100k tokens: ~138,400 -> ~146,310 (~+7,910)
- Overlap clusters: 2 -> 3 (+1)
New servers:
- notion: 96 tools, ~41,800 Claude tokens
Largest changes to existing servers:
- github: 188 -> 200 (+12) tools, ~+8,420 Claude tokensFail a PR when context weight or tool surface grows too much:
npx tare-mcp diff \
--base .tare/baseline.json \
--head tare-report.json \
--max-token-increase 5000 \
--max-tool-increase 20 \
--max-server-increase 1 \
--max-overlap-increase 0Token thresholds use the Claude estimate by default. Use OpenAI cl100k estimates when that is the budget you care about:
npx tare-mcp diff \
--base .tare/baseline.json \
--head tare-report.json \
--max-token-increase 5000 \
--tokenizer openaiThe selected tokenizer applies to threshold math and to the server/tool detail sections in human output. Token values are always rendered as estimates with ~.
Human output shows new servers, removed servers, largest changes to existing servers, new tools, removed tools, changed tools, and overlap-cluster changes. JSON output is available for automation:
npx tare-mcp diff --base .tare/baseline.json --head tare-report.json --jsonWhen a growth is intentional, regenerate .tare/baseline.json on the accepted setup and commit that change in the same PR.
Exit codes:
| Code | Meaning |
| ---: | ------------------------------------------------------------------------------------------------ |
| 0 | Diff completed and thresholds passed, or no thresholds were set. |
| 1 | A configured regression threshold was exceeded. |
| 2 | Invalid usage or invalid input, including missing files, invalid JSON, or missing report fields. |
Estimates are still estimates. The value of the baseline workflow is consistency: the same tool estimates are compared over time, so accidental MCP bloat becomes visible during review.
CI usage
name: MCP context budget
on:
pull_request:
jobs:
tare:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npx tare-mcp --budget 40000For PR regression checks, commit .tare/baseline.json and compare the generated report in CI:
name: MCP context regression
on:
pull_request:
jobs:
tare-mcp:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npx tare-mcp --json > tare-report.json
- run: |
npx tare-mcp diff \
--base .tare/baseline.json \
--head tare-report.json \
--max-token-increase 5000 \
--max-tool-increase 20 \
--max-overlap-increase 0For CI systems that should not execute local MCP server commands, use static-only mode and treat the result as insufficient:
npx tare-mcp --no-exec --jsonPublishing to npm
This repository includes .github/workflows/publish-npm.yml.
Maintainers should use the release checklist.
To publish from GitHub Actions:
- Create an npm automation token.
- Add it to the repository as
NPM_TOKEN. - Publish a GitHub release or run the workflow manually.
The workflow runs:
pnpm install --frozen-lockfile
pnpm test
pnpm run lint
pnpm build
npm pack --dry-run
npm publish --access public --provenanceThe npm package is named tare-mcp because the unscoped tare package name is already occupied on npm.
Users can install it with:
npm install --save-dev tare-mcp
npx tare-mcpFor one-off usage:
npx tare-mcpThe installed binary is named tare-mcp, so global installs work as tare-mcp:
npm install --global tare-mcp
tare-mcpCLI
Usage: tare-mcp [options]
Analyze MCP context weight and tool ambiguity.
MCP made tools easy to connect. It did not make them cheap to carry.
Options:
--no-exec Static-only mode. Does not spawn MCP servers or call hosted MCP URLs.
--timeout <ms> Live inspection timeout per server. Default: 5000.
--budget <tokens> Fail if estimated context weight exceeds budget.
--tokenizer <name> Budget tokenizer: claude or openai. Default: claude.
--json Output JSON report.
--claude-tokenizer <mode> Claude tokenizer mode: local or api. Default: local.
-h, --help Display help.Usage: tare-mcp diff [options] [base-report] [head-report]
Compare two tare-mcp JSON reports without inspecting MCP servers.
Options:
--base <path> Baseline JSON report from tare-mcp --json.
--head <path> Head JSON report from tare-mcp --json.
--json Output JSON diff report.
--max-token-increase <tokens> Fail if token increase exceeds this value.
--max-tool-increase <tools> Fail if tool count increase exceeds this value.
--max-server-increase <servers> Fail if server count increase exceeds this value.
--max-overlap-increase <clusters> Fail if new overlap cluster count exceeds this value.
--tokenizer <name> Tokenizer for --max-token-increase: claude or openai. Default: claude.Roadmap
v0.3:
- [x] Programmatic API for running agents through
measureTools() - [x] Programmatic JSON reports compatible with
tare-mcp diff
v0.2:
- [x] PR diff/regression mode for JSON reports
- [x] Threshold flags for token, tool, server, and overlap growth
Next:
- [ ] Per-tool schema breakdown
- [ ] Context budget config file (
tare.config.json)
Later:
- [ ] Better SSE fallback
- [ ] Improved Claude local token estimator
- [ ] Opt-in API-backed token counting improvements
- [ ] Dedicated GitHub Action wrapper
- [ ] HTML reports
- [ ] MCP profile generator
- [ ]
tare-mcp --fixto generate lean MCP profiles
Dashboards, profile generation, and auto-fix are intentionally not part of v0.3.
License
MIT
