Vercel Chat SDKは、Slack・Microsoft Teams・Google Chat・Discordなどのボットを、1つのTypeScriptのコードで動かすためのVercel製オープンソースSDKです。この記事では、npmのchatパッケージ4.45系を前提に、create-chat-sdkでSlackボットの雛形を作り、Next.jsのWebhookルートで受け、同じハンドラーにTeamsを足すまでをコード付きで示します。Vercel AI SDKの応答をスレッドへストリーミングする書き方と、プラットフォームごとに変わる表示の仕方も、2026年11月時点の公式ドキュメントで確認しました。後半では、Redisのステート、同時に届いたメッセージの扱い、関数の実行時間上限という本番で詰まる3点と、Boltなど公式SDKを選ぶべき条件を言い切ります。
まとめ:Vercel Chat SDKは複数のチャットへ同じハンドラーを配る中継層
Chat SDKが引き受けるのは、各チャットサービスのWebhookの署名検証、受信データの共通形式への変換、返信の各サービス向け書式への変換です。開発者はonNewMentionなどのハンドラーを1回書けば、登録したアダプターの数だけ同じ処理が動きます。ライブラリ自体は無料で、費用が発生するのは関数の実行、Redisなどのステート置き場、LLMの利用料です。
本番で外せない設定は3つあります。ステートをメモリからRedisかPostgreSQLに替えること、AIの回答ボットでは同時実行の戦略を既定のdropからqueueなどに変えること、関数の実行時間上限(Hobbyは300秒)に収まらない処理をVercel Workflowsへ逃がすことです。
選び方も明快です。2つ以上のチャットサービスに同じボットを出す予定があり、TypeScriptで書けるなら採用してよい。Slackだけで完結し、Slack固有の機能を深く使うなら、Boltなど公式SDKのほうが筋がよいと判断します。
Vercel Chat SDKの構成要素とChat・アダプター・ステートの役割分担
Vercelそのものの仕組みとホスティングの全体像はVercel(ヴァーセル)とは?サーバー不要の仕組みとできること・料金で扱っています。ここではChat SDKの部品の分かれ方に絞ります。
2026年2月のパブリックベータ公開とnpmパッケージchat 4.45系の現状
Chat SDKは2026年2月23日にVercelのchangelogでオープンソースのパブリックベータとして発表されました。発表時の対応はSlack・Teams・Google Chat・Discord・GitHub・Linearの6つです。ソースはGitHubのvercel/chatリポジトリにあり、ライセンスはMITです。
npm registryで2026年11月15日に確認した最新版は、本体のchatが4.45.0(11月13日公開)でした。Slack・Teams・Redis用のアダプターも同じ4.45.0で、本体と版番号をそろえて出ています。2026年11月時点の公式ドキュメントにベータの表記は見当たりませんが、正式版への移行を告げる発表も確認できていません。毎週のように版が上がっているため、package.jsonでは本体とアダプターの版を同時に更新する運用が安全です。
Chatクラス・プラットフォームアダプター・ステートアダプターの3層構造
Chat SDKの公式ドキュメントは、部品を3つに分けて説明しています。
| 部品 | 担当する処理 | 代表的なパッケージ |
|---|---|---|
| Chat | イベントの振り分け | chat |
| プラットフォームアダプター | 署名検証・変換・API呼び出し | @chat-adapter/slack |
| ステートアダプター | 購読・ロック・重複排除 | state-redis など |
ボットがthread.subscribe()でスレッドを購読すると、その記録はステートに残ります。以後そのスレッドの発言はonNewMentionではなくonSubscribedMessageに届く仕組みです。サーバーレスでは呼び出しごとに別のインスタンスが動くので、購読もロックも外部のストアに置く必要があります。
公式18種とLINEを含むコミュニティ製に分かれる対応アダプター
2026年11月時点で、Vercelが保守する公式のプラットフォームアダプターは18種です。Slack、Teams、Google Chat、Gmail、Discord、Telegram、GitHub、GitLab、Linear、Notion、WhatsApp Business Cloud、Twilio、Messenger、Instagram、X、XChat、Twitch、ブラウザ用のWebがそろっています。ステート用はメモリ・Redis・ioredis・PostgreSQLの4種です。
このほかに、各サービスの提供元が保守するvendor-officialと、第三者が作るcommunityの2つの階層があります。国内で需要の多いLINEはcommunity階層で、LINEアダプターのページによるとパッケージ名はchat-adapter-lineです。Flex Messageのカードやポストバックは扱えますが、応答はまとめて送る方式で、ファイルの送信には対応していません。LINEだけのボットなら、LINE Bot APIの実装手順:Webhook署名検証・reply・生成AI接続をPythonで解説のとおり公式APIを直接呼ぶほうが制約は少なくなります。
create-chat-sdkでSlackボットの雛形を作りWebhookで受ける手順
ここからは、Slackのメンションに反応するボットをNext.jsで作り、Vercelに置くまでを順に進めます。
create-chat-sdkの非対話モードでSlackとRedisの雛形を生成する手順
create-chat-sdkのドキュメントのとおり、アダプターを--adapterで渡すと質問なしで雛形ができます。npmの場合は--の区切りが必須です。
# SlackとRedisを選んで雛形を作る(npmでは -- が必要)
npm create chat-sdk@latest -- my-bot --adapter slack redis -y
cd my-bot
cp .env.example .env.local
# Vercelのプロジェクトに紐づけ、Upstash Redisを作ってREDIS_URLを取り込む
vercel link
vercel integration add upstash/upstash-kv
npm run dev
生成されるのはWebhook専用のNext.jsアプリで、画面は含まれません。主なファイルはsrc/lib/bot.ts(ボットの設定とハンドラー)とsrc/app/api/webhooks/[platform]/route.ts(Webhookの受け口)の2つです。Claude CodeやCursorから実行すると、CLIは自動で非対話モードになり、ステートを指定しなければmemoryが選ばれます。
マニフェストでSlackアプリを作りSLACK_BOT_TOKENを設定する手順
Slackアダプターのページには、api.slack.com/appsでそのまま貼れるマニフェストが載っています。抜粋すると、必要なのは次の権限とイベントです。
oauth_config:
scopes:
bot:
- app_mentions:read
- channels:history
- chat:write
- im:history
- reactions:write
- users:read
settings:
event_subscriptions:
request_url: https://your-domain.com/api/webhooks/slack
bot_events:
- app_mention
- message.channels
- message.im
interactivity:
is_enabled: true
request_url: https://your-domain.com/api/webhooks/slack
作成後、Bot User OAuth TokenをSLACK_BOT_TOKENに、Signing SecretをSLACK_SIGNING_SECRETに入れます。アダプターはこの2つを環境変数から自動で読むため、コードには書きません。Productionと Previewで値を分ける方法はVercelの環境変数の設定手順:Production・Previewの分け方とvercel env pull・Secret型にまとめました。複数のワークスペースに配る場合は、代わりにSLACK_CLIENT_IDとSLACK_CLIENT_SECRETを使うOAuthの構成になります。
bot.tsとSlack用route.tsで書くWebhook受信の最小コード例
雛形の中身を最小にすると、次の2ファイルです。コードはChatインスタンスの作成ページの例に沿っています。
// src/lib/bot.ts
import { Chat } from "chat";
import { createSlackAdapter } from "@chat-adapter/slack";
import { createRedisState } from "@chat-adapter/state-redis";
export const bot = new Chat({
userName: "mybot",
adapters: {
slack: createSlackAdapter(), // SLACK_BOT_TOKEN と SLACK_SIGNING_SECRET を自動で読む
},
state: createRedisState(), // REDIS_URL を自動で読む
});
bot.onNewMention(async (thread) => {
await thread.subscribe(); // 以後このスレッドの発言は onSubscribedMessage へ
await thread.post("このスレッドでの質問に答えます。");
});
// src/app/api/webhooks/slack/route.ts
import { after } from "next/server";
import { bot } from "@/lib/bot";
export const maxDuration = 300; // 応答後の処理もこの上限に含まれる
export async function POST(request: Request): Promise<Response> {
return bot.webhooks.slack(request, {
waitUntil: (task) => after(() => task),
});
}
要はwaitUntilです。SlackのEvents APIの仕様では、イベントには3秒以内に2xxを返す必要があり、失敗するとほぼ即時・1分後・5分後の3回再送されます。Chat SDKは先に200を返し、ハンドラーの処理をNext.jsのafterで続けます。再送された同じイベントは、既定で10分間(dedupeTtlMsの600000ミリ秒)重複として捨てられるため、二重返信も起きません。
同じハンドラーにTeamsを足しAI SDKの応答をストリーミングで返す実装
Chat SDKを選ぶ理由は、ここで書くコードの少なさにあります。Slack用に書いたハンドラーを変えずに、アダプターを1行足すだけでTeamsにも同じボットが出ます。
Teams CLIでAzureのボットを登録しTEAMS_APP_IDを取り込む手順
Teamsアダプターのページは、Microsoftが配布するTeams CLIでの登録を案内しています。Entra IDのアプリ登録、クライアントシークレットの発行、ボットの登録、Teamsチャネルの設定が1つのコマンドで済みます。
npm install -g @microsoft/teams.cli
teams login
teams app create --name "My Bot" --endpoint "https://your-domain.com/api/webhooks/teams" --env .env
# .env に出力された値を、アダプターが読む名前に付け替える
TEAMS_APP_ID=<CLIENT_ID>
TEAMS_APP_PASSWORD=<CLIENT_SECRET>
TEAMS_APP_TENANT_ID=<TENANT_ID>
# 登録内容とエンドポイントを点検する
teams app doctor <appId>
シングルテナントのアプリと、過去のメッセージを読む処理ではTEAMS_APP_TENANT_IDが必須です。Graph APIでの送信やAdaptive Cardの扱いをTeams単体で深掘りしたい場合は、Teams APIでメッセージ送信する方法:Graph・Workflows・Botの実装とメンション・カードのコード例を参照してください。
toAiMessagesでスレッド履歴を渡しfullStreamで返すAI回答のコード例
bot.tsにteams: createTeamsAdapter()を足し、app/api/webhooks/teams/route.tsをbot.webhooks.teamsで作れば受信側は完了です。そのうえで、購読中のスレッドの発言にLLMで答えるハンドラーを書きます。ストリーミングのドキュメントは、AI SDKのfullStreamとtextStreamのどちらもthread.post()に直接渡せるとしています。
// src/lib/bot.ts への追記(npm i ai @chat-adapter/teams)
import { streamText } from "ai";
import { toAiMessages } from "chat/ai";
const model = process.env.AI_GATEWAY_MODEL; // AI GatewayのモデルIDを環境変数で渡す
if (!model) throw new Error("AI_GATEWAY_MODEL を設定してください");
bot.onSubscribedMessage(async (thread) => {
await thread.startTyping();
// スレッドの発言を集め、直近20件だけをモデルに渡す
const all = [];
for await (const msg of thread.allMessages) all.push(msg);
const history = await toAiMessages(all.slice(-20));
const result = streamText({
model,
system: "社内ヘルプデスクとして、日本語で簡潔に答えてください。",
messages: history,
abortSignal: thread.signal, // Slackの停止ボタンで生成を止める
});
await thread.post(result.fullStream);
});
textStreamではなくfullStreamを渡すのは、ツール呼び出しを挟む複数ステップの応答で段落の区切りを保つためです。textStreamはステップ間の文をつなげてしまい、「hello.how are you?」のような表示になると公式が説明しています。モデルの呼び出し先をAI Gatewayにする設定はVercel AI Gatewayとは:複数LLMを1つのAPIで切り替える設定手順と料金・LiteLLMとの選び方、streamTextやツール定義の書き方はVercel AI SDKの使い方|AI SDK 7でNext.jsチャットとLangChain連携を実装で解説しています。
Slackはネイティブ・TeamsのグループやGitHubはまとめ送信になる表示差
同じthread.post(result.fullStream)でも、相手先によって表示のされ方は変わります。2026年11月時点の公式ドキュメントの記載は次のとおりです。
| プラットフォーム | ストリーミングの方式 |
|---|---|
| Slack | ネイティブ(chatStream) |
| Teams | DMはネイティブ・グループはまとめ送信 |
| Google Chat・Discord | 投稿してから編集 |
| Telegram | 投稿してから編集(毎秒1回程度) |
| GitHub・WhatsApp | 完成後にまとめて1通 |
「投稿してから編集」の方式では、最初に「…」を出し、500ミリ秒ごとに本文を書き換えます。間隔はstreamingUpdateIntervalMsで変えられます。TeamsのチャネルやWhatsAppでは、応答が完成するまで何も表示されません。長い回答では利用者が待たされるため、先に「調べています」と短く投稿しておく設計が有効です。
本番運用で詰まるRedisステート・同時実行・関数の実行時間上限の設定
手元で動いたボットが本番で黙る原因は、コードよりも設定の側にあります。ここでは公式ドキュメントで既定値が決まっている3点を確認します。
メモリステートを本番で使えない理由とRedis 6.0以上・クラスター非対応
ステートアダプターのドキュメントによると、メモリ版はプロセスの中にしか状態を持たないため、ローカル開発とテスト用です。Vercelでは呼び出しごとにインスタンスが変わりうるので、メモリ版のまま公開すると購読が消え、2回目以降の発言に反応しなくなります。
本番はRedis・ioredis・PostgreSQLのいずれかです。Redisアダプターのページは、Redis 6.0以上、永続化(RDBかAOF)の有効化、メモリ上限の明示を求めています。Redis Clusterにはどちらのアダプターも対応していません。既存のRedisを共用するなら、keyPrefix(既定はchat-sdk)をボットごとに分けておくと、キーの衝突を避けられます。社内でPostgreSQLを運用しているなら、Redisを増やさずに@chat-adapter/state-pgを選ぶ判断もあります。
既定dropが連投を捨てる挙動とAIボット向けqueue・burstの設定
AIの回答ボットで最初に問題になるのが、同時実行の既定値です。同時実行のドキュメントによると、既定のdropでは、ハンドラーが処理中のスレッドに届いた新しい発言はLockErrorとともに捨てられます。LLMが30秒かけて答えている間に追加した質問は、記録もされずに消える仕様です。
// src/lib/bot.ts の Chat 生成部分(import は前のコード例と同じ)
const bot = new Chat({
userName: "mybot",
adapters: { slack: createSlackAdapter(), teams: createTeamsAdapter() },
state: createRedisState(),
// 処理中に届いた発言を最大10件ためて、終わったら最新の1件で再実行する
concurrency: { strategy: "queue", maxQueueSize: 10, queueEntryTtlMs: 90_000 },
});
// ためられた発言は context.skipped で受け取れる
bot.onSubscribedMessage(async (thread, message, context) => {
const turn = [...(context?.skipped ?? []), message].map((m) => m.text).join("\n\n");
await thread.post(`受け取った質問:${turn.slice(0, 200)}`);
});
戦略は次の5つです。社内ヘルプデスクのように追加の質問を拾いたいならqueue、「すみません」「質問です」と分けて送られがちな相手なら最初の発言から1.5秒待ってまとめるburstが向いています。WhatsAppやTelegramは既定でロックの単位がチャンネルになるため、短文の連投にはdebounceを検討します。
after内の処理も含むHobby300秒・Pro800秒の上限
afterで続けた処理も、関数の実行時間上限の内側で動きます。関数の実行時間のドキュメントでは、Fluid compute有効時の上限はHobbyが300秒、ProとEnterpriseが800秒で、ベータとして最大1,800秒まで延ばせます。既定値はどのプランでも300秒です。
RAGの検索と複数回のツール呼び出しを重ねるエージェントは、300秒を超えることがあります。超えると関数は途中で打ち切られ、スレッドには途中までの応答が残る形です。人の承認を待つ処理や数分を超える処理は、Vercel公式のChat SDKのページが案内するとおりVercel Workflowsに切り出します。Discordの通常メッセージを受けるGatewayの常時接続も、9分おきのCronで保つ構成なので、Proプラン以上が前提です。プランごとの単価と無料枠はVercel料金|Hobby無料枠とPro($20)の中身・超過単価で確認できます。
Vercel Chat SDKとBolt・プラットフォーム公式SDKの選定条件
比べる相手は、Slackが提供するBoltや、TeamsのBot Framework、Discordの各ライブラリといった単一プラットフォーム用の公式SDKです。Slack単体の実装はSlack Botの作り方:Bolt for Pythonでメンション返信からLambda運用まで、Discord単体はDiscord Botの作り方|アプリ登録から24時間稼働までを手順どおりにで扱いました。
| 比較の観点 | Vercel Chat SDK | Boltなど公式SDK |
|---|---|---|
| 対応先 | 1つのコードで複数 | 1サービスのみ |
| 言語 | TypeScriptのみ | Python・Java等も可 |
| 固有機能の網羅 | アダプターごとに差 | 全機能 |
| モーダル | SlackとTeamsのみ | 各サービスの仕様どおり |
| AIの逐次表示 | post一つで共通化 | サービスごとに実装 |
モーダルがSlackとTeamsに限られる点は、VercelのChat SDK完全ガイドに明記されています。共通化の代わりに、各サービスの細部はアダプターの実装しだいになる構造です。
Vercel Chat SDKを採用してよい社内ボット・AIエージェントの条件
次のいずれかに当てはまるなら、Chat SDKを選んで構いません。
- SlackとTeamsを併用している、またはグループ会社ごとに使うチャットが違う
- LLMの回答をスレッドで逐次表示したく、その実装をサービスごとに書きたくない
- フロントエンドがNext.jsで、ボットも同じTypeScriptのコードベースで保守したい
- 将来、GitHubやLinearのコメント欄にも同じエージェントを出す可能性がある
最初はSlackだけでも、2つ目のチャットが視野に入っているなら最初からChat SDKで書く価値があります。後から乗り換えるより、アダプターを1つ足すほうが手間は小さく済みます。
Vercel Chat SDKを見送り公式SDKや受託開発を選ぶべき場面
逆に、次のどれかに当てはまるならChat SDKは採用しません。対象がSlackだけで、Slack固有のAPIを細部まで作り込む場合。バックエンドがPythonやJavaで統一されていて、TypeScriptの実行環境を新たに持ちたくない場合。LINEが主な窓口で、ファイル送信や公式の機能をすべて使いたい場合です。
1つ目の場合はBoltで書くほうが早く、AIの回答を付ける実装もSlack BotにAIで回答させる作り方:Bolt×LLM APIのストリーミング・RAG・権限設計の構成で足ります。社内のSlackやTeamsに生成AIの回答ボットを置きたいが、権限設計や社内文書の検索まで含めて任せたい場合は、AIチャットボット開発でご相談いただけます。
Vercel Chat SDKの導入前によく挙がる質問と公式ドキュメントの回答
導入の検討で確認されることの多い5点を、2026年11月時点の公式ドキュメントに沿って答えます。
Vercel Chat SDKは無料で使えますか?
ライブラリはMITライセンスのオープンソースで、利用料はかかりません。費用が発生するのは、Webhookを受ける関数の実行、ステートを置くRedisやPostgreSQL、LLMの利用料の3つです。各チャットサービスのAPIの利用条件とレート制限には別途従います。
Vercel以外のサーバーやクラウドでも動かせますか?
Vercel以外でも動かせます。Webhookのハンドラーは標準のWebのRequestを受けてResponseを返す作りなので、Request/Responseを扱えるランタイムなら置き場所を選びません。応答後に処理を続ける関数は、各基盤のバックグラウンド実行の仕組みをwaitUntilに渡します。Vercel Connectによる認証情報の管理は任意で、使わない場合は環境変数から読み込みます。
Chat SDKとVercel AI SDKは何が違いますか?
別の製品です。AI SDK(npmのai、2026年11月時点で7.0系)はLLMを呼び出して応答を生成する側、Chat SDKはその応答をSlackやTeamsのスレッドに届ける側を担当します。Chat SDKはLLMなしでも使え、スラッシュコマンドへの応答や通知だけのボットも作れます。組み合わせる場合は、AI SDKのfullStreamをthread.post()に渡すだけです。
LINEやChatworkのボットにも使えますか?
LINEはコミュニティ製のchat-adapter-lineで接続できますが、Vercelの保守対象ではありません。応答は500文字を超えた時点で1通ずつ送られ、1回の応答は最大5通までです。Chatworkのアダプターは、2026年11月15日時点の公式の一覧に見当たりませんでした。必要なら、公開されているAdapterのインターフェースに沿って自作します。
ボットが2回目以降の発言に反応しないのはなぜですか?
最も多い原因は、ステートがメモリ版のままになっていることです。購読の記録が呼び出しをまたいで残らないため、onSubscribedMessageに振り分けられません。Redisなどに替えても直らない場合は、同時実行の戦略が既定のdropのままで、処理中に届いた発言が捨てられていないかを確認します。ログにmessage-droppedのイベントが出ていれば、それが原因です。
関連記事
- Vercel(ヴァーセル)とは?サーバー不要の仕組みとできること・料金:Vercelの全体像とホスティングの料金
- Vercel AI SDKの使い方|AI SDK 7でNext.jsチャットとLangChain連携を実装:ボットに渡す応答を生成する側の実装
- Vercel AI Gatewayとは:複数LLMを1つのAPIで切り替える設定手順と料金・LiteLLMとの選び方:ボットから呼ぶLLMの窓口と予算設定
- Slack Botの作り方:Bolt for Pythonでメンション返信からLambda運用まで:Slack単体で作る場合の公式SDKの手順
- Teams APIでメッセージ送信する方法:Graph・Workflows・Botの実装とメンション・カードのコード例:Teams単体の送信方式の比較