slackapi / slackapi/slack-skills-plugin
slack_read_channel/thread/search return a placeholder instead of content for forwarded (is_share) messages
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 132
- Forks
- 34
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 16
Description
AI;DR
Claude's Slack plugin can't read forwarded messages from private channels or private DMs that I'm not in. The Slack MCP returns:
Forwarded message from Bob Johnson (permalink) — use slack_read_thread or slack_read_channel to read the original message
... but those tools fail because I don't have access to those private channels / DMs.
Plugin version
1.3.0 (slack@claude-plugins-official). Also reproduced with the Slack connector in Claude Desktop, which uses the same hosted server at https://mcp.slack.com/mcp.
Client
Claude Code
Client version
Claude Code 2.1.273 (terminal). Same result in the Claude Desktop app's Slack connector.
Operating System
macOS 26.6.2
Description
When a user forwards (shares) a message into a DM or channel, Slack stores the forwarded content as an attachments[] entry with is_share: true, and leaves the top-level text empty. slack_read_channel, slack_read_thread, and slack_search_public_and_private all return a placeholder for such messages instead of the quoted content:
Forwarded message from <name> (https://<workspace>.slack.com/archives/C.../p...) — use slack_read_thread or slack_read_channel to read the original message
This happens for every forwarded message, regardless of where it came from. I verified it on forwards whose source is a public channel, and on forwards whose source is a private channel the reading user is a member of; both return the same placeholder with the quoted text omitted, so the source's visibility is not the trigger. In that case the hint is followable, at the cost of a second tool call that the model must remember to make. When the source is a conversation the user is not a member of (a common reason to forward something in the first place), following the hint fails with channel_not_found and the content is unreachable through the MCP, even though the quoted text is present in the payload the server already fetched (attachments[0].text, attachments[0].fallback, and attachments[0].blocks) and the Slack client renders it.
This is the same "content lives in attachments" family as #24 / #45, but the server-side fix shipped for those on 2026-08-25 does not cover shares: forwarded messages still come back as the placeholder (reproduced 2026-09-15 and 2026-09-16). Opening a focused issue as requested in #24.
Steps to reproduce
- In Slack, open any message and forward it. To see the placeholder alone, use a message from a channel (public or private) that user B is a member of. To see the dead end, use a message from a conversation user B is not a member of (a private channel or a DM with someone else).
- Use "Forward message" to send it to user B in a DM (or a channel B is in). Slack renders the quoted text for B.
- As user B, in Claude Code with the
slackplugin authenticated, ask Claude to read that DM:slack_read_channelon the DM (orslack_read_threadon the forwarded message'sts, orslack_search_public_and_privatewithfrom:andon:filters).
Expected result
The forwarded content is rendered inline, labeled as forwarded, with the permalink as secondary detail, e.g.:
=== Message from B at ... ===
Message TS: ...
Forwarded from <author> in <#C...> (permalink):
> <quoted text of the original message>
At minimum, attachments[].fallback (which Slack always populates for shares) should be shown when text is empty, matching the behavior already applied to legacy bot attachments in #24.
Actual result
slack_read_channel (detailed):
=== Message from B <b@example.com> (U...) at 2026-09-15 16:39:10 PDT ===
Message TS: 1789515550.308789
Forwarded message from A (https://example.slack.com/archives/C.../p...?thread_ts=...&cid=C...) — use slack_read_thread or slack_read_channel to read the original message
slack_read_thread on the same ts returns the same placeholder plus "No thread messages". slack_search_public_and_private finds the message but its Text: field is empty. slack_read_thread on the permalink's channel returns channel_not_found (expected, B is not a member).
Raw message as returned by the Web API for the same ts (identifiers and names replaced):
{
"type": "message",
"ts": "1789515550.308789",
"user": "U_SENDER",
"text": "",
"attachments": [
{
"fallback": "[September 11th, 2026 9:15 AM] a.sender: > quoted line one\nquoted line two",
"text": "> quoted line one\nquoted line two",
"ts": "1789143307.754349",
"author_id": "U_ORIGINAL_AUTHOR",
"author_name": "A. Sender",
"channel_id": "C_SOURCE_CHANNEL",
"is_share": true,
"is_msg_unfurl": true,
"is_reply_unfurl": true,
"from_url": "https://example.slack.com/archives/C_SOURCE_CHANNEL/p1789143307754349?thread_ts=1789142668.120629&cid=C_SOURCE_CHANNEL",
"footer": "Thread in Slack Conversation",
"blocks": [
{
"type": "rich_text",
"elements": [
{ "type": "rich_text_quote", "elements": [ { "type": "text", "text": "quoted line one" } ] },
{ "type": "rich_text_section", "elements": [ { "type": "text", "text": "\nquoted line two" } ] }
]
}
]
}
]
}
Anything else?
- Related: #24 and #45 (legacy bot attachments, fixed server-side 2026-08-25) and #126 (table blocks nested in attachments, open). Shares appear to take a separate code path that emits the "Forwarded message from …" hint, so the #24 fix did not reach them.
- Reproduced with both
is_reply_unfurl: true(forward of a thread reply) and a forward of a top-level channel message; both render as the placeholder. - Rendering
fallbackalone would already resolve the practical problem; rendering therich_text_quoteblock would be nicer.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing how slack_read_channel, slack_read_thread, and slack_search_public_and_private format messages whose top-level text is empty. Reproduce with a forwarded message containing attachments[0].fallback or blocks, then verify the three tools render the forwarded content inline and retain the permalink as secondary detail, including when the source conversation is inaccessible.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 72/100