Model inserts hard line breaks in MCP tool parameters, breaking Slack/Gmail draft formatting

Status Closed — not planned
Reported on v2.1.70
Maintainer reply None cached
Activity 12 comments · opened Mar 10, 2026 · closed May 3, 2026

When composing text for MCP tool parameters (e.g., slack_send_message_draft, gmail_create_draft), the model inserts hard \n characters within paragraphs at ~60-70 character intervals. These newlines are passed through by the MCP transport to the downstream APIs (Slack, Gmail), which render them as visible line breaks instead of allowing text to reflow naturally.

This is particularly bad on mobile, where every mid-paragraph line break becomes a visible new line, making messages look broken.

Reproduction:

  1. Use Claude Code with the built-in Slack MCP integration
  2. Ask Claude to draft a Slack message longer than ~70 characters
  3. Observe that the message parameter contains \n characters mid-paragraph
  4. The resulting Slack draft renders with hard line breaks instead of flowing text

Same behavior occurs with Gmail draft creation and any text output intended for copy-paste into external tools.

Timeline:

  • Not observed prior to ~March 7, 2026
  • First noticed March 9, 2026
  • Confirmed March 11, 2026 (affects all drafted comms)

Investigation: Reviewed changelogs for Claude Code 2.1.70 through 2.1.72. No changes to MCP transport, tool parameter handling, or text formatting. The issue appears to be a model-side behavior change, not a client-side regression.

Workaround: Instructing the model via CLAUDE.md to "write every paragraph as ONE continuous unbroken string with no mid-paragraph line breaks" partially mitigates the issue, but compliance is inconsistent.

Expected behavior: Text composed for MCP tool string parameters should not contain hard line breaks within paragraphs. Paragraph separation (\n\n) is fine; mid-sentence wrapping (\n at ~70 chars) is not.

Environment: Claude Code 2.1.72, macOS, claude-opus-4-6, built-in Slack and Gmail MCP integrations

View original on GitHub ↗

12 Comments

github-actions[bot] · 5 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/6827

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

marty-itsec · 5 months ago

This is not a duplicate of #6827. While the root cause may be related (the model's tendency to hard-wrap text at ~70-80 chars), the manifestation and required fix are different:

  • #6827 is about hard line breaks in terminal/IDE display output — text rendered in the VS Code panel or CLI terminal that breaks copy-paste workflows. That's a rendering/display layer issue.
  • - This issue is about hard line breaks injected into MCP tool call parameters — the JSON string values sent over the wire to downstream APIs (Slack, Gmail, etc.) via MCP. The text never passes through terminal rendering. It goes directly from model token generation into the MCP transport as structured data.

Even if #6827 were fully fixed (i.e., terminal output rendered with soft wrapping), this issue would persist because the \n characters are embedded in the tool parameter strings themselves, not in display formatting.

Why this matters separately: MCP tool parameters are machine-to-machine data. When the model writes {"message": "Hey team,\nI wanted to share..."}, that literal \n gets passed by the MCP transport to Slack's API, which renders it as a visible line break. There is no display layer to fix — the data itself is corrupted.

Workaround: A PreToolUse hook that intercepts MCP tool calls and strips solo \n (preserving \n\n paragraph breaks) from string parameters before they reach the MCP server. Happy to share the script if others are hitting this.

lucasbennington17 · 5 months ago

Please share the script, I am running into this too and it is driving me crazy

marty-itsec · 5 months ago

Here's the hook script. Fair warning: it helps but doesn't catch everything. The model still occasionally slips newlines through in ways that dodge the replacement logic. Better than nothing though.

Drop this in your hooks directory and wire it up as a PreToolUse hook in your settings.json.

#!/usr/bin/env bash
#
# fix-mcp-newlines.sh
# Claude Code PreToolUse hook that strips hard mid-paragraph line breaks
# from MCP tool string parameters before they reach downstream APIs.
#
# Problem: The model inserts \n at ~70-char intervals inside paragraphs
# when composing text for MCP tool parameters (Slack drafts, Gmail drafts,
# etc.), causing ugly hard wraps in the rendered output.
#
# Fix: Replace solo \n with a space, preserving \n\n paragraph breaks.

set -euo pipefail

# Read the hook input from stdin
INPUT=$(cat)

# Use python3 (available on macOS) to process the JSON.
# For each string value in tool_input, replace solo \n with space,
# keeping \n\n (paragraph breaks) intact.
RESULT=$(python3 -c "
import json, sys, re

data = json.loads(sys.argv[1])
tool_name = data.get('tool_name', '')

# Only process MCP tools
if not tool_name.startswith('mcp__'):
    sys.exit(0)

tool_input = data.get('tool_input', {})
changed = False

def fix_newlines(s):
    if not isinstance(s, str):
        return s, False
    # Protect paragraph breaks (double newlines) with a placeholder
    protected = s.replace('\n\n', '\x00PARA\x00')
    # Replace remaining solo newlines with a space
    if '\n' in protected:
        fixed = protected.replace('\n', ' ')
        # Collapse any resulting double spaces
        fixed = re.sub(r'  +', ' ', fixed)
        # Restore paragraph breaks
        fixed = fixed.replace('\x00PARA\x00', '\n\n')
        return fixed, (fixed != s)
    # Restore paragraph breaks even if no changes
    restored = protected.replace('\x00PARA\x00', '\n\n')
    return restored, False

updated_input = {}
for key, value in tool_input.items():
    if isinstance(value, str):
        fixed, was_changed = fix_newlines(value)
        updated_input[key] = fixed
        if was_changed:
            changed = True
    else:
        updated_input[key] = value

if changed:
    output = {
        'hookSpecificOutput': {
            'hookEventName': 'PreToolUse',
            'updatedInput': updated_input
        }
    }
    print(json.dumps(output))
else:
    # No changes needed — output nothing, exit 0
    pass
" "$INPUT")

# If the script produced output, echo it
if [ -n "$RESULT" ]; then
    echo "$RESULT"
fi

exit 0

Hook config in your settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__*",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/fix-mcp-newlines.sh"
          }
        ]
      }
    ]
  }
}

Like I said, it's a bandaid. The real fix needs to happen model-side.

m13v · 5 months ago

we see this too when our agents compose messages through MCP tools. the model seems to hard-wrap text at around 70 chars regardless of whether the output will be rendered in a fixed-width or proportional font context. for our social media posting pipeline we work around it by stripping artificial line breaks in the MCP tool handler before passing to the API - basically replace any \n that's not preceded by a period, colon, or double newline with a space. not ideal but it makes the output readable on mobile and in web UIs.

m13v · 5 months ago

fwiw we hit something similar building our MCP server for macOS automation - the tool parameter descriptions would get mangled when they contained newlines. ended up being a JSON encoding issue on our side. here's the actual server implementation if it helps as a reference: https://github.com/mediar-ai/mcp-server-macos-use/blob/main/Sources/MCPServer/main.swift

culstrup · 5 months ago

Hitting this too. Both Gmail and Slack drafts composed via the hosted MCP connectors come out with hard \n at ~70 chars mid-paragraph. Particularly bad on mobile where every break becomes a visible new line.

Confirmed it's model-side — the draft looks fine in Gmail's compose view (soft-wrapped), but the literal newlines are in the MIME body and render as hard breaks for recipients.

Currently working around it by using the gws CLI (Google Workspace CLI) for email composition instead of gmail_create_draft, which avoids the issue entirely since the text doesn't pass through model tool parameters in the same way.

Would love to see this fixed at the model level — MCP tool string parameters shouldn't be treated like terminal output.

LucasGSD · 5 months ago

Same here - also switched over to gws CLI (Google Workspace CLI). Kind of annoying though, especially since the drafts look like they're neatly formatted.

m13v · 5 months ago

interesting that it shows up in the MIME body specifically. so it sounds like this is a different manifestation than what we hit - ours was about tool param descriptions getting dropped during schema parsing, but yours is about the model inserting literal newlines into generated content that then get preserved verbatim in the email body. the gws CLI workaround makes sense as a stopgap. have you tried post-processing the draft body to collapse single newlines into spaces before sending? something like replacing lone \n (not \n\n) with a space might preserve intentional paragraph breaks while fixing the mid-paragraph wrapping.

m13v · 5 months ago

yeah the deceptive part is that it looks correct in compose view. Gmail's editor soft-wraps so you can't tell the hard breaks are there until the recipient opens it. definitely a bug that should be fixed upstream rather than worked around.

github-actions[bot] · 3 months ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

github-actions[bot] · 2 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.