A CLI for the Slack Web API, designed for coding agents. Used as the Slack driver for Nori Sessions background agents.
nori-slack-cli is a thin command-line wrapper around the Slack Web API that maps 1:1 to Bolt (@slack/web-api). Every method Bolt exposes is reachable through a single dynamic command — there is no curated subset, no opinionated abstraction layer, and no business logic. If Bolt can call it, this CLI can call it.
Bolt is built for human developers writing TypeScript. This CLI is built for coding agents that need to drive Slack from a shell. That shapes every design decision:
- No interactive prompts, no ASCII art. Every successful response is a single line of JSON on stdout. Errors are JSON on stdout and a human-readable line on stderr.
- Exhaustive surface. The agent has access to the full Slack Web API — not a hand-picked subset. Capability boundaries are enforced through bot token scopes, not through code.
- Two transports, one interface. Direct mode calls Slack with
SLACK_BOT_TOKEN; proxy mode routes the same{method, args}calls through a Nori Sessions broker usingNORI_SLACK_PROXY_URL+NORI_SLACK_CONTEXT_TOKEN, so managed sessions never hold a raw bot token. There is no user-OAuth flow because there is no human in the loop. - Self-locating errors. Every error response includes a
sourcefield with the on-disk path to the CLI, so an agent can read the source code to debug. - Install from npm.
npm install -g nori-slack-cliputsnori-slackon yourPATH. Cloning and building from source is also supported for contributors.
From npm:
npm install -g nori-slack-cliFrom source (for contributors):
git clone https://github.com/tilework-tech/nori-slack-cli.git
cd nori-slack-cli
npm install
npm run build
npm link # makes `nori-slack` available globallyThen set credentials for one of the two transports (see Authentication):
# Direct mode
export SLACK_BOT_TOKEN=xoxb-...
# Proxy mode (set automatically inside Nori Sessions)
export NORI_SLACK_PROXY_URL=https://broker.example.com/api/slack-proxy
export NORI_SLACK_CONTEXT_TOKEN=...The general shape is nori-slack <method> [--param value ...], where <method> is any Slack Web API method (e.g. chat.postMessage, conversations.list, users.info).
# Send a message
nori-slack chat.postMessage --channel C123 --text "Hello"
# List channels
nori-slack conversations.list --limit 10
# Auto-paginate and merge results
nori-slack conversations.list --paginate
# Pipe parameters in as JSON
echo '{"channel":"C123","text":"hi"}' | nori-slack chat.postMessage --json-input
# Preview a request without sending it (no token required)
nori-slack chat.postMessage --channel C123 --text "Hello" --dry-runFlags are converted from --kebab-case to snake_case to match Slack's parameter names. Values are auto-coerced (true/false → boolean, numerics → number, inline JSON → object/array). A bare --flag with no value is treated as boolean true.
# List every known method, optionally filtered by namespace
nori-slack list-methods --namespace chat
nori-slack list-methods --descriptions
# Get parameter docs, required/optional fields, pagination support, and docs URL for a method
nori-slack describe chat.postMessage| Flag | Purpose |
|---|---|
--json-input |
Read parameters as JSON from stdin (CLI flags override stdin values). |
--paginate |
Use cursor pagination and return a single merged JSON response. |
--dry-run |
Resolve params and print the planned request without calling the API. |
0— success1— Slack API error, proxy error, or missing credentials2— bad CLI usage (missing args, invalid stdin JSON)
The CLI supports two transports, selected from the environment:
| Mode | Environment | Behavior |
|---|---|---|
| Proxy | NORI_SLACK_PROXY_URL + NORI_SLACK_CONTEXT_TOKEN |
POSTs {method, args} to <url>/method with the context token as a bearer token. Used inside Nori Sessions, where the broker enforces a per-session access grant and the raw bot token never reaches the machine. |
| Direct | SLACK_BOT_TOKEN |
Calls the Slack Web API directly via @slack/web-api. |
When both are configured, proxy mode wins. All CLI features (--json-input, --paginate, --dry-run, kebab-case conversion, type coercion, error suggestions) behave identically in both modes. --dry-run reports which transport would be used via the transport field (proxy, direct, or none).
In direct mode, capability boundaries come from the bot token's OAuth scopes. In proxy mode, the broker additionally restricts methods and channels to the session's access grant — requests outside the grant fail with a structured proxy_error.
See LICENSE and LICENSE-ADDENDUM.txt.
Created and maintained by Nori.