---
title: "MCP Server Logging: Where Logs Go, What Changed and What to Record"
description: "MCP server logging goes to stderr on stdio, to your own log pipeline or OpenTelemetry on HTTP. Protocol log messages are deprecated since 2026-07-28."
canonical: https://luckpanda.app/blog/mcp-server-logging
author: "Dhrumil Barot"
author_bio: "Dhrumil is a small-business operations and compliance expert. Dhrumil built LuckPanda so small businesses can run invoicing, inventory, accounts and compliance in one place."
published: 2026-11-11
updated: 2026-11-11
category: "AI agents and automation"
keywords: "mcp server logging"
language: en
---

# MCP Server Logging: Where Logs Go, What Changed and What to Record

MCP server logging goes to stderr on stdio and to your own logs or OpenTelemetry on HTTP. Protocol log messages are deprecated since 2026-07-28.

By [Dhrumil Barot](https://luckpanda.app/about#founder), Founder. Published 11 Nov 2026. Updated 11 Nov 2026. 6 min read.

**TL;DR:** MCP server logging is the record a Model Context Protocol server keeps of what it did: startups, tool calls, errors. On the stdio transport, logs go to stderr, never stdout. On Streamable HTTP, they go to your own log pipeline or OpenTelemetry. The protocol's own log channel (notifications/message) still works but has been deprecated since protocol version 2026-07-28.

MCP server logging is the practice of recording what a Model Context Protocol server does, so that you can debug it and audit it. The specification gives you three places to send that record. The first is **stderr**: a stdio server may write any UTF-8 text there, and the host application usually captures it. The second is **your own logging stack**: files, a log aggregator or **OpenTelemetry**. Streamable HTTP servers have to use this one, because the client never sees their stderr. The third is the **MCP logging utility**: the server declares a logging capability and sends notifications/message with one of the eight RFC 5424 severity levels. That third option is the one most tutorials still teach. In revision 2026-07-28, SEP-2577 deprecated it, and new implementations should not adopt it.

## What is MCP server logging?

MCP server logging covers everything a server records about its own work. That includes startup steps, resource access, tool execution, error conditions and performance metrics. The official [debugging guide](https://modelcontextprotocol.io/docs/tools/debugging) lists exactly these five as the important events to log. The audience is the developer debugging the server and the person who later has to answer "what did the agent do?".

That second audience is why logging matters more for MCP than for a typical web service. An MCP server is called by a language model, not by a person clicking a button. Picture an agent that reads twelve customer records and drafts three invoices at 2 a.m. Your logs are the only account of which tool ran, with which arguments, under which key.

![Where an MCP server's logs end up for each transport and option](https://cdn.sanity.io/images/pcmcvz9y/production/bc748ddf451a5659a4fc82a5748d90ce4ed1d46a-1344x410.png?w=1200&auto=format&fit=max)

*Use the first two for new servers.*

## Does MCP support logging?

Yes, MCP has a logging utility, but it is now deprecated. Here is how it works in protocol version [2025-06-18](https://modelcontextprotocol.io/specification/2025-06-18/server/utilities/logging):

1. The server declares "capabilities": {"logging": {}}.
2. The client may send logging/setLevel with a minimum level, for example {"level": "info"}.
3. The server sends notifications/message with level, an optional logger name (such as database) and data, which can be any JSON value.
4. An invalid level returns JSON-RPC error -32602 (Invalid params). Configuration errors return -32603 (Internal error).

The levels follow syslog ([RFC 5424](https://datatracker.ietf.org/doc/html/rfc5424)):

| Level | Meaning | Example use |
| --- | --- | --- |
| debug | Detailed debugging information | Function entry and exit |
| info | General information | Operation progress |
| notice | Normal but significant event | Configuration change |
| warning | Warning condition | Deprecated feature used |
| error | Error condition | Operation failed |
| critical | Critical condition | Component failure |
| alert | Act immediately | Data corruption detected |
| emergency | System unusable | Complete failure |

## What changed in protocol version 2026-07-28?

Revision 2026-07-28 made MCP stateless, and that changed how protocol logging works. The [changelog](https://modelcontextprotocol.io/specification/2026-07-28/changelog) removes logging/setLevel entirely. A client now asks for log messages one request at a time by putting io.modelcontextprotocol/logLevel in that request's \_meta. The server must not send notifications/message for any request that leaves this field out. When it does send them, they travel only on the response stream of that request, never on the long-lived subscriptions/listen stream.

In the same revision, SEP-2577 deprecated the whole Logging feature, along with Roots and Sampling. Under the feature lifecycle policy, it stays in the specification for at least twelve months. Existing servers should migrate to stderr for stdio and to OpenTelemetry for structured observability ([logging spec, 2026-07-28](https://modelcontextprotocol.io/specification/2026-07-28/server/utilities/logging)). The revision also defines \_meta keys for trace context (traceparent, tracestate, baggage, SEP-414). That lets one trace follow a call from the client through your server.

Most pages that rank for this query were written before this change. They still show logging/setLevel as the way to do it.

![How MCP logging changed between protocol revisions](https://cdn.sanity.io/images/pcmcvz9y/production/14de722ba7f424dd6717aa818b999fc61f13be83-1344x410.png?w=1200&auto=format&fit=max)

*Migrate to stderr or OpenTelemetry.*

## Where do MCP server logs go?

Where the logs go depends on the transport.

**stdio.** The client launches the server as a subprocess. Anything the server writes to stderr is captured by the host application. The [transport spec](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports) says the client "MAY capture, forward, or ignore" it. Claude Desktop, for example, writes server logs to mcp\*.log files in these folders:

| OS | Log folder |
| --- | --- |
| macOS | ~/Library/Logs/Claude |
| Windows | %APPDATA%\Claude\logs |
| Linux | ~/.config/Claude/logs |

You can follow them live with tail -n 20 -F ~/Library/Logs/Claude/mcp\*.log on macOS. Other clients (Cursor, VS Code, Claude Code) keep their own logs, so check the client's documentation.

**Streamable HTTP.** The server is an independent process. The debugging guide states that stderr "is not captured by the client". Your logs go wherever you send them: platform logs, a file, a log service or an OpenTelemetry collector. To inspect traffic, use standard HTTP tools such as curl or the browser's Network panel.

## Why can't a stdio MCP server print to stdout?

A stdio MCP server cannot print to stdout because stdout is the protocol channel. The spec says the server "MUST NOT write anything to its stdout that is not a valid MCP message". Messages are newline-delimited JSON-RPC and must not contain embedded newlines. A single stray print("connected") puts a non-JSON line into the client's parser. The usual result is a parse error or a server that "connects" and then shows no tools.

Common culprits:

- print() in Python or console.log() in Node, which both write to stdout by default. Use logging (stderr by default) or console.error() instead.
- A dependency that prints a banner or a progress bar on import.
- Configuring a logging library to write to the console's stdout stream.

Two related pitfalls from the debugging guide: the working directory of a stdio server launched by a client may be undefined (/ on macOS), so log file paths must be absolute. And stdio servers inherit only a limited set of environment variables, so a missing LOG_LEVEL may simply never arrive.

## What should an MCP server log for audit?

For audit, an MCP server should log one structured record per tool call that answers who, what, on which data, and with what result. A debug log answers "why did it break?". An audit log answers "who did this, and was it allowed?". When an agent can touch business data such as customers, invoices or payments, you need the second one as well.

A worked example. An agent calls create_invoice_draft for a client. The total is ₹1,18,000.00: ₹1,00,000 plus 18% GST. A useful audit record has these fields:

| Field | Example value | Why |
| --- | --- | --- |
| timestamp | 2026-11-11T09:14:03Z | Ordering and retention |
| actor | agent key ak\_…7f2 on behalf of user 41 | Who acted, human and agent |
| client | clientInfo.name from \_meta | Which app made the call |
| tool | create_invoice_draft | What ran |
| target | client 908, draft 5521 | Which records it touched |
| arguments | IDs and amounts, no personal data | Reproduce the call |
| decision | allowed (draft only) | Permission outcome |
| result | success, total 118000.00 INR | Outcome |
| idempotency key | 6c1e… | Spot retries, not duplicates |
| trace id | from traceparent | Join with request traces |

![An audit trail follows each agent tool call from request to stored record](https://cdn.sanity.io/images/pcmcvz9y/production/67b1f4d4bce89291bf34e953e227e531ca740a4c-1344x326.png?w=1200&auto=format&fit=max)

*The audit record is written whether the call succeeds or is refused.*

Rules worth keeping:

- **Log refusals, not only successes.** A refused attempt to send or pay is the most important line in the file.
- **Never log secrets or personal data.** The logging spec says log messages "MUST NOT contain" credentials, secrets or personally identifying information. Log IDs, not names, emails or bank numbers.
- **Separate the streams.** Debug logs rotate in days. Audit records belong in an append-only store with a retention period that your accounting or tax rules require.
- **Rate limit.** The spec asks servers to rate limit log messages. A looping agent can produce thousands of calls in minutes.

This is the approach LuckPanda takes for its own [MCP server](https://luckpanda.app/modules/workspace/developers). Every change is recorded with the person or agent that made it. Agent keys can search, read and draft, but they are refused for issuing, sending, payment links and recording payments. A person reviews each draft and decides, using the [approval workflows](https://luckpanda.app/modules/workspace/approvals) in the Workspace module.

## MCP logging vs OpenTelemetry tracing

MCP protocol logging and OpenTelemetry solve different problems, and the spec now points new servers to OpenTelemetry.

|  | notifications/message | stderr | OpenTelemetry |
| --- | --- | --- | --- |
| Who receives it | The MCP client | The host app (stdio only) | Your collector |
| Structured | Yes (JSON data) | Only if you write JSON | Yes, with traces and metrics |
| Survives the session | Only if the client persists it | Client log files | Yes, in your backend |
| Status in 2026-07-28 | Deprecated | Recommended for stdio | Recommended |
| Good for audit | No | Weak | Yes, with a durable store |

Use protocol notifications only when a client UI genuinely needs to show progress text for a single request. Use stderr for local debugging. Use OpenTelemetry, or your platform's log service, for anything you will need to search next month.

## Pitfalls

- Teaching new code logging/setLevel. It no longer exists in 2026-07-28 servers.
- Expecting client-side logs on Streamable HTTP. The client never sees your stderr.
- Logging full tool arguments that contain names, addresses or account numbers.
- Logging only errors, which leaves you nothing to reconstruct a successful but wrong agent action.

For how MCP compares with a plain REST API as an integration surface, see [MCP vs API](https://luckpanda.app/blog/mcp-vs-api). For deciding who may approve what, see the [delegated authority matrix](https://luckpanda.app/blog/delegated-authority-matrix).

## Questions

### Does MCP support logging?

Yes. The specification has a logging utility: a logging capability, eight RFC 5424 levels and notifications/message. Since protocol version 2026-07-28 it is deprecated (SEP-2577). It stays for at least twelve months, but new servers should log to stderr or OpenTelemetry instead.

### Where do MCP server logs go?

For stdio servers, logs written to stderr are captured by the host app. Claude Desktop, for example, writes them to mcp\*.log in \~/Library/Logs/Claude on macOS, %APPDATA%\Claude\logs on Windows and \~/.config/Claude/logs on Linux. For Streamable HTTP servers, logs go wherever your server sends them.

### Why can't a stdio MCP server print to stdout?

Stdout carries the newline-delimited JSON-RPC messages. The spec forbids writing anything there that is not a valid MCP message, so a stray print breaks the client's parser. Write logs to stderr.

### What should an MCP server log for audit?

One record per tool call with the timestamp, the actor (user and agent key), the client, the tool, the target records, the arguments without personal data, the permission decision, the result, the idempotency key and the trace ID. Keep it in an append-only store.

### How do I set the log level in MCP?

In protocol 2025-06-18, the client sends logging/setLevel. In 2026-07-28, it sets io.modelcontextprotocol/logLevel in each request's \_meta, and the server sends messages at or above that level only for that request.

### What is the MCP Inspector for?

The MCP Inspector is the official interactive testing UI. It connects to stdio or Streamable HTTP servers, invokes tools and shows the notification stream. The debugging guide recommends it as the first stop.

## More guides

- [MCP vs API: What Changes When an AI Agent Is the Caller](https://luckpanda.app/blog/mcp-vs-api)
- [Invoice Approval Process: Steps, Matrix and Deadlines](https://luckpanda.app/blog/invoice-approval-process)
- [Best AI Chatbot for Small Business: 8 Tools Compared on Real Monthly Cost](https://luckpanda.app/blog/best-ai-chatbot-for-small-business)

## Related tools

- [Invoice generator](https://luckpanda.app/tools/invoice-generator): Bill a client in any currency, with tax worked out as you type.

## About the author

**Written by [Dhrumil Barot](https://luckpanda.app/about#founder)**, Founder. Dhrumil is a small-business operations and compliance expert. Dhrumil built LuckPanda so small businesses can run invoicing, inventory, accounts and compliance in one place. [LinkedIn](https://www.linkedin.com/in/bdhrumil) · [X](https://x.com/fyidhrumil)
