Skip to content

slack_explore_workspace: expose DM channel IDs and add im:read to the tool's scope set #145

Description

@peteski22

Summary

slack_explore_workspace currently surfaces channels (public + private) and users, but the user listing has no link back to the per-user direct-message channel. Callers that want to send a DM must either ask the model to call conversations.open themselves or carry that mapping out-of-band.

The Slack Web API exposes existing DM channels via conversations.list?types=im, which returns { id, user } pairs for every IM channel the user already has open. Including this in the workspace listing lets the model resolve user_id → dm_channel_id directly from one discovery call, avoiding a follow-up conversations.open round-trip in the common case.

Current behavior

The ## Users section of the tool's output looks like:

## Users
- Alice (U01234567)
- Bob (U01234568)

Proposed behavior

Append — DM channel: <id> to a user row when an IM channel already exists for that user:

## Users
- Alice (U01234567) — DM channel: D01234567
- Bob (U01234568)

Rows for users without an existing IM channel are unchanged. The docstring example for the tool should be updated to match.

Implementation sketch

  1. Scope — add SlackScope.IM_READ (already defined in apron_tools.providers.slack.scopes) to slack_explore_workspace's entry in SCOPES.
  2. Fetch — after fetching users.list, paginate conversations.list?types=im&limit=1000, threading cursor from response_metadata.next_cursor until exhausted. Collect { user_id: channel_id }.
  3. Annotate — when emitting each user row, look up the user's id in that map and append " — DM channel: {id}" if present; otherwise emit the row unchanged.
  4. Partial failure — if the conversations.list IM call raises, treat it as a soft failure consistent with the existing per-type channel fetch (contextlib.suppress(SlackApiError)): users without DM channel IDs are still listed and the rest of the output is unaffected. If the error is missing_scope, the result should still degrade gracefully (the scope set already recommends im:read so the consent modal can surface it).

Why im:read (not im:history)

im:read grants the bare minimum: list IM channels and their members. The tool only needs the { id, user } mapping; it does not read message history. im:history would be over-privileged for this use case.

Test coverage to add

  • Happy path: two users, one with an existing IM channel, one without — assert the row format differs.
  • DM pagination: conversations.list returns next_cursor, second page contains the matching IM — assert that user's row gains the suffix.
  • missing_scope on conversations.list (im): assert the user listing still renders (without DM suffixes).
  • Generic conversations.list failure: assert the listing still renders.

Out of scope (and why)

  • Changes to slack_send_direct_message (echoing the DM channel ID in success output, improved error formatting). Why: these are separable, additive improvements with no shared code path with the discovery tool — folding them in would widen the diff and couple two unrelated changes. Happy to file separately if useful.
  • Caching the user → DM-channel map between invocations. Why: the tool is discovery-shaped — a fresh fetch per call keeps it stateless and avoids stale mappings when DMs are opened/closed between calls. The added latency (one paginated conversations.list) is acceptable for a discovery call.

Compatibility

Pure additive: the change adds one scope to the tool's recommended set and one optional suffix per user row. Callers that already grant im:read see DM channels appear; callers that don't see the existing output unchanged. No tool name, parameter shape, or output schema change beyond the suffix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestproviderIssue or PR scoped to a specific providerslackProvider: slack

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions