Skill v1.0.1
currentAutomated scan100/100+1 new
version: "1.0.1" name: using-slack-connector description: Implements Slack messaging, channel operations, and Web API calls using generated clients and MCP tools. Use when doing ANYTHING that touches Slack in any way, load this skill.
Major Platform Resource: Slack
Common: Interacting with Resources
Security: Never connect directly to databases/APIs. Never use credentials in code. Always use generated clients or MCP tools.
Three ways to interact with Slack:
- MCP tools (direct, no code needed): Tools follow the pattern
mcp__resources__<resourcetype>_<toolname>. Usemcp__resources__list_resourcesto discover available resources and their IDs. - Generated TypeScript clients (for app code): Call
mcp__resource-tools__add-resource-clientwith aresourceIdto generate a typed client. Clients are created in/clients/(Next.js) or/src/clients/(Vite). - HTTP proxy (Next.js apps): Use
createProxyFetchfrom@major-tech/resource-client/nextto call the Slack API directly with automatic auth injection. See using-http-proxy for setup and usage — preferred when you need to hit endpoints not covered by MCP tools or the typed client, or when using an official SDK that accepts a customfetch.
CRITICAL: Do NOT guess client method names or signatures. The TypeScript clients in @major-tech/resource-client have strongly typed inputs and outputs. ALWAYS read the actual client source code in the generated /clients/ directory (or the package itself) to verify available methods and their exact signatures before writing any client code.
Framework note: Next.js = resource clients must be used in server-side code only (Server Components, Server Actions, API Routes). Vite = call directly from frontend.
Error handling: Always check result.ok before accessing result.result.
Invocation keys must be static strings — use descriptive literals like "fetch-user-orders", never dynamic values like ` ${date}-records `.
CRITICAL: Channel Access Verification
Before sending messages, posting files, or reading history from any channel, you MUST verify the bot has access to that channel. Never attempt to post to a channel without confirming access first.
Required workflow:
- Call
mcp__resources__slack_list_channelsto get the list of channels the bot can see. - Check if the target channel appears in the results.
- If the channel is NOT in the list: Tell the user that the bot does not currently have access to that channel. Ask them to invite the bot by going to the channel and @mentioning @Major Slack Integration. Once the user confirms they have done this, call
mcp__resources__slack_list_channelsagain to verify the channel now appears. - Only after the channel is confirmed visible in the list may you proceed with sending messages, reading history, or any other channel operation.
Do NOT skip this check. Do NOT assume the bot has access to a channel just because the user mentioned it by name.
CRITICAL: Rate Limits
Slack's Web API rate limits are aggressive and easy to blow through, especially on history/read methods:
- Rate limits are per-workspace, tiered by method, and enforced with
429+ aRetry-Afterheader. Tiers range from Tier 1 (~1 request/minute) to Tier 4 (~100+ requests/minute), and non-Marketplace apps got hit hard by a 2026 change:conversations.historydropped from Tier 3 (~50 req/min, 100+ messages per call) to Tier 1 (1 req/min, max 15 messages per call) for non-Marketplace/custom apps. Assume you are on the low end unless you've confirmed otherwise. - Do not parallelize Slack queries. Firing requests from multiple agents/workflows at once (e.g. one sub-agent per channel) multiplies 429s instead of avoiding them — all requests share the same workspace-level bucket. Query Slack serially, one call at a time.
- When pulling a lot of message history, use the highest `limit` the read tool allows per call (up to its max, e.g. 1000 if supported) instead of small page sizes. Fewer, larger calls burn far less of the rate-limit budget than many small ones — pulling 20 messages at a time across many pages is the single biggest way to exhaust the limit for no benefit.
- If you keep hitting rate limit errors, stop and reassess instead of retrying the same way. Repeatedly re-calling into a 429 (or spinning up more parallel agents to "get around it") burns time and money without getting more data — it just waits out the same shared limit slower. Back off, reduce concurrency to 1, and if you've already gathered a reasonable amount of history, answer the user's question with what you have rather than grinding to fetch everything.
MCP Tools
mcp__resources__slack_call— Call any Slack Web API method. Args:resourceId,method,body?mcp__resources__slack_list_channels— List channels in the workspace. Args:resourceId,limit?mcp__resources__slack_post_message— Post a message to a channel. Args:resourceId,channel,text,blocks?mcp__resources__slack_get_history— Get message history from a channel. Args:resourceId,channel,limit?
TypeScript Client
import { slackClient } from "./clients";// invoke(method, invocationKey, options?)// The `method` parameter is the Slack API method nameconst result = await slackClient.invoke("chat.postMessage", "post-update", {body: { channel: "C0123456", text: "Hello from the app!" },});// List channelsawait slackClient.invoke("conversations.list", "list-channels", {body: { limit: 100 },});// getUploadURL(filename, length, invocationKey, options?)// completeUpload(files, channelId, invocationKey, options?)// See "File Upload" section below for usage
File Upload
Slack uses a 3-step flow for uploading files/images to channels:
import { slackClient } from "./clients";// Step 1: Get a pre-signed upload URLconst urlResult = await slackClient.getUploadURL("chart.png", // filename with extensionfileBytes.length, // file size in bytes"get-upload-url",);if (!urlResult.ok) throw new Error(urlResult.error.message);const { upload_url, file_id } = urlResult.result.body.value;// Step 2: Upload the file binary to the pre-signed URLawait fetch(upload_url, {method: "POST",headers: { "Content-Type": "application/octet-stream" },body: fileBytes,});// Step 3: Complete the upload and share to a channelconst completeResult = await slackClient.completeUpload([{ id: file_id, title: "Weekly Chart" }],"C0123456", // channel ID"complete-upload",{ initialComment: "Here's this week's chart" },);
- The upload URL from step 1 is temporary — complete all 3 steps without delay
- Step 2 is a direct HTTP POST (no auth needed, the URL is pre-signed)
- You can upload multiple files by calling step 1+2 for each, then passing all file IDs to a single step 3
- Use
threadTsin step 3 options to upload into a thread - Requires
files:writeOAuth scope (included in the "Read & Write" preset)
Tips
- The
methodparam is the Slack API method name (e.g.,chat.postMessage,conversations.list,users.list) - For the TypeScript client, all parameters go in the
bodyoption — Slack's Web API uses POST with JSON body - Check Slack API methods list for available methods and their parameters
- If a message/history response is too large and gets written to a file instead of returned inline, read it with `jq` rather than loading the whole file — e.g.
jq '.messages[] | {user, text, ts}' file.json— to avoid pulling the entire payload into context.
Docs: Slack API Reference