Slack
Search Slack conversations, read threads, and post messages.
Two tools: query_slack for searching and reading, update_slack for posting messages.
Complete Setup before running this example. Search needs a user token in addition to the bot token. Save as slack_context.py and run python slack_context.py:
import asyncio
from agno.agent import Agent
from agno.context.slack import SlackContextProvider
from agno.models.openai import OpenAIResponses
async def main():
slack = SlackContextProvider(write=False, model=OpenAIResponses(id="gpt-5.4-mini"))
agent = Agent(
model=OpenAIResponses(id="gpt-5.4"),
tools=slack.get_tools(),
instructions=slack.instructions(),
)
await agent.aprint_response("What did the team discuss about the auth migration?")
if __name__ == "__main__":
asyncio.run(main())The agent can call query_slack; its sub-agent searches and reads relevant threads. This example exposes no posting tool.
When to use this vs SlackTools
| Use SlackContextProvider when... | Use SlackTools directly when... |
|---|---|
| Slack is one of several context sources | Slack is the primary task surface |
| You want a small source-level tool surface | You need fine control over individual API calls |
| The agent should ask questions, not orchestrate API calls | You're building a Slack-specific agent |
Setup
In a virtual environment, install the SDKs and configure your Slack app tokens:
uv pip install -U agno openai slack-sdk aiohttp
export OPENAI_API_KEY="your-openai-api-key"
export SLACK_USER_TOKEN=xoxp-...
export SLACK_BOT_TOKEN=xoxb-...Or pass directly: SlackContextProvider(token="xoxb-...").
Install the Slack app in your workspace with the scopes needed for search and channel/thread reads. Invite it to private channels it must read. With only a bot token, ask about a specific accessible channel or thread; full-text search is unavailable.
See SlackTools for OAuth scope requirements.
Example queries
Queries that work well give the provider a topic to search and context to narrow results:
| Query | Why it works |
|---|---|
| "What did the team decide about billing migration?" | Topic-driven search with synthesis |
| "Summarize the thread where Alex discussed OAuth scopes" | Combines search + thread reading |
| "What open questions came up in #launch this week?" | Channel + time constraints |
| "Post a short update to #support saying the issue is resolved" | Clear channel + action |
Queries that are too vague:
| Less effective | Better |
|---|---|
| "Search Slack" | "Search for recent discussion about webhook retries" |
| "What happened?" | "What happened in #incidents about the API outage?" |
| "Post it" | "Post this summary to #engineering" |
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
id | str | "slack" | Changes tool names to query_<id> and update_<id>. |
model | Model | None | Model for the sub-agents. Defaults to Agno's default model. |
read | bool | True | Expose query_slack. |
write | bool | True | Expose update_slack. |
enable_media_tools | bool | False | Enables download_file for read tools and upload_file for write tools. |
mode | ContextMode | default | default exposes both tools. tools exposes read-only SlackTools. |
Read/write modes
Control what the agent can do:
# Research agent: can search but not post
slack = SlackContextProvider(write=False)
# Notification agent: can post but not search
slack = SlackContextProvider(read=False)Use write=False for research, triage, audit, and eval agents. The read sub-agent physically cannot post because send_message isn't in its toolkit.
Multi-provider example
After completing Google Drive setup too, replace the body of main() with this composition fragment. Add the imports at module scope:
from agno.context.slack import SlackContextProvider
from agno.context.gdrive import GoogleDriveContextProvider
slack = SlackContextProvider(write=False)
drive = GoogleDriveContextProvider()
agent = Agent(
model=OpenAIResponses(id="gpt-5.4"),
tools=[*slack.get_tools(), *drive.get_tools()],
instructions="Use Slack for team discussion, Drive for docs. Note when they disagree.",
)
await agent.aprint_response("What's the current auth spec and did engineering raise concerns?")The agent sees 2 tools (query_slack, query_gdrive) on the calling agent.
Tips
- Token naming:
SLACK_BOT_TOKENis preferred;SLACK_TOKENis a fallback. - Search:
search_messagesneeds a user token. SetSLACK_USER_TOKENor passuser_token=. With only a bot token, the read sub-agent uses channel history and threads. - Private channels: The bot must be invited to access private channel history.
- Thread context: Ask for "the thread" or "the decision" to read replies rather than only the parent. The current
get_threadcall defaults to at most 20 messages and does not paginate. - Channel names: Natural names like
#generalwork. The sub-agent resolves them to IDs. - Write clarity: "Post this to #team" is safer than vague "send it."