mcp-filter 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +187 -348
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/core/config-loader.d.ts +0 -22
- package/dist/core/config-loader.d.ts.map +0 -1
- package/dist/core/config-loader.js +0 -308
- package/dist/core/config-loader.js.map +0 -1
- package/dist/core/config-schema.d.ts +0 -1363
- package/dist/core/config-schema.d.ts.map +0 -1
- package/dist/core/config-schema.js +0 -139
- package/dist/core/config-schema.js.map +0 -1
- package/dist/core/server-manager.d.ts +0 -51
- package/dist/core/server-manager.d.ts.map +0 -1
- package/dist/core/server-manager.js +0 -149
- package/dist/core/server-manager.js.map +0 -1
- package/dist/features/discovery/config-init.d.ts +0 -37
- package/dist/features/discovery/config-init.d.ts.map +0 -1
- package/dist/features/discovery/config-init.js +0 -95
- package/dist/features/discovery/config-init.js.map +0 -1
- package/dist/features/discovery/discovery-handler.d.ts +0 -128
- package/dist/features/discovery/discovery-handler.d.ts.map +0 -1
- package/dist/features/discovery/discovery-handler.js +0 -629
- package/dist/features/discovery/discovery-handler.js.map +0 -1
- package/dist/features/discovery/initialization.d.ts +0 -126
- package/dist/features/discovery/initialization.d.ts.map +0 -1
- package/dist/features/discovery/initialization.js +0 -314
- package/dist/features/discovery/initialization.js.map +0 -1
- package/dist/features/discovery/search-phase.d.ts +0 -19
- package/dist/features/discovery/search-phase.d.ts.map +0 -1
- package/dist/features/discovery/search-phase.js +0 -76
- package/dist/features/discovery/search-phase.js.map +0 -1
- package/dist/features/discovery/simple-rag.d.ts +0 -30
- package/dist/features/discovery/simple-rag.d.ts.map +0 -1
- package/dist/features/discovery/simple-rag.js +0 -85
- package/dist/features/discovery/simple-rag.js.map +0 -1
- package/dist/features/filtering/filter.d.ts +0 -18
- package/dist/features/filtering/filter.d.ts.map +0 -1
- package/dist/features/filtering/filter.js +0 -43
- package/dist/features/filtering/filter.js.map +0 -1
- package/dist/features/overrides/override-manager.d.ts +0 -25
- package/dist/features/overrides/override-manager.d.ts.map +0 -1
- package/dist/features/overrides/override-manager.js +0 -67
- package/dist/features/overrides/override-manager.js.map +0 -1
- package/dist/utils/debug-logger.d.ts +0 -2
- package/dist/utils/debug-logger.d.ts.map +0 -1
- package/dist/utils/debug-logger.js +0 -33
- package/dist/utils/debug-logger.js.map +0 -1
package/README.md
CHANGED
|
@@ -19,159 +19,198 @@
|
|
|
19
19
|
|
|
20
20
|
## Why mcp-filter?
|
|
21
21
|
|
|
22
|
-
MCP
|
|
22
|
+
[GitHub MCP Server](https://github.com/github/github-mcp-server) exposes 79 tools — **~52,000 tokens** of context window per request. A PR review agent needs maybe 10 of them. The other 69 (gists, stars, security advisories, dependabot, discussions...) are noise that costs tokens and confuses tool selection.
|
|
23
23
|
|
|
24
|
-
-
|
|
25
|
-
- **Focus** — Whitelist only the tools an agent needs for its specific task
|
|
26
|
-
- **Cost** — Fewer tools in context means fewer tokens consumed per request
|
|
27
|
-
- **Flexibility** — Works with any MCP server, any MCP client, any transport
|
|
24
|
+
`mcp-filter` sits between your MCP client and upstream server, filtering what gets through:
|
|
28
25
|
|
|
29
|
-
|
|
26
|
+
- **Cost** — 79 tools → 10 tools = ~80% fewer tokens per request
|
|
27
|
+
- **Focus** — Fewer tools means better tool selection by the model
|
|
28
|
+
- **Security** — Block destructive operations (`delete_*`, `push_*`) before they reach the agent
|
|
29
|
+
- **Future-proof** — `--include` whitelists are immune to upstream adding new tools
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
npm install -g mcp-filter
|
|
33
|
-
# or use directly with npx (no install needed)
|
|
34
|
-
```
|
|
31
|
+
## Quick Start
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
# Filter a local MCP server (stdio)
|
|
38
|
-
npx mcp-filter --exclude "browser_close" --exclude "browser_evaluate" -- npx @playwright/mcp
|
|
33
|
+
Add mcp-filter to your MCP client config. No install needed — `npx` downloads it automatically.
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
npx mcp-filter --exclude "delete_*" --upstream-url https://mcp.notion.com/mcp
|
|
35
|
+
**Local server** — filter tools from a stdio MCP server:
|
|
42
36
|
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"playwright-safe": {
|
|
41
|
+
"command": "npx",
|
|
42
|
+
"args": [
|
|
43
|
+
"mcp-filter",
|
|
44
|
+
"--exclude", "browser_close",
|
|
45
|
+
"--exclude", "browser_evaluate",
|
|
46
|
+
"--include", "browser_*",
|
|
47
|
+
"--",
|
|
48
|
+
"npx", "@playwright/mcp@latest"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
45
53
|
```
|
|
46
54
|
|
|
47
|
-
|
|
55
|
+
**Remote server** — filter tools from an HTTP MCP server:
|
|
48
56
|
|
|
49
|
-
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"stripe-safe": {
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": [
|
|
63
|
+
"mcp-filter",
|
|
64
|
+
"--exclude", "create_refund",
|
|
65
|
+
"--exclude", "delete_*",
|
|
66
|
+
"--upstream-url", "https://mcp.stripe.com"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
50
72
|
|
|
51
|
-
|
|
52
|
-
|-----------|------|----------|--------|
|
|
53
|
-
| **Stdio** | `-- <command>` | Local servers spawned as subprocesses | Stable |
|
|
54
|
-
| **HTTP** | `--upstream-url <url>` | Remote servers via Streamable HTTP | Stable |
|
|
55
|
-
| **SSE** | `--transport sse --upstream-url <url>` | Legacy remote servers via Server-Sent Events | Deprecated |
|
|
73
|
+
**Remote server with authentication:**
|
|
56
74
|
|
|
57
|
-
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"mcpServers": {
|
|
78
|
+
"api-readonly": {
|
|
79
|
+
"command": "npx",
|
|
80
|
+
"args": [
|
|
81
|
+
"mcp-filter",
|
|
82
|
+
"--exclude", "write_*",
|
|
83
|
+
"--exclude", "delete_*",
|
|
84
|
+
"--upstream-url", "https://api.example.com/mcp",
|
|
85
|
+
"--authorization", "Bearer your-oauth-token"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
58
91
|
|
|
59
|
-
|
|
92
|
+
For additional headers, use `--header "Key: Value"` (repeatable).
|
|
60
93
|
|
|
61
|
-
|
|
94
|
+
This JSON format works with Cursor (`.cursor/mcp.json`), VS Code (`.vscode/mcp.json`), Claude Desktop (`claude_desktop_config.json`), and any MCP client that supports stdio servers.
|
|
62
95
|
|
|
63
|
-
|
|
64
|
-
# Exclude specific tools
|
|
65
|
-
npx mcp-filter --exclude "playwright*" -- npx @playwright/mcp
|
|
96
|
+
## Examples: GitHub MCP Server
|
|
66
97
|
|
|
67
|
-
|
|
68
|
-
npx mcp-filter --include "browser_navigate" --include "browser_screenshot" -- npx @playwright/mcp
|
|
98
|
+
The [GitHub MCP Server](https://github.com/github/github-mcp-server) is a good example of a large server that benefits from filtering. All examples below work with any MCP client that supports the JSON config format.
|
|
69
99
|
|
|
70
|
-
|
|
71
|
-
npx mcp-filter --exclude "browser_close" --include "browser_*" -- npx @playwright/mcp
|
|
100
|
+
**PR review agent** — whitelist only what's needed (79 → ~10 tools):
|
|
72
101
|
|
|
73
|
-
|
|
74
|
-
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"mcpServers": {
|
|
105
|
+
"github-pr-review": {
|
|
106
|
+
"command": "npx",
|
|
107
|
+
"args": [
|
|
108
|
+
"mcp-filter",
|
|
109
|
+
"--include", "pull_request_*",
|
|
110
|
+
"--include", "issue_*",
|
|
111
|
+
"--include", "get_file_contents",
|
|
112
|
+
"--include", "list_commits",
|
|
113
|
+
"--include", "search_code",
|
|
114
|
+
"--",
|
|
115
|
+
"github-mcp-server", "stdio"
|
|
116
|
+
],
|
|
117
|
+
"env": {
|
|
118
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your-token>"
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
75
123
|
```
|
|
76
124
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
# Filter a remote HTTP MCP server
|
|
81
|
-
npx mcp-filter --exclude "delete_*" --upstream-url https://mcp.notion.com/mcp
|
|
82
|
-
|
|
83
|
-
# With authentication headers
|
|
84
|
-
npx mcp-filter --exclude "admin_*" \
|
|
85
|
-
--upstream-url https://api.example.com/mcp \
|
|
86
|
-
--header "Authorization: Bearer your-token-here"
|
|
87
|
-
|
|
88
|
-
# Multiple headers
|
|
89
|
-
npx mcp-filter --exclude "write_*" \
|
|
90
|
-
--upstream-url https://api.example.com/mcp \
|
|
91
|
-
--header "Authorization: Bearer token" \
|
|
92
|
-
--header "X-Team-Id: engineering"
|
|
93
|
-
```
|
|
125
|
+
When GitHub adds new tools tomorrow, they won't leak through — only `pull_request_*`, `issue_*`, and the explicitly listed tools are visible.
|
|
94
126
|
|
|
95
|
-
|
|
127
|
+
**Read-only agent** — exclude all mutations, keep all reads:
|
|
96
128
|
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"mcpServers": {
|
|
132
|
+
"github-readonly": {
|
|
133
|
+
"command": "npx",
|
|
134
|
+
"args": [
|
|
135
|
+
"mcp-filter",
|
|
136
|
+
"--exclude", "create_*",
|
|
137
|
+
"--exclude", "update_*",
|
|
138
|
+
"--exclude", "delete_*",
|
|
139
|
+
"--exclude", "push_*",
|
|
140
|
+
"--exclude", "merge_*",
|
|
141
|
+
"--exclude", "fork_*",
|
|
142
|
+
"--exclude", "*_write",
|
|
143
|
+
"--",
|
|
144
|
+
"github-mcp-server", "stdio"
|
|
145
|
+
],
|
|
146
|
+
"env": {
|
|
147
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your-token>"
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
102
152
|
```
|
|
103
153
|
|
|
104
|
-
|
|
154
|
+
**PR tools without merge** — rsync-style, first match wins:
|
|
105
155
|
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"mcpServers": {
|
|
159
|
+
"github-pr-safe": {
|
|
160
|
+
"command": "npx",
|
|
161
|
+
"args": [
|
|
162
|
+
"mcp-filter",
|
|
163
|
+
"--exclude", "merge_pull_request",
|
|
164
|
+
"--include", "pull_request_*",
|
|
165
|
+
"--include", "list_pull_requests",
|
|
166
|
+
"--include", "search_pull_requests",
|
|
167
|
+
"--",
|
|
168
|
+
"github-mcp-server", "stdio"
|
|
169
|
+
],
|
|
170
|
+
"env": {
|
|
171
|
+
"GITHUB_PERSONAL_ACCESS_TOKEN": "<your-token>"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
106
176
|
```
|
|
107
|
-
mcp-filter [options] -- <upstream-command>
|
|
108
|
-
mcp-filter [options] --upstream-url <url>
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Filtering Options
|
|
112
|
-
|
|
113
|
-
| Flag | Description |
|
|
114
|
-
|------|-------------|
|
|
115
|
-
| `--exclude <pattern>` | Exclude items matching glob pattern (repeatable) |
|
|
116
|
-
| `--include <pattern>` | Include only items matching glob pattern (repeatable) |
|
|
117
|
-
|
|
118
|
-
### Transport Options
|
|
119
|
-
|
|
120
|
-
| Flag | Description |
|
|
121
|
-
|------|-------------|
|
|
122
|
-
| `--upstream-url <url>` | Connect to remote HTTP/SSE server |
|
|
123
|
-
| `--transport <type>` | Transport type: `stdio`, `http`, `sse` (auto-detected) |
|
|
124
|
-
| `--header <header>` | HTTP header as `"Key: Value"` (repeatable, HTTP/SSE only) |
|
|
125
|
-
| `--` | Separates mcp-filter options from upstream command (stdio only) |
|
|
126
|
-
|
|
127
|
-
### Other Options
|
|
128
|
-
|
|
129
|
-
| Flag | Description |
|
|
130
|
-
|------|-------------|
|
|
131
|
-
| `--help` | Show help message |
|
|
132
|
-
| `--version` | Show version number |
|
|
133
177
|
|
|
134
|
-
|
|
178
|
+
`merge_pull_request` matches `--exclude` first, so it's blocked. The remaining `pull_request_*` tools pass through.
|
|
135
179
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
### Exclude Mode
|
|
180
|
+
### Claude Code
|
|
139
181
|
|
|
140
|
-
|
|
182
|
+
Claude Code uses `claude mcp add` instead of JSON:
|
|
141
183
|
|
|
142
184
|
```bash
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
185
|
+
# Local server (two -- separators: first for Claude, second for mcp-filter)
|
|
186
|
+
claude mcp add playwright-safe -- \
|
|
187
|
+
npx mcp-filter \
|
|
188
|
+
--exclude "browser_close" \
|
|
189
|
+
--exclude "browser_evaluate" \
|
|
190
|
+
--include "browser_*" \
|
|
191
|
+
-- npx @playwright/mcp@latest
|
|
149
192
|
|
|
150
|
-
|
|
151
|
-
|
|
193
|
+
# Remote HTTP server (no second --)
|
|
194
|
+
claude mcp add stripe-safe -- \
|
|
195
|
+
npx mcp-filter \
|
|
196
|
+
--exclude "create_refund" \
|
|
197
|
+
--exclude "delete_*" \
|
|
198
|
+
--upstream-url https://mcp.stripe.com
|
|
152
199
|
```
|
|
153
200
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
Patterns are evaluated **in order** — first match wins. This lets you combine `--include` and `--exclude` for fine-grained control:
|
|
201
|
+
## Options
|
|
157
202
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
# browser_close → matches --exclude "browser_close" → EXCLUDED
|
|
168
|
-
# browser_evaluate → matches --exclude "browser_evaluate" → EXCLUDED
|
|
169
|
-
# browser_navigate → matches --include "browser_*" → included
|
|
170
|
-
# browser_screenshot → matches --include "browser_*" → included
|
|
171
|
-
# some_other_tool → no match, --include exists → excluded (whitelist mode)
|
|
172
|
-
```
|
|
203
|
+
| Option | Description |
|
|
204
|
+
|--------|-------------|
|
|
205
|
+
| `--exclude <pattern>` | Exclude items matching glob pattern (repeatable) |
|
|
206
|
+
| `--include <pattern>` | Include only items matching glob pattern (repeatable) |
|
|
207
|
+
| `--upstream-url <url>` | Connect to remote HTTP/SSE server (mutually exclusive with `--`) |
|
|
208
|
+
| `--transport <type>` | Transport type: `stdio`, `http`, `sse` (auto-detected) |
|
|
209
|
+
| `--authorization <value>` | Set Authorization header (e.g. `"Bearer token"`, HTTP/SSE only) |
|
|
210
|
+
| `--header <header>` | HTTP header as `"Key: Value"` (repeatable, HTTP/SSE only) |
|
|
211
|
+
| `--` | Separates mcp-filter options from upstream command (stdio only) |
|
|
173
212
|
|
|
174
|
-
|
|
213
|
+
## Filtering
|
|
175
214
|
|
|
176
215
|
### Pattern Syntax
|
|
177
216
|
|
|
@@ -185,7 +224,7 @@ Patterns use glob syntax via [minimatch](https://github.com/isaacs/minimatch):
|
|
|
185
224
|
| `exact_name` | Exact match only |
|
|
186
225
|
| `*` | Everything |
|
|
187
226
|
|
|
188
|
-
###
|
|
227
|
+
### Modes
|
|
189
228
|
|
|
190
229
|
| Configuration | Unmatched items are... |
|
|
191
230
|
|---------------|------------------------|
|
|
@@ -194,224 +233,41 @@ Patterns use glob syntax via [minimatch](https://github.com/isaacs/minimatch):
|
|
|
194
233
|
| `--include` only | Excluded (whitelist mode) |
|
|
195
234
|
| Mixed | Excluded if any `--include` exists |
|
|
196
235
|
|
|
197
|
-
|
|
236
|
+
### Rsync-Style Ordering
|
|
198
237
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
### Filtered Operations
|
|
202
|
-
|
|
203
|
-
| Operation | Behavior |
|
|
204
|
-
|-----------|----------|
|
|
205
|
-
| `tools/list` | Drains all pages, filters by name, returns filtered list |
|
|
206
|
-
| `tools/call` | Blocks excluded tools with `-32602 InvalidParams` |
|
|
207
|
-
| `resources/list` | Drains all pages, filters by name |
|
|
208
|
-
| `resources/templates/list` | Drains all pages, filters by name |
|
|
209
|
-
| `resources/read` | Forwarded (filtered resources won't appear in list) |
|
|
210
|
-
| `resources/subscribe` | Forwarded to upstream |
|
|
211
|
-
| `resources/unsubscribe` | Forwarded to upstream |
|
|
212
|
-
| `prompts/list` | Drains all pages, filters by name |
|
|
213
|
-
| `prompts/get` | Blocks excluded prompts with `-32602 InvalidParams` |
|
|
214
|
-
| `completion/complete` | Blocks if referenced prompt is filtered; forwards resource completions |
|
|
215
|
-
| `logging/setLevel` | Forwarded to upstream |
|
|
216
|
-
|
|
217
|
-
### Forwarded Notifications
|
|
218
|
-
|
|
219
|
-
| Direction | Notification | Description |
|
|
220
|
-
|-----------|-------------|-------------|
|
|
221
|
-
| Upstream → Downstream | `notifications/tools/list_changed` | Tool list changed |
|
|
222
|
-
| Upstream → Downstream | `notifications/resources/list_changed` | Resource list changed |
|
|
223
|
-
| Upstream → Downstream | `notifications/prompts/list_changed` | Prompt list changed |
|
|
224
|
-
| Upstream → Downstream | `notifications/resources/updated` | Resource content updated |
|
|
225
|
-
| Upstream → Downstream | `notifications/message` | Log messages |
|
|
226
|
-
| Downstream → Upstream | `notifications/roots/list_changed` | Client roots changed |
|
|
227
|
-
|
|
228
|
-
### Reverse-Direction Requests (Server → Client)
|
|
229
|
-
|
|
230
|
-
| Request | Description |
|
|
231
|
-
|---------|-------------|
|
|
232
|
-
| `sampling/createMessage` | Forwarded from upstream to downstream client |
|
|
233
|
-
| `roots/list` | Forwarded from upstream to downstream client |
|
|
234
|
-
| `elicitation/create` | Forwarded from upstream to downstream client |
|
|
235
|
-
|
|
236
|
-
### Protocol Features
|
|
237
|
-
|
|
238
|
-
| Feature | Support |
|
|
239
|
-
|---------|---------|
|
|
240
|
-
| **Capability gating** | Only advertises capabilities the upstream server supports |
|
|
241
|
-
| **Instructions forwarding** | Upstream server instructions passed through to downstream |
|
|
242
|
-
| **Pagination** | Drains all pages before filtering (max 100 pages) |
|
|
243
|
-
| **Progress notifications** | Forwarded on `tools/call`, `prompts/get`, `resources/read` |
|
|
244
|
-
| **Cancellation propagation** | Downstream abort triggers upstream `notifications/cancelled` |
|
|
245
|
-
| **Graceful shutdown** | SIGINT/SIGTERM cleanly close both transports |
|
|
246
|
-
| **Connection timeout** | 30-second timeout prevents hang on unreachable servers |
|
|
247
|
-
|
|
248
|
-
## Architecture
|
|
249
|
-
|
|
250
|
-
```
|
|
251
|
-
┌─────────────┐ ┌──────────────────────────────────────┐ ┌──────────────┐
|
|
252
|
-
│ MCP Client │ stdio │ mcp-filter │ stdio/ │ Upstream │
|
|
253
|
-
│ (Claude, │◄───────►│ │ HTTP/ │ MCP Server │
|
|
254
|
-
│ Cursor, │ │ ┌────────┐ ┌────────┐ ┌────────┐ │ SSE │ (Playwright, │
|
|
255
|
-
│ etc.) │ │ │ Server │→ │ Filter │→ │ Client │◄┼────────►│ Notion, │
|
|
256
|
-
│ │ │ └────────┘ └────────┘ └────────┘ │ │ etc.) │
|
|
257
|
-
└─────────────┘ └──────────────────────────────────────┘ └──────────────┘
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
**Four-layer design:**
|
|
261
|
-
|
|
262
|
-
1. **CLI** (`src/cli.ts`) — Parses arguments into a typed `FilterConfig`
|
|
263
|
-
2. **Transport Factory** (`src/transport.ts`) — Creates the appropriate client transport (stdio/HTTP/SSE)
|
|
264
|
-
3. **Filter Engine** (`src/filter.ts`) — Glob pattern matching via minimatch
|
|
265
|
-
4. **Proxy Server** (`src/proxy.ts`) — Dual-role MCP client + server with two-phase initialization
|
|
266
|
-
|
|
267
|
-
## Integration Guides
|
|
268
|
-
|
|
269
|
-
### Claude Code
|
|
270
|
-
|
|
271
|
-
```bash
|
|
272
|
-
# Add a filtered MCP server
|
|
273
|
-
claude mcp add playwright-safe -- \
|
|
274
|
-
npx mcp-filter \
|
|
275
|
-
--exclude "browser_close" \
|
|
276
|
-
--exclude "browser_evaluate" \
|
|
277
|
-
--include "browser_*" \
|
|
278
|
-
-- npx @playwright/mcp@latest
|
|
279
|
-
|
|
280
|
-
# Scopes: local (default), user (all projects), project (team-shared .mcp.json)
|
|
281
|
-
claude mcp add --scope user playwright-safe -- \
|
|
282
|
-
npx mcp-filter --include "browser_*" -- npx @playwright/mcp@latest
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
**Command structure:** first `--` separates Claude options from mcp-filter; second `--` separates mcp-filter options from the upstream command.
|
|
286
|
-
|
|
287
|
-
<details>
|
|
288
|
-
<summary>More Claude Code examples</summary>
|
|
289
|
-
|
|
290
|
-
**Read-only monitoring agent:**
|
|
291
|
-
|
|
292
|
-
```bash
|
|
293
|
-
claude mcp add browser-monitor -- \
|
|
294
|
-
npx mcp-filter \
|
|
295
|
-
--include "browser_navigate" \
|
|
296
|
-
--include "browser_snapshot" \
|
|
297
|
-
--include "browser_console_messages" \
|
|
298
|
-
--include "browser_network_requests" \
|
|
299
|
-
--include "browser_take_screenshot" \
|
|
300
|
-
-- npx @playwright/mcp@latest
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
**Testing agent (no destructive actions):**
|
|
304
|
-
|
|
305
|
-
```bash
|
|
306
|
-
claude mcp add browser-test -- \
|
|
307
|
-
npx mcp-filter \
|
|
308
|
-
--exclude "browser_close" \
|
|
309
|
-
--exclude "browser_tabs" \
|
|
310
|
-
--exclude "browser_evaluate" \
|
|
311
|
-
--include "browser_*" \
|
|
312
|
-
-- npx @playwright/mcp@latest
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
**Managing servers:**
|
|
316
|
-
|
|
317
|
-
```bash
|
|
318
|
-
claude mcp list # List all servers
|
|
319
|
-
claude mcp get playwright-safe # Show server details
|
|
320
|
-
claude mcp remove playwright-safe # Remove a server
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
</details>
|
|
324
|
-
|
|
325
|
-
### Cursor IDE
|
|
326
|
-
|
|
327
|
-
Add to `.cursor/mcp.json` or `~/.cursor/mcp.json`:
|
|
238
|
+
Patterns are evaluated **in order** — first match wins. This lets you combine `--include` and `--exclude` for fine-grained control:
|
|
328
239
|
|
|
329
240
|
```json
|
|
330
|
-
|
|
331
|
-
"
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
"--include", "browser_*",
|
|
339
|
-
"--",
|
|
340
|
-
"npx", "@playwright/mcp@latest"
|
|
341
|
-
]
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
}
|
|
241
|
+
"args": [
|
|
242
|
+
"mcp-filter",
|
|
243
|
+
"--exclude", "merge_pull_request",
|
|
244
|
+
"--include", "pull_request_*",
|
|
245
|
+
"--include", "list_pull_requests",
|
|
246
|
+
"--",
|
|
247
|
+
"github-mcp-server", "stdio"
|
|
248
|
+
]
|
|
345
249
|
```
|
|
346
250
|
|
|
347
|
-
<details>
|
|
348
|
-
<summary>More Cursor examples</summary>
|
|
349
|
-
|
|
350
|
-
**Whitelist mode:**
|
|
351
|
-
|
|
352
|
-
```json
|
|
353
|
-
{
|
|
354
|
-
"mcpServers": {
|
|
355
|
-
"playwright-readonly": {
|
|
356
|
-
"command": "npx",
|
|
357
|
-
"args": [
|
|
358
|
-
"mcp-filter",
|
|
359
|
-
"--include", "browser_navigate",
|
|
360
|
-
"--include", "browser_screenshot",
|
|
361
|
-
"--include", "browser_snapshot",
|
|
362
|
-
"--",
|
|
363
|
-
"npx", "@playwright/mcp@latest"
|
|
364
|
-
]
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
251
|
```
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
{
|
|
374
|
-
"mcpServers": {
|
|
375
|
-
"notion-safe": {
|
|
376
|
-
"command": "npx",
|
|
377
|
-
"args": [
|
|
378
|
-
"mcp-filter",
|
|
379
|
-
"--exclude", "delete_*",
|
|
380
|
-
"--exclude", "archive_*",
|
|
381
|
-
"--upstream-url", "https://mcp.notion.com/mcp",
|
|
382
|
-
"--header", "Authorization: Bearer your-token"
|
|
383
|
-
]
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
252
|
+
merge_pull_request → matches --exclude → EXCLUDED
|
|
253
|
+
pull_request_read → matches --include → included
|
|
254
|
+
list_pull_requests → matches --include → included
|
|
255
|
+
create_gist → no match, --include exists → excluded (whitelist mode)
|
|
387
256
|
```
|
|
388
257
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
### Any MCP Client
|
|
392
|
-
|
|
393
|
-
mcp-filter works with any MCP client that supports stdio servers. The upstream server can be local (stdio) or remote (HTTP/SSE):
|
|
394
|
-
|
|
395
|
-
```json
|
|
396
|
-
{
|
|
397
|
-
"command": "npx",
|
|
398
|
-
"args": ["mcp-filter", "--exclude", "pattern", "--", "npx", "your-mcp-server"]
|
|
399
|
-
}
|
|
400
|
-
```
|
|
258
|
+
> **Order matters!** `--exclude "merge_pull_request" --include "pull_request_*"` blocks merge then includes the rest. Reversing the order would include `merge_pull_request` (it matches `pull_request_*` first).
|
|
401
259
|
|
|
402
260
|
## Common Mistakes
|
|
403
261
|
|
|
404
262
|
### JSON args must be separate strings
|
|
405
263
|
|
|
406
|
-
In JSON configs
|
|
264
|
+
In JSON configs, each argument must be its own array element. The shell splits arguments for you — JSON doesn't.
|
|
407
265
|
|
|
408
|
-
|
|
409
|
-
|
|
266
|
+
```jsonc
|
|
267
|
+
// WRONG
|
|
410
268
|
"args": ["mcp-filter", "--include browser_*", "--", "npx", "server"]
|
|
411
|
-
```
|
|
412
269
|
|
|
413
|
-
|
|
414
|
-
```json
|
|
270
|
+
// CORRECT
|
|
415
271
|
"args": ["mcp-filter", "--include", "browser_*", "--", "npx", "server"]
|
|
416
272
|
```
|
|
417
273
|
|
|
@@ -424,11 +280,9 @@ Put `--exclude` patterns **before** `--include` to create exceptions. First matc
|
|
|
424
280
|
```bash
|
|
425
281
|
# CORRECT: exclude first, then include the rest
|
|
426
282
|
--exclude "browser_close" --include "browser_*"
|
|
427
|
-
# Result: browser_close blocked, other browser_* allowed
|
|
428
283
|
|
|
429
|
-
# WRONG
|
|
284
|
+
# WRONG: include matches first, exclude never fires
|
|
430
285
|
--include "browser_*" --exclude "browser_close"
|
|
431
|
-
# Result: ALL browser_* allowed including browser_close
|
|
432
286
|
```
|
|
433
287
|
|
|
434
288
|
### Two `--` separators in Claude Code
|
|
@@ -441,30 +295,13 @@ claude mcp add my-server -- npx mcp-filter --exclude "dangerous_*" -- npx upstre
|
|
|
441
295
|
# Claude's -- mcp-filter's --
|
|
442
296
|
```
|
|
443
297
|
|
|
444
|
-
##
|
|
298
|
+
## Transports
|
|
445
299
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
import { createTransport } from 'mcp-filter/transport';
|
|
452
|
-
import type { FilterConfig, TransportConfig } from 'mcp-filter/types';
|
|
453
|
-
```
|
|
454
|
-
|
|
455
|
-
## Testing
|
|
456
|
-
|
|
457
|
-
187+ tests across 15 test suites covering unit tests, integration tests with real MCP servers, and full spec compliance validation.
|
|
458
|
-
|
|
459
|
-
```bash
|
|
460
|
-
pnpm test # Run all tests
|
|
461
|
-
pnpm test:coverage # With coverage report
|
|
462
|
-
pnpm test tests/unit/ # Unit tests only (fast)
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
Tests run on **Node 20, 22, and 24** via GitHub Actions CI.
|
|
466
|
-
|
|
467
|
-
See [TESTING.md](TESTING.md) for the full testing guide.
|
|
300
|
+
| Transport | Flag | Use Case | Status |
|
|
301
|
+
|-----------|------|----------|--------|
|
|
302
|
+
| **Stdio** | `-- <command>` | Local servers spawned as subprocesses | Stable |
|
|
303
|
+
| **HTTP** | `--upstream-url <url>` | Remote servers via Streamable HTTP | Stable |
|
|
304
|
+
| **SSE** | `--transport sse --upstream-url <url>` | Legacy remote servers via Server-Sent Events | Deprecated |
|
|
468
305
|
|
|
469
306
|
## Development
|
|
470
307
|
|
|
@@ -476,13 +313,15 @@ pnpm run build
|
|
|
476
313
|
pnpm test
|
|
477
314
|
```
|
|
478
315
|
|
|
479
|
-
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
|
|
316
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines and [TESTING.md](TESTING.md) for the testing guide.
|
|
480
317
|
|
|
481
318
|
## Links
|
|
482
319
|
|
|
483
320
|
- [npm package](https://www.npmjs.com/package/mcp-filter)
|
|
484
321
|
- [GitHub repository](https://github.com/baranovxyz/mcp-filter)
|
|
485
322
|
- [Changelog](CHANGELOG.md)
|
|
323
|
+
- [Spec conformance](docs/spec-conformance.md)
|
|
324
|
+
- [Architecture decisions](docs/architecture-decisions.md)
|
|
486
325
|
- [MCP specification](https://modelcontextprotocol.io)
|
|
487
326
|
|
|
488
327
|
## License
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EAGb,MAAM,YAAY,CAAC;AAwCpB,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,YAAY,EAGb,MAAM,YAAY,CAAC;AAwCpB,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,YAAY,CA6KtD"}
|
package/dist/cli.js
CHANGED
|
@@ -98,6 +98,14 @@ export function parseArgs(args) {
|
|
|
98
98
|
headers[key] = value;
|
|
99
99
|
continue;
|
|
100
100
|
}
|
|
101
|
+
if (arg === "--authorization") {
|
|
102
|
+
const value = args[++i];
|
|
103
|
+
if (!value) {
|
|
104
|
+
throw new Error("--authorization requires a value (e.g. 'Bearer token')");
|
|
105
|
+
}
|
|
106
|
+
headers["Authorization"] = value;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
101
109
|
if (arg === "--help" || arg === "-h") {
|
|
102
110
|
throw new Error("help");
|
|
103
111
|
}
|
|
@@ -150,7 +158,7 @@ export function parseArgs(args) {
|
|
|
150
158
|
throw new Error(`--transport ${explicitTransport} requires --upstream-url. Use --transport stdio or omit --transport for command-based servers.`);
|
|
151
159
|
}
|
|
152
160
|
if (Object.keys(headers).length > 0) {
|
|
153
|
-
throw new Error("--header can only be used with --upstream-url");
|
|
161
|
+
throw new Error("--header/--authorization can only be used with --upstream-url");
|
|
154
162
|
}
|
|
155
163
|
transportConfig = {
|
|
156
164
|
type: "stdio",
|