72 Commits

Author SHA1 Message Date
Paymahn Moghadasian d781728c6c feat: slack-cli — a no-daemon CLI over the slack-mcp-server engine (#1)
* 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)
2026-06-26 12:45:38 -05:00
George Bashi ae3c7b4f13 perf: remove sort param from channels_me, always use fast path
The sort=popularity option forced fetching every channel the user belongs
to before sorting client-side — hundreds of API calls on large workspaces.
The Slack API doesn't support server-side sorting, so this was inherently
expensive. Remove it and always stop fetching once we have enough results,
using the Slack API's native cursor for pagination.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-14 22:20:31 +02:00
George Bashi 55842fe2c8 feat: add channels_me tool
Add a new MCP tool to list channels the calling user is a member of,
using the users.conversations API. Follows the same pattern as
usergroups_me vs usergroups_list.

Unlike channels_list which returns all workspace channels, channels_me
returns only channels the user has joined — useful on large workspaces
where channels_list returns thousands of results.

Supports channel_types, sort (by popularity), limit, and cursor
parameters.
2026-05-14 22:20:31 +02:00
Dmitrii Korotovskii e49db6aa24 Merge pull request #252 from unsafe9/feature/channels-list-query-filter
feat: add query and query_targets parameters to channels_list tool
2026-05-14 22:11:40 +02:00
Dmitrii Korotovskii 69cd994d9d Merge pull request #240 from jonzarecki/feat/saved-items
feat: add saved_list, saved_update, and saved_clear_completed tools
2026-05-14 22:11:31 +02:00
Dmitrii Korotovskii 29a73b191a Merge pull request #294 from arnstarn/feat/blocks-and-dep-bump
feat: add blocks parameter and bump slack-go-util to v0.4.0
2026-05-14 21:03:14 +02:00
Dmitrii Korotovskii 9e9f84cfba Merge pull request #219 from georgebashi/feat/conversations-join-leave
feat: add conversations_join and conversations_leave tools
2026-05-14 20:47:36 +02:00
Dmitrii Korotovskii 5e72c6bb91 Merge pull request #263 from Christian-Sidak/feat/users-search-by-id
feat: support direct user ID lookup in users_search
2026-05-14 20:47:31 +02:00
Arnold Mendez 97b24505c9 feat: add blocks parameter and bump slack-go-util to v0.4.0
Add optional `blocks` parameter to conversations_add_message for raw
Slack Block Kit JSON support (rich_text lists, code blocks, etc.).

When blocks is provided it takes precedence over text/content_type for
message rendering. The text parameter serves as notification fallback.

The blocks argument accepts both a JSON string and a raw JSON array to
accommodate different MCP client serialization behaviors.

Also bumps takara2314/slack-go-util from v0.3.0 to v0.4.0 which adds
nested list support to the existing text/markdown conversion path.
2026-05-07 10:10:53 -04:00
Braj Baheti cf81046419 Include file names alongside file IDs in AttachmentIDs field
Previously, the AttachmentIDs field only contained raw file IDs
(e.g. "F08ABC1234"), making it impossible to identify which file
an ID corresponds to without calling attachment_get_data first.

Now the field includes filenames: "F08ABC1234 (contract.pdf)".
This makes it practical to use AttachmentIDs to selectively
download relevant attachments.

Fixes #260
2026-04-15 20:46:05 +05:30
32134178csa 869899c18e feat: support direct user ID lookup in users_search tool
When the query matches a Slack user ID pattern (e.g., U07VCEPP4N5),
use the users.info API for direct lookup instead of searching by
name/email/display name. This is useful when you already have a
user ID (from message metadata, mentions, etc.) and need to resolve
it to user details.

Falls back to existing search behavior for non-ID queries.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 20:42:23 -07:00
unsafe9 06fa3b1931 feat: add query and query_targets parameters to channels_list tool
Closes #251. Adds keyword filtering to channels_list so users can find
channels without paginating through the entire list. Matches are
case-insensitive substrings; query_targets controls which fields to
search (name by default, optionally topic and purpose).
2026-03-25 00:51:57 +09:00
Jonathan Zarecki 3617d5ee65 feat: add saved_list, saved_update, and saved_clear_completed tools
Add three new tools for managing Slack's "Save for Later" panel,
replacing the deprecated stars.* API (March 2023). Uses internal
saved.list, saved.update, and saved.clearCompleted endpoints.

- saved_list: List saved items with filter (saved/completed/archived),
  auto-pagination, and optional message content fetching
- saved_update: Mark items as completed and/or set due dates
- saved_clear_completed: Bulk-clear all completed items

All tools require browser session tokens (xoxc/xoxd) and are guarded
at registration time for bot/OAuth tokens.

Includes unit tests for CSV format, field extraction, timestamp
formatting, parameter validation, and response parsing.

Made-with: Cursor
2026-03-08 22:07:54 +02:00
George Bashi 110ae5a42f feat: add conversations_join and conversations_leave tools
Add two new MCP tools for channel membership management:

- conversations_join: Join public channels via conversations.join API.
  Idempotent — joining a channel you're already in is a no-op.
  Requires channels:join (xoxb) or channels:write (xoxp) scope.

- conversations_leave: Leave channels, group conversations, or DMs
  via conversations.leave API. Marked as destructive.
  Requires channels:manage (xoxb) or channels:write (xoxp) scope.
  On Enterprise Grid with session tokens (xoxc/xoxd), routes through
  the edge API to bypass enterprise_is_restricted errors.

Both tools accept channel IDs (Cxxxxxxxxxx) or names (#channel-name)
using the existing resolveChannelID helper.

Updated docs/01-authentication-setup.md with new OAuth scopes
(channels:join, channels:manage) and docs/03-configuration-and-usage.md
with the new tool names in the available tools list.
2026-03-03 15:20:18 -08:00
Erik Jankovič 6f7acbdc3e chore: allow to filter out users_search tool
Signed-off-by: Erik Jankovič <erik.jankovic@gmail.com>
2026-02-27 01:32:36 +01:00
Manuel Kreutz 43a2ec1145 fix: token-type routing and validation for conversations_unreads (#3)
* fix: pre-check token type to avoid faulty ClientCounts call

client.counts only works with browser session tokens (xoxc/xoxd). OAuth
tokens (xoxp) get 'not_allowed_token_type' and bot tokens (xoxb) have no
concept of user-level unreads.

Instead of calling ClientCounts and catching the error after the fact,
pre-check the token type using the existing IsOAuth()/IsBotToken()
infrastructure and route directly to the appropriate path:
- xoxc/xoxd: fast path via client.counts (unchanged)
- xoxp: conversations.info fallback (no wasted API call)
- xoxb: clear error message

Follows the same pattern used by SearchUsers and GetConversationsContext
which already branch on token type.

Addresses review comments on korotovsky/slack-mcp-server#171.

* fix: exclude conversations_unreads tool for bot tokens

Bot tokens (xoxb) don't support unread tracking — it's a user-level
concept. Don't register the tool at all for bot token users, matching
the same pattern used for conversations_search_messages.

This gives a clean UX: bot users simply don't see the tool, rather
than getting a runtime error.

* fix: cap xoxp fallback scan depth and document limitations

The xoxp path previously scanned ALL channels (~2000+ API calls on large
workspaces). Slack's API has no bulk unread endpoint for xoxp tokens —
every other open-source implementation (agent-kit, NextNotifier, wee-slack)
uses the same per-channel scanning approach.

Changes:
- Cap scan depth to budget*2 per type group (~300 API calls max with
  default max_channels=50, down from ~2000+)
- Return scan metadata (channels scanned, API calls) from each type group
- Prepend an xoxp limitation note to response text so the LLM knows
  results may be partial
- Update tool description to explain xoxc vs xoxp behavior
- Fix inaccurate comment about conversations.list sort order

* fix: validate GetMutedChannels response to surface xoxp failures

Add validate() call after ParseResponse in GetMutedChannels (prefs.go),
matching the pattern used in ClientCounts. Without this, xoxp tokens
receive {ok:false, error:missing_scope} but ParseResponse only checks
HTTP status — the error was silently swallowed, returning nil/nil.

This caused muted channels to leak into xoxp results (17/28 channels
were muted in testing). Now the error propagates to the handler's
existing warn-and-proceed logic.

Also surface the limitation in the xoxp response note so the LLM
knows muted filtering is unavailable.

* fix: add shouldAddTool gating for conversations_unreads and conversations_mark

Both tools were missing Tool* constants, ValidToolNames entries, and
shouldAddTool() wrapping — breaking the established pattern used by
every other tool in the server. This prevented selective enable/disable
via the enabledTools config.

Also fixes indentation on conversations_mark registration block.
2026-02-13 14:01:09 -06:00
Manuel Kreutz 3e0e4ea792 fix: unread count persistence + backfill + muted channel filtering (#2)
* fix: use index-based range loop so unread counts persist

The message-fetch loop uses 'for _, uc := range unreadChannels', so
writes to uc.UnreadCount go to a copy — the original slice element
stays at 0. Messages are fetched correctly (appended to a separate
slice), but the unread count column is silently wrong.

Switch to 'for i := range' and index into the slice directly.
Also aligns struct field formatting (gofmt).

* feat: backfill unread counts via conversations.history

client.counts returns HasUnreads (bool) and MentionCount per channel,
but MentionCount is only non-zero for @mentions. Regular channel
activity shows as 'has unreads' with count 0.

For channels where HasUnreads=true and MentionCount=0, call
conversations.history(oldest=lastRead, limit=20) to count actual
unread messages. DMs don't need this since every message counts
as a mention.

* feat: filter muted channels from unreads by default

Fetches muted channel set from users.prefs.get (all_notifications_prefs)
and excludes them from both ClientCounts and conversations.info fallback
paths. Adds include_muted opt-in parameter to show muted channels when
explicitly requested.

Muted status is only available in users.prefs.get as a nested JSON
string — it is not exposed by client.counts, conversations.info, or
conversations.list.
2026-02-11 19:43:05 -06:00
Saoud Rizwan cfde97c945 feat: add mentions_only filter and conversations_mark tool
- Add mentions_only parameter to conversations_unreads to filter
  channels to only those with @mentions (priority inbox)
- Add conversations_mark tool to mark channels/DMs as read
  - Supports channel IDs, #channel names, and @username
  - If no timestamp provided, marks all messages as read
2026-02-11 17:10:03 -06:00
Saoud Rizwan 7fc9c7f08c feat: add conversations_unreads tool for efficient unread message retrieval
- Uses ClientUserBoot to get all channels with LastRead/Latest in one API call
- Filters channels where Latest > LastRead to find unreads
- Prioritizes: DMs > group DMs > partner channels (ext-*) > internal
- Only fetches message history for channels with actual unreads
- Supports filtering by channel type and configurable limits

Addresses issue #114
2026-02-11 17:09:21 -06:00
Dmitrii Korotovskii cae899838a Merge pull request #193 from RedSlowpoke/feat/usergroups-management
feat: add user group management tools
2026-02-11 23:40:42 +01:00
Amin Saedi 25aea2bbce test: add integration test for error recovery middleware
Verifies that the error recovery middleware correctly converts handler
errors into isError tool results instead of JSON-RPC protocol errors.
Uses mcp-go client/server wired via stdio pipes to test the full
middleware chain without external dependencies.
2026-02-09 14:20:39 -05:00
Amin Saedi ae5a6649e7 fix: return tool errors as isError results instead of JSON-RPC errors
Per the MCP specification, tool execution errors should be returned as
tool results with isError=true, not as JSON-RPC protocol errors (-32603).
This allows LLMs to see the error message and self-correct by retrying
with different parameters.

Previously, all handler errors were returned as Go errors which mcp-go
converted into JSON-RPC -32603 responses. MCP clients either crash or
silently discard these, so the LLM never sees what went wrong.

This adds a middleware that catches handler errors and converts them to
mcp.NewToolResultError() responses. The middleware is registered before
the logger and auth middlewares, so it catches all tool handler errors
regardless of which tool triggered them.
2026-02-09 12:28:37 -05:00
sspiridonov b9ffee0bc3 feat: add enabled-tools support for usergroups tools
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-09 03:53:55 +01:00
sspiridonov d5ed2c4834 Merge branch 'master' into feat/usergroups-management 2026-02-08 23:30:14 +01:00
sspiridonov 9b29f7d907 Merge origin/master into feature/enabled-tools-flag
Resolve conflict in README.md by keeping updated env var descriptions
that document the SLACK_MCP_ENABLED_TOOLS interaction.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 22:23:46 +01:00
sspiridonov 829a70065c refactor: unify shouldAddTool function and clean up comments
- Merge shouldAddTool and shouldAddWriteTool into single function
- Remove redundant comments
- Add envVarName parameter for write tools requiring explicit enablement

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 20:55:49 +01:00
sspiridonov 2ef39feae8 feat: don't register write tools unless explicitly enabled
Write tools (conversations_add_message, reactions_add, reactions_remove,
attachment_get_data) now require explicit enablement:
- If ENABLED_TOOLS explicitly includes the tool, register it
- If ENABLED_TOOLS is empty, only register if tool-specific env var is set
- If ENABLED_TOOLS excludes the tool, don't register

This resolves the conflict where ENABLED_TOOLS="" would register all tools
but SLACK_MCP_ADD_MESSAGE_TOOL="" would fail at runtime with an error.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 20:31:35 +01:00
sspiridonov 9afc063f24 feat: add user group management tools
Add five new tools for managing Slack user groups (subteams):

- usergroups_list: List all user groups in the workspace
- usergroups_me: Manage your own membership (list/join/leave)
- usergroups_create: Create a new user group
- usergroups_update: Update group metadata (name, handle, description)
- usergroups_users_update: Replace all members of a group

The usergroups_me tool provides a convenient way to join or leave
groups without needing to know the full member list. It handles
fetching current members and updating the list automatically.

Required OAuth scopes:
- usergroups:read (for list operations)
- usergroups:write (for create/update/join/leave operations)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 20:14:45 +01:00
sspiridonov d012daea6e fix: rename payload parameter to text in conversations_add_message
The parameter name was inconsistent with the error message which said
"text must be a string". Changed to use "text" as the primary parameter
name with backward compatibility for "payload".

Fixes #181

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-08 16:02:40 +01:00
flare576 3c19a58e86 refactor: move token type note from tool description to README 2026-02-05 16:14:45 -06:00
flare576 a7b7e58dbd feat: add users_search tool for finding users by name/email
Adds a new users_search tool that searches for Slack users by name,
email, or display name using the Edge API. Returns user details
including UserID, username, real name, display name, email, title,
and DM channel ID (if available in cache).

Note: This feature requires browser session tokens (xoxc/xoxd),
not OAuth tokens (xoxp/xoxb), similar to conversations_unreads.
2026-02-04 17:40:28 -06:00
sspiridonov d06ce63995 feat: add tool name constants and validation for --enabled-tools
- Add Tool* constants to avoid magic strings in tool registration
- Add ValidToolNames slice and ValidateEnabledTools function
- Validate tool names at startup, fail with helpful error message
- Update tests to use constants

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 15:19:45 +01:00
sspiridonov cc489e8379 refactor: simplify --enabled-tools to only control registration
Simplify the logic so that:
- ENABLED_TOOLS only controls which tools are registered/exposed via MCP
- Empty ENABLED_TOOLS = all tools registered (no filtering)
- Runtime permissions (ADD_MESSAGE_TOOL, REACTION_TOOL, ATTACHMENT_TOOL)
  are always enforced in handlers regardless of ENABLED_TOOLS

This keeps the two concerns separate:
1. Registration/visibility (ENABLED_TOOLS)
2. Runtime permissions (individual tool env vars)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 14:50:54 +01:00
sspiridonov 1a4efadc15 add tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 13:21:45 +01:00
sspiridonov 274a156282 feat: add --enabled-tools flag to filter available MCP tools
Add a new CLI flag `--enabled-tools` (alias `-e`) and environment variable
`SLACK_MCP_ENABLED_TOOLS` to allow users to specify which MCP tools should
be loaded. This provides flexibility to limit tool exposure based on
security requirements or use case.

- If not set, all tools are enabled (backward compatible)
- Accepts comma-separated tool names
- CLI flag takes precedence over environment variable

Available tools: conversations_history, conversations_replies,
conversations_add_message, reactions_add, reactions_remove,
attachment_get_data, conversations_search_messages, channels_list

Example usage:
  slack-mcp-server --enabled-tools=conversations_history,channels_list
  SLACK_MCP_ENABLED_TOOLS=channels_list slack-mcp-server

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 23:09:35 +01:00
flare576 ef8fc03a85 refactor: rename files_get to attachment_get_data for Slack terminology consistency
- Tool: files_get → attachment_get_data
- Field: FileIDs → AttachmentIDs
- Env var: SLACK_MCP_FILES_TOOL → SLACK_MCP_ATTACHMENT_TOOL

Per maintainer feedback - aligns with Slack's 'attachment' terminology
and may improve LLM performance due to training data prevalence.
2026-01-29 17:18:14 -06:00
flare576 81606854f1 feat: add files_get tool for downloading file content
Adds ability to download file content by file ID, addressing maintainer
request on PR #170.

- New files_get tool gated by SLACK_MCP_FILES_TOOL env var
- Text files (text/*, application/json, etc.) returned as plain text
- Binary files returned as base64-encoded content
- 5MB size limit to keep responses reasonable for LLM context
- Returns structured JSON: file_id, filename, mimetype, size, encoding, content
2026-01-29 16:50:05 -06:00
flare576 0e6b185ea2 Add destructive hints to reaction tools, use dedicated SLACK_MCP_REACTION_TOOL env var 2026-01-29 15:28:09 -06:00
flare576 f483c35aca feat: add reactions_remove tool
Companion to reactions_add (merged in #141). Allows removing emoji
reactions from messages using the same channel/timestamp/emoji params.

Tested with xoxb, xoxc/xoxd token types.
2026-01-29 14:47:10 -06:00
Dmitrii Korotovskii 9844d5b0ad Merge pull request #141 from xav-ie/feat-add-reactions
feat-add-reactions
2026-01-29 21:44:24 +01:00
triepod-ai 3ae68d4096 feat: Add tool annotations for improved LLM tool understanding
Add readOnlyHint and destructiveHint annotations to all tools
to help LLMs better understand tool behavior and make safer decisions.

Changes:
- Added readOnlyHint: true to read-only tools (conversations_history,
  conversations_replies, conversations_search_messages, channels_list)
- Added destructiveHint: true to conversations_add_message tool
- Added title annotations for human-readable display

This improves tool safety metadata for MCP clients by providing
semantic hints about whether tools modify data or just read it.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-21 12:26:58 -06:00
Xavier Ruiz af19d5ee17 feat: add reaction tool 2025-12-18 22:13:51 -05:00
Sanghoon Hong bb9469d3e6 Add bot token (xoxb) support
- Add support for bot tokens as an alternative authentication method
- Conditionally register search tool only for non-bot tokens (bots cannot use search.messages API)
- Update isOAuth definition to include both xoxp and xoxb tokens for cleaner implementation
- Add bot token configuration to manifest-dxt.json for DXT extension
- Document bot token limitations in README and authentication setup guide

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 01:26:48 +09:00
WT 470b3fecef Fix filter by private channel id, and private channel name 2025-10-03 14:53:48 -07:00
graf2242 4a5b79c1ed Rename SLACK_MCP_SSE_API_KEY to SLACK_MCP_API_KEY
Update documentation
2025-09-24 12:03:09 +03:00
graf2242 ae31b32be7 Implement Streamable HTTP MCP transport 2025-09-24 11:47:00 +03:00
Dmitrii Korotovskii c01f9845be fetch message by full slack url 2025-07-20 21:22:09 +02:00
Dmitrii Korotovskii c6d84413b1 reduce logs 2025-07-20 13:43:04 +02:00
Dmitrii Korotovskii 85d15730d1 fix regression in search 2025-07-20 13:32:33 +02:00
Dmitrii Korotovskii a9fbd54fa9 Improve logging 2025-07-20 09:46:30 +02:00