An agent calls your MCP server’s search_orders tool. The call fails, but the client only shows “internal error.” Was the upstream API down? Did the server reject a token? Did the response fail validation? Logging is how you answer those questions without asking the model to guess.
There is one wrinkle: MCP Logging, the protocol feature for sending log messages from a server to a client, is deprecated in the 2026-07-28 specification. Your server still needs logs. For a new implementation, though, the client’s notification stream is the wrong place to build your diagnostic record.
The core idea
MCP Logging defines a structured notifications/message notification. A server can attach a severity level, a logger name, and JSON-serializable data. A client may show those messages to the developer or keep them in its own logs. That is different from returning an error to the agent: logs help the operator understand what happened; tool results and errors tell the caller what the operation returned.
Existing integrations can still use the feature. The current specification says new implementations should not adopt it and directs developers toward stderr for stdio servers or OpenTelemetry for structured observability. If a tutorial tells you to wire up MCP log notifications first, check which protocol version it covers.
How it works in the current specification
A server that emits protocol log notifications declares the logging capability. For a particular request, the client puts io.modelcontextprotocol/logLevel in the request’s _meta. The server may then emit notifications/message at or above that level on that request’s response stream, before the final response. It must not emit those notifications for requests that did not ask for them.
Here is the shape of a notification, adapted from the MCP Logging specification:
{
"jsonrpc": "2.0",
"method": "notifications/message",
"params": {
"level": "error",
"logger": "orders-api",
"data": {
"error": "Upstream request timed out",
"operation": "search_orders"
}
}
}
The levels follow syslog’s severity names: debug, info, notice, warning, error, critical, alert, and emergency. In the current spec, an unrecognized per-request log level should be rejected as invalid parameters. Do not copy older examples built around a persistent logging/setLevel setting without checking the protocol version your client and server actually implement.
The client might display that notification, filter it out, or discard it. You cannot count on finding it during an incident. Keep durable records in an observability system you control.
Why it matters for AI agents
Agent workflows can cross several boundaries in a single turn: model, client, MCP server, and external API. An error at any of those points can look like a bad tool call. Logs narrow the fault down without forcing the agent to expose infrastructure details in its answer.
They also create a security obligation. The specification forbids credentials, personal identifying information, and internal details that could aid attacks in protocol log messages. That is especially important if a client stores logs with a conversation transcript. Log an operation name and failure category; do not dump an authorization header, a customer record, or a raw upstream response.
A tool annotation describes what a tool is expected to do; a log describes what happened while it ran. A resource is something the model can read. Don’t turn your diagnostic stream into either one just to make debugging easier. That’s an easy way to spill internal details into the agent’s context.
MCP logging in practice
Suppose you run a local stdio server that looks up orders. A developer reports intermittent timeouts. Start with a concise, structured line on stderr containing the operation, an internal request identifier, the duration, and a safe failure category. Never print diagnostics to stdout: in a stdio transport, stdout carries protocol messages, so an ordinary print can break the connection. The official debugging guide recommends stderr for stdio and OpenTelemetry for cross-transport tracing.
For a remote server, instrument the incoming request and the upstream lookup with OpenTelemetry. Record latency and sanitized errors where your team can query them. If you also maintain an older client integration that uses notifications/message, keep it compatible while planning its replacement. Don’t build a new monitoring pipeline that depends on clients retaining those notifications.
When the timeout happens again, match the request identifier to your server logs. Did the upstream call start? How long did it take? Did your server return an error before the timeout? Those answers tell you where to investigate. The agent only needs a safe failure message, not your stack trace.
FAQ
Is MCP Logging gone? No. It is deprecated in the 2026-07-28 specification, not removed. Existing implementations have a migration window; new ones should use stderr or OpenTelemetry rather than adopt the deprecated notification feature.
Can my server send log notifications whenever it wants? Not under the current specification. The client must request a log level in the particular request’s _meta, and the server can send matching notifications only on that request’s response stream before the final response.
Where should I write logs for a stdio MCP server? Write diagnostics to stderr, not stdout. Keep the data structured and scrub secrets before it leaves the server.
Do logs belong in a tool result? Usually not. Return a useful, safe error or result to the agent and keep diagnostic details in the operator’s logs. Mixing the two can leak sensitive data and waste the model’s context.