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>
This commit is contained in:
Sanghoon Hong
2025-10-18 22:09:39 +09:00
parent 7920208b78
commit bb9469d3e6
7 changed files with 131 additions and 19 deletions
+10 -1
View File
@@ -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_XOXC_TOKEN="" # Replace with your actual token
SLACK_MCP_XOXD_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
+4 -1
View File
@@ -60,6 +60,8 @@ Add a message to a public channel, private channel, or direct message (DM, or IM
### 4. conversations_search_messages ### 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. 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:** - **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. - `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. - `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.
@@ -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_XOXC_TOKEN` | Yes* | `nil` | Slack browser token (`xoxc-...`) |
| `SLACK_MCP_XOXD_TOKEN` | Yes* | `nil` | Slack browser cookie `d` (`xoxd-...`) | | `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_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_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_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 | | `SLACK_MCP_API_KEY` | No | `nil` | Bearer token for SSE and HTTP transports |
@@ -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_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` | | `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 ### Limitations matrix & Cache
+20 -4
View File
@@ -2,7 +2,11 @@
Open up your Slack in your browser and login. 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. - Open your browser's Developer Console.
- In Firefox, under `Tools -> Browser Tools -> Web Developer tools` in the menu bar - In Firefox, under `Tools -> Browser Tools -> Web Developer tools` in the menu bar
@@ -16,7 +20,7 @@ Open up your Slack in your browser and login.
Token value is printed right after the executed command (it starts with Token value is printed right after the executed command (it starts with
`xoxc-`), save it somewhere for now. `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. - 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`. - Find the cookie with the name `d`. That's right, just the letter `d`.
@@ -24,7 +28,7 @@ Token value is printed right after the executed command (it starts with
- Press Ctrl+C or Cmd+C to copy it's value to clipboard. - Press Ctrl+C or Cmd+C to copy it's value to clipboard.
- Save it for later. - 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: Instead of using browser-based tokens (`xoxc`/`xoxd`), you can use a User OAuth token:
@@ -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 3. Install the app to your workspace
4. Copy the "User OAuth Token" (starts with `xoxp-`) 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: To create the app from a manifest with permissions preconfigured, use the following code snippet:
```json ```json
@@ -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) See next: [Installation](02-installation.md)
+24 -3
View File
@@ -10,8 +10,8 @@ For [Claude Desktop](https://claude.ai/download) users, you can use the DXT exte
2. Click on the `Extensions` tab. 2. Click on the `Extensions` tab.
3. Drag and drop the downloaded .dxt file to install it and click "Install". 3. Drag and drop the downloaded .dxt file to install it and click "Install".
5. Fill all required configuration fields 5. Fill all required configuration fields
- Authentication method: `xoxc/xoxd` or `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, or `SLACK_MCP_XOXP_TOKEN` in case of `xoxp`. - 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 enable `Add Message Tool` to allow posting messages to channels.
- You may also change User-Agent if needed if you have Enterprise Slack. - You may also change User-Agent if needed if you have Enterprise Slack.
6. Enable MCP Server. 6. Enable MCP Server.
@@ -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 `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 `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] > [!IMPORTANT]
> Remember to replace tokens in the configuration with your own tokens, as they are just examples. > Remember to replace tokens in the configuration with your own tokens, as they are just examples.
@@ -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 ``` json
{ {
"mcpServers": { "mcpServers": {
+10 -2
View File
@@ -31,6 +31,7 @@
"SLACK_MCP_XOXC_TOKEN": "${user_config.xoxc_token}", "SLACK_MCP_XOXC_TOKEN": "${user_config.xoxc_token}",
"SLACK_MCP_XOXD_TOKEN": "${user_config.xoxd_token}", "SLACK_MCP_XOXD_TOKEN": "${user_config.xoxd_token}",
"SLACK_MCP_XOXP_TOKEN": "${user_config.xoxp_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_USER_AGENT": "${user_config.user_agent}",
"SLACK_MCP_ADD_MESSAGE_TOOL": "${user_config.add_message_tool}", "SLACK_MCP_ADD_MESSAGE_TOOL": "${user_config.add_message_tool}",
"SLACK_MCP_USERS_CACHE": "${HOME}/.users_cache.json", "SLACK_MCP_USERS_CACHE": "${HOME}/.users_cache.json",
@@ -51,8 +52,8 @@
"user_config": { "user_config": {
"auth_method": { "auth_method": {
"type": "string", "type": "string",
"title": "Authentication Method: xoxc/xoxd or xoxp", "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) or 'xoxp' (OAuth2).", "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": "", "default": "",
"required": true "required": true
}, },
@@ -77,6 +78,13 @@
"sensitive": true, "sensitive": true,
"required": false "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": { "user_agent": {
"type": "string", "type": "string",
"title": "Value for user agent", "title": "Value for user agent",
+57 -6
View File
@@ -79,6 +79,7 @@ type MCPSlackClient struct {
isEnterprise bool isEnterprise bool
isOAuth bool isOAuth bool
isBotToken bool
teamEndpoint string teamEndpoint string
} }
@@ -135,6 +136,13 @@ func NewMCPSlackClient(authProvider auth.Provider, logger *zap.Logger) (*MCPSlac
} }
isEnterprise := authResp.EnterpriseID != "" 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{ return &MCPSlackClient{
slackClient: slackClient, slackClient: slackClient,
@@ -142,7 +150,8 @@ func NewMCPSlackClient(authProvider auth.Provider, logger *zap.Logger) (*MCPSlac
authResponse: authResponse, authResponse: authResponse,
authProvider: authProvider, authProvider: authProvider,
isEnterprise: isEnterprise, isEnterprise: isEnterprise,
isOAuth: strings.HasPrefix(authProvider.SlackToken(), "xoxp-"), isOAuth: isOAuth,
isBotToken: isBotToken,
teamEndpoint: authResp.URL, teamEndpoint: authResp.URL,
}, nil }, nil
} }
@@ -268,6 +277,10 @@ func (c *MCPSlackClient) AuthResponse() *slack.AuthTestResponse {
return c.authResponse return c.authResponse
} }
func (c *MCPSlackClient) IsBotToken() bool {
return c.isBotToken
}
func (c *MCPSlackClient) Raw() struct { func (c *MCPSlackClient) Raw() struct {
Slack *slack.Client Slack *slack.Client
Edge *edge.Client Edge *edge.Client
@@ -287,8 +300,23 @@ func New(transport string, logger *zap.Logger) *ApiProvider {
err error err error
) )
// Check for XOXP token first (User OAuth) // Read all environment variables
xoxpToken := os.Getenv("SLACK_MCP_XOXP_TOKEN") 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 != "" { if xoxpToken != "" {
authProvider, err = auth.NewValueAuth(xoxpToken, "") authProvider, err = auth.NewValueAuth(xoxpToken, "")
if err != nil { if err != nil {
@@ -298,12 +326,24 @@ func New(transport string, logger *zap.Logger) *ApiProvider {
return newWithXOXP(transport, authProvider, logger) return newWithXOXP(transport, authProvider, logger)
} }
// Fall back to XOXC/XOXD tokens (session-based) // Priority 2: XOXB token (Bot)
xoxcToken := os.Getenv("SLACK_MCP_XOXC_TOKEN") if xoxbToken != "" {
xoxdToken := os.Getenv("SLACK_MCP_XOXD_TOKEN") 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 == "" { 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) authProvider, err = auth.NewValueAuth(xoxcToken, xoxdToken)
@@ -356,6 +396,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 { func newWithXOXC(transport string, authProvider auth.ValueAuth, logger *zap.Logger) *ApiProvider {
var ( var (
client *MCPSlackClient client *MCPSlackClient
@@ -676,6 +722,11 @@ func (ap *ApiProvider) Slack() SlackAPI {
return ap.client return ap.client
} }
func (ap *ApiProvider) IsBotToken() bool {
client, ok := ap.client.(*MCPSlackClient)
return ok && client != nil && client.IsBotToken()
}
func mapChannel( func mapChannel(
id, name, nameNormalized, topic, purpose, user string, id, name, nameNormalized, topic, purpose, user string,
members []string, members []string,
+6 -2
View File
@@ -93,7 +93,7 @@ func NewMCPServer(provider *provider.ApiProvider, logger *zap.Logger) *MCPServer
), ),
), conversationsHandler.ConversationsAddMessageHandler) ), 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.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.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."), 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."),
@@ -133,7 +133,11 @@ func NewMCPServer(provider *provider.ApiProvider, logger *zap.Logger) *MCPServer
mcp.DefaultNumber(20), mcp.DefaultNumber(20),
mcp.Description("The maximum number of items to return. Must be an integer between 1 and 100."), 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) channelsHandler := handler.NewChannelsHandler(provider, logger)