feat(gateway): capability-gated Slack read tools (channel history) + capability model#1848
Open
kgabryje wants to merge 1 commit into
Open
Conversation
…capability model
Per-channel agent_tools config gates agent-callable Slack MCP tools and
drives the manifest scopes from one source of truth:
- config.agent_tools.{thread_history,channel_history} booleans on Slack
gateway channels; thread_history defaults enabled (preserves shipped
behavior), channel_history defaults disabled (new data surface).
- requireGatewayCapability fails on call with an actionable error (the
MCP tool registry is a global singleton, so per-channel hiding at
discovery is not possible).
- SLACK_AGENT_TOOL_SCOPES maps each capability to its Slack OAuth
scopes; requiredBotScopes consumes it via SlackWizardOptions.agentTools
so tool-gating and manifest scopes cannot drift.
- New agor_gateway_slack_channel_history_get tool wrapping
conversations.history: capability-gated, branch-bound like the thread
tool, whitelist-enforced, defaulting to the calling gateway session's
own channel.
- Existing thread-history tool now gated behind thread_history.
- Toggles surfaced in the UI wizard/edit form and the MCP manifest tool
the same way ingest_files is wired.
Slack search is intentionally not exposed: search.messages is
user-token-only and assistant.search.context needs a user-interaction
action_token.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces the Slack capability model — per-channel checkboxes that map 1:1 to agent-visible MCP tools — and lands its first new consumer: reading whole-channel history (#1731).
Capability model (single source of truth)
config.agent_toolsobject on Slack gateway channels (SlackAgentToolsConfiginpackages/core/src/types/gateway.ts): booleans for agent-callable tools, currentlythread_historyandchannel_history. Extending it (PR D: reactions, file upload) is one seam — add a key, a default, and a scope list.requireGatewayCapability(channel, capability)gates tools at call time with an actionable error (which capability, that an admin enables it on the channel, and that enabling can add Slack scopes requiring a manifest update + reinstall). Fail-on-call rather than hide-at-discovery because the MCP tool registry is a global singleton built once for all channels/callers.gateway_sourceis still the default way the new tool resolves its target channel.SLACK_AGENT_TOOL_SCOPESinslack-manifest.tsmaps each capability to its OAuth scopes andrequiredBotScopesconsumes it via the new requiredSlackWizardOptions.agentTools. Toggles are surfaced in the UI wizard/edit form andagor_gateway_slack_manifest_generateexactly likeingestFilesin feat(gateway): ingest inbound Slack image attachments #1847.Defaults (no behavior regression)
thread_historydefaults enabled — the thread tool shipped ungated, so absent config keeps existing channels working. It contributes no extra scopes (mapped threads only exist on surfaces whose history scopes the manifest already requests).channel_historydefaults disabled — reading arbitrary channel content is a broader data surface than the mapped thread and needs scopes (channels:history,groups:history,mpim:history) the installed app may not hold, so it's explicit opt-in.New read tool:
agor_gateway_slack_channel_history_get(#1731)SlackConnector.fetchChannelHistorywrapsconversations.history(shared pagination/normalization loop withfetchThreadHistory), enforcesallowed_channel_ids, and returns the most recent matches in chronological order. Params mirror the thread tool (oldestTs/latestTs/limit≤200/includeBotMessages/format: messages|markdown).all.agor_gateway_slack_thread_history_getis now gated behindthread_history.Out of scope: Slack search is intentionally excluded —
search.messagesis user-token-only andassistant.search.contextneeds a user-interactionaction_token, so neither works with the gateway bot token (noted in code). The optionaluser_lookuptool was deferred to keep the PR focused. Outbound reactions/file-upload/thread-reply land in PR D on top of this.Tests
slack-manifest.test.ts: capability→scope mapping (on → history scopes present, off/absent → absent), thread_history contributes none,resolveSlackAgentToolsdefaults + malformed input.slack.test.ts:fetchChannelHistorynormalization/ordering/bot filtering, most-recent-first truncation,allowed_channel_idsallow/deny before any API call.gateway-channels.test.ts: channel tool happy path via gateway-session defaults, markdown format, capability-disabled → actionable error, cross-branch denied even for admins, no-session path requires explicit IDs + admin/branch-all, thread tool denied whenthread_historydisabled (absent config covered by existing tests), manifest tool scope/config-hint wiring + schema defaults.pnpm typecheck,lint,build, and core (2772) / daemon (1529) / UI (796) test suites green. Thecheck:multitenancy-boundaries5-file failure,@agor/gitno-test-files exit, and 2 executor test failures are pre-existing on the base (verified via stash) and untouched by this PR.🤖 Generated with Claude Code