Add Slack
The shop's mechanics live in Slack all day. Making them open a browser tab to ask the dispatcher anything seems rude. So let's meet them where they already are.
Adding Slack should leave the tools, skill, state, and approval logic untouched. A channel normalizes incoming messages, tracks how to resume the conversation, and sends replies. One file in channels/ makes Slack another client of the dispatcher.
Credentials are the new concern here. Vercel Connect manages them, so your code never handles a SLACK_BOT_TOKEN.
Outcome
The dispatcher answers @mentions in Slack, in threads, with no changes to any tool, skill, or state.
Hands-on exercise
Set up Connect. Slack delivers events to a public URL and needs a verified bot token to reply. Vercel Connect brokers both, so there's no signing secret or bot token in your code. Connect authenticates your deployment by its Vercel project's OIDC token, so first link the project and pull a development token:
vercel link
vercel env pullNow create a Slack connector and attach it to the project, with its webhook trigger pointed at eve's Slack route:
vercel connect create slack --name spoke-and-mirror --triggers
vercel connect attach slack/spoke-and-mirror --triggers --trigger-path /eve/v1/slackNote --triggers appears on both commands, and both matter:
create … --triggersregisters the connector and turns on webhook forwarding for it. Your browser opens to set it up and authorize the Slack app for your workspace. Without--triggershere, no events ever flow, even if you register a destination, andattachwill warn you about exactly that.attach … --triggersregisters this project as the destination Connect forwards verified events to.--trigger-path /eve/v1/slackis required because the default is/slackand eve serves its Slack route at/eve/v1/slack.
Write the channel. Install the Connect helper and create agent/channels/slack.ts:
npm install @vercel/connectThe channel defines where credentials come from, when to dispatch a turn, and how to deliver the reply.
- Credentials:
connectSlackCredentials(process.env.SLACK_CONNECTOR ?? "slack/spoke-and-mirror")returns the bot token and webhook verifier, both managed by Connect. Reading the uid fromSLACK_CONNECTORkeeps it out of code; the fallback matches the connector you just named, so it works without setting the variable. - Dispatch:
onAppMentiondecides whether a mention becomes a turn. UsedefaultSlackAuthto stamp workspace-scoped auth (the sameauththe per-tier playbook reads), and ignore bot chatter. - Delivery: on
message.completed, post the final reply to the thread, skipping interim tool-call narration.
Because Slack delivers over the public internet, you can't exercise this one on localhost. You'll deploy to get a URL. We cover deployment properly in Section 5; for now, ship it with npx eve deploy, which wraps vercel deploy --prod, installs dependencies, and pulls your environment:
npx eve deployTry It
In a Slack workspace where the app is installed, mention the bot in a channel:
@dispatcher my commuter's front brake is rubbing, what's that cost to fix?The bot replies in a thread, runs lookup_service, and quotes the catalog price. A reply in the thread continues the session. The approval gate also carries over: ask it to book the Full Overhaul and Slack renders the approve/deny prompt as buttons.
Bot shows up in Slack but never replies? First confirm forwarding is enabled on the connector. vercel connect attach warns Triggers are not enabled on this connector if you created it without --triggers. Recreate it with vercel connect remove slack/spoke-and-mirror --disconnect-all --yes, followed by create … --triggers and another attach. Then confirm the destination uses --triggers and --trigger-path /eve/v1/slack. By default, the channel gives the model the triggering mention rather than the earlier thread backlog. Opt into thread context if you need that history.
Done-When
- A Connect Slack client is attached with trigger path
/eve/v1/slack. agent/channels/slack.tsexportsslackChannelwithconnectSlackCredentials.@mentioningthe bot returns a real, tool-backed answer in a thread.- An expensive booking renders approve/deny as Slack buttons.
Solution
import { connectSlackCredentials } from "@vercel/connect/eve";
import { defaultSlackAuth, slackChannel } from "eve/channels/slack";
export default slackChannel({
// The connector uid lives in SLACK_CONNECTOR (set it on the project, or leave
// it unset). The fallback matches the connector you named with `vercel connect
// create slack --name spoke-and-mirror`, so this works out of the box.
credentials: connectSlackCredentials(
process.env.SLACK_CONNECTOR ?? "slack/spoke-and-mirror",
),
// Answer @mentions from a real user; ignore bot chatter. defaultSlackAuth
// stamps workspace-scoped auth, which is what the per-tier playbook reads.
onAppMention: (ctx, message) =>
message.author ? { auth: defaultSlackAuth(message, ctx) } : null,
events: {
// Post the final reply to the thread, skipping interim tool-call narration.
// Event handlers receive (eventData, channel, ctx); Slack handles live on `channel`.
"message.completed"(eventData, channel, ctx) {
if (eventData.finishReason === "tool-calls") return;
if (eventData.message) channel.thread.post(eventData.message);
},
},
});The same agent now has web and Slack entrypoints. Before shipping, we'll replace the test identity with authenticated customer data.
Was this helpful?