███╗ ███╗ ██████╗██████╗ ██████╗ ████████╗██╗ ██╗
████╗ ████║██╔════╝██╔══██╗ ██╔══██╗╚══██╔══╝██║ ██╔╝
██╔████╔██║██║ ██████╔╝█████╗██████╔╝ ██║ █████╔╝
██║╚██╔╝██║██║ ██╔═══╝ ╚════╝██╔══██╗ ██║ ██╔═██╗
██║ ╚═╝ ██║╚██████╗██║ ██║ ██║ ██║ ██║ ██╗
╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
mcp-rtk
MCP proxy that cuts 60-90% of tokens from tool responses
Drop it in front of any MCP server. Same tools, way fewer tokens.
Quick start • Presets • Commands • Configuration • Contributing presets
The problem
MCP servers return raw API responses. A single list_issues call from GitLab can dump 180K+ tokens of JSON into your context: full user objects with avatar URLs, null fields everywhere, nested metadata nobody asked for. You pay for all of it.
What mcp-rtk does
It sits between Claude and your MCP server as a transparent proxy. Tool calls go through unchanged, but responses get compressed through an 8-stage filter pipeline before they hit your context window.
Claude ←(stdio)→ mcp-rtk ←(stdio)→ upstream MCP server
The LLM gets the same information in a fraction of the tokens.
Install
Quick start
Wrap your existing MCP server command with mcp-rtk --. One line change in your Claude Code config.
Before (~/.claude.json):
After:
mcp-rtk detects the upstream server from the command and loads the matching preset automatically.
Filter pipeline
Every response passes through 8 stages, in order:
| Stage | Effect |
|---|---|
keep_fields |
Whitelist: only retain specified fields |
strip_fields |
Blacklist: recursively remove fields like avatar_url, _links |
condense_users |
{id, name, username, avatar_url, ...} becomes "username" |
strip_nulls |
Drop all null and empty string values |
flatten |
{"data": [...]} becomes [...] |
truncate_strings |
Cap long strings (descriptions, diffs) at N chars |
collapse_arrays |
Keep first N items, append "... and X more" |
custom_transforms |
Regex-based string replacements |
Even without a preset, generic defaults apply: null stripping, user condensing, flattening, and field removal. This alone typically saves 30-40%.
Presets
Presets are TOML files with tool-specific filter rules. mcp-rtk ships with community-maintained presets and auto-detects which one to use based on the upstream command.
| Preset | Detected from | Tools covered |
|---|---|---|
gitlab |
gitlab-mcp, gitlab |
45+ tools across MRs, issues, pipelines, commits, projects, labels, releases |
grafana |
mcp-grafana, grafana |
15+ tools for dashboards, datasources, Prometheus, Loki |
You can force a specific preset:
What a preset looks like
Each preset defines per-tool rules that get merged on top of the generic defaults. Here's an excerpt from the GitLab preset:
[]
= ["iid", "title", "state", "author", "source_branch", "target_branch", "web_url"]
= 20
= true
[]
= ["old_path", "new_path", "diff", "new_file", "deleted_file"]
= 2000
= 50
[]
= ["iid", "title", "state", "author", "labels", "assignees", "web_url"]
= 20
= true
The full GitLab preset covers merge requests, pipelines, jobs, issues, commits, files, projects, members, labels, releases, and events.
External presets (auto-discovery)
Drop any .toml preset file into ~/.local/share/mcp-rtk/presets/ and it will be auto-discovered at startup. No recompilation, no config change needed.
~/.local/share/mcp-rtk/presets/
github.toml
slack.toml
my-internal-api.toml
External presets use the same [tools.*] format as built-in presets. Add an optional [meta] section with detection keywords to enable auto-detection from the upstream command:
# ~/.local/share/mcp-rtk/presets/github.toml
[]
= ["github-mcp", "github"]
[]
= ["id", "name", "full_name", "description", "html_url", "language"]
= 20
[]
= ["number", "title", "state", "body", "user", "labels", "assignees"]
= 1500
= true
Without [meta], the preset can still be used with --preset <name> (where name = filename without .toml).
External presets are fetched by mcp-rtk presets pull and scaffolded by mcp-rtk presets init, so the ecosystem grows without waiting for built-in releases.
Hot reload
External presets are hot-reloaded while the proxy is running. When you add, edit, or remove a .toml file in ~/.local/share/mcp-rtk/presets/, mcp-rtk detects the change and atomically rebuilds the filter engine within 500 ms. In-flight requests complete with the previous rules, while new requests use the updated ones. No restart needed.
This makes it easy to iterate on filter rules: edit the preset, save, and the next tool call uses the new config.
Creating your own preset
If you use an MCP server that doesn't have a built-in preset, you can write your own as a TOML config:
# ~/.config/mcp-rtk/my-server.toml
[]
= true
= true
= 800
= 25
= ["avatar_url", "_links", "metadata"]
= true
[]
= ["id", "name", "status", "created_at"]
= 15
[] # glob pattern — matches all get_ tools
= 1500
= ["internal_id", "legacy_data"]
Then pass it with --config:
Or drop it as a preset in ~/.local/share/mcp-rtk/presets/ for auto-discovery (use [tools.*] format with an optional [meta] section instead of [filters.*]).
Contributing a preset
Found good filter rules for an MCP server you use? Submit a preset so others can benefit too.
- Create a TOML file in
config/presets/named after the server (e.g.,github.toml) - Add your tool-specific filter rules (look at
gitlab.tomlorgrafana.tomlfor reference) - Register it in
src/config.rsin thePRESETSarray with detection keywords - Open a merge request
The more MCP servers get presets, the more tokens everyone saves.
Configuration
For power users who want to tweak filter behavior beyond what presets provide.
[]
= true
= true
= 500
= 20
= ["avatar_url", "_links", "time_stats"]
= true
= [
{ = "https://gitlab\\.com/[^ ]+", = "[link]" }
]
[]
= 2000
[]
= ["iid", "title", "state", "author", "source_branch", "target_branch"]
= 15
[]
= true
= "~/.local/share/mcp-rtk/metrics.db"
User config overrides preset rules. Tool-specific strip_fields and custom_transforms are concatenated with defaults, everything else replaces them.
Tool names support glob patterns: * matches any sequence, ? matches a single character. Exact matches always take priority over patterns.
Commands
mcp-rtk gain
See how many tokens you've saved.
mcp-rtk discover
Scan your Claude Code session logs to find MCP servers that aren't proxied yet and estimate how many tokens you could save.
mcp-rtk install
Automatically wrap MCP servers in a config file with mcp-rtk. Scans for stdio servers and rewrites their command/args.
mcp-rtk uninstall
Remove mcp-rtk wrapping from MCP servers in a config file.
mcp-rtk presets
Browse, create, and fetch presets.
mcp-rtk validate-preset
Check a preset or config file for syntax errors and potential issues before using it.
mcp-rtk dry-run
Test filters on JSON from stdin without running a proxy. Stats go to stderr, filtered JSON to stdout.
|
|
mcp-rtk diff
Show a colored side-by-side diff between raw and filtered JSON. Reads from stdin.
|
|
Safety
Responses are filtered, never blocked. A few guardrails keep things predictable:
- 10 MB response cap: oversized responses get truncated before parsing
- 128-level recursion limit, matching serde_json's default
- String truncation respects UTF-8 character boundaries
- Non-JSON responses pass through with only string truncation applied
- Images and resources are forwarded unchanged