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
Add the Slack channel. 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. eve guides the whole setup:
npx eve add channel/slackThe command links or creates a Vercel project, creates or reuses a Slack connector, opens Slack authorization in your browser, registers /eve/v1/slack as the trigger destination, and writes agent/channels/slack.ts. The scaffold from 1.1 already lists @vercel/connect in package.json, so there's nothing extra to install. Follow the prompts and finish the workspace authorization in the browser.
Inspect and extend the channel. The generated agent/channels/slack.ts defines where credentials come from. Replace it with the version in the solution below to make the dispatch rule explicit.
- 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 assumes the guided setup named the connector after yourspoke-and-mirrorproject. If the generated file shows a different uid, setSLACK_CONNECTORto that one. - Dispatch:
onAppMentiondecides whether a mention becomes a turn. UsedefaultSlackAuthto stamp trusted Slack identity, and returnnullwhenmessage.author?.isBotis true so other bots can't start turns. eve already drops messages from your own app, so the dispatcher never answers itself. - Delivery: nothing to write. eve's default renderer posts the final reply to the thread and shows interim tool-call narration as the thread's status line (
Thinking..., then the tool it's running), so the thread holds the conversation and not the play-by-play.
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? Re-run npx eve add channel/slack; it inspects the current project, connector, Slack installation, and trigger destination without blindly creating another app. Confirm the destination is /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 a connector named after the spoke-and-mirror
// project, which is what `eve add channel/slack` usually creates.
credentials: connectSlackCredentials(
process.env.SLACK_CONNECTOR ?? "slack/spoke-and-mirror",
),
// Answer @mentions from people; ignore other bots. defaultSlackAuth stamps
// Slack identity, but does not invent a shop membership tier.
onAppMention: (ctx, message) =>
message.author?.isBot ? null : { auth: defaultSlackAuth(message, ctx) },
// No delivery code needed: eve's default renderer posts the final reply to
// the thread and shows tool-call narration as the thread status instead.
});The same agent now has web and Slack entrypoints. Before shipping, we'll replace the test identity with authenticated customer data.
Was this helpful?