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,54 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const GetThreadSchema: z.ZodObject<{
|
|
5
|
+
channel_id: z.ZodString;
|
|
6
|
+
thread_ts: z.ZodString;
|
|
7
|
+
reply_limit: z.ZodDefault<z.ZodNumber>;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
channel_id: string;
|
|
10
|
+
thread_ts: string;
|
|
11
|
+
reply_limit: number;
|
|
12
|
+
}, {
|
|
13
|
+
channel_id: string;
|
|
14
|
+
thread_ts: string;
|
|
15
|
+
reply_limit?: number | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function getThreadTool(server: Server, clientFactory: ClientFactory): {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object";
|
|
22
|
+
properties: {
|
|
23
|
+
channel_id: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
thread_ts: {
|
|
28
|
+
type: string;
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
reply_limit: {
|
|
32
|
+
type: string;
|
|
33
|
+
default: number;
|
|
34
|
+
minimum: number;
|
|
35
|
+
maximum: number;
|
|
36
|
+
description: string;
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
required: string[];
|
|
40
|
+
};
|
|
41
|
+
handler: (args: unknown) => Promise<{
|
|
42
|
+
content: {
|
|
43
|
+
type: string;
|
|
44
|
+
text: string;
|
|
45
|
+
}[];
|
|
46
|
+
isError?: undefined;
|
|
47
|
+
} | {
|
|
48
|
+
content: {
|
|
49
|
+
type: string;
|
|
50
|
+
text: string;
|
|
51
|
+
}[];
|
|
52
|
+
isError: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
channel_id: 'The channel ID where the thread exists (e.g., "C1234567890"). ' +
|
|
4
|
+
'Get channel IDs using the slack_get_channels tool.',
|
|
5
|
+
thread_ts: 'The timestamp of the parent message that started the thread (e.g., "1234567890.123456"). ' +
|
|
6
|
+
'This is shown as "ts" in message outputs.',
|
|
7
|
+
reply_limit: 'Maximum number of replies to return. Default: 50. Maximum: 200. ' +
|
|
8
|
+
'The parent message is always included.',
|
|
9
|
+
};
|
|
10
|
+
export const GetThreadSchema = z.object({
|
|
11
|
+
channel_id: z.string().min(1).describe(PARAM_DESCRIPTIONS.channel_id),
|
|
12
|
+
thread_ts: z.string().min(1).describe(PARAM_DESCRIPTIONS.thread_ts),
|
|
13
|
+
reply_limit: z.number().min(1).max(200).default(50).describe(PARAM_DESCRIPTIONS.reply_limit),
|
|
14
|
+
});
|
|
15
|
+
const TOOL_DESCRIPTION = `Get a thread conversation with all its replies.
|
|
16
|
+
|
|
17
|
+
Returns the parent message and all replies in a thread. This is equivalent to clicking on "X replies" in Slack to expand a thread.
|
|
18
|
+
|
|
19
|
+
**Returns:**
|
|
20
|
+
- Parent message with full content
|
|
21
|
+
- All reply messages in chronological order
|
|
22
|
+
- Reactions on each message
|
|
23
|
+
- Timestamps for each message
|
|
24
|
+
|
|
25
|
+
**Use cases:**
|
|
26
|
+
- Read an entire threaded conversation
|
|
27
|
+
- Get context before replying to a thread
|
|
28
|
+
- Review discussion history on a topic
|
|
29
|
+
- Find the latest reply timestamp for adding new replies
|
|
30
|
+
|
|
31
|
+
**Note:** The thread_ts parameter is the timestamp of the parent message, not a reply.`;
|
|
32
|
+
export function getThreadTool(server, clientFactory) {
|
|
33
|
+
return {
|
|
34
|
+
name: 'slack_get_thread',
|
|
35
|
+
description: TOOL_DESCRIPTION,
|
|
36
|
+
inputSchema: {
|
|
37
|
+
type: 'object',
|
|
38
|
+
properties: {
|
|
39
|
+
channel_id: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: PARAM_DESCRIPTIONS.channel_id,
|
|
42
|
+
},
|
|
43
|
+
thread_ts: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: PARAM_DESCRIPTIONS.thread_ts,
|
|
46
|
+
},
|
|
47
|
+
reply_limit: {
|
|
48
|
+
type: 'number',
|
|
49
|
+
default: 50,
|
|
50
|
+
minimum: 1,
|
|
51
|
+
maximum: 200,
|
|
52
|
+
description: PARAM_DESCRIPTIONS.reply_limit,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
required: ['channel_id', 'thread_ts'],
|
|
56
|
+
},
|
|
57
|
+
handler: async (args) => {
|
|
58
|
+
try {
|
|
59
|
+
const parsed = GetThreadSchema.parse(args);
|
|
60
|
+
const client = clientFactory();
|
|
61
|
+
const { messages, hasMore } = await client.getThread(parsed.channel_id, parsed.thread_ts, {
|
|
62
|
+
limit: parsed.reply_limit,
|
|
63
|
+
});
|
|
64
|
+
if (messages.length === 0) {
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: 'text',
|
|
69
|
+
text: 'Thread not found or has no messages.',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// First message is the parent
|
|
75
|
+
const parent = messages[0];
|
|
76
|
+
const replies = messages.slice(1);
|
|
77
|
+
let output = `# Thread in channel ${parsed.channel_id}\n\n`;
|
|
78
|
+
// Format parent message
|
|
79
|
+
const parentTime = new Date(parseFloat(parent.ts) * 1000).toISOString();
|
|
80
|
+
const parentSender = parent.user || parent.bot_id || 'unknown';
|
|
81
|
+
const parentReactions = parent.reactions
|
|
82
|
+
? `\nReactions: ${parent.reactions.map((r) => `:${r.name}: ${r.count}`).join(' ')}`
|
|
83
|
+
: '';
|
|
84
|
+
output += `## Parent Message\n`;
|
|
85
|
+
output += `**${parentSender}** (${parentTime})\n`;
|
|
86
|
+
output += `${parent.text}${parentReactions}\n`;
|
|
87
|
+
output += `ts: ${parent.ts}\n\n`;
|
|
88
|
+
// Format replies
|
|
89
|
+
if (replies.length > 0) {
|
|
90
|
+
output += `## Replies (${replies.length}${hasMore ? '+' : ''}):\n\n`;
|
|
91
|
+
for (const reply of replies) {
|
|
92
|
+
const time = new Date(parseFloat(reply.ts) * 1000).toISOString();
|
|
93
|
+
const sender = reply.user || reply.bot_id || 'unknown';
|
|
94
|
+
const reactions = reply.reactions
|
|
95
|
+
? `\nReactions: ${reply.reactions.map((r) => `:${r.name}: ${r.count}`).join(' ')}`
|
|
96
|
+
: '';
|
|
97
|
+
output += `**${sender}** (${time})\n`;
|
|
98
|
+
output += `${reply.text}${reactions}\n`;
|
|
99
|
+
output += `ts: ${reply.ts}\n\n`;
|
|
100
|
+
}
|
|
101
|
+
if (hasMore) {
|
|
102
|
+
output += `_More replies available. Increase reply_limit to see more._\n`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
output += '_No replies yet._\n';
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
content: [
|
|
110
|
+
{
|
|
111
|
+
type: 'text',
|
|
112
|
+
text: output,
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
return {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: 'text',
|
|
122
|
+
text: `Error fetching thread: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
isError: true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const PostMessageSchema: z.ZodObject<{
|
|
5
|
+
channel_id: z.ZodString;
|
|
6
|
+
text: z.ZodString;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
text: string;
|
|
9
|
+
channel_id: string;
|
|
10
|
+
}, {
|
|
11
|
+
text: string;
|
|
12
|
+
channel_id: string;
|
|
13
|
+
}>;
|
|
14
|
+
export declare function postMessageTool(server: Server, clientFactory: ClientFactory): {
|
|
15
|
+
name: string;
|
|
16
|
+
description: string;
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: "object";
|
|
19
|
+
properties: {
|
|
20
|
+
channel_id: {
|
|
21
|
+
type: string;
|
|
22
|
+
description: string;
|
|
23
|
+
};
|
|
24
|
+
text: {
|
|
25
|
+
type: string;
|
|
26
|
+
description: string;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
required: string[];
|
|
30
|
+
};
|
|
31
|
+
handler: (args: unknown) => Promise<{
|
|
32
|
+
content: {
|
|
33
|
+
type: string;
|
|
34
|
+
text: string;
|
|
35
|
+
}[];
|
|
36
|
+
isError?: undefined;
|
|
37
|
+
} | {
|
|
38
|
+
content: {
|
|
39
|
+
type: string;
|
|
40
|
+
text: string;
|
|
41
|
+
}[];
|
|
42
|
+
isError: boolean;
|
|
43
|
+
}>;
|
|
44
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
channel_id: 'The channel ID to post the message to (e.g., "C1234567890"). ' +
|
|
4
|
+
'Get channel IDs using the slack_get_channels tool.',
|
|
5
|
+
text: 'The message content to post. Supports Slack markdown formatting: ' +
|
|
6
|
+
'*bold*, _italic_, `code`, ```code blocks```, and <URL|link text>.',
|
|
7
|
+
};
|
|
8
|
+
export const PostMessageSchema = z.object({
|
|
9
|
+
channel_id: z.string().min(1).describe(PARAM_DESCRIPTIONS.channel_id),
|
|
10
|
+
text: z.string().min(1).describe(PARAM_DESCRIPTIONS.text),
|
|
11
|
+
});
|
|
12
|
+
const TOOL_DESCRIPTION = `Post a new message to a Slack channel.
|
|
13
|
+
|
|
14
|
+
Creates a new message in the specified channel. The message appears as a new post, not as a reply to any thread.
|
|
15
|
+
|
|
16
|
+
**Returns:**
|
|
17
|
+
- Confirmation of the posted message
|
|
18
|
+
- The message timestamp (ts) which can be used to:
|
|
19
|
+
- Start a thread by replying with slack_reply_to_thread
|
|
20
|
+
- Edit the message with slack_update_message
|
|
21
|
+
- Add reactions with slack_react_to_message
|
|
22
|
+
|
|
23
|
+
**Use cases:**
|
|
24
|
+
- Share information with a channel
|
|
25
|
+
- Start a new discussion topic
|
|
26
|
+
- Post announcements or updates
|
|
27
|
+
- Ask questions to the channel
|
|
28
|
+
|
|
29
|
+
**Note:** To reply to an existing thread, use slack_reply_to_thread instead.`;
|
|
30
|
+
export function postMessageTool(server, clientFactory) {
|
|
31
|
+
return {
|
|
32
|
+
name: 'slack_post_message',
|
|
33
|
+
description: TOOL_DESCRIPTION,
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: 'object',
|
|
36
|
+
properties: {
|
|
37
|
+
channel_id: {
|
|
38
|
+
type: 'string',
|
|
39
|
+
description: PARAM_DESCRIPTIONS.channel_id,
|
|
40
|
+
},
|
|
41
|
+
text: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: PARAM_DESCRIPTIONS.text,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ['channel_id', 'text'],
|
|
47
|
+
},
|
|
48
|
+
handler: async (args) => {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = PostMessageSchema.parse(args);
|
|
51
|
+
const client = clientFactory();
|
|
52
|
+
const message = await client.postMessage(parsed.channel_id, parsed.text);
|
|
53
|
+
const time = new Date(parseFloat(message.ts) * 1000).toISOString();
|
|
54
|
+
return {
|
|
55
|
+
content: [
|
|
56
|
+
{
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: `Message posted successfully!\n\n` +
|
|
59
|
+
`Channel: ${parsed.channel_id}\n` +
|
|
60
|
+
`Timestamp: ${message.ts}\n` +
|
|
61
|
+
`Posted at: ${time}\n\n` +
|
|
62
|
+
`Content:\n${parsed.text}\n\n` +
|
|
63
|
+
`Use the timestamp (ts: ${message.ts}) to reply to this message or add reactions.`,
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: `Error posting message: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
isError: true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const ReactToMessageSchema: z.ZodObject<{
|
|
5
|
+
channel_id: z.ZodString;
|
|
6
|
+
message_ts: z.ZodString;
|
|
7
|
+
emoji: z.ZodString;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
emoji: string;
|
|
10
|
+
channel_id: string;
|
|
11
|
+
message_ts: string;
|
|
12
|
+
}, {
|
|
13
|
+
emoji: string;
|
|
14
|
+
channel_id: string;
|
|
15
|
+
message_ts: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function reactToMessageTool(server: Server, clientFactory: ClientFactory): {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object";
|
|
22
|
+
properties: {
|
|
23
|
+
channel_id: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
message_ts: {
|
|
28
|
+
type: string;
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
emoji: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
required: string[];
|
|
37
|
+
};
|
|
38
|
+
handler: (args: unknown) => Promise<{
|
|
39
|
+
content: {
|
|
40
|
+
type: string;
|
|
41
|
+
text: string;
|
|
42
|
+
}[];
|
|
43
|
+
isError?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
content: {
|
|
46
|
+
type: string;
|
|
47
|
+
text: string;
|
|
48
|
+
}[];
|
|
49
|
+
isError: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
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 react to (e.g., "1234567890.123456"). ' +
|
|
6
|
+
'This is shown as "ts" in message outputs.',
|
|
7
|
+
emoji: 'The emoji name without colons (e.g., "thumbsup", "white_check_mark", "eyes"). ' +
|
|
8
|
+
'Common reactions: thumbsup, thumbsdown, heart, eyes, white_check_mark, x, tada.',
|
|
9
|
+
};
|
|
10
|
+
export const ReactToMessageSchema = 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
|
+
emoji: z.string().min(1).describe(PARAM_DESCRIPTIONS.emoji),
|
|
14
|
+
});
|
|
15
|
+
const TOOL_DESCRIPTION = `Add an emoji reaction to a message in Slack.
|
|
16
|
+
|
|
17
|
+
Adds a reaction (emoji) to a specific message. Reactions are a lightweight way to acknowledge or respond to messages without creating a new message.
|
|
18
|
+
|
|
19
|
+
**Returns:**
|
|
20
|
+
- Confirmation that the reaction was added
|
|
21
|
+
|
|
22
|
+
**Common emoji names:**
|
|
23
|
+
- Positive: thumbsup, +1, heart, tada, star, clap
|
|
24
|
+
- Acknowledgment: eyes, white_check_mark, ok_hand
|
|
25
|
+
- Negative: thumbsdown, -1, x
|
|
26
|
+
- Status: hourglass, warning, question, exclamation
|
|
27
|
+
|
|
28
|
+
**Use cases:**
|
|
29
|
+
- Acknowledge that you've seen a message (:eyes:)
|
|
30
|
+
- Mark a task as complete (:white_check_mark:)
|
|
31
|
+
- Show approval or agreement (:thumbsup:)
|
|
32
|
+
- Celebrate achievements (:tada:)
|
|
33
|
+
- Signal that something needs attention (:warning:)
|
|
34
|
+
|
|
35
|
+
**Note:** If the reaction already exists from this bot, no error is returned.`;
|
|
36
|
+
export function reactToMessageTool(server, clientFactory) {
|
|
37
|
+
return {
|
|
38
|
+
name: 'slack_react_to_message',
|
|
39
|
+
description: TOOL_DESCRIPTION,
|
|
40
|
+
inputSchema: {
|
|
41
|
+
type: 'object',
|
|
42
|
+
properties: {
|
|
43
|
+
channel_id: {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: PARAM_DESCRIPTIONS.channel_id,
|
|
46
|
+
},
|
|
47
|
+
message_ts: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
description: PARAM_DESCRIPTIONS.message_ts,
|
|
50
|
+
},
|
|
51
|
+
emoji: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
description: PARAM_DESCRIPTIONS.emoji,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: ['channel_id', 'message_ts', 'emoji'],
|
|
57
|
+
},
|
|
58
|
+
handler: async (args) => {
|
|
59
|
+
try {
|
|
60
|
+
const parsed = ReactToMessageSchema.parse(args);
|
|
61
|
+
const client = clientFactory();
|
|
62
|
+
// Remove colons if the user included them
|
|
63
|
+
const emojiName = parsed.emoji.replace(/^:/, '').replace(/:$/, '');
|
|
64
|
+
await client.addReaction(parsed.channel_id, parsed.message_ts, emojiName);
|
|
65
|
+
return {
|
|
66
|
+
content: [
|
|
67
|
+
{
|
|
68
|
+
type: 'text',
|
|
69
|
+
text: `Reaction added successfully!\n\n` +
|
|
70
|
+
`Channel: ${parsed.channel_id}\n` +
|
|
71
|
+
`Message: ${parsed.message_ts}\n` +
|
|
72
|
+
`Reaction: :${emojiName}:`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: `Error adding reaction: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const ReplyToThreadSchema: z.ZodObject<{
|
|
5
|
+
channel_id: z.ZodString;
|
|
6
|
+
thread_ts: z.ZodString;
|
|
7
|
+
text: z.ZodString;
|
|
8
|
+
broadcast: z.ZodDefault<z.ZodBoolean>;
|
|
9
|
+
}, "strip", z.ZodTypeAny, {
|
|
10
|
+
text: string;
|
|
11
|
+
channel_id: string;
|
|
12
|
+
thread_ts: string;
|
|
13
|
+
broadcast: boolean;
|
|
14
|
+
}, {
|
|
15
|
+
text: string;
|
|
16
|
+
channel_id: string;
|
|
17
|
+
thread_ts: string;
|
|
18
|
+
broadcast?: boolean | undefined;
|
|
19
|
+
}>;
|
|
20
|
+
export declare function replyToThreadTool(server: Server, clientFactory: ClientFactory): {
|
|
21
|
+
name: string;
|
|
22
|
+
description: string;
|
|
23
|
+
inputSchema: {
|
|
24
|
+
type: "object";
|
|
25
|
+
properties: {
|
|
26
|
+
channel_id: {
|
|
27
|
+
type: string;
|
|
28
|
+
description: string;
|
|
29
|
+
};
|
|
30
|
+
thread_ts: {
|
|
31
|
+
type: string;
|
|
32
|
+
description: string;
|
|
33
|
+
};
|
|
34
|
+
text: {
|
|
35
|
+
type: string;
|
|
36
|
+
description: string;
|
|
37
|
+
};
|
|
38
|
+
broadcast: {
|
|
39
|
+
type: string;
|
|
40
|
+
default: boolean;
|
|
41
|
+
description: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
required: string[];
|
|
45
|
+
};
|
|
46
|
+
handler: (args: unknown) => Promise<{
|
|
47
|
+
content: {
|
|
48
|
+
type: string;
|
|
49
|
+
text: string;
|
|
50
|
+
}[];
|
|
51
|
+
isError?: undefined;
|
|
52
|
+
} | {
|
|
53
|
+
content: {
|
|
54
|
+
type: string;
|
|
55
|
+
text: string;
|
|
56
|
+
}[];
|
|
57
|
+
isError: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const PARAM_DESCRIPTIONS = {
|
|
3
|
+
channel_id: 'The channel ID where the thread exists (e.g., "C1234567890"). ' +
|
|
4
|
+
'Get channel IDs using the slack_get_channels tool.',
|
|
5
|
+
thread_ts: 'The timestamp of the parent message to reply to (e.g., "1234567890.123456"). ' +
|
|
6
|
+
'This creates a threaded reply under that message.',
|
|
7
|
+
text: 'The reply content. Supports Slack markdown formatting: ' +
|
|
8
|
+
'*bold*, _italic_, `code`, ```code blocks```, and <URL|link text>.',
|
|
9
|
+
broadcast: 'Also post the reply to the channel (not just the thread). Default: false. ' +
|
|
10
|
+
'When true, the reply appears both in the thread and as a channel message.',
|
|
11
|
+
};
|
|
12
|
+
export const ReplyToThreadSchema = z.object({
|
|
13
|
+
channel_id: z.string().min(1).describe(PARAM_DESCRIPTIONS.channel_id),
|
|
14
|
+
thread_ts: z.string().min(1).describe(PARAM_DESCRIPTIONS.thread_ts),
|
|
15
|
+
text: z.string().min(1).describe(PARAM_DESCRIPTIONS.text),
|
|
16
|
+
broadcast: z.boolean().default(false).describe(PARAM_DESCRIPTIONS.broadcast),
|
|
17
|
+
});
|
|
18
|
+
const TOOL_DESCRIPTION = `Reply to an existing thread in Slack.
|
|
19
|
+
|
|
20
|
+
Posts a message as a reply to a specific thread. The reply will appear nested under the parent message.
|
|
21
|
+
|
|
22
|
+
**Returns:**
|
|
23
|
+
- Confirmation of the posted reply
|
|
24
|
+
- The reply's timestamp (ts) for further operations
|
|
25
|
+
|
|
26
|
+
**Use cases:**
|
|
27
|
+
- Continue a threaded conversation
|
|
28
|
+
- Answer a question in a thread
|
|
29
|
+
- Provide follow-up information
|
|
30
|
+
- Keep discussions organized within threads
|
|
31
|
+
|
|
32
|
+
**Note:** Use slack_get_thread first to read the conversation before replying.`;
|
|
33
|
+
export function replyToThreadTool(server, clientFactory) {
|
|
34
|
+
return {
|
|
35
|
+
name: 'slack_reply_to_thread',
|
|
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
|
+
thread_ts: {
|
|
45
|
+
type: 'string',
|
|
46
|
+
description: PARAM_DESCRIPTIONS.thread_ts,
|
|
47
|
+
},
|
|
48
|
+
text: {
|
|
49
|
+
type: 'string',
|
|
50
|
+
description: PARAM_DESCRIPTIONS.text,
|
|
51
|
+
},
|
|
52
|
+
broadcast: {
|
|
53
|
+
type: 'boolean',
|
|
54
|
+
default: false,
|
|
55
|
+
description: PARAM_DESCRIPTIONS.broadcast,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
required: ['channel_id', 'thread_ts', 'text'],
|
|
59
|
+
},
|
|
60
|
+
handler: async (args) => {
|
|
61
|
+
try {
|
|
62
|
+
const parsed = ReplyToThreadSchema.parse(args);
|
|
63
|
+
const client = clientFactory();
|
|
64
|
+
const message = await client.postMessage(parsed.channel_id, parsed.text, {
|
|
65
|
+
threadTs: parsed.thread_ts,
|
|
66
|
+
replyBroadcast: parsed.broadcast,
|
|
67
|
+
});
|
|
68
|
+
const time = new Date(parseFloat(message.ts) * 1000).toISOString();
|
|
69
|
+
return {
|
|
70
|
+
content: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
text: `Reply posted successfully!\n\n` +
|
|
74
|
+
`Channel: ${parsed.channel_id}\n` +
|
|
75
|
+
`Thread: ${parsed.thread_ts}\n` +
|
|
76
|
+
`Reply timestamp: ${message.ts}\n` +
|
|
77
|
+
`Posted at: ${time}\n` +
|
|
78
|
+
(parsed.broadcast ? 'Broadcasted to channel: Yes\n' : '') +
|
|
79
|
+
`\nContent:\n${parsed.text}`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: 'text',
|
|
89
|
+
text: `Error posting reply: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
90
|
+
},
|
|
91
|
+
],
|
|
92
|
+
isError: true,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientFactory } from '../server.js';
|
|
4
|
+
export declare const UpdateMessageSchema: z.ZodObject<{
|
|
5
|
+
channel_id: z.ZodString;
|
|
6
|
+
message_ts: z.ZodString;
|
|
7
|
+
text: z.ZodString;
|
|
8
|
+
}, "strip", z.ZodTypeAny, {
|
|
9
|
+
text: string;
|
|
10
|
+
channel_id: string;
|
|
11
|
+
message_ts: string;
|
|
12
|
+
}, {
|
|
13
|
+
text: string;
|
|
14
|
+
channel_id: string;
|
|
15
|
+
message_ts: string;
|
|
16
|
+
}>;
|
|
17
|
+
export declare function updateMessageTool(server: Server, clientFactory: ClientFactory): {
|
|
18
|
+
name: string;
|
|
19
|
+
description: string;
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object";
|
|
22
|
+
properties: {
|
|
23
|
+
channel_id: {
|
|
24
|
+
type: string;
|
|
25
|
+
description: string;
|
|
26
|
+
};
|
|
27
|
+
message_ts: {
|
|
28
|
+
type: string;
|
|
29
|
+
description: string;
|
|
30
|
+
};
|
|
31
|
+
text: {
|
|
32
|
+
type: string;
|
|
33
|
+
description: string;
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
required: string[];
|
|
37
|
+
};
|
|
38
|
+
handler: (args: unknown) => Promise<{
|
|
39
|
+
content: {
|
|
40
|
+
type: string;
|
|
41
|
+
text: string;
|
|
42
|
+
}[];
|
|
43
|
+
isError?: undefined;
|
|
44
|
+
} | {
|
|
45
|
+
content: {
|
|
46
|
+
type: string;
|
|
47
|
+
text: string;
|
|
48
|
+
}[];
|
|
49
|
+
isError: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
};
|