mcp-fw
MCP servers can read your files, run processes, and access the network — all without asking.
mcp-fw is a firewall proxy that sits between Claude Desktop and MCP servers. It uses NAIL effect labels to control exactly what each server can do.
By default, mcp-fw allows tools subject to effect policy, but blocks MCP resources and prompts unless you explicitly opt in with allow_resources: true / allow_prompts: true.
Claude Desktop ←→ mcp-fw (proxy) ←→ MCP Server
↑
policy.yaml
allow: [FS, IO]
deny: [NET]
Why
MCP servers are powerful — but there's no built-in permission model. A filesystem server could silently make network calls. An "everything" server could spawn processes.
mcp-fw enforces boundaries:
| Effect | What it controls | Example |
|---|---|---|
FS |
File read/write | Read a file, list directory |
IO |
General I/O | stdin/stdout, echo |
NET |
Network access | HTTP requests, DNS |
PROC |
Process execution | Spawn subprocess, exec |
TIME |
Time/clock access | Get current time |
RAND |
Randomness | Generate random numbers |
Quick Start
pip install mcp-fw
Commands
Primary CLI:
mcp-fw run --config policy.yaml --server filesystem [--verbose] [--log-file /path/to/file.log]
mcp-fw inspect --config policy.yaml --server filesystem
mcp-fw status --server filesystem
mcp-fw stop --server filesystem
mcp-fw claude-remove [--server filesystem]
mcp-fw menubar --config policy.yaml
Legacy shorthand still supported:
mcp-fw --config policy.yaml --server filesystem [--verbose] [--log-file /path/to/file.log]
Dedicated menubar entry point:
mcp-fw-menubar --config policy.yaml
1. Write a policy
# policy.yaml
servers:
filesystem:
command: npx
args: ["@modelcontextprotocol/server-filesystem", "/tmp"]
allow: [FS, IO]
deny: [NET]
This lets the filesystem server read/write files (FS, IO) but blocks all network access (NET).
It also keeps MCP resources and prompts blocked unless you explicitly enable them.
2. Point Claude Desktop to the proxy
In claude_desktop_config.json:
{
"mcpServers": {
"filesystem-fw": {
"command": "mcp-fw",
"args": ["--config", "/path/to/policy.yaml", "--server", "filesystem"]
}
}
}
That's it. Claude Desktop now talks to mcp-fw, which filters tools before forwarding to the real server.
If your server uses MCP resources or prompts, opt in explicitly:
servers:
filesystem:
allow_resources: true
allow_prompts: true
To remove mcp-fw entries later:
mcp-fw claude-remove
# or only one server
mcp-fw claude-remove --server filesystem
3. See what happened
$ mcp-fw --config policy.yaml --server filesystem --verbose
2025-01-15 10:00:01 [INFO] Filtered 5 → 3 tools (allowed effects: ['FS', 'IO'])
2025-01-15 10:00:05 [WARNING] Blocked tool call: fetch_url
To stop a running proxy process:
mcp-fw stop --server filesystem
To inspect how NAIL classified tools before changing policy:
mcp-fw inspect --config policy.yaml --server filesystem
The output shows inferred effects, effective effects after overrides, whether an override changed classification, and the final allowed/blocked result.
Menubar App (macOS)
For a GUI experience:
pip install "mcp-fw[menubar]"
mcp-fw menubar --config policy.yaml
# or:
mcp-fw-menubar --config policy.yaml
A [FW] icon appears in your menubar:
[FW]
├── mcp-fw v0.2.8
├── ────────
├── ● filesystem
│ ├── Status: Running
│ ├── Checked = allowed, unchecked = blocked
│ ├── ────────
│ ├── [x] FS
│ ├── [x] IO
│ ├── [ ] NET ← toggle effects live
│ ├── [ ] PROC
│ ├── Stop Proxy
│ └── ...
├── ○ everything
├── ────────
├── Edit Policy YAML
├── View Logs...
├── ────────
├── Sync to Claude Desktop
├── Remove from Claude Desktop
└── Quit
- Toggle effects with checkboxes — checked means allowed, unchecked means blocked, and changes are written to
policy.yamlinstantly - Sync to Claude Desktop generates
claude_desktop_config.jsonentries automatically - Stop Proxy terminates a running proxy process for a server
- Remove from Claude Desktop deletes generated
*-fwentries fromclaude_desktop_config.json - View Logs opens a live log viewer
- Process monitor shows ● running / ○ stopped status per server
How It Works
1. Claude calls list_tools()
→ mcp-fw forwards to the real server
→ NAIL annotates each tool with effect labels
→ mcp-fw filters out tools that violate the policy
→ Claude only sees allowed tools
2. Claude calls a tool
→ mcp-fw checks if it's in the allowed set
→ Allowed: forward to server
→ Blocked: return error
The key insight: tools aren't just allowed or denied by name — they're classified by what they do (filesystem, network, process, etc.) using NAIL's effect system. This means mcp-fw can enforce policies on servers it has never seen before.
Policy Reference
servers:
my-server:
command: npx # server command
args: ["@org/server", "/tmp"] # command arguments
allow: [FS, IO] # permitted effects (empty = all)
deny: [NET] # blocked effects (overrides allow)
allow_resources: false # permit MCP resources API
allow_prompts: false # permit MCP prompts API
tool_overrides: # per-tool effect corrections
safe_fetch: [IO] # override NAIL's auto-detection
Rules:
allow: [](empty) = all effects permitted, thendenysubtractsallow: [FS, IO]= only these effects, thendenysubtractsdenyalways wins overallowallow_resourcesandallow_promptsare explicit opt-ins; by default they are blockedtool_overrideslets you correct NAIL's automatic effect detection for specific tools
License
MIT
Metadata
Release files for mcp-fw 0.2.8
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mcp_fw-0.2.8.tar.gz | 65.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_fw-0.2.8-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 111.9 kB
Release files / mcp_fw-0.2.8.tar.gz
| Download URL | mcp_fw-0.2.8.tar.gz |
|---|---|
| Size | 65.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7868974fc3fa92a9b024411195a28a357a83e919ff3cc1cb7b598b604b6da66f
|
|
BLAKE2b-256 checksum How to use checksums |
0e3a0ce3d8cd40f93fa53e7adeea51c02be96c5bcc3ee0ca60bf83545852786b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 13, 2026.
Transparency logRelease files / mcp_fw-0.2.8-py3-none-any.whl
| Download URL | mcp_fw-0.2.8-py3-none-any.whl |
|---|---|
| Size | 46.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
14d996b66ec36c79b23e9499016f58db1b8ed11d6f5479e643aaea1e0b3ba218
|
|
BLAKE2b-256 checksum How to use checksums |
5e5663fa11b069a1fbece7c700d585ef333195003ae3d60307e7ead2cbab2e27
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 13, 2026.
Transparency log