yt-slack-mcp 0.4.0 → 0.5.0
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/LICENSE +21 -21
- package/README.md +31 -3
- package/index.js +41 -7
- package/package.json +1 -1
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Maksim Korinets
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Maksim Korinets
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@ A host-agnostic [Model Context Protocol](https://modelcontextprotocol.io) server
|
|
|
9
9
|
- Return image attachments inline, extract text from PDFs, and return `text/*` attachment contents inline.
|
|
10
10
|
- Retrieve private attachment contents through the authenticated Slack browser session.
|
|
11
11
|
- Reply to a thread, add or remove a reaction, edit an authored message or thread reply, or upload local files only in explicitly allowed channel IDs.
|
|
12
|
+
- Prefix every message the agent posts with `🤖:` — or `🤖 <model>:` when the call names the model — so agent replies stand apart from the account's own messages; an edit can opt out with `agent_authored: false`.
|
|
12
13
|
|
|
13
14
|
Attachment limits: images and text files are limited to 10 MB; text attachments are truncated at 100,000 characters. PDFs are limited to 20 MB, 50 pages, and 100,000 extracted characters.
|
|
14
15
|
|
|
@@ -19,11 +20,38 @@ Attachment limits: images and text files are limited to 10 MB; text attachments
|
|
|
19
20
|
| `slack_read_permalink` | Read a message or thread from a channel, DM, or group-DM permalink, including supported attachments. |
|
|
20
21
|
| `slack_get_replies` | Read replies for a thread by channel ID and timestamp. |
|
|
21
22
|
| `slack_get_history` | Read recent channel history. |
|
|
22
|
-
| `slack_reply_to_thread` | Reply to a thread; restricted to allowed channels. |
|
|
23
|
+
| `slack_reply_to_thread` | Reply to a thread, with optional `model`, in a `🤖:`-prefixed message; restricted to allowed channels. |
|
|
23
24
|
| `slack_add_reaction` | Add a reaction; restricted to allowed channels. |
|
|
24
25
|
| `slack_remove_reaction` | Remove a reaction added by this account; restricted to allowed channels. |
|
|
25
|
-
| `slack_upload_files` | Upload up to 10 local files to a channel or thread, with an optional
|
|
26
|
-
| `slack_edit_message` | Edit an authored message or thread reply by channel ID and timestamp; replaces the message text (Slack removes any existing Block Kit blocks) and returns the updated timestamp and permalink; restricted to allowed channels. |
|
|
26
|
+
| `slack_upload_files` | Upload up to 10 local files to a channel or thread, with an optional `initial_comment` prefixed with `🤖:` and an optional `model` (only used with a comment); returns file and message permalinks; restricted to allowed channels. |
|
|
27
|
+
| `slack_edit_message` | Edit an authored message or thread reply by channel ID and timestamp; replaces the message text with `🤖:`-prefixed text and an optional `model`, or with the text as given when `agent_authored` is `false` (Slack removes any existing Block Kit blocks) and returns the updated timestamp and permalink; restricted to allowed channels. |
|
|
28
|
+
|
|
29
|
+
## Agent message prefix
|
|
30
|
+
|
|
31
|
+
The write tools post through the account's own browser session, so Slack shows
|
|
32
|
+
the account name on them. To keep agent-written messages distinguishable, the
|
|
33
|
+
text of every posted message is prefixed with `🤖:` by default:
|
|
34
|
+
|
|
35
|
+
```text
|
|
36
|
+
🤖: deployed revision 4f2c1a9
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The MCP transport does not report which model is calling, so a write tool call
|
|
40
|
+
can pass `model` with the model it is running; the prefix then names it:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
🤖 claude-sonnet-4-5: deployed revision 4f2c1a9
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The prefix is added once: text that already starts with `🤖` is sent unchanged,
|
|
47
|
+
so editing a prefixed message does not stack prefixes. A file upload without an
|
|
48
|
+
`initial_comment` — or with only whitespace — gets no comment rather than a bare
|
|
49
|
+
prefix, and `model` has no effect then.
|
|
50
|
+
|
|
51
|
+
`slack_edit_message` can also replace a message with text that is not
|
|
52
|
+
agent-written, such as a correction to the user's own message: pass
|
|
53
|
+
`agent_authored: false` and the text is stored as given, without the prefix.
|
|
54
|
+
`model` has no effect when `agent_authored` is `false`.
|
|
27
55
|
|
|
28
56
|
## Setup
|
|
29
57
|
|
package/index.js
CHANGED
|
@@ -370,8 +370,33 @@ function assertAllowedWriteChannel(channelId, allowed) {
|
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
const AGENT_PREFIX_EMOJI = "🤖";
|
|
374
|
+
|
|
375
|
+
// Slack shows the account's own name on everything this server posts, so mark
|
|
376
|
+
// agent-written text with a robot emoji and name the model that wrote it when
|
|
377
|
+
// the caller says which one it is running. Text is trimmed and blank text has no
|
|
378
|
+
// prefix to carry, so an optional comment stays absent. Text that already
|
|
379
|
+
// carries the prefix is returned unchanged, so editing a prefixed message does
|
|
380
|
+
// not stack prefixes.
|
|
381
|
+
function withAgentPrefix(text, model) {
|
|
382
|
+
const value = typeof text === "string" ? text.trim() : "";
|
|
383
|
+
if (!value) {
|
|
384
|
+
return undefined;
|
|
385
|
+
}
|
|
386
|
+
if (value.startsWith(AGENT_PREFIX_EMOJI)) {
|
|
387
|
+
return value;
|
|
388
|
+
}
|
|
389
|
+
const name = typeof model === "string" ? model.trim() : "";
|
|
390
|
+
return `${AGENT_PREFIX_EMOJI}${name ? ` ${name}` : ""}: ${value}`;
|
|
391
|
+
}
|
|
392
|
+
|
|
373
393
|
// ── Schemas ─────────────────────────────────────────────────────────────────
|
|
374
394
|
|
|
395
|
+
const AgentModelField = z
|
|
396
|
+
.string()
|
|
397
|
+
.optional()
|
|
398
|
+
.describe("Model you are running, e.g. gpt-5.1, shown in the 🤖 prefix. Omit when unknown.");
|
|
399
|
+
|
|
375
400
|
const ReadPermalinkSchema = z.object({
|
|
376
401
|
url: z.string().describe("Slack permalink, e.g. https://<workspace>.slack.com/archives/C.../p..."),
|
|
377
402
|
});
|
|
@@ -391,6 +416,7 @@ const ReplyToThreadSchema = z.object({
|
|
|
391
416
|
channel_id: z.string(),
|
|
392
417
|
thread_ts: z.string(),
|
|
393
418
|
text: z.string(),
|
|
419
|
+
model: AgentModelField,
|
|
394
420
|
});
|
|
395
421
|
|
|
396
422
|
const AddReactionSchema = z.object({
|
|
@@ -404,12 +430,19 @@ const UploadFilesSchema = z.object({
|
|
|
404
430
|
file_paths: z.array(z.string().min(1)).min(1).max(10),
|
|
405
431
|
thread_ts: z.string().optional(),
|
|
406
432
|
initial_comment: z.string().optional(),
|
|
433
|
+
model: AgentModelField,
|
|
407
434
|
});
|
|
408
435
|
|
|
409
436
|
const EditMessageSchema = z.object({
|
|
410
437
|
channel_id: z.string(),
|
|
411
438
|
timestamp: z.string().describe("Timestamp of the message or thread reply to edit."),
|
|
412
439
|
text: z.string(),
|
|
440
|
+
agent_authored: z
|
|
441
|
+
.boolean()
|
|
442
|
+
.optional()
|
|
443
|
+
.default(true)
|
|
444
|
+
.describe("Set false when this text is not agent-written, for example correcting the user's own message, so it is stored without the 🤖 prefix; model is ignored when this is false."),
|
|
445
|
+
model: AgentModelField,
|
|
413
446
|
});
|
|
414
447
|
|
|
415
448
|
// ── Tool definitions ─────────────────────────────────────────────────────────
|
|
@@ -432,7 +465,7 @@ const TOOLS = [
|
|
|
432
465
|
},
|
|
433
466
|
{
|
|
434
467
|
name: "slack_reply_to_thread",
|
|
435
|
-
description: "Reply to a Slack thread. Restricted to allowed channels.",
|
|
468
|
+
description: "Reply to a Slack thread. The posted text is prefixed with `🤖:` — or `🤖 <model>:` when model is given — so readers can tell it came from an agent; pass model to name the model writing it. Restricted to allowed channels.",
|
|
436
469
|
inputSchema: zodToJsonSchema(ReplyToThreadSchema),
|
|
437
470
|
},
|
|
438
471
|
{
|
|
@@ -447,12 +480,12 @@ const TOOLS = [
|
|
|
447
480
|
},
|
|
448
481
|
{
|
|
449
482
|
name: "slack_upload_files",
|
|
450
|
-
description: "Upload one or more local files to a channel or thread. Restricted to allowed channels.",
|
|
483
|
+
description: "Upload one or more local files to a channel or thread. A given initial_comment is prefixed with `🤖:` — or `🤖 <model>:` when model is given — so readers can tell it came from an agent; pass model to name the model writing it, and note that model has no effect without initial_comment. Restricted to allowed channels.",
|
|
451
484
|
inputSchema: zodToJsonSchema(UploadFilesSchema),
|
|
452
485
|
},
|
|
453
486
|
{
|
|
454
487
|
name: "slack_edit_message",
|
|
455
|
-
description: "Edit an authored Slack message or thread reply by channel id and timestamp. Replaces the message text;
|
|
488
|
+
description: "Edit an authored Slack message or thread reply by channel id and timestamp. Replaces the message text, prefixed with `🤖:` — or `🤖 <model>:` when model is given — unless it already carries the prefix; pass model to name the model writing it, and set agent_authored to false when the new text is not agent-written. Any existing Block Kit blocks are removed by Slack. Restricted to allowed channels.",
|
|
456
489
|
inputSchema: zodToJsonSchema(EditMessageSchema),
|
|
457
490
|
},
|
|
458
491
|
];
|
|
@@ -500,7 +533,7 @@ function createExecuteToolCall({ slackApi, formatMessages, uploadSlackFiles, all
|
|
|
500
533
|
const response = await slackApi("chat.postMessage", {
|
|
501
534
|
channel: channelId,
|
|
502
535
|
thread_ts: requireString(a.thread_ts, "thread_ts"),
|
|
503
|
-
text: requireString(a.text, "text"),
|
|
536
|
+
text: withAgentPrefix(requireString(a.text, "text"), a.model),
|
|
504
537
|
}, "POST");
|
|
505
538
|
return { content: [{ type: "text", text: `posted reply at ${response.ts}` }] };
|
|
506
539
|
}
|
|
@@ -537,7 +570,7 @@ function createExecuteToolCall({ slackApi, formatMessages, uploadSlackFiles, all
|
|
|
537
570
|
filePaths: a.file_paths,
|
|
538
571
|
channelId,
|
|
539
572
|
threadTs: a.thread_ts && requireString(a.thread_ts, "thread_ts"),
|
|
540
|
-
initialComment: a.initial_comment,
|
|
573
|
+
initialComment: withAgentPrefix(a.initial_comment, a.model),
|
|
541
574
|
});
|
|
542
575
|
return {
|
|
543
576
|
content: files.map((file) => ({
|
|
@@ -558,7 +591,7 @@ function createExecuteToolCall({ slackApi, formatMessages, uploadSlackFiles, all
|
|
|
558
591
|
updated = await slackApi("chat.update", {
|
|
559
592
|
channel: channelId,
|
|
560
593
|
ts: timestamp,
|
|
561
|
-
text,
|
|
594
|
+
text: a.agent_authored ? withAgentPrefix(text, a.model) : text,
|
|
562
595
|
}, "POST");
|
|
563
596
|
} catch (error) {
|
|
564
597
|
const reason = String(error?.message ?? error).replace(/^slack chat\.update failed: /, "");
|
|
@@ -616,7 +649,7 @@ async function main() {
|
|
|
616
649
|
{
|
|
617
650
|
capabilities: { tools: {} },
|
|
618
651
|
instructions:
|
|
619
|
-
"MCP server for Slack. Read with slack_read_permalink, slack_get_replies, slack_get_history; write with slack_reply_to_thread, slack_add_reaction, slack_remove_reaction, slack_upload_files, slack_edit_message (restricted to allowed channels).",
|
|
652
|
+
"MCP server for Slack. Read with slack_read_permalink, slack_get_replies, slack_get_history; write with slack_reply_to_thread, slack_add_reaction, slack_remove_reaction, slack_upload_files, slack_edit_message (restricted to allowed channels). Messages sent by the write tools are prefixed with `🤖:` by default, and slack_edit_message can opt out with agent_authored false; pass model with the model you are running to include it in the prefix.",
|
|
620
653
|
}
|
|
621
654
|
);
|
|
622
655
|
|
|
@@ -661,6 +694,7 @@ export {
|
|
|
661
694
|
parsePermalink,
|
|
662
695
|
requireString,
|
|
663
696
|
assertAllowedWriteChannel,
|
|
697
|
+
withAgentPrefix,
|
|
664
698
|
createExecuteToolCall,
|
|
665
699
|
createSlackClient,
|
|
666
700
|
loadConfig,
|
package/package.json
CHANGED