Configuration & Environment
The server emits no logs, no metrics, and no audit trail of tool calls, so an incident cannot be investigated and a compromise cannot be detected. Pair with the sensitive-data-exposure rules when adding logs: do not log raw request bodies, headers, or PII — see MCP-202 (secrets in responses), MCP-251 (PII in logs), and MCP-306 (auth headers before auth check).
Not a vulnerability in the usual sense — the server is not directly *exploitable* because of missing logs. But without logs you cannot tell whether it was already exploited. In an LLM-driven system where tool calls are generated non-deterministically, the log *is* the reconstruction.
MCP servers are frequently written as "just a script" and inherit that script's logging posture: `print` if lucky, nothing if not. In a session where the model made 200 tool calls, there is often no way after the fact to answer "what did my agent actually do to my GitHub account last Tuesday?"
@server.tool() |
def delete_file(path: str) -> None: |
os.remove(path) # no record that this happened |
import structlog |
log = structlog.get_logger() |
@server.tool() |
def delete_file(path: str, ctx: Context) -> None: |
log.info("tool.delete_file", path=path, principal=ctx.principal.id) |
os.remove(path) |
We grep for tool handlers with side effects (filesystem writes, network calls, database mutations) that contain no logging statement. This is a code-smell rule — false-positive rate is higher than for other rules.
See the full threat catalog for every documented detection.
MCPSafe runs this check — and every other rule in the catalog — on any MCP server you paste in.
Scan now