Skip to content

feat(tools): add SlackSendMessageTool and SlackChannelHistoryTool - #6689

Open
Shubhanshi-Gaudani wants to merge 2 commits into
crewAIInc:mainfrom
Shubhanshi-Gaudani:feat/crewai-tools-slack-tool
Open

feat(tools): add SlackSendMessageTool and SlackChannelHistoryTool#6689
Shubhanshi-Gaudani wants to merge 2 commits into
crewAIInc:mainfrom
Shubhanshi-Gaudani:feat/crewai-tools-slack-tool

Conversation

@Shubhanshi-Gaudani

Copy link
Copy Markdown

Summary

  • Adds SlackSendMessageTool (posts a message, optionally as a threaded reply via thread_ts) and SlackChannelHistoryTool (fetches recent channel messages for context), both under lib/crewai-tools/src/crewai_tools/tools/slack_tool/
  • Uses slack_sdk (added as an optional dependency, extra name slack-sdk), lazily imported so it's not a hard dependency for users who don't need it
  • Requires a SLACK_BOT_TOKEN env var; both tools validate its presence at init and return a descriptive string (rather than raising) on Slack API errors, following the pattern used by other tools in this repo

Why

There's currently no Slack tool in crewai-tools, despite Slack being one of the most common integration targets for agentic workflows (notifying a channel, or giving an agent context from recent conversation). This fills that gap.

Testing

  • 8 new unit tests in tests/tools/slack_tool_test.py, all mocking slack_sdk (no real Slack workspace/token touched), covering: missing-token validation, happy path for both tools, thread replies, Slack API error handling, empty-history handling, and input validation (limit bounds)
  • ruff check, ruff format --check, and mypy all pass on the new source
  • Full existing lib/crewai-tools/tests/ suite run for regressions: 270 passed, 2 skipped; remaining failures are pre-existing environment gaps unrelated to this change (missing optional oxylabs dependency, an interactive MongoDB test prompt)
  • uv.lock regenerated with the exact uv version pinned in CI (0.11.3) to keep the diff minimal

Disclosure

This PR was developed with AI assistance (Claude Code), with human review of the design, code, and test coverage before submission.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds optional Slack SDK support and two CrewAI tools for posting messages and retrieving channel history. The tools validate configuration, handle Slack API errors, expose synchronous and asynchronous entry points, and include documentation and mocked tests.

Changes

Slack tools integration

Layer / File(s) Summary
Slack tool runtime and validation
lib/crewai-tools/pyproject.toml, lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py, lib/crewai-tools/tests/tools/slack_tool_test.py
Adds the optional slack-sdk dependency, Slack message and history schemas, prerequisite validation, Slack API calls, formatted responses, asynchronous wrappers, and tests for posting, threading, history retrieval, validation, empty results, and API errors.
Public exports and documentation
lib/crewai-tools/src/crewai_tools/tools/slack_tool/__init__.py, lib/crewai-tools/src/crewai_tools/tools/__init__.py, lib/crewai-tools/src/crewai_tools/__init__.py, lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md
Exports both Slack tools from package entry points and documents installation, configuration, permissions, usage, channel identifiers, and error behavior.

Sequence Diagram(s)

sequenceDiagram
  participant SlackTool
  participant WebClient
  participant SlackAPI
  SlackTool->>WebClient: Call chat_postMessage or conversations_history
  WebClient->>SlackAPI: Send Slack API request
  SlackAPI-->>WebClient: Return response or API error
  WebClient-->>SlackTool: Return message data or error
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding two Slack tools.
Description check ✅ Passed The description matches the changeset by describing the new Slack tools, optional dependency, token handling, and tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Shubhanshi-Gaudani

Copy link
Copy Markdown
Author

Note for maintainers: this PR was developed with AI assistance (Claude Code) per the AI-contribution guidelines — flagging for the llm-generated label since I don't have permission to add it myself.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py (1)

54-60: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the public tool classes in code.

  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py#L54-L60: add a class docstring describing message posting and threaded replies.
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py#L124-L132: add a class docstring describing history retrieval, ordering, and scope expectations.

As per coding guidelines, “Document public APIs and complex logic in Python code.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py` around
lines 54 - 60, Document both public Slack tool classes: add a class docstring to
SlackSendMessageTool describing channel message posting and threaded replies,
and add a class docstring to the history-retrieval tool at
lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py lines 124-132
describing result ordering and required Slack scope expectations.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md`:
- Around line 10-14: Update the Installation instructions in the Slack tool
README so the pip command explicitly installs the Slack SDK extra, matching the
existing crewai-tools optional extra declaration; keep the uv command unchanged.

In `@lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py`:
- Around line 100-105: Prevent synchronous Slack HTTP work from blocking the
event loop in both SlackSendMessageTool._arun and SlackChannelHistoryTool._arun.
Use AsyncWebClient with the required slack-sdk version, or offload each existing
_run call via asyncio.to_thread, and add run_async() coverage that verifies
event-loop responsiveness during Slack I/O; apply the same change at both
affected sites in slack_tool.py (lines 100-105 and 175-176).
- Around line 116-120: The Slack history limit still defaults to 20 and permits
values above Slack’s supported cap. In slack_tool.py, update both sync and async
limit defaults to 15 and set Field validation maximum to 15; update the limit=20
usage in lib/crewai-tools/tests/tools/slack_tool_test.py and
lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md to limit=15.
- Around line 87-89: Update SlackTool’s chat_postMessage calls in
lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py:87-89 to
generate one deterministic client_msg_id per operation and pass it for both
top-level and threaded messages. In
lib/crewai-tools/tests/tools/slack_tool_test.py:48-50 and :66-68, assert that
client_msg_id is included for top-level posts and thread replies.

---

Nitpick comments:
In `@lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py`:
- Around line 54-60: Document both public Slack tool classes: add a class
docstring to SlackSendMessageTool describing channel message posting and
threaded replies, and add a class docstring to the history-retrieval tool at
lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py lines 124-132
describing result ordering and required Slack scope expectations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a64d8adc-02d8-4b77-967a-0c47fab953e0

📥 Commits

Reviewing files that changed from the base of the PR and between e2c8d7c and 9c05d99.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • lib/crewai-tools/pyproject.toml
  • lib/crewai-tools/src/crewai_tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/__init__.py
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py
  • lib/crewai-tools/tests/tools/slack_tool_test.py

Comment thread lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md
Comment thread lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py
Comment thread lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py
Comment on lines +116 to +120
limit: int = Field(
20,
ge=1,
le=200,
description="Maximum number of messages to fetch, most recent first.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

What default and maximum conversations.history limit currently applies to new commercially distributed Slack apps that are not approved for the Slack Marketplace?

💡 Result:

For new commercially distributed Slack applications that are not approved for the Slack Marketplace, both the default and maximum values for the limit parameter in the conversations.history method are set to 15 objects [1]. These restrictions, which became effective on May 29, 2025, also include a significantly reduced rate limit of 1 request per minute for this method [1]. Existing installations of commercially distributed applications created prior to this date are not subject to these new limits [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -a 'slack_tool.py|slack_tool_test.py|README.md' lib/crewai-tools 2>/dev/null | sed 's#^\./##'

echo
echo "== relevant source snippets =="
for f in \
  "lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py" \
  "lib/crewai-tools/tests/tools/slack_tool_test.py" \
  "lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md"
do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    nl -ba "$f" | sed -n '/limit/,$p' | sed -n '1,180p'
  fi
done

echo
echo "== deterministic check of SlackToolField range vs Slack cap for new commercially-distributed non-Marketplace apps =="
python3 - <<'PY'
from pathlib import Path
import re

tool_file = Path("lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py")
text = tool_file.read_text()

m = re.search(r'limit:\s*int\s*=\s*Field\s*\(\s*(\d+)[^)]*ge=\s*(\d+)[^)]*le=\s*(\d+)', text, re.S)
sl = SlackDevData := {
    "commercials_non_marketplace_new_since_29_may_2025": {
        "default": 15,
        "maximum": 15,
    }
}
print("field_default=", m.group(1) if m else None)
print("field_gte=", m.group(2) if m else None)
print("field_lte=", m.group(3) if m else None)
print("supported_max=", SlackDevData["commercials_non_marketplace_new_since_29_may_2025"]["default"])
print("default_exceeds_supported_cap=", int(m.group(1)) if m else None, ">", SlackDevData["commercials_non_marketplace_new_since_29_may_2025"]["default"])
print("upper_bound_exceeds_supported_cap=", int(m.group(3)) if m else None, ">", SlackDevData["commercials_non_marketplace_new_since_29_may_2025"]["maximum"])
PY

Repository: crewAIInc/crewAI

Length of output: 7521


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant source snippets =="
for f in \
  "lib/crew ai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py" \
  "lib/crewai-tools/tests/tools/slack_tool_test.py" \
  "lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md"
do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    awk 'NR>=90 && NR<=140 {printf "%6d\t%s\n", NR, $0}' "$f"
  fi
done

echo
echo "== focused limit occurrences in Slack tool files =="
python3 - <<'PY'
from pathlib import Path
paths = [
    Path("lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py"),
    Path("lib/crewai-tools/tests/tools/slack_tool_test.py"),
    Path("lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md"),
]
for p in paths:
    print(f"--- {p} ---")
    for i, line in enumerate(p.read_text().splitlines(), 1):
        if "limit" in line.lower():
            print(f"{i:4d}: {line}")
PY

echo
echo "== deterministic Field bounds check =="
python3 - <<'PY'
import re
from pathlib import Path

text = Path("lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py").read_text()
m = re.search(r'limit:\s*int\s*=\s*Field\s*\(\s*(\d+)[^)]*ge=\s*(\d+)[^)]*le=\s*(\d+)', text, re.S)
print("current_field_default=", int(m.group(1)) if m else None)
print("current_field_upper=", int(m.group(3)) if m else None)
print("slack_new_commercial_non_marketplace_max=", 15)
print("default_exceeds_slack_max=", (int(m.group(1)) if m else None) > 15)
print("upper_bound_exceeds_slack_max=", (int(m.group(3)) if m else None) > 15)
PY

Repository: crewAIInc/crewAI

Length of output: 3492


Lower the Slack history limit cap and default.

Slack caps conversations.history at 15 for new commercially distributed non-Marketplace apps, while this tool still defaults to 20 and allows values up to 200, so valid public app usage can hit a rejected request.

  • Default the sync/async limit to 15 and update the Field validation to a maximum that matches Slack’s supported cap when required.
  • Update the test and docs example that currently use limit=20.
📍 Affects 3 files
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py#L116-L120 (this comment)
  • lib/crewai-tools/tests/tools/slack_tool_test.py#L103-L108
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md#L23-L24
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py` around
lines 116 - 120, The Slack history limit still defaults to 20 and permits values
above Slack’s supported cap. In slack_tool.py, update both sync and async limit
defaults to 15 and set Field validation maximum to 15; update the limit=20 usage
in lib/crewai-tools/tests/tools/slack_tool_test.py and
lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md to limit=15.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai-tools/tests/tools/slack_tool_test.py`:
- Around line 177-202: Update test_send_message_arun_does_not_block_event_loop
to make the mocked chat_postMessage block via a controlled side effect until the
heartbeat has progressed, then release it and return the successful Slack
response. Assert heartbeat progress while the call remains blocked, preserving
the existing result assertion afterward.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5c2d27d3-4905-4c31-893d-1e79fa544000

📥 Commits

Reviewing files that changed from the base of the PR and between 9c05d99 and b50cf21.

📒 Files selected for processing (3)
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.md
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py
  • lib/crewai-tools/tests/tools/slack_tool_test.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.py

Comment on lines +177 to +202
@pytest.mark.asyncio
@patch("crewai_tools.tools.slack_tool.slack_tool._require_slack_sdk")
async def test_send_message_arun_does_not_block_event_loop(mock_require_sdk):
mock_client_cls = MagicMock()
mock_client_cls.return_value = _mock_web_client(
return_value={"ok": True, "ts": "1234567890.000400"}
)
mock_require_sdk.return_value = mock_client_cls

tool = SlackSendMessageTool()
heartbeat_ticks = 0

async def heartbeat() -> None:
nonlocal heartbeat_ticks
for _ in range(5):
await asyncio.sleep(0)
heartbeat_ticks += 1

result, _ = await asyncio.gather(
tool._arun(channel="#general", message="hello async"),
heartbeat(),
)

assert "Message posted to #general" in result
assert heartbeat_ticks == 5

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Make the async non-blocking test actually block the worker.

chat_postMessage returns immediately, so a broken synchronous _arun implementation could still pass after the heartbeat runs. Use a controlled blocking side effect, then assert heartbeat progress while the Slack call is blocked.

As per coding guidelines, this test should verify observable behavior rather than only an instantaneous mock response.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-tools/tests/tools/slack_tool_test.py` around lines 177 - 202,
Update test_send_message_arun_does_not_block_event_loop to make the mocked
chat_postMessage block via a controlled side effect until the heartbeat has
progressed, then release it and return the successful Slack response. Assert
heartbeat progress while the call remains blocked, preserving the existing
result assertion afterward.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant