From ce3f794bb5695fdf9bde5afcc4dcf6c9c37a8fad Mon Sep 17 00:00:00 2001 From: Phorcys Date: Tue, 24 Feb 2026 02:16:43 +0000 Subject: [PATCH 1/5] feat: add `mcp_config` input variable to vscode-desktop-core module --- .../coder/modules/vscode-desktop-core/main.tf | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/registry/coder/modules/vscode-desktop-core/main.tf b/registry/coder/modules/vscode-desktop-core/main.tf index 9a7da34c1..ad203a2b7 100644 --- a/registry/coder/modules/vscode-desktop-core/main.tf +++ b/registry/coder/modules/vscode-desktop-core/main.tf @@ -26,11 +26,22 @@ variable "open_recent" { default = false } +variable "mcp_config" { + type = string + description = "JSON-encoded string to configure MCP servers for the IDE. When set, writes mcp_config.json in var.config_folder." + default = "" +} + variable "protocol" { type = string description = "The URI protocol the IDE." } +variable "config_folder" { + type = string + description = "The path of the IDE's configuration folder." +} + variable "coder_app_icon" { type = string description = "The icon of the coder_app." @@ -85,18 +96,29 @@ resource "coder_app" "vscode-desktop" { data.coder_workspace.me.access_url, "&token=$SESSION_TOKEN", ]) +} - /* - url = join("", [ - "vscode://coder.coder-remote/open", - "?owner=${data.coder_workspace_owner.me.name}", - "&workspace=${data.coder_workspace.me.name}", - var.folder != "" ? join("", ["&folder=", var.folder]) : "", - var.open_recent ? "&openRecent" : "", - "&url=${data.coder_workspace.me.access_url}", - "&token=$SESSION_TOKEN", - ]) - */ +resource "coder_script" "vscode-desktop-mcp" { + agent_id = var.agent_id + count = var.mcp_config != "" ? 1 : 0 + + icon = var.coder_app_icon + display_name = "${var.coder_app_display_name} MCP" + + run_on_start = true + start_blocks_login = false + + script = <<-EOT + #!/bin/sh + set -euo pipefail + + IDE_CONFIG_FOLDER="${var.config_folder}" + IDE_MCP_CONFIG_PATH="$IDE_CONFIG_FOLDER/mcp_config.json" + + mkdir -p "$IDE_CONFIG_FOLDER" + echo -n "${base64encode(var.mcp_config)}" | base64 -d > "$IDE_MCP_CONFIG_PATH" + chmod 600 "$IDE_MCP_CONFIG_PATH" + EOT } output "ide_uri" { From bec945775d7ec586ef5df9ca556f3f16f9d33ebe Mon Sep 17 00:00:00 2001 From: Phorcys Date: Tue, 24 Feb 2026 02:43:59 +0000 Subject: [PATCH 2/5] fix: add tests --- .../modules/vscode-desktop-core/main.test.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/registry/coder/modules/vscode-desktop-core/main.test.ts b/registry/coder/modules/vscode-desktop-core/main.test.ts index 87674f32d..692a5e584 100644 --- a/registry/coder/modules/vscode-desktop-core/main.test.ts +++ b/registry/coder/modules/vscode-desktop-core/main.test.ts @@ -3,6 +3,11 @@ import { runTerraformApply, runTerraformInit, testRequiredVariables, + runContainer, + execContainer, + removeContainer, + findResourceInstance, + readFileContainer, } from "~test"; // hardcoded coder_app name in main.tf @@ -16,6 +21,7 @@ const defaultVariables = { coder_app_display_name: "VS Code Desktop", protocol: "vscode", + config_folder: "$HOME/.vscode" }; describe("vscode-desktop-core", async () => { @@ -134,4 +140,41 @@ describe("vscode-desktop-core", async () => { expect(coder_app?.instances[0].attributes.group).toBe("web-app-group"); }); }); + + it("writes mcp_config.json when mcp_config variable provided", async () => { + const id = await runContainer("alpine"); + + try { + const mcp_config = JSON.stringify({ + servers: { demo: { url: "http://localhost:1234" } }, + }); + + const state = await runTerraformApply(import.meta.dir, { + ...defaultVariables, + + mcp_config, + }); + + const script = findResourceInstance( + state, + "coder_script", + "vscode-desktop-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, + `${defaultVariables.config_folder.replace("$HOME", "/root")}/mcp_config.json`, + ); + expect(content).toBe(mcp_config); + } finally { + await removeContainer(id); + } + }, 10000); }); From 1bfe6a57e8dd2febe0cab7eceef43e91464e4ebe Mon Sep 17 00:00:00 2001 From: Phorcys Date: Tue, 24 Feb 2026 02:52:06 +0000 Subject: [PATCH 3/5] chore: `bun fmt` --- registry/coder/modules/vscode-desktop-core/main.test.ts | 2 +- registry/coder/modules/vscode-desktop-core/main.tf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/coder/modules/vscode-desktop-core/main.test.ts b/registry/coder/modules/vscode-desktop-core/main.test.ts index 692a5e584..2db12a2d0 100644 --- a/registry/coder/modules/vscode-desktop-core/main.test.ts +++ b/registry/coder/modules/vscode-desktop-core/main.test.ts @@ -21,7 +21,7 @@ const defaultVariables = { coder_app_display_name: "VS Code Desktop", protocol: "vscode", - config_folder: "$HOME/.vscode" + config_folder: "$HOME/.vscode", }; describe("vscode-desktop-core", async () => { diff --git a/registry/coder/modules/vscode-desktop-core/main.tf b/registry/coder/modules/vscode-desktop-core/main.tf index ad203a2b7..4d16420f0 100644 --- a/registry/coder/modules/vscode-desktop-core/main.tf +++ b/registry/coder/modules/vscode-desktop-core/main.tf @@ -108,7 +108,7 @@ resource "coder_script" "vscode-desktop-mcp" { run_on_start = true start_blocks_login = false - script = <<-EOT + script = <<-EOT #!/bin/sh set -euo pipefail @@ -124,4 +124,4 @@ resource "coder_script" "vscode-desktop-mcp" { output "ide_uri" { value = coder_app.vscode-desktop.url description = "IDE URI." -} \ No newline at end of file +} From 909016687412fda7455828febb4a41074d793dfd Mon Sep 17 00:00:00 2001 From: Phorcys Date: Tue, 24 Feb 2026 02:58:14 +0000 Subject: [PATCH 4/5] feat: make `mcp_config` type `map(any)` to enforce valid JSON and prevent mistakes --- registry/coder/modules/vscode-desktop-core/main.tf | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/registry/coder/modules/vscode-desktop-core/main.tf b/registry/coder/modules/vscode-desktop-core/main.tf index 4d16420f0..416d03bd4 100644 --- a/registry/coder/modules/vscode-desktop-core/main.tf +++ b/registry/coder/modules/vscode-desktop-core/main.tf @@ -27,9 +27,9 @@ variable "open_recent" { } variable "mcp_config" { - type = string - description = "JSON-encoded string to configure MCP servers for the IDE. When set, writes mcp_config.json in var.config_folder." - default = "" + type = map(any) + description = "MCP server configuration for the IDE. When set, writes mcp_config.json in var.config_folder." + default = null } variable "protocol" { @@ -100,7 +100,7 @@ resource "coder_app" "vscode-desktop" { resource "coder_script" "vscode-desktop-mcp" { agent_id = var.agent_id - count = var.mcp_config != "" ? 1 : 0 + count = var.mcp_config != null ? 1 : 0 icon = var.coder_app_icon display_name = "${var.coder_app_display_name} MCP" @@ -116,7 +116,7 @@ resource "coder_script" "vscode-desktop-mcp" { IDE_MCP_CONFIG_PATH="$IDE_CONFIG_FOLDER/mcp_config.json" mkdir -p "$IDE_CONFIG_FOLDER" - echo -n "${base64encode(var.mcp_config)}" | base64 -d > "$IDE_MCP_CONFIG_PATH" + echo -n "${base64encode(jsonencode(var.mcp_config))}" | base64 -d > "$IDE_MCP_CONFIG_PATH" chmod 600 "$IDE_MCP_CONFIG_PATH" EOT } From 6840be6315921951d0f9c08d02a8d11335aa0448 Mon Sep 17 00:00:00 2001 From: Phorcys Date: Tue, 24 Feb 2026 14:33:38 +0000 Subject: [PATCH 5/5] feat: add symlink to `mcp.json` in vscode-desktop-core --- registry/coder/modules/vscode-desktop-core/main.tf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/registry/coder/modules/vscode-desktop-core/main.tf b/registry/coder/modules/vscode-desktop-core/main.tf index 416d03bd4..9ea962886 100644 --- a/registry/coder/modules/vscode-desktop-core/main.tf +++ b/registry/coder/modules/vscode-desktop-core/main.tf @@ -116,8 +116,12 @@ resource "coder_script" "vscode-desktop-mcp" { IDE_MCP_CONFIG_PATH="$IDE_CONFIG_FOLDER/mcp_config.json" mkdir -p "$IDE_CONFIG_FOLDER" + echo -n "${base64encode(jsonencode(var.mcp_config))}" | base64 -d > "$IDE_MCP_CONFIG_PATH" chmod 600 "$IDE_MCP_CONFIG_PATH" + + # Cursor/Windsurf use this config instead, no need for chmod as symlinks do not have modes + ln -s "$IDE_MCP_CONFIG_PATH" "$IDE_CONFIG_FOLDER/mcp.json" EOT }