New Project
An open-source AI Slackbot.

An AI-powered chatbot for Slack powered by the AI SDK by Vercel.
policies/, editable without touching TypeScript. See policies/README.md.opa) — only needed to edit policies (pnpm policy:build / pnpm policy:test); the committed policies/policy.wasm runs without itnpm install# orpnpm install
Go to "Basic Information"
SLACK_SIGNING_SECRETGo to "App Home"
Go to "OAuth & Permissions"
Add the following Bot Token Scopes:
app_mentions:readassistant:writechat:writechannels:history (read thread history when continuing a conversation in a public channel; add groups:history for private channels and mpim:history for group DMs)im:historyim:readim:writeInstall the app to your workspace and note down the "Bot User OAuth Token" for the environment variable SLACK_BOT_TOKEN
Scopes are easy to miss:
channels:historyis required to continue a conversation in a thread (mention in a public channel). If you add scopes after installing, you must reinstall the app to your workspace for them to take effect.
Go to "Event Subscriptions"
https://your-app.vercel.app/api/events)app_mentionassistant_thread_startedmessage:imRemember to include
/api/eventsin the Request URL.
You may need to refresh Slack with CMD+R or CTRL+R to pick up certain changes, such as enabling the chat tab
Create a .env file in the root of your project with the following:
# Slack CredentialsSLACK_BOT_TOKEN=xoxb-your-bot-tokenSLACK_SIGNING_SECRET=your-signing-secret# Model provider (defaults to OpenAI gpt-4o)OPENAI_API_KEY=your-openai-api-key# Exa API Key (for web search functionality)EXA_API_KEY=your-exa-api-key
Replace the placeholder values with your actual tokens. See .env.example for optional settings (MODEL_PROVIDER, OLLAMA_MODEL, OPENAI_MODEL, POLICY_MODE).
Use the Vercel CLI and untun to test out this project locally:
pnpm i -g vercelpnpm vercel dev --listen 3000 --yes
npx untun@latest tunnel http://localhost:3000
Make sure to modify the subscription URL to the untun URL.
Note: you may encounter issues locally with
waitUntil. This is being investigated.
Push your code to a GitHub repository
Deploy to Vercel:
Add your environment variables in the Vercel project settings:
SLACK_BOT_TOKENSLACK_SIGNING_SECRETOPENAI_API_KEYEXA_API_KEYAfter deployment, Vercel will provide you with a production URL
Update your Slack App configuration:
Go to your Slack App settings
Select your app
Go to "Event Subscriptions"
https://your-app.vercel.app/api/eventsSave Changes
The bot will respond to:
@YourBotNameThe bot maintains context within both threads and direct messages, so it can follow along with the conversation.
Weather Tool: The bot can fetch real-time weather information for any location.
Web Search: The bot can search the web for up-to-date information using Exa.
The chatbot is built with an extensible architecture using the AI SDK's tool system. You can easily add new tools such as:
To add a new tool, extend the tools object in the lib/generate-response.ts file following the existing pattern.
You can also disable any of the existing tools by removing the tool in the lib/generate-response.ts file.
Every tool call is gated by Open Policy Agent (Rego) policies in policies/decision.rego — you can change what the bot is allowed to do by editing that file, no TypeScript changes required. POLICY_MODE defaults to in-process WASM (the committed policies/policy.wasm), so running the bot needs no opa binary. See policies/README.md for the input shape, how to add a rule, dev (HTTP hot-reload) vs prod (WASM), and pnpm policy:test.
The policy gates tool calls — what a tool is allowed to do. It does not
constrain the model's free text. For example, the searchWeb policy guarantees a
web search only ever queries vercel.com; it cannot stop the model from then
answering a question from its own training knowledge (e.g. explaining Bitcoin even
when the vercel.com search returned nothing). The system prompt asks the model to
answer only from tool results, but that is best-effort, not enforced — a hard
"only answer from allowed sources" guarantee would require output-side validation
(checking the answer is supported by the tool results before sending), which is
out of scope here. The 🔧 footer on each reply shows which tool actually ran, so
a tool-less or unscoped answer is at least visible.
This makes policy gating a strong fit for deterministic, inspectable tool
calls — running a shell/git command, writing a file, a Slack/MCP action —
where "is this action allowed?" is a clear decision on concrete inputs, and the
decision fully bounds the effect. It's a weaker fit for content-oriented tools
(like web search) whose value is the nuanced text the model synthesizes from the
result: gating the call constrains what gets fetched, not what the model
ultimately says. Reach for this pattern where the tool is the action; pair it
with other controls where the risk is in generated content.
Instead of OpenAI you can run a local model for free, offline:
brew install ollama # or download from https://ollama.comollama pull llama3.1ollama serve # if not already running
Then set MODEL_PROVIDER=ollama in .env. You don't need an OPENAI_API_KEY on this path — a full .env for local Ollama looks like:
# Slack CredentialsSLACK_BOT_TOKEN=xoxb-your-bot-tokenSLACK_SIGNING_SECRET=your-signing-secret# Use a local Ollama model instead of OpenAI (no OPENAI_API_KEY needed)MODEL_PROVIDER=ollamaOLLAMA_MODEL=llama3.1 # optional; defaults to llama3.1# Exa API Key (only needed if you want web search to run)EXA_API_KEY=your-exa-api-key
Small models like
llama3.1:8bsometimes call tools when they shouldn't (e.g. answering "hi" with a weather report) or invent sources.qwen2.5follows tool instructions more reliably —ollama pull qwen2.5and setOLLAMA_MODEL=qwen2.5. The default hosted model (gpt-4o) doesn't have this problem.
Local only: Ollama serves on
localhost:11434, which a deployed Vercel function cannot reach. Use Ollama for local development; deploy with a hosted provider (the default OpenAI, or point a provider at a reachable host).
MIT