Two-way Slack messaging (notify/channels/inbox/reply/health) over Socket Mode, no public callback needed.
Install
# from npm (prebuilt)
dsh plugin --profile web add dsh-slack
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:STARDUSTLC666/dsh-slack
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time — pnpm blocks those until you allow them, so an install can stop with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED or ERR_PNPM_IGNORED_BUILDS; dsh prints the exact key to add under allowBuilds in your profile’s pnpm-workspace.yaml, and the install works on the next run. Allowing a build is a trust decision: only install sources you trust, and pin a commit (github:owner/repo#sha).
README
dsh-slack
DSH (DeepSeek Harness) community plugin: lets the agent communicate with Slack bidirectionally.
v0.2 scope (two-way): v0.1 only did one-way "agent → Slack" notifications; v0.2 adds Socket Mode, enabling "Slack message → agent":
slack_inboxreceives messages,slack_replyreplies in threads. RTM and interactive components (buttons/modals/slash command replies) are out of scope for v0.2 — see Known limitations and roadmap below.
Features
slack_notify: send a Markdown text message to a channel (or thread), returning the messagets.slack_channels: list the channels currently visible to the bot (conversations.list, paginating throughnext_cursoruntil complete).slack_inbox: read messages received via Socket Mode (in-memory queue, keeps up to 200; deduplicates retries and consumes atomically withmarkRead=true).slack_reply: reply to an inbox message as a thread (chat.postMessagewiththread_ts).- WebClient instances are cached by
token + slackApiUrland rebuilt automatically when configuration changes. - Configuration via
cordis.patch.yml; tokens support environment-variable fallback (DSH_SLACK_TOKEN/DSH_SLACK_APP_TOKEN).
Changelog
- 0.3.0: new
slack_healthself-check (one-call token / Socket Mode config health); fixed a startup crash from optional chaining when only some config fields are set (e.g. token without appToken). - 0.2.3:
slack_channelspaginates automatically so large workspaces no longer lose channels after the first page.slack_inboxdeduplicates Slack's at-least-once event deliveries and drains atomically onmarkRead.- WebClient reuse avoids rebuilding clients on every tool call.
- Pagination has a page cap to prevent loops from a misbehaving
next_cursor. - Error mapping adds
not_authed/is_archived/msg_too_long/ratelimited.
Compatibility
Validation host: Harness 0.2.0-rc.1 built from official sources (commit 407e65c8) with Node 24.16.0 on 2026-09-28. All 43 plugin tests pass in an isolated environment; all 18 plugins mount together in one host registering 5 tools, with tool schemas and health-check contracts passing. No live ports or external services were exercised in this round.
Installation
The plugin runs inside the host process, is installed into the profile via dsh plugin, and takes effect after a restart:
dsh plugin --profile web add dsh-slack
After installing, restart your dsh Web service; the four tools slack_notify / slack_channels / slack_inbox / slack_reply become visible to the model.
Uninstall
dsh plugin --profile web remove dsh-slack
Then restart the web service. To clean up fully, also remove the plugin entry from your profile cordis.patch.yml if you overrode it.
Configuration
Configuration lives in the profile's cordis.patch.yml; override this plugin's line by id: slack (overriding replaces that line's config wholesale — it does not merge). Available options:
| Key | Type | Required | Description |
|---|---|---|---|
token |
string | yes* | Slack token: bot token (xoxb-) or user token (xoxp-). When empty, falls back to the DSH_SLACK_TOKEN env var. |
appToken |
string | no* | App-Level Token (starts with xapp-); used to receive messages after enabling Socket Mode. When empty, falls back to the DSH_SLACK_APP_TOKEN env var. |
defaultChannel |
string | no | Default channel (e.g. #general). The default target when the model doesn't specify a channel; written into the channel parameter description. |
* token may be left empty at the "config layer", in which case it falls back to the env var; when both are empty the plugin still loads, but calling the send/list/reply tools returns a Chinese error. An empty appToken only warns — no crash — and slack_inbox returns an empty queue (one-way mode).
Method 1: environment variables (recommended, no hard-coded tokens)
# 在启动 dsh 的进程里设置
export DSH_SLACK_TOKEN=xoxb-你的机器人令牌
export DSH_SLACK_APP_TOKEN=xapp-你的App级令牌
Method 2: override in the profile's cordis.patch.yml
In your profile directory ($DSH_HOME/profiles/web/cordis.patch.yml) append:
# 覆盖 dsh-slack 的 slack 行配置(整体替换)
- id: slack
config:
token: 'xoxb-你的机器人令牌'
appToken: 'xapp-你的App级令牌'
defaultChannel: '#general'
Priority:
config.token> env varDSH_SLACK_TOKEN;config.appToken> env varDSH_SLACK_APP_TOKEN.
Create a Slack App and get a token
- Open https://api.slack.com/apps, click Create New App (choose
From scratch, name the app, select the workspace). - On the OAuth & Permissions page, under Scopes → Bot Token Scopes check:
chat:write(required to send messages)channels:read(required to list channels)
- Back at the top, click Install to Workspace (authorize).
- Grab the Bot User OAuth Token (starts with
xoxb-). - Add the bot (App) to the channels it should post in:
/invite @your-botin the channel (required for private channels).
Enable Socket Mode (two-way message receiving)
To receive Slack messages (slack_inbox / slack_reply), enable Socket Mode and generate an App-Level Token:
- Open https://api.slack.com/apps and open your App.
- Open the Socket Mode page → turn on the toggle (Enable Socket Mode).
- Click Generate Token and Scopes to create an App-Level Token: name the token, check the
connections:writescope. Copy thexapp-App-Level Token (shown only once — save it immediately). - Open the Event Subscriptions page, enable events, and under Subscribe to bot events → Add Bot User Event add:
message.channels(public channel messages)message.im(bot DMs)
- Put the App-Level Token into
appToken(or theDSH_SLACK_APP_TOKENenv var). - Restart the dsh Web service; the plugin connects automatically via Socket Mode and starts receiving messages.
Without
appTokenthe plugin won't crash: it only prints a warning andslack_inboxreturns an empty queue (with a Chinese hint). Socket Mode network errors are auto-reconnected by the SDK; the plugin only logs a warning and never throws.
Tool reference
slack_notify
Send Markdown text to a channel/thread (underlying chat.postMessage).
| Param | Type | Required | Description |
|---|---|---|---|
channel |
string | yes | Channel name (e.g. #general) or channel ID. |
text |
string | yes | The Markdown text to send. |
thread_ts |
string | no | The ts of the thread to reply to. |
Returns: { "ts": "...", "channel": "#general" } (ts for later thread_ts reference).
slack_channels
List channels visible to the bot (underlying conversations.list).
No parameters. Returns: { "channels": [{ "id": "...", "name": "..." }, ...] }.
slack_inbox
Read messages received via Socket Mode (in-memory queue, keeps up to 200, newest first).
| Param | Type | Required | Description |
|---|---|---|---|
limit |
integer | no | Maximum number of messages to return (default 10, range 1-50). |
markRead |
boolean | no | When true, clears the inbox queue after returning (mark as read). |
Returns: { "messages": [{ "ts": "...", "channel": "...", "user": "...", "text": "..." }, ...] }.
slack_reply
Reply to an inbox message as a thread (underlying chat.postMessage with thread_ts).
| Param | Type | Required | Description |
|---|---|---|---|
channel |
string | yes | Channel name (e.g. #general) or channel ID. |
text |
string | yes | Reply content (Markdown text). |
thread_ts |
string | yes | The ts of the message to reply to (from slack_inbox). |
Returns: { "ts": "...", "channel": "#general" }.
Error handling
All error messages are in Chinese, readable by both the model and the user:
- Unconfigured token:
token 未配置:缺少 Slack 机器人令牌(xoxb-)…请在 profile 的 cordis.patch.yml 覆盖 slack 行的 config.token 并重启,或设置环境变量 DSH_SLACK_TOKEN。 - Unconfigured App-Level Token: only a warning;
slack_inboxreturns an empty queue (with a Chinese hint) and other tools are unaffected. invalid_auth: hints to check/regenerate the token.channel_not_found: hints the channel name or ID is wrong.not_in_channel: hints to invite the bot App into the channel first.token_revoked/account_inactive/missing_scope/not_authed: hints permission or token expiry, reinstall the App.is_archived: hints the channel is archived and cannot receive messages.msg_too_long: hints the message exceeds Slack's 40,000-character limit.ratelimited: hints to retry after a short wait.
Development and testing
pnpm install # 安装依赖并触发 prepare(tsc 构建)
pnpm build # tsc 编译 src → lib
pnpm test # 先 build,再用 node:test 跑 test/*.test.mjs
Tests need no real token: parameter compilation, config parsing (incl. env fallback), tool registration (4 tools + Chinese error on missing config),
injecting a fake client to assert postMessage arguments (incl. thread_ts), output schema pure-JSON validation,
inbox queue capacity/clear/deduplication/atomic drain, Socket Mode event parsing (fake event objects), and no-crash on missing appToken.
Known limitations and roadmap
- v0.1 is one-way notification (agent→Slack); v0.2 is two-way via Socket Mode (
slack_inbox/slack_reply). - No RTM or interactive components (buttons/modals/slash command replies).
slack_inboxis an in-process memory queue: cleared on restart, not persisted; keeps up to 200, dropping the oldest when full.channelis a required parameter;defaultChannelis currently only written into thechannelparameter description as a hint — it does not replace the requiredchannel.- With a missing token the plugin still loads (lazy loading); the error is only thrown when the tool is called; a missing appToken only warns.
Roadmap: v0.3 plans to introduce interactive components (buttons/modals) and a persisted inbox.
Dependencies
- Runtime:
@slack/web-api(official WebClient),@slack/socket-mode(Socket Mode client) - peer (provided by the host; not imported directly by the plugin at runtime):
@deepseek-ai/cordis,@deepseek-ai/dsh-tools
License
MIT
Links
More in this category
xmanrui/dsh-im★ 1525
Connect IM bots to DeepSeek Harness via QR codes or bot credentials (9 channels: Feishu, WeChat, DingTalk, WeCom, QQ, Slack, Telegram, Discord, and WhatsApp).
shaobeichen/dsh-pocket★ 1396
Remote phone access to the DSH Web UI: scan a QR code for LAN or public (cloudflared tunnel) access with real-time sync, a mobile-adaptive layout, and a settings tab.
inclusionAI/Avernet#deepseek-harness-channel-bcn★ 572
Connects DeepSeek Harness to Avernet's Bot Collaboration Network over WebSocket V2, with automatic onboarding, isolated agent sessions, tool-call events, and multi-bot routing tools.
omdsh-dev/dsh-notification★ 85
Desktop notifications for turn completions, with per-outcome controls and keyword rules.
whyihaveyou/dsh-suite#plugin-notify★ 57
IM webhook and local notifications on turn completion, errors, or approval (Feishu/WeCom/DingTalk/Slack/Discord/custom).
omdsh-dev/dsh-lark★ 55
Lark/Feishu bot channel for DeepSeek Harness: each chat drives its own agent, and tool approvals, model questions, and plan reviews return as cards answered by a button or a reply. Switch workspace and model from the chat (`/cd`, `/model`, `/new`), and run several bots that keep separate sessions and can hand turns to each other in one group.
Community comments
Comments are public GitHub Discussions. Loading them connects to GitHub and Giscus; a GitHub account is required to post.