feat(tools): add SlackSendMessageTool and SlackChannelHistoryTool - #6689
feat(tools): add SlackSendMessageTool and SlackChannelHistoryTool#6689Shubhanshi-Gaudani wants to merge 2 commits into
Conversation
📝 WalkthroughWalkthroughAdds 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. ChangesSlack tools integration
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
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
Note for maintainers: this PR was developed with AI assistance (Claude Code) per the AI-contribution guidelines — flagging for the |
There was a problem hiding this comment.
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 winDocument 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
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
lib/crewai-tools/pyproject.tomllib/crewai-tools/src/crewai_tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/__init__.pylib/crewai-tools/src/crewai_tools/tools/slack_tool/README.mdlib/crewai-tools/src/crewai_tools/tools/slack_tool/__init__.pylib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.pylib/crewai-tools/tests/tools/slack_tool_test.py
| limit: int = Field( | ||
| 20, | ||
| ge=1, | ||
| le=200, | ||
| description="Maximum number of messages to fetch, most recent first.", |
There was a problem hiding this comment.
🎯 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:
- 1: https://docs.slack.dev/reference/methods/conversations.history.md
- 2: https://slack.dev/changelog-recap-april-june-2025/
🏁 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"])
PYRepository: 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)
PYRepository: 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
limitto 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-L108lib/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.
There was a problem hiding this comment.
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
📒 Files selected for processing (3)
lib/crewai-tools/src/crewai_tools/tools/slack_tool/README.mdlib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_tool.pylib/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
| @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 | ||
|
|
There was a problem hiding this comment.
🎯 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
Summary
SlackSendMessageTool(posts a message, optionally as a threaded reply viathread_ts) andSlackChannelHistoryTool(fetches recent channel messages for context), both underlib/crewai-tools/src/crewai_tools/tools/slack_tool/slack_sdk(added as an optional dependency, extra nameslack-sdk), lazily imported so it's not a hard dependency for users who don't need itSLACK_BOT_TOKENenv 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 repoWhy
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
tests/tools/slack_tool_test.py, all mockingslack_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 (limitbounds)ruff check,ruff format --check, andmypyall pass on the new sourcelib/crewai-tools/tests/suite run for regressions: 270 passed, 2 skipped; remaining failures are pre-existing environment gaps unrelated to this change (missing optionaloxylabsdependency, an interactive MongoDB test prompt)uv.lockregenerated with the exact uv version pinned in CI (0.11.3) to keep the diff minimalDisclosure
This PR was developed with AI assistance (Claude Code), with human review of the design, code, and test coverage before submission.