> **Building with AI coding agents?** Install the authstack plugin with one command. This equips your agent with accurate Scalekit implementation patterns.
>
> **Recommended** (the `-y` flags skip prompts, so the command runs without an interactive terminal):
> ```bash
> npx -y @scalekit-inc/cli setup -y
> ```
>
> Global:
> ```bash
> npm install -g @scalekit-inc/cli
> scalekit setup -y
> ```
>
> Supports Claude Code, Cursor, GitHub Copilot, Codex + skills for other Agent Skills-compatible agents in `~/.agents/skills`.
> Skills: integrate-agentkit, implement-saaskit, add-mcp-oauth, implement-sso, implement-scim.
> [Full setup guide](https://docs.scalekit.com/dev-kit/build-with-ai/)

---

# Connect AI agents to the Slack MCP server

The Slack MCP connector routes your AI agent's tool calls to Slack's own MCP server through Scalekit. Each user signs in to Slack once, and Scalekit stores and refreshes their tokens, so your agent never handles credentials. It comes with 19 tools.

**Authentication:** OAuth 2.1
**Categories:** Communication, Collaboration, Productivity
**Tools:** 19: 12 read, 6 write, 1 destructive
**Users sign in with:** OAuth
**OAuth app:** Scalekit's or your own
**Built by:** Vendor MCP
**Try it:** [Playground in the Scalekit dashboard](https://app.scalekit.com/ws/signup?sk_intent=playground&provider=SLACKMCP)

## What you can do

- **Send messages**: post to channels, DMs, and threads across your Slack workspace
- **Read conversations**: retrieve channel history, thread replies, and direct messages
- **Search channels and users**: find channels, team members, and public messages by keyword
- **Manage canvases**: create, read, and update Slack Canvas documents
- **React to messages**: add and retrieve emoji reactions on any message
- **Schedule messages**: deliver messages to channels at a specified future time

## Setup

1. ### Install the SDK

   **Node.js**

   ```bash
   npm install @scalekit-sdk/node dotenv
   ```

   **Python**

   ```bash
   pip install scalekit-sdk-python python-dotenv
   ```

2. ### Set your credentials

   Add your Scalekit credentials to your `.env` file. Find values in **[app.scalekit.com](https://app.scalekit.com)** > **Developers** > **API Credentials**.

   ```sh title=".env"
   SCALEKIT_ENVIRONMENT_URL=<your-environment-url>
   SCALEKIT_CLIENT_ID=<your-client-id>
   SCALEKIT_CLIENT_SECRET=<your-client-secret>
   ```

3. ### Create the Slack MCP connection

   In **AgentKit > Connections**, create a Slack MCP connection. The name you give it is the `connection_name` your code passes. See [Configure connections](/agentkit/connections/).

   Scalekit credentials are available for Slack, so you don't need to register an OAuth app. To show your own app on the consent screen, use your own credentials instead.

   **Use your own OAuth app:**

   Slack serves Model Context Protocol (MCP) traffic only to apps that have its MCP server turned on. Enable MCP access on the Slack app whose client ID and secret you use for the connection, then create the connection in Scalekit.

   1. ### Enable MCP access in your Slack app

      - Go to [api.slack.com/apps](https://api.slack.com/apps) and open the app whose **Client ID** and **Client Secret** you configure on the Slack MCP connection. MCP access applies per app, so enabling it on any other app has no effect on the connection.
      - In the left sidebar, under **Features**, click **Agents & AI Apps**. Some workspaces label this item **Agents**.
      - Turn on **Slack Model Context Protocol (MCP) Server**.

      > Image: Turn on the Slack Model Context Protocol (MCP) Server toggle in the Slack app portal

   2. ### Connect in Scalekit

      In [Scalekit dashboard](https://app.scalekit.com), go to **AgentKit** > **Connections** > **Create Connection**. Find **Slack MCP** and click **Create**. Enter the **Client ID** and **Client Secret** of the same Slack app, then click **Save**.

4. ### Authorize a user and make your first call

   **Node.js** (`quickstart.mts`)

   ```typescript
   import { ScalekitClient } from '@scalekit-sdk/node'
   import 'dotenv/config'
   import { createInterface } from 'node:readline/promises'

   const scalekit = new ScalekitClient(
     process.env.SCALEKIT_ENVIRONMENT_URL,
     process.env.SCALEKIT_CLIENT_ID,
     process.env.SCALEKIT_CLIENT_SECRET,
   )
   const actions = scalekit.actions

   const connector = 'slackmcp'
   const identifier = 'user_123'

   // Generate an authorization link for the user
   const { link } = await actions.getAuthorizationLink({ connectionName: connector, identifier })
   console.log('Authorize Slack MCP:', link)
   const rl = createInterface({ input: process.stdin, output: process.stdout })
   await rl.question('Press Enter after authorizing...')
   rl.close()

   // Make your first call
   const result = await actions.executeTool({
     connector,
     identifier,
     toolName: 'slackmcp_slack_read_user_profile',
     toolInput: {},
   })
   console.log(result)
   ```

   ```bash
   npx tsx quickstart.mts
   ```

   **Python** (`quickstart.py`)

   ```python
   import os
   from scalekit import ScalekitClient
   from dotenv import load_dotenv
   load_dotenv()

   scalekit_client = ScalekitClient(
       env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"),
       client_id=os.getenv("SCALEKIT_CLIENT_ID"),
       client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"),
   )
   actions = scalekit_client.actions

   connection_name = "slackmcp"
   identifier = "user_123"

   # Generate an authorization link for the user
   link_response = actions.get_authorization_link(
       connection_name=connection_name,
       identifier=identifier,
   )
   print("Authorize Slack MCP:", link_response.link)
   input("Press Enter after authorizing...")

   # Make your first call
   result = actions.execute_tool(
       tool_input={},
       tool_name="slackmcp_slack_read_user_profile",
       connection_name=connection_name,
       identifier=identifier,
   )
   print(result)
   ```

   ```bash
   python quickstart.py
   ```

   Each user signs in once. See [Authorize a user](/agentkit/tools/authorize/) for the full flow and statuses.

## Tools

Pass the exact name to `execute_tool`, with an input like each tool's example. To ask for a tool that's missing, use the [request form](https://scalekitsupport.portal.usepylon.com/forms/request-a-connector-tool).

**Node.js**

```typescript
const result = await actions.executeTool({
  toolName: 'slackmcp_slack_get_reactions',
  toolInput: {
    channel_id: '<channel_id>',
    message_ts: '<message_ts>',
  },
  connector: 'slackmcp',
  identifier: 'user_123',
})
```

**Python**

```python
result = actions.execute_tool(
    tool_name="slackmcp_slack_get_reactions",
    tool_input={
        "channel_id": "<channel_id>",
        "message_ts": "<message_ts>",
    },
    connection_name="slackmcp",
    identifier="user_123",
)
```

`result.data` is the app's response as JSON, and `result.execution_id` (`executionId` in Node.js) is the ID of the call.

### `slackmcp_slack_get_reactions`

Slack Get Reactions · Read-only

Retrieve all emoji reactions on a specific Slack message.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `message_ts` (`string`, required): Timestamp of the message. Get it from slack_read_channel or slack_read_thread.

Example input: `{"channel_id":"<channel_id>","message_ts":"<message_ts>"}`

### `slackmcp_slack_list_channel_members`

Slack List Channel Members · Read-only

List members of a Slack channel, group, or group DM with profile details.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `cursor` (`string`, optional): Pagination cursor from the previous response to fetch the next page.
- `include_bots` (`boolean`, optional): Include bots and apps in the member list (default: false)
- `include_deleted` (`boolean`, optional): Include deleted/deactivated users in the member list (default: false)
- `limit` (`integer`, optional): Maximum number of results to return per page.
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.

Example input: `{"channel_id":"<channel_id>"}`

### `slackmcp_slack_read_canvas`

Slack Read Canvas · Read-only

Retrieve the Markdown content and section ID mapping of a Slack Canvas document.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `canvas_id` (`string`, required): ID of the Slack canvas document. Get it from slack_search_public.

Example input: `{"canvas_id":"<canvas_id>"}`

### `slackmcp_slack_read_channel`

Slack Read Channel · Read-only

Read messages from a Slack channel in reverse chronological order (newest first).

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `cursor` (`string`, optional): Pagination cursor from the previous response to fetch the next page.
- `latest` (`string`, optional): Only return messages before this Unix timestamp (e.g. 1640995200.000000).
- `limit` (`integer`, optional): Maximum number of results to return per page.
- `oldest` (`string`, optional): Only return messages after this Unix timestamp (e.g. 1609459200.000000).
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.

Example input: `{"channel_id":"<channel_id>"}`

### `slackmcp_slack_read_file`

Slack Read File · Read-only

Read a Slack file's content by file ID. Returns text or base64-encoded content.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `file_id` (`string`, required): ID of the Slack file. Get it from slack_search_public.

Example input: `{"file_id":"<file_id>"}`

### `slackmcp_slack_read_thread`

Slack Read Thread · Read-only

Read all messages in a Slack thread — the parent message and its replies.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `message_ts` (`string`, required): Timestamp of the message. Get it from slack_read_channel or slack_read_thread.
- `cursor` (`string`, optional): Pagination cursor from the previous response to fetch the next page.
- `latest` (`string`, optional): Only return messages before this Unix timestamp (e.g. 1640995200.000000).
- `limit` (`integer`, optional): Maximum number of results to return per page.
- `oldest` (`string`, optional): Only return messages after this Unix timestamp (e.g. 1609459200.000000).
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.

Example input: `{"channel_id":"<channel_id>","message_ts":"<message_ts>"}`

### `slackmcp_slack_read_user_profile`

Slack Read User Profile · Read-only

Retrieve detailed profile information for a Slack user including status and contact info.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `include_locale` (`boolean`, optional): Include user's locale information. Default: false
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.
- `user_id` (`string`, optional): ID of the Slack user. Get it from slack_search_users.

Example input: `{}`

### `slackmcp_slack_search_channels`

Slack Search Channels · Read-only

Search for Slack channels by name or description and return channel IDs and metadata.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `query` (`string`, required): Search query string.
- `channel_types` (`string`, optional): Comma-separated channel types to filter. Accepted values: public_channel, private_channel, mpim, im.
- `cursor` (`string`, optional): Pagination cursor from the previous response to fetch the next page.
- `include_archived` (`boolean`, optional): Include archived channels in the search results
- `limit` (`integer`, optional): Maximum number of results to return per page.
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.

Example input: `{"query":"<query>"}`

### `slackmcp_slack_search_emojis`

Slack Search Emojis · Read-only

Search custom emojis available in this Slack workspace by name.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `query` (`string`, required): Search query string.

Example input: `{"query":"<query>"}`

### `slackmcp_slack_search_public`

Slack Search Public · Read-only

Search messages and files in public Slack channels only.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `query` (`string`, required): Search query string.
- `after` (`string`, optional): Only messages after this Unix timestamp (inclusive)
- `before` (`string`, optional): Only messages before this Unix timestamp (inclusive)
- `content_types` (`string`, optional): Comma-separated content types to search. Accepted values: messages, files.
- `context_channel_id` (`string`, optional): Channel ID to boost relevance of results from that channel.
- `cursor` (`string`, optional): Pagination cursor from the previous response to fetch the next page.
- `include_bots` (`boolean`, optional): Include bot messages (default: false)
- `include_context` (`boolean`, optional): Include surrounding context messages for each result. Defaults to true.
- `limit` (`integer`, optional): Maximum number of results to return per page.
- `max_context_length` (`integer`, optional): Max character length for each context message. Longer messages are truncated.
- `only_my_channels` (`boolean`, optional): Limit results to public channels the user is a member of. Set to true when the user asks to search only their own or joined channels. Default: false.
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.
- `sort` (`string`, optional): Sort by relevance or date (default: 'score'). Options: 'score', 'timestamp'
- `sort_dir` (`string`, optional): Sort direction (default: 'desc'). Options: 'asc', 'desc'

Example input: `{"query":"<query>"}`

### `slackmcp_slack_search_public_and_private`

Slack Search Public And Private · Read-only

Search messages and files across all Slack channels including private ones the user has access to.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `query` (`string`, required): Search query string.
- `after` (`string`, optional): Only messages after this Unix timestamp (inclusive)
- `before` (`string`, optional): Only messages before this Unix timestamp (inclusive)
- `channel_types` (`string`, optional): Comma-separated channel types to filter. Accepted values: public_channel, private_channel, mpim, im.
- `content_types` (`string`, optional): Comma-separated content types to search. Accepted values: messages, files.
- `context_channel_id` (`string`, optional): Channel ID to boost relevance of results from that channel.
- `cursor` (`string`, optional): Pagination cursor from the previous response to fetch the next page.
- `include_bots` (`boolean`, optional): Include bot messages (default: false)
- `include_context` (`boolean`, optional): Include surrounding context messages for each result. Defaults to true.
- `limit` (`integer`, optional): Maximum number of results to return per page.
- `max_context_length` (`integer`, optional): Max character length for each context message. Longer messages are truncated.
- `only_my_channels` (`boolean`, optional): Limit results to channels the user is a member of. Set to true when the user asks to search only their own or joined channels. Default: false.
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.
- `sort` (`string`, optional): Sort by relevance or date (default: 'score'). Options: 'score', 'timestamp'
- `sort_dir` (`string`, optional): Sort direction (default: 'desc'). Options: 'asc', 'desc'

Example input: `{"query":"<query>"}`

### `slackmcp_slack_search_users`

Slack Search Users · Read-only

Search for Slack users by name, email, or profile attributes.

Read only: yes. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `query` (`string`, required): Search query string.
- `cursor` (`string`, optional): Pagination cursor from the previous response to fetch the next page.
- `limit` (`integer`, optional): Maximum number of results to return per page.
- `response_format` (`string`, optional): Level of detail in the response. Accepted values: detailed, concise, ids_only.

Example input: `{"query":"<query>"}`

### `slackmcp_slack_add_reaction`

Slack Add Reaction · Write

Add an emoji reaction to a Slack message. Requires the channel ID, message timestamp, and emoji name.

Read only: no. Destructive: no. Idempotent (safe to retry): yes.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `emoji` (`string`, required): Reaction (emoji) name without colons
- `message_ts` (`string`, required): Timestamp of the message. Get it from slack_read_channel or slack_read_thread.

Example input: `{"channel_id":"<channel_id>","emoji":"<emoji>","message_ts":"<message_ts>"}`

### `slackmcp_slack_create_canvas`

Slack Create Canvas · Write

Create a Slack Canvas document from Canvas-flavored Markdown content.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `content` (`string`, required): Canvas-flavored Markdown content for the canvas body.
- `title` (`string`, required): Display name shown at the top of the canvas.

Example input: `{"content":"<content>","title":"<title>"}`

### `slackmcp_slack_create_conversation`

Slack Create Conversation · Write

Create a channel, DM, or group DM. Returns a channel ID for sending messages.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `channel_name` (`string`, optional): Name for the new channel (lowercase, hyphens, max 80 chars). Omit to create a DM instead.
- `is_private` (`boolean`, optional): If true, creates a private channel. Only used with channel_name. Default: false.
- `user_ids` (`array`, optional): Slack user IDs to invite. 1 ID = DM, 2–8 IDs = group DM, up to 1000 IDs for a channel.

Example input: `{}`

### `slackmcp_slack_schedule_message`

Slack Schedule Message · Write

Schedule a message for future delivery to a Slack channel at a specified Unix timestamp.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `message` (`string`, required): Message content to schedule
- `post_at` (`integer`, required): Unix timestamp (seconds) for when to send the scheduled message.
- `reply_broadcast` (`boolean`, optional): Broadcast thread reply to channel
- `thread_ts` (`string`, optional): Timestamp of the parent message to reply in a thread. Get it from slack_read_channel.

Example input: `{"channel_id":"<channel_id>","message":"<message>","post_at":1}`

### `slackmcp_slack_send_message`

Slack Send Message · Write

Send a message to a Slack channel or user. Use a user ID as channel_id to send a DM.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `message` (`string`, required): Add a message
- `draft_id` (`string`, optional): ID of a previously saved draft to delete after sending.
- `reply_broadcast` (`boolean`, optional): Also send to conversation
- `thread_ts` (`string`, optional): Timestamp of the parent message to reply in a thread. Get it from slack_read_channel.
- `unfurl_app_links` (`boolean`, optional): Set to true to enable link previews from installed apps (e.g. GitHub, Jira) in the message.

Example input: `{"channel_id":"<channel_id>","message":"<message>"}`

### `slackmcp_slack_send_message_draft`

Slack Send Message Draft · Write

Save a message as a draft in a Slack channel without sending it.

Read only: no. Destructive: no. Idempotent (safe to retry): no.

Inputs:

- `channel_id` (`string`, required): ID of the Slack channel. Get it from slack_search_channels.
- `message` (`string`, required): The message content in standard markdown
- `thread_ts` (`string`, optional): Timestamp of the parent message to reply in a thread. Get it from slack_read_channel.

Example input: `{"channel_id":"<channel_id>","message":"<message>"}`

### `slackmcp_slack_update_canvas`

Slack Update Canvas · Destructive

Update an existing Slack Canvas document by appending, replacing, or deleting content. Prefer `sections` for atomic multi-edit operations; `action`/`content`/`section_id` remain as a legacy single-edit path.

Read only: no. Destructive: yes. Idempotent (safe to retry): no.

Inputs:

- `canvas_id` (`string`, required): ID of the canvas to update. Get it from slack_search_public.
- `action` (`string`, optional): Legacy single-edit type. Prefer `sections`. One of append, prepend, replace. One of: `append`, `prepend`, `replace`.
- `content` (`string`, optional): Legacy single-edit markdown content. Prefer `sections`.
- `section_id` (`string`, optional): Legacy single-edit target section ID from slack_read_canvas. Prefer `sections`.
- `sections` (`array`, optional): Preferred: array of edit operations applied atomically against a single document snapshot. Max 100 operations. Use this for any update with one or more edits.

Example input: `{"canvas_id":"<canvas_id>"}`

## Workflows

### Proxy API call

  **Node.js**

  ```ts
  import { ScalekitClient } from '@scalekit-sdk/node'
  import 'dotenv/config'

  const scalekit = new ScalekitClient(
    process.env.SCALEKIT_ENVIRONMENT_URL,
    process.env.SCALEKIT_CLIENT_ID,
    process.env.SCALEKIT_CLIENT_SECRET,
  )
  const actions = scalekit.actions

  const connector = 'slackmcp'
  const identifier = 'user_123'

  // Read a channel's message history
  const result = await actions.executeTool({
    connector,
    identifier,
    toolName: 'slackmcp_slack_read_channel',
    toolInput: { channel_id: 'C01234567' },
  })
  console.log(result)
  ```

  **Python**

  ```python
  from scalekit import ScalekitClient
  import os

  scalekit_client = ScalekitClient(
      os.environ['SCALEKIT_ENVIRONMENT_URL'],
      os.environ['SCALEKIT_CLIENT_ID'],
      os.environ['SCALEKIT_CLIENT_SECRET'],
  )

  connector = 'slackmcp'
  identifier = 'user_123'

  # Read a channel's message history
  result = scalekit_client.actions.execute_tool(
      connection_name=connector,
      identifier=identifier,
      tool_name='slackmcp_slack_read_channel',
      tool_input={'channel_id': 'C01234567'},
  )
  print(result)
  ```

## Related

Other ways to connect Slack:

- [OAuth](/agentkit/connectors/slack/): Scalekit connector, OAuth, 96 tools

Other Communication connectors ([all 57](/agentkit/connectors/?category=communication)):

- [Gmail](/agentkit/connectors/gmail/): Scalekit connector, OAuth, 48 tools
- [Google Calendar](/agentkit/connectors/googlecalendar/): Scalekit connector, OAuth, 30 tools
- [Intercom](/agentkit/connectors/intercom/): Scalekit connector, OAuth, 113 tools


---

## More Scalekit documentation

| Resource | What it contains | When to use it |
|----------|-----------------|----------------|
| [/llms.txt](/llms.txt) | Structured index with routing hints per product area | Start here — find which documentation set covers your topic before loading full content |
| [/llms-full.txt](/llms-full.txt) | Complete documentation for all Scalekit products in one file | Use when you need exhaustive context across multiple products or when the topic spans several areas |
| [sitemap-0.xml](https://docs.scalekit.com/sitemap-0.xml) | Full URL list of every documentation page | Use to discover specific page URLs you can fetch for targeted, page-level answers |
