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 +26 -24
- package/build/index.integration-with-mock.js +9 -1
- package/build/index.js +9 -1
- package/package.json +1 -1
- package/shared/index.d.ts +1 -1
- package/shared/index.js +1 -1
- package/shared/server.d.ts +4 -1
- package/shared/server.js +2 -2
- package/shared/tools/format-message-extras.d.ts +6 -0
- package/shared/tools/format-message-extras.js +71 -0
- package/shared/tools/get-channel.js +2 -0
- package/shared/tools/get-thread.js +3 -0
- package/shared/types.d.ts +22 -0
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
|
-
##
|
|
15
|
+
## Setup
|
|
16
16
|
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
###
|
|
22
|
+
### Environment Variables
|
|
36
23
|
|
|
37
|
-
| Variable | Description | Default |
|
|
38
|
-
| -------------------- | ----------------------------------- | ----------- |
|
|
39
|
-
| `
|
|
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
|
-
###
|
|
44
|
+
### Claude Desktop
|
|
57
45
|
|
|
58
|
-
|
|
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
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
|
package/shared/server.d.ts
CHANGED
|
@@ -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
|
|
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:
|
|
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;
|