# Slack (/context-providers/providers/slack)



Two tools: `query_slack` for searching and reading, `update_slack` for posting messages.

Complete [Setup](#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`:

```python title="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 [#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 [#setup]

In a [virtual environment](/sdk/setup), install the SDKs and configure your Slack app tokens:

```shell
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](/tools/toolkits/social/slack#toolkit-params) for OAuth scope requirements.

## Example queries [#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 [#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 [#readwrite-modes]

Control what the agent can do:

```python
# 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 [#multi-provider-example]

After completing [Google Drive setup](/context-providers/providers/drive) too, replace the body of `main()` with this composition fragment. Add the imports at module scope:

```python
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 [#tips]

* **Token naming**: `SLACK_BOT_TOKEN` is preferred; `SLACK_TOKEN` is a fallback.
* **Search**: `search_messages` needs a user token. Set `SLACK_USER_TOKEN` or pass `user_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_thread` call defaults to at most 20 messages and does not paginate.
* **Channel names**: Natural names like `#general` work. The sub-agent resolves them to IDs.
* **Write clarity**: "Post this to #team" is safer than vague "send it."

## Resources [#resources]

<CardGroup cols="2">
  <Card title="SlackTools Reference" icon="wrench" href="/tools/toolkits/social/slack">
    Toolkit methods and OAuth scope requirements
  </Card>

  <Card title="Cookbook Example" icon="book" href="https://github.com/agno-agi/agno/blob/main/cookbook/12_context/05_slack.py">
    Working example with search and posting
  </Card>
</CardGroup>
