Zuplo
Model Context Protocol

Audit Every Tool Call Your Agents Make to MCP Servers

Martyn DaviesMartyn Davies
June 30, 2026
5 min read

A direct connection to a third-party MCP server leaves no record of what your agents called. Route it through a gateway and every tool call is logged, attributed to an identity, and ready to ship to your SIEM.

Your agents talk to the Stripe MCP server, Linear, GitHub, an internal server a teammate stood up last week. Ask which tools they called yesterday, under which identity, and whether any of those calls failed, and on a direct connection the honest answer is nothing. The agent talks straight to the upstream, and the only record is whatever that upstream chooses to keep, in a console you do not control.

For a security review, “the Stripe integration did it” is not an answer. You need the tool, the caller, the time, and the result, for every call.

Use this approach if you're:
  • Running agents against third-party MCP servers like Stripe, Linear, or GitHub
  • Asked by security or compliance to show what your agents actually called
  • Trying to attribute a tool call to a real identity, not a shared service credential

Direct connections leave no trail

When an agent connects straight to an MCP server, the tool calls cross a boundary you have no view into. The server returns its tools through tools/list, the agent picks one, the call runs, and none of it is attributed to a caller you can name on your side. If the upstream logs anything, it logs the shared credential the integration authenticates with, not the person or agent that triggered the call. The trail stops at the server’s edge, which belongs to someone else.

Route through a gateway

The Zuplo MCP Gateway fronts an upstream MCP server with a virtual server of your own: a gateway-side endpoint that maps to one upstream and is what your agents connect to instead of reaching the upstream directly. Every request the agent makes crosses the gateway, so the gateway sees and records each one.

This is the same choke point that lets you allowlist the tools you trust and bind tokens to one server. Once traffic flows through a point you own, that point can write down what happened.

The gateway emits structured events for the moments that matter, grouped into three families:

Event family What it captures
mcp_request The route boundary: whether the gateway accepted or rejected a request
capability_invocation The actual tool, prompt, and resource calls and their results
auth_event The OAuth lifecycle, from token issued to token validated

Each tool call your agent makes is a capability_invocation. That is the record the rest of this post is about.

Every tool call in one dashboard

In the portal, open Observability, then Analytics, then the MCP tab. At the account level it aggregates across every project with MCP routes; inside a single project it scopes to that project. The MCP section appears once the first MCP request is recorded, so there is nothing to switch on.

The Zuplo MCP Gateway analytics dashboard showing the Capabilities panel, with tool calls ranked by volume alongside error rate and p95 latency, above the Consumers panel, where calls are attributed to individual identities by email.

The dashboard reads as a set of panels, each answering one question:

Panel Question it answers
Capabilities Which tools, prompts, and resources get called the most, fail the most, and run the slowest
Consumers Which identities are driving the calls
MCP Methods Which MCP methods are in play: tools/call, tools/list, resources/read
Clients Which client apps are connecting, by name and kind
Failure Origins Whether a failure came from the gateway, the upstream, or the client
JSON-RPC Error Codes / Reason Codes The exact error and reason codes behind the failed calls

Capabilities is the per-tool view, sortable by any column, with a Type column separating tools from prompts and resources. It is the direct answer to “which tools did our agents call, and how often.”

Who made the call

The dashboard is useful because the underlying records carry identity. Three fields land on every entry:

  • operationId, the route the call hit.
  • upstreamServerId, the upstream the call was bound for, shown in the Server column of the Capabilities panel.
  • subjectId, the authenticated caller, listed in the Consumers panel as an email where one exists.

So “which identity tried to call create_refund” is a question with an answer. On a direct connection the upstream sees one shared credential for all of it; through the gateway each call is tied to the subject that authenticated through the OAuth flow. That is the difference between a log and an audit trail.

Failures and denials

A call that never succeeds is often the one you most want to see. The gateway sorts every result into one of seven color-coded outcome classes. The four you reach for first (the other three cover partial, cancelled, and connect_required states):

Outcome What happened
success The call completed
denied The gateway rejected it at the boundary, before the upstream saw it
application_error The upstream returned an MCP-level error inside a 200 response
failure The call never got a clean response, a transport or operational fault rather than an error the upstream chose to return

A failureOrigin field then tells you whether the gateway, the upstream, or the client was the source. So when an agent reaches for a tool you filtered off the route, the blocked call is not silence, it is a denied event with the subject attached, and you can ask who tried it.

Send the logs to your SIEM

The dashboard is the fast read. For retention and compliance you want the events in the system you already audit against, and the same MCP events feed Zuplo’s standard logging pipeline: Datadog, Splunk, AWS CloudWatch, Google Cloud Logging, New Relic, Sumo Logic, Loki, Dynatrace, and VMware Log Insight, plus OpenTelemetry for traces and logs in OTLP format.

The OAuth lifecycle events are recorded as audit entries, at info severity, for compliance review. The gateway is deliberate about what it leaves out: bearer tokens, authorization codes, client secrets, and customer request bodies are never written to a log entry, so shipping the audit trail to a third party does not ship your secrets with it.

MCP Gateway analytics

The full panel reference: every event family, the dimensions you can group by, and the outcome classes the dashboard reports.

Decide visibility up front

We run our own agents’ access to third-party servers through virtual servers. The first thing the dashboard told us was which tools the agents actually lean on, which is rarely the list you would guess. The audit trail is a byproduct of routing through a point you own, the same point that governs shadow MCP connections and scopes what each agent can reach. You do not bolt logging on afterward. You get it the moment the traffic stops going direct.

Public beta

Get an audit trail for your agents' MCP tool calls

The Zuplo MCP Gateway fronts any upstream MCP server and records every tool call, attributed to an identity, in the analytics dashboard and your existing log sinks.

  • Per-tool-call analytics in the portal
  • Every call attributed to a subject
  • Ships to Datadog, Splunk, OpenTelemetry, and more