team-slack-bridge
Allows posting messages to Slack channels or DMs, either as the bot or explicitly as a specific authorized user, from scripts, agents, or CI jobs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@team-slack-bridgepost to #deploys: release v2.4.1 shipped"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
team-slack-bridge
Post to Slack — as a bot, or explicitly as a specific authorized person — and DM, from any script, agent, or CI job. Standalone: no dependency on any particular orchestration system, AI agent framework, or dashboard.
Runtime: Node >=26 (package.json's engines). .nvmrc pins 26 for local dev on
this machine so the bridge stays on the current Node line rather than LTS.
Four surfaces, one core
See PLAN.md for the full architecture and decisions log. In short: every capability is
a plain function in core/ (tokens as parameters, no ambient state, no logging), and
each way of reaching it is a thin adapter that adds no behaviour of its own:
Direct —
node cli/post.js …, orimport { postToChannel } from './core/post.js'. Root-levelpost.js/get-user-token.js/verify-dm.jsstill work unchanged.MCP server (local) —
node mcp/server.jsover stdio, full tool set. Point any MCP-capable client at it:{ "mcpServers": { "team-slack-bridge": { "command": "node", "args": ["/abs/path/to/team-slack-bridge/mcp/server.js"] } } }Skill —
skill/SKILL.md, shells out to the CLI. Invoke by name in Claude Code.Dashboard agent — the dashboard imports
core/directly or calls the CLI.
Every outbound CLI/MCP command supports --dry-run (resolve and format the Slack call,
send nothing) and --json (structured { ok, ... } output, non-zero exit on failure).
node cli/doctor.js --json reports install health without ever printing a token value.
Built for local/team use now: outbound posting, DMs, channel/thread reads, scheduling, human approval/questions over DM, Socket Mode listening, listener daemon control, progress-message updates, and a locked-down Slackbot remote MCP surface.
For the feature matrix and exact on/off switches, see FEATURES.md.
Related MCP server: Slack Notify MCP
Design principles
One shared Slack app for everyone who uses it — not one app per person. The bot identity is shared infrastructure: every user of this tool can point at the same Bot Token, since the bot always acts as "the bot," never as a specific person. There's no conflict in sharing that token across people.
The user token (for posting as a specific person) is inherently per-person and can't be shared, by construction of Slack's own OAuth. Slack's OAuth flow only ever produces a token for whoever personally authorizes it — each person who wants the "as-user" capability runs
npm run get-user-tokenthemselves, once, into their own local.env. Nobody's install ever contains anyone else's user token.Naming a person is not authorization. If you build identity-bound routing on top of this (e.g. "post as whoever this request is from"), verify that the request's own origin actually matches the named person — don't trust a caller-supplied name alone. This library doesn't enforce that for you (it's a single-token-per-install tool by design), but if you're wiring several people's tokens into one system, that check is yours to build.
Every install is local, holding its own credentials. This is not a hosted service — no token, credential, or account-specific value ships in this repo (only
.env.example, with empty values)..envis git-ignored.Neither as-user posting nor DM-reading is a default, always-on capability. Both require explicit setup (the OAuth user-token flow; a local password/authorization gate you add yourself if you build DM-reading on top of this) before they do anything at all.
One-time setup
Create a Slack app from
config/slack-app-manifest.template.json, or create a Blank app and configure the same scopes/events by hand. Under OAuth & Permissions -> Scopes, add these Bot Token Scopes:chat:write— post messages as the botim:write— open/send DMsim:read,im:history— only needed if you're also building DM-reading on top of this (e.g. a note-to-self pattern) — without these the bot can send a DM but not read what comes back. Skip if you only need outbound posting.channels:read,channels:history,groups:read,groups:history— read public/private channels the bot is invited to (only needed if you'll query message history later — not used by anything in this repo yet)users:read— resolve handles to user IDs
If you also want the "post as a specific person" capability, add
chat:writeunder User Token Scopes too, and set an OAuth redirect URL (see step 3).Hard platform limit, not a scope issue: none of the above ever lets the bot read a DM between two other people, or a DM the bot isn't a member of — Slack bots can only see conversations they're part of. The only way to read a person's own DM history is a user token authenticated as that person, which is a materially bigger privacy grant than anything else here — don't add it without deciding to.
For Socket Mode, create an app-level token with
connections:write. For future HTTP endpoints or slash commands, copy the Slack Signing Secret from Basic Information -> App Credentials intoSLACK_SIGNING_SECRET.Install the app to your workspace. Invite the bot to any channel you want it posting in (
/invite @your-app-name). To DM the bot yourself, search its name in Slack or find it under Apps in the sidebar — no separate "invite to DM" step exists.Run the setup wizard:
node cli/setup.js initIt writes
.envandslack-config.json. Token prompts are local terminal input; do not run setup in a shared recording or paste tokens into chat.If you want the "post as me" capability:
npm run get-user-tokenOpen the printed URL, approve, and the script writes
SLACK_USER_TOKENinto.envitself — the token is never printed to the terminal.
Usage
# default: post as the bot
node post.js --text "build finished" --channel "#deploys"
# explicit: post as your own authorized Slack identity
node post.js --text "reviewing this now" --channel "#deploys" --as-user
# DM someone (bot identity) — find their Slack member ID via their profile > "Copy member ID"
node post.js --text "can you take a look at this?" --dm U0123ABC
# setup and health
node cli/setup.js init
node cli/doctor.js --json
# run the Socket Mode listener as a daemon
node cli/daemon.js start --json
node cli/daemon.js status --json
node cli/daemon.js logs --lines 80
node cli/daemon.js stop --json
# progress message that gets edited in place
node cli/progress.js start --channel "#deploys" --label "Deploy" --detail "starting" --json
node cli/progress.js update --channel "#deploys" --ts "1699999999.000100" --label "Deploy" --status "running" --detail "tests passed" --json
node cli/progress.js finish --channel "#deploys" --ts "1699999999.000100" --label "Deploy" --detail "released" --json
# local output-mode control; the same can be exposed as Slack /outputmode
node cli/output-mode.js medium --jsonRuntime Notes
Slack Web API calls go through the official @slack/web-api WebClient, with the
existing core result shape preserved. Repeated DM sends cache the opened DM channel in
the local SQLite DB, so later sends to the same user avoid another conversations.open.
The listener uses Bolt Socket Mode; keep it running through cli/daemon.js for
approval buttons and event-driven free-text answers.
Dormant surfaces are config-gated and off by default:
{
"outputMode": "medium",
"http": { "enabled": false, "port": 8917, "verifySlackSignatures": true },
"slashCommands": { "enabled": false, "outputModeCommand": "/outputmode" },
"agentSessions": { "enabled": false, "autoCreateSession": false, "provider": "none" },
"openacp": { "enabled": false, "adapterPackage": "@openacp/slack-adapter", "autoCreateSession": false },
"slackbotMcp": {
"enabled": false,
"serverKey": "team-slack-bridge",
"url": "",
"authType": "slack_identity_auth",
"authProviderKey": "",
"exposeWriteTools": false,
"allowedTools": [],
"rateLimitPerMinute": 30
}
}SLACK_SIGNING_SECRET is required when http.enabled is true and signature verification
remains enabled; it is always required when slackbotMcp.enabled is true, because /mcp
trusts Slack identity only after verifying Slack's request signature. openacp.enabled
only attempts to load the adapter package; it does not add OpenACP as a hard dependency or
change bridge behavior while disabled.
The HTTP Events API endpoint is /webhook, so a local listener runs at
http://localhost:8917/webhook. Slack itself cannot reach localhost; use this for
local tunnel testing or replace it with a public HTTPS URL in Slack Event Subscriptions.
Slackbot MCP Client support is also dormant by default. Keep slackbotMcp.enabled:false
until there is a public HTTPS MCP endpoint and a deliberately chosen safe tool set.
When enabling it, merge config/slackbot-mcp.manifest.fragment.json into the Slack app
manifest, set its mcp_servers.<serverKey>.url to the public /mcp endpoint, and add
the mcp:connect bot scope. Prefer slack_identity_auth for this repo so Slack user
and team identity are available to the MCP layer; leave exposeWriteTools:false unless
write tools have explicit authorization rules. The /mcp endpoint is implemented in
the optional HTTP listener, but it returns disabled by default; when enabled, it exposes
only slack_doctor unless slackbotMcp.allowedTools is set. Channel read/write tools
are additionally restricted by remote.readableChannels and remote.postableChannels;
DMs, human ask/approval, post-as-user, search, App Home publishing, scheduling, progress
mutation, and agent-session creation are absent from the remote registry by construction.
Two MCP-server options, if a typed-tool front door is wanted instead of/alongside the CLI — pick deliberately, don't default to whichever is more capable:
@modelcontextprotocol/server-slack(reference implementation) — exposes Slack as agent-callable tools. Provides no identity-bound authorization (requestedPersonName == callerIdentity) or bot-default/as-user-explicit rule — a consuming system still has to layer that on itself.slack-mcp-server(community, more capable — search, threads, reactions, unread tracking; posting disabled by default, matching this repo's own bot-default caution) — but it also supports browser-session "stealth mode" tokens (xoxc/xoxd): full account access via browser cookie, no bot app install required. That's the opposite of everything this repo is designed around (scoped bot tokens, real OAuth for user tokens, identity-bound authorization). If this server is ever used, OAuth-token mode only — never stealth mode. The extra capability (message/thread search) is worth having; the bypass-every-guardrail auth mode is not.
Not Slack-specific, noted for a different reason — novu. A general multi-channel
notification platform (email/SMS/push/13 chat providers including Slack), not a
Slack library. Conceptually a good fit for "integrations-as-data" — one provider
abstraction instead of N bespoke outbound agents once email/Drive/etc. are real. But it
requires running its own hosted/self-hosted backend (Docker) — no lightweight
backend-free mode exists. Premature to adopt for "post to one Slack channel" — revisit
only if/when Slack + email + something else all need unified outbound at once.
Scope note
Default post-as identity is always the bot — posting "as a specific person"
requires the explicit --as-user flag, never inferred or defaulted. If you build
anything routing requests to different people's tokens, keep that rule: explicit
beats inferred, always.
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Enable interaction with Slack workspaces. Supports subscribing to Slack events through Resources.
Syslog receiver and MCP server for homelab log intelligence.
Syslog receiver and MCP server for homelab log intelligence.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseBqualityDmaintenanceThe most powerful MCP server for Slack Workspaces. This integration supports both Stdio and SSE transports, proxy settings and does not require any permissions or bots being created or approved by Workspace admins 😏.21,814MIT
- AlicenseNot gradedqualityDmaintenanceMCP server for posting messages to Slack channels via webhooks or bot API. Supports configurable usernames, emojis, and both webhook and bot token authentication modes.5 npmMIT
- AlicenseAqualityFmaintenanceA minimal MCP server for sending Slack messages, supporting both bot and user tokens with concurrent safety and an AI bot signature in user-token mode.110 npm1MIT
- FlicenseAqualityCmaintenanceEnables posting messages to a Slack channel using a bot token, with support for stdio and HTTP modes.1-