dsh-slack-mcp
by arucil
README.md
# dsh-slack-mcp
A local, keyring-backed Slack MCP server for DSH / Codex. It keeps the pinned
upstream package (`@nrjdalal/slack-mcp-server`) unchanged and layers a small set
of conversation and workflow tools on top.
The Slack credential never lives in config, environment files, or the repo: a
small Go launcher reads a **user token** (`xoxp-…`) from the Secret Service and
passes it to the Node server through the child environment.
## Layout
```
launcher/ Go launcher: keyring → exec node server.mjs
server.mjs MCP server assembly (upstream server + extensions)
conversations.mjs conversation tools (info/members/invite/open/mark)
workflows.mjs workflow tools (featured list / webhook start)
slack-api.mjs Slack call wrapper with a public error allowlist
test/ node:test suites
```
## Tools
| Tool | Side effect | Notes |
| --- | --- | --- |
| `conversations_info` | none | channel/DM metadata |
| `conversations_members` | none | member IDs, paginated |
| `conversations_invite` | membership | explicit channel + user IDs only |
| `conversations_open` | DM creation | `prevent_creation=true` for lookup only |
| `workflows_featured_list` | none | workflow names / trigger IDs |
| `workflow_webhook_start` | external POST | one configured Workflow Builder webhook |
The upstream package contributes the ordinary Slack tools (`chat_post_message`,
`conversations_history`, `conversations_replies`, `conversations_list`,
`conversations_unreads`, `conversations_mark`, `search_messages`, reactions,
files). A deployment that fronts this server with the shared gateway filters
that catalog through its own allowlist (`shared/policy.mjs` in the gateway),
so enabling `conversations_mark` there is a policy change, not a code change.
Setting `SLACK_MCP_ALLOW_WRITE=false` (or `0`) omits every write tool.
## Requirements
- Node.js 20+
- Go 1.22+ (launcher only)
- A Slack app user token with: `channels:history groups:history im:history
mpim:history channels:read groups:read im:read mpim:read search:read
files:read channels:write groups:write im:write mpim:write chat:write
reactions:write channels:write.invites groups:write.invites`
- `zod` and the two pinned packages from `package.json`
`conversations_mark` needs the same `*:write` scopes; it does not need
`stars:*` or `reminders:*`. It requires an explicit `ts` (fetch the latest via
`conversations_history` with `limit: 1` first).
## Install
```bash
npm ci
go build -o ~/.local/bin/dsh-slack-mcp ./launcher
```
Store the token once (stdin only, never an argument):
```bash
secret-tool store --label='Slack user token' \
service donut-slack-task-dispatcher username slack-user-token
```
The launcher reads exactly that service/username pair, rejects anything that is
not an `xoxp-` token, and refuses to start if `server.mjs` is missing.
## MCP client configuration
```toml
[mcp_servers.slack-local]
command = "/home/you/.local/bin/dsh-slack-mcp"
```
The process speaks MCP over stdio and logs one line to stderr on startup.
## Tests
```bash
npm test
```
The suites use an in-memory transport and a fake Slack caller; no network call
is made and no token is required.
## Security notes
- The token is read from Secret Service at launch and passed only through the
child environment; it is never written to disk by this project.
- Slack error payloads are reduced to a public allowlist. Raw SDK errors,
headers, and tokens are never returned to the model.
- Writes are single-shot: a transport failure reports `outcome_unknown` instead
of replaying the request. Read back state before retrying.
- `workflow_webhook_start` performs one POST to a preconfigured webhook URL
stored in Secret Service under `service=codex-slack-mcp-workflows`. An
accepted trigger is not a completed workflow.
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues