Skip to content

Commit 3dad640

Browse files
authored
Merge pull request #15 from axivo/fix/code-improvements
fix: code improvements
2 parents e595c28 + 8722b23 commit 3dad640

File tree

5 files changed

+104
-86
lines changed

5 files changed

+104
-86
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ A MCP (Model Context Protocol) server for interacting with Slack.
88

99
### Security Features
1010

11-
- **Markdown Conversion**: Automatically converts GitHub Flavored Markdown to Slack `mrkdwn` format
1211
- **Link Unfurling Disabled**: Prevents automatic link crawling that could leak sensitive information
1312
- **Rate Limiting**: Enforces 60 requests per minute per endpoint
13+
- **Markdown Conversion**: Automatically converts GitHub Flavored Markdown to Slack `mrkdwn` format
1414

1515
### Slack Setup
1616

@@ -69,7 +69,20 @@ Optional variables:
6969

7070
- `SLACK_CHANNEL_IDS` - Comma-separated list of channel IDs to restrict access to specific channels only. If not set, the server can access all public channels the bot has permissions for.
7171

72-
### Tools
72+
### Prompt Examples
73+
74+
Here are practical examples of how to use the Slack MCP server with natural language prompts:
75+
76+
- "*Mention my name into #general Slack channel and post a project update*"
77+
- "*Read the last 20 messages from #marketing Slack channel to catch up on discussions*"
78+
- "*Reply to the latest thread in #support Slack channel with a solution*"
79+
- "*Add a thumbs up reaction to the latest message in #announcements Slack channel*"
80+
- "*Get all replies from that long thread about the API changes*"
81+
- "*Edit my last message in #general Slack channel to fix the typo*"
82+
- "*List all channels I have access to and find the engineering-related ones*"
83+
- "*Look up John's profile information and timezone*"
84+
85+
### MCP Tools
7386

7487
1. `add_reaction`
7588
- Add a reaction emoji to a message

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@axivo/mcp-slack",
3-
"version": "1.0.3",
3+
"version": "1.0.4",
44
"type": "module",
55
"description": "MCP server for interacting with Slack",
66
"keywords": [

src/server/client.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ import { readFileSync } from 'fs';
1010
import { dirname, join } from 'path';
1111
import { fileURLToPath } from 'url';
1212

13-
const slackApi = 'https://slack.com/api';
14-
1513
/**
1614
* Slack API client
1715
*
@@ -21,8 +19,12 @@ const slackApi = 'https://slack.com/api';
2119
* @class SlackClient
2220
*/
2321
export class SlackClient {
24-
private botHeaders: { Authorization: string; "Content-Type": string };
22+
private botHeaders: { Authorization: string; 'Content-Type': string };
2523
private rateLimiter: Map<string, number> = new Map();
24+
private readonly API = 'https://slack.com/api';
25+
private readonly MAX_LIMIT_CHANNELS = 200;
26+
private readonly MAX_LIMIT_HISTORY = 1000;
27+
private readonly MAX_LIMIT_USERS = 200;
2628
private readonly RATE_LIMIT_MAX_REQUESTS = 60;
2729
private readonly RATE_LIMIT_WINDOW = 60000;
2830

@@ -32,10 +34,7 @@ export class SlackClient {
3234
* @param {string} botToken - Slack bot token for API authentication
3335
*/
3436
constructor(botToken: string) {
35-
this.botHeaders = {
36-
Authorization: `Bearer ${botToken}`,
37-
'Content-Type': 'application/json'
38-
};
37+
this.botHeaders = { Authorization: `Bearer ${botToken}`, 'Content-Type': 'application/json' };
3938
}
4039

4140
/**
@@ -73,7 +72,7 @@ export class SlackClient {
7372
async addReaction(channel_id: string, timestamp: string, reaction: string): Promise<any> {
7473
this.checkRateLimit('addReaction');
7574
const sanitizedReaction = reaction.replace(/[^a-zA-Z0-9_]/g, '');
76-
const response = await fetch(`${slackApi}/reactions.add`, {
75+
const response = await fetch(`${this.API}/reactions.add`, {
7776
method: 'POST',
7877
headers: this.botHeaders,
7978
body: JSON.stringify({
@@ -95,13 +94,13 @@ export class SlackClient {
9594
*/
9695
async editMessage(channel_id: string, timestamp: string, text: string): Promise<any> {
9796
this.checkRateLimit('editMessage');
98-
const response = await fetch(`${slackApi}/chat.update`, {
97+
const response = await fetch(`${this.API}/chat.update`, {
9998
method: 'POST',
10099
headers: this.botHeaders,
101100
body: JSON.stringify({
102101
channel: channel_id,
103102
link_names: true,
104-
parse: "full",
103+
parse: 'full',
105104
text: text,
106105
ts: timestamp,
107106
unfurl_links: false,
@@ -122,10 +121,10 @@ export class SlackClient {
122121
this.checkRateLimit('getChannelHistory');
123122
const params = new URLSearchParams({
124123
channel: channel_id,
125-
limit: Math.min(limit, 1000).toString()
124+
limit: Math.min(limit, this.MAX_LIMIT_HISTORY).toString()
126125
});
127126
const response = await fetch(
128-
`${slackApi}/conversations.history?${params}`,
127+
`${this.API}/conversations.history?${params}`,
129128
{ headers: this.botHeaders }
130129
);
131130
return response.json();
@@ -143,7 +142,7 @@ export class SlackClient {
143142
channel: channel_id
144143
});
145144
const response = await fetch(
146-
`${slackApi}/conversations.info?${params}`,
145+
`${this.API}/conversations.info?${params}`,
147146
{ headers: this.botHeaders }
148147
);
149148
const result = await response.json();
@@ -164,26 +163,26 @@ export class SlackClient {
164163
const params = new URLSearchParams({
165164
types: 'public_channel',
166165
exclude_archived: 'true',
167-
limit: Math.min(limit, 200).toString(),
166+
limit: Math.min(limit, this.MAX_LIMIT_CHANNELS).toString(),
168167
team_id: process.env.SLACK_TEAM_ID!
169168
});
170169
if (cursor) {
171-
params.append("cursor", cursor);
170+
params.append('cursor', cursor);
172171
}
173172
const response = await fetch(
174-
`${slackApi}/conversations.list?${params}`,
173+
`${this.API}/conversations.list?${params}`,
175174
{ headers: this.botHeaders }
176175
);
177176
return response.json();
178177
}
179-
const predefinedChannelIdsArray = predefinedChannelIds.split(',').map((id: string) => id.trim());
178+
const channelIds = predefinedChannelIds.split(',').map((id: string) => id.trim());
180179
const channels = [];
181-
for (const channelId of predefinedChannelIdsArray) {
180+
for (const channelId of channelIds) {
182181
const params = new URLSearchParams({
183182
channel: channelId
184183
});
185184
const response = await fetch(
186-
`${slackApi}/conversations.info?${params}`,
185+
`${this.API}/conversations.info?${params}`,
187186
{ headers: this.botHeaders }
188187
);
189188
const data = await response.json();
@@ -212,7 +211,7 @@ export class SlackClient {
212211
ts: thread_ts
213212
});
214213
const response = await fetch(
215-
`${slackApi}/conversations.replies?${params}`,
214+
`${this.API}/conversations.replies?${params}`,
216215
{ headers: this.botHeaders }
217216
);
218217
return response.json();
@@ -230,7 +229,7 @@ export class SlackClient {
230229
user: user_id
231230
});
232231
const response = await fetch(
233-
`${slackApi}/users.info?${params}`,
232+
`${this.API}/users.info?${params}`,
234233
{ headers: this.botHeaders }
235234
);
236235
const result = await response.json();
@@ -250,7 +249,7 @@ export class SlackClient {
250249
include_labels: 'true'
251250
});
252251
const response = await fetch(
253-
`${slackApi}/users.profile.get?${params}`,
252+
`${this.API}/users.profile.get?${params}`,
254253
{ headers: this.botHeaders }
255254
);
256255
return response.json();
@@ -266,13 +265,13 @@ export class SlackClient {
266265
async getUsers(limit: number = 100, cursor?: string): Promise<any> {
267266
this.checkRateLimit('getUsers');
268267
const params = new URLSearchParams({
269-
limit: Math.min(limit, 200).toString(),
268+
limit: Math.min(limit, this.MAX_LIMIT_USERS).toString(),
270269
team_id: process.env.SLACK_TEAM_ID!
271270
});
272271
if (cursor) {
273-
params.append("cursor", cursor);
272+
params.append('cursor', cursor);
274273
}
275-
const response = await fetch(`${slackApi}/users.list?${params}`, {
274+
const response = await fetch(`${this.API}/users.list?${params}`, {
276275
headers: this.botHeaders
277276
});
278277
return response.json();
@@ -287,13 +286,13 @@ export class SlackClient {
287286
*/
288287
async postMessage(channel_id: string, text: string): Promise<any> {
289288
this.checkRateLimit('postMessage');
290-
const response = await fetch(`${slackApi}/chat.postMessage`, {
289+
const response = await fetch(`${this.API}/chat.postMessage`, {
291290
method: 'POST',
292291
headers: this.botHeaders,
293292
body: JSON.stringify({
294293
channel: channel_id,
295294
link_names: true,
296-
parse: "full",
295+
parse: 'full',
297296
text: text,
298297
unfurl_links: false,
299298
unfurl_media: false
@@ -316,7 +315,7 @@ export class SlackClient {
316315
const body: any = {
317316
channel: channel_id,
318317
link_names: true,
319-
parse: "full",
318+
parse: 'full',
320319
text: text,
321320
thread_ts: thread_ts,
322321
unfurl_links: false,
@@ -325,7 +324,7 @@ export class SlackClient {
325324
if (broadcast) {
326325
body.reply_broadcast = true;
327326
}
328-
const response = await fetch(`${slackApi}/chat.postMessage`, {
327+
const response = await fetch(`${this.API}/chat.postMessage`, {
329328
method: 'POST',
330329
headers: this.botHeaders,
331330
body: JSON.stringify(body)

0 commit comments

Comments
 (0)