slack-workspace-mcp-server 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +122 -0
  2. package/build/index.d.ts +2 -0
  3. package/build/index.integration-with-mock.d.ts +2 -0
  4. package/build/index.integration-with-mock.js +34 -0
  5. package/build/index.js +76 -0
  6. package/package.json +47 -0
  7. package/shared/index.d.ts +4 -0
  8. package/shared/index.js +7 -0
  9. package/shared/logging.d.ts +10 -0
  10. package/shared/logging.js +21 -0
  11. package/shared/server.d.ts +134 -0
  12. package/shared/server.js +68 -0
  13. package/shared/slack-client/lib/add-reaction.d.ts +5 -0
  14. package/shared/slack-client/lib/add-reaction.js +30 -0
  15. package/shared/slack-client/lib/get-channel.d.ts +5 -0
  16. package/shared/slack-client/lib/get-channel.js +24 -0
  17. package/shared/slack-client/lib/get-channels.d.ts +10 -0
  18. package/shared/slack-client/lib/get-channels.js +37 -0
  19. package/shared/slack-client/lib/get-messages.d.ts +16 -0
  20. package/shared/slack-client/lib/get-messages.js +38 -0
  21. package/shared/slack-client/lib/get-thread.d.ts +16 -0
  22. package/shared/slack-client/lib/get-thread.js +39 -0
  23. package/shared/slack-client/lib/post-message.d.ts +10 -0
  24. package/shared/slack-client/lib/post-message.js +44 -0
  25. package/shared/slack-client/lib/update-message.d.ts +5 -0
  26. package/shared/slack-client/lib/update-message.js +32 -0
  27. package/shared/slack-client/slack-client.integration-mock.d.ts +16 -0
  28. package/shared/slack-client/slack-client.integration-mock.js +106 -0
  29. package/shared/tools/get-channel.d.ts +55 -0
  30. package/shared/tools/get-channel.js +136 -0
  31. package/shared/tools/get-channels.d.ts +46 -0
  32. package/shared/tools/get-channels.js +107 -0
  33. package/shared/tools/get-thread.d.ts +54 -0
  34. package/shared/tools/get-thread.js +130 -0
  35. package/shared/tools/post-message.d.ts +44 -0
  36. package/shared/tools/post-message.js +81 -0
  37. package/shared/tools/react-to-message.d.ts +51 -0
  38. package/shared/tools/react-to-message.js +90 -0
  39. package/shared/tools/reply-to-thread.d.ts +59 -0
  40. package/shared/tools/reply-to-thread.js +97 -0
  41. package/shared/tools/update-message.d.ts +51 -0
  42. package/shared/tools/update-message.js +87 -0
  43. package/shared/tools.d.ts +18 -0
  44. package/shared/tools.js +78 -0
  45. package/shared/types.d.ts +112 -0
  46. package/shared/types.js +5 -0
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # Slack MCP Server
2
+
3
+ A Model Context Protocol (MCP) server for integrating with Slack workspaces. This server provides tools for reading and writing messages, managing threads, and reacting to messages in Slack.
4
+
5
+ ## Features
6
+
7
+ - **List Channels** - View all accessible public and private channels
8
+ - **Read Channel** - Get channel info and recent messages
9
+ - **Read Threads** - Get full threaded conversations
10
+ - **Post Messages** - Send new messages to channels
11
+ - **Reply to Threads** - Continue threaded conversations
12
+ - **Update Messages** - Edit previously posted messages
13
+ - **React to Messages** - Add emoji reactions to messages
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install slack-workspace-mcp-server
19
+ ```
20
+
21
+ Or run directly with npx:
22
+
23
+ ```bash
24
+ npx slack-workspace-mcp-server
25
+ ```
26
+
27
+ ## Configuration
28
+
29
+ ### Required Environment Variables
30
+
31
+ | Variable | Description | Example |
32
+ | ----------------- | -------------------------- | -------------- |
33
+ | `SLACK_BOT_TOKEN` | Slack Bot User OAuth Token | `xoxb-1234...` |
34
+
35
+ ### Optional Environment Variables
36
+
37
+ | Variable | Description | Default |
38
+ | -------------------- | ----------------------------------- | ----------- |
39
+ | `ENABLED_TOOLGROUPS` | Comma-separated list of tool groups | All enabled |
40
+
41
+ ### Getting a Bot Token
42
+
43
+ 1. Go to [api.slack.com/apps](https://api.slack.com/apps)
44
+ 2. Create a new app or select an existing one
45
+ 3. Navigate to **OAuth & Permissions**
46
+ 4. Add the following **Bot Token Scopes**:
47
+ - `channels:read` - View basic channel information
48
+ - `channels:history` - View messages in public channels
49
+ - `groups:read` - View private channels
50
+ - `groups:history` - View messages in private channels
51
+ - `chat:write` - Send messages
52
+ - `reactions:write` - Add reactions
53
+ 5. Install the app to your workspace
54
+ 6. Copy the **Bot User OAuth Token** (starts with `xoxb-`)
55
+
56
+ ### MCP Client Configuration
57
+
58
+ For Claude Desktop, add to your configuration:
59
+
60
+ ```json
61
+ {
62
+ "mcpServers": {
63
+ "slack": {
64
+ "command": "npx",
65
+ "args": ["slack-workspace-mcp-server"],
66
+ "env": {
67
+ "SLACK_BOT_TOKEN": "xoxb-your-token-here"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ ```
73
+
74
+ ## Available Tools
75
+
76
+ ### Read-Only Tools
77
+
78
+ | Tool | Description |
79
+ | -------------------- | ------------------------------------- |
80
+ | `slack_get_channels` | List all accessible channels |
81
+ | `slack_get_channel` | Get channel info with recent messages |
82
+ | `slack_get_thread` | Get a thread with all replies |
83
+
84
+ ### Write Tools
85
+
86
+ | Tool | Description |
87
+ | ------------------------ | ---------------------------------- |
88
+ | `slack_post_message` | Post a new message to a channel |
89
+ | `slack_reply_to_thread` | Reply to an existing thread |
90
+ | `slack_update_message` | Update a previously posted message |
91
+ | `slack_react_to_message` | Add an emoji reaction to a message |
92
+
93
+ ## Tool Groups
94
+
95
+ You can control which tools are available using the `ENABLED_TOOLGROUPS` environment variable:
96
+
97
+ - `readonly` - Only read operations (get channels, messages, threads)
98
+ - `write` - All operations including posting and reactions
99
+
100
+ Example: `ENABLED_TOOLGROUPS=readonly` to disable all write operations.
101
+
102
+ ## Development
103
+
104
+ ```bash
105
+ # Install dependencies
106
+ npm run install-all
107
+
108
+ # Run in development mode
109
+ npm run dev
110
+
111
+ # Run tests
112
+ npm test # Functional tests
113
+ npm run test:integration # Integration tests
114
+ npm run test:manual # Manual tests (requires real credentials)
115
+
116
+ # Build
117
+ npm run build
118
+ ```
119
+
120
+ ## License
121
+
122
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Integration Test Server Entry Point with Mock Client Factory
4
+ *
5
+ * This file is used for integration testing with a mocked SlackClient.
6
+ * It uses the real MCP server but injects a mock client factory.
7
+ *
8
+ * Mock data is passed via the SLACK_MOCK_DATA environment variable.
9
+ */
10
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
11
+ import { createMCPServer } from '../shared/index.js';
12
+ import { createIntegrationMockSlackClient } from '../shared/slack-client/slack-client.integration-mock.js';
13
+ async function main() {
14
+ const transport = new StdioServerTransport();
15
+ // Parse mock data from environment variable
16
+ let mockData = {};
17
+ if (process.env.SLACK_MOCK_DATA) {
18
+ try {
19
+ mockData = JSON.parse(process.env.SLACK_MOCK_DATA);
20
+ }
21
+ catch (e) {
22
+ console.error('Failed to parse SLACK_MOCK_DATA:', e);
23
+ }
24
+ }
25
+ // Create client factory that returns our mock
26
+ const clientFactory = () => createIntegrationMockSlackClient(mockData);
27
+ const { server, registerHandlers } = createMCPServer();
28
+ await registerHandlers(server, clientFactory);
29
+ await server.connect(transport);
30
+ }
31
+ main().catch((error) => {
32
+ console.error('Server error:', error);
33
+ process.exit(1);
34
+ });
package/build/index.js ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { createMCPServer } from '../shared/index.js';
4
+ import { logServerStart, logError, logWarning } from '../shared/logging.js';
5
+ // =============================================================================
6
+ // ENVIRONMENT VALIDATION
7
+ // =============================================================================
8
+ function validateEnvironment() {
9
+ const required = [
10
+ {
11
+ name: 'SLACK_BOT_TOKEN',
12
+ description: 'Slack Bot User OAuth Token (starts with xoxb-)',
13
+ example: 'xoxb-YOUR-WORKSPACE-ID-YOUR-BOT-TOKEN-HERE',
14
+ },
15
+ ];
16
+ const optional = [
17
+ {
18
+ name: 'ENABLED_TOOLGROUPS',
19
+ description: 'Comma-separated list of tool groups to enable (readonly, write)',
20
+ defaultValue: 'all groups enabled',
21
+ },
22
+ ];
23
+ const missing = required.filter(({ name }) => !process.env[name]);
24
+ if (missing.length > 0) {
25
+ logError('validateEnvironment', 'Missing required environment variables:');
26
+ missing.forEach(({ name, description, example }) => {
27
+ console.error(` - ${name}: ${description}`);
28
+ console.error(` Example: ${example}`);
29
+ });
30
+ if (optional.length > 0) {
31
+ console.error('\nOptional environment variables:');
32
+ optional.forEach(({ name, description, defaultValue }) => {
33
+ const defaultStr = defaultValue ? ` (default: ${defaultValue})` : '';
34
+ console.error(` - ${name}: ${description}${defaultStr}`);
35
+ });
36
+ }
37
+ console.error('\n----------------------------------------');
38
+ console.error('Please set the required environment variables and try again.');
39
+ console.error('\nTo get a bot token:');
40
+ console.error('1. Go to https://api.slack.com/apps');
41
+ console.error('2. Create or select your app');
42
+ console.error('3. Go to "OAuth & Permissions"');
43
+ console.error('4. Copy the "Bot User OAuth Token"');
44
+ console.error('----------------------------------------\n');
45
+ process.exit(1);
46
+ }
47
+ // Validate token format
48
+ const token = process.env.SLACK_BOT_TOKEN;
49
+ if (!token.startsWith('xoxb-')) {
50
+ logWarning('validateEnvironment', 'SLACK_BOT_TOKEN should start with "xoxb-"');
51
+ }
52
+ // Log active tool groups if configured
53
+ if (process.env.ENABLED_TOOLGROUPS) {
54
+ logWarning('config', `Tool groups filter active: ${process.env.ENABLED_TOOLGROUPS}`);
55
+ }
56
+ }
57
+ // =============================================================================
58
+ // MAIN ENTRY POINT
59
+ // =============================================================================
60
+ async function main() {
61
+ // Step 1: Validate environment variables
62
+ validateEnvironment();
63
+ // Step 2: Create server using factory
64
+ const { server, registerHandlers } = createMCPServer();
65
+ // Step 3: Register all handlers (tools)
66
+ await registerHandlers(server);
67
+ // Step 4: Start server with stdio transport
68
+ const transport = new StdioServerTransport();
69
+ await server.connect(transport);
70
+ logServerStart('Slack');
71
+ }
72
+ // Run the server
73
+ main().catch((error) => {
74
+ logError('main', error);
75
+ process.exit(1);
76
+ });
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "slack-workspace-mcp-server",
3
+ "version": "0.0.1",
4
+ "description": "MCP server for Slack workspace integration",
5
+ "main": "build/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "slack-workspace-mcp-server": "./build/index.js"
9
+ },
10
+ "files": [
11
+ "build/**/*.js",
12
+ "build/**/*.d.ts",
13
+ "shared/**/*.js",
14
+ "shared/**/*.d.ts",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc && npm run build:integration",
19
+ "build:integration": "tsc -p tsconfig.integration.json",
20
+ "start": "node build/index.js",
21
+ "dev": "tsx src/index.ts",
22
+ "predev": "cd ../shared && npm run build && cd ../local && node setup-dev.js",
23
+ "prebuild": "cd ../shared && npm run build && cd ../local && node setup-dev.js",
24
+ "prepublishOnly": "node prepare-publish.js && node ../scripts/prepare-npm-readme.js",
25
+ "lint": "eslint . --ext .ts,.tsx",
26
+ "lint:fix": "eslint . --ext .ts,.tsx --fix",
27
+ "format": "prettier --write .",
28
+ "format:check": "prettier --check .",
29
+ "stage-publish": "npm version"
30
+ },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.19.1",
33
+ "zod": "^3.24.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^22.10.6",
37
+ "tsx": "^4.19.4",
38
+ "typescript": "^5.7.3"
39
+ },
40
+ "keywords": [
41
+ "mcp",
42
+ "slack",
43
+ "model-context-protocol"
44
+ ],
45
+ "author": "PulseMCP",
46
+ "license": "MIT"
47
+ }
@@ -0,0 +1,4 @@
1
+ export { createMCPServer, SlackClient, type ISlackClient, type ClientFactory } from './server.js';
2
+ export { createRegisterTools, registerTools } from './tools.js';
3
+ export { logServerStart, logError, logWarning, logDebug } from './logging.js';
4
+ export type { Channel, Message, Reaction, Attachment, Block, ThreadWithReplies, User, PaginatedResponse, } from './types.js';
@@ -0,0 +1,7 @@
1
+ // Main exports for Slack MCP Server
2
+ // Server and client
3
+ export { createMCPServer, SlackClient } from './server.js';
4
+ // Tools
5
+ export { createRegisterTools, registerTools } from './tools.js';
6
+ // Logging utilities
7
+ export { logServerStart, logError, logWarning, logDebug } from './logging.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Centralized logging utilities for Slack MCP Server
3
+ *
4
+ * IMPORTANT: All logging uses console.error() to write to stderr.
5
+ * The MCP protocol requires stdout to contain only JSON messages.
6
+ */
7
+ export declare function logServerStart(serverName: string, transport?: string): void;
8
+ export declare function logError(context: string, error: unknown): void;
9
+ export declare function logWarning(context: string, message: string): void;
10
+ export declare function logDebug(context: string, message: string): void;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Centralized logging utilities for Slack MCP Server
3
+ *
4
+ * IMPORTANT: All logging uses console.error() to write to stderr.
5
+ * The MCP protocol requires stdout to contain only JSON messages.
6
+ */
7
+ export function logServerStart(serverName, transport = 'stdio') {
8
+ console.error(`MCP server ${serverName} running on ${transport}`);
9
+ }
10
+ export function logError(context, error) {
11
+ const message = error instanceof Error ? error.message : String(error);
12
+ console.error(`[ERROR] ${context}: ${message}`);
13
+ }
14
+ export function logWarning(context, message) {
15
+ console.error(`[WARN] ${context}: ${message}`);
16
+ }
17
+ export function logDebug(context, message) {
18
+ if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
19
+ console.error(`[DEBUG] ${context}: ${message}`);
20
+ }
21
+ }
@@ -0,0 +1,134 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import type { Channel, Message } from './types.js';
3
+ /**
4
+ * Slack API client interface
5
+ * Defines all methods for interacting with the Slack Web API
6
+ */
7
+ export interface ISlackClient {
8
+ /**
9
+ * Get all channels the bot has access to
10
+ */
11
+ getChannels(options?: {
12
+ types?: string;
13
+ excludeArchived?: boolean;
14
+ }): Promise<Channel[]>;
15
+ /**
16
+ * Get information about a specific channel
17
+ */
18
+ getChannel(channelId: string): Promise<Channel>;
19
+ /**
20
+ * Get message history from a channel
21
+ */
22
+ getMessages(channelId: string, options?: {
23
+ limit?: number;
24
+ cursor?: string;
25
+ oldest?: string;
26
+ latest?: string;
27
+ }): Promise<{
28
+ messages: Message[];
29
+ hasMore: boolean;
30
+ nextCursor?: string;
31
+ }>;
32
+ /**
33
+ * Get all replies in a thread
34
+ */
35
+ getThread(channelId: string, threadTs: string, options?: {
36
+ limit?: number;
37
+ cursor?: string;
38
+ }): Promise<{
39
+ messages: Message[];
40
+ hasMore: boolean;
41
+ nextCursor?: string;
42
+ }>;
43
+ /**
44
+ * Post a new message to a channel
45
+ */
46
+ postMessage(channelId: string, text: string, options?: {
47
+ threadTs?: string;
48
+ replyBroadcast?: boolean;
49
+ }): Promise<Message>;
50
+ /**
51
+ * Update an existing message
52
+ */
53
+ updateMessage(channelId: string, ts: string, text: string): Promise<Message>;
54
+ /**
55
+ * Add a reaction to a message
56
+ */
57
+ addReaction(channelId: string, timestamp: string, emoji: string): Promise<void>;
58
+ }
59
+ /**
60
+ * Slack API client implementation
61
+ */
62
+ export declare class SlackClient implements ISlackClient {
63
+ private botToken;
64
+ private baseUrl;
65
+ private headers;
66
+ constructor(botToken: string);
67
+ getChannels(options?: {
68
+ types?: string;
69
+ excludeArchived?: boolean;
70
+ }): Promise<Channel[]>;
71
+ getChannel(channelId: string): Promise<Channel>;
72
+ getMessages(channelId: string, options?: {
73
+ limit?: number;
74
+ cursor?: string;
75
+ oldest?: string;
76
+ latest?: string;
77
+ }): Promise<{
78
+ messages: Message[];
79
+ hasMore: boolean;
80
+ nextCursor?: string;
81
+ }>;
82
+ getThread(channelId: string, threadTs: string, options?: {
83
+ limit?: number;
84
+ cursor?: string;
85
+ }): Promise<{
86
+ messages: Message[];
87
+ hasMore: boolean;
88
+ nextCursor?: string;
89
+ }>;
90
+ postMessage(channelId: string, text: string, options?: {
91
+ threadTs?: string;
92
+ replyBroadcast?: boolean;
93
+ }): Promise<Message>;
94
+ updateMessage(channelId: string, ts: string, text: string): Promise<Message>;
95
+ addReaction(channelId: string, timestamp: string, emoji: string): Promise<void>;
96
+ }
97
+ export type ClientFactory = () => ISlackClient;
98
+ export declare function createMCPServer(): {
99
+ server: Server<{
100
+ method: string;
101
+ params?: {
102
+ [x: string]: unknown;
103
+ _meta?: {
104
+ [x: string]: unknown;
105
+ progressToken?: string | number | undefined;
106
+ "io.modelcontextprotocol/related-task"?: {
107
+ taskId: string;
108
+ } | undefined;
109
+ } | undefined;
110
+ } | undefined;
111
+ }, {
112
+ method: string;
113
+ params?: {
114
+ [x: string]: unknown;
115
+ _meta?: {
116
+ [x: string]: unknown;
117
+ progressToken?: string | number | undefined;
118
+ "io.modelcontextprotocol/related-task"?: {
119
+ taskId: string;
120
+ } | undefined;
121
+ } | undefined;
122
+ } | undefined;
123
+ }, {
124
+ [x: string]: unknown;
125
+ _meta?: {
126
+ [x: string]: unknown;
127
+ progressToken?: string | number | undefined;
128
+ "io.modelcontextprotocol/related-task"?: {
129
+ taskId: string;
130
+ } | undefined;
131
+ } | undefined;
132
+ }>;
133
+ registerHandlers: (server: Server, clientFactory?: ClientFactory) => Promise<void>;
134
+ };
@@ -0,0 +1,68 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+ import { createRegisterTools } from './tools.js';
3
+ /**
4
+ * Slack API client implementation
5
+ */
6
+ export class SlackClient {
7
+ botToken;
8
+ baseUrl = 'https://slack.com/api';
9
+ headers;
10
+ constructor(botToken) {
11
+ this.botToken = botToken;
12
+ this.headers = {
13
+ Authorization: `Bearer ${botToken}`,
14
+ };
15
+ }
16
+ async getChannels(options) {
17
+ const { getChannels } = await import('./slack-client/lib/get-channels.js');
18
+ return getChannels(this.baseUrl, this.headers, options);
19
+ }
20
+ async getChannel(channelId) {
21
+ const { getChannel } = await import('./slack-client/lib/get-channel.js');
22
+ return getChannel(this.baseUrl, this.headers, channelId);
23
+ }
24
+ async getMessages(channelId, options) {
25
+ const { getMessages } = await import('./slack-client/lib/get-messages.js');
26
+ return getMessages(this.baseUrl, this.headers, channelId, options);
27
+ }
28
+ async getThread(channelId, threadTs, options) {
29
+ const { getThread } = await import('./slack-client/lib/get-thread.js');
30
+ return getThread(this.baseUrl, this.headers, channelId, threadTs, options);
31
+ }
32
+ async postMessage(channelId, text, options) {
33
+ const { postMessage } = await import('./slack-client/lib/post-message.js');
34
+ return postMessage(this.baseUrl, this.headers, channelId, text, options);
35
+ }
36
+ async updateMessage(channelId, ts, text) {
37
+ const { updateMessage } = await import('./slack-client/lib/update-message.js');
38
+ return updateMessage(this.baseUrl, this.headers, channelId, ts, text);
39
+ }
40
+ async addReaction(channelId, timestamp, emoji) {
41
+ const { addReaction } = await import('./slack-client/lib/add-reaction.js');
42
+ return addReaction(this.baseUrl, this.headers, channelId, timestamp, emoji);
43
+ }
44
+ }
45
+ export function createMCPServer() {
46
+ const server = new Server({
47
+ name: 'slack-workspace-mcp-server',
48
+ version: '0.0.1',
49
+ }, {
50
+ capabilities: {
51
+ tools: {},
52
+ },
53
+ });
54
+ const registerHandlers = async (server, clientFactory) => {
55
+ // Use provided factory or create default client
56
+ const factory = clientFactory ||
57
+ (() => {
58
+ const botToken = process.env.SLACK_BOT_TOKEN;
59
+ if (!botToken) {
60
+ throw new Error('SLACK_BOT_TOKEN environment variable must be configured');
61
+ }
62
+ return new SlackClient(botToken);
63
+ });
64
+ const registerTools = createRegisterTools(factory);
65
+ registerTools(server);
66
+ };
67
+ return { server, registerHandlers };
68
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Adds a reaction (emoji) to a message
3
+ * @param name - The emoji name without colons (e.g., "thumbsup" not ":thumbsup:")
4
+ */
5
+ export declare function addReaction(baseUrl: string, headers: Record<string, string>, channelId: string, timestamp: string, name: string): Promise<void>;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Adds a reaction (emoji) to a message
3
+ * @param name - The emoji name without colons (e.g., "thumbsup" not ":thumbsup:")
4
+ */
5
+ export async function addReaction(baseUrl, headers, channelId, timestamp, name) {
6
+ const body = {
7
+ channel: channelId,
8
+ timestamp,
9
+ name,
10
+ };
11
+ const response = await fetch(`${baseUrl}/reactions.add`, {
12
+ method: 'POST',
13
+ headers: {
14
+ ...headers,
15
+ 'Content-Type': 'application/json; charset=utf-8',
16
+ },
17
+ body: JSON.stringify(body),
18
+ });
19
+ if (!response.ok) {
20
+ throw new Error(`Failed to add reaction: ${response.status} ${response.statusText}`);
21
+ }
22
+ const data = (await response.json());
23
+ if (!data.ok) {
24
+ // already_reacted is not really an error for our purposes
25
+ if (data.error === 'already_reacted') {
26
+ return;
27
+ }
28
+ throw new Error(`Slack API error: ${data.error}`);
29
+ }
30
+ }
@@ -0,0 +1,5 @@
1
+ import type { Channel } from '../../types.js';
2
+ /**
3
+ * Fetches information about a specific channel
4
+ */
5
+ export declare function getChannel(baseUrl: string, headers: Record<string, string>, channelId: string): Promise<Channel>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Fetches information about a specific channel
3
+ */
4
+ export async function getChannel(baseUrl, headers, channelId) {
5
+ const params = new URLSearchParams({
6
+ channel: channelId,
7
+ include_num_members: 'true',
8
+ });
9
+ const response = await fetch(`${baseUrl}/conversations.info?${params}`, {
10
+ method: 'GET',
11
+ headers,
12
+ });
13
+ if (!response.ok) {
14
+ throw new Error(`Failed to fetch channel: ${response.status} ${response.statusText}`);
15
+ }
16
+ const data = (await response.json());
17
+ if (!data.ok) {
18
+ throw new Error(`Slack API error: ${data.error}`);
19
+ }
20
+ if (!data.channel) {
21
+ throw new Error('Channel not found in response');
22
+ }
23
+ return data.channel;
24
+ }
@@ -0,0 +1,10 @@
1
+ import type { Channel } from '../../types.js';
2
+ /**
3
+ * Fetches all channels the bot has access to
4
+ * Uses cursor-based pagination to get all results
5
+ */
6
+ export declare function getChannels(baseUrl: string, headers: Record<string, string>, options?: {
7
+ types?: string;
8
+ excludeArchived?: boolean;
9
+ limit?: number;
10
+ }): Promise<Channel[]>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Fetches all channels the bot has access to
3
+ * Uses cursor-based pagination to get all results
4
+ */
5
+ export async function getChannels(baseUrl, headers, options) {
6
+ const allChannels = [];
7
+ let cursor;
8
+ const types = options?.types ?? 'public_channel,private_channel';
9
+ const excludeArchived = options?.excludeArchived ?? true;
10
+ const limit = options?.limit ?? 200;
11
+ do {
12
+ const params = new URLSearchParams({
13
+ types,
14
+ exclude_archived: excludeArchived.toString(),
15
+ limit: limit.toString(),
16
+ });
17
+ if (cursor) {
18
+ params.set('cursor', cursor);
19
+ }
20
+ const response = await fetch(`${baseUrl}/conversations.list?${params}`, {
21
+ method: 'GET',
22
+ headers,
23
+ });
24
+ if (!response.ok) {
25
+ throw new Error(`Failed to fetch channels: ${response.status} ${response.statusText}`);
26
+ }
27
+ const data = (await response.json());
28
+ if (!data.ok) {
29
+ throw new Error(`Slack API error: ${data.error}`);
30
+ }
31
+ if (data.channels) {
32
+ allChannels.push(...data.channels);
33
+ }
34
+ cursor = data.response_metadata?.next_cursor;
35
+ } while (cursor);
36
+ return allChannels;
37
+ }
@@ -0,0 +1,16 @@
1
+ import type { Message } from '../../types.js';
2
+ /**
3
+ * Fetches message history from a channel
4
+ * Returns messages in reverse chronological order (newest first)
5
+ */
6
+ export declare function getMessages(baseUrl: string, headers: Record<string, string>, channelId: string, options?: {
7
+ limit?: number;
8
+ cursor?: string;
9
+ oldest?: string;
10
+ latest?: string;
11
+ inclusive?: boolean;
12
+ }): Promise<{
13
+ messages: Message[];
14
+ hasMore: boolean;
15
+ nextCursor?: string;
16
+ }>;