Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .env.dist
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# User OAuth Token (starts with xoxp-)
SLACK_MCP_XOXP_TOKEN="" # Replace with your user OAuth token

# Bot OAuth Token (starts with xoxb-)
SLACK_MCP_XOXB_TOKEN="" # Replace with your bot token

# Session-based tokens (browser tokens)
SLACK_MCP_XOXC_TOKEN="" # Replace with your actual token
SLACK_MCP_XOXD_TOKEN="" # Replace with your actual token
SLACK_MCP_SSE_API_KEY="" # Replace with your actual API key

# SSE API Key for authentication
SLACK_MCP_SSE_API_KEY="" # Replace with your actual API key
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ Add a message to a public channel, private channel, or direct message (DM, or IM

### 4. conversations_search_messages
Search messages in a public channel, private channel, or direct message (DM, or IM) conversation using filters. All filters are optional, if not provided then search_query is required.

> **Note**: This tool is not available when using bot tokens (`xoxb-*`). Bot tokens cannot use the `search.messages` API.
- **Parameters:**
- `search_query` (string, optional): Search query to filter messages. Example: 'marketing report' or full URL of Slack message e.g. 'https://slack.com/archives/C1234567890/p1234567890123456', then the tool will return a single message matching given URL, herewith all other parameters will be ignored.
- `filter_in_channel` (string, optional): Filter messages in a specific channel by its ID or name. Example: `C1234567890` or `#general`. If not provided, all channels will be searched.
Expand Down Expand Up @@ -123,6 +125,7 @@ Fetches a CSV directory of all users in the workspace.
| `SLACK_MCP_XOXC_TOKEN` | Yes* | `nil` | Slack browser token (`xoxc-...`) |
| `SLACK_MCP_XOXD_TOKEN` | Yes* | `nil` | Slack browser cookie `d` (`xoxd-...`) |
| `SLACK_MCP_XOXP_TOKEN` | Yes* | `nil` | User OAuth token (`xoxp-...`) — alternative to xoxc/xoxd |
| `SLACK_MCP_XOXB_TOKEN` | Yes* | `nil` | Bot token (`xoxb-...`) — alternative to xoxp/xoxc/xoxd. Bot has limited access (invited channels only, no search) |
| `SLACK_MCP_PORT` | No | `13080` | Port for the MCP server to listen on |
| `SLACK_MCP_HOST` | No | `127.0.0.1` | Host for the MCP server to listen on |
| `SLACK_MCP_API_KEY` | No | `nil` | Bearer token for SSE and HTTP transports |
Expand All @@ -139,7 +142,7 @@ Fetches a CSV directory of all users in the workspace.
| `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. |
| `SLACK_MCP_LOG_LEVEL` | No | `info` | Log-level for stdout or stderr. Valid values are: `debug`, `info`, `warn`, `error`, `panic` and `fatal` |

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

### Limitations matrix & Cache

Expand Down
24 changes: 20 additions & 4 deletions docs/01-authentication-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

Open up your Slack in your browser and login.

#### Lookup `SLACK_MCP_XOXC_TOKEN`
> **Note**: You only need one of the following: an `xoxp-*` User OAuth token, an `xoxb-*` Bot token, or both `xoxc-*` and `xoxd-*` session tokens. User/Bot tokens are more secure and do not require a browser session. If multiple are provided, priority is `xoxp` > `xoxb` > `xoxc/xoxd`.

#### Option 1: Using `SLACK_MCP_XOXC_TOKEN`/`SLACK_MCP_XOXD_TOKEN` (Browser session)

##### Lookup `SLACK_MCP_XOXC_TOKEN`

- Open your browser's Developer Console.
- In Firefox, under `Tools -> Browser Tools -> Web Developer tools` in the menu bar
Expand All @@ -16,15 +20,15 @@ Open up your Slack in your browser and login.
Token value is printed right after the executed command (it starts with
`xoxc-`), save it somewhere for now.

#### Lookup `SLACK_MCP_XOXD_TOKEN`
##### Lookup `SLACK_MCP_XOXD_TOKEN`

- Switch to "Application" tab and select "Cookies" in the left navigation pane.
- Find the cookie with the name `d`. That's right, just the letter `d`.
- Double-click the Value of this cookie.
- Press Ctrl+C or Cmd+C to copy it's value to clipboard.
- Save it for later.

#### Alternative: Using `SLACK_MCP_XOXP_TOKEN` (User OAuth)
#### Option 2: Using `SLACK_MCP_XOXP_TOKEN` (User OAuth)

Instead of using browser-based tokens (`xoxc`/`xoxd`), you can use a User OAuth token:

Expand All @@ -47,6 +51,7 @@ Instead of using browser-based tokens (`xoxc`/`xoxd`), you can use a User OAuth
3. Install the app to your workspace
4. Copy the "User OAuth Token" (starts with `xoxp-`)

##### App manifest (preconfigured scopes)
To create the app from a manifest with permissions preconfigured, use the following code snippet:

```json
Expand Down Expand Up @@ -81,6 +86,17 @@ To create the app from a manifest with permissions preconfigured, use the follow
}
```

> **Note**: You only need **either** XOXP token **or** both XOXC/XOXD tokens. XOXP user tokens are more secure and don't require browser session extraction.
#### Option 3: Using `SLACK_MCP_XOXB_TOKEN` (Bot Token)

You can also use a Bot token instead of a User token:

1. Go to [api.slack.com/apps](https://api.slack.com/apps) and create a new app
2. Under "OAuth & Permissions", add Bot Token Scopes (same as User scopes above, except `search:read`)
3. Install the app to your workspace
4. Copy the "Bot User OAuth Token" (starts with `xoxb-`)
5. **Important**: Bot must be invited to channels for access

> **Note**: Bot tokens cannot use `search.messages` API, so `conversations_search_messages` tool will not be available.


See next: [Installation](02-installation.md)
27 changes: 24 additions & 3 deletions docs/03-configuration-and-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ For [Claude Desktop](https://claude.ai/download) users, you can use the DXT exte
2. Click on the `Extensions` tab.
3. Drag and drop the downloaded .dxt file to install it and click "Install".
5. Fill all required configuration fields
- Authentication method: `xoxc/xoxd` or `xoxp`.
- Value for `SLACK_MCP_XOXC_TOKEN` and `SLACK_MCP_XOXD_TOKEN` in case of `xoxc/xoxd` method, or `SLACK_MCP_XOXP_TOKEN` in case of `xoxp`.
- Authentication method: `xoxc/xoxd`, `xoxp`, or `xoxb`.
- Value for `SLACK_MCP_XOXC_TOKEN` and `SLACK_MCP_XOXD_TOKEN` in case of `xoxc/xoxd` method, `SLACK_MCP_XOXP_TOKEN` in case of `xoxp`, or `SLACK_MCP_XOXB_TOKEN` in case of `xoxb`.
- You may also enable `Add Message Tool` to allow posting messages to channels.
- You may also change User-Agent if needed if you have Enterprise Slack.
6. Enable MCP Server.
Expand All @@ -27,6 +27,7 @@ Below are prepared configurations:

- `npx` and `xoxc/xoxd` method: [![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=slack-mcp-server&config=eyJjb21tYW5kIjogIm5weCAteSBzbGFjay1tY3Atc2VydmVyQGxhdGVzdCAtLXRyYW5zcG9ydCBzdGRpbyIsImVudiI6IHsiU0xBQ0tfTUNQX1hPWENfVE9LRU4iOiAieG94Yy0uLi4iLCAiU0xBQ0tfTUNQX1hPWERfVE9LRU4iOiAieG94ZC0uLi4ifSwiZGlzYWJsZWQiOiBmYWxzZSwiYXV0b0FwcHJvdmUiOiBbXX0%3D)
- `npx` and `xoxp` method: [![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=slack-mcp-server&config=eyJjb21tYW5kIjogIm5weCAteSBzbGFjay1tY3Atc2VydmVyQGxhdGVzdCAtLXRyYW5zcG9ydCBzdGRpbyIsImVudiI6IHsiU0xBQ0tfTUNQX1hPWFBfVE9LRU4iOiAieG94cC0uLi4ifSwiZGlzYWJsZWQiOiBmYWxzZSwiYXV0b0FwcHJvdmUiOiBbXX0%3D)
- `npx` and `xoxb` method: [![Install MCP Server](https://cursor.com/deeplink/mcp-install-light.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=slack-mcp-server&config=eyJjb21tYW5kIjogIm5weCAteSBzbGFjay1tY3Atc2VydmVyQGxhdGVzdCAtLXRyYW5zcG9ydCBzdGRpbyIsImVudiI6IHsiU0xBQ0tfTUNQX1hPWEJfVE9LRU4iOiAieG94Yi0uLi4ifSwiZGlzYWJsZWQiOiBmYWxzZSwiYXV0b0FwcHJvdmUiOiBbXX0%3D)

> [!IMPORTANT]
> Remember to replace tokens in the configuration with your own tokens, as they are just examples.
Expand Down Expand Up @@ -60,7 +61,27 @@ Open your `claude_desktop_config.json` and add the mcp server to the list of `mc
}
```

**Option 2: Using XOXC/XOXD Tokens**
**Option 2: Using XOXB Token (Bot)**
``` json
{
"mcpServers": {
"slack": {
"command": "npx",
"args": [
"-y",
"slack-mcp-server@latest",
"--transport",
"stdio"
],
"env": {
"SLACK_MCP_XOXB_TOKEN": "xoxb-..."
}
}
}
}
```

**Option 3: Using XOXC/XOXD Tokens**
``` json
{
"mcpServers": {
Expand Down
12 changes: 10 additions & 2 deletions manifest-dxt.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"SLACK_MCP_XOXC_TOKEN": "${user_config.xoxc_token}",
"SLACK_MCP_XOXD_TOKEN": "${user_config.xoxd_token}",
"SLACK_MCP_XOXP_TOKEN": "${user_config.xoxp_token}",
"SLACK_MCP_XOXB_TOKEN": "${user_config.xoxb_token}",
"SLACK_MCP_USER_AGENT": "${user_config.user_agent}",
"SLACK_MCP_ADD_MESSAGE_TOOL": "${user_config.add_message_tool}",
"SLACK_MCP_USERS_CACHE": "${HOME}/.users_cache.json",
Expand All @@ -51,8 +52,8 @@
"user_config": {
"auth_method": {
"type": "string",
"title": "Authentication Method: xoxc/xoxd or xoxp",
"description": "Select the authentication method for the Slack MCP Server. You can choose between 'xoxc/xoxd' (browser based) or 'xoxp' (OAuth2).",
"title": "Authentication Method: xoxc/xoxd, xoxp, or xoxb",
"description": "Select the authentication method for the Slack MCP Server. You can choose between 'xoxc/xoxd' (browser based), 'xoxp' (User OAuth), or 'xoxb' (Bot OAuth).",
"default": "",
"required": true
},
Expand All @@ -77,6 +78,13 @@
"sensitive": true,
"required": false
},
"xoxb_token": {
"type": "string",
"title": "Value for xoxb token (Bot)",
"description": "Bot OAuth token for channel-specific automation. Bot must be invited to channels for access. Note: Search functionality is not available with bot tokens.",
"sensitive": true,
"required": false
},
"user_agent": {
"type": "string",
"title": "Value for user agent",
Expand Down
63 changes: 57 additions & 6 deletions pkg/provider/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ type MCPSlackClient struct {

isEnterprise bool
isOAuth bool
isBotToken bool
teamEndpoint string
}

Expand Down Expand Up @@ -137,14 +138,22 @@ func NewMCPSlackClient(authProvider auth.Provider, logger *zap.Logger) (*MCPSlac
}

isEnterprise := authResp.EnterpriseID != ""
token := authProvider.SlackToken()

// Token type detection
// isOAuth: Official OAuth tokens (xoxp or xoxb) - uses Standard API
// isBotToken: Bot token - determines feature availability (e.g., search)
isOAuth := strings.HasPrefix(token, "xoxp-") || strings.HasPrefix(token, "xoxb-")
isBotToken := strings.HasPrefix(token, "xoxb-")

return &MCPSlackClient{
slackClient: slackClient,
edgeClient: edgeClient,
authResponse: authResponse,
authProvider: authProvider,
isEnterprise: isEnterprise,
isOAuth: strings.HasPrefix(authProvider.SlackToken(), "xoxp-"),
isOAuth: isOAuth,
isBotToken: isBotToken,
teamEndpoint: authResp.URL,
}, nil
}
Expand Down Expand Up @@ -270,6 +279,10 @@ func (c *MCPSlackClient) AuthResponse() *slack.AuthTestResponse {
return c.authResponse
}

func (c *MCPSlackClient) IsBotToken() bool {
return c.isBotToken
}

func (c *MCPSlackClient) Raw() struct {
Slack *slack.Client
Edge *edge.Client
Expand All @@ -289,8 +302,23 @@ func New(transport string, logger *zap.Logger) *ApiProvider {
err error
)

// Check for XOXP token first (User OAuth)
// Read all environment variables
xoxpToken := os.Getenv("SLACK_MCP_XOXP_TOKEN")
xoxbToken := os.Getenv("SLACK_MCP_XOXB_TOKEN")
xoxcToken := os.Getenv("SLACK_MCP_XOXC_TOKEN")
xoxdToken := os.Getenv("SLACK_MCP_XOXD_TOKEN")

// Warn if both user and bot tokens are set
if xoxpToken != "" && xoxbToken != "" {
logger.Warn(
"Both SLACK_MCP_XOXP_TOKEN and SLACK_MCP_XOXB_TOKEN are set. "+
"Using User token (xoxp) for full features. "+
"Bot token will be ignored.",
zap.String("context", "console"),
)
}

// Priority 1: XOXP token (User OAuth)
if xoxpToken != "" {
authProvider, err = auth.NewValueAuth(xoxpToken, "")
if err != nil {
Expand All @@ -300,12 +328,24 @@ func New(transport string, logger *zap.Logger) *ApiProvider {
return newWithXOXP(transport, authProvider, logger)
}

// Fall back to XOXC/XOXD tokens (session-based)
xoxcToken := os.Getenv("SLACK_MCP_XOXC_TOKEN")
xoxdToken := os.Getenv("SLACK_MCP_XOXD_TOKEN")
// Priority 2: XOXB token (Bot)
if xoxbToken != "" {
authProvider, err = auth.NewValueAuth(xoxbToken, "")
if err != nil {
logger.Fatal("Failed to create auth provider with XOXB token", zap.Error(err))
}

logger.Info("Using Bot token authentication",
zap.String("context", "console"),
zap.String("token_type", "xoxb"),
)

return newWithXOXB(transport, authProvider, logger)
}

// Priority 3: XOXC/XOXD tokens (session-based)
if xoxcToken == "" || xoxdToken == "" {
logger.Fatal("Authentication required: Either SLACK_MCP_XOXP_TOKEN (User OAuth) or both SLACK_MCP_XOXC_TOKEN and SLACK_MCP_XOXD_TOKEN (session-based) environment variables must be provided")
logger.Fatal("Authentication required: Either SLACK_MCP_XOXP_TOKEN, SLACK_MCP_XOXB_TOKEN, or both SLACK_MCP_XOXC_TOKEN and SLACK_MCP_XOXD_TOKEN must be provided")
}

authProvider, err = auth.NewValueAuth(xoxcToken, xoxdToken)
Expand Down Expand Up @@ -358,6 +398,12 @@ func newWithXOXP(transport string, authProvider auth.ValueAuth, logger *zap.Logg
}
}

func newWithXOXB(transport string, authProvider auth.ValueAuth, logger *zap.Logger) *ApiProvider {
// Bot tokens do not support demo mode, but otherwise share the same
// initialization logic as user OAuth tokens.
return newWithXOXP(transport, authProvider, logger)
}

func newWithXOXC(transport string, authProvider auth.ValueAuth, logger *zap.Logger) *ApiProvider {
var (
client *MCPSlackClient
Expand Down Expand Up @@ -693,6 +739,11 @@ func (ap *ApiProvider) Slack() SlackAPI {
return ap.client
}

func (ap *ApiProvider) IsBotToken() bool {
client, ok := ap.client.(*MCPSlackClient)
return ok && client != nil && client.IsBotToken()
}

func mapChannel(
id, name, nameNormalized, topic, purpose, user string,
members []string,
Expand Down
8 changes: 6 additions & 2 deletions pkg/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ func NewMCPServer(provider *provider.ApiProvider, logger *zap.Logger) *MCPServer
),
), conversationsHandler.ConversationsAddMessageHandler)

s.AddTool(mcp.NewTool("conversations_search_messages",
conversationsSearchTool := mcp.NewTool("conversations_search_messages",
mcp.WithDescription("Search messages in a public channel, private channel, or direct message (DM, or IM) conversation using filters. All filters are optional, if not provided then search_query is required."),
mcp.WithString("search_query",
mcp.Description("Search query to filter messages. Example: 'marketing report' or full URL of Slack message e.g. 'https://slack.com/archives/C1234567890/p1234567890123456', then the tool will return a single message matching given URL, herewith all other parameters will be ignored."),
Expand Down Expand Up @@ -133,7 +133,11 @@ func NewMCPServer(provider *provider.ApiProvider, logger *zap.Logger) *MCPServer
mcp.DefaultNumber(20),
mcp.Description("The maximum number of items to return. Must be an integer between 1 and 100."),
),
), conversationsHandler.ConversationsSearchHandler)
)
// Only register search tool for non-bot tokens (bot tokens cannot use search.messages API)
if !provider.IsBotToken() {
s.AddTool(conversationsSearchTool, conversationsHandler.ConversationsSearchHandler)
}

channelsHandler := handler.NewChannelsHandler(provider, logger)

Expand Down