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.
Files changed (51) hide show
  1. package/README.md +187 -348
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +9 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.js +1 -0
  6. package/dist/index.js.map +1 -1
  7. package/package.json +1 -1
  8. package/dist/core/config-loader.d.ts +0 -22
  9. package/dist/core/config-loader.d.ts.map +0 -1
  10. package/dist/core/config-loader.js +0 -308
  11. package/dist/core/config-loader.js.map +0 -1
  12. package/dist/core/config-schema.d.ts +0 -1363
  13. package/dist/core/config-schema.d.ts.map +0 -1
  14. package/dist/core/config-schema.js +0 -139
  15. package/dist/core/config-schema.js.map +0 -1
  16. package/dist/core/server-manager.d.ts +0 -51
  17. package/dist/core/server-manager.d.ts.map +0 -1
  18. package/dist/core/server-manager.js +0 -149
  19. package/dist/core/server-manager.js.map +0 -1
  20. package/dist/features/discovery/config-init.d.ts +0 -37
  21. package/dist/features/discovery/config-init.d.ts.map +0 -1
  22. package/dist/features/discovery/config-init.js +0 -95
  23. package/dist/features/discovery/config-init.js.map +0 -1
  24. package/dist/features/discovery/discovery-handler.d.ts +0 -128
  25. package/dist/features/discovery/discovery-handler.d.ts.map +0 -1
  26. package/dist/features/discovery/discovery-handler.js +0 -629
  27. package/dist/features/discovery/discovery-handler.js.map +0 -1
  28. package/dist/features/discovery/initialization.d.ts +0 -126
  29. package/dist/features/discovery/initialization.d.ts.map +0 -1
  30. package/dist/features/discovery/initialization.js +0 -314
  31. package/dist/features/discovery/initialization.js.map +0 -1
  32. package/dist/features/discovery/search-phase.d.ts +0 -19
  33. package/dist/features/discovery/search-phase.d.ts.map +0 -1
  34. package/dist/features/discovery/search-phase.js +0 -76
  35. package/dist/features/discovery/search-phase.js.map +0 -1
  36. package/dist/features/discovery/simple-rag.d.ts +0 -30
  37. package/dist/features/discovery/simple-rag.d.ts.map +0 -1
  38. package/dist/features/discovery/simple-rag.js +0 -85
  39. package/dist/features/discovery/simple-rag.js.map +0 -1
  40. package/dist/features/filtering/filter.d.ts +0 -18
  41. package/dist/features/filtering/filter.d.ts.map +0 -1
  42. package/dist/features/filtering/filter.js +0 -43
  43. package/dist/features/filtering/filter.js.map +0 -1
  44. package/dist/features/overrides/override-manager.d.ts +0 -25
  45. package/dist/features/overrides/override-manager.d.ts.map +0 -1
  46. package/dist/features/overrides/override-manager.js +0 -67
  47. package/dist/features/overrides/override-manager.js.map +0 -1
  48. package/dist/utils/debug-logger.d.ts +0 -2
  49. package/dist/utils/debug-logger.d.ts.map +0 -1
  50. package/dist/utils/debug-logger.js +0 -33
  51. 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 servers expose tools, resources, and prompts to AI agents but you don't always want agents to have access to everything. `mcp-filter` sits between your MCP client and upstream server, filtering what gets through:
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
- - **Security** Block destructive operations (`delete_*`, `admin_*`) before they reach the agent
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
- ## Quick Start
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
- ```bash
32
- npm install -g mcp-filter
33
- # or use directly with npx (no install needed)
34
- ```
31
+ ## Quick Start
35
32
 
36
- ```bash
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
- # Filter a remote MCP server (HTTP)
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
- # Whitelist mode — only allow specific tools
44
- npx mcp-filter --include "browser_navigate" --include "browser_screenshot" -- npx @playwright/mcp
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
- ## Supported Transports
55
+ **Remote server** — filter tools from an HTTP MCP server:
48
56
 
49
- mcp-filter connects to upstream MCP servers via three transport types:
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
- | Transport | Flag | Use Case | Status |
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
- Transport is auto-detected: `--upstream-url` selects HTTP by default, `-- <command>` selects stdio. Override with `--transport`.
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
- ## Usage
92
+ For additional headers, use `--header "Key: Value"` (repeatable).
60
93
 
61
- ### Local Servers (Stdio)
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
- ```bash
64
- # Exclude specific tools
65
- npx mcp-filter --exclude "playwright*" -- npx @playwright/mcp
96
+ ## Examples: GitHub MCP Server
66
97
 
67
- # Include only specific tools (whitelist)
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
- # Rsync-style: exclude exceptions, then include category
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
- # Works with any local MCP server
74
- npx mcp-filter --exclude "debug*" -- node my-mcp-server.js
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
- ### Remote Servers (HTTP)
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
- ### Legacy SSE Servers
127
+ **Read-only agent** exclude all mutations, keep all reads:
96
128
 
97
- ```bash
98
- # SSE transport (deprecated — prefer HTTP for new deployments)
99
- npx mcp-filter --transport sse \
100
- --upstream-url https://mcp.example.com/sse \
101
- --exclude "dangerous_*"
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
- ## CLI Reference
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
- > **Note**: `--upstream-url` and `-- <command>` are mutually exclusive.
178
+ `merge_pull_request` matches `--exclude` first, so it's blocked. The remaining `pull_request_*` tools pass through.
135
179
 
136
- ## Filtering Modes
137
-
138
- ### Exclude Mode
180
+ ### Claude Code
139
181
 
140
- Block specific items, allow everything else:
182
+ Claude Code uses `claude mcp add` instead of JSON:
141
183
 
142
184
  ```bash
143
- npx mcp-filter --exclude "browser_close" --exclude "browser_evaluate" -- npx @playwright/mcp
144
- ```
145
-
146
- ### Include Mode (Whitelist)
147
-
148
- Allow only specified items, block everything else:
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
- ```bash
151
- npx mcp-filter --include "browser_navigate" --include "browser_screenshot" -- npx @playwright/mcp
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
- ### Rsync-Style Combination
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
- ```bash
159
- # Exclude specific tools, then include the rest of the category
160
- npx mcp-filter \
161
- --exclude "browser_close" \
162
- --exclude "browser_evaluate" \
163
- --include "browser_*" \
164
- -- npx @playwright/mcp
165
-
166
- # Evaluation:
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
- > **Order matters!** `--exclude "browser_close" --include "browser_*"` excludes `browser_close` then includes the rest. Reversing the order would include `browser_close` (it matches `browser_*` first).
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
- ### Default Behavior
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
- ## MCP Spec Conformance
236
+ ### Rsync-Style Ordering
198
237
 
199
- mcp-filter is a fully conformant MCP proxy. It transparently forwards all MCP protocol features while applying filters only to list/call operations.
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
- "mcpServers": {
332
- "playwright-safe": {
333
- "command": "npx",
334
- "args": [
335
- "mcp-filter",
336
- "--exclude", "browser_close",
337
- "--exclude", "browser_evaluate",
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
- **Remote server with auth:**
371
-
372
- ```json
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
- </details>
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 (Claude Desktop, Cursor, VS Code), each argument must be its own array element. The shell splits arguments for you — JSON doesn't.
264
+ In JSON configs, each argument must be its own array element. The shell splits arguments for you — JSON doesn't.
407
265
 
408
- **WRONG:**
409
- ```json
266
+ ```jsonc
267
+ // WRONG
410
268
  "args": ["mcp-filter", "--include browser_*", "--", "npx", "server"]
411
- ```
412
269
 
413
- **CORRECT:**
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 order: include matches first, exclude never fires
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
- ## Programmatic API
298
+ ## Transports
445
299
 
446
- mcp-filter exports its core modules for use in custom tooling:
447
-
448
- ```typescript
449
- import { FilterEngine } from 'mcp-filter/filter';
450
- import { ProxyServer } from 'mcp-filter/proxy';
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,CAoKtD"}
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",