slack-workspace-mcp-server 0.0.1 → 0.0.2

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.
package/README.md CHANGED
@@ -12,31 +12,19 @@ A Model Context Protocol (MCP) server for integrating with Slack workspaces. Thi
12
12
  - **Update Messages** - Edit previously posted messages
13
13
  - **React to Messages** - Add emoji reactions to messages
14
14
 
15
- ## Installation
15
+ ## Setup
16
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
17
+ ### Prerequisites
30
18
 
31
- | Variable | Description | Example |
32
- | ----------------- | -------------------------- | -------------- |
33
- | `SLACK_BOT_TOKEN` | Slack Bot User OAuth Token | `xoxb-1234...` |
19
+ - A Slack workspace with admin permissions to create apps
20
+ - A Slack Bot Token (see instructions below)
34
21
 
35
- ### Optional Environment Variables
22
+ ### Environment Variables
36
23
 
37
- | Variable | Description | Default |
38
- | -------------------- | ----------------------------------- | ----------- |
39
- | `ENABLED_TOOLGROUPS` | Comma-separated list of tool groups | All enabled |
24
+ | Variable | Required | Description | Default |
25
+ | -------------------- | -------- | ----------------------------------- | ----------- |
26
+ | `SLACK_BOT_TOKEN` | Yes | Slack Bot User OAuth Token | - |
27
+ | `ENABLED_TOOLGROUPS` | No | Comma-separated list of tool groups | All enabled |
40
28
 
41
29
  ### Getting a Bot Token
42
30
 
@@ -53,16 +41,28 @@ npx slack-workspace-mcp-server
53
41
  5. Install the app to your workspace
54
42
  6. Copy the **Bot User OAuth Token** (starts with `xoxb-`)
55
43
 
56
- ### MCP Client Configuration
44
+ ### Claude Desktop
57
45
 
58
- For Claude Desktop, add to your configuration:
46
+ Make sure you have your Slack Bot Token ready.
47
+
48
+ Then proceed to the setup instructions below. If this is your first time using MCP Servers, you'll want to make sure you have the [Claude Desktop application](https://claude.ai/download) and follow the [official MCP setup instructions](https://modelcontextprotocol.io/quickstart/user).
49
+
50
+ #### Manual Setup
51
+
52
+ You're going to need Node working on your machine so you can run `npx` commands in your terminal. If you don't have Node, you can install it from [nodejs.org](https://nodejs.org/en/download).
53
+
54
+ macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
55
+
56
+ Windows: `%APPDATA%\Claude\claude_desktop_config.json`
57
+
58
+ Modify your `claude_desktop_config.json` file to add the following:
59
59
 
60
60
  ```json
61
61
  {
62
62
  "mcpServers": {
63
63
  "slack": {
64
64
  "command": "npx",
65
- "args": ["slack-workspace-mcp-server"],
65
+ "args": ["-y", "slack-workspace-mcp-server"],
66
66
  "env": {
67
67
  "SLACK_BOT_TOKEN": "xoxb-your-token-here"
68
68
  }
@@ -71,6 +71,8 @@ For Claude Desktop, add to your configuration:
71
71
  }
72
72
  ```
73
73
 
74
+ Restart Claude Desktop and you should be ready to go!
75
+
74
76
  ## Available Tools
75
77
 
76
78
  ### Read-Only Tools
@@ -8,8 +8,16 @@
8
8
  * Mock data is passed via the SLACK_MOCK_DATA environment variable.
9
9
  */
10
10
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
11
+ import { readFileSync } from 'fs';
12
+ import { dirname, join } from 'path';
13
+ import { fileURLToPath } from 'url';
11
14
  import { createMCPServer } from '../shared/index.js';
12
15
  import { createIntegrationMockSlackClient } from '../shared/slack-client/slack-client.integration-mock.js';
16
+ // Read version from package.json
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ const packageJsonPath = join(__dirname, '..', 'package.json');
19
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
20
+ const VERSION = packageJson.version;
13
21
  async function main() {
14
22
  const transport = new StdioServerTransport();
15
23
  // Parse mock data from environment variable
@@ -24,7 +32,7 @@ async function main() {
24
32
  }
25
33
  // Create client factory that returns our mock
26
34
  const clientFactory = () => createIntegrationMockSlackClient(mockData);
27
- const { server, registerHandlers } = createMCPServer();
35
+ const { server, registerHandlers } = createMCPServer({ version: VERSION });
28
36
  await registerHandlers(server, clientFactory);
29
37
  await server.connect(transport);
30
38
  }
package/build/index.js CHANGED
@@ -1,7 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { readFileSync } from 'fs';
4
+ import { dirname, join } from 'path';
5
+ import { fileURLToPath } from 'url';
3
6
  import { createMCPServer } from '../shared/index.js';
4
7
  import { logServerStart, logError, logWarning } from '../shared/logging.js';
8
+ // Read version from package.json
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const packageJsonPath = join(__dirname, '..', 'package.json');
11
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
12
+ const VERSION = packageJson.version;
5
13
  // =============================================================================
6
14
  // ENVIRONMENT VALIDATION
7
15
  // =============================================================================
@@ -61,7 +69,7 @@ async function main() {
61
69
  // Step 1: Validate environment variables
62
70
  validateEnvironment();
63
71
  // Step 2: Create server using factory
64
- const { server, registerHandlers } = createMCPServer();
72
+ const { server, registerHandlers } = createMCPServer({ version: VERSION });
65
73
  // Step 3: Register all handlers (tools)
66
74
  await registerHandlers(server);
67
75
  // Step 4: Start server with stdio transport
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slack-workspace-mcp-server",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "MCP server for Slack workspace integration",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
package/shared/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { createMCPServer, SlackClient, type ISlackClient, type ClientFactory } from './server.js';
1
+ export { createMCPServer, SlackClient, type ISlackClient, type ClientFactory, type CreateMCPServerOptions, } from './server.js';
2
2
  export { createRegisterTools, registerTools } from './tools.js';
3
3
  export { logServerStart, logError, logWarning, logDebug } from './logging.js';
4
4
  export type { Channel, Message, Reaction, Attachment, Block, ThreadWithReplies, User, PaginatedResponse, } from './types.js';
package/shared/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Main exports for Slack MCP Server
2
2
  // Server and client
3
- export { createMCPServer, SlackClient } from './server.js';
3
+ export { createMCPServer, SlackClient, } from './server.js';
4
4
  // Tools
5
5
  export { createRegisterTools, registerTools } from './tools.js';
6
6
  // Logging utilities
@@ -95,7 +95,10 @@ export declare class SlackClient implements ISlackClient {
95
95
  addReaction(channelId: string, timestamp: string, emoji: string): Promise<void>;
96
96
  }
97
97
  export type ClientFactory = () => ISlackClient;
98
- export declare function createMCPServer(): {
98
+ export interface CreateMCPServerOptions {
99
+ version: string;
100
+ }
101
+ export declare function createMCPServer(options: CreateMCPServerOptions): {
99
102
  server: Server<{
100
103
  method: string;
101
104
  params?: {
package/shared/server.js CHANGED
@@ -42,10 +42,10 @@ export class SlackClient {
42
42
  return addReaction(this.baseUrl, this.headers, channelId, timestamp, emoji);
43
43
  }
44
44
  }
45
- export function createMCPServer() {
45
+ export function createMCPServer(options) {
46
46
  const server = new Server({
47
47
  name: 'slack-workspace-mcp-server',
48
- version: '0.0.1',
48
+ version: options.version,
49
49
  }, {
50
50
  capabilities: {
51
51
  tools: {},
@@ -0,0 +1,6 @@
1
+ import type { Message } from '../types.js';
2
+ /**
3
+ * Formats attachment and file information from a Slack message into markdown text.
4
+ * Returns an empty string if the message has no attachments or files.
5
+ */
6
+ export declare function formatMessageExtras(msg: Message): string;
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Formats attachment and file information from a Slack message into markdown text.
3
+ * Returns an empty string if the message has no attachments or files.
4
+ */
5
+ export function formatMessageExtras(msg) {
6
+ let output = '';
7
+ if (msg.attachments && msg.attachments.length > 0) {
8
+ for (const att of msg.attachments) {
9
+ const parts = [];
10
+ if (att.pretext) {
11
+ parts.push(att.pretext);
12
+ }
13
+ // Title with optional link
14
+ if (att.title) {
15
+ parts.push(att.title_link ? `[${att.title}](${att.title_link})` : att.title);
16
+ }
17
+ // Service/source info for unfurled links
18
+ if (att.service_name) {
19
+ parts.push(`(${att.service_name})`);
20
+ }
21
+ if (att.author_name) {
22
+ parts.push(`By: ${att.author_name}`);
23
+ }
24
+ if (att.text) {
25
+ parts.push(att.text);
26
+ }
27
+ // Image URL (unfurled preview or inline image)
28
+ if (att.image_url) {
29
+ parts.push(`Image: ${att.image_url}`);
30
+ }
31
+ else if (att.thumb_url) {
32
+ parts.push(`Thumbnail: ${att.thumb_url}`);
33
+ }
34
+ // Original URL for unfurled links
35
+ if (att.from_url) {
36
+ parts.push(`URL: ${att.from_url}`);
37
+ }
38
+ if (parts.length > 0) {
39
+ output += ` 📎 Attachment: ${parts.join(' | ')}\n`;
40
+ }
41
+ }
42
+ }
43
+ if (msg.files && msg.files.length > 0) {
44
+ for (const file of msg.files) {
45
+ const parts = [];
46
+ const name = file.name || file.title || 'Untitled file';
47
+ parts.push(name);
48
+ if (file.mimetype) {
49
+ parts.push(`(${file.mimetype})`);
50
+ }
51
+ if (file.size) {
52
+ parts.push(formatFileSize(file.size));
53
+ }
54
+ if (file.permalink) {
55
+ parts.push(`Link: ${file.permalink}`);
56
+ }
57
+ else if (file.url_private) {
58
+ parts.push(`Link: ${file.url_private}`);
59
+ }
60
+ output += ` 📄 File: ${parts.join(' | ')}\n`;
61
+ }
62
+ }
63
+ return output;
64
+ }
65
+ function formatFileSize(bytes) {
66
+ if (bytes < 1024)
67
+ return `${bytes} B`;
68
+ if (bytes < 1024 * 1024)
69
+ return `${(bytes / 1024).toFixed(1)} KB`;
70
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
71
+ }
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { formatMessageExtras } from './format-message-extras.js';
2
3
  const PARAM_DESCRIPTIONS = {
3
4
  channel_id: 'The unique identifier of the channel (e.g., "C1234567890"). ' +
4
5
  'Get channel IDs using the slack_get_channels tool.',
@@ -98,6 +99,7 @@ export function getChannelTool(server, clientFactory) {
98
99
  : '';
99
100
  output += `**${sender}** (${time})${threadInfo}${reactions}\n`;
100
101
  output += `${msg.text}\n`;
102
+ output += formatMessageExtras(msg);
101
103
  if (msg.thread_ts && msg.thread_ts !== msg.ts) {
102
104
  output += `↳ Reply to thread: ${msg.thread_ts}\n`;
103
105
  }
@@ -1,4 +1,5 @@
1
1
  import { z } from 'zod';
2
+ import { formatMessageExtras } from './format-message-extras.js';
2
3
  const PARAM_DESCRIPTIONS = {
3
4
  channel_id: 'The channel ID where the thread exists (e.g., "C1234567890"). ' +
4
5
  'Get channel IDs using the slack_get_channels tool.',
@@ -84,6 +85,7 @@ export function getThreadTool(server, clientFactory) {
84
85
  output += `## Parent Message\n`;
85
86
  output += `**${parentSender}** (${parentTime})\n`;
86
87
  output += `${parent.text}${parentReactions}\n`;
88
+ output += formatMessageExtras(parent);
87
89
  output += `ts: ${parent.ts}\n\n`;
88
90
  // Format replies
89
91
  if (replies.length > 0) {
@@ -96,6 +98,7 @@ export function getThreadTool(server, clientFactory) {
96
98
  : '';
97
99
  output += `**${sender}** (${time})\n`;
98
100
  output += `${reply.text}${reactions}\n`;
101
+ output += formatMessageExtras(reply);
99
102
  output += `ts: ${reply.ts}\n\n`;
100
103
  }
101
104
  if (hasMore) {
package/shared/types.d.ts CHANGED
@@ -41,6 +41,7 @@ export interface Message {
41
41
  reply_users?: string[];
42
42
  reactions?: Reaction[];
43
43
  attachments?: Attachment[];
44
+ files?: SlackFile[];
44
45
  blocks?: Block[];
45
46
  edited?: {
46
47
  user: string;
@@ -73,6 +74,27 @@ export interface Attachment {
73
74
  footer?: string;
74
75
  footer_icon?: string;
75
76
  ts?: number;
77
+ from_url?: string;
78
+ original_url?: string;
79
+ service_name?: string;
80
+ service_icon?: string;
81
+ }
82
+ export interface SlackFile {
83
+ id: string;
84
+ name?: string;
85
+ title?: string;
86
+ mimetype?: string;
87
+ filetype?: string;
88
+ size?: number;
89
+ url_private?: string;
90
+ url_private_download?: string;
91
+ permalink?: string;
92
+ permalink_public?: string;
93
+ thumb_360?: string;
94
+ thumb_480?: string;
95
+ thumb_720?: string;
96
+ thumb_800?: string;
97
+ thumb_1024?: string;
76
98
  }
77
99
  export interface Block {
78
100
  type: string;