feat(slack_v2): eval-driven fixes for the MCP tool surface by dylburger · Pull Request #21493 · PipedreamHQ/pipedream · GitHub
Skip to content

feat(slack_v2): eval-driven fixes for the MCP tool surface - #21493

Merged
michelle0927 merged 13 commits into
masterfrom
mcp/slack_v2
Aug 5, 2026
Merged

michelle0927 merged 13 commits into
masterfrom
mcp/slack_v2

Conversation

@dylburger

@dylburger dylburger commented Jul 25, 2026 •

Copy link
Copy Markdown
Contributor

What & why

Fixes to the AI-optimized slack_v2 tool surface, driven by the MCP eval suite in
pd-connect-eval-monster/evals/slack_v2. The suite went 23/35 → 33/35 on Sonnet 5
(pass²); every change below is tied to a red-to-green flip or a measured payload cut.

Changes (per component)

  • list-channels (0.1.4 → 0.2.0, minor) — returned every field of every channel with
    numPages defaulting to 1, so a 62-channel workspace produced ~55k chars, exceeded the
    25k-token MCP output ceiling, and the model received a file path instead of the data (one
    eval burned all 20 turns re-calling it). Added an additive fields projection, a cursor
    prop, and has_more/next_cursor so truncation is visible instead of silent. Payload
    55k → 5.9k chars; unblocked evals Remove JSON.parse from default http component #2/Update default timer to 15 mins #10/updates to props and emit summary #11/Polish updates #16/Jotform #19/Add HTTP response for all webhook requests #35.
  • slack_v2.app.mjs — assistantSearch called sdk().apiCall() directly, the one path in
    the app that bypassed _withRetries, while the client is constructed with
    rejectRateLimitedCalls: true — so every 429 rejected instantly. 8 of 12 search calls
    errored in one run (Trying yet again #24/Fauna #26/Update rss.js #27). Now routed through _withRetries. Also adds
    resolveUserId (id / email / display name) for the invite fix below.
  • delete-message (0.1.4 → 0.2.0, minor) — post-message sends no as_user so it posts
    as the USER, while this action defaulted as_user: false, which routes to the BOT token. A
    message just posted could never be deleted (Pipedream SQL Source #20, cant_delete_message). Now retries with the
    other identity on that error; the default is unchanged so existing workflows are unaffected.
    Description also gained confirmation guidance for the destructive path (GitHub #33).
  • invite-user-to-channel (0.0.29 → 0.1.0, minor) — failed with user_not_found when the
    agent passed the email from the prompt (Update default timer to 15 mins #10). Now resolves user id / email / display name,
    and the channel by name.
  • get-channel-details, list-members-in-channel, set-channel-topic (→ 0.1.0, minor) — every
    AI-optimized action in this app resolves channel NAMES server-side and these did not, so an
    agent that read #seinfeld-general from a prompt got channel_not_found (Jotform #19, safely get oauth tokens #22) or a bare
    internal_error (Update default timer to 15 mins #10). All three now use resolveChannelId.
  • get-channel-history, get-thread-replies (→ 0.1.0, minor) — outputs measured at 17k chars
    average (worst 49k) and 25k average respectively; every call burned that much of the agent's
    context. Added the same additive fields projection.
  • get-user-details, get-current-user (patch) — description-only. NOTE: this did not
    change routing — the agent still picks get-current-user in every trial. The actual fix is
    removing one of the two from the /v3 component allowlist; these edits only make the
    intended split legible.

All payload changes are additive: omit fields and each action returns exactly what it
returns today.

Verification

  • Suite: 35 evals, 33 passing on Sonnet 5 at pass² (each eval run twice, passes only if both
    pass). Report: evals/slack_v2/results/2026-07-25T00-21-03-010Z.report.md.
  • Remaining two: Adding Mediums feed of updates. #1 (the identity duplication described above — an allowlist decision, not a
    component bug) and Fauna #26 (flaky — Slack rate-limits assistant.search.context under high eval
    concurrency; the retry fix here addresses the hard-error case, not the rate limit itself).
  • Not run: eslint (the working clone has no node_modules) and a Haiku confirming pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Channel actions accept names as well as IDs, with automatic resolution.
    • User references support IDs, emails, usernames, real names, and display names.
    • Channel lists support pagination, continuation cursors, and field selection.
    • Message history and thread replies support optional field selection.
    • Channel invitations support multiple users with individual results.
  • Bug Fixes

    • Message deletion retries with an alternate identity when eligible.
    • Assistant searches retry transient API failures.
  • Documentation

    • Added guidance for deletion safety, identity fallback, pagination, field selection, and user lookups.

Iterated against the MCP eval suite (pd-connect-eval-monster/evals/slack_v2).
Suite went 23/35 -> 33/35 on Sonnet 5 (pass^2); every change below is tied to a
specific red-to-green flip or a measured payload reduction.

- list-channels: evals #2/#10/#11/#16/#19/#35 all failed the same way — the
  action returned every field of every channel with numPages defaulting to 1, so
  a 62-channel workspace produced ~55k chars, blew the 25k-token MCP ceiling, and
  the model was handed a file path instead of data (#2 burned all 20 turns
  re-calling it). Added an additive `fields` projection, a `cursor` prop, and
  `has_more`/`next_cursor` so truncation is visible instead of silent. Payload
  55k -> 5.9k chars; all six evals pass.  [minor]

- slack_v2.app.mjs: `assistantSearch` called `sdk().apiCall()` directly — the one
  path in the app that bypassed `_withRetries` — while the client is built with
  `rejectRateLimitedCalls: true`, so every 429 rejected instantly. 8 of 12 search
  calls errored in one run (#24/#26/#27). Routed through `_withRetries`. Also adds
  `resolveUserId` (id / email / display name) for the invite fix below.

- delete-message: eval #20 ("post a note then take it back") failed every run with
  cant_delete_message — post-message sends no `as_user` so it posts as the USER,
  while this action defaulted `as_user: false`, which routes to the BOT token. A
  message just posted could never be deleted. Now retries with the other identity
  on cant_delete_message; the default is unchanged, so existing workflows are
  unaffected. Description also gained confirmation guidance for the destructive
  path (#33).  [minor]

- invite-user-to-channel: eval #10 failed with user_not_found — the agent passed
  the email from the prompt. Now resolves user id / email / display name, and the
  channel by name.  [minor]

- get-channel-details, list-members-in-channel, set-channel-topic: every
  AI-optimized action in this app resolves channel NAMES server-side and these did
  not, so agents that read "#seinfeld-general" from a prompt got channel_not_found
  (#19, #22) or a bare internal_error (#10). All three now use resolveChannelId.
  [minor]

- get-channel-history, get-thread-replies: outputs measured at 17k chars average
  (worst 49k) and 25k average respectively — every call burned that much of the
  agent's context. Added the same additive `fields` projection.  [minor]

- get-user-details, get-current-user: two tools answer "who am I" and the agent
  chose the legacy one in 10/10 trials (#1). Descriptions now differentiate them.
  NOTE: this did NOT change routing — the agent still picks get-current-user. The
  actual fix is removing one of the two from the /v3 component allowlist; these
  edits only make the intended split legible.  [patch]

App package.json bumped 0.6.1 -> 0.7.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 25, 2026 •

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Jul 25, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Slack v2 adds channel and user reference resolution, selective response fields, channel pagination continuation, delete-message identity fallback, assistant-search retries, and updated action, source, and package metadata.

Changes

Slack v2 action enhancements

Layer / File(s) Summary
Channel and user reference resolution
components/slack_v2/slack_v2.app.mjs, components/slack_v2/actions/get-channel-details/*, components/slack_v2/actions/invite-user-to-channel/*, components/slack_v2/actions/list-members-in-channel/*, components/slack_v2/actions/set-channel-topic/*
Actions resolve channel and user references before Slack API calls. Assistant search now uses retry handling.
Selective message response fields
components/slack_v2/common/utils.mjs, components/slack_v2/actions/get-channel-history/*, components/slack_v2/actions/get-thread-replies/*
Shared helpers support array or comma-separated field input. History and thread actions return projected messages when fields are supplied.
Channel listing pagination and filtering
components/slack_v2/actions/list-channels/*
List Channels accepts fields and cursor, follows page cursors, and returns has_more with conditional next_cursor.
Deletion identity fallback
components/slack_v2/actions/delete-message/*
Delete Message retries with the alternate identity only after cant_delete_message; other errors are rethrown.
Action guidance and package release
components/slack_v2/actions/*, components/slack_v2/sources/*, components/slack_v2/package.json
Action descriptions and versions are updated across Slack v2. Source versions and the package version are also incremented.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SlackAction
  participant SlackV2App
  participant SlackAPI
  SlackAction->>SlackV2App: resolve channel or user reference
  SlackV2App->>SlackAPI: look up reference
  SlackAPI-->>SlackV2App: return canonical ID
  SlackV2App-->>SlackAction: provide resolved ID
  SlackAction->>SlackAPI: execute operation
Loading

Possibly related PRs

Suggested labels: blocked

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the eval-driven fixes to the Slack v2 MCP tool surface.
Description check ✅ Passed The description provides detailed objectives, component changes, version updates, evaluation results, and known limitations.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mcp/slack_v2

Comment @coderabbitai help to get the list of available commands.

@dylburger dylburger left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per-change rationale inline — each comment cites the eval that motivated it, and says plainly where a change did not have the intended effect. Full suite and report live in pd-connect-eval-monster/evals/slack_v2.

version: "0.1.4",
description:
"Return a list of channels in a workspace."
+ " **Always pass `fields`** with just the properties you need (e.g. `id,name`) —"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: six evals (#2/#10/#11/#16/#19/#35) failed the same way — with numPages defaulting to 1 and every field of every channel returned, a 62-channel workspace produced ~55k chars, over the 25k-token MCP output ceiling. Claude Code spilled it to a file and handed the model a path, so it never saw the data; one eval burned all 20 turns re-calling this action.

Change: additive fields projection, a cursor prop, and has_more/next_cursor so a partial list is visible rather than silent.

Result: 55k → 5.9k chars, all six evals pass, and the agent now passes fields:["id","name"] and raises numPages unprompted. Minor — new props and new output fields; omit fields and the response is byte-identical to today.

return this.sdk().apiCall("assistant.search.context", {
// a method on WebClient — but it must STILL go through _withRetries. Calling
// apiCall bare was the one path in this app that skipped the retry wrapper, and
// the client is built with `rejectRateLimitedCalls: true`, so a 429 rejected

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: assistantSearch called sdk().apiCall() directly — the only path in this app that bypassed _withRetries — while the client is constructed with rejectRateLimitedCalls: true, so every 429 rejected instantly instead of backing off. Slack rate-limits assistant.search.context readily (retryAfter: 60): 8 of 12 search calls errored in one run (#24/#26/#27).

Change: routed through the same _withRetries wrapper every other method already uses.

Result: no more hard 429 tool errors.

version: "0.1.4",
description:
"Permanently delete a message. **This cannot be undone — confirm the exact message with the"
+ " user before calling it.** Identify the message first with **Get Channel History** or"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: eval #20 ("post a note then take it back") failed every run with cant_delete_message. post-message sends no as_user, so it posts as the USER; this action defaulted as_user: false, which makeRequest routes to the BOT token. A message the agent had just posted could never be deleted.

Change: try the configured identity, fall back to the other one on cant_delete_message. The default is untouched, so existing workflows behave exactly as before. The description also gained confirmation guidance for the destructive path (#33).

Result: #20 and #33 pass. Minor — backwards-compatible behaviour improvement.

version: "0.0.29",
description: "Invite a user to an existing channel. Accepts a channel ID or NAME, and a user ID, EMAIL address or display name — all resolved automatically. [See the documentation](https://api.slack.com/methods/conversations.invite)",
version: "0.1.0",
annotations: {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: eval #10 failed with user_not_found — asked to invite dylan@pipedream.com, the agent passed exactly that, and the raw API wants a user id.

Change: new resolveUserId app method (id passthrough / users.lookupByEmail / display-name scan), plus channel-name resolution.

Result: #10 passes. Minor — accepts more identifier shapes, rejects none.

version: "0.0.5",
description: "Retrieve details for a Slack channel by selecting it or providing an ID. Accepts a channel ID or NAME — names are resolved automatically. [See the documentation](https://api.slack.com/methods/conversations.info)",
version: "0.1.0",
type: "action",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: eval #22 got channel_not_found passing kramerica-industries. Every AI-optimized action in this app resolves channel names server-side and this one did not — agents cannot tell which is which, so they pass the name they read in the prompt.

Change: resolveChannelId.

Result: #22 passes. Minor.

version: "0.0.29",
description: "Set the topic on a selected channel. Accepts a channel ID or NAME — names are resolved automatically. [See the documentation](https://api.slack.com/methods/conversations.setTopic)",
version: "0.1.0",
annotations: {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: eval #10 got a bare internal_error — Slack's unhelpful response to a channel NAME where it wants an id.

Change: resolveChannelId.

Result: the topic step of #10 succeeds. Minor.

+ " Use this when you want to see a channel's latest messages — unlike **Search** which finds messages by keyword."
+ " Returns messages with text, timestamps (ts), reactions, and user IDs."
+ " Message timestamps can be used with **Get Thread Replies**, **Edit Message**, and **Add Reaction**."
+ " **Pass `fields`** (e.g. `text,ts,user`) unless you need full message objects — raw Slack"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: outputs measured at 17k chars average, worst case 49k — every call burned that much of the agent's context and degraded the rest of the task.

Change: additive fields projection (text,ts,user covers most tasks).

Result: a channel-summary eval now returns 1.6k chars instead of tens of thousands. Minor — omit fields and the response is byte-identical to today.

+ " Accepts a channel ID or channel name (resolved automatically)."
+ " Use **Get Channel History** or **Search** to find the parent message's timestamp (thread_ts)."
+ " Returns the parent message followed by all replies in chronological order."
+ " **Pass `fields`** (e.g. `text,ts,user`) unless you need full message objects — raw"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: outputs averaged 25k chars (worst 35k) on a long thread — raw Slack messages carry blocks, attachments and edit metadata.

Change: the same additive fields projection.

Result: thread reads stay well under the output ceiling. Minor.

description:
"Retrieve the authenticated user's identity and workspace context."
"**Use when the user asks who am I, what's my user ID, what's my username, which workspace"
+ " am I in, or what's my email** — this is the default identity lookup and the one to call"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: eval #1 ("who am I in Slack?") pins this tool, and the agent chose the legacy get-current-user in 10/10 trials across 7 runs.

Change: the description now leads with the phrasings users actually type and defers the full-profile case to get-current-user.

Result — honest caveat: this did NOT change routing. The agent still picks get-current-user. The tool NAME is the retrieval key under tool search, and "Get Current User" simply matches "who am I" better than "Get User Details"; no description tuning arbitrates two tools that answer one question. The real fix is dropping one of the two from the /v3 component allowlist — this edit only makes the intended split legible. Patch — description only.

version: "0.0.4",
description: "Do NOT use for a plain \"who am I\" / \"what's my user ID\" question — call **Get User Details** for that; it answers the same question with a far smaller payload. Use this ONLY when you specifically need the full member profile: locale, timezone, presence/status, admin and owner flags, name variants, or workspace domain and enterprise metadata. Combines `auth.test`, `users.info`, `users.profile.get` and `team.info`. [See Slack API docs](https://api.slack.com/methods/auth.test).",
version: "0.0.5",
type: "action",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why: paired with the get-user-details change above — two tools answer one question and the agent cannot arbitrate between them.

Change: the description now says explicitly not to use this for a plain "who am I", and scopes it to the full-profile case (locale, presence, admin flags).

Result: no measured routing change — see the note on get-user-details. Patch — description only.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/slack_v2/actions/get-channel-details/get-channel-details.mjs`:
- Line 6: Update the description at
components/slack_v2/actions/get-channel-details/get-channel-details.mjs:6 to
remove “by selecting it or providing an ID” and describe the channel input
directly by ID or name. Also update
components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs:6 to replace
“selected channel” with equivalent channel-ID-or-name wording, avoiding
UI-centric language in both descriptions.

In `@components/slack_v2/actions/list-channels/list-channels.mjs`:
- Line 117: Resolve the ESLint formatting violations in the field-splitting
expressions around the ternary branches at the referenced locations, including
the corresponding expression near the later occurrence. Reformat the chained
split, trim, and filter calls according to the project’s lint rules without
changing their behavior.

In `@components/slack_v2/slack_v2.app.mjs`:
- Line 1230: Fix the reported lint formatting in all three sites: expand the
destructuring declaration around makeRequest in
components/slack_v2/slack_v2.app.mjs (1230-1230) across multiple lines, and move
the chained .filter(Boolean) calls onto their own lines in
components/slack_v2/actions/get-channel-history/get-channel-history.mjs (82-82)
and components/slack_v2/actions/get-thread-replies/get-thread-replies.mjs
(75-75), without changing behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 92a797b1-8dc0-40e1-88ae-6645cfc1e145

📥 Commits

Reviewing files that changed from the base of the PR and between 934cd82 and d3c9318.

📒 Files selected for processing (12)
  • components/slack_v2/actions/delete-message/delete-message.mjs
  • components/slack_v2/actions/get-channel-details/get-channel-details.mjs
  • components/slack_v2/actions/get-channel-history/get-channel-history.mjs
  • components/slack_v2/actions/get-current-user/get-current-user.mjs
  • components/slack_v2/actions/get-thread-replies/get-thread-replies.mjs
  • components/slack_v2/actions/get-user-details/get-user-details.mjs
  • components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs
  • components/slack_v2/actions/list-channels/list-channels.mjs
  • components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs
  • components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs
  • components/slack_v2/package.json
  • components/slack_v2/slack_v2.app.mjs

Comment thread components/slack_v2/actions/get-channel-details/get-channel-details.mjs Outdated
Comment thread components/slack_v2/actions/list-channels/list-channels.mjs Outdated
Comment thread components/slack_v2/slack_v2.app.mjs Outdated
… UI-centric copy

- Lint Code Base: 9 errors, all in the files this branch touches and all
  formatting. Broke the `.split().map().filter()` chains across lines
  (newline-per-chained-call), split the `has_more` spread ternary and its object
  literal (multiline-ternary, object-curly-newline), and expanded the
  `users.list` destructure in resolveUserId (object-curly-newline).

- Ensure component commits modify component versions: this branch edits
  slack_v2.app.mjs, so every component importing it needs a version bump.
  Patch-bumped the 48 actions/sources that were not already versioned in the
  previous commit.

- Removed UI-centric wording from two descriptions, which read oddly for an
  agent calling the tool over MCP and contradicted the resolution behaviour
  added in the previous commit:
  - get-channel-details: "by selecting it or providing an ID" -> "specified by
    ID or by name" (it had claimed both "selecting it" and "Accepts a channel
    ID or NAME" in the same sentence).
  - set-channel-topic: "a selected channel" -> "a channel, specified by ID or
    by name".

Verified locally with ESLint 9 against the repo's own rule options for the three
failing rules: 0 violations across components/slack_v2.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/slack_v2/slack_v2.app.mjs (1)

1219-1219: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Do not treat every ID-shaped token as a confirmed Slack user ID.

The case-insensitive regex returns values such as user1234 immediately, so a valid exact name with that shape never reaches the paginated name lookup. Validate the ID against Slack (or fall back to name resolution when it is not confirmed) before returning it. Slack documents U/W prefixes but cautions against assuming a complete ID format. (api.slack.com)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/slack_v2/slack_v2.app.mjs` at line 1219, Update the input
resolution logic around the /^[UW].../ check so ID-shaped tokens are not
returned immediately. Confirm candidate IDs through Slack before returning them,
and fall back to the existing paginated name lookup when validation fails,
preserving direct handling for confirmed user IDs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@components/slack_v2/slack_v2.app.mjs`:
- Line 1219: Update the input resolution logic around the /^[UW].../ check so
ID-shaped tokens are not returned immediately. Confirm candidate IDs through
Slack before returning them, and fall back to the existing paginated name lookup
when validation fails, preserving direct handling for confirmed user IDs.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7abda907-7f2d-4cfe-9e02-37dc8ba49b7c

📥 Commits

Reviewing files that changed from the base of the PR and between d3c9318 and 0339a62.

📒 Files selected for processing (54)
  • components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs
  • components/slack_v2/actions/add-reaction/add-reaction.mjs
  • components/slack_v2/actions/approve-workflow/approve-workflow.mjs
  • components/slack_v2/actions/archive-channel/archive-channel.mjs
  • components/slack_v2/actions/browse-files/browse-files.mjs
  • components/slack_v2/actions/create-channel/create-channel.mjs
  • components/slack_v2/actions/create-reminder/create-reminder.mjs
  • components/slack_v2/actions/delete-file/delete-file.mjs
  • components/slack_v2/actions/edit-message/edit-message.mjs
  • components/slack_v2/actions/find-message/find-message.mjs
  • components/slack_v2/actions/find-user-by-email/find-user-by-email.mjs
  • components/slack_v2/actions/find-user-by-id/find-user-by-id.mjs
  • components/slack_v2/actions/get-channel-details/get-channel-details.mjs
  • components/slack_v2/actions/get-channel-history/get-channel-history.mjs
  • components/slack_v2/actions/get-file/get-file.mjs
  • components/slack_v2/actions/get-thread-replies/get-thread-replies.mjs
  • components/slack_v2/actions/kick-user/kick-user.mjs
  • components/slack_v2/actions/list-channels/list-channels.mjs
  • components/slack_v2/actions/list-emojis/list-emojis.mjs
  • components/slack_v2/actions/list-files/list-files.mjs
  • components/slack_v2/actions/list-group-members/list-group-members.mjs
  • components/slack_v2/actions/list-icon-emoji-options/list-icon-emoji-options.mjs
  • components/slack_v2/actions/list-messages/list-messages.mjs
  • components/slack_v2/actions/list-reminder-options/list-reminder-options.mjs
  • components/slack_v2/actions/list-replies/list-replies.mjs
  • components/slack_v2/actions/list-user-group-options/list-user-group-options.mjs
  • components/slack_v2/actions/list-users/list-users.mjs
  • components/slack_v2/actions/post-message/post-message.mjs
  • components/slack_v2/actions/reply-to-a-message/reply-to-a-message.mjs
  • components/slack_v2/actions/search/search.mjs
  • components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs
  • components/slack_v2/actions/send-large-message/send-large-message.mjs
  • components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs
  • components/slack_v2/actions/send-message-to-channel/send-message-to-channel.mjs
  • components/slack_v2/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs
  • components/slack_v2/actions/send-message/send-message.mjs
  • components/slack_v2/actions/set-channel-description/set-channel-description.mjs
  • components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs
  • components/slack_v2/actions/set-status/set-status.mjs
  • components/slack_v2/actions/update-group-members/update-group-members.mjs
  • components/slack_v2/actions/update-message/update-message.mjs
  • components/slack_v2/actions/update-profile/update-profile.mjs
  • components/slack_v2/actions/upload-file/upload-file.mjs
  • components/slack_v2/actions/verify-slack-signature/verify-slack-signature.mjs
  • components/slack_v2/slack_v2.app.mjs
  • components/slack_v2/sources/new-channel-created/new-channel-created.mjs
  • components/slack_v2/sources/new-interaction-event-received/new-interaction-event-received.mjs
  • components/slack_v2/sources/new-keyword-mention/new-keyword-mention.mjs
  • components/slack_v2/sources/new-message-in-channels/new-message-in-channels.mjs
  • components/slack_v2/sources/new-private-channel-created/new-private-channel-created.mjs
  • components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs
  • components/slack_v2/sources/new-saved-message/new-saved-message.mjs
  • components/slack_v2/sources/new-user-added/new-user-added.mjs
  • components/slack_v2/sources/new-user-mention/new-user-mention.mjs

@dylburger
dylburger marked this pull request as draft July 27, 2026 17:31
@michelle0927
michelle0927 marked this pull request as ready for review July 27, 2026 20:35

@michelle0927 michelle0927 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one minor comment.

Comment thread components/slack_v2/actions/get-channel-history/get-channel-history.mjs Outdated
@michelle0927

Copy link
Copy Markdown
Collaborator

Hi @dylburger, this has a merge conflict to resolve.

Dylan Sather and others added 6 commits August 3, 2026 14:12
…entity fallback

invite-user-to-channel: conversations.invite documents `users` as comma-separated and
the prop is a plain string, so "U01ABCDEF,U02GHIJKL" was a working input. The new
resolveUserId() matches neither the ID regex (no comma) nor the email regex, then
falls through to an exhaustive paged users.list scan that cannot match, and throws.
Adds resolveUserIds() (plural), which splits on commas — or accepts an array —
resolves each token independently, and rejoins. An all-IDs input still makes zero
extra API calls, so the common path is unchanged in cost as well as result.

delete-message: makeRequest() routes to the bot token only on `as_user === false`, so
`!this.as_user` made the retry a no-op whenever as_user arrived nullish — undefined
and true both route to the user token, so it retried as the identity that had just
been refused. Changed to `this.as_user === false`. Attempt 1 still passes the
configured value verbatim.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts:
#	components/slack_v2/actions/approve-workflow/approve-workflow.mjs
resolveUserId/resolveUserIds were inserted between resolveChannelId's doc comment
and resolveChannelId itself, leaving two stacked doc blocks above resolveUserId and
no doc on resolveChannelId. Comment move only; no code change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Addresses review feedback on #21493: `pickFields` was copy-pasted identically into
get-channel-history, get-thread-replies and list-channels.

Moved to components/slack_v2/common/utils.mjs, alongside the CSV-or-array `fields`
normalizer that was duplicated in the same three places — both halves of one feature,
so splitting them across files would just invite the next divergence.

`projectFields(records, fields)` composes the two and keeps the additive contract:
with no `fields`, it returns the ORIGINAL array, so a caller that omits the prop gets
exactly what these actions have always returned.

Behavior-identical to the three removed copies, verified against them across
undefined / empty array / empty string / array / CSV / CSV-with-spaces / unknown
field / all-unknown inputs, including array identity on the no-fields path.

No version bumps: all three components are already ahead of master on this branch,
so the PR diff still shows a version change for each.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs (1)

39-49: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve failures for bulk invitations.

When one resolved user is already in the channel, Slack rejects the bulk request and does not invite the remaining users. The catch at lines 46–49 reports this failed request as successful. Handle already_in_channel as success only for a single-user request, or invite users individually and report each outcome.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs`
around lines 39 - 49, Update the error handling around inviteToConversation in
the action so already_in_channel is treated as success only when the request
contains a single user. For bulk invitations, preserve the failure or invite
users individually and report each result, ensuring remaining users are not
hidden behind a successful summary; use the existing channel, user, and summary
flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/slack_v2/actions/delete-message/delete-message.mjs`:
- Around line 62-69: Update the retry logic around makeRequest’s attempt calls
to check bot-token availability before falling back from the initial as_user
value. Only call attempt(other) when a bot token exists and the alternate
identity differs; otherwise rethrow the original cant_delete_message error
without making a second request.

In `@components/slack_v2/actions/get-current-user/get-current-user.mjs`:
- Around line 7-10: Update the action description and related metadata for the
get-current-user action to remove the instruction to call it first in every
session. Scope its usage explicitly to requests requiring Slack identity data,
while preserving the existing identity lookup behavior.

In `@components/slack_v2/slack_v2.app.mjs`:
- Around line 1225-1230: Update resolveUserId(), used by resolveUserIds(), to
collect every exact match across all users.list pages instead of returning the
first match. Return the ID only when exactly one account matches; throw
ConfigurationError for ambiguous name matches and require callers to use an ID
or email, while preserving the existing no-match behavior.

---

Outside diff comments:
In
`@components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs`:
- Around line 39-49: Update the error handling around inviteToConversation in
the action so already_in_channel is treated as success only when the request
contains a single user. For bulk invitations, preserve the failure or invite
users individually and report each result, ensuring remaining users are not
hidden behind a successful summary; use the existing channel, user, and summary
flow.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e633c6ce-2140-4646-867b-bdf700c44d6e

📥 Commits

Reviewing files that changed from the base of the PR and between 3e8672a and 4b5e5ce.

📒 Files selected for processing (60)
  • components/slack_v2/actions/add-emoji-reaction/add-emoji-reaction.mjs
  • components/slack_v2/actions/add-reaction/add-reaction.mjs
  • components/slack_v2/actions/archive-channel/archive-channel.mjs
  • components/slack_v2/actions/browse-files/browse-files.mjs
  • components/slack_v2/actions/create-channel/create-channel.mjs
  • components/slack_v2/actions/create-reminder/create-reminder.mjs
  • components/slack_v2/actions/delete-file/delete-file.mjs
  • components/slack_v2/actions/delete-message/delete-message.mjs
  • components/slack_v2/actions/edit-message/edit-message.mjs
  • components/slack_v2/actions/find-message/find-message.mjs
  • components/slack_v2/actions/find-user-by-email/find-user-by-email.mjs
  • components/slack_v2/actions/find-user-by-id/find-user-by-id.mjs
  • components/slack_v2/actions/get-channel-details/get-channel-details.mjs
  • components/slack_v2/actions/get-channel-history/get-channel-history.mjs
  • components/slack_v2/actions/get-current-user/get-current-user.mjs
  • components/slack_v2/actions/get-file/get-file.mjs
  • components/slack_v2/actions/get-thread-replies/get-thread-replies.mjs
  • components/slack_v2/actions/get-user-details/get-user-details.mjs
  • components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs
  • components/slack_v2/actions/kick-user/kick-user.mjs
  • components/slack_v2/actions/list-channels/list-channels.mjs
  • components/slack_v2/actions/list-emojis/list-emojis.mjs
  • components/slack_v2/actions/list-files/list-files.mjs
  • components/slack_v2/actions/list-group-members/list-group-members.mjs
  • components/slack_v2/actions/list-icon-emoji-options/list-icon-emoji-options.mjs
  • components/slack_v2/actions/list-members-in-channel/list-members-in-channel.mjs
  • components/slack_v2/actions/list-messages/list-messages.mjs
  • components/slack_v2/actions/list-reminder-options/list-reminder-options.mjs
  • components/slack_v2/actions/list-replies/list-replies.mjs
  • components/slack_v2/actions/list-user-group-options/list-user-group-options.mjs
  • components/slack_v2/actions/list-users/list-users.mjs
  • components/slack_v2/actions/post-message/post-message.mjs
  • components/slack_v2/actions/reply-to-a-message/reply-to-a-message.mjs
  • components/slack_v2/actions/search/search.mjs
  • components/slack_v2/actions/send-block-kit-message/send-block-kit-message.mjs
  • components/slack_v2/actions/send-large-message/send-large-message.mjs
  • components/slack_v2/actions/send-message-advanced/send-message-advanced.mjs
  • components/slack_v2/actions/send-message-to-channel/send-message-to-channel.mjs
  • components/slack_v2/actions/send-message-to-user-or-group/send-message-to-user-or-group.mjs
  • components/slack_v2/actions/send-message/send-message.mjs
  • components/slack_v2/actions/set-channel-description/set-channel-description.mjs
  • components/slack_v2/actions/set-channel-topic/set-channel-topic.mjs
  • components/slack_v2/actions/set-status/set-status.mjs
  • components/slack_v2/actions/update-group-members/update-group-members.mjs
  • components/slack_v2/actions/update-message/update-message.mjs
  • components/slack_v2/actions/update-profile/update-profile.mjs
  • components/slack_v2/actions/upload-file/upload-file.mjs
  • components/slack_v2/actions/verify-slack-signature/verify-slack-signature.mjs
  • components/slack_v2/common/utils.mjs
  • components/slack_v2/package.json
  • components/slack_v2/slack_v2.app.mjs
  • components/slack_v2/sources/new-channel-created/new-channel-created.mjs
  • components/slack_v2/sources/new-interaction-event-received/new-interaction-event-received.mjs
  • components/slack_v2/sources/new-keyword-mention/new-keyword-mention.mjs
  • components/slack_v2/sources/new-message-in-channels/new-message-in-channels.mjs
  • components/slack_v2/sources/new-private-channel-created/new-private-channel-created.mjs
  • components/slack_v2/sources/new-reaction-added/new-reaction-added.mjs
  • components/slack_v2/sources/new-saved-message/new-saved-message.mjs
  • components/slack_v2/sources/new-user-added/new-user-added.mjs
  • components/slack_v2/sources/new-user-mention/new-user-mention.mjs

Comment thread components/slack_v2/actions/delete-message/delete-message.mjs
Comment thread components/slack_v2/actions/get-current-user/get-current-user.mjs
Comment thread components/slack_v2/slack_v2.app.mjs Outdated
…Id, invite-user-to-channel

- delete-message: only retry with the other identity when a bot token exists,
  since otherwise both attempts hit the same token and just repeat the error.
- resolveUserId: scan all users.list pages and require an exact, unambiguous
  name match; throw ConfigurationError instead of silently picking the first.
- invite-user-to-channel: treat already_in_channel as success only for a
  single user; bulk invites are now sent per-user with individual results so
  failures aren't hidden behind a successful summary.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs`:
- Line 39: Update the user resolution flow around resolveUserIds and the userIds
assignment to remove empty comma-separated tokens before building the Slack
request. When no resolved user IDs remain, throw ConfigurationError for the
invalid input instead of sending an empty users value; preserve normal behavior
for valid IDs.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8804d5e9-e06c-4f0f-997f-ae34966e9554

📥 Commits

Reviewing files that changed from the base of the PR and between 4b5e5ce and 5f2ce1e.

📒 Files selected for processing (3)
  • components/slack_v2/actions/delete-message/delete-message.mjs
  • components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs
  • components/slack_v2/slack_v2.app.mjs

Comment thread components/slack_v2/actions/invite-user-to-channel/invite-user-to-channel.mjs Outdated
…er list

resolveUserIds() returns "" when every comma-separated token is blank, and
"".split(",") yields [""] rather than [], which slipped through the
single-user path and sent an empty users value to Slack. Filter empty tokens
and throw ConfigurationError when none remain.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
michelle0927
michelle0927 previously approved these changes Aug 4, 2026

@michelle0927 michelle0927 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Ready for QA.

…ite as no-op

- delete-message now calls resolveChannelId() before deleting, like
  every other AI-optimized action in this app. Previously it passed
  `conversation` straight through, so a channel NAME (rather than ID)
  failed with channel_not_found.
- invite-user-to-channel: Slack refuses to let an identity invite
  itself (cant_invite_self) before it would ever reach an
  already_in_channel check. Treat that the same way — a no-op, not an
  error — since to the caller it means the same thing: the user has no
  further action needed on that channel.

Found via eval-driven regression testing on PR #21493.
…to-channel

Keep at 0.2.0/0.1.0 rather than bumping per fix.
@michelle0927

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants