Skip to main content
Glama
kais-radwan

mcp-tool-filter

by kais-radwan
README.md
# mcp-tool-filter

A generic MCP proxy that filters which tools are exposed from a remote MCP server. Reduces context window token usage by only loading the tools you actually need.

## The Problem

Remote MCP servers (Linear, GitHub, Notion, etc.) expose **all** their tools to your AI coding assistant. A single server can blast 20,000+ tokens of tool definitions into your context window — even if you only use 5 of them. That's 10% of your context gone before you type a single message.

## The Solution

`mcp-tool-filter` sits between your AI client and the remote MCP server as a lightweight stdio proxy. It fetches all tools from upstream but only exposes the ones you whitelist. Everything else is filtered out, saving thousands of tokens per conversation.

```
┌─────────────┐     stdio      ┌──────────────────┐     HTTP      ┌──────────────┐
│  Claude Code │ ◄────────────► │  mcp-tool-filter  │ ◄──────────► │  Remote MCP   │
│  (6 tools)   │                │  (filters tools)  │              │  (29 tools)   │
└─────────────┘                └──────────────────┘              └──────────────┘
```

## Quick Start

### Interactive Setup (Recommended)

```bash
npx mcp-tool-filter add
```

This walks you through:
1. Enter the upstream MCP server URL
2. Confirm the server name
3. Authenticate (if needed — opens browser automatically)
4. Select which tools to expose from the full list
5. Choose where to save the config

### Manual Setup

Add to your `.mcp.json`:

```json
{
  "mcpServers": {
    "linear": {
      "command": "npx",
      "args": [
        "-y", "mcp-tool-filter",
        "--url", "https://mcp.linear.app/mcp",
        "--tools", "get_issue", "update_issue", "create_issue"
      ]
    }
  }
}
```

Or with the Claude Code CLI:

```bash
claude mcp add linear -- npx -y mcp-tool-filter \
  --url https://mcp.linear.app/mcp \
  --tools get_issue update_issue create_issue
```

## Usage

```
mcp-tool-filter --url <upstream-mcp-url> [--name <server-name>] [--tools <tool1> <tool2> ...]
```

### Options

| Flag | Required | Description |
|------|----------|-------------|
| `--url` | Yes | The upstream MCP server URL |
| `--tools` | No | Space-separated list of tool names to expose. If omitted, all tools are passed through |
| `--name` | No | Server name for token storage. Auto-derived from URL if not provided |

### Updating Tool Selection

Need to add or remove tools from an existing server? Run `update` with the server name from your `.mcp.json`:

```bash
npx mcp-tool-filter update linear
```

This will:
1. Read the existing config from your `.mcp.json`
2. Connect to the upstream server
3. Show all available tools with your current selection pre-checked
4. Update the config with your new selection

### Pass-through Mode

Omit `--tools` to proxy all tools without filtering (useful if you only need the OAuth/auth handling):

```json
{
  "mcpServers": {
    "linear": {
      "command": "npx",
      "args": ["-y", "mcp-tool-filter", "--url", "https://mcp.linear.app/mcp"]
    }
  }
}
```

## Authentication

`mcp-tool-filter` handles OAuth automatically:

1. **First run**: The upstream server returns 401, the proxy opens your browser for OAuth login, waits for the callback, and saves the tokens
2. **Subsequent runs**: Stored tokens are reused automatically — no browser needed

Tokens are persisted in `~/.mcp-tool-filter/<server-name>.json`.

To re-authenticate, delete the token file:

```bash
rm ~/.mcp-tool-filter/linear.json
```

### Servers Without OAuth

If the upstream server doesn't require authentication, the proxy connects directly — no OAuth flow needed.

## Examples

### Linear (6 tools instead of 29)

```json
{
  "mcpServers": {
    "linear": {
      "command": "npx",
      "args": [
        "-y", "mcp-tool-filter",
        "--url", "https://mcp.linear.app/mcp",
        "--tools", "get_issue", "update_issue", "create_issue",
        "list_issues", "list_issue_labels", "get_project"
      ]
    }
  }
}
```

### Multiple Filtered Servers

```json
{
  "mcpServers": {
    "linear": {
      "command": "npx",
      "args": [
        "-y", "mcp-tool-filter",
        "--url", "https://mcp.linear.app/mcp",
        "--tools", "get_issue", "update_issue"
      ]
    },
    "another-server": {
      "command": "npx",
      "args": [
        "-y", "mcp-tool-filter",
        "--url", "https://another-mcp-server.com/mcp",
        "--tools", "read_document", "search"
      ]
    }
  }
}
```

## How It Works

1. Starts as a **stdio** MCP server (what your AI client connects to)
2. Connects to the upstream server via **Streamable HTTP** (with SSE fallback)
3. On `tools/list` — fetches all tools from upstream, returns only the allowed ones
4. On `tools/call` — forwards the call to upstream, returns the response unchanged
5. Handles OAuth automatically with token persistence

## Transport Support

| Transport | Status |
|-----------|--------|
| Streamable HTTP | Supported (preferred) |
| Server-Sent Events (SSE) | Supported (fallback) |
| stdio upstream | Not supported (use for local servers directly) |

## Compatibility

Works with any MCP client that supports stdio servers:

- Claude Code
- Claude Desktop
- Cursor
- Windsurf
- Any MCP-compatible client

## Development

```bash
git clone https://github.com/kais-radwan/mcp-tool-filter.git
cd mcp-tool-filter
npm install
npm run build
```

Test locally:

```json
{
  "mcpServers": {
    "linear": {
      "command": "node",
      "args": [
        "/path/to/mcp-tool-filter/dist/index.js",
        "--url", "https://mcp.linear.app/mcp",
        "--tools", "get_issue"
      ]
    }
  }
}
```

## License

MIT