Skip to content
verify mcp Beta VerifyMCP is currently in beta. If you notice any issues, get in touch and we’ll put it right.

MCP observability after logging was deprecated

By Stuart · 17 Aug 2026 · 7 min read
MCP observability after logging was deprecated, VerifyMCP

MCP observability is the work of knowing what a Model Context Protocol server is doing at run time: which tools get called, whether they succeed, how long they take, and which protocol revision the caller speaks. Under the current spec revision, none of that arrives through the protocol itself. It comes from the same telemetry stack you run for the rest of your services.

MCP did have a logging feature: a server could send structured log messages to the client over the protocol, and the client could set a level with logging/setLevel. As of 2026-07-28 that feature is deprecated by SEP-2577, and logging/setLevel is gone from the revision entirely.

The deprecated features registry names the migration in one line: log to stderr for stdio transports, and use OpenTelemetry for observability.

The practical effect is that server visibility now sits outside the protocol. If you already run a server with standard telemetry, little changes. If your only visibility came through the client, you have to add it.

Why was protocol-level logging deprecated?

The general-purpose tooling already covered the same ground, and the protocol channel had limits the tooling does not.

Protocol logging could only ever reach the client on the other end of one connection. It could not span a request that fanned out to three upstream APIs, could not correlate with the traces your platform already collects, and could not be read at all when nobody was connected. It also put log volume on the same channel as protocol traffic.

The stateless rework in the same revision made the case stronger. With no session, “set the log level for this connection” no longer describes anything coherent.

What should an MCP server emit instead?

The answer differs by transport.

For a stdio server, standard output belongs to the protocol. Writing anything to stdout that is not a protocol message corrupts the stream, which is the single most common cause of a server that “starts and then hangs”. Log to stderr, which the host captures and which cannot break framing.

For a remote server, you are running a normal HTTP service and should instrument it like one. OpenTelemetry gives you traces, metrics and logs under one set of conventions, and the useful unit of work is the tool call.

A good deal of that arrives without you writing any instrumentation code. MCP’s remote transport is HTTP, and OpenTelemetry’s zero-code instrumentation hooks the libraries an application already uses, so inbound requests, outbound calls and database queries produce spans on their own. OpenTelemetry lists zero-code support for .NET, Go, Java, JavaScript, PHP and Python. What it cannot see is MCP itself: every request is a POST to the same endpoint, so the tool name, the outcome and the protocol revision are attributes you have to add.

A span per tool call, at minimum, carrying:

  • the tool name, which is the highest-cardinality thing you actually want to group by
  • the outcome, separating a protocol error from a tool that ran and returned an error result, because those have different causes
  • duration, and the duration of each upstream call inside it
  • the authenticated subject, as an opaque identifier rather than an email
  • the declared protocol revision the request arrived with, which is how you find out you still have clients on 2025-03-26

That last one is worth setting up before you need it. Deprecations land on a twelve-month clock, and knowing which revisions your live traffic actually speaks turns a migration decision into a lookup.

Any OTLP-compatible backend will take the result. If you want one you can run yourself for nothing, SigNoz is open source, OpenTelemetry-native, and ingests OTLP directly rather than through a vendor SDK. We have no affiliation with it.

What does monitoring an MCP server look like in practice?

The health question for an MCP server is not “is the process up”. A server can be up, reachable and completely useless to an agent.

Four checks worth having, in the order they tend to fail:

  1. Does it still answer server/discover? Under 2026-07-28 this replaced the initialize handshake, and it is the cheapest liveness probe that proves the server is speaking MCP rather than merely returning HTTP 200. Our own transport check is essentially this, run against every declared endpoint in the registry.
  2. Has the tool list changed? Alert on the diff, not the count. A deploy that silently drops a tool breaks every agent depending on it, and nothing else in your stack will notice.
  3. Are tool schemas still valid? A malformed schema makes a tool unusable by a model while the endpoint stays perfectly healthy.
  4. Is the auth challenge still correct? A 401 that stops carrying its WWW-Authenticate header leaves conforming clients unable to discover how to authenticate, and looks fine to an uptime monitor.

Those are the same properties we score from the outside, and there is a reason to run them yourself: we cannot see past an authentication challenge. For an auth-gated server, your own monitoring is the only thing checking the surface behind the login.

How does observability relate to the trust score?

Indirectly but measurably, through stability. We probe declared endpoints repeatedly, and a server that intermittently fails to complete a handshake scores worse than one that answers consistently. Instrumentation does not raise a score by existing. It is how you find out why the score moved.

The two views measure different things. We measure the outside of your server on a schedule; your telemetry measures the inside continuously. Where they disagree, the gap is usually a network path, a TLS renewal, or an edge cache rather than the application itself.

What about MCP client-side observability?

If you operate agents that consume MCP servers, the interesting metrics are on your side: which tools the model actually chose, how often a call failed and was retried, and how much of your context window tool descriptions are consuming. How much that costs depends entirely on your host, since some forward every description on every turn while others cache them, filter them, or expose only a selected subset. So measure your actual prompt usage rather than assuming; a server shipping verbose descriptions may be costing you on every call or almost nothing, and the only way to know is to look.

That is also a security surface, since descriptions are untrusted text the model reads. Recording the description hash per server version gives you a way to notice when a server you depend on quietly changes what it tells your model to do.

Where to go next

With observability outside the protocol, an MCP server has no built-in visibility. What you get is whatever you instrument. A span per tool call and the protocol revision on every request cover most of it, with argument values left out unless you have classified them as safe to record.