MCPSafe.io
RegistryThreatsMethodologyDocsPricingScanSign in
MCPSafe.io

Security checks for MCP servers โ€” public packages and private repos, fast or deep.

Legal

Privacy PolicyCookie PolicyTerms of ServiceSecurity disclosure

Resources

State of MCP SecuritySupportSystem statusMade in Germany ๐Ÿ‡ฉ๐Ÿ‡ช

ยฉ 2026 MCPSafe. All rights reserved.

GDPR โ€” Privacy Policy
โ† Threat Catalog

Configuration & Environment

Destructive MCP Tool With No Audit Event

MEDIUMCWE: CWE-778Rule: MCP-284

An MCP tool performs an irreversible action (DELETE FROM, DROP TABLE, fs.unlink, shutil.rmtree, subprocess, HTTP DELETE/PUT/PATCH) and the file emits no audit-event identifier anywhere. There is no forensic trail of who did what to which resource โ€” the OWASP MCP Top 10:2025 MCP08 (Lack of Audit and Telemetry) gap, narrower than MCP-283.

What it is

An audit event is distinct from a generic invocation log: it records the security-relevant fields a forensic investigator needs (actor, action, target, outcome, request_id) in a tamper-evident sink that survives long enough to be subpoenaed. CWE-778 (Insufficient Logging) covers both โ€” but for destructive actions specifically, regulators (SOC2 CC7.2, GDPR Art. 30, HIPAA ยง164.312(b)) explicitly require an immutable record of every state change. A tool that deletes a record without an audit event is the difference between a defensible operations review and a compliance finding.

Why it matters for MCP

MCP tools are increasingly given destructive capabilities (database writes, file deletion, ticket closure, payment refunds) because that is where the agentic value lives. The same architecture means the destructive call sits behind layers of LLM reasoning โ€” when something goes wrong, the question is rarely "did the API work?" but "why did the model decide to call delete_record on this customer?" Without an audit event tying the call to a specific user_id, request_id, and tool argument set, post-incident review collapses into log archaeology. MCP-201 covers the pre-action confirmation gap; MCP-284 covers the post-action audit gap; both can co-fire on the same handler and both have distinct remediations.

Vulnerable example

example.py
1
from fastmcp import FastMCP
2
from acme.db import cursor
3
4
mcp = FastMCP("acme")
5
6
7
@mcp.tool()
8
def delete_user(user_id: str) -> dict:
9
    cursor.execute("DELETE FROM users WHERE id = %s", (user_id,))
10
    # No audit event โ€” the delete is silent.
11
    return {"deleted": user_id}

Secure example

example.py
1
from fastmcp import FastMCP
2
from acme.db import cursor
3
from acme.audit import audit_log
4
5
mcp = FastMCP("acme")
6
7
8
@mcp.tool()
9
def delete_user(user_id: str, actor: str, request_id: str) -> dict:
10
    cursor.execute("DELETE FROM users WHERE id = %s", (user_id,))
11
    audit_log(
12
        actor=actor,
13
        action="delete_user",
14
        target=user_id,
15
        outcome="success",
16
        request_id=request_id,
17
    )
18
    return {"deleted": user_id}

How MCPSafe detects this

MCPSafe fires when the file registers an MCP tool, contains a destructive sink (the MCP-201 sink list โ€” `fs.unlink`/`shutil.rmtree`/`DROP TABLE`/`DELETE FROM`/`TRUNCATE TABLE`/`UPDATE ... SET`/HTTP `DELETE`|`PUT`|`PATCH`/`subprocess.run|Popen|call`/`child_process.exec|execSync|spawn|spawnSync`), and contains no audit-event identifier file-wide. Allow-list silences: `audit_log`, `audit.log|write|emit|record`, `emit_audit`, `audit_event`, `auditLog`, `record_audit`, `track_event` / `trackEvent`, `capture_event` / `captureEvent`, `structured_log`. The rule co-fires with MCP-201 (no confirmation) when both gaps are present โ€” different remediations.

See the full threat catalog for every documented detection.

Further reading

  • OWASP MCP Top 10:2025 โ€” MCP08 Lack of Audit and Telemetry
  • CWE-778: Insufficient Logging
  • AICPA SOC2 CC7.2 โ€” System Monitoring

Scan an MCP server for this issue

MCPSafe runs this check โ€” and every other rule in the catalog โ€” on any MCP server you paste in.

Scan now