[BUG] Telegram plugin: Claude's markdown syntax sent literally, not rendered
Summary
When the Telegram plugin's reply tool is called with format omitted (default 'text'), Claude's markdown-formatted text (e.g. **bold**, ` code , [link](url)`) is sent to Telegram as literal characters. Recipients see the asterisks/backticks/brackets instead of formatted output.
Current behavior
- Claude tends to produce markdown in responses by default (per its training).
- The plugin's default
format='text'sends the string as-is. - Telegram with no
parse_moderenders it as literal text, so users see**bold**instead of bold.
What I tested
format='text'(default) — sends raw string,**bold**shown literally. ✗format='markdownv2'unescaped — e.g."*bold* _italic_code\\nPlain line with a period."→ Telegram returns400: Bad Request: can't parse entities: Character '.' is reserved and must be escaped with the preceding '\\'. Any unescaped reserved character in plain text kills the whole message. ✗format='markdownv2'with strict escaping — e.g."*bold* _italic_code~strike~\\nPlain line with an escaped period\\\\."→ renders correctly. ✓- HTML mode — not exposed by current tool (enum is
text | markdownv2). Would also work per Telegram's Bot API formatting docs.
Why the default breaks
MarkdownV2 mode requires backslash-escaping every occurrence of these characters outside of formatting spans: _ * [ ] ( ) ~ \ > # + - = | { } . !. In practice Claude's output almost always contains unescaped periods, parens, or dashes, so switching the default to markdownv2` without escaping is strictly worse.
Claude also uses GitHub-flavored markdown syntax (**bold**, *italic*), not MarkdownV2 (*bold*, _italic_), so there's a syntax mismatch even before escaping.
Suggested fix (concrete)
In the reply / send-message tool, intercept the text before calling sendMessage:
Path A — plain-text default (least risky):
- When
formatis omitted or'text', run a minimal markdown stripper: remove**/__pairs, remove single*/_/``, convert[text](url)→text (url)ortext, strip> ` block quotes, etc. - This keeps messages readable without any parse_mode risk.
Path B — rich formatting by default:
- Add a new
format: 'auto'(and make it the default). The plugin:
- Converts Claude's GFM (
**bold**→*bold*,*italic*→_italic_,[t](u)→[t](u), fenced ``to``). - Backslash-escapes every MarkdownV2 reserved char that sits outside a formatting span.
- Sends with
parse_mode: MarkdownV2.
- If conversion fails (malformed spans), fall back to Path A (stripped plain text) instead of returning an API error.
Path C — tool-description nudge (minimum viable):
- Update the
replytool description to tell Claude: "Default mode sends plain text — do not use markdown syntax unless you also passformat: 'markdownv2'and escape reserved characters per Telegram MarkdownV2 rules." - Cheap, but relies on model compliance every turn.
Best combo is probably A by default + B behind format: 'auto'. Either is strictly better than current behavior where markdown leaks through as literal text.
Related
- #36622 (closed as invalid) — requested MarkdownV2 support; this is a different angle (Claude's already-produced markdown silently breaks in plain-text mode).
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗