@jtalk22/slack-mcp 4.1.2 → 4.2.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 CHANGED
@@ -4,7 +4,7 @@
4
4
  [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-blue)](https://registry.modelcontextprotocol.io)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- Give your AI agent full Slack access. No app registration, no admin approval, no OAuth. One command, 16 tools, works with any MCP client.
7
+ Give your AI agent full Slack access — and structured workflow output the AI can actually use. No app registration, no admin approval, no OAuth. One command, 21 tools, works with any MCP client.
8
8
 
9
9
  ```bash
10
10
  npx -y @jtalk22/slack-mcp --setup
@@ -39,9 +39,29 @@ This server uses your browser's session tokens instead. If you can see it in Sla
39
39
  | Works with Gemini CLI | No | **Yes** |
40
40
  | Works with Codex CLI | No | **Yes** |
41
41
  | Setup time | ~30 min | **~2 min** |
42
- | Tools | Limited | **16** |
42
+ | Tools | Limited | **21** |
43
43
  | Visible to admins | Yes | **No — session-token transport** |
44
44
 
45
+ ## Workflow Primitives (new in 4.2)
46
+
47
+ Save a workflow profile that binds a `workflow_kind` to channels + priority people + retention + cadence. Stored locally at `~/.slack-mcp-workflows.json`. The hosted brain at [mcp.revasserlabs.com](https://mcp.revasserlabs.com) reads these profiles and returns **structured JSON per workflow_kind** — downstream automation (Linear, Notion, status dashboards) consumes the JSON directly.
48
+
49
+ | `workflow_kind` | Returns (structured JSON) |
50
+ |---|---|
51
+ | `incident_room` | `{incident_summary, timeline, open_risks, owner_gaps, next_actions}` |
52
+ | `exec_brief` | `{summary, decisions, risks, asks, action_items}` |
53
+ | `support_inbox` | `{open_threads, ack_lag, owner_gaps, escalations, next_actions}` |
54
+ | `product_launch_watch` | `{launch_signals, feedback_themes, blockers, metrics, next_actions}` |
55
+ | `custom` | `{summary, highlights, open_questions, next_actions}` |
56
+
57
+ Six prebuilt templates ship with the package:
58
+
59
+ ```bash
60
+ npx -y @jtalk22/slack-mcp --apply-template oncall-handoff --channels C012345,C067890
61
+ ```
62
+
63
+ Available templates: `oncall-handoff`, `support-triage`, `exec-monday`, `sprint-tracker`, `customer-feedback`, `incident-room`. The structural primitives (`slack_workflow_save`, `slack_workflows`) are free forever in OSS; the hosted brain is `$0` to start (no card) and `$9/mo` Pro for unlimited AI tools (scheduled morning catch-up DM rolling out Q2 2026).
64
+
45
65
  ## Quick Start per Client
46
66
 
47
67
  <details>
@@ -129,11 +149,18 @@ Or via CLI: `codex mcp add slack -- npx -y @jtalk22/slack-mcp`
129
149
  | `slack_add_reaction` | Add an emoji reaction to a message | **destructive** |
130
150
  | `slack_remove_reaction` | Remove an emoji reaction from a message | **destructive** |
131
151
  | `slack_conversations_mark` | Mark a conversation as read | **destructive** |
152
+ | `slack_workflow_save` | Save a workflow profile (channels, kind, retention, cadence) to `~/.slack-mcp-workflows.json` | local-write |
153
+ | `slack_workflows` | List saved workflow profiles | read-only |
154
+ | `slack_smart_search` | Semantic search across indexed channels — hosted brain | hosted-stub† |
155
+ | `slack_catch_me_up` | AI-summarized digest of unreads + priority threads — hosted brain | hosted-stub† |
156
+ | `slack_triage` | Prioritized action queue across channels — hosted brain | hosted-stub† |
132
157
 
133
- 12 read-only, 4 write-path. All carry [MCP safety annotations](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#annotations).
158
+ 21 tools total: 12 read-only Slack, 4 write-path Slack, 2 workflow profile primitives (1 local-write, 1 read-only), 3 hosted stubs. All carry [MCP safety annotations](https://modelcontextprotocol.io/specification/2025-03-26/server/tools#annotations).
134
159
 
135
160
  \* `slack_refresh_tokens` modifies local token file only.
136
161
 
162
+ † Hosted stubs return a structured upgrade payload (`signup_url`, `free_tier_quota`, `pro_value_prop`) — no Slack write occurs from OSS. Activate the brain at [mcp.revasserlabs.com](https://mcp.revasserlabs.com) (free tier, no card).
163
+
137
164
  ## Install
138
165
 
139
166
  **Node.js 20+**
@@ -234,11 +261,11 @@ Hosted tiers at [mcp.revasserlabs.com](https://mcp.revasserlabs.com):
234
261
 
235
262
  | Tier | Price | What it owns |
236
263
  |------|-------|-------------|
237
- | Self-host | Free | Local stdio, all 16 tools, MIT licensed |
238
- | Solo | $19/mo | Managed endpoint + OAuth 2.1 bridge (required for Claude.ai web) + encrypted storage |
239
- | Team | $49/mo | Solo features + multi-seat routing |
240
- | Turnkey Team Launch | from $2,500+ | Dedicated instance + 30-day setup support |
241
- | Managed Reliability | from $800/mo+ | SLA-backed instance + incident response |
264
+ | Self-host | Free (MIT) | Local stdio, all 21 tools (16 read/write Slack + 2 workflow profile primitives + 3 discoverable upgrade stubs to hosted brain) |
265
+ | Hosted Free | $0 (no card) | Email signup, 1 workspace, 10 smart_search/mo + 3 catch_me_up/mo + 5 triage/day. All 5 workflow profile types. 7-day index retention. |
266
+ | Pro | $9/mo | Unlimited AI tools, **scheduled morning catch-up DM** *(rolling out Q2 2026, 8am workspace tz)*, permanent OAuth, 90-day Vectorize, 2 workspaces |
267
+ | Team | $49/mo flat | Pro + shared workflow profiles + audit log + 24h support + scheduled catch-up to channel + 5 workspaces |
268
+ | Ops | from $199/mo (custom) | SLA, custom retention, SOC2 evidence path, multi-tenant isolation, 10+ workspaces, dedicated workflow tuning |
242
269
 
243
270
  </details>
244
271
 
@@ -278,13 +305,15 @@ Session tokens (`xoxc-` + `xoxd-`) from your browser. If you can see it in Slack
278
305
 
279
306
  Tokens expire. The server notices before you do — proactive health monitoring, automatic refresh on macOS, warnings when tokens age out. File writes are atomic (temp file → chmod → rename) to prevent corruption. Concurrent refresh attempts are mutex-locked.
280
307
 
281
- ## What's New in 4.1.2
308
+ ## What's New in 4.2.0
282
309
 
283
- - **LevelDB extraction** — reads tokens directly from Chrome's LevelDB store. No live Slack tab required, no AppleScript flag dependency.
284
- - **Multi-profile enumeration** — automatically picks the freshest Chrome profile. Override with `SLACK_MCP_CHROME_USER_DATA_DIR`, `SLACK_MCP_CHROME_PROFILE`, or `SLACK_MCP_EXTRACTION_MODE`.
285
- - **Explicit shutdown handlers** — SIGTERM/SIGINT/SIGHUP/stdin EOF/stdin error all exit cleanly. Zero zombie processes.
310
+ - **Workflow primitives** — `slack_workflow_save` + `slack_workflows` bind a `workflow_kind` (`incident_room`, `exec_brief`, `support_inbox`, `product_launch_watch`, `custom`) to channels, priority people, retention, and cadence. The hosted brain returns structured JSON per kind — `incident_room` returns `{incident_summary, timeline, open_risks, owner_gaps, next_actions}`, `exec_brief` returns `{summary, decisions, risks, asks, action_items}`. Downstream automation (Linear, Notion, dashboards) consumes the JSON directly.
311
+ - **Discoverable upgrade stubs** — `slack_smart_search`, `slack_catch_me_up`, `slack_triage` appear in OSS as upgrade payloads pointing at the hosted brain. Response shape is `{signup_url, free_tier_quota, pro_value_prop}` no interruptions, the AI routes the user cleanly.
312
+ - **Six prebuilt templates** — apply with `npx -y @jtalk22/slack-mcp --apply-template <name> --channels C012,C034`. Names: `oncall-handoff`, `support-triage`, `exec-monday`, `sprint-tracker`, `customer-feedback`, `incident-room`. Read them, fork them, edit them — they're JSON profiles.
313
+ - **Setup wizard hosted bridge** — six in-wizard moments surface the hosted free tier (no card) where it matches the user's pain. Stays out of the way otherwise.
314
+ - **Prior reliability fixes carried forward** — LevelDB token extraction, multi-profile enumeration, and explicit SIGTERM/SIGINT/SIGHUP/stdin shutdown handlers ship in 4.2.0 too.
286
315
 
287
- Full release notes in [docs/INDEX.md](docs/INDEX.md) and on [GitHub releases/latest](https://github.com/jtalk22/slack-mcp-server/releases/latest).
316
+ Full release notes on [GitHub releases/latest](https://github.com/jtalk22/slack-mcp-server/releases/latest).
288
317
 
289
318
  ## Hosted HTTP Mode
290
319
 
@@ -342,4 +371,4 @@ Not affiliated with Slack Technologies, Inc. Uses browser session credentials
342
371
 
343
372
  ---
344
373
 
345
- Hosted tiers live at [mcp.revasserlabs.com](https://mcp.revasserlabs.com): Solo $19/mo, Team $49/mo, Turnkey Team Launch from $2,500+, Managed Reliability from $800/mo+. Hosted owns the managed MCP endpoint, the OAuth 2.1 bridge into Claude.ai, encrypted credential storage, and the structural absence of the zombie-process class. It does not replace Chrome the user still pastes `xoxc-`/`xoxd-` at setup.
374
+ Hosted version live at [mcp.revasserlabs.com](https://mcp.revasserlabs.com): Free tier (no card), $9/mo Pro, $49/mo Team flat, Ops from $199/mo. Hosted owns the AI brain (smart_search, catch_me_up, triage), the scheduled morning catch-up DM at 8am workspace time *(rolling out Q2 2026)*, permanent OAuth (no 2-week token rotation), 90-day Vectorize retention, and shared workflow profiles. The OSS package owns local stdio + all 16 read/write Slack tools + workflow profile primitives (slack_workflow_save, slack_workflows). The 3 paid stubs (slack_smart_search, slack_catch_me_up, slack_triage) appear in OSS as discoverable upgrade prompts.
@@ -0,0 +1,56 @@
1
+ # Deployment Modes
2
+
3
+ Use this guide to choose the right operating mode before rollout.
4
+
5
+ ## Quick Chooser
6
+
7
+ - Choose `stdio` for personal self-hosted use in Claude Desktop/Claude Code.
8
+ - Choose local `web` for browser workflows and manual Slack browsing.
9
+ - Choose hosted HTTP only when you need remote execution and can handle token operations.
10
+ - Choose Smithery/Worker only when your consumers require registry-hosted MCP transport.
11
+ - A managed **Hosted** version is live at [mcp.revasserlabs.com](https://mcp.revasserlabs.com) — Free tier (no card), Pro $9/mo, Team $49/mo flat, Ops from $199/mo. See [pricing](https://mcp.revasserlabs.com/pricing).
12
+
13
+ ## Mode Matrix
14
+
15
+ | Mode | Start Command | Best For | Auth Material | Exposure | Notes |
16
+ |------|---------------|----------|---------------|----------|-------|
17
+ | Local MCP (`stdio`) | `npx -y @jtalk22/slack-mcp` | Individual daily usage in Claude | `SLACK_TOKEN` + `SLACK_COOKIE` via token file/env | Local process | Lowest ops burden. Free. 21 tools (16 read/write + 2 workflow profile primitives + 3 discoverable upgrade stubs to hosted). |
18
+ | Local Web UI (`web`) | `npx -y @jtalk22/slack-mcp web` | Browser-first usage, manual search/send | Same as above + generated API key | `localhost` by default | Useful when MCP is not available |
19
+ | Hosted MCP (`http`) | `node src/server-http.js` | Controlled hosted integration | Env-injected Slack token/cookie + HTTP bearer token | Remote endpoint | `/mcp` is bearer-protected by default; configure CORS allowlist |
20
+ | Smithery/Worker | `wrangler deploy` + Smithery publish flow | Registry distribution for hosted consumers | Query/env token handoff | Remote endpoint | Keep worker version parity with npm release |
21
+
22
+ ## Team Deployment Guidance
23
+
24
+ If you are deploying for more than one operator:
25
+
26
+ 1. Start with one maintainer on local `stdio`.
27
+ 2. Document token lifecycle and rotation ownership.
28
+ 3. Define support window and incident contact before enabling hosted mode.
29
+ 4. Validate `/health` and MCP initialize responses on every release.
30
+
31
+ ## Release Checklist by Mode
32
+
33
+ ### Local `stdio`
34
+
35
+ 1. `npx -y @jtalk22/slack-mcp --status`
36
+ 2. `npx -y @jtalk22/slack-mcp --help`
37
+ 3. Confirm tool list in Claude client.
38
+
39
+ ### Local `web`
40
+
41
+ 1. `npx -y @jtalk22/slack-mcp web`
42
+ 2. Verify API key generation at `~/.slack-mcp-api-key`.
43
+ 3. Verify `/health`, `/conversations`, and `/search` endpoints.
44
+
45
+ ### Hosted (`http` or Worker)
46
+
47
+ 1. Verify `version` parity across `package.json`, server metadata, and health responses.
48
+ 2. Verify HTTP auth behavior:
49
+ - missing `SLACK_MCP_HTTP_AUTH_TOKEN` returns `503`
50
+ - bad bearer token returns `401`
51
+ - valid bearer token succeeds
52
+ 3. Verify CORS behavior:
53
+ - denied by default
54
+ - allowed origins work when listed in `SLACK_MCP_HTTP_ALLOWED_ORIGINS`
55
+ 4. Confirm `slack_get_thread`, `slack_search_messages`, and `slack_users_info` behavior.
56
+ 5. Confirm token handling mode (ephemeral vs env persistence) is documented.
@@ -31,7 +31,9 @@ If `--version` fails here, the issue is install/runtime path, not Slack credenti
31
31
 
32
32
  ## Hosted Version
33
33
 
34
- A hosted version with permanent OAuth tokens, semantic search, and AI summaries is coming soon at [mcp.revasserlabs.com](https://mcp.revasserlabs.com). The current release is self-hosted only.
34
+ The hosted version is live at [mcp.revasserlabs.com](https://mcp.revasserlabs.com). Free tier (no card) ships 10 smart_search/mo + 3 catch_me_up/mo + 5 triage/day + all 5 workflow profile types. Pro at $9/mo unlocks unlimited AI tools, the scheduled morning catch-up DM at 8am workspace time *(rolling out Q2 2026)*, permanent OAuth (no 2-week token rotation), and 90-day Vectorize retention. Team at $49/mo flat covers 5 workspaces with shared workflow profiles and audit log. Ops engagement starts at $199/mo (custom) for 10+ workspace organizations with SLA, custom retention, SOC2 evidence, or multi-tenant isolation.
35
+
36
+ The OSS package keeps the local-machine path. The hosted version adds the AI brain (smart_search, catch_me_up, triage) — these tools also appear in the OSS package as discoverable upgrade stubs that point at the hosted signup.
35
37
 
36
38
  ---
37
39
 
package/lib/handlers.js CHANGED
@@ -16,6 +16,11 @@ import {
16
16
  getLastExtractionError
17
17
  } from "./token-store.js";
18
18
  import { slackAPI, resolveUser, formatTimestamp, sleep, checkTokenHealth, getUserCacheStats } from "./slack-client.js";
19
+ import {
20
+ saveProfile as workflowSaveProfile,
21
+ listProfiles as workflowListProfiles,
22
+ ALLOWED_WORKFLOW_KINDS_LIST,
23
+ } from "./workflow-store.js";
19
24
 
20
25
  // ============ Utilities ============
21
26
 
@@ -773,3 +778,90 @@ export async function handleUsersSearch(args) {
773
778
  users: allUsers.slice(0, limit)
774
779
  });
775
780
  }
781
+
782
+ // ============ Workflow Profile Primitives (OSS) ============
783
+
784
+ /**
785
+ * Save (or update) a workflow profile to ~/.slack-mcp-workflows.json
786
+ * Profile binds a workflow_kind to channels + priority_people + retention + cadence.
787
+ */
788
+ export async function handleWorkflowSave(args) {
789
+ const safeArgs = args || {};
790
+ const result = workflowSaveProfile({
791
+ profile_name: safeArgs.profile_name,
792
+ workflow_kind: safeArgs.workflow_kind,
793
+ channels: safeArgs.channels,
794
+ priority_people: safeArgs.priority_people,
795
+ retention_mode: safeArgs.retention_mode,
796
+ summary_cadence: safeArgs.summary_cadence,
797
+ });
798
+ if (!result.ok) {
799
+ return asMcpJson({ error: "invalid_workflow_profile", errors: result.errors }, true);
800
+ }
801
+ return asMcpJson({
802
+ ok: true,
803
+ profile_name: result.profile_name,
804
+ profile: result.profile,
805
+ note: "Profile saved locally. Hosted free tier syncs profiles automatically when you connect your account; OSS-only mode keeps them on your machine.",
806
+ });
807
+ }
808
+
809
+ /**
810
+ * List saved workflow profiles, optionally filtered by workflow_kind.
811
+ */
812
+ export async function handleWorkflows(args) {
813
+ const result = workflowListProfiles({ workflow_kind: args && args.workflow_kind });
814
+ if (!result.ok) {
815
+ return asMcpJson({ error: "invalid_workflow_filter", errors: result.errors }, true);
816
+ }
817
+ return asMcpJson({
818
+ ok: true,
819
+ count: result.profiles.length,
820
+ workflow_kinds: ALLOWED_WORKFLOW_KINDS_LIST,
821
+ profiles: result.profiles,
822
+ });
823
+ }
824
+
825
+ // ============ Hosted-Only AI Tool Stubs ============
826
+ // These three tools appear in the OSS package's MCP tool list so users
827
+ // discover them. The handlers return a structured upgrade message —
828
+ // actual execution happens on the hosted worker (mcp.revasserlabs.com).
829
+
830
+ const HOSTED_UPGRADE_PAYLOAD = {
831
+ error: "tool_requires_hosted",
832
+ message: "This tool needs hosted mode (Vectorize + Workers AI). Get free monthly credits at mcp.revasserlabs.com — no card required.",
833
+ signup_url: "https://mcp.revasserlabs.com/signup",
834
+ upgrade_url: "https://mcp.revasserlabs.com/pricing",
835
+ free_tier_quota: "10 smart_search + 3 catch_me_up per month, 5 triage per day",
836
+ pro_value_prop: "Pro $9/mo unlocks unlimited AI tools (scheduled morning catch-up DM at 8am workspace time rolling out Q2 2026).",
837
+ };
838
+
839
+ export async function handleSmartSearch(args) {
840
+ return asMcpJson(
841
+ {
842
+ ...HOSTED_UPGRADE_PAYLOAD,
843
+ requested: { tool: "slack_smart_search", args: args || {} },
844
+ },
845
+ true
846
+ );
847
+ }
848
+
849
+ export async function handleCatchMeUp(args) {
850
+ return asMcpJson(
851
+ {
852
+ ...HOSTED_UPGRADE_PAYLOAD,
853
+ requested: { tool: "slack_catch_me_up", args: args || {} },
854
+ },
855
+ true
856
+ );
857
+ }
858
+
859
+ export async function handleTriage(args) {
860
+ return asMcpJson(
861
+ {
862
+ ...HOSTED_UPGRADE_PAYLOAD,
863
+ requested: { tool: "slack_triage", args: args || {} },
864
+ },
865
+ true
866
+ );
867
+ }
@@ -18,5 +18,5 @@ export const PUBLIC_METADATA = Object.freeze({
18
18
  cloudSupportUrl: "https://mcp.revasserlabs.com/support",
19
19
  cloudStatusUrl: "https://mcp.revasserlabs.com/status",
20
20
  supportEmail: "support@revasserlabs.com",
21
- selfHostedToolCount: 16,
21
+ selfHostedToolCount: 21,
22
22
  });
@@ -63,7 +63,7 @@ function shareLinks() {
63
63
  }
64
64
 
65
65
  function shareNote() {
66
- return `<strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives ${PUBLIC_METADATA.selfHostedToolCount} tools with session-based auth. Works with any MCP client — Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf. Hosted version coming soon at <a href="${PUBLIC_METADATA.canonicalSiteUrl}">mcp.revasserlabs.com</a>.`;
66
+ return `<strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives ${PUBLIC_METADATA.selfHostedToolCount} tools with session-based auth. Works with any MCP client — Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf. Hosted free tier (no card) live at <a href="${PUBLIC_METADATA.canonicalSiteUrl}">mcp.revasserlabs.com</a> — Pro $9/mo unlocks unlimited AI tools (scheduled morning catch-up DM rolling out Q2 2026).`;
67
67
  }
68
68
 
69
69
  function demoLinks() {
@@ -75,7 +75,7 @@ function demoLinks() {
75
75
  }
76
76
 
77
77
  function demoNote() {
78
- return `Self-host free for ${PUBLIC_METADATA.selfHostedToolCount} tools with session-based auth. Works with Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf, and any other MCP client. No OAuth app, no admin approval. Hosted version coming soon at <a href="${PUBLIC_METADATA.canonicalSiteUrl}" target="_blank" rel="noopener noreferrer">mcp.revasserlabs.com</a>.`;
78
+ return `Self-host free for ${PUBLIC_METADATA.selfHostedToolCount} tools with session-based auth. Works with Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf, and any other MCP client. No OAuth app, no admin approval. Hosted free tier (no card) live at <a href="${PUBLIC_METADATA.canonicalSiteUrl}" target="_blank" rel="noopener noreferrer">mcp.revasserlabs.com</a> — Pro $9/mo unlocks unlimited AI tools (scheduled morning catch-up DM rolling out Q2 2026).`;
79
79
  }
80
80
 
81
81
  function demoFooterLinks() {
@@ -113,8 +113,8 @@ function commonTokens() {
113
113
  SELF_HOSTED_TOOL_COUNT: String(PUBLIC_METADATA.selfHostedToolCount),
114
114
  CLOUD_MANAGED_TOOL_COUNT: "15",
115
115
  TEAM_AI_WORKFLOW_COUNT: "3",
116
- CLOUD_SOLO_PRICE: "coming soon",
117
- CLOUD_TEAM_PRICE: "coming soon",
116
+ CLOUD_SOLO_PRICE: "$9/mo",
117
+ CLOUD_TEAM_PRICE: "$49/mo",
118
118
  CLOUD_TURNKEY_LAUNCH_PRICE: "contact us",
119
119
  CLOUD_MANAGED_RELIABILITY_PRICE: "contact us",
120
120
  SUPPORT_EMAIL: PUBLIC_METADATA.supportEmail,
package/lib/tools.js CHANGED
@@ -399,5 +399,165 @@ export const TOOLS = [
399
399
  idempotentHint: true,
400
400
  openWorldHint: true
401
401
  }
402
+ },
403
+ // ============ Workflow Profile Primitives (OSS) ============
404
+ // Local JSON storage at ~/.slack-mcp-workflows.json. Defines the
405
+ // structural primitive that the hosted AI brain (smart_search,
406
+ // catch_me_up, triage) consumes to return structured JSON.
407
+ {
408
+ name: "slack_workflow_save",
409
+ description: "Save or update a workflow profile that binds a workflow_kind (support_inbox | incident_room | exec_brief | product_launch_watch | custom) to channels, priority people, retention mode, and summary cadence. Stored locally at ~/.slack-mcp-workflows.json. Hosted brain reads these to return structured JSON per the workflow_kind.",
410
+ inputSchema: {
411
+ type: "object",
412
+ properties: {
413
+ profile_name: {
414
+ type: "string",
415
+ description: "Unique name for this workflow profile (e.g. 'morning-exec-brief', 'on-call-rotation')"
416
+ },
417
+ workflow_kind: {
418
+ type: "string",
419
+ enum: ["support_inbox", "incident_room", "exec_brief", "product_launch_watch", "custom"],
420
+ description: "Workflow kind. Determines structured JSON output shape from the hosted AI brain."
421
+ },
422
+ channels: {
423
+ type: "array",
424
+ items: { type: "string" },
425
+ description: "Slack channel IDs to read (e.g. ['C012345', 'C067890'])"
426
+ },
427
+ priority_people: {
428
+ type: "array",
429
+ items: { type: "string" },
430
+ description: "Slack user IDs whose messages get extra weight in summaries"
431
+ },
432
+ retention_mode: {
433
+ type: "string",
434
+ enum: ["ephemeral", "persistent"],
435
+ description: "Token retention mode for hosted execution. Default ephemeral."
436
+ },
437
+ summary_cadence: {
438
+ type: "string",
439
+ enum: ["on_demand", "daily_8am", "weekly_monday"],
440
+ description: "When the hosted brain auto-runs slack_catch_me_up against this profile. on_demand only on hosted free; daily_8am and weekly_monday require Pro or Team."
441
+ }
442
+ },
443
+ required: ["profile_name", "workflow_kind"]
444
+ },
445
+ annotations: {
446
+ title: "Save Workflow Profile",
447
+ readOnlyHint: false,
448
+ destructiveHint: false,
449
+ idempotentHint: true,
450
+ openWorldHint: false
451
+ }
452
+ },
453
+ {
454
+ name: "slack_workflows",
455
+ description: "List all saved workflow profiles from ~/.slack-mcp-workflows.json. Optionally filter by workflow_kind. Returns profile_name, channels, priority_people, retention_mode, summary_cadence, structured_keys, created_at, updated_at.",
456
+ inputSchema: {
457
+ type: "object",
458
+ properties: {
459
+ workflow_kind: {
460
+ type: "string",
461
+ enum: ["support_inbox", "incident_room", "exec_brief", "product_launch_watch", "custom"],
462
+ description: "Optional filter — return only profiles of this workflow_kind"
463
+ }
464
+ }
465
+ },
466
+ annotations: {
467
+ title: "List Workflow Profiles",
468
+ readOnlyHint: true,
469
+ idempotentHint: true,
470
+ openWorldHint: false
471
+ }
472
+ },
473
+ // ============ Hosted-Only AI Tools (OSS = upgrade stubs) ============
474
+ // These tool definitions appear in every MCP client's tool list so users
475
+ // discover them. The OSS handlers return a structured upgrade message
476
+ // pointing at mcp.revasserlabs.com — the hosted worker actually runs them.
477
+ {
478
+ name: "slack_smart_search",
479
+ description: "Semantic + lexical hybrid search across your indexed Slack history. Returns ranked results with relevance scores, channel context, thread context, and matched terms. Hosted-only (requires Vectorize + Workers AI). Free tier ships 10 calls/month; upgrade to Pro $9/mo for unlimited at mcp.revasserlabs.com/pricing.",
480
+ inputSchema: {
481
+ type: "object",
482
+ properties: {
483
+ query: {
484
+ type: "string",
485
+ description: "Natural language or keyword query (semantic + lexical hybrid)"
486
+ },
487
+ channel_ids: {
488
+ type: "array",
489
+ items: { type: "string" },
490
+ description: "Optional — restrict search to these channel IDs"
491
+ },
492
+ days_back: {
493
+ type: "number",
494
+ description: "Optional — restrict search to the last N days (max 90 on Pro+, 7 on Free)"
495
+ },
496
+ limit: {
497
+ type: "number",
498
+ description: "Maximum results to return (default 10, max 50)"
499
+ }
500
+ },
501
+ required: ["query"]
502
+ },
503
+ annotations: {
504
+ title: "Smart Search (hosted)",
505
+ readOnlyHint: true,
506
+ idempotentHint: true,
507
+ openWorldHint: true
508
+ }
509
+ },
510
+ {
511
+ name: "slack_catch_me_up",
512
+ description: "Run a structured catch-up against a saved workflow profile. Returns structured JSON per the profile's workflow_kind: support_inbox returns {open_threads, ack_lag, owner_gaps, escalations, next_actions}; incident_room returns {incident_summary, timeline, open_risks, owner_gaps, next_actions}; exec_brief returns {summary, decisions, risks, asks, action_items}; product_launch_watch returns {launch_signals, feedback_themes, blockers, metrics, next_actions}; custom returns {summary, highlights, open_questions, next_actions}. Hosted-only. Free tier ships 3 calls/month; Pro $9/mo unlocks unlimited (scheduled morning DM at 8am workspace tz rolling out Q2 2026).",
513
+ inputSchema: {
514
+ type: "object",
515
+ properties: {
516
+ profile_name: {
517
+ type: "string",
518
+ description: "Name of a workflow profile saved via slack_workflow_save (or use --apply-template at install time to seed one)"
519
+ },
520
+ since: {
521
+ type: "string",
522
+ description: "Optional ISO8601 timestamp — only consider Slack messages newer than this. Default: 24 hours ago for daily-cadence profiles, 7 days for weekly."
523
+ }
524
+ },
525
+ required: ["profile_name"]
526
+ },
527
+ annotations: {
528
+ title: "Catch Me Up (hosted)",
529
+ readOnlyHint: true,
530
+ idempotentHint: false,
531
+ openWorldHint: true
532
+ }
533
+ },
534
+ {
535
+ name: "slack_triage",
536
+ description: "Classify and route Slack threads against a workflow profile. Returns triage decisions per thread: priority (low|medium|high|urgent), suggested owner, escalation flag, time-sensitivity, and a routing recommendation. Hosted-only. Free tier ships 5 triage runs per day; Pro $9/mo unlocks unlimited.",
537
+ inputSchema: {
538
+ type: "object",
539
+ properties: {
540
+ profile_name: {
541
+ type: "string",
542
+ description: "Name of a workflow profile saved via slack_workflow_save"
543
+ },
544
+ channel_ids: {
545
+ type: "array",
546
+ items: { type: "string" },
547
+ description: "Optional — restrict triage to these channels (defaults to profile's channels)"
548
+ },
549
+ thread_ts: {
550
+ type: "string",
551
+ description: "Optional — triage a specific thread instead of the full inbox"
552
+ }
553
+ },
554
+ required: ["profile_name"]
555
+ },
556
+ annotations: {
557
+ title: "Triage (hosted)",
558
+ readOnlyHint: true,
559
+ idempotentHint: false,
560
+ openWorldHint: true
561
+ }
402
562
  }
403
563
  ];
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Workflow Profile Store (local JSON, OSS-only)
3
+ *
4
+ * Stores user-defined workflow profiles that bind a workflow_kind
5
+ * (support_inbox | incident_room | exec_brief | product_launch_watch | custom)
6
+ * to a set of channels, priority people, retention mode, and summary cadence.
7
+ *
8
+ * The hosted AI brain (smart_search, catch_me_up, triage) consumes these
9
+ * profiles and returns structured JSON per the workflow_kind. The OSS
10
+ * package ships the profile primitives but not the AI brain — it's
11
+ * hosted-only because it needs Vectorize + Workers AI.
12
+ *
13
+ * File: ~/.slack-mcp-workflows.json (chmod 600)
14
+ * Atomic write pattern (temp → chmod → rename) prevents corruption from
15
+ * concurrent writes by multiple slack-mcp-server instances.
16
+ */
17
+
18
+ import { writeFileSync, readFileSync, existsSync, renameSync, unlinkSync, chmodSync } from "fs";
19
+ import { homedir, platform } from "os";
20
+ import { join } from "path";
21
+
22
+ const STORE_FILE = join(homedir(), ".slack-mcp-workflows.json");
23
+ const STORE_VERSION = 1;
24
+
25
+ const ALLOWED_WORKFLOW_KINDS = new Set([
26
+ "support_inbox",
27
+ "incident_room",
28
+ "exec_brief",
29
+ "product_launch_watch",
30
+ "custom",
31
+ ]);
32
+
33
+ const ALLOWED_RETENTION_MODES = new Set(["ephemeral", "persistent"]);
34
+ const ALLOWED_SUMMARY_CADENCES = new Set(["on_demand", "daily_8am", "weekly_monday"]);
35
+
36
+ const STRUCTURED_KEYS_BY_KIND = {
37
+ support_inbox: ["open_threads", "ack_lag", "owner_gaps", "escalations", "next_actions"],
38
+ incident_room: ["incident_summary", "timeline", "open_risks", "owner_gaps", "next_actions"],
39
+ exec_brief: ["summary", "decisions", "risks", "asks", "action_items"],
40
+ product_launch_watch: ["launch_signals", "feedback_themes", "blockers", "metrics", "next_actions"],
41
+ custom: ["summary", "highlights", "open_questions", "next_actions"],
42
+ };
43
+
44
+ function emptyStore() {
45
+ return { version: STORE_VERSION, profiles: {} };
46
+ }
47
+
48
+ function atomicWriteSync(filePath, content) {
49
+ const tempPath = `${filePath}.${process.pid}.tmp`;
50
+ try {
51
+ writeFileSync(tempPath, content);
52
+ if (platform() === "darwin" || platform() === "linux") {
53
+ try { chmodSync(tempPath, 0o600); } catch {}
54
+ }
55
+ renameSync(tempPath, filePath);
56
+ } catch (e) {
57
+ try { unlinkSync(tempPath); } catch {}
58
+ throw e;
59
+ }
60
+ }
61
+
62
+ export function loadStore() {
63
+ if (!existsSync(STORE_FILE)) return emptyStore();
64
+ let raw;
65
+ try {
66
+ raw = readFileSync(STORE_FILE, "utf-8");
67
+ } catch {
68
+ return emptyStore();
69
+ }
70
+ try {
71
+ const data = JSON.parse(raw);
72
+ if (!data || typeof data !== "object" || !data.profiles) {
73
+ backupCorruptStore(raw, "shape-invalid");
74
+ return emptyStore();
75
+ }
76
+ if (data.version !== STORE_VERSION) {
77
+ // Future: migration logic. For now, back up the unrecognized version
78
+ // before falling back to empty so the old data is recoverable.
79
+ backupCorruptStore(raw, `version-${data.version}`);
80
+ return emptyStore();
81
+ }
82
+ return data;
83
+ } catch {
84
+ backupCorruptStore(raw, "json-parse-error");
85
+ return emptyStore();
86
+ }
87
+ }
88
+
89
+ function backupCorruptStore(raw, reasonTag) {
90
+ try {
91
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
92
+ const backupPath = `${STORE_FILE}.bak.${reasonTag}.${stamp}`;
93
+ writeFileSync(backupPath, raw);
94
+ if (platform() === "darwin" || platform() === "linux") {
95
+ try { chmodSync(backupPath, 0o600); } catch {}
96
+ }
97
+ } catch {
98
+ // Backup is best-effort; do not throw on backup failure.
99
+ }
100
+ }
101
+
102
+ export function saveStore(store) {
103
+ atomicWriteSync(STORE_FILE, JSON.stringify(store, null, 2));
104
+ }
105
+
106
+ export function structuredKeysFor(workflowKind) {
107
+ return STRUCTURED_KEYS_BY_KIND[workflowKind] || STRUCTURED_KEYS_BY_KIND.custom;
108
+ }
109
+
110
+ export function validateProfile(input) {
111
+ const errors = [];
112
+ if (!input || typeof input !== "object") {
113
+ return { valid: false, errors: ["profile must be an object"] };
114
+ }
115
+ if (!input.profile_name || typeof input.profile_name !== "string" || !input.profile_name.trim()) {
116
+ errors.push("profile_name is required (non-empty string)");
117
+ }
118
+ if (!input.workflow_kind || !ALLOWED_WORKFLOW_KINDS.has(input.workflow_kind)) {
119
+ errors.push(`workflow_kind must be one of: ${Array.from(ALLOWED_WORKFLOW_KINDS).join(", ")}`);
120
+ }
121
+ if (input.channels && (!Array.isArray(input.channels) || input.channels.some((c) => typeof c !== "string"))) {
122
+ errors.push("channels must be an array of strings (Slack channel IDs)");
123
+ }
124
+ if (input.priority_people && (!Array.isArray(input.priority_people) || input.priority_people.some((p) => typeof p !== "string"))) {
125
+ errors.push("priority_people must be an array of strings (Slack user IDs)");
126
+ }
127
+ if (input.retention_mode && !ALLOWED_RETENTION_MODES.has(input.retention_mode)) {
128
+ errors.push(`retention_mode must be one of: ${Array.from(ALLOWED_RETENTION_MODES).join(", ")}`);
129
+ }
130
+ if (input.summary_cadence && !ALLOWED_SUMMARY_CADENCES.has(input.summary_cadence)) {
131
+ errors.push(`summary_cadence must be one of: ${Array.from(ALLOWED_SUMMARY_CADENCES).join(", ")}`);
132
+ }
133
+ return { valid: errors.length === 0, errors };
134
+ }
135
+
136
+ export function saveProfile(input) {
137
+ const { valid, errors } = validateProfile(input);
138
+ if (!valid) {
139
+ return { ok: false, errors };
140
+ }
141
+ const store = loadStore();
142
+ const now = new Date().toISOString();
143
+ const existing = store.profiles[input.profile_name];
144
+ const profile = {
145
+ workflow_kind: input.workflow_kind,
146
+ channels: Array.isArray(input.channels) ? [...input.channels] : [],
147
+ priority_people: Array.isArray(input.priority_people) ? [...input.priority_people] : [],
148
+ retention_mode: input.retention_mode || "ephemeral",
149
+ summary_cadence: input.summary_cadence || "on_demand",
150
+ structured_keys: structuredKeysFor(input.workflow_kind),
151
+ created_at: existing && existing.created_at ? existing.created_at : now,
152
+ updated_at: now,
153
+ };
154
+ store.profiles[input.profile_name] = profile;
155
+ saveStore(store);
156
+ return { ok: true, profile_name: input.profile_name, profile };
157
+ }
158
+
159
+ export function listProfiles({ workflow_kind } = {}) {
160
+ const store = loadStore();
161
+ const entries = Object.entries(store.profiles).map(([name, profile]) => ({ profile_name: name, ...profile }));
162
+ if (workflow_kind) {
163
+ if (!ALLOWED_WORKFLOW_KINDS.has(workflow_kind)) {
164
+ return { ok: false, errors: [`workflow_kind filter must be one of: ${Array.from(ALLOWED_WORKFLOW_KINDS).join(", ")}`] };
165
+ }
166
+ return { ok: true, profiles: entries.filter((p) => p.workflow_kind === workflow_kind) };
167
+ }
168
+ return { ok: true, profiles: entries };
169
+ }
170
+
171
+ export function deleteProfile(profile_name) {
172
+ const store = loadStore();
173
+ if (!store.profiles[profile_name]) {
174
+ return { ok: false, errors: [`profile_name "${profile_name}" not found`] };
175
+ }
176
+ delete store.profiles[profile_name];
177
+ saveStore(store);
178
+ return { ok: true, profile_name };
179
+ }
180
+
181
+ export function getProfile(profile_name) {
182
+ const store = loadStore();
183
+ const profile = store.profiles[profile_name];
184
+ if (!profile) return null;
185
+ return { profile_name, ...profile };
186
+ }
187
+
188
+ export const ALLOWED_WORKFLOW_KINDS_LIST = Array.from(ALLOWED_WORKFLOW_KINDS);
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@jtalk22/slack-mcp",
3
3
  "mcpName": "io.github.jtalk22/slack-mcp-server",
4
- "version": "4.1.2",
5
- "description": "Slack MCP without OAuth no app registration, no admin approval. Works with Claude Code, Cursor, Copilot (where the official server doesn't). 16 tools, one command.",
4
+ "version": "4.2.1",
5
+ "description": "Slack MCP without OAuth. 21 tools (16 read/write Slack + 2 workflow profile primitives + 3 hosted-brain upgrade stubs). Free OSS or hosted (free tier no card; $9/mo Pro = unlimited; morning DM rolling out Q2 2026).",
6
6
  "type": "module",
7
7
  "main": "src/server.js",
8
8
  "bin": {
@@ -90,7 +90,7 @@
90
90
  },
91
91
  "dependencies": {
92
92
  "@modelcontextprotocol/sdk": "^1.27.0",
93
- "express": "^4.18.2"
93
+ "express": "^5.2.1"
94
94
  },
95
95
  "files": [
96
96
  "src/",
@@ -99,9 +99,12 @@
99
99
  "public/share.html",
100
100
  "scripts/setup-wizard.js",
101
101
  "scripts/token-cli.js",
102
+ "scripts/apply-template.js",
103
+ "templates/workflow-profiles/",
102
104
  "docs/SETUP.md",
103
105
  "docs/API.md",
104
106
  "docs/TROUBLESHOOTING.md",
107
+ "docs/DEPLOYMENT-MODES.md",
105
108
  "docs/assets/icon.svg",
106
109
  "docs/assets/icon-512.png",
107
110
  "README.md",
package/public/index.html CHANGED
@@ -4,9 +4,10 @@
4
4
  <meta charset="UTF-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Slack MCP Server — Web Dashboard</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap">
7
10
  <style>
8
- @import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500;600&family=Space+Grotesk:wght@500;600;700&display=swap');
9
-
10
11
  :root {
11
12
  --font-heading: "Space Grotesk", "Avenir Next", "Segoe UI", sans-serif;
12
13
  --font-body: "IBM Plex Sans", "Inter", "Segoe UI", sans-serif;
package/public/share.html CHANGED
@@ -4,17 +4,17 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Slack MCP Server</title>
7
- <meta name="description" content="No OAuth. No admin. 16 Slack tools for Claude, Cursor, Copilot, Gemini, and any MCP client. One command: npx -y @jtalk22/slack-mcp --setup">
7
+ <meta name="description" content="No OAuth. No admin. 21 Slack tools for Claude, Cursor, Copilot, Gemini, and any MCP client. One command: npx -y @jtalk22/slack-mcp --setup">
8
8
  <meta property="og:type" content="website">
9
9
  <meta property="og:title" content="Slack MCP Server — No OAuth, no admin, just your browser session">
10
- <meta property="og:description" content="Slack's official MCP needs OAuth + admin. This one uses your browser session. 16 tools, works with Claude, Cursor, Copilot, Gemini.">
10
+ <meta property="og:description" content="Slack's official MCP needs OAuth + admin. This one uses your browser session. 21 tools, works with Claude, Cursor, Copilot, Gemini.">
11
11
  <meta property="og:url" content="https://jtalk22.github.io/slack-mcp-server/public/share.html">
12
12
  <meta property="og:image" content="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png">
13
13
  <meta property="og:image:width" content="1280">
14
14
  <meta property="og:image:height" content="640">
15
15
  <meta name="twitter:card" content="summary_large_image">
16
16
  <meta name="twitter:title" content="Slack MCP Server — No OAuth, no admin, just your browser session">
17
- <meta name="twitter:description" content="16 tools for Claude, Cursor, Copilot, Gemini. npx -y @jtalk22/slack-mcp --setup">
17
+ <meta name="twitter:description" content="21 tools for Claude, Cursor, Copilot, Gemini. npx -y @jtalk22/slack-mcp --setup">
18
18
  <meta name="twitter:image" content="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png">
19
19
  <link rel="icon" href="https://jtalk22.github.io/slack-mcp-server/docs/assets/icon-512.png" type="image/png">
20
20
  <style>
@@ -107,7 +107,7 @@
107
107
  <body>
108
108
  <main class="wrap">
109
109
  <h1>Slack MCP Server</h1>
110
- <p class="sub">Give Claude full access to your Slack. Self-host 16 tools for free. Hosted version with semantic search, AI summaries, and permanent OAuth coming soon.</p>
110
+ <p class="sub">Give Claude full access to your Slack. Self-host 21 tools for free (16 read/write + 2 workflow profile primitives + 3 paid stubs that point at hosted). Hosted free tier workflow continuity + AI catch-up. Sign up no card at <a href="https://mcp.revasserlabs.com">mcp.revasserlabs.com</a>.</p>
111
111
 
112
112
  <a class="preview" href="https://github.com/jtalk22/slack-mcp-server" rel="noopener">
113
113
  <img src="https://jtalk22.github.io/slack-mcp-server/docs/images/social-preview-v3.png" alt="Slack MCP Server social preview card">
@@ -123,7 +123,7 @@
123
123
  <a href="https://mcp.revasserlabs.com" rel="noopener" style="background:rgba(240,194,70,0.18);border-color:rgba(240,194,70,0.45);color:#f0c246">Hosted</a>
124
124
  </div>
125
125
 
126
- <p class="note"><strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives 16 tools with session-based auth. Works with any MCP client — Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf. Hosted version coming soon at <a href="https://mcp.revasserlabs.com">mcp.revasserlabs.com</a>.</p>
126
+ <p class="note"><strong>Verify in 30 seconds:</strong> <code>--version</code>, <code>--doctor</code>, <code>--status</code>. Self-host gives 21 tools with session-based auth. Works with any MCP client — Claude, ChatGPT, Cursor, Copilot, Gemini, Windsurf. Hosted free tier (no card) live at <a href="https://mcp.revasserlabs.com">mcp.revasserlabs.com</a> — Pro $9/mo unlocks unlimited AI tools (scheduled morning catch-up DM rolling out Q2 2026).</p>
127
127
  </main>
128
128
  </body>
129
129
  </html>
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Apply a workflow profile template to ~/.slack-mcp-workflows.json
4
+ *
5
+ * Usage:
6
+ * slack-mcp --apply-template <template-name>
7
+ * slack-mcp --apply-template <template-name> --channels C012,C067 [--priority-people U0PRIME]
8
+ *
9
+ * Templates ship in templates/workflow-profiles/. Available templates:
10
+ * oncall-handoff, support-triage, exec-monday, sprint-tracker,
11
+ * customer-feedback, incident-room
12
+ *
13
+ * If --channels is not provided, the template is applied with empty
14
+ * channels — you can add them later via slack_workflow_save.
15
+ */
16
+
17
+ import { readFileSync, existsSync, readdirSync } from "fs";
18
+ import { dirname, join } from "path";
19
+ import { fileURLToPath } from "url";
20
+ import { saveProfile } from "../lib/workflow-store.js";
21
+
22
+ const __dirname = dirname(fileURLToPath(import.meta.url));
23
+ const TEMPLATES_DIR = join(__dirname, "..", "templates", "workflow-profiles");
24
+
25
+ const args = process.argv.slice(2);
26
+
27
+ function listTemplates() {
28
+ if (!existsSync(TEMPLATES_DIR)) return [];
29
+ return readdirSync(TEMPLATES_DIR)
30
+ .filter((f) => f.endsWith(".json"))
31
+ .map((f) => f.replace(/\.json$/, ""));
32
+ }
33
+
34
+ function printUsage() {
35
+ console.log("Usage: slack-mcp --apply-template <template-name> [--channels C012,C067] [--priority-people U0PRIME,U0SECONDARY] [--profile-name custom-name]");
36
+ console.log("");
37
+ console.log("Available templates:");
38
+ for (const t of listTemplates()) {
39
+ console.log(" " + t);
40
+ }
41
+ console.log("");
42
+ console.log("Example:");
43
+ console.log(" slack-mcp --apply-template support-triage --channels C012345,C067890");
44
+ console.log("");
45
+ console.log("Templates write to ~/.slack-mcp-workflows.json. The hosted AI brain at");
46
+ console.log("mcp.revasserlabs.com (free tier or Pro $9/mo) reads these profiles and");
47
+ console.log("returns structured JSON per the workflow_kind. The OSS package ships the");
48
+ console.log("profile primitives + 3 discoverable upgrade stubs (slack_smart_search,");
49
+ console.log("slack_catch_me_up, slack_triage). The brain is hosted-only.");
50
+ }
51
+
52
+ function parseFlag(flag) {
53
+ const idx = args.indexOf(flag);
54
+ if (idx === -1) return null;
55
+ return args[idx + 1];
56
+ }
57
+
58
+ const templateName = args[0];
59
+ if (!templateName || templateName === "--help" || templateName === "-h") {
60
+ printUsage();
61
+ process.exit(templateName ? 0 : 1);
62
+ }
63
+
64
+ if (templateName.startsWith("--") || templateName.startsWith("-")) {
65
+ console.error(`Missing template name. Got "${templateName}" as the first positional argument.`);
66
+ console.error("");
67
+ printUsage();
68
+ process.exit(1);
69
+ }
70
+
71
+ const templatePath = join(TEMPLATES_DIR, `${templateName}.json`);
72
+ if (!existsSync(templatePath)) {
73
+ console.error(`Template "${templateName}" not found at ${templatePath}`);
74
+ console.error("");
75
+ console.error("Available templates: " + listTemplates().join(", "));
76
+ process.exit(1);
77
+ }
78
+
79
+ let template;
80
+ try {
81
+ template = JSON.parse(readFileSync(templatePath, "utf-8"));
82
+ } catch (err) {
83
+ console.error(`Failed to parse template "${templateName}": ${err.message}`);
84
+ process.exit(1);
85
+ }
86
+
87
+ const channelsArg = parseFlag("--channels");
88
+ const priorityArg = parseFlag("--priority-people");
89
+ const profileNameOverride = parseFlag("--profile-name");
90
+
91
+ const profile = {
92
+ profile_name: profileNameOverride || template.profile_name,
93
+ workflow_kind: template.workflow_kind,
94
+ channels: channelsArg ? channelsArg.split(",").map((s) => s.trim()).filter(Boolean) : (template.channels || []),
95
+ priority_people: priorityArg ? priorityArg.split(",").map((s) => s.trim()).filter(Boolean) : (template.priority_people || []),
96
+ retention_mode: template.retention_mode || "ephemeral",
97
+ summary_cadence: template.summary_cadence || "on_demand",
98
+ };
99
+
100
+ const result = saveProfile(profile);
101
+ if (!result.ok) {
102
+ console.error("Failed to save profile:");
103
+ for (const err of result.errors) console.error(" " + err);
104
+ process.exit(1);
105
+ }
106
+
107
+ console.log(`Saved workflow profile "${result.profile_name}" to ~/.slack-mcp-workflows.json`);
108
+ console.log(JSON.stringify(result.profile, null, 2));
109
+ console.log("");
110
+ if (!profile.channels.length) {
111
+ console.log("Note: no channels set. Add channels with:");
112
+ console.log(` slack-mcp --apply-template ${templateName} --channels C012345,C067890`);
113
+ console.log("Or call slack_workflow_save from your MCP client to update.");
114
+ } else {
115
+ console.log("Profile is ready. Run slack_catch_me_up against it from your MCP client.");
116
+ console.log(`(Free tier: 3 catch_me_up calls/month. Pro $9/mo unlocks unlimited; scheduled morning DM rolling out Q2 2026.)`);
117
+ }
@@ -148,6 +148,9 @@ async function runMacOSSetup(rl) {
148
148
  print(" 1. Chrome is running");
149
149
  print(" 2. You have a Slack tab open (app.slack.com)");
150
150
  print(" 3. You're logged into that workspace");
151
+ print();
152
+ print(`${colors.dim}Chrome-free or non-macOS? Hosted tier bypasses Chrome entirely:${colors.reset}`);
153
+ print(`${colors.dim} https://mcp.revasserlabs.com${colors.reset}`);
151
154
  }
152
155
  print();
153
156
 
@@ -283,6 +286,9 @@ async function runManualSetup(rl) {
283
286
  print(" • Tokens expired - try refreshing Slack and copying again");
284
287
  print(" • Wrong workspace - make sure you copied from the right tab");
285
288
  print(" • Incomplete copy - ensure you got the full token/cookie");
289
+ print();
290
+ print(`${colors.dim}Tired of paste-the-token loops? Hosted tier uses OAuth:${colors.reset}`);
291
+ print(`${colors.dim} https://mcp.revasserlabs.com${colors.reset}`);
286
292
  return false;
287
293
  }
288
294
 
@@ -402,6 +408,9 @@ async function runDoctor() {
402
408
  print();
403
409
  print("Next action:");
404
410
  print(" npx -y @jtalk22/slack-mcp --setup");
411
+ print();
412
+ print(`${colors.dim}Prefer no local tokens? Hosted tier uses OAuth:${colors.reset}`);
413
+ print(`${colors.dim} https://mcp.revasserlabs.com${colors.reset}`);
405
414
  process.exit(1);
406
415
  }
407
416
 
@@ -424,6 +433,9 @@ async function runDoctor() {
424
433
  print("Next action:");
425
434
  if (exitCode === 2) {
426
435
  print(" npx -y @jtalk22/slack-mcp --setup");
436
+ print();
437
+ print(`${colors.dim}Tokens expire every 1-2 weeks. Hosted tier has permanent OAuth:${colors.reset}`);
438
+ print(`${colors.dim} https://mcp.revasserlabs.com${colors.reset}`);
427
439
  } else {
428
440
  print(" Check network connectivity and retry:");
429
441
  print(" npx -y @jtalk22/slack-mcp --doctor");
@@ -460,6 +472,10 @@ async function showHelp() {
460
472
  print();
461
473
  print(`${colors.bold}More info:${colors.reset}`);
462
474
  print(" https://github.com/jtalk22/slack-mcp-server");
475
+ print();
476
+ print(`${colors.bold}Hosted tier:${colors.reset}`);
477
+ print(" https://mcp.revasserlabs.com — $9/mo Pro, permanent OAuth,");
478
+ print(" semantic search, workflow continuity across channels.");
463
479
  }
464
480
 
465
481
  async function main() {
@@ -530,6 +546,9 @@ async function main() {
530
546
  print(" • Verify: npx -y @jtalk22/slack-mcp --status");
531
547
  print(" • Start server: npx -y @jtalk22/slack-mcp");
532
548
  print(" • Or add to Claude Desktop config");
549
+ print();
550
+ print(`${colors.dim}Want permanent tokens, semantic search, and workflow continuity?${colors.reset}`);
551
+ print(`${colors.dim}Hosted tier: https://mcp.revasserlabs.com — $9/mo Pro, 10 free paid calls.${colors.reset}`);
533
552
  } else {
534
553
  print(`${colors.red}Setup failed.${colors.reset} See errors above.`);
535
554
  process.exit(1);
package/server.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.jtalk22/slack-mcp-server",
4
4
  "title": "Slack MCP Server",
5
- "description": "Slack MCP without OAuth no app registration, no admin approval. Works with Claude Code, Cursor, Copilot (where the official server doesn't). 16 tools, one command.",
5
+ "description": "Slack MCP without OAuth. 21 tools (16 read/write Slack + 2 workflow profile primitives + 3 hosted-brain upgrade stubs). Free OSS or hosted (free tier no card; $9/mo Pro = unlimited; morning DM rolling out Q2 2026).",
6
6
  "websiteUrl": "https://mcp.revasserlabs.com",
7
7
  "icons": [
8
8
  {
@@ -17,7 +17,7 @@
17
17
  "url": "https://github.com/jtalk22/slack-mcp-server",
18
18
  "source": "github"
19
19
  },
20
- "version": "4.1.2",
20
+ "version": "4.2.1",
21
21
  "remotes": [
22
22
  {
23
23
  "type": "streamable-http",
@@ -28,7 +28,7 @@
28
28
  {
29
29
  "registryType": "npm",
30
30
  "identifier": "@jtalk22/slack-mcp",
31
- "version": "4.1.2",
31
+ "version": "4.2.1",
32
32
  "transport": {
33
33
  "type": "stdio"
34
34
  },
package/smithery.yaml CHANGED
@@ -1,5 +1,7 @@
1
1
  # Smithery configuration for slack-mcp-server
2
2
  # https://smithery.ai/docs/build/project-config/smithery-yaml
3
+ # Slack MCP — free OSS or hosted (free tier + $9/mo Pro). 21 tools:
4
+ # read/write Slack + workflow profile primitives + AI brain via hosted upgrade.
3
5
 
4
6
  startCommand:
5
7
  type: stdio
package/src/cli.js CHANGED
@@ -34,6 +34,9 @@ if (firstArg === "web") {
34
34
  } else if (firstArg === "http") {
35
35
  scriptPath = join(__dirname, "server-http.js");
36
36
  scriptArgs = args.slice(1);
37
+ } else if (firstArg === "--apply-template" || firstArg === "apply-template") {
38
+ scriptPath = join(__dirname, "../scripts/apply-template.js");
39
+ scriptArgs = args.slice(1);
37
40
  } else if (WIZARD_ARGS.has(firstArg)) {
38
41
  scriptPath = join(__dirname, "../scripts/setup-wizard.js");
39
42
  scriptArgs = args;
package/src/server.js CHANGED
@@ -46,6 +46,11 @@ import {
46
46
  handleConversationsMark,
47
47
  handleConversationsUnreads,
48
48
  handleUsersSearch,
49
+ handleWorkflowSave,
50
+ handleWorkflows,
51
+ handleSmartSearch,
52
+ handleCatchMeUp,
53
+ handleTriage,
49
54
  } from "../lib/handlers.js";
50
55
 
51
56
  // Background refresh interval (4 hours)
@@ -275,6 +280,23 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
275
280
  case "slack_users_search":
276
281
  return await handleUsersSearch(args);
277
282
 
283
+ // Workflow profile primitives (OSS local JSON store)
284
+ case "slack_workflow_save":
285
+ return await handleWorkflowSave(args);
286
+
287
+ case "slack_workflows":
288
+ return await handleWorkflows(args);
289
+
290
+ // Hosted-only AI tools (OSS = upgrade stubs)
291
+ case "slack_smart_search":
292
+ return await handleSmartSearch(args);
293
+
294
+ case "slack_catch_me_up":
295
+ return await handleCatchMeUp(args);
296
+
297
+ case "slack_triage":
298
+ return await handleTriage(args);
299
+
278
300
  default:
279
301
  return {
280
302
  content: [{
@@ -0,0 +1,10 @@
1
+ {
2
+ "profile_name": "customer-feedback",
3
+ "workflow_kind": "product_launch_watch",
4
+ "channels": [],
5
+ "priority_people": [],
6
+ "retention_mode": "persistent",
7
+ "summary_cadence": "daily_8am",
8
+ "structured_keys": ["launch_signals", "feedback_themes", "blockers", "metrics", "next_actions"],
9
+ "_template_notes": "Apply with: slack-mcp --apply-template customer-feedback --channels C0FEEDBACK,C0CUSTOMERS,C0SUPPORT. retention_mode=persistent so feedback themes accumulate across days. Weekly synthesis becomes more valuable as the index grows."
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "profile_name": "exec-monday",
3
+ "workflow_kind": "exec_brief",
4
+ "channels": [],
5
+ "priority_people": [],
6
+ "retention_mode": "ephemeral",
7
+ "summary_cadence": "weekly_monday",
8
+ "structured_keys": ["summary", "decisions", "risks", "asks", "action_items"],
9
+ "_template_notes": "Apply with: slack-mcp --apply-template exec-monday --channels C0EXEC,C0PRODUCT,C0OPS --priority-people U0CEO,U0CTO. Weekly Monday cadence requires Pro or Team. Posts a structured brief to your DM at 8am workspace time on Mondays."
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "profile_name": "incident-room",
3
+ "workflow_kind": "incident_room",
4
+ "channels": [],
5
+ "priority_people": [],
6
+ "retention_mode": "persistent",
7
+ "summary_cadence": "on_demand",
8
+ "structured_keys": ["incident_summary", "timeline", "open_risks", "owner_gaps", "next_actions"],
9
+ "_template_notes": "Apply with: slack-mcp --apply-template incident-room --channels C0INCIDENTS --priority-people U0ONCALL. on_demand cadence — call slack_catch_me_up at handoff or before stakeholder updates. retention_mode=persistent so post-incident review has the timeline."
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "profile_name": "oncall-handoff",
3
+ "workflow_kind": "incident_room",
4
+ "channels": [],
5
+ "priority_people": [],
6
+ "retention_mode": "persistent",
7
+ "summary_cadence": "on_demand",
8
+ "structured_keys": ["incident_summary", "timeline", "open_risks", "owner_gaps", "next_actions"],
9
+ "_template_notes": "Apply with: slack-mcp --apply-template oncall-handoff --channels C012345,C067890 --priority-people U0PRIME,U0SECONDARY. retention_mode=persistent so the next responder can see prior incident context. Run slack_catch_me_up against this profile before each handoff."
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "profile_name": "sprint-tracker",
3
+ "workflow_kind": "product_launch_watch",
4
+ "channels": [],
5
+ "priority_people": [],
6
+ "retention_mode": "ephemeral",
7
+ "summary_cadence": "daily_8am",
8
+ "structured_keys": ["launch_signals", "feedback_themes", "blockers", "metrics", "next_actions"],
9
+ "_template_notes": "Apply with: slack-mcp --apply-template sprint-tracker --channels C0SPRINT,C0BUGS,C0LAUNCHES. Use during active release cycles. Returns structured JSON so downstream automation (Linear, Notion, status dashboards) can consume directly."
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "profile_name": "support-triage",
3
+ "workflow_kind": "support_inbox",
4
+ "channels": [],
5
+ "priority_people": [],
6
+ "retention_mode": "ephemeral",
7
+ "summary_cadence": "daily_8am",
8
+ "structured_keys": ["open_threads", "ack_lag", "owner_gaps", "escalations", "next_actions"],
9
+ "_template_notes": "Apply with: slack-mcp --apply-template support-triage --channels C0SUPPORT,C0ESCALATIONS. summary_cadence=daily_8am needs Pro or Team for the scheduled morning DM (rolling out Q2 2026). Free tier can run on_demand."
10
+ }