Unlisted Slack apps get 1 req/min and 15 msgs/page on
conversations.history and conversations.replies. Wait Retry-After,
cap the page, and share that slot across CLI processes.
* fix: retrieve image attachment bytes via `attachments get -o`
Image files come back from the upstream FilesGet handler as an out-of-band
MCP ImageContent block; the CLI's toolcall adapter extracted only
TextContent, so `attachments get <image_id>` emitted only metadata and
silently dropped the image bytes (even with --raw). Non-image binaries
were unaffected (single base64-in-JSON text envelope).
CLI-layer fix (pkg/handler stays byte-for-byte upstream for clean merges):
- toolcall.InvokeResult captures ImageContent into Result.Images; Invoke
is now a thin text-only wrapper, so existing callers are unchanged.
- `attachments get` gains -o/--output <path>: decodes the bytes straight
to a file and prints a small metadata confirmation, keeping stdout free
of a multi-MB base64 blob. Without -o, image bytes are folded into the
same {..,"encoding":"base64","content":..} envelope as non-image
binaries, so `jq -r .content | base64 --decode` works uniformly.
- Update SKILL.md and README with the -o flag and decode recipes.
Adds unit tests for InvokeResult image capture and the attachment
rendering paths (recover-in-envelope, -o writes raw bytes, stdout has no
blob).
* fix: always enable `attachments get` (drop SLACK_MCP_ATTACHMENT_TOOL gate)
Downloading an attachment is a read-only operation, not a mutation like
posting or reacting, so requiring an opt-in env var was needless friction.
The upstream FilesGet handler reads SLACK_MCP_ATTACHMENT_TOOL from the
environment at parse time; the CLI now sets it to "true" in-process before
invoking (same in-process-env pattern as auth/govslack), so `attachments
get` always works. pkg/handler stays byte-for-byte upstream.
Verified live against the real workspace: `attachments get F0000000000`
with no env var now returns Slack `file_not_found` instead of the
"attachment_get_data tool is disabled" gate error.
Docs: move `attachments get` from the write/sensitive section to the read
commands and drop the env-var prefix from recipes (SKILL.md + README).
CLI list/table commands (channels, messages, users, saved items, user
groups) now print a JSON array of objects instead of CSV, so agents can
pipe straight to jq. Conversion lives in the CLI output layer
(internal/output): tabular commands call emitTable, which parses the
handler's CSV into a JSON array (column order preserved, HTML escaping
off so Slack <@U>/links/& stay legible). JSON-returning handlers are
re-indented; plain-text status lines pass through. --raw still prints the
original CSV/text verbatim.
pkg/handler is untouched, so the bundled MCP server's output is unchanged
and upstream merges stay clean. Values are strings (CSV carries no types);
use jq's tonumber when you need numbers.
* feat: add slack-cli, a no-daemon CLI over the slack-mcp-server engine
Turn the forked slack-mcp-server into a CLI so running many agents no
longer means one resident MCP process each. Every command is a
short-lived process that reads the shared on-disk cache.
- rename module to github.com/paymog/slack-cli (go install/homebrew/ldflags)
- internal/toolcall: invoke the upstream tool handlers in-process; the only
mcp-go coupling lives here, so pkg/handler and pkg/provider are reused
byte-for-byte (clean upstream merges, fork-and-extend)
- internal/{cli,cmds,config,credstore,runtime,output}: cobra command tree,
keyring-backed credential profiles, provider bootstrap, result printing
- 21 tools as subcommands (channels, conversations, users, usergroups,
saved, reactions, attachments, cache); write tools keep their env gating
- goreleaser + homebrew release workflow; ships a skills/slack-cli skill
- unit tests for config/credstore/toolcall; MCP server still builds
The MCP server (cmd/slack-mcp-server) is kept intact.
* chore(napkin): record real-workspace verification
* docs: explain how the CLI works (in-process handler invocation, shared cache)