From d3fbd8a990d39c9357339045f9fa7f9050e6306d Mon Sep 17 00:00:00 2001 From: Zach Yannes Date: Wed, 21 Oct 2026 12:03:51 -0400 Subject: [PATCH] Revert "feat(mandoline-mcp): add Mandoline MCP installer module (#17)" This reverts commit 51730789b4a9c3eabe43dbbf7d35b231d87816a5. --- .github/workflows/mandoline-mcp.yaml | 26 ------- README.md | 14 ---- modules/mandoline-mcp/README.md | 82 -------------------- modules/mandoline-mcp/main.tf | 25 ------ modules/mandoline-mcp/main.tftest.hcl | 61 --------------- modules/mandoline-mcp/outputs.tf | 19 ----- modules/mandoline-mcp/scripts/install.sh.tpl | 63 --------------- modules/mandoline-mcp/variables.tf | 15 ---- 8 files changed, 305 deletions(-) delete mode 100644 .github/workflows/mandoline-mcp.yaml delete mode 100644 modules/mandoline-mcp/README.md delete mode 100644 modules/mandoline-mcp/main.tf delete mode 100644 modules/mandoline-mcp/main.tftest.hcl delete mode 100644 modules/mandoline-mcp/outputs.tf delete mode 100644 modules/mandoline-mcp/scripts/install.sh.tpl delete mode 100644 modules/mandoline-mcp/variables.tf diff --git a/.github/workflows/mandoline-mcp.yaml b/.github/workflows/mandoline-mcp.yaml deleted file mode 100644 index 6a25262..0000000 --- a/.github/workflows/mandoline-mcp.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Mandoline MCP tests -on: - pull_request: - paths: - - "modules/mandoline-mcp/**" - - ".github/workflows/mandoline-mcp.yaml" - push: - branches: [main] - paths: - - "modules/mandoline-mcp/**" - - ".github/workflows/mandoline-mcp.yaml" - -jobs: - terraform: - runs-on: ubuntu-latest - defaults: - run: - working-directory: modules/mandoline-mcp - steps: - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 - - uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3 - with: - terraform_version: "1.13.5" - - run: terraform fmt -check - - run: terraform init -backend=false - - run: terraform test diff --git a/README.md b/README.md index 6900186..3516195 100644 --- a/README.md +++ b/README.md @@ -92,17 +92,3 @@ module "git_credential_coder" { agent_id = coder_agent.main.id } ``` - -## `mandoline-mcp` - -[`modules/mandoline-mcp`](modules/mandoline-mcp) installs Mandoline on the -agent PATH so clients can run `mandoline mcp` as a local stdio MCP server. -The startup script downloads the Linux x86_64 release from GitHub. The calling -template registers `mandoline` with args `["mcp"]` in its coding clients. - -```hcl -module "mandoline_mcp" { - source = "./modules/mandoline-mcp" - agent_id = coder_agent.main.id -} -``` diff --git a/modules/mandoline-mcp/README.md b/modules/mandoline-mcp/README.md deleted file mode 100644 index 7bcbe4b..0000000 --- a/modules/mandoline-mcp/README.md +++ /dev/null @@ -1,82 +0,0 @@ -# Mandoline MCP - -Installs [Mandoline](https://github.com/semgrep/mandoline) on the agent PATH -so coding clients can run `mandoline mcp` as a stdio MCP server. The startup -script downloads the Linux x86_64 release archive from the private Mandoline -GitHub repository using the authenticated GitHub CLI. - -## Usage - -```hcl -module "mandoline_mcp" { - source = "./modules/mandoline-mcp" - agent_id = coder_agent.main.id - # Optional: pin an exact release instead of latest. - # mandoline_version = "0.15.0" -} -``` - -The module installs `mandoline` in `/usr/local/bin` and prepends that directory to -the agent PATH. Register `module.mandoline_mcp.launcher_command` with -`module.mandoline_mcp.launcher_args` in your clients' MCP configuration. For -example, Claude Code accepts: - -```json -{ - "mcpServers": { - "mandoline": { "command": "mandoline", "args": ["mcp"] } - } -} -``` - -Codex accepts this TOML: - -```toml -[mcp_servers.mandoline] -command = "mandoline" -args = ["mcp"] -``` - -Client registration remains the calling template's responsibility. Registration -applies to the agent on which the module runs; clients in another container need -access to the installed Mandoline binary. - -## Installation - -The agent image must provide `gh`, `tar`, and a POSIX shell, and the agent user -must be able to write to `/usr/local/bin`. Because Mandoline releases are private, -`gh` must already be authenticated to GitHub with access to -`semgrep/mandoline` (for example via `GH_TOKEN` or `gh auth login`). - -The installer targets Linux x86_64 (`amd64`) only. For `latest`, it resolves -the newest non-draft, non-prerelease with `gh release list`, then runs -`gh release download` for `mandoline-linux-x86_64.tar.gz`. It extracts the -archive in a temporary directory, installs `/usr/local/bin/mandoline`, and removes -the temporary files. - -Pin an exact semantic version such as `0.15.0` to download the matching -`v0.15.0` release tag and reinstall when that pin changes. Changing the -selector, or removing `/usr/local/bin/mandoline` (and `/usr/local/bin/mandoline.version`), -refreshes the binary on the next workspace start. - -Mandoline analyzes the client's working directory; no Coder external -authentication is required for the MCP server itself. - -| Input | Default | Description | -| --- | --- | --- | -| `agent_id` | Required | Coder agent receiving the installer and PATH entry | -| `mandoline_version` | `latest` | `latest` or an exact release such as `0.15.0` | - -Outputs: `launcher_command` is `mandoline`; `launcher_args` is `["mcp"]`; -`launcher_path` is `/usr/local/bin/mandoline`; `mandoline_version` echoes the -configured selector. - -## Validation - -```sh -cd modules/mandoline-mcp -terraform init -backend=false -terraform test -``` - -Terraform tests use a mock Coder provider. diff --git a/modules/mandoline-mcp/main.tf b/modules/mandoline-mcp/main.tf deleted file mode 100644 index 76fc29a..0000000 --- a/modules/mandoline-mcp/main.tf +++ /dev/null @@ -1,25 +0,0 @@ -terraform { - required_providers { - coder = { - source = "coder/coder" - } - } -} - -resource "coder_env" "mandoline_mcp_path" { - agent_id = var.agent_id - name = "PATH" - value = "/usr/local/bin:$PATH" - merge_strategy = "prepend" -} - -resource "coder_script" "mandoline_mcp" { - agent_id = var.agent_id - display_name = "Install Mandoline MCP" - run_on_start = true - start_blocks_login = true - timeout = 300 - script = templatefile("${path.module}/scripts/install.sh.tpl", { - mandoline_version = var.mandoline_version - }) -} diff --git a/modules/mandoline-mcp/main.tftest.hcl b/modules/mandoline-mcp/main.tftest.hcl deleted file mode 100644 index 965c7c5..0000000 --- a/modules/mandoline-mcp/main.tftest.hcl +++ /dev/null @@ -1,61 +0,0 @@ -mock_provider "coder" {} - -run "defaults" { - command = plan - variables { - agent_id = "test-agent" - } - assert { - condition = output.launcher_command == "mandoline" - error_message = "Clients must receive the mandoline binary as the MCP command." - } - assert { - condition = length(output.launcher_args) == 1 && output.launcher_args[0] == "mcp" - error_message = "Clients must receive mcp as the Mandoline MCP argument." - } - assert { - condition = output.launcher_path == "/usr/local/bin/mandoline" - error_message = "Clients must receive the /usr/local/bin Mandoline binary path." - } - assert { - condition = coder_env.mandoline_mcp_path.name == "PATH" && coder_env.mandoline_mcp_path.merge_strategy == "prepend" && coder_env.mandoline_mcp_path.value == "/usr/local/bin:$PATH" - error_message = "The Mandoline install directory must be prepended to PATH." - } - assert { - condition = strcontains(coder_script.mandoline_mcp.script, "gh release list --repo \"$repo\"") && strcontains(coder_script.mandoline_mcp.script, "gh release download \"$release\" --repo \"$repo\" --pattern \"$asset\"") && strcontains(coder_script.mandoline_mcp.script, "repo='semgrep/mandoline'") - error_message = "The installer must download private Mandoline releases with an authenticated gh CLI." - } - assert { - condition = strcontains(coder_script.mandoline_mcp.script, "install_dir=\"/usr/local/bin\"") && strcontains(coder_script.mandoline_mcp.script, "mktemp -d") - error_message = "The installer must install into /usr/local/bin and stage downloads with mktemp -d." - } -} - -run "pinned_version" { - command = plan - variables { - agent_id = "test-agent" - mandoline_version = "0.15.0" - } - assert { - condition = strcontains(coder_script.mandoline_mcp.script, "selector='0.15.0'") - error_message = "A pinned version must be recorded for idempotent upgrades." - } - assert { - condition = strcontains(coder_script.mandoline_mcp.script, "release=\"v$${selector}\"") - error_message = "A pinned version must download the matching Mandoline release tag." - } - assert { - condition = output.mandoline_version == "0.15.0" - error_message = "The configured Mandoline version must be exported." - } -} - -run "reject_invalid_version" { - command = plan - variables { - agent_id = "test-agent" - mandoline_version = "v0.15.0" - } - expect_failures = [var.mandoline_version] -} diff --git a/modules/mandoline-mcp/outputs.tf b/modules/mandoline-mcp/outputs.tf deleted file mode 100644 index a2a90c6..0000000 --- a/modules/mandoline-mcp/outputs.tf +++ /dev/null @@ -1,19 +0,0 @@ -output "launcher_command" { - description = "MCP stdio command on the agent PATH. Pair with launcher_args." - value = "mandoline" -} - -output "launcher_args" { - description = "Arguments that start Mandoline as an MCP stdio server." - value = ["mcp"] -} - -output "launcher_path" { - description = "Installed Mandoline binary path." - value = "/usr/local/bin/mandoline" -} - -output "mandoline_version" { - description = "Configured Mandoline release selector used by the installer." - value = var.mandoline_version -} diff --git a/modules/mandoline-mcp/scripts/install.sh.tpl b/modules/mandoline-mcp/scripts/install.sh.tpl deleted file mode 100644 index 40fd546..0000000 --- a/modules/mandoline-mcp/scripts/install.sh.tpl +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh -# Download Mandoline via gh and place it on the agent PATH for `mandoline mcp`. -set -eu -umask 077 - -install_dir="/usr/local/bin" -version_file="$install_dir/mandoline.version" -selector='${mandoline_version}' -asset='mandoline-linux-x86_64.tar.gz' -repo='semgrep/mandoline' - -fail() { - echo "Mandoline MCP: $1" >&2 - exit 1 -} - -command -v gh >/dev/null 2>&1 || fail 'GitHub CLI (gh) is required to download Mandoline.' -command -v tar >/dev/null 2>&1 || fail 'tar is required to extract Mandoline.' - -case "$(uname -s)" in - Linux) ;; - *) fail "Unsupported operating system for Mandoline MCP: $(uname -s)." ;; -esac - -case "$(uname -m)" in - x86_64|amd64) ;; - *) fail "Unsupported architecture for Mandoline MCP: $(uname -m). Expected x86_64." ;; -esac - -if [ "$selector" = latest ]; then - release="$(gh release list --repo "$repo" --exclude-drafts --exclude-pre-releases --limit 1 --json name --jq '.[0].name')" \ - || fail "Could not list Mandoline releases from $repo." - [ -n "$release" ] && [ "$release" != null ] \ - || fail "No Mandoline releases found in $repo." -else - release="v$${selector}" -fi - -mkdir -p "$install_dir" - -if [ ! -x "$install_dir/mandoline" ] || [ "$(cat "$version_file" 2>/dev/null || true)" != "$release" ]; then - staging="$(mktemp -d)" - trap 'rm -rf "$staging"' EXIT HUP INT TERM - echo "Downloading Mandoline ($release)…" - ( - cd "$staging" - gh release download "$release" --repo "$repo" --pattern "$asset" \ - || fail "Could not download Mandoline release $release from $repo." - tar -xf "$asset" \ - || fail 'Could not extract the Mandoline archive.' - [ -f mandoline ] || fail 'Mandoline archive did not contain a mandoline binary.' - chmod 0755 mandoline - mv mandoline "$install_dir/mandoline" - rm -f "$asset" - ) - printf '%s\n' "$release" > "$version_file" - chmod 600 "$version_file" - rm -rf "$staging" - trap - EXIT HUP INT TERM -fi - -[ -x "$install_dir/mandoline" ] || fail 'Mandoline installation finished without an executable binary.' -echo "Mandoline MCP installed ($release); use \`mandoline mcp\`." diff --git a/modules/mandoline-mcp/variables.tf b/modules/mandoline-mcp/variables.tf deleted file mode 100644 index 35f0f05..0000000 --- a/modules/mandoline-mcp/variables.tf +++ /dev/null @@ -1,15 +0,0 @@ -variable "agent_id" { - description = "Coder agent on which to install the Mandoline MCP launcher." - type = string -} - -variable "mandoline_version" { - description = "Desired Mandoline release, or latest to install from the GitHub latest release asset." - type = string - default = "latest" - - validation { - condition = var.mandoline_version == "latest" || can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?$", var.mandoline_version)) - error_message = "mandoline_version must be latest or an exact semantic version such as 0.17.0." - } -}