We have a standard MCP server deployment for internal tool use. The current logging is ad-hoc, per-server, and easy to bypass if a server is poorly configured.
I'm looking for a way to enforce a tamper-evident audit trail for all MCP transactions—client and server. This needs to capture the full request/response, timestamps, principal, and tool call parameters. The goal is to have a central, immutable record for forensic review in case of a suspected agent compromise or tool abuse.
Has anyone built a proxy, a wrapper, or modified an MCP server implementation to do this? The main challenges I see are intercepting the traffic without breaking TLS, handling diverse transport layers (stdio, HTTP), and ensuring the log itself is secure from agent manipulation.
—frank (mod)
stay on topic or stay off my board
Yeah, that's a tough one. The transport layer diversity is the real kicker. For stdio-based servers, I've had some luck with a wrapper script that sits between the client and server process. It pipes the stdio through a logging function that writes to a secured, append-only file descriptor before passing it along. It's brittle, but it works for the stdio case.
For HTTP, I ended up building a simple MITM proxy that terminates TLS, logs the full JSON-RPC payload (after decrypting), then re-establishes the connection to the actual server. You have to manage certs, but it gives you that central log. The trick is making sure the agent can't access the log sink, which means sending it to a separate, locked-down system.
Frankly, the MCP spec could really use a native auditing extension, where servers and clients could report to a designated audit endpoint as part of the handshake.
Injection? Where?
The transport layer issue is real. We handle it by treating the MCP server as an untrusted component and enforcing audit at the orchestration level.
For our homelab setup, we run every MCP server as a separate container. The container runtime (we use podman) pipes all stdout/stderr to a Fluentbit sidecar that forwards to a dedicated logging cluster. The agent never touches those logs. It's not perfect for HTTP, but for stdio servers it creates a clear boundary - the agent can't tamper with the container's log stream.
This moves the problem from "how to intercept" to "how to orchestrate securely", which might be a better fit for small teams anyway. You still have to parse the JSON-RPC from the log stream, but at least the capture is guaranteed.
--Emily
Bad goal. You can't guarantee a tamper-evident log from inside the same trust domain as the agent. If the agent is compromised, it owns the runtime.
The problem is your threat model. You're trying to audit a component the agent fully controls. Enclave? It can subvert it. Proxy? It can bypass it. Wrapper script? It can kill it.
Stop trying to instrument the server. Move the audit boundary. Treat the entire agent process as untrusted and audit its OS-level interactions instead. That's the only thing you can actually enforce.
Less is more.
Yeah, I feel you on this. We went through the same thing. We built a lightweight wrapper library in Rust that instruments the MCP client itself, not the server or transport. Every tool call passes through a logging module that writes to a local Unix socket connected to a separate, tiny audit daemon running under a different user. The daemon does the actual writing to an append-only file. It's not perfect, but it keeps the log sink out of the agent's direct control.
The biggest headache for us was the "principal" part. If your agent can be assumed compromised, then any identity it reports is suspect. We ended up tying the log entries to a host-level process ID and letting the audit daemon add its own timestamp from its own clock. That at least gives you a correlation point with system logs.
I can share the crate if you want to take a look - it's a bit rough, but it works for our stdio and SSE setups. Still figuring out the HTTP side cleanly.
build and break
You're right about the trust domain, and that's the core difficulty. But I think there's a middle ground.
Even a subverted agent operating within its own runtime still has to interact with the host OS through defined interfaces - syscalls, the filesystem, network. If you can't trust the agent's self-reporting, you have to lift the audit to that layer, like you said. The valuable record isn't the agent's narrative of events, but the OS's observation of its behavior.
The practical challenge becomes mapping those low-level OS events back to meaningful MCP semantics. If an agent process writes 2KB to a pipe, how do I know that was a `filesystem/read` call and not a `math/sqrt` call? That correlation needs external instrumentation, which brings us back to the wrapper or proxy problem, just at a different layer.
Maybe the solution is a combination: treat the agent as a black box and log its external interactions, but also require the MCP *server* to provide a separate, signed audit trail from its own perspective. You'd have two parallel logs to compare.
Test early, test often.