MCP debugging security

Sharing MCP Error Logs Without Sharing Your Tokens

The Prompt Shelf ·

Before sharing MCP error logs, create a separate support example with dummy values and a small set of reviewed diagnostic facts. Replacing the token you remember is not a reliable clearance check for an entire log. Keep the client and server versions, launch method, transport, failing stage and relevant error location; leave credentials, customer data and unrelated context out.

We tested that distinction locally with synthetic strings in Python 3.14.3. Literal replacement removed a dummy token from two representations but missed five other cases. This was a string-handling experiment, not a test of an MCP application’s logging or a production redaction system. The runnable fixture below makes the limits visible without reading your environment, opening your logs or calling an API.

Moving a message to stderr does not make it private

MCP has more than one place where diagnostic data can travel. The stdio transport specification reserves stdout for protocol messages and permits logging to stderr; a client may capture or forward stderr. Correctly choosing the stream prevents a protocol problem. It does not establish who will subsequently see the message.

The MCP logging specification also supports structured notifications with JSON-serializable data. Its security guidance excludes credentials and other sensitive information. A nested object remains something you must review, even if the logger labels it an error.

For the separate stream-integrity problem, see our stdio stdout-noise experiment. This article concerns what leaves your machine when you ask for help, not whether the server can initialize.

Seven inputs that expose the limits of one replacement

Our fixture uses the invented value DEMO_ONLY_a+b/c=42. It is not a credential. For each input, the operation is simply:

shared_text = input_text.replace(dummy_token, "[REMOVED]")
Synthetic inputResult of literal replacementWhat the check establishes
JSON with the literal token as a valueToken replacedThis exact spelling was covered
Authorization: Bearer followed by the literal tokenToken replacedThis exact header value was covered
URL query with percent-encoded tokenUnchangedDecoding the query recovers the dummy token
JSON with Unicode escapes for the tokenUnchangedParsing the JSON recovers the dummy token
Base64 representationUnchangedBase64 decoding recovers the dummy token
Value split across two linesUnchangedJoining the two parts recovers the dummy token
A different dummy session cookieUnchangedKnowing one token does not cover other secrets

The lab also tries removing a top-level JSON field named token. The same value survives in a free-text message and a nested Authorization field. Recursion would fix that particular nesting omission, but a denylist still needs to know which fields and free-text content are sensitive.

These are counterexamples to the tested approaches, not a claim that every MCP client emits these encodings. They also do not establish how often leaks occur. No amount of success on this small fixture proves that an arbitrary real log is safe to publish.

Run the synthetic lab

Download the self-contained Python fixture. Read it before running, save it in an empty scratch directory, then use an existing Python 3 interpreter:

python3 mcp-log-sharing-lab.py

It requires no third-party packages. It takes no log path or token argument, reads no environment variables, makes no network requests and writes no files. Output contains only invented examples, test results and a sample report. Our run returned passed: true for ten grouped checks: seven input cases, the top-level-filter counterexample, omission of fixture values from the sample report, and preservation of diagnostic fields. Other Python versions have not been tested here.

Do not adapt this into a command that uploads your log or prints your environment. Its useful output is evidence about a failed assumption, not a sanitized version of your incident.

Build the report from facts you can review

Start with a new text file rather than a copy of the raw log. The OWASP logging guidance recommends excluding or treating sensitive material such as access tokens, passwords and connection strings appropriately. For a public support question, a smaller reconstructed example is easier to inspect than a large dump with uncertain omissions.

Use this order:

  1. Locate the boundary. Did the executable fail to start, did initialization fail, or did a particular tool call return an error? Record what you observed, separately from your suspected cause.
  2. Record exact component versions. Include the client, server package and runtime, plus OS/version if relevant. Review version-command output locally before copying it; custom commands can emit more than a version. Do not run an unfamiliar command merely because a support template asks for it.
  3. Describe the launch shape. Say whether the host starts a local subprocess or connects over HTTP. Show a reviewed command with dummy paths and placeholders, not shell history, a full configuration file or command-line tokens. Mark every replacement.
  4. State required settings without their values. For example, DEMO_SERVICE_TOKEN: present in parent; child presence not yet checked. Do not equate parent presence with successful inheritance or valid authentication. Our environment inheritance experiment covers that separate boundary.
  5. Preserve the error’s structure. Keep the error code, failing stage and a relevant filename/line/column when those can be shared. Replace private path prefixes consistently. Review message text and stack frames individually; they may contain query data, URLs, usernames or source excerpts. If you omit a frame or alter a line, label the omission.
  6. Try a minimal dummy reproduction only when feasible. Remove unrelated inputs and use a local fixture without real service access. If it no longer fails, say so. Do not report a made-up failure as a reproduced incident or change credentials just to make the example run.
  7. Inspect the exact outgoing artifact. Review the rendered post and every attachment, screenshot, URL, filename and code block. Check authorization/cookie headers, query strings, connection strings, tool inputs/results and environment dumps. Searching for known tokens is a supplementary check, not permission to skip the rest.

Even an allowlisted field can contain a secret: copying an arbitrary error.message into a field called summary does not make it safe. This procedure relies on reviewing and reconstructing the values, not just selecting friendly field names. When necessary detail cannot be shared publicly, ask for the maintainer’s approved private support process instead of publishing the original dump.

A support packet that admits what is unknown

The following is entirely illustrative, not an error observed in a real MCP product. Replace version placeholders with reviewed facts for your incident; never copy the example as evidence that you reproduced it.

Client / version: [exact client and version]
Server / version: [exact package and version]
Runtime / OS: [reviewed version information]
Transport: stdio
Launch shape: node ./demo-server.mjs
  (illustrative command; no server file is supplied by this article)
Required setting: DEMO_SERVICE_TOKEN present in parent; value omitted
Child setting presence: not yet tested
Failing stage: tools/call
Error code: -32602 (illustrative, not an observed incident)
Location: server.mjs:42:7 (synthetic location)
Expected: invalid input rejected without printing credentials
Actual: [reviewed observation, not a full raw log]
Dummy reproduction: [steps and result, or "not reproduced with dummy data"]
Edits: private paths replaced; request payload and headers omitted
Question: what additional non-secret evidence would distinguish the causes?

Do not append an environment dump “for completeness.” If a helper needs a specific setting’s presence or a configuration shape, add only that reviewed information. Preserve placeholder consistency when two frames refer to the same path, but do not expose part of a token as a correlation identifier.

Secret storage and log sharing are separate decisions

This workflow is free: a scratch file, dummy input and careful review can be enough for a one-off support question. A paid secrets manager is not required to perform the lab or prepare the report. Runtime secret injection also cannot prevent application code from printing a value after it receives it.

If the underlying problem is repeated team handling of credentials, the separate Git and secret-management guide includes the distinction between free Git checks and an optional paid secrets workflow. Evaluate that need separately; this experiment does not compare vendors, validate a paid product or show that buying one makes logs safe. There is no direct purchase link in this article.

After you have verified your own workflow, you can add a short instruction to a project rule from the rules gallery: “For support reports, reconstruct a minimal dummy example; do not paste raw logs, environment dumps or credentials. Mark unverified assumptions.” A rule is a reminder, not an enforcement mechanism.

If a real secret has already been shared, redacting a later copy does not undo exposure. Stop distributing the artifact and follow the relevant provider’s credential-revocation and incident process through an authorized operator. This article and its fixture do not perform those actions.

Related Articles

Explore the collection

Browse all AI coding rules — CLAUDE.md, .cursorrules, AGENTS.md, and more.

Browse Rules