copilotkit-channels について
CopilotKit のマネージド Intelligence Channels を設定・カスタマイズし、Slack や Microsoft Teams のメッセージをエージェントのランタイムへ届けるスキルです。プロバイダー設定、ランタイム接続、接続や配信の問題の調査を扱います。
主な機能
- 01Slack と Teams のマネージド設定
- 02生成済み・既存プロジェクトの接続
- 03Channel ハンドラーのカスタマイズ
- 04プロバイダー・ソース・サーバーの状態確認
- 05有効化と配信障害の調査
活用シーン
- 01エージェントのランタイムを Slack アプリに接続
- 02Intelligence で Teams Channel を設定
- 03メッセージやメンション処理をカスタマイズ
- 04オンラインなのに応答しない Channel を調査
MCP 対応
CopilotKit Intelligence Channels を扱うスキルです。README には MCP との連携について記載されていません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ copilotkit/ copilotkit --skill copilotkit-channelsインストール後、Claude Code で/copilotkit-channelsを入力して読み込まれたか確認します。
同梱ファイルsources.md
詳細
CopilotKit Channels
マネージドチャネルを使うと、エージェントは Slack または Microsoft Teams で応答できます。プロバイダーとの接続部分(署名付きの受信、送信、認証情報の保存)は Intelligence が管理し、リアルタイムトランスポートを介してターンをランタイムに届けます。
対象範囲
このスキルでは、マネージド Intelligence Channels(CopilotIntelligenceRuntime、app-api の Channel リソース、リアルタイムゲートウェイ)を扱います。
セルフホスト型の @copilotkit/channels プロバイダーアダプター(@copilotkit/channels-slack、-teams、-discord、-whatsapp)は対象外です。これらはプロセス内にプロバイダーの認証情報を保持し、プロバイダーと直接通信します。どちらの製品も「channels」と「Slack」という語を使うため、何かを接続する前に、ユーザーがどちらを指しているのか確認してください。ユーザー自身が Slack トークンを保持し、独自の受信処理を運用したい場合は、このスキルではなくアダプターパッケージを使います。
まず、どちらの方法を使うか確認する
以降の内容はすべてこの判断に依存します。間違えると、1 つの Channel を取り合う 2 つの Channel 宣言があるプロジェクトになってしまいます。
プロジェクトのルートに channel-host.mts があるか確認してください。
| ファイルがある場合 | ファイルがない場合 |
|---|---|
プロジェクトは npx copilotkit init でスキャフォールディングされています。コード側の作業はすでに完了しています。「スキャフォールディング済みの Channel のカスタマイズ」に進んでください。新しいホストや 2 つ目の createChannel は作成しないでください。 | プロジェクトは Channel より前から存在するか、CLI で作成されていません。末尾の「CLI で生成されていないプロジェクトの接続」に進んでください。 |
スキャフォールディング済みの方法は、エンドツーエンドでテストされています。既存のプロジェクトの接続もサポートされており、動作しますが、利用例は比較的少ない方法です。CLI は自ら生成していないソースを読み取るため、一般的でない構成のプロジェクトでは、確実な回答ではなく undetermined として status が報告されることがあります。これは意図的な動作です。不確かな回答のほうが、誤った回答よりも望ましいからです。ただし、この場合は緑色のレポートを信頼するのではなく、接続設定をご自身で確認してください。
Channel は 2 つの要素で構成されます
Channel が機能するには、両方の要素が正しく設定されている必要があります。それぞれの要素は、もう一方からは確認できません。
- プロバイダー側 — Slack または Microsoft にアプリを登録し、認証情報をサーバー側に保存して、受信処理の接続先を Intelligence にします。この設定への入口は 3 つあります。新規プロジェクトでは
npx copilotkit initが案内し、既存のプロジェクトまたはエージェントではnpx copilotkit channels addが対応し、ブラウザーではダッシュボードのウィザードが使えます。新しい Teams アプリの場合は、まずブラウザーで下書きを作成し、以下の 2 つの方法のいずれかを使います。 - コード側 — Channel を宣言し、アクティベーションを待機する、長時間実行されるランタイムです。スキャフォールディング済みのプロジェクトにはすでに含まれています。それ以外のプロジェクトで、このスキルが作成するのはこちらです。
この製品で最も混乱しやすい問題は、プロバイダー側は正しく設定されているのに、コード側が欠けているケースです。アプリは通常どおり HTTP を提供し、エラーも報告せず、ダッシュボードには期待を持たせるバッジが表示されるのに、応答は返しません。欠けている要素はソースツリーにあるため、ブラウザー上では原因を特定できません。
Teams プロバイダーのセットアップには、同等の 2 つの方法があります
Teams の場合は、Microsoft リソースをプロビジョニングする前に、Intelligence で永続的な Channel の下書きを作成してください。その後、ブラウザーに次の方法が表示されます。
-
高速 CLI セットアップ(推奨): 下書きから、スコープが完全に指定されたコマンドをコピーします。
npx copilotkit@latest channels add --project-id <project-id> --channel-id <channel-id> --adapter teams --provision --via skill:copilotkit-channelsコピーしたコマンドに
--via skill:copilotkit-channelsを追加し、このスキルの CLI コマンドすべてに付けてください。これは、呼び出しがこのスキルから行われたことを CopilotKit のテレメトリに伝えるだけです。CLI がUnknown option '--via'と報告した場合、npx は古いキャッシュ済みバージョンを実行しています。フラグを付けずにコマンドを再実行してください。リポジトリ外でも動作し、CopilotKit のログイン状態を再利用して、Microsoft テナントを確認し、シングルテナントの Teams 管理アプリを 1 つ作成して、生成されたシークレットを暗号化された Intelligence ストレージに直接送信します。シークレットを出力したり、プロバイダー環境ファイルを書き込んだり、ランタイムコードを変更したりすることはありません。
-
ガイド付き手動セットアップ: Teams Developer Portal と Microsoft Entra を使い、書き込み専用の認証情報を入力して、完成したパッケージをブラウザーからダウンロードします。通常の手順では Azure Bot は不要で、ユーザーに
manifest.jsonの編集を求めることもありません。
どちらの方法も同じ永続的なセットアップ状態を使用し、セットアップの途中で相互に切り替えることができます。Files.ReadWrite.All への管理者の同意と、チームに追加するインストールが 1 回必要です。管理者以外による一時停止は、承認リンク 1 つと同じ再開コマンドを伴う、正常な blocked の結果です。再実行しても、既存の Microsoft アプリを再利用する必要があります。
カスタムアイコンとパッケージのバイト列は、ブラウザー/CLI のメモリ内または一時的なローカルディレクトリに保持してください。Intelligence、Redis、その他のリレーに送信してはいけません。アップロード前に処理が停止した場合は、ユーザーにカスタムアイコンを再選択させる(またはデフォルトの kite を明示的に選ばせる)必要があります。また、終了時には一時コピーをすべて削除してください。
作成およびインストール済みは、範囲を限定して解釈してください。Microsoft アプリが存在し、認証情報が暗号化されて保存され、ファイルと Team メッセージへのアクセスが承認され、Team へのインストールが記録されていることを意味します。ランタイムがオンラインであることや、メッセージが配信されたことを示すものではありません。
スキャフォールドされた Channel のカスタマイズ
スキャフォールドされたプロジェクトには 3 つのファイルが含まれますが、変更するのはそのうち 1 つだけです。
| ファイル | 内容 | 変更しますか? |
|---|---|---|
channels.mts | Channel:名前の解決、エージェント、ハンドラー | はい — カスタマイズするのはこのファイルです |
channel-host.mts | Channel のライフサイクルを管理するプロセス | いいえ。すべてのスターターおよびプロバイダーで同一です |
src/agent.ts(または app/agent.ts) | Web ルートと Channel の両方が利用するエージェント | エージェント自体を変更する場合のみ |
次のコマンドで実行します。
npm run channel
ゲートウェイ接続が確立すると Channel "<name>" is online と出力されます。プロバイダー側がまだ未完成の場合は declared but no provider is attached yet と出力されます。これは待機状態であり、エラーではありません。
変更する内容と場所
すべての変更は、channels.mts の createDefaultChannel 内で、返却前に channel オブジェクトに対して行います。
// Reply to a thread the bot is mentioned in, in addition to the messages it
// already handles. Registering onMention does NOT replace onMessage: a
// non-mention turn is only ever dispatched to message handlers, so removing
// onMessage makes the bot silent in DMs and on 1:1 platforms.
channel.onMention(async ({ thread, message }) => {
/* ... */
});
// Act on a reaction. `added` distinguishes an added emoji from a removed one,
// and `thread` is the conversation it happened in.
channel.onReaction(async ({ thread, emoji, added }) => {
/* ... */
});
onCommand をこの一覧に含めていないのは意図的です。マネージド Slack Channel はイベント専用であり、マネージドのスラッシュコマンド受信機能はありません。そのため、これを登録しても、呼び出されることのないハンドラーが作られるだけです。
プロバイダー固有のツールとコンテキスト(defaultSlackTools、defaultSlackContext)をスキャフォールドから意図的に省いているのは、見落としではありません。これらをインポートするとファイルがプロバイダー固有になる一方、スキャフォールドされた Channel はそうではありません。プロジェクトが 1 つのプロバイダーだけを対象とする場合は、ここに追加してください。
スキャフォールドされたプロジェクトでしてはいけない2つのこと
- 2つ目の
createChannelを追加しないでください。 ホストは.copilotkit/channels.jsonから Channel 名を1つだけ解決し、複数が宣言されている場合は起動を拒否します。ソースに2つ目を宣言しても、ボットが2つになるわけではありません。プロジェクトが起動しなくなります。 - Channel を Next.js のルートに移動しないでください。 そのルートはサーバーレスで、接続を開いたままにできません。別のホストが用意されているのはそのためです。
検証 — どちらの方法でも
npx copilotkit channels status --via skill:copilotkit-channels
これは一致している必要がある3つの要素、つまり宣言された設定、プロジェクトのソース、サーバーを照合し、不足しているものを示します。次に、以下を行います。
- 長時間実行するホストを起動します。スキャフォールドされたプロジェクトでは
npm run channelを実行します。アクティベーションのログが表示されるはずです。 - Slack では、アプリをチャンネルに招待します(
channels statusを実行すると、正しいハンドルを含む招待コマンドが表示されます)。Teams では、プロバイダーの設定で チームに追加 がすでに記録されている必要があります。個人用のみのインストールで代用しないでください。 - 実際にメンションするか、ダイレクトメッセージを送信します。返信があるはずです。
何も起きず、エラーもない場合は、次の順に確認します。activateChannels: false がどこかに設定されていないか、遅延マウントの場合は ready() が待機されているか、intelligence が渡されているか(runner ではなく)、リアルタイム URL が正しいか、Slack ではアプリ作成後に再インストールされ、ボットが招待されているかを確認してください。
「再インストール」は誤記ではありません。Slack はアプリの作成時に2つのスコープだけでアプリをインストールし、残りのスコープを付与するには再インストールが必要です。詳しくは、以下の「オンラインなのに無反応で、ログにも何も出ない場合」を参照してください。
認証情報が保存されたという理由だけで、Channel が動作していると報告しないでください。アダプターが保存されたことから分かるのは、認証情報が有効であること、つまりサーバーが認証情報を検証したことだけです。アプリがインストールされていること、チャンネルに招待されていること、ランタイムが接続されていることは証明されません。
オンラインなのに無反応 — 別の問題
このチェックリストが対象としているのは、Channel が一度も接続されないケースだけです。ホストのログに Channel "<name>" is online と表示されているのにボットが何も返さない場合、コード側は問題ありません。アクティベーションは成功しており、上記の項目はすべて正しい結果になります。同じリストをもう一度確認しても時間の無駄です。
ホスト自身の出力で、次のログを探してください。
{ meta: { deliveryId: 'dlv_...', errorCategory: 'validation' } } channel delivery claim or join failed
これは、サーバーが送信したターンをランタイムが結合境界で拒否したことを示しています。ハンドラーが実行される前の段階です。この経路ではプロバイダーに何も返信されないため、エラーではなく無反応に見えます。
通常の原因は、インストールされている @copilotkit/channels-* パッケージと、それらを提供する Intelligence デプロイメントのバージョン不一致です。ランタイムは各配信を厳密に検証し、許容範囲の広い部分一致ではなく、フィールドの完全一致を要求します。そのため、サーバーがまだ送信しないフィールドをクライアントが想定していると、その種類のターンはすべて失敗します。一方、そのようなターンを受け取らない Channel(Teams のみの場合や、アイドル状態の Slack アプリなど)は、正常に見えます。
そのため、次の手順を行います。
- インストールされているバージョンを確認します。
npm ls @copilotkit/runtime @copilotkit/channels - Intelligence デプロイメントのバージョンと比較します。ホスト型 Intelligence では、カナリア版やプレリリース版のクライアントがデプロイ済みのバージョンより先行している場合があります。セルフホストの場合は、通常、デプロイメントのほうが古くなっています。
- 両者のバージョン系列を一致させます。クライアントをプレリリース版に固定するのが、この状態に陥る一般的な原因です。
errorCategory は分類情報であり、メッセージではありません。元のエラーテキストは意図的にログに記録されないため、このカテゴリとこの説明が、得られるすべての情報です。
オンラインなのに応答がなく、ログにも何も出ない — スコープが不足した Slack トークン
ホストのログにはオンラインと表示され、ボットが何も返さず、出力に配信に関する行が一切ない場合、そのターンはあなたに届いていません。Slack から送信されていないのです。Slack では、ほとんどの場合、ボットトークンを早い段階でコピーしたことが原因です。
マニフェストから Slack アプリを作成すると、インストールも実行され、そのインストールで付与されるスコープは channels:history と chat:write の 2 つだけです。マニフェストに宣言されたスコープはアプリの設定には反映されますが、付与される権限には反映されません。Slack の黄色い「権限スコープが変更されました」というバナーは、この状態を示しています。ワークスペースに再インストール → 許可を一度実行すると、付与される権限が完全なスコープ一式に引き上げられます。
再インストール前にコピーしたトークンが落とし穴です。すべてのチェックを通過してしまうからです。
auth.testは成功するため、接続時に保存され、アダプターは正常と報告されます。chat:writeは存在するため、ボットは投稿できます。明らかな問題には見えません。app_mentions:readがないため、Slack はapp_mentionを配信せず、ハンドラーも実行されません。
その結果、Channel は実際にはオンラインでありながら、構造的に何も受け取れない状態になります。上記のバージョン不一致との見分け方は、分かれば簡単です。あちらの障害では配信が拒否されたログが出ますが、こちらでは拒否する対象がないため、何もログに出ません。
修正するには、Slack アプリを再インストールし、新たに発行された Bot User OAuth Token をコピーします。再インストールすると新しいトークンが発行されます。その後、保存済みの認証情報をローテーションします(npx copilotkit channels rotate <name> --adapter slack --via skill:copilotkit-channels)。
Intelligence は、貼り付けられたトークンのスコープが不足している場合、CHANNEL_ADAPTER_SLACK_TOKEN_SCOPES_INCOMPLETE を返して受け付けないようになり、不足しているスコープを示します。このエラーはセットアップの失敗ではなく、この問題を早期に検出できたものとして扱ってください。このチェックが導入される前に接続された Channel は、今もこの状態のままになっている可能性があります。これを解消するには、ローテーションが必要です。
再インストールしても解決しない場合、アプリが現在のマニフェストより前のもので、保存済みの設定も不完全です。Slack アプリの App Manifest に現在のマニフェストを貼り付けてから、再インストールしてください。
CLI で生成されなかったプロジェクトを接続する
ここからはすべて手動で接続する方法です。あまり一般的ではありません。channel-host.mts が存在する場合は、このセクションは対象外です。
前提条件
スキャフォールドされたプロジェクトでは、以下の 4 つがすでにすべて満たされています。始める前に、4 つすべてを確認してください。どれか 1 つでも満たしていない場合は、先に修正してください。それぞれエラーではなく、何も起こらない状態を引き起こします。
- Intelligence ランタイムが接続されています。 SSE モードでは
channelsを使用できません。その場合の型はchannels?: undefinedです。プロジェクトでまだrunnerを指定してCopilotRuntimeを構築しており、intelligenceを指定していない場合は、まずプロジェクトを Intelligence に接続してください。copilotkit-cli スキルに従って、copilotkit loginを実行し、続けてcopilotkit project selectを実行します。 - 長時間実行されるホストがあること。 アクティベーションでは永続的な接続が開かれるため、プロセスはリクエストの終了後も実行され続ける必要があります。Next.js のサーバーレスのルートハンドラー、Lambda、エッジ関数では Channel をホストできません。以下の「デプロイ形態」を参照してください。
- ホスト環境の値が設定されていること — プロジェクト API キーと、デフォルトを上書きする場合はリアルタイム URL です。
- プロバイダー側の準備が完了しているか、作業中であること。 2 つの側面はどちらから進めてもかまいませんが、両方が完了するまで Channel は応答しません。
ステップ 1:インストール
npm install @copilotkit/channels
@copilotkit/channels は createChannel を提供します。マネージドトランスポート自体は @copilotkit/runtime に組み込まれています。マネージド方式では、別途インストールするアダプターパッケージはなく、プロセス内にプロバイダーの認証情報を置く必要もありません。
Step 2: Channel を宣言する
Channel の name は、ここでコード上に設定します。これはランタイムが管理対象 Channel のアクティベーション設定(プロジェクト ID、アダプター、ソケット URL、認証)を導出するために使う、プロジェクト内で一意の識別子です。そのため、これらはいずれも指定する必要はありません。
import { createChannel } from "@copilotkit/channels";
const support = createChannel({
// Must match the Channel name configured on the provider half.
// Lowercase kebab-case, unique within the project.
name: "support",
agent: (threadId) => {
const agent = new MyAgent();
agent.threadId = threadId;
return agent;
},
});
support.onMention(async ({ thread, message }) => {
await thread.runAgent({
// Channel history does NOT include the in-flight turn, so pass the current
// message explicitly -- otherwise the agent runs with zero messages.
prompt: message.contentParts?.length ? message.contentParts : message.text,
});
});
エージェントはフレームワークに依存しません。agent にはリモートのものも含め、任意の AG-UI AbstractAgent を渡せます。Python や .NET のエージェントはそのままの場所に置き、小さな TypeScript ホストがそれへプロキシします。Channels の採用にあたって、エージェントを移植する必要はありません。
Step 3: Channel をランタイムに渡す
import { CopilotRuntime, CopilotKitIntelligence } from "@copilotkit/runtime/v2";
const intelligence = new CopilotKitIntelligence({
apiKey: process.env.CPK_INTELLIGENCE_API_KEY!,
});
const runtime = new CopilotRuntime({
// A Channel supplies its own agent, so runtime-hosted agents are optional here.
agents: {},
intelligence,
identifyUser: (request) => resolveUserFromSession(request),
channels: [support],
});
Step 4: 長時間稼働するホストをマウントする
import { createServer } from "node:http";
import { createCopilotNodeListener } from "@copilotkit/runtime/v2/node";
const listener = createCopilotNodeListener({
runtime,
basePath: "/api/copilotkit",
});
createServer(listener).listen(Number(process.env.PORT ?? 8300));
// Optional on this mount, and worth doing: it turns a failed activation into a
// startup failure instead of a line in the logs.
await listener.channels.ready({ timeoutMs: 30_000 });
ready() を呼ぶ必要があるかどうかは、マウント方法によって異なります
これは Channels について最も誤解されやすい点です。アクティベーションはどこでも遅延されるわけではありません。
| マウント方法 | アクティベーション | ready() は必要か |
|---|---|---|
createCopilotNodeListener | リスナーの作成時に開始 | いいえ — 観測するなら await する |
createCopilotExpressHandler | ルーターの作成時に開始 | いいえ — 観測するなら await する |
createCopilotHonoHandler | 最初の ready() まで遅延 | はい |
createCopilotRuntimeHandler(汎用 fetch) | 最初の ready() まで遅延 | はい |
activateChannels: false を指定したマウント | なし。コントロール面もソケットもない | 何も接続されない |
ライフサイクルを自ら管理する 2 つのラッパーは、プロセスの寿命を自分で所有しているため、自動的に開始します。宣言された Channel は宣言されたという理由だけで接続され、忘れてはならない呼び出しは存在しません。fetch と hono のハンドラーは意図的に遅延させています。これらはサーバーレスおよびエッジのエントリーポイントであり、アイソレートはリクエストごとに凍結・再利用されるため、起動のたびに別々のコールドスタートが同じ Channel に対する競合するリスナーを生成してしまうからです。
したがって、サイレントな障害——HTTP を提供し、正常に見えるのに何も応答しないプロセス——は、遅延するマウントで誰も ready() を呼ばない場合に発生します。node や express のホストでは、ready() が欠けていてもこの障害にはなりません。
どちらの場合も、以下の 2 点に注意してください。
-
ready()は一回限りで、冪等です。初回のアクティベーション結果で確定するため、自動開始後に await しても、2 回目のアクティベーションを起こすのではなく、そのアクティベーションを観測することになります。結果がsetup_requiredとして解決されることもあり、これはアクティベーションが確定したことを意味するのであって、配信が正常であることを意味するわけではありません。接続済みと判断するのはstatus().overall === "online"の場合のみにしてください。 -
再デプロイ時に接続がきれいに解放されるよう、アクティベーションとシャットダウンをセットで扱ってください:
process.on("SIGTERM", async () => { await listener.channels.stop(); process.exit(0); });
デプロイ形態
このスキルが判断するのは、プロジェクトにすでに長時間稼働するホストがあるかどうかです。プロジェクト独自の依存関係と起動スクリプトを確認し、現在どのように動作しているかを把握したうえで、次のように判断してください。
| プロジェクトの現在の実行形態 | 対応 |
|---|---|
| スタンドアロンの Node/Express/Hono サーバー | 既存のランタイム上で Channel を宣言する |
| Vercel、Lambda、またはエッジ関数上の Next.js | Channel 用に別の長時間稼働プロセスを追加する |
| Python または .NET のエージェント | そのままにして、プロキシする小規模な TypeScript チャネルホストを追加する |
別プロセスは必須ではありません。たとえば React UI を提供するなど、プロジェクトですでに長時間稼働するランタイムを使用している場合、同じランタイムで Channels を宣言できます。別プロセスが必要なのは、既存のランタイムがサーバーレスの場合に限られます。
別のホストが必要な場合でも、小規模なプログラムで十分です。ランタイム、Channel、そして待機する ready 呼び出しで構成されます。UI を提供する必要も、HTTP サーバーを用意する必要もありません。そこに呼び出しを行うものはありません。ゲートウェイへの接続はアウトバウンドであり、それを開いたままにすることでプロセスが動作し続けます。scaffold で生成される channel-host.mts はまさにこの構成であり、手動で作成する場合も参考実装として読む価値があります。
絶対に行わないこと
- 管理対象の Channel 用にプロバイダーの認証情報をプロジェクトに書き込まないでください。 Intelligence がサーバー側で認証情報を保存するため、ランタイムがそれを読み取ることはありません。管理対象の Channel 用にプロジェクトが Slack ボットトークンを保持している場合、何かの設定が間違っています。
- サーバーレスのルートハンドラーに Channel を配置しないでください。 デプロイできたように見えても、接続されません。
ready()が必要、または不要だと決めつけないでください。 マウント方法を確認してください。不要な呼び出しを追加するよう指示するのも、必要な呼び出しを省略するのも、どちらも役に立ちません。intelligenceと一緒にrunnerを渡さないでください。 そうすると、スレッドがメモリ内に保持され続けてしまいます。- scaffold で生成されたプロジェクトに Channel を手動で接続しないでください。
channel-host.mtsが存在する場合、コード側の作業は完了しています。宣言を重複して追加すると、ボットが追加されるのではなく、ホストの起動が止まります。 - Channel のインフラストラクチャ ID を推測して作成しないでください。 プロジェクト、アダプター、チャネルの ID は、Intelligence の設定と Channel の
nameから導出されます。 - Teams のアイコンやパッケージのバイトデータを Intelligence 経由で送信しないでください。 ブラウザーで生成された成果物は、実行中の試行でローカルに保持し、その後破棄してください。
- 通常の管理対象 Teams の設定に Azure Bot を使用しないでください。 起動手順は Teams Developer Portal と Entra を使用するか、Teams 管理の Fast CLI を使用する方法です。
- 設定に「Created and installed」と表示されたからといって、Teams の Channel が動作していると判断しないでください。 プロバイダー側の設定完了と、ランタイム/メッセージの正常性は別々に確認する必要があります。