Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions registry/coder-labs/modules/codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,27 @@ module "codex" {
args = ["-y", "@modelcontextprotocol/server-github"]
type = "stdio"
EOT

mcp_config_remote_path = [
"https://example.com/codex/mcp.toml",
"https://raw.githubusercontent.com/acme/platform/main/.codex-mcp.toml",
]
}
```

> [!NOTE]
> Servers configured through `mcp` or `mcp_config_remote_path` are appended to Codex's user `config.toml`, making them available across every project the workspace owner opens.

> [!NOTE]
> Remote URLs should return valid TOML snippets, for example:
>
> ```toml
> [mcp_servers.github]
> command = "npx"
> args = ["-y", "@modelcontextprotocol/server-github"]
> type = "stdio"
> ```

### Serialize a downstream `coder_script` after the install pipeline

The module exposes the `scripts` output: an ordered list of `coder exp sync` names for the scripts this module creates (pre_install, install, post_install). Scripts that were not configured are absent.
Expand Down
48 changes: 48 additions & 0 deletions registry/coder-labs/modules/codex/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,54 @@ describe("codex", async () => {
expect(resp).toContain("GitHub integration");
});

test("remote-mcp-configs", async () => {
const successUrl = "file:///tmp/codex-remote-mcp.toml";
const failingUrl = "file:///tmp/does-not-exist.toml";
const remoteConfig = [
"[mcp_servers.RemoteGitHub]",
'command = "npx"',
'args = ["-y", "@modelcontextprotocol/server-github"]',
'type = "stdio"',
"",
"[mcp_servers.RemoteFilesystem]",
'command = "npx"',
'args = ["-y", "@modelcontextprotocol/server-filesystem", "/workspace"]',
'type = "stdio"',
].join("\n");

const { id, scripts } = await setup({
moduleVariables: {
mcp_config_remote_path: JSON.stringify([failingUrl, successUrl]),
},
});
await execContainer(id, [
"bash",
"-c",
`cat <<'EOF' > /tmp/codex-remote-mcp.toml\n${remoteConfig}\nEOF`,
]);
await runScripts(id, scripts);

const installLog = await readFileContainer(
id,
"/home/coder/.coder-modules/coder-labs/codex/logs/install.log",
);
expect(installLog).toContain(failingUrl);
expect(installLog).toContain(successUrl);
expect(installLog).toContain(
`Warning: Failed to fetch MCP configuration from '${failingUrl}'`,
);
expect(installLog).toContain(
`Appending remote MCP config from ${successUrl}`,
);

const configToml = await readFileContainer(
id,
"/home/coder/.codex/config.toml",
);
expect(configToml).toContain("[mcp_servers.RemoteGitHub]");
expect(configToml).toContain("[mcp_servers.RemoteFilesystem]");
});

test("minimal-default-config", async () => {
const { id, scripts } = await setup();
await runScripts(id, scripts);
Expand Down
7 changes: 7 additions & 0 deletions registry/coder-labs/modules/codex/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ variable "mcp" {
default = ""
}

variable "mcp_config_remote_path" {
type = list(string)
description = "List of URLs that return TOML MCP server configurations. When set, each config is fetched at install time and appended to Codex config.toml."
default = []
}

variable "model_reasoning_effort" {
type = string
description = "The reasoning effort for the model. One of: none, minimal, low, medium, high, xhigh. See https://platform.openai.com/docs/guides/latest-model#lower-reasoning-effort"
Expand Down Expand Up @@ -141,6 +147,7 @@ locals {
ARG_WORKDIR = local.workdir != "" ? base64encode(local.workdir) : ""
ARG_BASE_CONFIG_TOML = var.base_config_toml != "" ? base64encode(var.base_config_toml) : ""
ARG_MCP = var.mcp != "" ? base64encode(var.mcp) : ""
ARG_MCP_CONFIG_REMOTE_PATH = base64encode(jsonencode(var.mcp_config_remote_path))
ARG_ENABLE_AI_GATEWAY = tostring(var.enable_ai_gateway)
ARG_AIBRIDGE_CONFIG = var.enable_ai_gateway ? base64encode(local.aibridge_config) : ""
ARG_MODEL_REASONING_EFFORT = var.model_reasoning_effort
Expand Down
16 changes: 16 additions & 0 deletions registry/coder-labs/modules/codex/scripts/install.sh.tftpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ARG_CODEX_VERSION='${ARG_CODEX_VERSION}'
ARG_WORKDIR=$(echo -n '${ARG_WORKDIR}' | base64 -d)
ARG_BASE_CONFIG_TOML=$(echo -n '${ARG_BASE_CONFIG_TOML}' | base64 -d)
ARG_MCP=$(echo -n '${ARG_MCP}' | base64 -d)
ARG_MCP_CONFIG_REMOTE_PATH=$(echo -n '${ARG_MCP_CONFIG_REMOTE_PATH}' | base64 -d)
ARG_ENABLE_AI_GATEWAY='${ARG_ENABLE_AI_GATEWAY}'
ARG_AIBRIDGE_CONFIG=$(echo -n '${ARG_AIBRIDGE_CONFIG}' | base64 -d)
ARG_MODEL_REASONING_EFFORT='${ARG_MODEL_REASONING_EFFORT}'
Expand All @@ -23,6 +24,7 @@ printf "codex_version: %s\n" "$${ARG_CODEX_VERSION}"
printf "workdir: %s\n" "$${ARG_WORKDIR}"
printf "enable_ai_gateway: %s\n" "$${ARG_ENABLE_AI_GATEWAY}"
printf "install_codex: %s\n" "$${ARG_INSTALL}"
printf "mcp_config_remote_path: %s\n" "$${ARG_MCP_CONFIG_REMOTE_PATH}"
printf "model_reasoning_effort: %s\n" "$${ARG_MODEL_REASONING_EFFORT}"
echo "--------------------------------"

Expand Down Expand Up @@ -149,6 +151,20 @@ function populate_config_toml() {
echo "$${ARG_MCP}" >> "$${config_path}"
fi

if [ -n "$${ARG_MCP_CONFIG_REMOTE_PATH}" ] && [ "$${ARG_MCP_CONFIG_REMOTE_PATH}" != "[]" ]; then
for url in $(echo "$${ARG_MCP_CONFIG_REMOTE_PATH}" | jq -r '.[]'); do
printf "Fetching remote MCP config from %s\n" "$${url}"
remote_mcp=$(curl -fsSL "$${url}") || {
echo "Warning: Failed to fetch MCP configuration from '$${url}', continuing..."
continue
}
if [ -n "$${remote_mcp}" ]; then
printf "Appending remote MCP config from %s\n" "$${url}"
printf "\n%s\n" "$${remote_mcp}" >> "$${config_path}"
fi
done
fi

if [ "$${ARG_ENABLE_AI_GATEWAY}" = "true" ] && [ -n "$${ARG_AIBRIDGE_CONFIG}" ]; then
if ! grep -q '\[model_providers\.aigateway\]' "$${config_path}" 2>/dev/null; then
printf "Adding AI Gateway configuration\n"
Expand Down