Polishing the Slack Experience

Strip mention tags, convert standard markdown to Slack formatting, and add a thinking indicator. Three improvements that turn a working integration into one people actually want to use.

3 min read
Slack markdown format thinking indicator Slack strip mention bot Slack bot UX polish Slack agent

Three problems to solve

The basic Slack integration works. But three things make the experience rough in practice:

1. The agent sees the raw @BotName tag in the message text instead of just the question 2. Responses come back in standard markdown — which Slack does not render correctly 3. There is no feedback while the agent is working — the user just waits in silence

These are not cosmetic. The tag in the message text affects the agent's interpretation. Badly formatted responses look broken. No feedback makes users think the bot is offline. All three are worth fixing before anyone else uses the bot.

Fix 1: Strip the mention tag

When a user types @ClaudeAgent what's our Q2 budget?, the agent receives the full string including the tag markup. Clean it before passing to the agent:

Create src/helpers.ts:

export function stripMentionText(text: string): string {
  return text.replace(/<@[A-Z0-9]+>/g, "").trim();
}

Use it in your event handlers:

const userMessage = stripMentionText(event.text ?? "");
const { response } = await agentChat(userMessage);

Fix 2: Slack markdown formatting

Slack uses a simplified version of markdown. The key differences:

Standard markdownSlack markdown
boldbold
italicitalic
link`text>`
`code` `code` (same)
# HeadingHeading (bold)

Handle this in two layers. First, update the system prompt in agent.ts to request Slack-formatted output:

const SYSTEM_PROMPT = `You are a helpful assistant deployed in Slack.
Format all responses using Slack markdown:
- Bold: *text* (single asterisks)
- Italic: _text_ (underscores)
- Code: \`code\` or \`\`\`code block\`\`\`
- Lists: - item (dash + space)
Do not use standard markdown heading syntax (#, ##).`;

Second, add a conversion function as a safety net in helpers.ts:

export function markdownToSlack(text: string): string {
  return text
    .replace(/\*\*(.*?)\*\*/g, "*$1*")      // **bold** → *bold*
    .replace(/__(.*?)__/g, "_$1_")            // __italic__ → _italic_
    .replace(/^#{1,6}\s+(.+)$/gm, "*$1*");   // headings → bold
}

Apply it when posting the response:

await app.client.chat.update({
  token: process.env.SLACK_BOT_TOKEN,
  channel: event.channel,
  ts: thinkingMessage.ts!,
  text: markdownToSlack(response),
});

Fix 3: Thinking indicator

Post a placeholder message immediately when the agent starts, then replace it with the actual response when it finishes:

app.event("app_mention", async ({ event, client }) => {
  const threadTs = event.thread_ts ?? event.ts;
  const userMessage = stripMentionText(event.text ?? "");

  // Post thinking placeholder immediately
  const thinkingMessage = await client.chat.postMessage({
    token: process.env.SLACK_BOT_TOKEN,
    channel: event.channel,
    thread_ts: threadTs,
    text: "_Thinking..._",
  });

  try {
    const { response } = await agentChat(userMessage);

    // Replace placeholder with actual response
    await client.chat.update({
      token: process.env.SLACK_BOT_TOKEN,
      channel: event.channel,
      ts: thinkingMessage.ts!,
      text: markdownToSlack(response),
    });
  } catch (error) {
    await client.chat.update({
      token: process.env.SLACK_BOT_TOKEN,
      channel: event.channel,
      ts: thinkingMessage.ts!,
      text: "Something went wrong.",
    });
  }
});

The user sees Thinking... in italics immediately, then it updates to the actual response. No silent wait.

Apply the same pattern to the DM handler — the structure is identical, just triggered by the message event.

The result

After these three changes, the Slack experience looks and feels like a proper integration:

  • Messages arrive clean, without tag artifacts
  • Responses format correctly with bold, italic, and code blocks
  • Users see immediate feedback while the agent works

These details matter for anything you deploy to a client team or use daily yourself. The agent's intelligence does not change — but the experience around it changes significantly.

---

Author: FractionalSkill

Keep Going

Ready to Start Building?

Pick the next step that matches where you are right now.

Tutorial
Claude Code Basics

Start with the terminal basics. A hands-on, step-by-step guide to your first 10 minutes with Claude Code.

Start the Tutorial
Guide
AI-Powered Workflows

Automate your client work. Learn how to connect AI tools into workflows that handle repetitive tasks for you.

Read the Guide
Community
Join the Community

Connect with other fractional leaders building with AI. Share workflows, get feedback, and learn from operators who are ahead of you.

Apply to Join