Skip to main content
MCP Filter

MCP Filter

A proxy MCP (Model Context Protocol) server that filters the upstream tool surface to just the tools you need. Expose one or two critical tools (for example, execute_sql on Supabase) while filtering out the token-costly ones, without having to modify the upstream implementation.

Before & After

Before: Unfiltered MCP servers consumed ~50k tokens on a fresh Claude Code session

⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁   claude-sonnet-4-5 · 112k/200k tokens (56%)
⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁
⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁   ⛁ System: 2.3k tokens
⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁   ⛁ System tools: 11.8k tokens
⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁   ⛁ MCP tools: 50.1k tokens (25%)  ← Large!
⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶   ⛶ Free space: 88k (44%)

After: With mcp-filter, reduced to ~13.7k tokens (72% reduction) while keeping all necessary tools.

⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛶ ⛶   claude-sonnet-4-5 · 80k/200k tokens (40%)
⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛁ ⛶ ⛶ ⛶
⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶   ⛁ System: 2.3k tokens
⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶   ⛁ System tools: 11.8k tokens
⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶   ⛁ MCP tools: 13.7k tokens (6.9%)  ← Filtered!
⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶ ⛶   ⛶ Free space: 120k (60%)  ← +32k gained

Filter only the tools you need, save context for longer sessions.

To see your token usage: Open Claude Code and run /context to view the breakdown.

What & Why

  • Static allowlist keeps the exposed tool list tiny, cutting context usage for clients that serialize tool schemas.
  • Drop-in proxy: stands in front of any MCP server that speaks stdio or HTTP/SSE, forwarding calls transparently.
  • Safety guardrails: optional regex-based deny list, optional prefix, and optional health tool for observability.

Installation

Python 3.10+ is supported; 3.11 is recommended.

Using pip

pip install mcp-filter

Using uv (faster, modern alternative)

# Run directly without installing (like npx)
uvx mcp-filter --version

# Or install globally
uv tool install mcp-filter

From source (development):

pyenv install 3.11.9
pyenv local 3.11.9
python -m venv .venv
source .venv/bin/activate
pip install -e '.[dev]'

Quick Start

mcp-filter run \
  -t stdio \
  --stdio-command npx \
  --stdio-arg=-y \
  --stdio-arg @supabase/mcp-server-supabase@latest \
  --stdio-arg=--access-token \
  --stdio-arg YOUR_TOKEN \
  -a "execute_sql,list_tables,get_project"

Shorthand flags: -t (transport), -a (allow-tool), -d (deny-pattern), -p (prefix)

Note: Each repeatable --stdio-arg value is forwarded as one literal argument. Use --stdio-arg=value when the value begins with -. Tool allowlists passed with -a may be comma-separated.

How to Wrap Your MCP

Concept

Transform any existing MCP server config by wrapping it with mcp-filter. The filter proxies your original command and only exposes the tools you specify with -a.

Before (original Supabase config):

{
  "mcpServers": {
    "supabase": {
      "command": "npx",
      "args": [
        "-y",
        "@supabase/mcp-server-supabase@latest",
        "--access-token",
        "YOUR_TOKEN"
      ]
    }
  }
}

This exposes all 29 tools (~20.8k tokens):

Show all tools
└ mcp__supabase__search_docs (supabase): 2.8k tokens
└ mcp__supabase__list_organizations (supabase): 582 tokens
└ mcp__supabase__get_organization (supabase): 604 tokens
└ mcp__supabase__list_projects (supabase): 600 tokens
└ mcp__supabase__get_project (supabase): 603 tokens
└ mcp__supabase__get_cost (supabase): 646 tokens
└ mcp__supabase__confirm_cost (supabase): 682 tokens
└ mcp__supabase__create_project (supabase): 832 tokens
└ mcp__supabase__pause_project (supabase): 599 tokens
└ mcp__supabase__restore_project (supabase): 599 tokens
└ mcp__supabase__list_tables (supabase): 640 tokens
└ mcp__supabase__list_extensions (supabase): 596 tokens
└ mcp__supabase__list_migrations (supabase): 596 tokens
└ mcp__supabase__apply_migration (supabase): 668 tokens
└ mcp__supabase__execute_sql (supabase): 657 tokens
└ mcp__supabase__get_logs (supabase): 677 tokens
└ mcp__supabase__get_advisors (supabase): 699 tokens
└ mcp__supabase__get_project_url (supabase): 599 tokens
└ mcp__supabase__get_anon_key (supabase): 601 tokens
└ mcp__supabase__generate_typescript_types (supabase): 600 tokens
└ mcp__supabase__list_edge_functions (supabase): 603 tokens
└ mcp__supabase__get_edge_function (supabase): 625 tokens
└ mcp__supabase__deploy_edge_function (supabase): 907 tokens
└ mcp__supabase__create_branch (supabase): 718 tokens
└ mcp__supabase__list_branches (supabase): 625 tokens
└ mcp__supabase__delete_branch (supabase): 596 tokens
└ mcp__supabase__merge_branch (supabase): 603 tokens
└ mcp__supabase__reset_branch (supabase): 636 tokens
└ mcp__supabase__rebase_branch (supabase): 617 tokens

After (wrapped with mcp-filter):

{
  "mcpServers": {
    "supabase": {
      "command": "mcp-filter",
      "args": [
        "run",
        "-t",
        "stdio",
        "--stdio-command",
        "npx",
        "--stdio-arg=-y",
        "--stdio-arg", "@supabase/mcp-server-supabase@latest",
        "--stdio-arg=--access-token",
        "--stdio-arg", "YOUR_TOKEN",
        "-a",
        "execute_sql,list_tables,get_project"
      ]
    }
  }
}

This only exposes 3 tools we allowed (~1.9k tokens = 91% reduction!):

└ mcp__supabase__get_project (supabase): 605 tokens
└ mcp__supabase__list_tables (supabase): 642 tokens
└ mcp__supabase__execute_sql (supabase): 659 tokens

Examples for Claude Code / Claude Desktop

examples/mcp.json.sample includes common setups. Add to your mcp.json:

Supabase (stdio) – wrap the official MCP binary:

"supabase": {
  "command": "mcp-filter",
  "args": [
    "run",
    "-t", "stdio",
    "--stdio-command", "npx",
    "--stdio-arg=-y",
    "--stdio-arg", "@supabase/mcp-server-supabase@latest",
    "--stdio-arg=--access-token",
    "--stdio-arg", "YOUR_TOKEN",
    "-a", "execute_sql,list_tables,get_project"
  ]
}

Or use uvx for faster, ephemeral execution:

"supabase": {
  "command": "uvx",
  "args": [
    "mcp-filter",
    "run",
    "-t", "stdio",
    "--stdio-command", "npx",
    "--stdio-arg=-y",
    "--stdio-arg", "@supabase/mcp-server-supabase@latest",
    "--stdio-arg=--access-token",
    "--stdio-arg", "YOUR_TOKEN",
    "-a", "execute_sql,list_tables,get_project"
  ]
}

Linear (stdio wrapping mcp-remote) – preserves OAuth browser flow:

"linear": {
  "command": "mcp-filter",
  "args": [
    "run",
    "-t", "stdio",
    "--stdio-command", "npx",
    "--stdio-arg=-y",
    "--stdio-arg", "mcp-remote",
    "--stdio-arg", "https://mcp.linear.app/sse",
    "-a", "get_issue,list_issues,create_issue,update_issue,create_comment"
  ]
}

Python MCP servers (uvx) - wrap a Python package without installing it globally:

"atlassian": {
  "command": "mcp-filter",
  "env": {
    "MF_STDIO_ENV": "JIRA_URL=https://your-instance.atlassian.net;JIRA_API_TOKEN=YOUR_API_TOKEN"
  },
  "args": [
    "run",
    "-t", "stdio",
    "--stdio-command", "uvx",
    "--stdio-arg", "mcp-atlassian",
    "-a", "jira_search,jira_get_issue"
  ]
}

Adjust auth-tokens/headers to match your environment; the filter never logs or exposes them.

Configuration Reference

CLI flags override environment variables prefixed with MF_. See .env.example for a template.

  • MF_TRANSPORT / -t: stdio (default) or http
  • MF_STDIO_COMMAND / MF_STDIO_ARGS: upstream binary + args
  • MF_STDIO_ENV / --stdio-env: explicit upstream environment (MF_STDIO_ENV uses semicolon-separated pairs; escape a value semicolon as \;). CLI entries replace, rather than merge with, MF_STDIO_ENV.
  • MF_HTTP_URL / MF_HTTP_HEADERS: SSE/HTTP endpoint and extra headers (key=value;Another=Value)
  • MF_ALLOW_TOOLS / -a: exact tool names (repeatable, or comma-separated)
  • MF_ALLOW_PATTERNS: regex patterns for tool names (repeatable, or comma-separated)
  • MF_DENY_PATTERNS / -d: regex patterns to block (repeatable, or comma-separated)
  • MF_RENAME_PREFIX / -p / --prefix: prefix exposed tool names (e.g., supabase_)
  • MF_INCLUDE_HEALTH_TOOL=1 / --health: enable built-in health tool (disabled by default)
  • MF_SHOW_TOKEN_ESTIMATES=1: enable token estimate logging (disabled by default)

FAQ

Can I expose more than one tool? Yes—pass multiple --allow-tool flags or use regex patterns. All exposed tools share the optional rename prefix.

Are my credentials safe? Yes. The filter never logs secrets passed as CLI arguments or headers.

What if the upstream goes down? Health checks surface the failure while the filter continues to reject new calls with a clear error.

Can I merge config files? Env + CLI merging is built in. For more complex setups, script the CLI invocation or add a thin wrapper that loads mf.toml and passes flags.

Advanced

Observability & Health

  • Rich-based structured logging annotates server name, transport, and exposed tool count.
  • Optional built-in health tool (disabled by default, ~500 tokens per server) returns upstream liveness and exposed tool names; output is safe JSON. Enable with MF_INCLUDE_HEALTH_TOOL=1 or --health.

Security Notes

  • Only tools explicitly allowlisted or matching an allow regex are exposed.
  • Deny-patterns apply last to ensure sensitive tools stay hidden.
  • Optional rename prefix avoids tool collisions when multiple filtered proxies run side-by-side.
  • Health payload (when enabled) avoids secrets—only structural metadata is emitted.
  • Credentials & secrets: Parsed credential values are never logged. Values supplied with --stdio-env may be visible in process listings, so prefer MF_STDIO_ENV for secrets.
  • Upstream subprocesses receive configured variables plus the MCP SDK's minimal process defaults (such as PATH and HOME), never the filter's full environment.
  • python and .py upstream commands continue to run with mcp-filter's current Python interpreter. Other commands are forwarded literally.
  • npx arguments are forwarded literally; mcp-filter does not inject --prefer-offline, so add that argument explicitly when required.

Requirements

  • Python 3.10+ (3.11 recommended)
  • mcp>=1.0.0 and fastmcp>=2.14.5 (installed automatically)
  • Upstream tool list is fixed per session; mid-run changes require restart
  • HTTP transport requires SSE-compatible upstream servers

Roadmap

v0.2.0 (Planned)

Schema Pruning — Additional 30-60% token reduction

  • Add --prune-schema [off|safe|aggressive] to strip non-essential schema fields
  • Safe mode: remove title, description, examples, default while preserving contract
  • Aggressive mode: strip all descriptions except top-level (≤140 chars)

Enhanced Error Handling

  • Return proper JSON-RPC error codes (e.g., -32602 for blocked tools)
  • Include helpful error details: {"public_tools": [...], "tool_requested": "...", "reason": "not_allowlisted"}

Collision-Safe Naming

  • Replace collision failures with deterministic suffixes (tool_name_{hash[:4]})
  • Log warnings but continue operation

Full JSON Schema Validation

  • Validate tool arguments locally before forwarding upstream
  • Provide clear, early error messages for invalid calls
  • Compile schemas at startup for performance

Structured Logging & Monitoring

  • Add --log-format [pretty|json] for production-friendly JSON lines
  • Add --redact-keys to automatically redact sensitive field names
  • Expose metrics for blocked calls, latency, and tool usage

Operational Controls

  • Add --timeout-ms (default: 120000) for per-call timeouts
  • Add --max-concurrency (default: 8) to limit parallel upstream calls

Resources & Prompts Filtering

  • Add --allow-resources and --allow-prompts (off by default)
  • Apply same pattern-based filtering as tools

Future Considerations

  • Multi-upstream mode: --upstream NAME=cmd... for managing multiple filtered servers
  • Compressor mode: meta-tools with on-demand forwarding (separate from filter mode)
  • Built-in deny presets: --deny-dangerous-verbs to block destructive operations by default

Testing

source .venv/bin/activate
python -m pytest

The suite exercises filtering precedence, rename collisions, schema validation, health handling, and call forwarding against a fake upstream server.

Metadata

Release files for mcp-filter 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-filter 0.2.0
File Size Uploaded Provenance
mcp_filter-0.2.0.tar.gz 3.3 MB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for mcp-filter 0.2.0
File Interpreter ABI Platform
mcp_filter-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 3.3 MB

Release files / mcp_filter-0.2.0.tar.gz

Download URL mcp_filter-0.2.0.tar.gz
Size 3.3 MB
Tags Source
SHA-256 checksum
How to use checksums
c6cd34a6c1cdbcc715ef477febebc8eeb6aa4c51b2af855b448636b1677ea523
BLAKE2b-256 checksum
How to use checksums
ef30d648ddc996dbac1ceee107fb73db2312178217abe7030d07b427810821d5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / mcp_filter-0.2.0-py3-none-any.whl

Download URL mcp_filter-0.2.0-py3-none-any.whl
Size 19.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
de7a3fab888f1529a6af8a8a1d2f501612d36bb121ad44ee639c75216e85bd75
BLAKE2b-256 checksum
How to use checksums
ab16fb111c0a1f2f12b6b17b172cefc08655cb98367825a554d2da24bf1ba71e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for mcp-filter 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

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