diff --git a/registry/coder/modules/cursor/README.md b/registry/coder/modules/cursor/README.md index c4c169d29..c61785656 100644 --- a/registry/coder/modules/cursor/README.md +++ b/registry/coder/modules/cursor/README.md @@ -16,7 +16,7 @@ Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder) module "cursor" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/cursor/coder" - version = "1.3.1" + version = "1.3.2" agent_id = coder_agent.example.id } ``` @@ -29,7 +29,7 @@ module "cursor" { module "cursor" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/cursor/coder" - version = "1.3.1" + version = "1.3.2" agent_id = coder_agent.example.id folder = "/home/coder/project" } @@ -45,7 +45,7 @@ The following example configures Cursor to use the GitHub MCP server with authen module "cursor" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/cursor/coder" - version = "1.3.1" + version = "1.3.2" agent_id = coder_agent.example.id folder = "/home/coder/project" mcp = jsonencode({ diff --git a/registry/coder/modules/cursor/main.tf b/registry/coder/modules/cursor/main.tf index 71aff72f1..d26000a83 100644 --- a/registry/coder/modules/cursor/main.tf +++ b/registry/coder/modules/cursor/main.tf @@ -98,6 +98,7 @@ resource "coder_script" "cursor_mcp" { set -eu mkdir -p "$HOME/.cursor" echo -n "${local.mcp_b64}" | base64 -d > "$HOME/.cursor/mcp.json" + chmod 600 "$HOME/.cursor/mcp.json" EOT } diff --git a/registry/coder/modules/kiro/README.md b/registry/coder/modules/kiro/README.md index 732472cb4..10d2117f5 100644 --- a/registry/coder/modules/kiro/README.md +++ b/registry/coder/modules/kiro/README.md @@ -18,7 +18,7 @@ Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder) module "kiro" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/kiro/coder" - version = "1.0.0" + version = "1.1.0" agent_id = coder_agent.example.id } ``` @@ -31,21 +31,39 @@ module "kiro" { module "kiro" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/kiro/coder" - version = "1.0.0" + version = "1.1.0" agent_id = coder_agent.example.id folder = "/home/coder/project" } ``` -### Open with custom display name and order +### Configure MCP servers for Kiro + +Provide a JSON-encoded string via the `mcp` input. When set, the module writes the value to `~/.kiro/settings/mcp.json` using a `coder_script` on workspace start. + +The following example configures Kiro to use the GitHub MCP server with authentication facilitated by the [`coder_external_auth`](https://coder.com/docs/admin/external-auth#configure-a-github-oauth-app) resource. ```tf module "kiro" { - count = data.coder_workspace.me.start_count - source = "registry.coder.com/coder/kiro/coder" - version = "1.0.0" - agent_id = coder_agent.example.id - display_name = "Kiro AI IDE" - order = 1 + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/kiro/coder" + version = "1.1.0" + agent_id = coder_agent.example.id + folder = "/home/coder/project" + mcp = jsonencode({ + mcpServers = { + "github" : { + "url" : "https://api.githubcopilot.com/mcp/", + "headers" : { + "Authorization" : "Bearer ${data.coder_external_auth.github.access_token}", + }, + "type" : "http" + } + } + }) +} + +data "coder_external_auth" "github" { + id = "github" } ``` diff --git a/registry/coder/modules/kiro/main.test.ts b/registry/coder/modules/kiro/main.test.ts index 89ea9cc78..f911de0cd 100644 --- a/registry/coder/modules/kiro/main.test.ts +++ b/registry/coder/modules/kiro/main.test.ts @@ -3,6 +3,11 @@ import { runTerraformApply, runTerraformInit, testRequiredVariables, + runContainer, + execContainer, + removeContainer, + findResourceInstance, + readFileContainer, } from "~test"; describe("kiro", async () => { @@ -90,4 +95,26 @@ describe("kiro", async () => { expect(coder_app?.instances[0].attributes.group).toBe("AI IDEs"); }); + + it("writes ~/.kiro/settings/mcp.json when mcp provided", async () => { + const id = await runContainer("alpine"); + try { + const mcp = JSON.stringify({ servers: { demo: { url: "http://localhost:1234" } } }); + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + mcp, + }); + const script = findResourceInstance(state, "coder_script", "kiro_mcp").script; + const resp = await execContainer(id, ["sh", "-c", script]); + if (resp.exitCode !== 0) { + console.log(resp.stdout); + console.log(resp.stderr); + } + expect(resp.exitCode).toBe(0); + const content = await readFileContainer(id, "/root/.kiro/settings/mcp.json"); + expect(content).toBe(mcp); + } finally { + await removeContainer(id); + } + }); }); diff --git a/registry/coder/modules/kiro/main.tf b/registry/coder/modules/kiro/main.tf index a6a49948d..fed8407b4 100644 --- a/registry/coder/modules/kiro/main.tf +++ b/registry/coder/modules/kiro/main.tf @@ -50,9 +50,19 @@ variable "display_name" { default = "Kiro IDE" } +variable "mcp" { + type = string + description = "JSON-encoded string to configure MCP servers for Kiro. When set, writes ~/.kiro/settings/mcp.json." + default = "" +} + data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} +locals { + mcp_b64 = var.mcp != "" ? base64encode(var.mcp) : "" +} + resource "coder_app" "kiro" { agent_id = var.agent_id external = true @@ -75,6 +85,22 @@ resource "coder_app" "kiro" { ]) } +resource "coder_script" "kiro_mcp" { + count = var.mcp != "" ? 1 : 0 + agent_id = var.agent_id + display_name = "Kiro MCP" + icon = "/icon/kiro.svg" + run_on_start = true + start_blocks_login = false + script = <<-EOT + #!/bin/sh + set -eu + mkdir -p "$HOME/.kiro/settings" + echo -n "${local.mcp_b64}" | base64 -d > "$HOME/.kiro/settings/mcp.json" + chmod 600 "$HOME/.kiro/settings/mcp.json" + EOT +} + output "kiro_url" { value = coder_app.kiro.url description = "Kiro IDE URL." diff --git a/registry/coder/modules/windsurf/README.md b/registry/coder/modules/windsurf/README.md index 4ac2e3611..e727cf118 100644 --- a/registry/coder/modules/windsurf/README.md +++ b/registry/coder/modules/windsurf/README.md @@ -16,7 +16,7 @@ Uses the [Coder Remote VS Code Extension](https://github.com/coder/vscode-coder) module "windsurf" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/windsurf/coder" - version = "1.1.1" + version = "1.2.0" agent_id = coder_agent.example.id } ``` @@ -29,8 +29,39 @@ module "windsurf" { module "windsurf" { count = data.coder_workspace.me.start_count source = "registry.coder.com/coder/windsurf/coder" - version = "1.1.1" + version = "1.2.0" agent_id = coder_agent.example.id folder = "/home/coder/project" } ``` + +### Configure MCP servers for Windsurf + +Provide a JSON-encoded string via the `mcp` input. When set, the module writes the value to `~/.codeium/windsurf/mcp_config.json` using a `coder_script` on workspace start. + +The following example configures Windsurf to use the GitHub MCP server with authentication facilitated by the [`coder_external_auth`](https://coder.com/docs/admin/external-auth#configure-a-github-oauth-app) resource. + +```tf +module "windsurf" { + count = data.coder_workspace.me.start_count + source = "registry.coder.com/coder/windsurf/coder" + version = "1.2.0" + agent_id = coder_agent.example.id + folder = "/home/coder/project" + mcp = jsonencode({ + mcpServers = { + "github" : { + "url" : "https://api.githubcopilot.com/mcp/", + "headers" : { + "Authorization" : "Bearer ${data.coder_external_auth.github.access_token}", + }, + "type" : "http" + } + } + }) +} + +data "coder_external_auth" "github" { + id = "github" +} +``` diff --git a/registry/coder/modules/windsurf/main.test.ts b/registry/coder/modules/windsurf/main.test.ts index 6b520d330..88e67f3cf 100644 --- a/registry/coder/modules/windsurf/main.test.ts +++ b/registry/coder/modules/windsurf/main.test.ts @@ -3,6 +3,11 @@ import { runTerraformApply, runTerraformInit, testRequiredVariables, + runContainer, + execContainer, + removeContainer, + findResourceInstance, + readFileContainer, } from "~test"; describe("windsurf", async () => { @@ -85,4 +90,26 @@ describe("windsurf", async () => { expect(coder_app?.instances.length).toBe(1); expect(coder_app?.instances[0].attributes.order).toBe(22); }); + + it("writes ~/.codeium/windsurf/mcp_config.json when mcp provided", async () => { + const id = await runContainer("alpine"); + try { + const mcp = JSON.stringify({ servers: { demo: { url: "http://localhost:1234" } } }); + const state = await runTerraformApply(import.meta.dir, { + agent_id: "foo", + mcp, + }); + const script = findResourceInstance(state, "coder_script", "windsurf_mcp").script; + const resp = await execContainer(id, ["sh", "-c", script]); + if (resp.exitCode !== 0) { + console.log(resp.stdout); + console.log(resp.stderr); + } + expect(resp.exitCode).toBe(0); + const content = await readFileContainer(id, "/root/.codeium/windsurf/mcp_config.json"); + expect(content).toBe(mcp); + } finally { + await removeContainer(id); + } + }); }); diff --git a/registry/coder/modules/windsurf/main.tf b/registry/coder/modules/windsurf/main.tf index 2f9d02a5c..b75f86386 100644 --- a/registry/coder/modules/windsurf/main.tf +++ b/registry/coder/modules/windsurf/main.tf @@ -38,15 +38,37 @@ variable "group" { default = null } +variable "slug" { + type = string + description = "The slug of the app." + default = "windsurf" +} + +variable "display_name" { + type = string + description = "The display name of the app." + default = "Windsurf Editor" +} + +variable "mcp" { + type = string + description = "JSON-encoded string to configure MCP servers for Windsurf. When set, writes ~/.codeium/windsurf/mcp_config.json." + default = "" +} + data "coder_workspace" "me" {} data "coder_workspace_owner" "me" {} +locals { + mcp_b64 = var.mcp != "" ? base64encode(var.mcp) : "" +} + resource "coder_app" "windsurf" { agent_id = var.agent_id external = true icon = "/icon/windsurf.svg" - slug = "windsurf" - display_name = "Windsurf Editor" + slug = var.slug + display_name = var.display_name order = var.order group = var.group url = join("", [ @@ -63,6 +85,22 @@ resource "coder_app" "windsurf" { ]) } +resource "coder_script" "windsurf_mcp" { + count = var.mcp != "" ? 1 : 0 + agent_id = var.agent_id + display_name = "Windsurf MCP" + icon = "/icon/windsurf.svg" + run_on_start = true + start_blocks_login = false + script = <<-EOT + #!/bin/sh + set -eu + mkdir -p "$HOME/.codeium/windsurf" + echo -n "${local.mcp_b64}" | base64 -d > "$HOME/.codeium/windsurf/mcp_config.json" + chmod 600 "$HOME/.codeium/windsurf/mcp_config.json" + EOT +} + output "windsurf_url" { value = coder_app.windsurf.url description = "Windsurf Editor URL."