次の場合に最適です。 同時ユーザーを処理する SaaS 製品、パートナー統合、内部プラットフォーム、バックエンド サービス。
このガイドは、次の場合に使用します。
ビルドする場合は、次のガイドを使用します。
- Copilotを利用するエージェントを埋め込むマルチユーザー SaaS 製品
- Copilot Studio または Fabric スタイルのパターンのような、パートナー連携のバックエンド
- 同時実行ユーザー、ワークスペース、テナント、または要求を処理するすべてのサーバー
- 複数の SDK クライアントが 1 つの Copilot ランタイム プロセスに接続する共有ランタイム
このガイドは スケーリングとマルチテナント の姉妹です。 トポロジ、負荷分散、およびストレージ パターンについては、このガイドを使用してください。 SDK レベルのオプションとランタイム分離の選択肢については、このガイドを使用してください。
主要な SDK オプション
| オプション | 次の目的に使用します: | メモ |
|---|---|---|
mode: "empty" | アンビエント OS ツールと CLI の既定値を無効にする | マルチユーザーまたは共有のシナリオに必要です。 |
sessionIdleTimeoutSeconds | アイドル状態のセッションのクリーンアップ | 実行時間の長いプロセスのサーバー側タイムアウトを設定します。 |
baseDirectory | ランタイム インスタンスごとの COPILOT_HOME の分離 | 既存のランタイムに接続するときに無視されます。 |
sessionFs | ローカル ディスクからセッション ファイル システム ストレージをルーティングする | セッションごとのファイルシステム プロバイダーとペアにします。 |
RuntimeConnection.forUri(url) | 既に実行されているランタイムの共有 | 言語名は異なります。以下のサンプルを参照してください。 |
セッションごと gitHubToken | 認証のスコープをリクエスト元のユーザーに限定する | これは、単一の共有ユーザー トークンよりも優先されます。 |
mode: "empty"
mode: "empty" は、オプションの Copilot CLI の動作を既定で無効にします。 マルチユーザー サーバー モードでは、セッションがアクセスできるツール、MCP サーバー、スキル、ワークスペース パスをアプリケーションが明示的に決定する必要があるため、これは安全なベースラインです。
共有サーバーには既定の mode: "copilot-cli" を使用しないでください。 このモードは、CLI に似たコーディング エージェントを対象としており、アンビエント ホスト ファイルシステムの機能を公開できます。
import { CopilotClient, RuntimeConnection } from "@github/copilot-sdk";
// baseDirectory and sessionIdleTimeoutSeconds apply when the SDK spawns the
// runtime. With RuntimeConnection.forUri(...) configure COPILOT_HOME and the
// idle timeout on the runtime process itself.
const client = new CopilotClient({
mode: "empty",
connection: RuntimeConnection.forUri(process.env.COPILOT_RUNTIME_URL!),
});
const session = await client.createSession({
sessionId: `user-${user.id}-${crypto.randomUUID()}`,
model: "gpt-4.1",
availableTools: ["custom:lookupOrder", "custom:createTicket"],
gitHubToken: user.githubToken,
});
from copilot import CopilotClient, RuntimeConnection
from copilot.session import PermissionHandler
client = CopilotClient(
mode="empty",
base_directory=f"/var/lib/my-app/copilot/{runtime_instance_id}",
session_idle_timeout_seconds=900,
connection=RuntimeConnection.for_uri(runtime_url),
)
await client.start()
session = await client.create_session(
session_id=f"user-{user.id}-{request_id}",
model="gpt-4.1",
available_tools=["custom:lookupOrder", "custom:createTicket"],
github_token=user.github_token,
on_permission_request=PermissionHandler.approve_all,
)
package main
import (
"context"
"fmt"
copilot "github.com/github/copilot-sdk/go"
)
type appUser struct {
ID string
GitHubToken string
}
func main() {
ctx := context.Background()
runtimeInstanceID := "instance-1"
runtimeURL := "http://127.0.0.1:8080"
requestID := "req-1"
user := appUser{ID: "alice", GitHubToken: "YOUR_GITHUB_TOKEN"}
client := copilot.NewClient(&copilot.ClientOptions{
Mode: copilot.ModeEmpty,
BaseDirectory: fmt.Sprintf("/var/lib/my-app/copilot/%s", runtimeInstanceID),
SessionIdleTimeoutSeconds: 900,
Connection: copilot.URIConnection{URL: runtimeURL},
})
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
SessionID: fmt.Sprintf("user-%s-%s", user.ID, requestID),
Model: "gpt-4.1",
AvailableTools: []string{"custom:lookupOrder", "custom:createTicket"},
GitHubToken: user.GitHubToken,
})
_ = session
_ = err
}
client := copilot.NewClient(&copilot.ClientOptions{
Mode: copilot.ModeEmpty,
BaseDirectory: fmt.Sprintf("/var/lib/my-app/copilot/%s", runtimeInstanceID),
SessionIdleTimeoutSeconds: 900,
Connection: copilot.URIConnection{URL: runtimeURL},
})
session, err := client.CreateSession(ctx, &copilot.SessionConfig{
SessionID: fmt.Sprintf("user-%s-%s", user.ID, requestID),
Model: "gpt-4.1",
AvailableTools: []string{"custom:lookupOrder", "custom:createTicket"},
GitHubToken: user.GitHubToken,
})
using GitHub.Copilot;
var runtimeInstanceId = "instance-1";
var runtimeUrl = "http://127.0.0.1:8080";
var requestId = "req-1";
var user = new { Id = "alice", GitHubToken = "YOUR_GITHUB_TOKEN" };
var client = new CopilotClient(new CopilotClientOptions
{
Mode = CopilotClientMode.Empty,
BaseDirectory = $"/var/lib/my-app/copilot/{runtimeInstanceId}",
SessionIdleTimeoutSeconds = 900,
Connection = RuntimeConnection.ForUri(runtimeUrl),
});
await using var session = await client.CreateSessionAsync(new SessionConfig
{
SessionId = $"user-{user.Id}-{requestId}",
Model = "gpt-4.1",
AvailableTools = ["custom:lookupOrder", "custom:createTicket"],
GitHubToken = user.GitHubToken,
});
var client = new CopilotClient(new CopilotClientOptions
{
Mode = CopilotClientMode.Empty,
BaseDirectory = $"/var/lib/my-app/copilot/{runtimeInstanceId}",
SessionIdleTimeoutSeconds = 900,
Connection = RuntimeConnection.ForUri(runtimeUrl),
});
await using var session = await client.CreateSessionAsync(new SessionConfig
{
SessionId = $"user-{user.Id}-{requestId}",
Model = "gpt-4.1",
AvailableTools = ["custom:lookupOrder", "custom:createTicket"],
GitHubToken = user.GitHubToken,
});
import java.util.List;
import com.github.copilot.CopilotClient;
import com.github.copilot.rpc.CopilotClientOptions;
import com.github.copilot.rpc.CopilotClientMode;
import com.github.copilot.rpc.SessionConfig;
public class MultiTenancyExample {
record User(String id, String gitHubToken) {}
public static void main(String[] args) throws Exception {
String runtimeUrl = "http://localhost:4321";
String requestId = "req-1";
User user = new User("u1", "ghu_token");
// setCopilotHome and setSessionIdleTimeoutSeconds are ignored when
// setCliUrl is used; configure those on the runtime process instead.
var client = new CopilotClient(new CopilotClientOptions()
.setMode(CopilotClientMode.EMPTY)
.setCliUrl(runtimeUrl)
);
var session = client.createSession(new SessionConfig()
.setSessionId("user-" + user.id() + "-" + requestId)
.setModel("gpt-4.1")
.setAvailableTools(List.of("custom:lookupOrder", "custom:createTicket"))
.setGitHubToken(user.gitHubToken())
).get();
}
}
// setCopilotHome and setSessionIdleTimeoutSeconds are ignored when
// setCliUrl is used; configure those on the runtime process instead.
var client = new CopilotClient(new CopilotClientOptions()
.setMode(CopilotClientMode.EMPTY)
.setCliUrl(runtimeUrl)
);
var session = client.createSession(new SessionConfig()
.setSessionId("user-" + user.id() + "-" + requestId)
.setModel("gpt-4.1")
.setAvailableTools(List.of("custom:lookupOrder", "custom:createTicket"))
.setGitHubToken(user.gitHubToken())
).get();
use std::path::PathBuf;
use github_copilot_sdk::{Client, ClientOptions, Transport};
use github_copilot_sdk::mode::ClientMode;
use github_copilot_sdk::types::SessionConfig;
let client = Client::start(
ClientOptions::new()
.with_mode(ClientMode::Empty)
.with_base_directory(PathBuf::from(format!(
"/var/lib/my-app/copilot/{runtime_instance_id}"
)))
.with_session_idle_timeout_seconds(900)
.with_transport(Transport::External {
host: runtime_host.to_string(),
port: runtime_port,
connection_token: None,
}),
).await?;
let session = client.create_session(
SessionConfig::default()
.with_session_id(format!("user-{}-{request_id}", user.id))
.with_model("gpt-4.1")
.with_available_tools(["custom:lookupOrder", "custom:createTicket"])
.with_github_token(user.github_token),
).await?;
sessionIdleTimeoutSeconds
非アクティブなセッションが自動的にクリーンアップされるように、サーバーに sessionIdleTimeoutSeconds を設定します。 これにより、実行時間の長いプロセスでのゾンビ セッションが防止され、メモリとファイル システムの負荷が軽減されます。
| Language | パブリック オプション |
|---|---|
| TypeScript | sessionIdleTimeoutSeconds |
| Python | session_idle_timeout_seconds |
| Go | SessionIdleTimeoutSeconds |
| .NET | SessionIdleTimeoutSeconds |
| Java | setSessionIdleTimeoutSeconds(...) |
| Rust | with_session_idle_timeout_seconds(...) |
製品の会話の有効期間に一致する値を使用します。 チャット バックエンドの場合、通常は 15 分から 30 分が適しています。 ワークフロー エージェントの場合は、ワークフローの完了時にタイムアウト時間が長くなり、明示的に削除されます。
baseDirectory
baseDirectory は、ランタイム インスタンスの COPILOT_HOME を設定します。 これを使用して、プロセス、ポッド、ワーカー、またはテナントの境界ごとにランタイム状態、資格情報、セッション データを分離します。
const client = new CopilotClient({
mode: "empty",
baseDirectory: `/var/lib/my-app/copilot/runtime-${process.env.HOSTNAME}`,
sessionIdleTimeoutSeconds: 900,
});
ランタイムは、COPILOT_HOMEを含め、構成されたsession-state/{sessionId}の下にセッション状態を格納します。 アプリで複数のランタイム インスタンスを実行する場合は、意図的に共有ストレージを使用しない限り、各インスタンスに個別のディレクトリを指定します。
SDK が RuntimeConnection.forUri(url) を使用してすでに実行中のランタイムに接続する場合、baseDirectory は SDK クライアントによって無視されます。 代わりに、ランタイム プロセスで COPILOT_HOME を構成します。
sessionFs
sessionFs はカスタム セッション ファイルシステム プロバイダーを登録するため、セッション スコープのファイル I/O をランタイムのローカル ディスクではなくアプリケーション ストレージ経由でルーティングできます。 ローカル ディスクが一時的な場合、セッション状態がオブジェクト ストレージに存在する必要がある場合、またはプラットフォームでテナント対応のストレージ パスを適用する必要がある場合に使用します。
const client = new CopilotClient({
mode: "empty",
sessionFs: {
initialCwd: "/workspace",
sessionStatePath: "/session-state",
conventions: "posix",
},
});
プロバイダー コールバックを公開する言語の場合は、クライアント レベルで sessionFs を構成し、セッションの作成時または再開時にセッションごとのファイルシステム ハンドラーを提供します。 永続化の概念とストレージのトレードオフについては、 セッションの再開と永続化 を参照してください。
検証済みのパブリック SDK サーフェス:
| Language | クライアント レベルの構成 | セッションごとのプロバイダー |
|---|---|---|
| TypeScript | sessionFs | |
createSessionFsAdapter / プロバイダーコールバック | ||
| Python | session_fs | create_session_fs_handler |
| Go | SessionFS | CreateSessionFSProvider |
| .NET | SessionFs | CreateSessionFsProvider |
| Rust | with_session_fs(...) | with_session_fs_provider(...) |
Javaは現在、検証済みのパブリック sessionFs オプションを公開していないため、このガイドでは Java sessionFs サンプルは表示されません。
RuntimeConnection.forUri(url)
複数の SDK クライアントが既に実行されている 1 つのランタイムを共有する必要がある場合は、外部ランタイム接続を使用します。 これは、ランタイム プロセスが要求ハンドラーとは別に管理されるバックエンド サービスで一般的です。
| Language | 外部ランタイム接続 |
|---|---|
| TypeScript | RuntimeConnection.forUri(url) |
| Python | RuntimeConnection.for_uri(url) |
| Go | copilot.URIConnection{URL: url} |
| .NET | RuntimeConnection.ForUri(url) |
| Java | setCliUrl(url) |
| Rust | Transport::External { host, port, connection_token } |
外部ランタイムは、独自のプロセス レベルの認証とストレージを管理します。 ユーザー固有の認証が必要な場合は、セッションごとのトークンを createSession または resumeSession に渡します。
セッションごと gitHubToken
各セッションで gitHubToken を設定して、GitHub 認証の対象を要求元のユーザーに限定します。 これは、ランタイム プロセスを認証するクライアント レベルのトークンとは異なります。
const session = await client.createSession({
sessionId: `user-${user.id}-support`,
model: "gpt-4.1",
availableTools: ["custom:*"],
gitHubToken: user.githubToken,
});
コンテンツの除外、モデル ルーティング、クォータ チェック、およびユーザー固有のCopilot アクセスには、セッションごとのトークンを使用します。 製品でサービス アカウント セマンティクスを意図的に使用しない限り、ユーザー間で 1 つのサービス トークンを共有しないようにします。
統合ID
ブランド化されたエージェントを構築するパートナーは、Mission Control 要求の統合 ID を設定できます。 ランタイムは、GITHUB_COPILOT_INTEGRATION_ID を読み取り、すべての Mission Control 要求で Copilot-Integration-Id HTTP ヘッダーとしてスタンプします。
GITHUB_COPILOT_INTEGRATION_ID=my-product-agent copilot --headless --port 4321
既定の統合 ID は copilot-developer-cli。 属性やルーティングには、 my-product-agent などの安定した値を使用します。 統合 ID は現在、環境変数によってのみ構成されています。これは、ファースト クラスの SDK オプションではありません。
SDK がランタイムを生成する場合は、クライアント環境オプションを使用して環境変数を渡します。
RuntimeConnection.forUri(url)で接続する場合は、ランタイム プロセス自体に環境変数を設定します。
セッションレベルの分離保証
セッション レベルの分離とは、ランタイムがグローバル共有状態ではなく、ユーザー固有のモデルと状態の情報をセッションのスコープに保持することを意味します。
| Surface | 分離動作 |
|---|---|
| モデル リスト キャッシュ | セッションごと。 モデル検索では、セッションのモデル リスト キャッシュが使用されます。 |
| セッションの状態 | |
COPILOT_HOME/session-state/{sessionId}配下のセッションIDごと。 | |
| GitHub ID 情報 | |
gitHubToken がセッションに設定されている場合は、セッションごと。 | |
| Tools | |
mode: "empty"では明示的、mode: "copilot-cli"では暗黙的です。 | |
| ホスト ファイルシステム | ホスト ツールが使用可能な場合は、ランタイム プロセスによって共有されます。 |
mode: "empty" は、共有ランタイム パターンを実行可能にする機能です。アプリケーションが登録または許可しない限り、アンビエント OS ツールは公開されません。
mode: "copilot-cli"では、OS ファイルシステム アクセスはホスト プロセスを通じて共有されるため、マルチユーザー サーバー モードにはそのモードを使用しないでください。
セッションの状態は、COPILOT_HOME/session-state/{sessionId}経由でルーティングしない限り、sessionFsに格納されます。 独自のテナントまたはユーザーの境界を含む一意のセッション ID を使用し、セッションを再開または削除する前にアクセス制御を適用します。
パターンの比較
| Pattern | 次の場合に使用します。 | Trade-offs |
|---|---|---|
| パターン 1: ユーザーごとに分離された CLI | ユーザーごとに最も強力な分離境界または個別のプロセス資格情報が必要です。 | 強力な分離。より高いリソース コスト。 「スケーリングとマルチテナント」を参照してください。 |
パターン 2: CLI との共有: mode: "empty" | アプリがツール、認証、およびセッション ID を制御している間、1 つのランタイムで多くのユーザーにサービスを提供する必要があります。 | 効率的;には、慎重なツール登録、セッションごとのトークン、およびアプリケーション レベルのアクセス チェックが必要です。 |
| パターン 3: ハイブリッド | コンピューティング負荷の高い作業をクラウド セッションにルーティングし、軽い作業をローカル セッションにルーティングします。 | 柔軟;には、ワークロード ルーティングとポリシー処理が必要です。 「クラウド セッション」を参照してください。 |
パターン 2: mode: "empty" を伴う共有 CLI
このパターンでは、すべてのユーザーがバックエンド経由で 1 つのランタイム プールに接続します。 アプリケーションはユーザー認証を実行し、セッション ID を選択し、セッションでユーザーのGitHub トークンを渡し、明示的なツール許可リストを提供します。

次の規則を使用します。
- 常にクライアントまたはランタイムを
mode: "empty"で起動します。 - 一意のセッション ID を使用し、所有権メタデータをアプリケーション データベースに格納します。
- セッション ID を参照する
resumeSession、deleteSession、または UI アクションの前に所有権を確認します。 - 要求をユーザーとして実行する必要がある場合は、セッションごとに
gitHubTokenを渡します。 - セッションに必要なツールのみを登録し、
custom:*やmcp:search_docsなどのソース修飾許可リストを優先します。 sessionIdleTimeoutSeconds設定し、完了したワークフロー セッションを明示的に削除します。
よくある落とし穴
mode: "empty"を忘れること。 既定のcopilot-cliモードでは、CLI スタイルの動作が公開され、アンビエント ツールを使用してホスト ファイル システムが公開される場合があります。sessionIdleTimeoutSecondsを設定していません。 実行時間の長いサーバーは、クリーンアップしないとアイドル 状態のセッションを蓄積する可能性があります。- セッションごとのトークンを渡すのではなく、ユーザー間で 1 つの
gitHubTokenを共有します。 - バックエンドの所有権を確認せずに、クライアントが提供するセッション ID を信頼する。
- 既存のランタイムに接続するクライアントで
baseDirectoryを設定し、ランタイム ストレージを移動することを想定しています。 代わりにランタイム プロセスを構成します。 - 各ツールがユーザーに適しているかどうかを確認することなく、
builtin:*などの広範なツール パターンを許可します。
参照
- スケーリングとマルチテナント: デプロイ トポロジ、ストレージ パターン、分離の比較
- バックエンド サービスのセットアップ: ヘッドレス サーバー モードでランタイムを実行する
- BYOK (独自のキーを持ち込む): 自分のモデルプロバイダーの認証情報を使用する
- クラウド セッション: 選択した作業をクラウド セッションにルーティングする
- セッションの再開と永続化: 再開可能なセッション状態の管理
- 機能: ツール、イベント、フック、および高度な SDK 機能