diff --git a/package-lock.json b/package-lock.json index cac04f11..7a410b60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3598,7 +3598,7 @@ "version": "0.1.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "0.5.0" + "@modelcontextprotocol/sdk": "0.6.0" }, "bin": { "mcp-server-slack": "dist/index.js" @@ -3607,6 +3607,17 @@ "shx": "^0.3.4", "typescript": "^5.6.2" } + }, + "src/slack/node_modules/@modelcontextprotocol/sdk": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-0.6.0.tgz", + "integrity": "sha512-9rsDudGhDtMbvxohPoMMyAUOmEzQsOK+XFchh6gZGqo8sx9sBuZQs+CUttXqa8RZXKDaJRCN2tUtgGof7jRkkw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "raw-body": "^3.0.0", + "zod": "^3.23.8" + } } } } diff --git a/src/slack/README.md b/src/slack/README.md new file mode 100644 index 00000000..7c99a4a7 --- /dev/null +++ b/src/slack/README.md @@ -0,0 +1,115 @@ +# Slack MCP Server + +MCP Server for the Slack API, enabling Claude to interact with Slack workspaces. + +## Tools + +1. `slack_list_channels` + - List public channels in the workspace + - Optional inputs: + - `limit` (number, default: 100, max: 200): Maximum number of channels to return + - `cursor` (string): Pagination cursor for next page + - Returns: List of channels with their IDs and information + +2. `slack_post_message` + - Post a new message to a Slack channel + - Required inputs: + - `channel_id` (string): The ID of the channel to post to + - `text` (string): The message text to post + - Returns: Message posting confirmation and timestamp + +3. `slack_reply_to_thread` + - Reply to a specific message thread + - Required inputs: + - `channel_id` (string): The channel containing the thread + - `thread_ts` (string): Timestamp of the parent message + - `text` (string): The reply text + - Returns: Reply confirmation and timestamp + +4. `slack_add_reaction` + - Add an emoji reaction to a message + - Required inputs: + - `channel_id` (string): The channel containing the message + - `timestamp` (string): Message timestamp to react to + - `reaction` (string): Emoji name without colons + - Returns: Reaction confirmation + +5. `slack_get_channel_history` + - Get recent messages from a channel + - Required inputs: + - `channel_id` (string): The channel ID + - Optional inputs: + - `limit` (number, default: 10): Number of messages to retrieve + - Returns: List of messages with their content and metadata + +6. `slack_get_thread_replies` + - Get all replies in a message thread + - Required inputs: + - `channel_id` (string): The channel containing the thread + - `thread_ts` (string): Timestamp of the parent message + - Returns: List of replies with their content and metadata + +7. `slack_search_messages` + - Search for messages across channels + - Required inputs: + - `query` (string): The search query + - Optional inputs: + - `count` (number, default: 5): Number of results to return + - Returns: Matching messages with their context + +8. `slack_get_users` + - Get list of workspace users with basic profile information + - Optional inputs: + - `cursor` (string): Pagination cursor for next page + - `limit` (number, default: 100, max: 200): Maximum users to return + - Returns: List of users with their basic profiles + +9. `slack_get_user_profile` + - Get detailed profile information for a specific user + - Required inputs: + - `user_id` (string): The user's ID + - Returns: Detailed user profile information + +## Setup + +1. Create a Slack App: + - Visit the [Slack Apps page](https://api.slack.com/apps) + - Click "Create New App" + - Choose "From scratch" + - Name your app and select your workspace + +2. Configure Bot Token Scopes: + Navigate to "OAuth & Permissions" and add these scopes: + - `channels:history` - View messages and other content in public channels + - `channels:read` - View basic channel information + - `chat:write` - Send messages as the app + - `reactions:write` - Add emoji reactions to messages + - `users:read` - View users and their basic information + +4. Install App to Workspace: + - Click "Install to Workspace" and authorize the app + - Save the "Bot User OAuth Token" that starts with `xoxb-` + +5. Get your Team ID (starts with a `T`) by following [this guidance](https://slack.com/help/articles/221769328-Locate-your-Slack-URL-or-ID#find-your-workspace-or-org-id) + +### Usage with the Claude Desktop app + Add the following to your `claude_desktop_config.json`: + ```json + { + "mcp-server-slack": { + "command": "mcp-server-slack", + "env": { + "SLACK_BOT_TOKEN": "xoxb-your-bot-token", + "SLACK_TEAM_ID": "T01234567" + } + } + } + ``` + +### Troubleshooting + +If you encounter permission errors, verify that: +1. All required scopes are added to your Slack app +2. The app is properly installed to your workspace +3. The tokens and workspace ID are correctly copied to your configuration +4. The app has been added to the channels it needs to access \ No newline at end of file diff --git a/src/slack/index.ts b/src/slack/index.ts index 6dc8051d..8cdc143c 100644 --- a/src/slack/index.ts +++ b/src/slack/index.ts @@ -41,11 +41,6 @@ interface GetThreadRepliesArgs { thread_ts: string; } -interface SearchMessagesArgs { - query: string; - count?: number; -} - interface GetUsersArgs { cursor?: string; limit?: number; @@ -180,26 +175,6 @@ const getThreadRepliesTool: Tool = { }, }; -const searchMessagesTool: Tool = { - name: "slack_search_messages", - description: "Search for messages across channels", - inputSchema: { - type: "object", - properties: { - query: { - type: "string", - description: "The search query", - }, - count: { - type: "number", - description: "Number of results to return (default 5)", - default: 5, - }, - }, - required: ["query"], - }, -}; - const getUsersTool: Tool = { name: "slack_get_users", description: @@ -237,17 +212,12 @@ const getUserProfileTool: Tool = { class SlackClient { private botHeaders: { Authorization: string; "Content-Type": string }; - private userHeaders: { Authorization: string; "Content-Type": string }; - constructor(botToken: string, userToken: string) { + constructor(botToken: string) { this.botHeaders = { Authorization: `Bearer ${botToken}`, "Content-Type": "application/json", }; - this.userHeaders = { - Authorization: `Bearer ${userToken}`, - "Content-Type": "application/json", - }; } async getChannels(limit: number = 100, cursor?: string): Promise { @@ -350,20 +320,6 @@ class SlackClient { return response.json(); } - async searchMessages(query: string, count: number = 5): Promise { - const params = new URLSearchParams({ - query: query, - count: count.toString(), - }); - - const response = await fetch( - `https://slack.com/api/search.messages?${params}`, - { headers: this.userHeaders }, - ); - - return response.json(); - } - async getUsers(limit: number = 100, cursor?: string): Promise { const params = new URLSearchParams({ limit: Math.min(limit, 200).toString(), @@ -398,12 +354,11 @@ class SlackClient { async function main() { const botToken = process.env.SLACK_BOT_TOKEN; - const userToken = process.env.SLACK_USER_TOKEN; const teamId = process.env.SLACK_TEAM_ID; - if (!botToken || !userToken || !teamId) { + if (!botToken || !teamId) { console.error( - "Please set SLACK_BOT_TOKEN, SLACK_USER_TOKEN, and SLACK_TEAM_ID environment variables", + "Please set SLACK_BOT_TOKEN and SLACK_TEAM_ID environment variables", ); process.exit(1); } @@ -421,7 +376,7 @@ async function main() { }, ); - const slackClient = new SlackClient(botToken, userToken); + const slackClient = new SlackClient(botToken); server.setRequestHandler( CallToolRequestSchema, @@ -528,21 +483,6 @@ async function main() { }; } - case "slack_search_messages": { - const args = request.params - .arguments as unknown as SearchMessagesArgs; - if (!args.query) { - throw new Error("Missing required argument: query"); - } - const response = await slackClient.searchMessages( - args.query, - args.count, - ); - return { - content: [{ type: "text", text: JSON.stringify(response) }], - }; - } - case "slack_get_users": { const args = request.params.arguments as unknown as GetUsersArgs; const response = await slackClient.getUsers( @@ -595,7 +535,6 @@ async function main() { addReactionTool, getChannelHistoryTool, getThreadRepliesTool, - searchMessagesTool, getUsersTool, getUserProfileTool, ], diff --git a/src/slack/package.json b/src/slack/package.json index 23333c80..5b2788a7 100644 --- a/src/slack/package.json +++ b/src/slack/package.json @@ -19,7 +19,7 @@ "watch": "tsc --watch" }, "dependencies": { - "@modelcontextprotocol/sdk": "0.5.0" + "@modelcontextprotocol/sdk": "0.6.0" }, "devDependencies": { "shx": "^0.3.4",