My team and I have been building a background agents as a service product. One of the things we needed pretty early on was for some way for the agents to be able to drive slack.
Right now, I don't think there are many good agent-first ways of doing this. I don't love MCP -- it's just too many tokens in the context window, and agents seem to do better with CLIs because they can embed them in code and so on. But other CLIs that exist are either a) subsets of the web api that only focus on human needs and b) have polished terminal UIs with spinners and colors and interactive things that make it hard for LLMs to actually use them.
nori-slack-cli is a very thin dynamic wrapper over @slack/web-api (i.e. bolt). Whatever the SDK can call, the CLI can call. When slack adds a new method it should just work without a new release on our end. (Inspired by the GWS cli: https://github.com/googleworkspace/cli).
So you(r agent) can do things like:
`nori-slack chat.postMessage --channel C123 --text "hi"`
`nori-slack admin.users.list`
`nori-slack bookmarks.add`
etc.
It's pretty easy to build a cli these days, so what makes this special? We spent a bunch of time thinking about what is necessary to make something 'agent first', which is I think the real value of this CLI: the design stuff.
- Every response is a one line JSON on stdout, no colors or interactivity.
- Every error includes a source field with the on-disk path to the CLI. When the agent is confused it can just go read the source (also useful for asking the agent to go add a feature on the fly)
- (Re the above, we generally recommend downloading and running the cli from source on your machine!)
- you can describe any method to get required/optional params, pagination support, and the docs URL without having your agent do additional webfetches- there's a list-methods function for discovery
- the 'paginate' flag auto-cursors and merges pages into a single response
- and my personal favorite: any unknown method will try and Levenshtein fuzzy match to surface other methods the agent may have meant
We purposely wanted the CLI to match the bolt API 1:1 because we felt it critical that the agent had the capability of doing anything it needed. Scopes are managed via the tokens the cli has access to.
Repo: https://github.com/tilework-tech/nori-slack-cli
Would love to hear feedback about what works for other people when designing cli products for agents