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.
- package/README.md +122 -0
- package/build/index.d.ts +2 -0
- package/build/index.integration-with-mock.d.ts +2 -0
- package/build/index.integration-with-mock.js +34 -0
- package/build/index.js +76 -0
- package/package.json +47 -0
- package/shared/index.d.ts +4 -0
- package/shared/index.js +7 -0
- package/shared/logging.d.ts +10 -0
- package/shared/logging.js +21 -0
- package/shared/server.d.ts +134 -0
- package/shared/server.js +68 -0
- package/shared/slack-client/lib/add-reaction.d.ts +5 -0
- package/shared/slack-client/lib/add-reaction.js +30 -0
- package/shared/slack-client/lib/get-channel.d.ts +5 -0
- package/shared/slack-client/lib/get-channel.js +24 -0
- package/shared/slack-client/lib/get-channels.d.ts +10 -0
- package/shared/slack-client/lib/get-channels.js +37 -0
- package/shared/slack-client/lib/get-messages.d.ts +16 -0
- package/shared/slack-client/lib/get-messages.js +38 -0
- package/shared/slack-client/lib/get-thread.d.ts +16 -0
- package/shared/slack-client/lib/get-thread.js +39 -0
- package/shared/slack-client/lib/post-message.d.ts +10 -0
- package/shared/slack-client/lib/post-message.js +44 -0
- package/shared/slack-client/lib/update-message.d.ts +5 -0
- package/shared/slack-client/lib/update-message.js +32 -0
- package/shared/slack-client/slack-client.integration-mock.d.ts +16 -0
- package/shared/slack-client/slack-client.integration-mock.js +106 -0
- package/shared/tools/get-channel.d.ts +55 -0
- package/shared/tools/get-channel.js +136 -0
- package/shared/tools/get-channels.d.ts +46 -0
- package/shared/tools/get-channels.js +107 -0
- package/shared/tools/get-thread.d.ts +54 -0
- package/shared/tools/get-thread.js +130 -0
- package/shared/tools/post-message.d.ts +44 -0
- package/shared/tools/post-message.js +81 -0
- package/shared/tools/react-to-message.d.ts +51 -0
- package/shared/tools/react-to-message.js +90 -0
- package/shared/tools/reply-to-thread.d.ts +59 -0
- package/shared/tools/reply-to-thread.js +97 -0
- package/shared/tools/update-message.d.ts +51 -0
- package/shared/tools/update-message.js +87 -0
- package/shared/tools.d.ts +18 -0
- package/shared/tools.js +78 -0
- package/shared/types.d.ts +112 -0
- package/shared/types.js +5 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
channel_id: 'The channel ID where the message exists (e.g., "C1234567890"). ' +
|
|
4
|
+
'Get channel IDs using the slack_get_channels tool.',
|
|
5
|
+
message_ts: 'The timestamp of the message to update (e.g., "1234567890.123456"). ' +
|
|
6
|
+
'This is shown as "ts" in message outputs.',
|
|
7
|
+
text: 'The new content for the message. Completely replaces the existing text. ' +
|
|
8
|
+
'Supports Slack markdown: *bold*, _italic_, `code`, ```code blocks```.',
|
|
9
|
+
};
|
|
10
|
+
export const UpdateMessageSchema = z.object({
|
|
11
|
+
channel_id: z.string().min(1).describe(PARAM_DESCRIPTIONS.channel_id),
|
|
12
|
+
message_ts: z.string().min(1).describe(PARAM_DESCRIPTIONS.message_ts),
|
|
13
|
+
text: z.string().min(1).describe(PARAM_DESCRIPTIONS.text),
|
|
14
|
+
});
|
|
15
|
+
const TOOL_DESCRIPTION = `Update an existing message in Slack.
|
|
16
|
+
|
|
17
|
+
Modifies the content of a message that was previously posted. The message will show an "(edited)" indicator after being updated.
|
|
18
|
+
|
|
19
|
+
**Returns:**
|
|
20
|
+
- Confirmation of the update
|
|
21
|
+
- The updated message content
|
|
22
|
+
|
|
23
|
+
**Use cases:**
|
|
24
|
+
- Fix typos or errors in a message
|
|
25
|
+
- Update information that has changed
|
|
26
|
+
- Add clarifications to a previous message
|
|
27
|
+
- Mark a task or question as resolved by editing the message
|
|
28
|
+
|
|
29
|
+
**Note:**
|
|
30
|
+
- You can only edit messages that the bot posted
|
|
31
|
+
- The edit history is not preserved - only the final content is shown
|
|
32
|
+
- Consider adding reactions instead if you just want to acknowledge a message`;
|
|
33
|
+
export function updateMessageTool(server, clientFactory) {
|
|
34
|
+
return {
|
|
35
|
+
name: 'slack_update_message',
|
|
36
|
+
description: TOOL_DESCRIPTION,
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
channel_id: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
description: PARAM_DESCRIPTIONS.channel_id,
|
|
43
|
+
},
|
|
44
|
+
message_ts: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: PARAM_DESCRIPTIONS.message_ts,
|
|
47
|
+
},
|
|
48
|
+
text: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: PARAM_DESCRIPTIONS.text,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
required: ['channel_id', 'message_ts', 'text'],
|
|
54
|
+
},
|
|
55
|
+
handler: async (args) => {
|
|
56
|
+
try {
|
|
57
|
+
const parsed = UpdateMessageSchema.parse(args);
|
|
58
|
+
const client = clientFactory();
|
|
59
|
+
const message = await client.updateMessage(parsed.channel_id, parsed.message_ts, parsed.text);
|
|
60
|
+
const time = new Date(parseFloat(message.ts) * 1000).toISOString();
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: 'text',
|
|
65
|
+
text: `Message updated successfully!\n\n` +
|
|
66
|
+
`Channel: ${parsed.channel_id}\n` +
|
|
67
|
+
`Timestamp: ${message.ts}\n` +
|
|
68
|
+
`Updated at: ${time}\n\n` +
|
|
69
|
+
`New content:\n${parsed.text}`,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: 'text',
|
|
79
|
+
text: `Error updating message: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
isError: true,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { ClientFactory } from './server.js';
|
|
3
|
+
/**
|
|
4
|
+
* Tool groups for permission-based access control
|
|
5
|
+
*/
|
|
6
|
+
export type ToolGroup = 'readonly' | 'write';
|
|
7
|
+
/**
|
|
8
|
+
* Parse enabled tool groups from environment variable
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseEnabledToolGroups(enabledGroupsParam?: string): ToolGroup[];
|
|
11
|
+
/**
|
|
12
|
+
* Creates a function to register all tools with the server
|
|
13
|
+
*/
|
|
14
|
+
export declare function createRegisterTools(clientFactory: ClientFactory, enabledGroups?: ToolGroup[]): (server: Server) => void;
|
|
15
|
+
/**
|
|
16
|
+
* Backward compatibility export
|
|
17
|
+
*/
|
|
18
|
+
export declare function registerTools(server: Server): void;
|
package/shared/tools.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { logWarning } from './logging.js';
|
|
3
|
+
import { getChannelsTool } from './tools/get-channels.js';
|
|
4
|
+
import { getChannelTool } from './tools/get-channel.js';
|
|
5
|
+
import { getThreadTool } from './tools/get-thread.js';
|
|
6
|
+
import { postMessageTool } from './tools/post-message.js';
|
|
7
|
+
import { replyToThreadTool } from './tools/reply-to-thread.js';
|
|
8
|
+
import { updateMessageTool } from './tools/update-message.js';
|
|
9
|
+
import { reactToMessageTool } from './tools/react-to-message.js';
|
|
10
|
+
const ALL_TOOL_GROUPS = ['readonly', 'write'];
|
|
11
|
+
/**
|
|
12
|
+
* Parse enabled tool groups from environment variable
|
|
13
|
+
*/
|
|
14
|
+
export function parseEnabledToolGroups(enabledGroupsParam) {
|
|
15
|
+
if (!enabledGroupsParam) {
|
|
16
|
+
return ALL_TOOL_GROUPS;
|
|
17
|
+
}
|
|
18
|
+
const requestedGroups = enabledGroupsParam.split(',').map((g) => g.trim().toLowerCase());
|
|
19
|
+
const validGroups = requestedGroups.filter((g) => ALL_TOOL_GROUPS.includes(g));
|
|
20
|
+
if (validGroups.length === 0) {
|
|
21
|
+
logWarning('parseEnabledToolGroups', `No valid tool groups found in "${enabledGroupsParam}". Valid groups: ${ALL_TOOL_GROUPS.join(', ')}`);
|
|
22
|
+
return ALL_TOOL_GROUPS;
|
|
23
|
+
}
|
|
24
|
+
return validGroups;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* All available tools with their group assignments
|
|
28
|
+
*/
|
|
29
|
+
const ALL_TOOLS = [
|
|
30
|
+
// Read-only tools
|
|
31
|
+
{ factory: getChannelsTool, groups: ['readonly', 'write'] },
|
|
32
|
+
{ factory: getChannelTool, groups: ['readonly', 'write'] },
|
|
33
|
+
{ factory: getThreadTool, groups: ['readonly', 'write'] },
|
|
34
|
+
// Write tools
|
|
35
|
+
{ factory: postMessageTool, groups: ['write'] },
|
|
36
|
+
{ factory: replyToThreadTool, groups: ['write'] },
|
|
37
|
+
{ factory: updateMessageTool, groups: ['write'] },
|
|
38
|
+
{ factory: reactToMessageTool, groups: ['write'] },
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Creates a function to register all tools with the server
|
|
42
|
+
*/
|
|
43
|
+
export function createRegisterTools(clientFactory, enabledGroups) {
|
|
44
|
+
const groups = enabledGroups || parseEnabledToolGroups(process.env.ENABLED_TOOLGROUPS);
|
|
45
|
+
return (server) => {
|
|
46
|
+
// Filter tools by enabled groups and create instances
|
|
47
|
+
const tools = ALL_TOOLS.filter((def) => def.groups.some((g) => groups.includes(g))).map((def) => def.factory(server, clientFactory));
|
|
48
|
+
// List available tools
|
|
49
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
50
|
+
return {
|
|
51
|
+
tools: tools.map((tool) => ({
|
|
52
|
+
name: tool.name,
|
|
53
|
+
description: tool.description,
|
|
54
|
+
inputSchema: tool.inputSchema,
|
|
55
|
+
})),
|
|
56
|
+
};
|
|
57
|
+
});
|
|
58
|
+
// Handle tool calls
|
|
59
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
60
|
+
const { name, arguments: args } = request.params;
|
|
61
|
+
const tool = tools.find((t) => t.name === name);
|
|
62
|
+
if (!tool) {
|
|
63
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
64
|
+
}
|
|
65
|
+
return await tool.handler(args);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Backward compatibility export
|
|
71
|
+
*/
|
|
72
|
+
export function registerTools(server) {
|
|
73
|
+
const factory = () => {
|
|
74
|
+
throw new Error('No client factory provided - use createRegisterTools for dependency injection');
|
|
75
|
+
};
|
|
76
|
+
const register = createRegisterTools(factory);
|
|
77
|
+
register(server);
|
|
78
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack API Types
|
|
3
|
+
* Based on Slack Web API responses
|
|
4
|
+
*/
|
|
5
|
+
export interface Channel {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
is_channel: boolean;
|
|
9
|
+
is_group: boolean;
|
|
10
|
+
is_im: boolean;
|
|
11
|
+
is_mpim: boolean;
|
|
12
|
+
is_private: boolean;
|
|
13
|
+
is_archived: boolean;
|
|
14
|
+
is_general: boolean;
|
|
15
|
+
is_member: boolean;
|
|
16
|
+
topic?: {
|
|
17
|
+
value: string;
|
|
18
|
+
creator: string;
|
|
19
|
+
last_set: number;
|
|
20
|
+
};
|
|
21
|
+
purpose?: {
|
|
22
|
+
value: string;
|
|
23
|
+
creator: string;
|
|
24
|
+
last_set: number;
|
|
25
|
+
};
|
|
26
|
+
num_members?: number;
|
|
27
|
+
created: number;
|
|
28
|
+
creator?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface Message {
|
|
31
|
+
type: string;
|
|
32
|
+
subtype?: string;
|
|
33
|
+
user?: string;
|
|
34
|
+
bot_id?: string;
|
|
35
|
+
text: string;
|
|
36
|
+
ts: string;
|
|
37
|
+
thread_ts?: string;
|
|
38
|
+
reply_count?: number;
|
|
39
|
+
reply_users_count?: number;
|
|
40
|
+
latest_reply?: string;
|
|
41
|
+
reply_users?: string[];
|
|
42
|
+
reactions?: Reaction[];
|
|
43
|
+
attachments?: Attachment[];
|
|
44
|
+
blocks?: Block[];
|
|
45
|
+
edited?: {
|
|
46
|
+
user: string;
|
|
47
|
+
ts: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface Reaction {
|
|
51
|
+
name: string;
|
|
52
|
+
count: number;
|
|
53
|
+
users: string[];
|
|
54
|
+
}
|
|
55
|
+
export interface Attachment {
|
|
56
|
+
id?: number;
|
|
57
|
+
fallback?: string;
|
|
58
|
+
color?: string;
|
|
59
|
+
pretext?: string;
|
|
60
|
+
author_name?: string;
|
|
61
|
+
author_link?: string;
|
|
62
|
+
author_icon?: string;
|
|
63
|
+
title?: string;
|
|
64
|
+
title_link?: string;
|
|
65
|
+
text?: string;
|
|
66
|
+
fields?: Array<{
|
|
67
|
+
title: string;
|
|
68
|
+
value: string;
|
|
69
|
+
short: boolean;
|
|
70
|
+
}>;
|
|
71
|
+
image_url?: string;
|
|
72
|
+
thumb_url?: string;
|
|
73
|
+
footer?: string;
|
|
74
|
+
footer_icon?: string;
|
|
75
|
+
ts?: number;
|
|
76
|
+
}
|
|
77
|
+
export interface Block {
|
|
78
|
+
type: string;
|
|
79
|
+
block_id?: string;
|
|
80
|
+
text?: {
|
|
81
|
+
type: string;
|
|
82
|
+
text: string;
|
|
83
|
+
emoji?: boolean;
|
|
84
|
+
};
|
|
85
|
+
elements?: unknown[];
|
|
86
|
+
}
|
|
87
|
+
export interface ThreadWithReplies {
|
|
88
|
+
messages: Message[];
|
|
89
|
+
has_more: boolean;
|
|
90
|
+
response_metadata?: {
|
|
91
|
+
next_cursor?: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export interface User {
|
|
95
|
+
id: string;
|
|
96
|
+
name: string;
|
|
97
|
+
real_name?: string;
|
|
98
|
+
display_name?: string;
|
|
99
|
+
is_bot: boolean;
|
|
100
|
+
is_admin?: boolean;
|
|
101
|
+
profile?: {
|
|
102
|
+
display_name?: string;
|
|
103
|
+
real_name?: string;
|
|
104
|
+
image_48?: string;
|
|
105
|
+
image_72?: string;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
export interface PaginatedResponse<T> {
|
|
109
|
+
items: T[];
|
|
110
|
+
has_more: boolean;
|
|
111
|
+
next_cursor?: string;
|
|
112
|
+
}
|