mcp-filter 0.6.0 → 1.0.1

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 (59) hide show
  1. package/CHANGELOG.md +29 -3
  2. package/README.md +399 -97
  3. package/dist/core/config-loader.d.ts +22 -0
  4. package/dist/core/config-loader.d.ts.map +1 -0
  5. package/dist/core/config-loader.js +308 -0
  6. package/dist/core/config-loader.js.map +1 -0
  7. package/dist/core/config-schema.d.ts +1363 -0
  8. package/dist/core/config-schema.d.ts.map +1 -0
  9. package/dist/core/config-schema.js +139 -0
  10. package/dist/core/config-schema.js.map +1 -0
  11. package/dist/core/server-manager.d.ts +51 -0
  12. package/dist/core/server-manager.d.ts.map +1 -0
  13. package/dist/core/server-manager.js +149 -0
  14. package/dist/core/server-manager.js.map +1 -0
  15. package/dist/features/discovery/config-init.d.ts +37 -0
  16. package/dist/features/discovery/config-init.d.ts.map +1 -0
  17. package/dist/features/discovery/config-init.js +95 -0
  18. package/dist/features/discovery/config-init.js.map +1 -0
  19. package/dist/features/discovery/discovery-handler.d.ts +128 -0
  20. package/dist/features/discovery/discovery-handler.d.ts.map +1 -0
  21. package/dist/features/discovery/discovery-handler.js +629 -0
  22. package/dist/features/discovery/discovery-handler.js.map +1 -0
  23. package/dist/features/discovery/initialization.d.ts +126 -0
  24. package/dist/features/discovery/initialization.d.ts.map +1 -0
  25. package/dist/features/discovery/initialization.js +314 -0
  26. package/dist/features/discovery/initialization.js.map +1 -0
  27. package/dist/features/discovery/search-phase.d.ts +19 -0
  28. package/dist/features/discovery/search-phase.d.ts.map +1 -0
  29. package/dist/features/discovery/search-phase.js +76 -0
  30. package/dist/features/discovery/search-phase.js.map +1 -0
  31. package/dist/features/discovery/simple-rag.d.ts +30 -0
  32. package/dist/features/discovery/simple-rag.d.ts.map +1 -0
  33. package/dist/features/discovery/simple-rag.js +85 -0
  34. package/dist/features/discovery/simple-rag.js.map +1 -0
  35. package/dist/features/filtering/filter.d.ts +18 -0
  36. package/dist/features/filtering/filter.d.ts.map +1 -0
  37. package/dist/features/filtering/filter.js +43 -0
  38. package/dist/features/filtering/filter.js.map +1 -0
  39. package/dist/features/overrides/override-manager.d.ts +25 -0
  40. package/dist/features/overrides/override-manager.d.ts.map +1 -0
  41. package/dist/features/overrides/override-manager.js +67 -0
  42. package/dist/features/overrides/override-manager.js.map +1 -0
  43. package/dist/index.js +73 -34
  44. package/dist/index.js.map +1 -1
  45. package/dist/logger.d.ts.map +1 -1
  46. package/dist/logger.js +3 -1
  47. package/dist/logger.js.map +1 -1
  48. package/dist/proxy.d.ts +21 -0
  49. package/dist/proxy.d.ts.map +1 -1
  50. package/dist/proxy.js +250 -41
  51. package/dist/proxy.js.map +1 -1
  52. package/dist/transport.d.ts.map +1 -1
  53. package/dist/transport.js +18 -1
  54. package/dist/transport.js.map +1 -1
  55. package/dist/utils/debug-logger.d.ts +2 -0
  56. package/dist/utils/debug-logger.d.ts.map +1 -0
  57. package/dist/utils/debug-logger.js +33 -0
  58. package/dist/utils/debug-logger.js.map +1 -0
  59. package/package.json +10 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.0.1] - 2026-03-15
9
+
10
+ ### Fixed
11
+ - Logger was writing info/success messages to stdout, corrupting MCP JSON-RPC stream and causing "not valid JSON" errors in clients like Cursor
12
+
13
+ ## [1.0.0] - 2026-03-14
14
+
15
+ ### Added
16
+ - `--help` and `--version` CLI flags
17
+ - CI workflow (GitHub Actions, Node 20/22/24)
18
+ - CONTRIBUTING.md for OSS contributors
19
+ - Test for "all items filtered out" empty-list edge case
20
+ - Tests for `--help`, `--version`, and invalid CLI args
21
+
22
+ ### Changed
23
+ - **Stable release**: Public API is now considered stable
24
+ - Version is now read from package.json at runtime (no longer hardcoded)
25
+ - List operations (`tools/list`, `resources/list`, `prompts/list`, `resources/templates/list`) now forward AbortSignal for cancellation support
26
+ - `fetchAllPages()` accepts AbortSignal and checks it between pages
27
+ - `resources/read` now forwards progress notifications (like `tools/call` and `prompts/get`)
28
+ - Reverse-direction handlers (`sampling/createMessage`, `roots/list`, `elicitation/create`) now forward cancellation signals
29
+
30
+ ### Fixed
31
+ - Cancellation not propagated during paginated list operations
32
+ - Missing signal forwarding in reverse-direction request handlers
33
+
8
34
  ## [0.6.0] - 2025-12-02
9
35
 
10
36
  ### Added
@@ -67,7 +93,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
67
93
  - ADR-003 documenting subprocess delegation pattern
68
94
  - Claude Code configuration with custom slash commands
69
95
 
70
- ## [0.3.0] - 2025-01-XX
96
+ ## [0.3.0] - 2025-09-15
71
97
 
72
98
  ### Added
73
99
  - Rsync-style pattern evaluation (first match wins)
@@ -76,13 +102,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
76
102
  ### Changed
77
103
  - Pattern matching now evaluates in order with first-match-wins semantics
78
104
 
79
- ## [0.2.0] - 2025-01-XX
105
+ ## [0.2.0] - 2025-09-01
80
106
 
81
107
  ### Added
82
108
  - Comprehensive documentation updates
83
109
  - Testing infrastructure with Vitest
84
110
 
85
- ## [0.1.0] - 2025-01-XX
111
+ ## [0.1.0] - 2025-08-15
86
112
 
87
113
  ### Added
88
114
  - Initial release
package/README.md CHANGED
@@ -1,119 +1,413 @@
1
- # mcp-filter
1
+ <p align="center">
2
+ <h1 align="center">mcp-filter</h1>
3
+ <p align="center">
4
+ MCP proxy that filters tools, resources, and prompts from upstream MCP servers using glob patterns.
5
+ <br />
6
+ Works with any MCP client. Supports local and remote servers.
7
+ </p>
8
+ </p>
9
+
10
+ <p align="center">
11
+ <a href="https://www.npmjs.com/package/mcp-filter"><img src="https://img.shields.io/npm/v/mcp-filter.svg?style=flat-square" alt="npm version" /></a>
12
+ <a href="https://github.com/baranovxyz/mcp-filter/actions"><img src="https://img.shields.io/github/actions/workflow/status/baranovxyz/mcp-filter/ci.yml?branch=main&style=flat-square&label=tests" alt="CI" /></a>
13
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License: MIT" /></a>
14
+ <a href="https://www.npmjs.com/package/mcp-filter"><img src="https://img.shields.io/node/v/mcp-filter?style=flat-square" alt="Node version" /></a>
15
+ <a href="https://modelcontextprotocol.io"><img src="https://img.shields.io/badge/MCP-spec%20conformant-green?style=flat-square" alt="MCP Spec" /></a>
16
+ </p>
17
+
18
+ ---
19
+
20
+ ## Why mcp-filter?
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:
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
2
28
 
3
- [![npm version](https://badge.fury.io/js/mcp-filter.svg)](https://www.npmjs.com/package/mcp-filter)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
29
+ ## Quick Start
5
30
 
6
- Filter tools, resources, and prompts from MCP servers. Control what AI agents can access.
31
+ ```bash
32
+ npm install -g mcp-filter
33
+ # or use directly with npx (no install needed)
34
+ ```
7
35
 
8
- ## Quick Start
36
+ ```bash
37
+ # Filter a local MCP server (stdio)
38
+ npx mcp-filter --exclude "browser_close" --exclude "browser_evaluate" -- npx @playwright/mcp
9
39
 
10
- Add to your MCP client config (Claude Desktop, Cursor, etc.):
40
+ # Filter a remote MCP server (HTTP)
41
+ npx mcp-filter --exclude "delete_*" --upstream-url https://mcp.notion.com/mcp
11
42
 
12
- ```json
13
- {
14
- "mcpServers": {
15
- "playwright-safe": {
16
- "command": "npx",
17
- "args": [
18
- "mcp-filter",
19
- "--exclude", "browser_close",
20
- "--exclude", "browser_evaluate",
21
- "--include", "browser_*",
22
- "--",
23
- "npx", "@playwright/mcp@latest"
24
- ]
25
- }
26
- }
27
- }
43
+ # Whitelist mode — only allow specific tools
44
+ npx mcp-filter --include "browser_navigate" --include "browser_screenshot" -- npx @playwright/mcp
28
45
  ```
29
46
 
30
- This allows all `browser_*` tools **except** `browser_close` and `browser_evaluate`.
47
+ ## Supported Transports
48
+
49
+ mcp-filter connects to upstream MCP servers via three transport types:
31
50
 
32
- ## Why?
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 |
33
56
 
34
- - **Security** - Block dangerous tools (eval, delete, admin operations)
35
- - **Control** - Whitelist only the tools your agent needs
36
- - **Works everywhere** - Proxy any MCP server (local or remote)
57
+ Transport is auto-detected: `--upstream-url` selects HTTP by default, `-- <command>` selects stdio. Override with `--transport`.
37
58
 
38
- ## Installation
59
+ ## Usage
60
+
61
+ ### Local Servers (Stdio)
39
62
 
40
63
  ```bash
41
- npx mcp-filter [options] -- <server-command>
42
- # or install globally
43
- npm install -g mcp-filter
64
+ # Exclude specific tools
65
+ npx mcp-filter --exclude "playwright*" -- npx @playwright/mcp
66
+
67
+ # Include only specific tools (whitelist)
68
+ npx mcp-filter --include "browser_navigate" --include "browser_screenshot" -- npx @playwright/mcp
69
+
70
+ # Rsync-style: exclude exceptions, then include category
71
+ npx mcp-filter --exclude "browser_close" --include "browser_*" -- npx @playwright/mcp
72
+
73
+ # Works with any local MCP server
74
+ npx mcp-filter --exclude "debug*" -- node my-mcp-server.js
44
75
  ```
45
76
 
46
- ## How Patterns Work
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
+ ```
47
94
 
48
- Use `--include` and `--exclude` with glob patterns:
95
+ ### Legacy SSE Servers
49
96
 
50
97
  ```bash
51
- --include "browser_*" # Allow all browser_* tools
52
- --exclude "browser_close" # Block browser_close
53
- --exclude "delete_*" # Block all delete_* tools
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_*"
54
102
  ```
55
103
 
56
- **Order matters!** Patterns are evaluated in order, first match wins:
104
+ ## CLI Reference
105
+
106
+ ```
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
+
134
+ > **Note**: `--upstream-url` and `-- <command>` are mutually exclusive.
135
+
136
+ ## Filtering Modes
137
+
138
+ ### Exclude Mode
139
+
140
+ Block specific items, allow everything else:
57
141
 
58
142
  ```bash
59
- # CORRECT: exclude first, then include
60
- --exclude "browser_close" --include "browser_*"
61
- # Result: browser_close blocked, other browser_* allowed
143
+ npx mcp-filter --exclude "browser_close" --exclude "browser_evaluate" -- npx @playwright/mcp
144
+ ```
62
145
 
63
- # WRONG: include first (exclude never matches!)
64
- --include "browser_*" --exclude "browser_close"
65
- # Result: ALL browser_* allowed (browser_* matches first)
146
+ ### Include Mode (Whitelist)
147
+
148
+ Allow only specified items, block everything else:
149
+
150
+ ```bash
151
+ npx mcp-filter --include "browser_navigate" --include "browser_screenshot" -- npx @playwright/mcp
152
+ ```
153
+
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:
157
+
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
+ ```
173
+
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).
175
+
176
+ ### Pattern Syntax
177
+
178
+ Patterns use glob syntax via [minimatch](https://github.com/isaacs/minimatch):
179
+
180
+ | Pattern | Matches |
181
+ |---------|---------|
182
+ | `browser_*` | All items starting with `browser_` |
183
+ | `*_admin` | All items ending with `_admin` |
184
+ | `test_*_debug` | Items like `test_foo_debug` |
185
+ | `exact_name` | Exact match only |
186
+ | `*` | Everything |
187
+
188
+ ### Default Behavior
189
+
190
+ | Configuration | Unmatched items are... |
191
+ |---------------|------------------------|
192
+ | No patterns | Allowed (passthrough) |
193
+ | `--exclude` only | Allowed |
194
+ | `--include` only | Excluded (whitelist mode) |
195
+ | Mixed | Excluded if any `--include` exists |
196
+
197
+ ## MCP Spec Conformance
198
+
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
66
313
  ```
67
314
 
68
- ## Configuration
315
+ **Managing servers:**
69
316
 
70
- ### JSON Config (Claude Desktop, Cursor, VS Code)
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
+ ```
71
322
 
72
- Each argument must be a **separate string** in the array:
323
+ </details>
324
+
325
+ ### Cursor IDE
326
+
327
+ Add to `.cursor/mcp.json` or `~/.cursor/mcp.json`:
73
328
 
74
329
  ```json
75
330
  {
76
331
  "mcpServers": {
77
- "my-server": {
332
+ "playwright-safe": {
78
333
  "command": "npx",
79
334
  "args": [
80
335
  "mcp-filter",
81
- "--exclude", "dangerous_*",
82
- "--include", "safe_*",
336
+ "--exclude", "browser_close",
337
+ "--exclude", "browser_evaluate",
338
+ "--include", "browser_*",
83
339
  "--",
84
- "npx", "your-mcp-server"
340
+ "npx", "@playwright/mcp@latest"
85
341
  ]
86
342
  }
87
343
  }
88
344
  }
89
345
  ```
90
346
 
91
- **Config file locations:**
92
- - Claude Desktop: `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS)
93
- - Cursor: `.cursor/mcp.json` or `~/.cursor/mcp.json`
347
+ <details>
348
+ <summary>More Cursor examples</summary>
94
349
 
95
- ### CLI Usage
350
+ **Whitelist mode:**
96
351
 
97
- ```bash
98
- # Local server (stdio)
99
- npx mcp-filter --exclude "admin_*" -- npx @playwright/mcp
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
+ ```
100
369
 
101
- # Remote server (HTTP)
102
- npx mcp-filter --exclude "delete_*" --upstream-url https://mcp.example.com/mcp
370
+ **Remote server with auth:**
103
371
 
104
- # With authentication header
105
- npx mcp-filter --exclude "admin_*" \
106
- --upstream-url https://api.example.com/mcp \
107
- --header "Authorization: Bearer token"
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
+ }
387
+ ```
388
+
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
+ }
108
400
  ```
109
401
 
110
402
  ## Common Mistakes
111
403
 
112
404
  ### JSON args must be separate strings
113
405
 
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.
407
+
114
408
  **WRONG:**
115
409
  ```json
116
- "args": ["mcp-filter", "--include browser_* --", "npx", "server"]
410
+ "args": ["mcp-filter", "--include browser_*", "--", "npx", "server"]
117
411
  ```
118
412
 
119
413
  **CORRECT:**
@@ -121,68 +415,76 @@ npx mcp-filter --exclude "admin_*" \
121
415
  "args": ["mcp-filter", "--include", "browser_*", "--", "npx", "server"]
122
416
  ```
123
417
 
124
- The shell splits arguments for you. JSON doesn't - you must split them manually.
418
+ mcp-filter detects this mistake and shows a corrective error message.
125
419
 
126
- mcp-filter detects this and shows a helpful error:
420
+ ### Pattern order matters
127
421
 
128
- ```
129
- Malformed argument: "--include browser_* --"
422
+ Put `--exclude` patterns **before** `--include` to create exceptions. First match wins.
130
423
 
131
- WRONG:
132
- "args": ["--include browser_* --", ...]
424
+ ```bash
425
+ # CORRECT: exclude first, then include the rest
426
+ --exclude "browser_close" --include "browser_*"
427
+ # Result: browser_close blocked, other browser_* allowed
133
428
 
134
- CORRECT:
135
- "args": ["--include", "browser_*", "--", ...]
429
+ # WRONG order: include matches first, exclude never fires
430
+ --include "browser_*" --exclude "browser_close"
431
+ # Result: ALL browser_* allowed including browser_close
136
432
  ```
137
433
 
138
- ### Pattern order matters
434
+ ### Two `--` separators in Claude Code
139
435
 
140
- Put `--exclude` patterns **before** `--include` to create exceptions:
436
+ When using `claude mcp add`, the first `--` separates Claude's options from the mcp-filter command. The second `--` separates mcp-filter's options from the upstream server command:
141
437
 
142
438
  ```bash
143
- # Block browser_close, allow other browser_* tools
144
- --exclude "browser_close" --include "browser_*"
439
+ claude mcp add my-server -- npx mcp-filter --exclude "dangerous_*" -- npx upstream-server
440
+ # ^^ ^^
441
+ # Claude's -- mcp-filter's --
145
442
  ```
146
443
 
147
- ## Options Reference
444
+ ## Programmatic API
148
445
 
149
- | Option | Description |
150
- |--------|-------------|
151
- | `--include <pattern>` | Include items matching pattern (whitelist) |
152
- | `--exclude <pattern>` | Exclude items matching pattern (blocklist) |
153
- | `--upstream-url <url>` | Connect to remote HTTP/SSE server |
154
- | `--transport <type>` | Transport: `stdio`, `http`, `sse` (auto-detected) |
155
- | `--header <header>` | HTTP header (format: `"Key: Value"`) |
156
- | `--help` | Show help |
446
+ mcp-filter exports its core modules for use in custom tooling:
157
447
 
158
- Options can be repeated. Patterns use glob syntax via [minimatch](https://github.com/isaacs/minimatch).
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
+ ```
159
454
 
160
- ## How It Works
455
+ ## Testing
161
456
 
162
- ```
163
- MCP Client → mcp-filter → Upstream MCP Server
164
-
165
- Filters tools/list
166
- Blocks excluded calls
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)
167
463
  ```
168
464
 
169
- 1. Proxies requests between your MCP client and upstream server
170
- 2. Filters `tools/list`, `resources/list`, `prompts/list` responses
171
- 3. Blocks calls to filtered items with error responses
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.
172
468
 
173
469
  ## Development
174
470
 
175
471
  ```bash
472
+ git clone https://github.com/baranovxyz/mcp-filter.git
473
+ cd mcp-filter
176
474
  pnpm install
177
475
  pnpm run build
178
476
  pnpm test
179
477
  ```
180
478
 
479
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines.
480
+
181
481
  ## Links
182
482
 
183
483
  - [npm package](https://www.npmjs.com/package/mcp-filter)
184
- - [GitHub](https://github.com/baranovxyz/mcp-filter)
484
+ - [GitHub repository](https://github.com/baranovxyz/mcp-filter)
485
+ - [Changelog](CHANGELOG.md)
486
+ - [MCP specification](https://modelcontextprotocol.io)
185
487
 
186
488
  ## License
187
489
 
188
- MIT
490
+ [MIT](LICENSE)
@@ -0,0 +1,22 @@
1
+ import { ConfigFile, ServerConfig, DiscoveryConfig, FilterConfig } from "../types.js";
2
+ import { ConfigValidationResult } from "./config-schema.js";
3
+ /**
4
+ * Loads and validates the config file from project root
5
+ * Returns config even if validation fails (for graceful error reporting)
6
+ */
7
+ export declare function loadConfigFile(configPath?: string): ConfigFile | null;
8
+ /**
9
+ * Validates config file structure
10
+ */
11
+ export declare function validateConfigFile(config: ConfigFile): void;
12
+ /**
13
+ * Merges CLI config with config file
14
+ * CLI args take precedence over config file values
15
+ * Returns validation errors if config file is invalid
16
+ */
17
+ export declare function mergeConfigs(cliConfig: FilterConfig | null, fileConfig: ConfigFile | null): {
18
+ servers: Map<string, ServerConfig>;
19
+ discovery: DiscoveryConfig;
20
+ validationErrors?: ConfigValidationResult;
21
+ };
22
+ //# sourceMappingURL=config-loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../src/core/config-loader.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAuC,MAAM,aAAa,CAAC;AAE3H,OAAO,EAAyB,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAEnF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAwDrE;AA8CD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CA0E3D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,YAAY,GAAG,IAAI,EAC9B,UAAU,EAAE,UAAU,GAAG,IAAI,GAC5B;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAAC,SAAS,EAAE,eAAe,CAAC;IAAC,gBAAgB,CAAC,EAAE,sBAAsB,CAAA;CAAE,CAyJ/G"}