Skip to main content
Glama
mhmdsami

Slack Code Mode MCP

by mhmdsami
README.md
# Slack Code Mode MCP

A Code Mode MCP server for Slack. It exposes two tools, `search` and `execute`, and runs agent-written JavaScript against the Slack Web API in a celld V8 isolate with no network access and no Slack token.

## Why Code Mode

`search` returns TypeScript signatures from the Slack SDK, so the agent calls methods with the right arguments instead of guessing them. One execution can chain several calls, such as resolving a user, reading a thread, posting a reply, and editing a canvas, without sending intermediate results back through the model.

The guest isolate never receives the Slack token. Calls go through a service binding to the host, which holds the credential and enforces the allowlist. The isolate has `globalOutbound: null`, so `fetch()` throws.

## Requirements

- Node.js 20+
- [celld](https://github.com/denoland/celld) v0.6.2 on `PATH` (macOS arm64 and Linux; Windows is not supported)
- A Slack internal app with PKCE enabled

## Install

```bash
git clone https://github.com/mhmdsami/slack-codemode-mcp.git
cd slack-codemode-mcp
npm ci
```

Celld is a separate binary. Put it on `PATH`, or point the server at it with `SLACK_CODE_CELLD_BIN`.

```bash
celld --version
```

## Configure the Slack app

1. Create an internal app at <https://api.slack.com/apps>.
2. Open **OAuth & Permissions** and enable **PKCE**. This is one-way and makes the app a public client, so no client secret is distributed.
3. Add the redirect URL `http://localhost:33417/callback`.
4. Add the user token scopes the server expects:

```bash
npm run scopes
```

A user token attributes messages to you and keeps canvases on your account. A bot token would post as an app and only see channels the bot joined. Because the app is a public client, colleagues need only the same Client ID.

## Add the Client ID

```bash
cp .env.example .env
$EDITOR .env
```

`.env` holds one value, `SLACK_CLIENT_ID`. It is gitignored.

## Authenticate

```bash
npm run auth
```

This opens Slack, completes the PKCE exchange, and stores the token in the macOS Keychain or `~/.config/slack-codemode/token` on Linux.

```bash
npm run doctor
```

The checker calls one method per area and reports any missing scope.

## Register the server

Pick your harness.

### Claude Code

Claude Code needs `--scope user` to make the server global. Without it, the entry is tied to the current directory.

```bash
claude mcp add --scope user slack-codemode-mcp -- node "$PWD/mcp/server.mjs"
```

### Codex

```bash
codex mcp add slack-codemode-mcp -- node "$PWD/mcp/server.mjs"
```

### OpenCode

```bash
opencode mcp add slack-codemode-mcp --global -- node "$PWD/mcp/server.mjs"
```

### Amp

```bash
amp mcp add slack-codemode-mcp -- node "$PWD/mcp/server.mjs"
```

### Pi

```bash
pi mcp add slack-codemode-mcp -- node "$PWD/mcp/server.mjs"
```

### Antigravity

```bash
agy mcp add slack-codemode-mcp node "$PWD/mcp/server.mjs"
```

### Zed

Add this to `~/.config/zed/settings.json`:

```jsonc
"context_servers": {
  "slack-codemode-mcp": {
    "command": "node",
    "args": ["/absolute/path/to/slack-codemode-mcp/mcp/server.mjs"]
  }
}
```

If a harness starts with a narrow `PATH`, set `SLACK_CODE_CELLD_BIN` to the celld binary.

## Try it

Ask the agent to run this through the MCP:

```js
return await app.whoami();
```

## Tools

| Tool | Purpose |
|---|---|
| `search` | Find an allowed method by name, or fetch a TypeScript type. |
| `execute` | Run JavaScript with `app.*`, `slack.*`, `api.call`, and `console`. |

`search` covers allowlisted methods by default. Pass `include_blocked: true` to search the full index.

## Actions

`app.*` covers lookups, DMs, posts, threads, message links, file uploads, and canvases. Actions paginate and cache in the host, not in the guest. Raw allowlisted methods remain available through `slack.*` and `api.call`.

See [docs/actions.md](docs/actions.md) for the full surface and examples.

## Security model

```
MCP client
  execute(code)
    └─ host: allowlist, token, Slack HTTP
         └─ celld: globalOutbound null
              └─ fresh isolate: slack/app/api, no token, no fs
                   └─ service binding → trusted worker
                        └─ authenticated loopback bridge
```

The host holds the token and the allowlist. The isolate has no network, no filesystem, and no credential. Each execution gets a random id and bridge token. See [docs/security.md](docs/security.md) for the threat model and [docs/architecture.md](docs/architecture.md) for the request flow.

## Development

```bash
npm run gate
npm run test
npm run format
npm run doctor
npm run generate
```

`gate` runs Biome and the unit tests, and is what CI runs. Lefthook runs Biome and tests on commit and the full gate on push:

```bash
npm install
npx lefthook install
```

CI runs the gate on Ubuntu and macOS (`.github/workflows/ci.yml`).

## Docs

- [Agent setup](docs/agent-setup.md)
- [Architecture](docs/architecture.md)
- [Authentication](docs/authentication.md)
- [Actions](docs/actions.md)
- [Security](docs/security.md)
- [Development](docs/development.md)
- [Troubleshooting](docs/troubleshooting.md)

Licensed under the [MIT License](LICENSE).

TDQS

A3.8/5.0

Scored across 2 tools

Disambiguation5/5

search and execute target completely different actions: one discovers Slack API methods, the other runs JavaScript. There is no plausible overlap, so an agent can always pick the right tool without hesitation.

Naming Consistency4/5

Both names are single lowercase verbs with a consistent, predictable style, so the set reads cleanly. It is not a strict verb_noun pattern (no noun component), which slightly limits the score.

Tool Count4/5

A code-mode server intentionally concentrates capability into a discover-and-execute pair, so 2 tools is defensible rather than a thin surface. It is on the low end though, since discovery (search) and execution (execute) are the only entry points.

Completeness4/5

The domain is programmatic Slack access, and the pair covers the full lifecycle: search to discover allowed Web API methods and referenced types, execute to call them. Minor gaps exist (no explicit auth/progress/error tooling), but the code-mode design lets users work around these by invoking the API directly.

Maintenance

ActivityMaintained
ResponsivenessNo issues