Skip to content

Commit 6065e51

Browse files
authored
Merge pull request #173 from RedSlowpoke/feature/enabled-tools-flag
feat: add --enabled-tools flag to filter available MCP tools
2 parents c3c8a9c + 9b29f7d commit 6065e51

File tree

7 files changed

+586
-38
lines changed

7 files changed

+586
-38
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,13 +173,14 @@ Fetches a CSV directory of all users in the workspace.
173173
| `SLACK_MCP_SERVER_CA` | No | `nil` | Path to CA certificate |
174174
| `SLACK_MCP_SERVER_CA_TOOLKIT` | No | `nil` | Inject HTTPToolkit CA certificate to root trust-store for MitM debugging |
175175
| `SLACK_MCP_SERVER_CA_INSECURE` | No | `false` | Trust all insecure requests (NOT RECOMMENDED) |
176-
| `SLACK_MCP_ADD_MESSAGE_TOOL` | No | `nil` | Enable message posting via `conversations_add_message` and emoji reactions via `reactions_add`/`reactions_remove` by setting it to true for all channels, a comma-separated list of channel IDs to whitelist specific channels, or use `!` before a channel ID to allow all except specified ones, while an empty value disables these tools by default. |
177-
| `SLACK_MCP_ADD_MESSAGE_MARK` | No | `nil` | When the `conversations_add_message` tool is enabled, any new message sent will automatically be marked as read. |
176+
| `SLACK_MCP_ADD_MESSAGE_TOOL` | No | `nil` | Enable message posting via `conversations_add_message` by setting it to `true` for all channels, a comma-separated list of channel IDs to whitelist specific channels, or use `!` before a channel ID to allow all except specified ones. If empty, the tool is only registered when explicitly listed in `SLACK_MCP_ENABLED_TOOLS`. |
177+
| `SLACK_MCP_ADD_MESSAGE_MARK` | No | `nil` | When `conversations_add_message` is enabled (via `SLACK_MCP_ADD_MESSAGE_TOOL` or `SLACK_MCP_ENABLED_TOOLS`), setting this to `true` will automatically mark sent messages as read. |
178178
| `SLACK_MCP_ADD_MESSAGE_UNFURLING` | No | `nil` | Enable to let Slack unfurl posted links or set comma-separated list of domains e.g. `github.com,slack.com` to whitelist unfurling only for them. If text contains whitelisted and unknown domain unfurling will be disabled for security reasons. |
179179
| `SLACK_MCP_USERS_CACHE` | No | `~/Library/Caches/slack-mcp-server/users_cache.json` (macOS)<br>`~/.cache/slack-mcp-server/users_cache.json` (Linux)<br>`%LocalAppData%/slack-mcp-server/users_cache.json` (Windows) | Path to the users cache file. Used to cache Slack user information to avoid repeated API calls on startup. |
180180
| `SLACK_MCP_CHANNELS_CACHE` | No | `~/Library/Caches/slack-mcp-server/channels_cache_v2.json` (macOS)<br>`~/.cache/slack-mcp-server/channels_cache_v2.json` (Linux)<br>`%LocalAppData%/slack-mcp-server/channels_cache_v2.json` (Windows) | Path to the channels cache file. Used to cache Slack channel information to avoid repeated API calls on startup. |
181181
| `SLACK_MCP_LOG_LEVEL` | No | `info` | Log-level for stdout or stderr. Valid values are: `debug`, `info`, `warn`, `error`, `panic` and `fatal` |
182182
| `SLACK_MCP_GOVSLACK` | No | `nil` | Set to `true` to enable [GovSlack](https://slack.com/solutions/govslack) mode. Routes API calls to `slack-gov.com` endpoints instead of `slack.com` for FedRAMP-compliant government workspaces. |
183+
| `SLACK_MCP_ENABLED_TOOLS` | No | `nil` | Comma-separated list of tools to register. If empty, all read-only tools are registered; write tools (`conversations_add_message`, `reactions_add`, `reactions_remove`, `attachment_get_data`) require their specific env var OR must be explicitly listed here. When a write tool is listed here, it's enabled without channel restrictions. Available tools: `conversations_history`, `conversations_replies`, `conversations_add_message`, `reactions_add`, `reactions_remove`, `attachment_get_data`, `conversations_search_messages`, `channels_list`. |
183184

184185
*You need one of: `xoxp` (user), `xoxb` (bot), or both `xoxc`/`xoxd` tokens for authentication.
185186

cmd/slack-mcp-server/main.go

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,52 @@ var defaultSsePort = 13080
2222

2323
func main() {
2424
var transport string
25+
var enabledToolsFlag string
2526
flag.StringVar(&transport, "t", "stdio", "Transport type (stdio, sse or http)")
2627
flag.StringVar(&transport, "transport", "stdio", "Transport type (stdio, sse or http)")
28+
flag.StringVar(&enabledToolsFlag, "e", "", "Comma-separated list of enabled tools (empty = all tools)")
29+
flag.StringVar(&enabledToolsFlag, "enabled-tools", "", "Comma-separated list of enabled tools (empty = all tools)")
2730
flag.Parse()
2831

32+
if enabledToolsFlag == "" {
33+
enabledToolsFlag = os.Getenv("SLACK_MCP_ENABLED_TOOLS")
34+
}
35+
36+
var enabledTools []string
37+
if enabledToolsFlag != "" {
38+
for _, tool := range strings.Split(enabledToolsFlag, ",") {
39+
tool = strings.TrimSpace(tool)
40+
if tool != "" {
41+
enabledTools = append(enabledTools, tool)
42+
}
43+
}
44+
}
45+
2946
logger, err := newLogger(transport)
3047
if err != nil {
3148
panic(err)
3249
}
3350
defer logger.Sync()
3451

35-
err = validateToolConfig(os.Getenv("SLACK_MCP_ADD_MESSAGE_TOOL"))
52+
addMessageToolEnv := os.Getenv("SLACK_MCP_ADD_MESSAGE_TOOL")
53+
err = validateToolConfig(addMessageToolEnv)
3654
if err != nil {
3755
logger.Fatal("error in SLACK_MCP_ADD_MESSAGE_TOOL",
3856
zap.String("context", "console"),
3957
zap.Error(err),
4058
)
4159
}
4260

61+
err = server.ValidateEnabledTools(enabledTools)
62+
if err != nil {
63+
logger.Fatal("error in SLACK_MCP_ENABLED_TOOLS",
64+
zap.String("context", "console"),
65+
zap.Error(err),
66+
)
67+
}
68+
4369
p := provider.New(transport, logger)
44-
s := server.NewMCPServer(p, logger)
70+
s := server.NewMCPServer(p, logger, enabledTools)
4571

4672
go func() {
4773
var once sync.Once

docs/03-configuration-and-usage.md

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,10 @@ docker-compose up -d
258258

259259
### Console Arguments
260260

261-
| Argument | Required ? | Description |
262-
|-----------------------|------------|--------------------------------------------------------------------------|
263-
| `--transport` or `-t` | Yes | Select transport for the MCP Server, possible values are: `stdio`, `sse` |
261+
| Argument | Required ? | Description |
262+
|-----------------------------|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
263+
| `--transport` or `-t` | Yes | Select transport for the MCP Server, possible values are: `stdio`, `sse` |
264+
| `--enabled-tools` or `-e` | No | Comma-separated list of tools to register. If not set, all tools are registered. Runtime permissions (e.g., `SLACK_MCP_ADD_MESSAGE_TOOL`) are still enforced. Available tools: `conversations_history`, `conversations_replies`, `conversations_add_message`, `reactions_add`, `reactions_remove`, `attachment_get_data`, `conversations_search_messages`, `channels_list`. |
264265

265266
### Environment Variables
266267

@@ -278,9 +279,86 @@ docker-compose up -d
278279
| `SLACK_MCP_SERVER_CA` | No | `nil` | Path to CA certificate |
279280
| `SLACK_MCP_SERVER_CA_TOOLKIT` | No | `nil` | Inject HTTPToolkit CA certificate to root trust-store for MitM debugging |
280281
| `SLACK_MCP_SERVER_CA_INSECURE` | No | `false` | Trust all insecure requests (NOT RECOMMENDED) |
281-
| `SLACK_MCP_ADD_MESSAGE_TOOL` | No | `nil` | Enable message posting via `conversations_add_message` by setting it to true for all channels, a comma-separated list of channel IDs to whitelist specific channels, or use `!` before a channel ID to allow all except specified ones, while an empty value disables posting by default. |
282-
| `SLACK_MCP_ADD_MESSAGE_MARK` | No | `nil` | When the `conversations_add_message` tool is enabled, any new message sent will automatically be marked as read. |
282+
| `SLACK_MCP_ADD_MESSAGE_TOOL` | No | `nil` | Enable message posting via `conversations_add_message` by setting it to `true` for all channels, a comma-separated list of channel IDs to whitelist specific channels, or use `!` before a channel ID to allow all except specified ones. If empty, the tool is only registered when explicitly listed in `SLACK_MCP_ENABLED_TOOLS`. |
283+
| `SLACK_MCP_ADD_MESSAGE_MARK` | No | `nil` | When `conversations_add_message` is enabled (via `SLACK_MCP_ADD_MESSAGE_TOOL` or `SLACK_MCP_ENABLED_TOOLS`), setting this to `true` will automatically mark sent messages as read. |
283284
| `SLACK_MCP_ADD_MESSAGE_UNFURLING` | No | `nil` | Enable to let Slack unfurl posted links or set comma-separated list of domains e.g. `github.com,slack.com` to whitelist unfurling only for them. If text contains whitelisted and unknown domain unfurling will be disabled for security reasons. |
284285
| `SLACK_MCP_USERS_CACHE` | No | `.users_cache.json` | Path to the users cache file. Used to cache Slack user information to avoid repeated API calls on startup. |
285286
| `SLACK_MCP_CHANNELS_CACHE` | No | `.channels_cache_v2.json` | Path to the channels cache file. Used to cache Slack channel information to avoid repeated API calls on startup. |
286287
| `SLACK_MCP_LOG_LEVEL` | No | `info` | Log-level for stdout or stderr. Valid values are: `debug`, `info`, `warn`, `error`, `panic` and `fatal` |
288+
| `SLACK_MCP_ENABLED_TOOLS` | No | `nil` | Comma-separated list of tools to register. If empty, all read-only tools are registered; write tools (`conversations_add_message`, `reactions_add`, `reactions_remove`, `attachment_get_data`) require their specific env var to be set OR must be explicitly listed here. When a write tool is listed here, it's enabled without channel restrictions. Available tools: `conversations_history`, `conversations_replies`, `conversations_add_message`, `reactions_add`, `reactions_remove`, `attachment_get_data`, `conversations_search_messages`, `channels_list`. |
289+
290+
### Tool Registration and Permissions
291+
292+
#### Overview
293+
294+
Tools are controlled at two levels:
295+
- **Registration** (`SLACK_MCP_ENABLED_TOOLS`) — determines which tools are visible to MCP clients
296+
- **Runtime permissions** (tool-specific env vars like `SLACK_MCP_ADD_MESSAGE_TOOL`) — channel restrictions for write tools
297+
298+
Write tools (`conversations_add_message`, `reactions_add`, `reactions_remove`, `attachment_get_data`) are **not registered by default** to prevent accidental exposure. To enable them, you must either:
299+
1. Set their specific environment variable (e.g., `SLACK_MCP_ADD_MESSAGE_TOOL`), or
300+
2. Explicitly list them in `SLACK_MCP_ENABLED_TOOLS`
301+
302+
#### Examples
303+
304+
**Example 1: Read-only mode (default)**
305+
306+
By default, only read-only tools are available. No write tools are registered.
307+
308+
```json
309+
{
310+
"env": {
311+
"SLACK_MCP_XOXP_TOKEN": "xoxp-..."
312+
}
313+
}
314+
```
315+
316+
**Example 2: Enable messaging to specific channels**
317+
318+
Use `SLACK_MCP_ADD_MESSAGE_TOOL` to enable messaging with channel restrictions:
319+
320+
```json
321+
{
322+
"env": {
323+
"SLACK_MCP_XOXP_TOKEN": "xoxp-...",
324+
"SLACK_MCP_ADD_MESSAGE_TOOL": "C123456789,C987654321"
325+
}
326+
}
327+
```
328+
329+
**Example 3: Enable messaging without channel restrictions**
330+
331+
Use `SLACK_MCP_ENABLED_TOOLS` to register write tools without restrictions:
332+
333+
```json
334+
{
335+
"env": {
336+
"SLACK_MCP_XOXP_TOKEN": "xoxp-...",
337+
"SLACK_MCP_ENABLED_TOOLS": "conversations_history,conversations_add_message,reactions_add"
338+
}
339+
}
340+
```
341+
342+
**Example 4: Minimal read-only setup**
343+
344+
Expose only specific tools:
345+
346+
```json
347+
{
348+
"env": {
349+
"SLACK_MCP_XOXP_TOKEN": "xoxp-...",
350+
"SLACK_MCP_ENABLED_TOOLS": "channels_list,conversations_history"
351+
}
352+
}
353+
```
354+
355+
#### Behavior Matrix
356+
357+
| `ENABLED_TOOLS` | Tool-specific env var | Write tool registered? | Channel restrictions |
358+
|-----------------|----------------------|------------------------|---------------------|
359+
| empty/not set | not set | No | N/A |
360+
| empty/not set | `true` | Yes | None |
361+
| empty/not set | `C123,C456` | Yes | Only listed channels |
362+
| includes tool | not set | Yes | None |
363+
| includes tool | `C123,C456` | Yes | Only listed channels |
364+
| excludes tool | any | No | N/A |

pkg/handler/conversations.go

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -867,14 +867,19 @@ func (ch *ConversationsHandler) parseParamsToolConversations(ctx context.Context
867867

868868
func (ch *ConversationsHandler) parseParamsToolAddMessage(ctx context.Context, request mcp.CallToolRequest) (*addMessageParams, error) {
869869
toolConfig := os.Getenv("SLACK_MCP_ADD_MESSAGE_TOOL")
870+
enabledTools := os.Getenv("SLACK_MCP_ENABLED_TOOLS")
871+
870872
if toolConfig == "" {
871-
ch.logger.Error("Add-message tool disabled by default")
872-
return nil, errors.New(
873-
"by default, the conversations_add_message tool is disabled to guard Slack workspaces against accidental spamming." +
874-
"To enable it, set the SLACK_MCP_ADD_MESSAGE_TOOL environment variable to true, 1, or comma separated list of channels" +
875-
"to limit where the MCP can post messages, e.g. 'SLACK_MCP_ADD_MESSAGE_TOOL=C1234567890,D0987654321', 'SLACK_MCP_ADD_MESSAGE_TOOL=!C1234567890'" +
876-
"to enable all except one or 'SLACK_MCP_ADD_MESSAGE_TOOL=true' for all channels and DMs",
877-
)
873+
if !strings.Contains(enabledTools, "conversations_add_message") {
874+
ch.logger.Error("Add-message tool disabled by default")
875+
return nil, errors.New(
876+
"by default, the conversations_add_message tool is disabled to guard Slack workspaces against accidental spamming. " +
877+
"To enable it, set the SLACK_MCP_ADD_MESSAGE_TOOL environment variable to true, 1, or comma separated list of channels " +
878+
"to limit where the MCP can post messages, e.g. 'SLACK_MCP_ADD_MESSAGE_TOOL=C1234567890,D0987654321', 'SLACK_MCP_ADD_MESSAGE_TOOL=!C1234567890' " +
879+
"to enable all except one or 'SLACK_MCP_ADD_MESSAGE_TOOL=true' for all channels and DMs",
880+
)
881+
}
882+
toolConfig = "true"
878883
}
879884

880885
channel := request.GetString("channel_id", "")
@@ -924,14 +929,19 @@ func (ch *ConversationsHandler) parseParamsToolAddMessage(ctx context.Context, r
924929

925930
func (ch *ConversationsHandler) parseParamsToolReaction(ctx context.Context, request mcp.CallToolRequest) (*addReactionParams, error) {
926931
toolConfig := os.Getenv("SLACK_MCP_REACTION_TOOL")
932+
enabledTools := os.Getenv("SLACK_MCP_ENABLED_TOOLS")
933+
927934
if toolConfig == "" {
928-
ch.logger.Error("Reactions tool disabled by default")
929-
return nil, errors.New(
930-
"by default, the reactions tools are disabled to guard Slack workspaces against accidental spamming. " +
931-
"To enable them, set the SLACK_MCP_REACTION_TOOL environment variable to true, 1, or comma separated list of channels " +
932-
"to limit where the MCP can manage reactions, e.g. 'SLACK_MCP_REACTION_TOOL=C1234567890,D0987654321', 'SLACK_MCP_REACTION_TOOL=!C1234567890' " +
933-
"to enable all except one or 'SLACK_MCP_REACTION_TOOL=true' for all channels and DMs",
934-
)
935+
if !strings.Contains(enabledTools, "reactions_add") && !strings.Contains(enabledTools, "reactions_remove") {
936+
ch.logger.Error("Reactions tool disabled by default")
937+
return nil, errors.New(
938+
"by default, the reactions tools are disabled to guard Slack workspaces against accidental spamming. " +
939+
"To enable them, set the SLACK_MCP_REACTION_TOOL environment variable to true, 1, or comma separated list of channels " +
940+
"to limit where the MCP can manage reactions, e.g. 'SLACK_MCP_REACTION_TOOL=C1234567890,D0987654321', 'SLACK_MCP_REACTION_TOOL=!C1234567890' " +
941+
"to enable all except one or 'SLACK_MCP_REACTION_TOOL=true' for all channels and DMs",
942+
)
943+
}
944+
toolConfig = "true"
935945
}
936946

937947
channel := request.GetString("channel_id", "")
@@ -967,12 +977,17 @@ func (ch *ConversationsHandler) parseParamsToolReaction(ctx context.Context, req
967977

968978
func (ch *ConversationsHandler) parseParamsToolFilesGet(request mcp.CallToolRequest) (*filesGetParams, error) {
969979
toolConfig := os.Getenv("SLACK_MCP_ATTACHMENT_TOOL")
980+
enabledTools := os.Getenv("SLACK_MCP_ENABLED_TOOLS")
981+
970982
if toolConfig == "" {
971-
ch.logger.Error("Attachment tool disabled by default")
972-
return nil, errors.New(
973-
"by default, the attachment_get_data tool is disabled. " +
974-
"To enable it, set the SLACK_MCP_ATTACHMENT_TOOL environment variable to true or 1",
975-
)
983+
if !strings.Contains(enabledTools, "attachment_get_data") {
984+
ch.logger.Error("Attachment tool disabled by default")
985+
return nil, errors.New(
986+
"by default, the attachment_get_data tool is disabled. " +
987+
"To enable it, set the SLACK_MCP_ATTACHMENT_TOOL environment variable to true or 1",
988+
)
989+
}
990+
toolConfig = "true"
976991
}
977992
if toolConfig != "true" && toolConfig != "1" && toolConfig != "yes" {
978993
ch.logger.Error("Attachment tool disabled", zap.String("config", toolConfig))

0 commit comments

Comments
 (0)