diff --git a/docs/cli/thv_client_register.md b/docs/cli/thv_client_register.md index 41c42e0a11..fda1e6003a 100644 --- a/docs/cli/thv_client_register.md +++ b/docs/cli/thv_client_register.md @@ -28,6 +28,7 @@ Valid clients: - cline: VS Code Cline extension - codex: OpenAI Codex CLI - continue: Continue.dev IDE plugins + - copilot-cli: GitHub Copilot CLI - cursor: Cursor editor - factory: Factory.ai Droid CLI - gemini-cli: Google Gemini CLI diff --git a/docs/cli/thv_client_remove.md b/docs/cli/thv_client_remove.md index f1d6044c01..f32115fc52 100644 --- a/docs/cli/thv_client_remove.md +++ b/docs/cli/thv_client_remove.md @@ -28,6 +28,7 @@ Valid clients: - cline: VS Code Cline extension - codex: OpenAI Codex CLI - continue: Continue.dev IDE plugins + - copilot-cli: GitHub Copilot CLI - cursor: Cursor editor - factory: Factory.ai Droid CLI - gemini-cli: Google Gemini CLI diff --git a/docs/server/docs.go b/docs/server/docs.go index c1555c358a..1c9dbdb7d0 100644 --- a/docs/server/docs.go +++ b/docs/server/docs.go @@ -866,7 +866,8 @@ const docTemplate = `{ "mistral-vibe", "codex", "kimi-cli", - "factory" + "factory", + "copilot-cli" ], "type": "string", "x-enum-varnames": [ @@ -896,7 +897,8 @@ const docTemplate = `{ "MistralVibe", "Codex", "KimiCli", - "Factory" + "Factory", + "CopilotCli" ] }, "github_com_stacklok_toolhive_pkg_client.ClientAppStatus": { diff --git a/docs/server/swagger.json b/docs/server/swagger.json index 60b748ea96..0b8ce797f3 100644 --- a/docs/server/swagger.json +++ b/docs/server/swagger.json @@ -859,7 +859,8 @@ "mistral-vibe", "codex", "kimi-cli", - "factory" + "factory", + "copilot-cli" ], "type": "string", "x-enum-varnames": [ @@ -889,7 +890,8 @@ "MistralVibe", "Codex", "KimiCli", - "Factory" + "Factory", + "CopilotCli" ] }, "github_com_stacklok_toolhive_pkg_client.ClientAppStatus": { diff --git a/docs/server/swagger.yaml b/docs/server/swagger.yaml index 84d7037d7f..8520dabc3f 100644 --- a/docs/server/swagger.yaml +++ b/docs/server/swagger.yaml @@ -909,6 +909,7 @@ components: - codex - kimi-cli - factory + - copilot-cli type: string x-enum-varnames: - RooCode @@ -938,6 +939,7 @@ components: - Codex - KimiCli - Factory + - CopilotCli github_com_stacklok_toolhive_pkg_client.ClientAppStatus: properties: client_type: diff --git a/pkg/client/config.go b/pkg/client/config.go index 39e26075a5..e76cc04974 100644 --- a/pkg/client/config.go +++ b/pkg/client/config.go @@ -98,6 +98,8 @@ const ( KimiCli ClientApp = "kimi-cli" // Factory represents the Factory.ai Droid CLI. Factory ClientApp = "factory" + // CopilotCli represents the GitHub Copilot CLI. + CopilotCli ClientApp = "copilot-cli" ) const ( @@ -1003,6 +1005,25 @@ var supportedClientIntegrations = []clientAppConfig{ SkillsGlobalPath: []string{".factory", skillsDirName}, SkillsProjectPath: []string{".factory", skillsDirName}, }, + { + ClientType: CopilotCli, + Description: "GitHub Copilot CLI", + SettingsFile: "mcp-config.json", + MCPServersPathPrefix: "/mcpServers", + RelPath: []string{".copilot"}, + Extension: JSON, + SupportedTransportTypesMap: map[types.TransportType]string{ + types.TransportTypeStdio: httpTransportLabel, + types.TransportTypeSSE: "sse", + types.TransportTypeStreamableHTTP: httpTransportLabel, + }, + IsTransportTypeFieldSupported: true, + MCPServersUrlLabelMap: map[types.TransportType]string{ + types.TransportTypeStdio: defaultURLFieldName, + types.TransportTypeSSE: defaultURLFieldName, + types.TransportTypeStreamableHTTP: defaultURLFieldName, + }, + }, { // Xcode does not support MCP; it is an LLM-gateway-only entry. // Cast LLMClientApp → ClientApp for internal config storage; the type diff --git a/pkg/client/config_test.go b/pkg/client/config_test.go index e8b1bdb4a2..389b10ade9 100644 --- a/pkg/client/config_test.go +++ b/pkg/client/config_test.go @@ -504,7 +504,7 @@ func TestSuccessfulClientConfigOperations(t *testing.T) { case AmpWindsurf: assert.Contains(t, string(content), `"mcpServers":`, "AmpWindsurf config should contain mcpServers key") - case LMStudio, Trae, Kiro, Antigravity, GeminiCli, KimiCli, Factory: + case LMStudio, Trae, Kiro, Antigravity, GeminiCli, KimiCli, Factory, CopilotCli: assert.Contains(t, string(content), `"mcpServers":`, "Config should contain mcpServers key") case VSCodeServer: @@ -563,7 +563,7 @@ func TestSuccessfulClientConfigOperations(t *testing.T) { "VSCode config should contain the server URL") case Cursor, RooCode, ClaudeCode, Cline, Windsurf, WindsurfJetBrains, AmpCli, AmpVSCode, AmpCursor, AmpVSCodeInsider, AmpWindsurf, LMStudio, Goose, Trae, Continue, OpenCode, Kiro, Antigravity, Zed, GeminiCli, VSCodeServer, - MistralVibe, Codex, KimiCli, Factory: + MistralVibe, Codex, KimiCli, Factory, CopilotCli: assert.Contains(t, string(content), testURL, "Config should contain the server URL") } @@ -1292,8 +1292,8 @@ func TestGetAllClients(t *testing.T) { clients := GetAllClients() - // Should return all 27 supported clients - assert.Len(t, clients, 27, "Expected 27 supported clients") + // Should return all 28 supported clients + assert.Len(t, clients, 28, "Expected 28 supported clients") // Verify the list is sorted alphabetically for i := 1; i < len(clients); i++ { @@ -1507,7 +1507,7 @@ func TestGetClientListCSV(t *testing.T) { clientNames[i-1], clientNames[i]) } - // Count the number of clients (should be 25) + // Count the number of clients (should be 28) clients := strings.Split(csv, ", ") - assert.Len(t, clients, 27, "Expected 27 clients in CSV list") + assert.Len(t, clients, 28, "Expected 28 clients in CSV list") }