npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcp-filter-proxy

v0.1.3

Published

MCP proxy that wraps any server to filter exposed tools and bridge across different transports (stdio, SSE, HTTP)

Readme

mcp-filter-proxy

A generic MCP proxy that wraps any MCP server and supports filtering which tools are exposed to clients and what protocol to use to expose that MCP server.

Supports all transport types (stdio, SSE, HTTP) and can bridge between them. For example, wrapping a stdio server and exposing it over HTTP.

How it works

flowchart LR
    A[AI Client] --> B["mcp-filter-proxy<br/>(allowlist filter)"] --> C[Your MCP Server]

The proxy sits between your AI client and an MCP server. Anything not on its allowlist is hidden: it never appears in the listing the LLM sees and any direct call to it is rejected, so the LLM is never aware it exists. The same allowlist mechanism applies to tools, resources, and prompts, each with its own env var. Leave an allowlist unset to forward that kind unfiltered.

The proxy is otherwise transparent: it advertises full client capabilities to the wrapped server and relays server-initiated requests through to your real client.

Connection Modes

The upstream MCP server is reached using one of three transports. You can set MCP_FILTER_PROXY_UPSTREAM_TRANSPORT explicitly, or leave it unset and let the proxy autodetect.

| Mode | When it activates | How it connects | | --- |----------------------------------------------------------------------------| --- | | stdio | stdio, or a command (not a URL) as the first positional argument | Spawns the wrapped command as a child process and talks over stdio | | SSE | sse, or autodetected when the URL path ends in /sse | Connects over Server-Sent Events (for older servers not yet on Streamable HTTP) | | HTTP | http, or autodetected when the first positional argument is an http(s) URL without an /sse path | Connects over Streamable HTTP |

Using with AI Tools

Any MCP-Compatible Tool

Tools such as Claude Desktop, Cursor, and Windsurf use a JSON config file. Add an entry under mcpServers.

To filter a local stdio server (the most common case), pass the wrapped command as CLI args. The proxy spawns it as a child process and communicates over stdio:

{
  "mcpServers": {
    "filtered-filesystem": {
      "command": "npx",
      "args": [
        "-y", "mcp-filter-proxy",
        "npx", "-y", "another-mcp-server"
      ],
      "env": {
        "MCP_FILTER_PROXY_ALLOWED_TOOLS": "read_file,list_directory,search_files"
      }
    }
  }
}

To wrap a remote server over Streamable HTTP, pass its URL as the first argument (any positional argument that parses as an http(s) URL is treated as the upstream server). The proxy re-exposes it as a stdio server to your client:

{
  "mcpServers": {
    "filtered-http-server": {
      "command": "npx",
      "args": ["-y", "mcp-filter-proxy", "http://my-server:3001/mcp"],
      "env": {
        "MCP_FILTER_PROXY_ALLOWED_TOOLS": "run_query,list_schemas"
      }
    }
  }
}

Leave MCP_FILTER_PROXY_ALLOWED_TOOLS/MCP_FILTER_PROXY_DENIED_TOOLS out entirely to allow all tools (useful when you only want the transport-bridging feature).

Claude Code

Use claude mcp add to register the server. The proxy config goes in --env flags, and the wrapped command comes after --:

claude mcp add --transport stdio filtered-filesystem \
  --env MCP_FILTER_PROXY_ALLOWED_TOOLS=read_file,list_directory \
  -- npx -y mcp-filter-proxy npx -y another-mcp-server

Hint: You can include --scope project to add the server only to the current project.

Environment Variables

All configuration is via environment variables.

Required

There is no single required variable, but you must give the proxy something to connect to as positional arguments: either a command to spawn (for a stdio upstream) or an http(s) URL (for a remote upstream). The transport is then autodetected unless you set MCP_FILTER_PROXY_UPSTREAM_TRANSPORT.

Optional

| Variable | Default | Description | | --- | --- | --- | | MCP_FILTER_PROXY_UPSTREAM_TRANSPORT | (auto) | Upstream transport: stdio, sse, or http. Leave unset to autodetect: a URL argument connects over Streamable HTTP, or SSE when its path ends in /sse, with fallback to the other variant; otherwise stdio. Set it to force a specific transport with no fallback | | MCP_FILTER_PROXY_HEADERS | {} | Extra headers to send to an http/sse upstream, as a JSON object (e.g. {"X-Api-Key":"${MY_KEY}"}). Values may reference env vars via ${VAR}, expanded at startup | | MCP_FILTER_PROXY_ALLOWED_TOOLS | (all) | Comma-separated list of tool-name globs to expose. Omit to allow everything | | MCP_FILTER_PROXY_DENIED_TOOLS | (none) | Comma-separated list of tool-name globs to hide. Everything else is exposed | | MCP_FILTER_PROXY_ALLOWED_RESOURCES | (all) | Comma-separated list of resource-name globs to expose. Disallowed resources are hidden from listings and resources/read of one is rejected. This also governs MCP-UI app/widget resources (e.g. Atlassian's Jira/Confluence widgets), which are exposed as ordinary resources | | MCP_FILTER_PROXY_DENIED_RESOURCES | (none) | Comma-separated list of resource-name globs to hide. Everything else is exposed | | MCP_FILTER_PROXY_ALLOWED_PROMPTS | (all) | Comma-separated list of prompt-name globs to expose. Disallowed prompts are hidden from listings and prompts/get of one is rejected | | MCP_FILTER_PROXY_DENIED_PROMPTS | (none) | Comma-separated list of prompt-name globs to hide. Everything else is exposed | | MCP_FILTER_PROXY_EXPOSE_TRANSPORT | stdio | How to expose the proxy to clients: stdio or http | | MCP_FILTER_PROXY_EXPOSE_PORT | 8808 | Port for the HTTP expose server | | MCP_FILTER_PROXY_EXPOSE_HOST | 127.0.0.1 | Bind address for the HTTP expose server |

Upstream authentication (SSE/HTTP)

| Variable | Default | Description | | --- | --- | --- | | MCP_FILTER_PROXY_UPSTREAM_AUTH | auto | auto performs an interactive browser OAuth flow when the upstream replies 401; none disables it | | MCP_FILTER_PROXY_AUTH_TOKEN | — | Pre-obtained credential sent as Authorization: <scheme> <token>. Takes precedence over OAuth (good for CI/headless). The value is sent verbatim after the scheme | | MCP_FILTER_PROXY_AUTH_SCHEME | bearer | Scheme for MCP_FILTER_PROXY_AUTH_TOKEN: bearer or basic. For basic, the token must be the base64 of username:password (e.g. printf 'user:pass' \| base64) | | MCP_FILTER_PROXY_OAUTH_CALLBACK_PORT | 8661 | Loopback port the OAuth redirect callback listens on. Left at the default, a busy port is skipped for the next free one (so concurrent first-time sign-ins don't collide); set it explicitly to pin a single port | | MCP_FILTER_PROXY_OAUTH_SCOPE | openid email profile | OAuth scope to request. Scopes the server advertises (via WWW-Authenticate or protected-resource metadata) take precedence; this is the fallback when it advertises none. Some servers (e.g. Atlassian) only return resources/prompts to a scoped token, so the default is non-empty | | MCP_FILTER_PROXY_OAUTH_RESOURCE | — | RFC 8707 resource (audience) to bind the token to. Omit to send none unless the server's protected-resource metadata supplies one. Useful for audience-bound tokens or multi-tenant servers | | MCP_FILTER_PROXY_OAUTH_CLIENT_NAME | MCP Filter Proxy | client_name advertised during dynamic client registration | | MCP_FILTER_PROXY_OAUTH_STORE_DIR | ~/.mcp-auth/mcp-filter-proxy-<version>/oauth | Directory where OAuth tokens and registration are cached |

Filtering with globs

Each kind (tools, resources, prompts) can be filtered with either an allowlist (ALLOWED_*, expose only matches) or a denylist (DENIED_*, expose everything except matches).

| Pattern | Matches | | --- | --- | | read_file | exactly read_file | | read_* | names starting with read_ | | *_file | names ending with _file | | *search* | names containing search | | [Rr]ead_file | either read_file or Read_file (a [...] set matches one character from it) |

Matching is case-sensitive, so a [...] character class is the way to accept more than one casing of a letter.

Authenticating to OAuth-protected upstreams

The proxy also supports interactive browser-based OAuth flows that some remote MCP servers require (for example the Atlassian MCP server).

The first run opens a browser for you to authorize. Tokens are cached under ~/.mcp-auth/mcp-filter-proxy-<version>/oauth (keyed per server URL, and versioned so an upgrade starts clean) and refreshed automatically on later runs, so you are not prompted again. To force re-authentication, delete that directory.

If you already hold a token (or run headless), set MCP_FILTER_PROXY_AUTH_TOKEN to skip the browser entirely, or set MCP_FILTER_PROXY_UPSTREAM_AUTH=none to disable upstream auth.

Finding tool names

To see which tools a server exposes, ask your AI assistant to list them, or use the MCP Inspector:

npx @modelcontextprotocol/inspector npx -y another-mcp-server

Open the Tools tab, then copy the names you want into MCP_FILTER_PROXY_ALLOWED_TOOLS.

Development

git clone https://github.com/SecretX33/mcp-filter-proxy.git
cd mcp-filter-proxy
pnpm install
pnpm build

The compiled server is written to dist/index.js. Run in watch mode during development:

pnpm dev

Run the test suite with pnpm test.

License

MIT