slack_searchlets the agent search the workspace with the permissions of the person who asked.slack_post_messagelets an agent reached over HTTP, A2A, or the CLI post into a channel as the bot, so an alert-driven or scheduled run can report into Slack.
Create the Slack app
Create the app from a manifest at api.slack.com/apps (Create New App → From a manifest), pick the workspace, and paste the manifest below. Adjustdisplay_information.name and the bot’s display_name.
Then:
- Under Basic Information → App-Level Tokens, generate a token with the
connections:writescope. This is thexapp-token. - Under Install App, install the app to the workspace. The Bot User OAuth Token on that page is the
xoxb-token. - Invite the bot to any channel it should answer in (
/invite @aura).
Enable the ingress
The ingress is off by default and is a deployment-level setting, like A2A. It needs both tokens.slack ingress enabled bot_user="aura" team="..." agent="..." followed by slack socket mode connected. The HTTP API keeps working alongside the ingress.
The agent config must not have a [hitl] block. See Limitations.
What the bot answers
When the bot accepts a message it reacts with 👀, runs the agent with the conversation history as prior turns, and posts the agent’s final text as the reply. If the run fails, it replies “Sorry, I hit an error answering that. The server log has the details.” and logs the cause.
History is rebuilt on every message from Slack (
conversations.replies for threads, conversations.history for direct messages, up to 100 messages); the server stores nothing between messages. Edits, deletions, joins, and messages from other bots are ignored.
Every run’s system prompt gains one line naming the channel (<#C…>) or DM and the sender (<@U…>), because neither the message text nor the history says where it came from. The agent’s reply is posted as Slack mrkdwn: *bold*, `code`, and bullet lists render, while Markdown headings and tables do not, so tell the agent in its system prompt to answer in that form.
Replies without a mention
Answering a thread reply that does not mention the bot is opt-in per Slack app. It needs themessage.channels and message.groups bot events (and the matching channels:history and groups:history scopes) from the manifest above. With those events subscribed, the bot answers un-mentioned replies in any thread it has already answered in, which makes a thread a conversation rather than a series of mentions.
An app that subscribes only to app_mention and message.im gets a mention-only bot in channels. That is the quieter choice for busy shared channels, and it is what you get if you drop the two events from the manifest.
Note that these replies arrive without an action_token, so the slack_search tool is absent on those runs. See Limitations.
Search Slack as the asker
With the Agents & AI Apps feature enabled on the app (agent_view in the manifest), Slack attaches an action_token to each mention and direct message. A run started by such a message gets a native slack_search tool that calls Slack’s assistant.search.context with the bot token plus that token, so results carry the visibility of the person asking, and Slack narrows them to the channel the message was sent in.
- The tool takes
query, an optionallimit, and an optionalcursorfor the next page. - Plain words or a natural-language question find things; quoted phrases and
ORchains usually return nothing. - One message has a budget of six searches shared by every agent working on it; a spent budget comes back as a tool error. Slack also rate limits searches per person, so the prompt tells the agent to spend one plain query first and reword at most once.
- The system prompt gains a citation instruction: results are cited inline as
<permalink|label>, which Slack renders as a link. - A bot token can hold only
search:read.public(plussearch:read.filesandsearch:read.users), so only public channels are searched. The search scopes for private channels, direct messages, and group direct messages exist for user tokens only. - Under orchestration, every worker gets its own instance of the tool; the coordinator, whose tools are routing only, gets none.
Post to Slack from any ingress
An agent that is reached over/v1/chat/completions, A2A, or the CLI in HTTP mode can post into a channel as the bot. It needs the server flag and a per-agent opt-in:
--enable-slack on and enable_slack_tools = true, the agent gets a native slack_post_message tool:
- Arguments:
channel(an id such asC0123456789or a name such as#incidents),text(mrkdwn), and an optionalthread_tsto reply in a thread. - Returns the message
ts, the channel id, and a permalink the agent can cite. - One run may post five times. Under orchestration every worker gets an instance and the run shares one budget.
- Slack errors are rephrased for the agent (channel not found, bot not a member, archived, text too long, missing scope, rate limited) and never carry the token.
GET /aura/info?detail=toolslists the tool under the opted-in agent, beside its MCP servers.
chat:write. Posting into a public channel it has not joined also needs chat:write.public; otherwise invite it. An opt-in on a server started without --enable-slack is warned about at startup and builds no tool. Runs that Slack itself started keep the reply path only and do not get the tool.
Agents that do not opt in build exactly as on a server without Slack.
Connection lifecycle
- Slack asks for a fresh Socket Mode connection roughly every five hours; the server reconnects at once.
- Any other end of the connection, including a peer that drops the link, reconnects after a delay that doubles from one second up to one minute.
- A connection with no frame for two minutes, Slack’s keepalive pings included, is dropped and redialled. A network path that dies without a close (a route change, an expired NAT entry) would otherwise leave the connection established and silent forever.
- Slack-driven runs count as active requests: on
SIGTERMthe server stops accepting new messages and gives in-flight runs theSHUTDOWN_TIMEOUT_SECSgrace period.
app_mention and message.im, and a payload not acknowledged within a few seconds is redelivered to any connection, and with several replicas either can produce two replies.
Limitations
- No human-in-the-loop approvals. The conversational Slack surface has nowhere to approve a tool call, so the ingress refuses to start on an agent config that has a
[hitl]block (the error names the agent). Give the Slack agent a config without[hitl], and keep gated write tools out of itsmcp_filtersince nothing would gate them. The HITL webhook route remains available to the same server’s HTTP and A2A callers. - Thread follow-ups cannot search. Slack attaches the
action_tokento mention and direct-message events but not to the thread replies delivered bymessage.channelsandmessage.groups. A reply without a mention therefore runs withoutslack_search, even in a thread whose first question had it. Mentioning the bot in the follow-up restores the tool. Tracked as mezmo/aura#776. - Public channels only.
slack_searchsees whatsearch:read.publicallows. Private channels, direct messages, and group direct messages are not searchable with a bot token. - Orchestration planners may not see run tools. With
tools_in_planning = "summary", the planner lists the firstmax_tools_per_workertool names per worker (default 10) and the run’s own tools come last, so on workers with longmcp_filterlistsslack_searchhides behind(+N more). Raisemax_tools_per_worker, or tell the coordinator in its system prompt that every worker holdsslack_searchon a run that a mention or DM started. - One agent per server. The ingress runs the agent named by
--slack-agent; other loaded configs stay reachable over HTTP and A2A only. - Replies come from the bot, not the agent that was asked. Runs started from Slack reply in place and do not get
slack_post_message; cross-posting from a Slack-started run is not supported.
See also
- Web Server Reference: the full flag and environment-variable list
- Configuration Reference:
enable_slack_toolsand the[orchestration]planner settings - HITL: approval gates on the HTTP and A2A paths

