
mcp-funnel
io.github.chris-schra/mcp-funnel
MCP proxy that aggregates multiple servers with tool filtering and customization
Documentation
MCP Funnel
A Model Context Protocol (MCP) proxy server that aggregates multiple MCP servers into a single interface, enabling you to use tools from multiple sources simultaneously through Claude Desktop or Claude Code CLI.
๐ฏ Purpose
Most MCP servers expose all their tools with no filtering options, consuming valuable context space
MCP Funnel enables you to:
- Connect to multiple MCP servers simultaneously (GitHub, Memory, Filesystem, etc.)
- Fine-grained tool filtering: Hide specific tools that you don't need
- Pattern-based filtering: Use wildcards to hide entire categories of tools
- Reduce context usage: Significantly decrease token consumption by exposing only necessary tools
๐๏ธ Architecture
โโโโโโโโโโโโโโโโโโโโโโโโโโ
โ CLI (e.g. Claude Code) โ
โโโโโโโโฌโโโโโโโโโโโโโโโโโโ
โ MCP Protocol via stdio
โโโโโโโโผโโโโโโโ
โ MCP Funnel โ โ Filtering and dynamic discovery happens here
โโโโโโโโฌโโโโโโโ
โ
โโโโโดโโโโโโโฌโโโโโโโโโโฌโโโโโโโโโโ
โ โ โ โ
โโโโผโโโโโ โโโโโผโโโโ โโโโโผโโโโ โโโโโผโโโโ
โGitHub โ โMemory โ โFS โ โ ... โ โ Each exposes all tools
โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ โโโโโโโโโ
MCP Funnel:
- Connects to multiple MCP servers as a client
- Receives all tools from each server
- Applies your filtering rules
- Exposes only the filtered tools to clients
- Routes tool calls to the appropriate backend server
๐ Features
- Multi-Server Aggregation: Connect to any number of MCP servers
- Tool Namespacing: Automatic prefixing prevents naming conflicts (
github__create_issue
,memory__store_memory
) - Flexible Filtering: Show/hide tools using wildcard patterns
- Granular Control: Filter individual tools that servers don't allow you to disable
- Context Optimization: Reduce MCP tool context usage by 40-60% through selective filtering
- Custom Transports: Supports stdio-based MCP servers (Docker, NPX, local binaries)
- Server Log Prefixing: Clear identification of which server is logging what
- Dynamic Tool Discovery: Experimental feature for reducing initial context usage (see limitations)
- Core Tools Mode: Ultra-minimal context mode exposing only selected MCP Funnel tools with dynamic bridging (95%+ context reduction)
๐ก Why Use MCP Funnel?
The Context Problem
A typical MCP setup might expose:
- GitHub MCP: ~70 tools
- Memory MCP: ~30 tools
- Filesystem MCP: ~15 tools
- Total: 115+ tools consuming 40k tokens
Many of these tools are rarely used:
- Workflow management tools
- Team/organization tools
- Debug and diagnostic tools
- Dashboard interfaces
- Advanced embedding operations
Or to "speak" with chat:
Before
For this `.mcp.json` config
{ย ย "mcpServers": {
ย ย ย ย "memory": {
ย ย ย ย ย ย "command": "uv",
ย ย ย ย ย ย "args": ["--directory", "/Users/me/_mcp/mcp-memory-service", "run", "memory", "server"]
ย ย ย ย },
ย ย ย ย "context7": {
ย ย ย ย ย ย "command": "npx",
ย ย ย ย ย ย "args": ["-y", "@upstash/context7-mcp", "--api-key", "API_KEY"]
ย ย ย ย },
ย ย ย ย "code-reasoning": {
ย ย ย ย ย ย "command": "npx",
ย ย ย ย ย ย "args": ["-y", "@mettamatt/code-reasoning"]
ย ย ย ย },
ย ย ย ย "filesystem": {
ย ย ย ย ย ย "command": "npx",
ย ย ย ย ย ย "args": [
ย ย ย ย ย ย ย ย "-y",
ย ย ย ย ย ย ย ย "@modelcontextprotocol/server-filesystem",
ย ย ย ย ย ย ย ย "allowed/file/path"
ย ย ย ย ย ย ]
ย ย ย ย },
ย ย ย ย "github": {
ย ย ย ย ย ย "command": "docker",
ย ย ย ย ย ย "args": [
ย ย ย ย ย ย ย ย "run",
ย ย ย ย ย ย ย ย "-i",
ย ย ย ย ย ย ย ย "--rm",
ย ย ย ย ย ย ย ย "ghcr.io/github/github-mcp-server"
ย ย ย ย ย ย ],
ย ย ย ย ย ย "secretProviders": [
ย ย ย ย ย ย ย ย { "type": "dotenv", "config": { "path": ".env" } }
ย ย ย ย ย ย ]
ย ย ย ย }
ย ย }
}
```
> /context
โฟ โ โ โ โ โ โ โ โ โ โ Context Usage
โ โ โ โ โ โ โ โ โ โ claude-opus-4-1-20250805 โข 42k/200k tokens (21%)
โถ โถ โถ โถ โถ โถ โถ โถ โถ โถ โ MCP tools: 25.4k tokens (12.7%)
โถ โถ โถ โถ โถ โถ โถ โถ โถ โถ โ Messages: 96 tokens (0.0%)
After
> /context
โฟ โ โ โ โ โ โ โ โ โ โ Context Usage
โ โ โถ โถ โถ โถ โถ โถ โถ โถ claude-opus-4-1-20250805 โข 18k/200k tokens (9%)
โถ โถ โถ โถ โถ โถ โถ โถ โถ โถ โ MCP tools: 2.4k tokens (1.2%)
โถ โถ โถ โถ โถ โถ โถ โถ โถ โถ โ Messages: 96 tokens (0.0%)
Then use it:
> discover tool for code reasoning
โบ I'll help you discover the code reasoning tool.
โบ mcp-funnel - discover_tools_by_words (MCP)(words: "reasoning", enable: false)
โฟ Found 1 tools:
โ code-reasoning__code-reasoning: ๐ง A detailed tool for dynamic and reflective problem-solving through sequential thinking.
โฆ +29 lines (ctrl+o to expand)
โบ Found it! The code-reasoning__code-reasoning tool is available for detailed code analysis and reasoning. Let me enable it:
โบ mcp-funnel - load_toolset (MCP)(tools: ["code-reasoning__code-reasoning"])
โฟ Loaded 1 tools matching specified patterns
โบ The code reasoning tool is now enabled. It provides:
- Dynamic problem-solving through sequential thinking
- Ability to branch and explore alternatives
- Revision capabilities to correct earlier thinking
- Flexible thought progression that adapts as understanding deepens
You can now use this tool for analyzing code logic, understanding complex implementations, and working through programming challenges step-by-step.
๐ง Installation
Installing Custom Commands
MCP Funnel supports dynamically installing additional commands from npm packages. You can install commands globally to your user directory (~/.mcp-funnel/packages) where they'll be available across all your projects.
Using the manage_commands
tool
manage_commands
is a built-in MCP Funnel tool that installs, updates, and removes command packages without requiring a separate CLI wrapper. The tool is exposed by default as long as exposeCoreTools
is unset (or explicitly includes manage_commands
).
Example install request through bridge_tool_request
(Claude, Codex CLI, etc.):
{
"name": "manage_commands",
"arguments": {
"action": "install",
"package": "@awesome-org/mcp-command",
"version": "1.2.3"
}
}
Supported payload fields:
action
:install
,update
, oruninstall
(required).package
: npm package spec or previously installed command name (required).version
: optional version (install only) โ e.g.,"1.2.3"
.force
: boolean flag to reinstall even if already present (install only).removeData
: boolean flag to delete cached data (uninstall only).
Responses include structured details about the command, any discovered tools, and whether a hot reload succeeded. When running inside an MCP client you can call the tool directly; no additional CLI plumbing is necessary.
Command Discovery
User-installed commands are automatically discovered from ~/.mcp-funnel/packages/node_modules/
and loaded alongside built-in commands. They respect your configuration:
- If
commands.list
is specified, only whitelisted commands are loaded - Commands can be hidden using
hideTools
patterns - Tools from commands are filtered by
exposeTools
patterns
โ๏ธ Configuration
MCP Funnel supports two ways to specify configuration:
-
Implicit (default): Looks for
.mcp-funnel.json
in the current working directorynpx mcp-funnel # Uses ./.mcp-funnel.json
-
Explicit: Specify a custom config file path
npx mcp-funnel /path/to/config.json
-
User Base Config (merged automatically)
If present,
~/.mcp-funnel/.mcp-funnel.json
is merged with the project config. Project values override user base values. Arrays are replaced (no concatenation).
Create a .mcp-funnel.json
file in your project directory:
{
"servers": {
"github": {
"command": "docker",
"args": ["run", "-i", "--rm", "ghcr.io/github/github-mcp-server"],
"secretProviders": [{ "type": "dotenv", "config": { "path": ".env" } }]
},
"memory": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-memory"]
},
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/path/to/allowed/directory"
]
}
},
"hideTools": [
"github__list_workflow_runs",
"github__get_workflow_run_logs",
"memory__debug_*",
"memory__dashboard_*",
"github__get_team_members"
]
}
Passing secrets / environment variables example: GitHub MCP
Here's how simple it is to configure GitHub MCP with secure token handling:
.mcp-funnel.json:
{
"servers": {
"github": {
"transport": {
"type": "streamable-http",
"url": "https://api.githubcopilot.com/mcp/"
},
"auth": {
"type": "bearer",
"token": "${GITHUB_PERSONAL_ACCESS_TOKEN}"
},
"secretProviders": [
{ "type": "dotenv", "config": { "path": ".env" } }
]
}
}
}
.env:
GITHUB_PERSONAL_ACCESS_TOKEN=ghp_your_github_token_here
That's it! The secretProviders
automatically loads your token from .env
, keeping it secure and out of your config files.
Configuration Options
- servers: Record of MCP servers to connect to (server name as key)
- Key: Server name (used as tool prefix)
command
: Command to executeargs
: Command arguments (optional)env
: Environment variables (optional, deprecated - use secretProviders instead)secretProviders
: Array of secret provider configurations for secure environment variable management (recommended)
- defaultSecretProviders: Default secret providers applied to all servers (optional)
- defaultPassthroughEnv: Environment variables passed to all servers by default (optional)
- alwaysVisibleTools: Patterns for tools that are always exposed, bypassing discovery mode (optional)
- exposeTools: Include patterns for external tools to expose (optional)
- hideTools: Exclude patterns for external tools to hide (optional)
- exposeCoreTools: Include patterns for internal MCP Funnel tools (optional, defaults to all enabled)
alwaysVisibleTools vs exposeTools
- Use exposeTools alone when you want a tool visible at startup. No duplication in alwaysVisibleTools is needed for server-backed tools.
- Use alwaysVisibleTools when you want a server tool to bypass all gating (expose/hide, future pattern changes). It wins over hideTools. You do not need to repeat it in exposeTools.
- Commands: Command tools are exposed using their tool names directly (e.g.,
npm_lookup
,ts-validate
) or with wildcards (e.g.,npm_*
) in exposeTools/hideTools patterns.
Filtering Patterns
Patterns match against the prefixed tool names (serverName__toolName
) and support wildcards (*
):
Individual tools:
github__get_team_members
- Hide specific tool from GitHub servermemory__check_database_health
- Hide specific tool from Memory server
Wildcard patterns:
memory__dashboard_*
- All dashboard tools from Memory servergithub__debug_*
- All debug tools from GitHub server*__workflow_*
- All workflow-related tools from any servermemory__ingest_*
- All ingestion tools from Memory server*__list_*
- All list tools from any server
Common filtering examples:
"hideTools": [
"memory__dashboard_*", // Hide all dashboard tools from Memory
"memory__debug_*", // Hide all debug tools from Memory
"memory__ingest_*", // Hide ingestion tools from Memory
"github__get_team_members", // Hide specific GitHub tool
"github__*_workflow_*", // Hide workflow tools from GitHub
"*__list_*_artifacts" // Hide artifact listing tools from all servers
]
Note: Always use the server prefix (e.g., github__
, memory__
) to target specific servers' tools. Use *__
at the beginning to match tools from any server.
Core Tool Filtering
MCP Funnel includes internal tools for discovery and bridging. Control which core tools are exposed using exposeCoreTools
:
"exposeCoreTools": ["discover_*", "load_toolset"] // Only expose discovery tools and toolset loading
Available core tools:
discover_tools_by_words
- Search for tools by keywordsget_tool_schema
- Get input schema for toolsbridge_tool_request
- Execute tools dynamicallyload_toolset
- Load predefined tool patterns
If exposeCoreTools
is not specified, all core tools are enabled by default.
๐ Usage
With Claude Code CLI
Add to your configuration (e.g. path/to/your/project/.mcp.json
):
{
"mcpServers": {
"mcp-funnel": {
"command": "npx",
"args": ["-y", "mcp-funnel"]
}
}
}
This will use .mcp-funnel.json
from your current working directory. To use a custom config path:
{
"mcpServers": {
"mcp-funnel": {
"command": "npx",
"args": ["-y", "mcp-funnel", "/path/to/your/.mcp-funnel.json"]
}
}
}
With Google Gemini
Add to your configuration (e.g. path/to/your/project/.gemini/settings.json
):
{
"mcpServers": {
"mcp-funnel": {
"command": "npx",
"args": ["-y", "mcp-funnel"]
}
}
}
With Codex CLI
Add to your configuration (e.g. ~/.codex/config.toml
):
[mcp_servers.mcp-funnel]
command = "npx"
args = ["-y", "mcp-funnel"]
CLI Onboarding
Kick off the guided migration flow to consolidate existing CLI configs:
npx mcp-funnel init
This command:
- Scans typical configuration files for Claude Code/Gemini (
.mcp.json
,.gemini/settings.json
), Claude Desktop (~/.claude.json
), and Codex (~/.codex/config.toml
). - Lists every MCP server it finds and lets you pick which ones should move into
.mcp-funnel.json
. - Merges the selected servers into
.mcp-funnel.json
, creating the file with recommended defaults when needed. - Rewrites each client config so that only a single
mcp-funnel
entry remains, pointing at the merged configuration.
Every write is gated behind an explicit confirmation, so you can review the proposed changes before they land.
Example Prompts
Once configured, you can use natural language to interact with your aggregated tools:
"Load PRs for https://github.com/chris-schra/mcp-funnel"
This works seamlessly because MCP Funnel aggregates your GitHub server's tools with proper namespacing!
๐ฎ Tool Visibility Control
MCP Funnel provides a three-tier visibility system for managing which tools are exposed:
1. Always Visible Tools (alwaysVisibleTools
)
Tools matching these patterns are always exposed from startup, even when using the dynamic discovery pattern (empty allowlist). Perfect for critical tools you always want available.
{
"alwaysVisibleTools": [
"github__create_pull_request", // Always show this specific tool
"memory__store_*" // Always show all store operations
]
}
2. Discoverable Tools (exposeTools
)
When using an empty allowlist (exposeTools: []
), these tools are hidden initially but can be discovered and enabled dynamically via load_toolset
. When allowlisted in exposeTools
, they're visible from startup.
3. Hidden Tools (hideTools
)
Tools matching these patterns are never exposed, regardless of other settings.
Dynamic Discovery
To start with a minimal surface and enable tools on demand:
{
"exposeTools": [],
"alwaysVisibleTools": [],
"exposeCoreTools": [
"discover_*",
"get_tool_schema",
"load_toolset",
"bridge_tool_request"
]
}
Runtime flow:
- Search:
discover_tools_by_words
with keywords (e.g., "context7"). - Enable:
load_toolset
with explicit tool names or patterns (e.g., ["context7__resolve_library_id", "context7__get-library-docs"]). - Call: Use the enabled tools normally.
๐ Core Tools Mode (Ultra-Low Context)
Core Tools Mode allows you to expose only MCP Funnel's internal tools for dynamic discovery. When you set exposeCoreTools
to a minimal set, MCP Funnel can expose as few as 3 tools instead of 100+:
- discover_tools_by_words: Search for tools by keywords
- get_tool_schema: Get input schema for any tool
- bridge_tool_request: Execute any tool dynamically
How It Works
{
"servers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"secretProviders": [{ "type": "dotenv", "config": { "path": ".env" } }]
}
},
"exposeCoreTools": [
"discover_tools_by_words",
"get_tool_schema",
"bridge_tool_request"
]
}
Usage Examples
Simple workflow:
User: "Load PRs for https://github.com/chris-schra/mcp-funnel"
Claude: *Automatically discovers GitHub tools, gets schema, and executes via bridge*
Step-by-step workflow:
1. "Find tools for working with files"
โ Claude uses discover_tools_by_words
โ Returns: filesystem__read_file, filesystem__write_file, etc.
2. "Get the schema for filesystem__read_file"
โ Claude uses get_tool_schema
โ Returns: Input parameters and types
3. "Read the README.md file"
โ Claude uses bridge_tool_request
โ Executes: {"tool": "filesystem__read_file", "arguments": {"path": "README.md"}}
Benefits
- Full functionality: All tools remain accessible
- Smart discovery: Claude can find and use tools naturally
- Works today: No waiting for Claude Code updates
Trade-offs
- Less discoverable (tools aren't visible upfront)
- Slight overhead for discovery/schema steps
- Best for scenarios where you use <10% of available tools
๐ Dynamic Tool Discovery (Experimental)
MCP Funnel includes a discover_tools_by_words
tool that allows searching for tools by keywords. However, this feature currently has limited utility:
โ ๏ธ Current Limitations
Claude Code CLI does not support dynamic tool updates. Once a session starts, the tool list is fixed. This means:
- The
discover_tools_by_words
tool can find matching tools - It can "enable" them in MCP Funnel's internal state
- But Claude won't see newly enabled tools until you restart the session
We're eagerly waiting for these issues to be resolved:
- claude-code#7519 - Dynamic tool discovery support
- claude-code#4118 - Runtime tool updates
Once these features land, dynamic discovery will significantly reduce initial context usage by loading only the tools you need on-demand.
๐ Security Considerations
๐ Secret Management
MCP Funnel includes a secure secret management system that follows the principle of least privilege. Instead of exposing all environment variables to MCP servers, you can use secret providers to control exactly which secrets each server receives.
Quick example:
{
"secretProviders": [
{ "type": "dotenv", "path": ".env" }, // Load from .env files
{ "type": "process", "prefix": "MCP_" }, // Filter env vars by prefix
{ "type": "inline", "values": { ... } } // Define inline secrets
]
}
Key benefits:
- Minimal environment variable exposure to child processes
- Multiple provider types (dotenv, process, inline) with precedence rules
- Built-in security filtering to prevent credential leakage
- Centralized secret management across all your MCP servers
๐ See the complete Secret Management guide โ
Infrastructure Security
- Filesystem access: Be careful with filesystem server paths
- Docker permissions: Ensure proper Docker socket access if using containerized servers
- Network isolation: Consider running in isolated environments for sensitive operations
๐บ๏ธ Roadmap
- Connection retry logic for resilient server management
- Health monitoring and status reporting
- Graceful degradation when servers fail
- Structured logging with configurable levels
- Metrics and performance monitoring
- WebSocket transport support
- Full dynamic tool discovery (blocked on Claude Code CLI support)
๐ ๏ธ Development
For local development setup, debugging, and testing:
๐ See the Development Guide โ
๐ค Contributing
Contributions are welcome! Key areas needing work:
- Error handling: Make MCP Funnel resilient to server failures
- Testing: Add comprehensive test coverage for other clients than Claude Code
- Logging: Implement structured logging
๐ License
MIT - See LICENSE file in the repository root
mcp-funnel
npm install mcp-funnel