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
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Fetches message history from a channel
3
+ * Returns messages in reverse chronological order (newest first)
4
+ */
5
+ export async function getMessages(baseUrl, headers, channelId, options) {
6
+ const params = new URLSearchParams({
7
+ channel: channelId,
8
+ limit: (options?.limit ?? 20).toString(),
9
+ });
10
+ if (options?.cursor) {
11
+ params.set('cursor', options.cursor);
12
+ }
13
+ if (options?.oldest) {
14
+ params.set('oldest', options.oldest);
15
+ }
16
+ if (options?.latest) {
17
+ params.set('latest', options.latest);
18
+ }
19
+ if (options?.inclusive !== undefined) {
20
+ params.set('inclusive', options.inclusive.toString());
21
+ }
22
+ const response = await fetch(`${baseUrl}/conversations.history?${params}`, {
23
+ method: 'GET',
24
+ headers,
25
+ });
26
+ if (!response.ok) {
27
+ throw new Error(`Failed to fetch messages: ${response.status} ${response.statusText}`);
28
+ }
29
+ const data = (await response.json());
30
+ if (!data.ok) {
31
+ throw new Error(`Slack API error: ${data.error}`);
32
+ }
33
+ return {
34
+ messages: data.messages ?? [],
35
+ hasMore: data.has_more ?? false,
36
+ nextCursor: data.response_metadata?.next_cursor,
37
+ };
38
+ }
@@ -0,0 +1,16 @@
1
+ import type { Message } from '../../types.js';
2
+ /**
3
+ * Fetches all replies in a thread
4
+ * The first message is the parent message, followed by replies
5
+ */
6
+ export declare function getThread(baseUrl: string, headers: Record<string, string>, channelId: string, threadTs: 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
+ }>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Fetches all replies in a thread
3
+ * The first message is the parent message, followed by replies
4
+ */
5
+ export async function getThread(baseUrl, headers, channelId, threadTs, options) {
6
+ const params = new URLSearchParams({
7
+ channel: channelId,
8
+ ts: threadTs,
9
+ limit: (options?.limit ?? 100).toString(),
10
+ });
11
+ if (options?.cursor) {
12
+ params.set('cursor', options.cursor);
13
+ }
14
+ if (options?.oldest) {
15
+ params.set('oldest', options.oldest);
16
+ }
17
+ if (options?.latest) {
18
+ params.set('latest', options.latest);
19
+ }
20
+ if (options?.inclusive !== undefined) {
21
+ params.set('inclusive', options.inclusive.toString());
22
+ }
23
+ const response = await fetch(`${baseUrl}/conversations.replies?${params}`, {
24
+ method: 'GET',
25
+ headers,
26
+ });
27
+ if (!response.ok) {
28
+ throw new Error(`Failed to fetch thread: ${response.status} ${response.statusText}`);
29
+ }
30
+ const data = (await response.json());
31
+ if (!data.ok) {
32
+ throw new Error(`Slack API error: ${data.error}`);
33
+ }
34
+ return {
35
+ messages: data.messages ?? [],
36
+ hasMore: data.has_more ?? false,
37
+ nextCursor: data.response_metadata?.next_cursor,
38
+ };
39
+ }
@@ -0,0 +1,10 @@
1
+ import type { Message } from '../../types.js';
2
+ /**
3
+ * Posts a new message to a channel
4
+ */
5
+ export declare function postMessage(baseUrl: string, headers: Record<string, string>, channelId: string, text: string, options?: {
6
+ threadTs?: string;
7
+ replyBroadcast?: boolean;
8
+ unfurlLinks?: boolean;
9
+ unfurlMedia?: boolean;
10
+ }): Promise<Message>;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Posts a new message to a channel
3
+ */
4
+ export async function postMessage(baseUrl, headers, channelId, text, options) {
5
+ const body = {
6
+ channel: channelId,
7
+ text,
8
+ };
9
+ if (options?.threadTs) {
10
+ body.thread_ts = options.threadTs;
11
+ }
12
+ if (options?.replyBroadcast !== undefined) {
13
+ body.reply_broadcast = options.replyBroadcast;
14
+ }
15
+ if (options?.unfurlLinks !== undefined) {
16
+ body.unfurl_links = options.unfurlLinks;
17
+ }
18
+ if (options?.unfurlMedia !== undefined) {
19
+ body.unfurl_media = options.unfurlMedia;
20
+ }
21
+ const response = await fetch(`${baseUrl}/chat.postMessage`, {
22
+ method: 'POST',
23
+ headers: {
24
+ ...headers,
25
+ 'Content-Type': 'application/json; charset=utf-8',
26
+ },
27
+ body: JSON.stringify(body),
28
+ });
29
+ if (!response.ok) {
30
+ throw new Error(`Failed to post message: ${response.status} ${response.statusText}`);
31
+ }
32
+ const data = (await response.json());
33
+ if (!data.ok) {
34
+ throw new Error(`Slack API error: ${data.error}`);
35
+ }
36
+ if (!data.message) {
37
+ throw new Error('Message not found in response');
38
+ }
39
+ // Ensure ts is set on the message
40
+ return {
41
+ ...data.message,
42
+ ts: data.ts ?? data.message.ts,
43
+ };
44
+ }
@@ -0,0 +1,5 @@
1
+ import type { Message } from '../../types.js';
2
+ /**
3
+ * Updates an existing message
4
+ */
5
+ export declare function updateMessage(baseUrl: string, headers: Record<string, string>, channelId: string, ts: string, text: string): Promise<Message>;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Updates an existing message
3
+ */
4
+ export async function updateMessage(baseUrl, headers, channelId, ts, text) {
5
+ const body = {
6
+ channel: channelId,
7
+ ts,
8
+ text,
9
+ };
10
+ const response = await fetch(`${baseUrl}/chat.update`, {
11
+ method: 'POST',
12
+ headers: {
13
+ ...headers,
14
+ 'Content-Type': 'application/json; charset=utf-8',
15
+ },
16
+ body: JSON.stringify(body),
17
+ });
18
+ if (!response.ok) {
19
+ throw new Error(`Failed to update message: ${response.status} ${response.statusText}`);
20
+ }
21
+ const data = (await response.json());
22
+ if (!data.ok) {
23
+ throw new Error(`Slack API error: ${data.error}`);
24
+ }
25
+ // Return the updated message
26
+ return {
27
+ type: 'message',
28
+ ts: data.ts ?? ts,
29
+ text: data.text ?? text,
30
+ ...data.message,
31
+ };
32
+ }
@@ -0,0 +1,16 @@
1
+ import type { ISlackClient } from '../server.js';
2
+ import type { Channel, Message } from '../types.js';
3
+ interface MockData {
4
+ channels?: Channel[];
5
+ messages?: Record<string, Message[]>;
6
+ threads?: Record<string, Message[]>;
7
+ [key: string]: unknown;
8
+ }
9
+ /**
10
+ * Creates a mock implementation of ISlackClient for integration tests.
11
+ * This mocks the EXTERNAL Slack API, NOT the MCP client.
12
+ */
13
+ export declare function createIntegrationMockSlackClient(mockData?: MockData): ISlackClient & {
14
+ mockData: MockData;
15
+ };
16
+ export {};
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Creates a mock implementation of ISlackClient for integration tests.
3
+ * This mocks the EXTERNAL Slack API, NOT the MCP client.
4
+ */
5
+ export function createIntegrationMockSlackClient(mockData = {}) {
6
+ // Track posted messages for testing
7
+ const postedMessages = [];
8
+ const client = {
9
+ mockData,
10
+ async getChannels() {
11
+ return (mockData.channels ?? [
12
+ {
13
+ id: 'C123456789',
14
+ name: 'general',
15
+ is_channel: true,
16
+ is_group: false,
17
+ is_im: false,
18
+ is_mpim: false,
19
+ is_private: false,
20
+ is_archived: false,
21
+ is_general: true,
22
+ is_member: true,
23
+ num_members: 10,
24
+ created: 1234567890,
25
+ },
26
+ ]);
27
+ },
28
+ async getChannel(channelId) {
29
+ const channel = mockData.channels?.find((c) => c.id === channelId);
30
+ if (channel) {
31
+ return channel;
32
+ }
33
+ return {
34
+ id: channelId,
35
+ name: 'mock-channel',
36
+ is_channel: true,
37
+ is_group: false,
38
+ is_im: false,
39
+ is_mpim: false,
40
+ is_private: false,
41
+ is_archived: false,
42
+ is_general: false,
43
+ is_member: true,
44
+ created: 1234567890,
45
+ };
46
+ },
47
+ async getMessages(channelId, options) {
48
+ const messages = mockData.messages?.[channelId] ?? [
49
+ {
50
+ type: 'message',
51
+ user: 'U123456789',
52
+ text: 'Mock message',
53
+ ts: '1234567890.123456',
54
+ },
55
+ ];
56
+ const limit = options?.limit ?? 20;
57
+ return {
58
+ messages: messages.slice(0, limit),
59
+ hasMore: messages.length > limit,
60
+ };
61
+ },
62
+ async getThread(channelId, threadTs, options) {
63
+ const key = `${channelId}:${threadTs}`;
64
+ const messages = mockData.threads?.[key] ?? [
65
+ {
66
+ type: 'message',
67
+ user: 'U123456789',
68
+ text: 'Mock parent message',
69
+ ts: threadTs,
70
+ thread_ts: threadTs,
71
+ },
72
+ ];
73
+ const limit = options?.limit ?? 50;
74
+ return {
75
+ messages: messages.slice(0, limit),
76
+ hasMore: messages.length > limit,
77
+ };
78
+ },
79
+ async postMessage(channelId, text, options) {
80
+ const ts = `${Date.now() / 1000}.123456`;
81
+ const message = {
82
+ type: 'message',
83
+ text,
84
+ ts,
85
+ thread_ts: options?.threadTs,
86
+ };
87
+ postedMessages.push(message);
88
+ return message;
89
+ },
90
+ async updateMessage(channelId, ts, text) {
91
+ return {
92
+ type: 'message',
93
+ text,
94
+ ts,
95
+ edited: {
96
+ user: 'U123456789',
97
+ ts: `${Date.now() / 1000}`,
98
+ },
99
+ };
100
+ },
101
+ async addReaction() {
102
+ // No-op for mock
103
+ },
104
+ };
105
+ return client;
106
+ }
@@ -0,0 +1,55 @@
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 GetChannelSchema: z.ZodObject<{
5
+ channel_id: z.ZodString;
6
+ include_messages: z.ZodDefault<z.ZodBoolean>;
7
+ message_limit: z.ZodDefault<z.ZodNumber>;
8
+ }, "strip", z.ZodTypeAny, {
9
+ channel_id: string;
10
+ include_messages: boolean;
11
+ message_limit: number;
12
+ }, {
13
+ channel_id: string;
14
+ include_messages?: boolean | undefined;
15
+ message_limit?: number | undefined;
16
+ }>;
17
+ export declare function getChannelTool(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
+ include_messages: {
28
+ type: string;
29
+ default: boolean;
30
+ description: string;
31
+ };
32
+ message_limit: {
33
+ type: string;
34
+ default: number;
35
+ minimum: number;
36
+ maximum: number;
37
+ description: string;
38
+ };
39
+ };
40
+ required: string[];
41
+ };
42
+ handler: (args: unknown) => Promise<{
43
+ content: {
44
+ type: string;
45
+ text: string;
46
+ }[];
47
+ isError?: undefined;
48
+ } | {
49
+ content: {
50
+ type: string;
51
+ text: string;
52
+ }[];
53
+ isError: boolean;
54
+ }>;
55
+ };
@@ -0,0 +1,136 @@
1
+ import { z } from 'zod';
2
+ const PARAM_DESCRIPTIONS = {
3
+ channel_id: 'The unique identifier of the channel (e.g., "C1234567890"). ' +
4
+ 'Get channel IDs using the slack_get_channels tool.',
5
+ include_messages: 'Whether to include recent messages from the channel. Default: true. ' +
6
+ 'Set to false to only get channel metadata.',
7
+ message_limit: 'Maximum number of messages to return when include_messages is true. ' +
8
+ 'Default: 20. Maximum: 100.',
9
+ };
10
+ export const GetChannelSchema = z.object({
11
+ channel_id: z.string().min(1).describe(PARAM_DESCRIPTIONS.channel_id),
12
+ include_messages: z.boolean().default(true).describe(PARAM_DESCRIPTIONS.include_messages),
13
+ message_limit: z.number().min(1).max(100).default(20).describe(PARAM_DESCRIPTIONS.message_limit),
14
+ });
15
+ const TOOL_DESCRIPTION = `Get detailed information about a Slack channel, optionally including recent messages.
16
+
17
+ Returns channel metadata (name, topic, purpose, member count) and optionally the most recent messages. This is equivalent to opening a channel in Slack.
18
+
19
+ **Returns:**
20
+ - Channel details: name, topic, purpose, member count, creation date
21
+ - Recent messages (if include_messages is true): sender, content, timestamp, reactions, thread info
22
+
23
+ **Use cases:**
24
+ - Get context about a channel before posting
25
+ - Read recent messages to understand current discussions
26
+ - Check if a channel is active or archived
27
+ - Find threads that may need responses
28
+
29
+ **Note:** Messages are returned newest first. Thread parent messages include reply counts.`;
30
+ export function getChannelTool(server, clientFactory) {
31
+ return {
32
+ name: 'slack_get_channel',
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
+ include_messages: {
42
+ type: 'boolean',
43
+ default: true,
44
+ description: PARAM_DESCRIPTIONS.include_messages,
45
+ },
46
+ message_limit: {
47
+ type: 'number',
48
+ default: 20,
49
+ minimum: 1,
50
+ maximum: 100,
51
+ description: PARAM_DESCRIPTIONS.message_limit,
52
+ },
53
+ },
54
+ required: ['channel_id'],
55
+ },
56
+ handler: async (args) => {
57
+ try {
58
+ const parsed = GetChannelSchema.parse(args);
59
+ const client = clientFactory();
60
+ // Get channel info
61
+ const channel = await client.getChannel(parsed.channel_id);
62
+ // Build channel info section
63
+ const flags = [];
64
+ if (channel.is_private)
65
+ flags.push('private');
66
+ if (channel.is_archived)
67
+ flags.push('archived');
68
+ if (channel.is_general)
69
+ flags.push('general');
70
+ let output = `# Channel: #${channel.name}\n`;
71
+ if (flags.length > 0) {
72
+ output += `Status: ${flags.join(', ')}\n`;
73
+ }
74
+ output += `ID: ${channel.id}\n`;
75
+ if (channel.topic?.value) {
76
+ output += `Topic: ${channel.topic.value}\n`;
77
+ }
78
+ if (channel.purpose?.value) {
79
+ output += `Purpose: ${channel.purpose.value}\n`;
80
+ }
81
+ if (channel.num_members !== undefined) {
82
+ output += `Members: ${channel.num_members}\n`;
83
+ }
84
+ output += `Created: ${new Date(channel.created * 1000).toISOString()}\n`;
85
+ // Get messages if requested
86
+ if (parsed.include_messages) {
87
+ const { messages, hasMore } = await client.getMessages(parsed.channel_id, {
88
+ limit: parsed.message_limit,
89
+ });
90
+ if (messages.length > 0) {
91
+ output += `\n## Recent Messages (${messages.length}${hasMore ? '+' : ''}):\n\n`;
92
+ for (const msg of messages) {
93
+ const time = new Date(parseFloat(msg.ts) * 1000).toISOString();
94
+ const sender = msg.user || msg.bot_id || 'unknown';
95
+ const threadInfo = msg.reply_count ? ` [${msg.reply_count} replies]` : '';
96
+ const reactions = msg.reactions
97
+ ? ` | Reactions: ${msg.reactions.map((r) => `:${r.name}: ${r.count}`).join(' ')}`
98
+ : '';
99
+ output += `**${sender}** (${time})${threadInfo}${reactions}\n`;
100
+ output += `${msg.text}\n`;
101
+ if (msg.thread_ts && msg.thread_ts !== msg.ts) {
102
+ output += `↳ Reply to thread: ${msg.thread_ts}\n`;
103
+ }
104
+ output += `ts: ${msg.ts}\n\n`;
105
+ }
106
+ if (hasMore) {
107
+ output += `_More messages available. Increase message_limit to see more._\n`;
108
+ }
109
+ }
110
+ else {
111
+ output += '\n_No messages in this channel yet._\n';
112
+ }
113
+ }
114
+ return {
115
+ content: [
116
+ {
117
+ type: 'text',
118
+ text: output,
119
+ },
120
+ ],
121
+ };
122
+ }
123
+ catch (error) {
124
+ return {
125
+ content: [
126
+ {
127
+ type: 'text',
128
+ text: `Error fetching channel: ${error instanceof Error ? error.message : 'Unknown error'}`,
129
+ },
130
+ ],
131
+ isError: true,
132
+ };
133
+ }
134
+ },
135
+ };
136
+ }
@@ -0,0 +1,46 @@
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 GetChannelsSchema: z.ZodObject<{
5
+ include_private: z.ZodDefault<z.ZodBoolean>;
6
+ include_archived: z.ZodDefault<z.ZodBoolean>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ include_private: boolean;
9
+ include_archived: boolean;
10
+ }, {
11
+ include_private?: boolean | undefined;
12
+ include_archived?: boolean | undefined;
13
+ }>;
14
+ export declare function getChannelsTool(server: Server, clientFactory: ClientFactory): {
15
+ name: string;
16
+ description: string;
17
+ inputSchema: {
18
+ type: "object";
19
+ properties: {
20
+ include_private: {
21
+ type: string;
22
+ default: boolean;
23
+ description: string;
24
+ };
25
+ include_archived: {
26
+ type: string;
27
+ default: boolean;
28
+ description: string;
29
+ };
30
+ };
31
+ required: never[];
32
+ };
33
+ handler: (args: unknown) => Promise<{
34
+ content: {
35
+ type: string;
36
+ text: string;
37
+ }[];
38
+ isError?: undefined;
39
+ } | {
40
+ content: {
41
+ type: string;
42
+ text: string;
43
+ }[];
44
+ isError: boolean;
45
+ }>;
46
+ };
@@ -0,0 +1,107 @@
1
+ import { z } from 'zod';
2
+ const PARAM_DESCRIPTIONS = {
3
+ include_private: 'Include private channels the bot is a member of. Default: true. ' +
4
+ 'Set to false to only list public channels.',
5
+ include_archived: 'Include archived channels. Default: false. ' +
6
+ 'Archived channels are read-only and cannot receive new messages.',
7
+ };
8
+ export const GetChannelsSchema = z.object({
9
+ include_private: z.boolean().default(true).describe(PARAM_DESCRIPTIONS.include_private),
10
+ include_archived: z.boolean().default(false).describe(PARAM_DESCRIPTIONS.include_archived),
11
+ });
12
+ const TOOL_DESCRIPTION = `List all Slack channels the bot has access to.
13
+
14
+ Returns a list of public and private channels in the workspace that the bot can see. Each channel includes its ID, name, topic, purpose, and member count.
15
+
16
+ **Returns:**
17
+ A formatted list of channels with their key details including:
18
+ - Channel ID (needed for other operations)
19
+ - Channel name
20
+ - Topic and purpose
21
+ - Whether the channel is private or archived
22
+ - Number of members
23
+
24
+ **Use cases:**
25
+ - Discover available channels to read or post to
26
+ - Find a specific channel's ID for subsequent operations
27
+ - Get an overview of the workspace's channel structure
28
+
29
+ **Note:** The bot can only see channels it has been invited to (for private channels) or all public channels.`;
30
+ export function getChannelsTool(server, clientFactory) {
31
+ return {
32
+ name: 'slack_get_channels',
33
+ description: TOOL_DESCRIPTION,
34
+ inputSchema: {
35
+ type: 'object',
36
+ properties: {
37
+ include_private: {
38
+ type: 'boolean',
39
+ default: true,
40
+ description: PARAM_DESCRIPTIONS.include_private,
41
+ },
42
+ include_archived: {
43
+ type: 'boolean',
44
+ default: false,
45
+ description: PARAM_DESCRIPTIONS.include_archived,
46
+ },
47
+ },
48
+ required: [],
49
+ },
50
+ handler: async (args) => {
51
+ try {
52
+ const parsed = GetChannelsSchema.parse(args ?? {});
53
+ const client = clientFactory();
54
+ const types = parsed.include_private ? 'public_channel,private_channel' : 'public_channel';
55
+ const channels = await client.getChannels({
56
+ types,
57
+ excludeArchived: !parsed.include_archived,
58
+ });
59
+ if (channels.length === 0) {
60
+ return {
61
+ content: [
62
+ {
63
+ type: 'text',
64
+ text: 'No channels found. The bot may not have access to any channels yet.',
65
+ },
66
+ ],
67
+ };
68
+ }
69
+ const channelList = channels
70
+ .map((ch) => {
71
+ const flags = [];
72
+ if (ch.is_private)
73
+ flags.push('private');
74
+ if (ch.is_archived)
75
+ flags.push('archived');
76
+ if (ch.is_general)
77
+ flags.push('general');
78
+ const flagStr = flags.length > 0 ? ` [${flags.join(', ')}]` : '';
79
+ const topic = ch.topic?.value ? `\n Topic: ${ch.topic.value}` : '';
80
+ const purpose = ch.purpose?.value ? `\n Purpose: ${ch.purpose.value}` : '';
81
+ const members = ch.num_members !== undefined ? `\n Members: ${ch.num_members}` : '';
82
+ return `• #${ch.name}${flagStr}\n ID: ${ch.id}${topic}${purpose}${members}`;
83
+ })
84
+ .join('\n\n');
85
+ return {
86
+ content: [
87
+ {
88
+ type: 'text',
89
+ text: `Found ${channels.length} channel(s):\n\n${channelList}`,
90
+ },
91
+ ],
92
+ };
93
+ }
94
+ catch (error) {
95
+ return {
96
+ content: [
97
+ {
98
+ type: 'text',
99
+ text: `Error fetching channels: ${error instanceof Error ? error.message : 'Unknown error'}`,
100
+ },
101
+ ],
102
+ isError: true,
103
+ };
104
+ }
105
+ },
106
+ };
107
+ }