ByteChef Embedded, Part 3: The ComponentKit Chat

On this page
TL;DR: Once your users have connected their apps, you can let an AI assistant use them. The embedded sample app's ComponentKit Chat is an assistant-ui chat whose backend route (
/api/chat-component-kit) fetches the user's tools from ByteChef, hands them to the model through the AI SDK, and sends every tool call back to ByteChef to run on that user's connection. Chat that does things, not just talks. This is part three of the series.
An AI chat that can only talk is a demo. An AI chat that can act - in your customer's real Slack, real inbox, real S3 bucket - is a feature. In part one your users connected their apps. This part turns those connections into tools a model can call, and the assistant becomes an agent.
Chat, Wired to Tools
The page is a standard assistant-ui thread over an AI SDK transport, plus a few welcome-screen suggestions:
const SUGGESTIONS: SuggestionConfig[] = [
{ title: "Send a Slack message", label: "to a channel", prompt: "Send a Slack message to a channel" },
{ title: "Draft and send an email", label: "with Gmail", prompt: "Draft and send an email with Gmail" },
// Mailchimp subscriber, AWS S3 upload...
];
export default function ChatComponentKitPage() {
const runtime = useChatRuntime({
transport: new DefaultChatTransport({ api: "/api/chat-component-kit" }),
});
const aui = useAui({ suggestions: Suggestions(SUGGESTIONS) }, { parent: null });
return (
<AssistantRuntimeProvider aui={aui} runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>
);
}The suggestions show users what the assistant can do before they type anything, so nobody has to read a manual.
The Backend Route: Ask ByteChef for Tools
The interesting half is the route. It doesn't hard-code any tools. It asks ByteChef which tools this connected user has:
const response = await fetch(
`${BYTECHEF_APP_BASE_URL}/api/embedded/v1/${BYTECHEF_EXTERNAL_USER_ID}/tools`,
{ headers: { Authorization: `Bearer ${jwtToken}`, "X-Environment": BYTECHEF_ENVIRONMENT } }
);ByteChef looks at the integrations the user has connected and returns the tools those components provide, grouped by component. Each tool comes with a name like SLACK_SEND_CHANNEL_MESSAGE, a description, and a JSON Schema for its inputs. That is everything a model needs, so the route turns each one into an AI SDK tool whose execute posts the call straight back:
defineTool({
description: curTool.function.description,
inputSchema: jsonSchema(JSON.parse(curTool.function.parameters)),
execute: async (params) => {
const res = await fetch(`${BYTECHEF_APP_BASE_URL}/api/embedded/v1/${BYTECHEF_EXTERNAL_USER_ID}/tools`, {
method: "POST",
headers: { Authorization: `Bearer ${jwtToken}`, "Content-Type": "application/json", "X-Environment": BYTECHEF_ENVIRONMENT },
body: JSON.stringify({ name: curTool.function.name, parameters: params }),
});
// ...throw on error, return the output to the model
},
});Then it's one streamText call:
const result = streamText({
model: openai.chat("gpt-5"),
messages: await convertToModelMessages(messages),
tools: { ...tools, ...frontendTools(clientTools ?? {}) },
stopWhen: stepCountIs(8),
});Here's a full turn:
- The user says "let the #sales channel know the demo is booked."
- The model calls
SLACK_SEND_CHANNEL_MESSAGEwith a channel and text. - The route posts that call to ByteChef, which finds this user's Slack connection and runs the action with their credentials.
- The result goes back to the model, which confirms in the chat.
stopWhen: stepCountIs(8) is what makes it an agent loop. Without it the AI SDK stops after the first tool call, and the model never sees the result. With it, the model can call a tool, read the output, call another, and answer, for up to eight steps.
Deciding What the Assistant Can Touch
The sample hands the model every tool the user has. In your product you'll usually want less, and there are two ways to get it:
- Integrations. Tools only exist for integrations the user has connected. An app nobody connected contributes nothing.
- Filters.
GET .../toolsacceptscategories,components, andtoolsquery parameters. Passcomponents=slack&components=gmailand those two apps are the whole toolbox, even if the user has connected ten.toolsgoes one level down and takes operation names, sotools=sendChannelMessagekeeps a single Slack action.
Be precise about what this limits. The model can't call a tool it was never given. But within the tools it has, it can do whatever the user's credentials allow: post to any channel, email anyone. If a tool is destructive or public-facing, either leave it out or add a confirmation step before it runs.
One more thing to notice in the route: frontendTools(clientTools) merges in tools the browser registers, and those run client-side. Drop that line if you want the server to be the only thing defining the toolbox.
What You Didn't Build
- A tool catalog. ByteChef generates names, descriptions, and input schemas from its components.
- A tool-calling agent loop. The AI SDK runs it.
- Per-user credential handling. Every call runs on the connection the user created in part one.
- Any per-app API client. Slack, Gmail, Mailchimp, and S3 are all one
POST .../tools. - The chat UI. assistant-ui provides streaming and suggestions.
Running It Yourself
On top of the setup from part one:
- Add an OpenAI key. The route uses
openai.chat("gpt-5"), so setOPENAI_API_KEYinfront-end/.env.local. - Connect an integration. Connect Slack, Gmail, Mailchimp, or AWS S3 for your demo user first. With nothing connected, the tools list is empty and the assistant can only talk.
What's Next
The ComponentKit Chat adapts ByteChef's tools to the AI SDK in your own route. Next up is the sample app's MCP Chat, which reaches the same kind of tools through the Model Context Protocol instead - the open standard that clients like Claude and Cursor already speak.
Following along? Connect an integration, add your OpenAI key, and open ComponentKit Chat in the sample app - your assistant can now act in your users' apps.
Subscribe to the ByteChef Newsletter
Get the latest guides on complex automation, AI agents, and visual workflow best practices delivered to your inbox.