Skip to content
Open
2 changes: 1 addition & 1 deletion registry/coder/modules/vscode-desktop-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The VSCode Desktop Core module is a building block for modules that need to expo
```tf
module "vscode-desktop-core" {
source = "registry.coder.com/coder/vscode-desktop-core/coder"
version = "1.0.2"
version = "1.1.0"

agent_id = var.agent_id

Expand Down
43 changes: 43 additions & 0 deletions registry/coder/modules/vscode-desktop-core/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import {
runTerraformApply,
runTerraformInit,
testRequiredVariables,
runContainer,
execContainer,
removeContainer,
findResourceInstance,
readFileContainer,
} from "~test";

// hardcoded coder_app name in main.tf
Expand All @@ -16,6 +21,7 @@ const defaultVariables = {
coder_app_display_name: "VS Code Desktop",

protocol: "vscode",
config_dir: "$HOME/.vscode",
};

describe("vscode-desktop-core", async () => {
Expand Down Expand Up @@ -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_dir.replace("$HOME", "/root")}/mcp_config.json`,
);
expect(content).toBe(mcp_config);
} finally {
await removeContainer(id);
}
}, 10000);
});
50 changes: 38 additions & 12 deletions registry/coder/modules/vscode-desktop-core/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,22 @@ variable "open_recent" {
default = false
}

variable "mcp_config" {
type = map(any)
description = "MCP server configuration for the IDE. When set, writes mcp_config.json in var.config_dir."
default = null
}

variable "protocol" {
type = string
description = "The URI protocol the IDE."
}

variable "config_dir" {
type = string
description = "The path of the IDE's configuration folder."
}

variable "coder_app_icon" {
type = string
description = "The icon of the coder_app."
Expand Down Expand Up @@ -85,21 +96,36 @@ 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 != null ? 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_dir}"
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
}

output "ide_uri" {
value = coder_app.vscode-desktop.url
description = "IDE URI."
}
}
Loading