From f5905ede3a742045958399d331a54d1d04d58fd1 Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 10 Mar 2026 12:04:11 +0200 Subject: [PATCH] feat: add Homebrew formula and cask for distribution Add Homebrew distribution files for MCPProxy: - Formula (homebrew/Formula/mcpproxy.rb): builds CLI from source with Go + Node.js, embeds frontend web UI, includes service block and test block - Cask (homebrew/Casks/mcpproxy.rb): installs signed/notarized tray app DMG with PKG installer, supports both arm64 and amd64 - Update script (homebrew/update-formula.sh): automates version bumps by downloading assets and computing SHA256 hashes - README with tap setup instructions and homebrew-core/homebrew-cask submission guide All SHA256 hashes are computed from real v0.20.2 release assets. Co-Authored-By: Claude Opus 4.6 --- homebrew/Casks/mcpproxy.rb | 70 +++++++++++++++++++ homebrew/Formula/mcpproxy.rb | 81 ++++++++++++++++++++++ homebrew/README.md | 131 +++++++++++++++++++++++++++++++++++ homebrew/update-formula.sh | 90 ++++++++++++++++++++++++ 4 files changed, 372 insertions(+) create mode 100644 homebrew/Casks/mcpproxy.rb create mode 100644 homebrew/Formula/mcpproxy.rb create mode 100644 homebrew/README.md create mode 100755 homebrew/update-formula.sh diff --git a/homebrew/Casks/mcpproxy.rb b/homebrew/Casks/mcpproxy.rb new file mode 100644 index 00000000..f0ad1851 --- /dev/null +++ b/homebrew/Casks/mcpproxy.rb @@ -0,0 +1,70 @@ +# typed: false +# frozen_string_literal: true + +# Homebrew cask for MCPProxy Tray App (macOS GUI application) +# To install from the tap: +# brew tap smart-mcp-proxy/mcpproxy +# brew install --cask mcpproxy +# +# The cask installs the tray app (GUI) which includes the core server. +# For the headless CLI only, use: brew install mcpproxy + +cask "mcpproxy" do + version "0.20.2" + + on_arm do + sha256 "ec7dafc012b5d7c08b582b5a70f8e7db2216758e5e27e0fbf1d385f212c26998" + url "https://github.com/smart-mcp-proxy/mcpproxy-go/releases/download/v#{version}/mcpproxy-#{version}-darwin-arm64-installer.dmg", + verified: "github.com/smart-mcp-proxy/mcpproxy-go/" + end + + on_intel do + sha256 "912031a7dc739641016c9fa79d76d1292e415cd781f5a4766eebd2cbc0fc4b9a" + url "https://github.com/smart-mcp-proxy/mcpproxy-go/releases/download/v#{version}/mcpproxy-#{version}-darwin-amd64-installer.dmg", + verified: "github.com/smart-mcp-proxy/mcpproxy-go/" + end + + name "MCPProxy" + desc "Smart MCP proxy with intelligent tool discovery for AI agents" + homepage "https://mcpproxy.app" + + livecheck do + url :url + strategy :github_latest + end + + depends_on macos: ">= :monterey" + + on_arm do + pkg "mcpproxy-#{version}-darwin-arm64.pkg" + end + + on_intel do + pkg "mcpproxy-#{version}-darwin-amd64.pkg" + end + + uninstall pkgutil: "com.smartmcpproxy.mcpproxy", + launchctl: "com.smartmcpproxy.mcpproxy", + delete: [ + "/usr/local/bin/mcpproxy", + ] + + zap trash: [ + "~/.mcpproxy", + "~/Library/Logs/mcpproxy", + ] + + caveats <<~EOS + MCPProxy has been installed! + + To start the tray app, open MCPProxy from your Applications folder. + + To use the CLI directly: + mcpproxy serve # Start the server + mcpproxy doctor # Run health checks + mcpproxy upstream list # List upstream servers + + Configuration: ~/.mcpproxy/mcp_config.json + Documentation: https://docs.mcpproxy.app + EOS +end diff --git a/homebrew/Formula/mcpproxy.rb b/homebrew/Formula/mcpproxy.rb new file mode 100644 index 00000000..c4231fd5 --- /dev/null +++ b/homebrew/Formula/mcpproxy.rb @@ -0,0 +1,81 @@ +# typed: false +# frozen_string_literal: true + +# Homebrew formula for MCPProxy CLI (headless server) +# To install from the tap: +# brew tap smart-mcp-proxy/mcpproxy +# brew install mcpproxy +# +# To submit to homebrew-core, see homebrew/README.md + +class Mcpproxy < Formula + desc "Smart MCP proxy with intelligent tool discovery for AI agents" + homepage "https://mcpproxy.app" + url "https://github.com/smart-mcp-proxy/mcpproxy-go/archive/refs/tags/v0.20.2.tar.gz" + sha256 "aec23fff361d3bc9c874de0a37472301404bdeed18b4625dddcef492fb364914" + license "MIT" + head "https://github.com/smart-mcp-proxy/mcpproxy-go.git", branch: "main" + + depends_on "go" => :build + depends_on "node" => :build + + def install + # Generate TypeScript types from Go contracts (needed before frontend build) + system "go", "run", "./cmd/generate-types" + + # Build frontend (embedded in the binary via go:embed) + cd "frontend" do + system "npm", "install" + system "npm", "run", "build" + end + + # Copy frontend dist for embedding + mkdir_p "web/frontend" + cp_r "frontend/dist", "web/frontend/" + + ldflags = %W[ + -s -w + -X main.version=v#{version} + -X main.commit=brew + -X main.date=#{time.iso8601} + -X github.com/smart-mcp-proxy/mcpproxy-go/internal/httpapi.buildVersion=v#{version} + ] + system "go", "build", *std_go_args(ldflags: ldflags), "./cmd/mcpproxy" + end + + def post_install + (var/"log/mcpproxy").mkpath + end + + service do + run [opt_bin/"mcpproxy", "serve"] + keep_alive true + log_path var/"log/mcpproxy/output.log" + error_log_path var/"log/mcpproxy/error.log" + end + + test do + # Verify version output + assert_match "MCPProxy v#{version}", shell_output("#{bin}/mcpproxy version") + + # Verify help output + assert_match "Smart MCP Proxy", shell_output("#{bin}/mcpproxy --help") + + # Verify serve command exists + assert_match "serve", shell_output("#{bin}/mcpproxy --help") + + # Try starting the server briefly to verify it can bind + port = free_port + pid = fork do + exec bin/"mcpproxy", "serve", "--listen", "127.0.0.1:#{port}" + end + sleep 2 + + # Check the status endpoint responds + output = shell_output("curl -sf http://127.0.0.1:#{port}/api/v1/status 2>/dev/null || true") + assert_match "edition", output if output.length > 0 + ensure + Process.kill("TERM", pid) if pid + Process.wait(pid) if pid + end +end diff --git a/homebrew/README.md b/homebrew/README.md new file mode 100644 index 00000000..50541298 --- /dev/null +++ b/homebrew/README.md @@ -0,0 +1,131 @@ +# Homebrew Distribution for MCPProxy + +MCPProxy is distributed via Homebrew in two ways: + +| Package | Type | What it installs | +|---------|------|-----------------| +| `mcpproxy` (formula) | CLI | Headless server binary, built from source | +| `mcpproxy` (cask) | GUI | macOS tray app + CLI via signed PKG installer | + +## Quick Start + +### Install the CLI (formula, builds from source) + +```bash +brew tap smart-mcp-proxy/mcpproxy +brew install mcpproxy +``` + +### Install the tray app (cask, prebuilt DMG) + +```bash +brew tap smart-mcp-proxy/mcpproxy +brew install --cask mcpproxy +``` + +## Setting Up the Tap Repository + +The tap repository must be created at `github.com/smart-mcp-proxy/homebrew-mcpproxy` with +this directory structure: + +``` +homebrew-mcpproxy/ + Formula/ + mcpproxy.rb # copy from homebrew/Formula/mcpproxy.rb + Casks/ + mcpproxy.rb # copy from homebrew/Casks/mcpproxy.rb +``` + +### Steps + +1. Create the repo: + ```bash + gh repo create smart-mcp-proxy/homebrew-mcpproxy --public \ + --description "Homebrew tap for MCPProxy" + ``` + +2. Copy the formula and cask files: + ```bash + git clone git@github.com:smart-mcp-proxy/homebrew-mcpproxy.git + cd homebrew-mcpproxy + mkdir -p Formula Casks + cp /path/to/mcpproxy-go/homebrew/Formula/mcpproxy.rb Formula/ + cp /path/to/mcpproxy-go/homebrew/Casks/mcpproxy.rb Casks/ + git add -A && git commit -m "Add mcpproxy formula and cask" + git push + ``` + +3. Test: + ```bash + brew tap smart-mcp-proxy/mcpproxy + brew install mcpproxy # formula (CLI) + brew install --cask mcpproxy # cask (tray app) + ``` + +## Updating to a New Release + +Run the update script from the mcpproxy-go repo: + +```bash +./homebrew/update-formula.sh v0.21.0 # specific version +./homebrew/update-formula.sh # auto-detect latest +``` + +This downloads the source tarball and DMG assets, computes SHA256 hashes, and updates +both files in place. Then copy the updated files to the tap repo and push. + +## Submitting to homebrew-core + +The `mcpproxy` formula (CLI) can be submitted to homebrew-core once the project meets +the acceptance criteria: + +### Requirements + +- **75+ GitHub stars** (currently 155+, met) +- **30+ forks OR 30+ watchers** (check current count) +- **No vendored dependencies** (uses Go modules, OK) +- **MIT license** (met) +- **Stable versioning** (met, uses semver tags) +- **Working test block** (included) + +### Submission Steps + +1. Fork `Homebrew/homebrew-core` +2. Add `Formula/m/mcpproxy.rb` with the formula content +3. Run local checks: + ```bash + brew audit --new --formula Formula/m/mcpproxy.rb + brew install --build-from-source Formula/m/mcpproxy.rb + brew test Formula/m/mcpproxy.rb + ``` +4. Open a PR to `Homebrew/homebrew-core` + +### Notes for homebrew-core + +- Remove the `head` stanza (not allowed in homebrew-core) +- Remove the `typed` and `frozen_string_literal` comments +- The `service` block is allowed in homebrew-core +- The `node` build dependency is acceptable (many Go projects embed web UIs) + +## Submitting the Cask to homebrew-cask + +Casks have a higher bar for acceptance: + +### Requirements + +- **225+ GitHub stars** (not yet met, currently ~155) +- Signed and notarized macOS binary (met) +- Stable download URLs (met, GitHub Releases) + +### When ready + +1. Fork `Homebrew/homebrew-cask` +2. Add `Casks/m/mcpproxy.rb` with the cask content +3. Run: + ```bash + brew audit --new --cask Casks/m/mcpproxy.rb + brew install --cask Casks/m/mcpproxy.rb + ``` +4. Open a PR to `Homebrew/homebrew-cask` + +Until then, the cask is available through the tap (`brew tap smart-mcp-proxy/mcpproxy`). diff --git a/homebrew/update-formula.sh b/homebrew/update-formula.sh new file mode 100755 index 00000000..9c958495 --- /dev/null +++ b/homebrew/update-formula.sh @@ -0,0 +1,90 @@ +#!/bin/bash +set -euo pipefail + +# Update Homebrew formula and cask with new version and SHA256 hashes. +# Usage: +# ./homebrew/update-formula.sh v0.21.0 +# ./homebrew/update-formula.sh # auto-detect latest release + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FORMULA="${SCRIPT_DIR}/Formula/mcpproxy.rb" +CASK="${SCRIPT_DIR}/Casks/mcpproxy.rb" +REPO="smart-mcp-proxy/mcpproxy-go" + +# Determine version +if [ -n "${1:-}" ]; then + VERSION="${1#v}" + TAG="v${VERSION}" +else + echo "Auto-detecting latest release..." + TAG=$(gh release view --repo "${REPO}" --json tagName -q '.tagName') + VERSION="${TAG#v}" +fi + +echo "Updating Homebrew files to version ${VERSION} (tag ${TAG})" + +# --- Source tarball (formula) --- +SOURCE_URL="https://github.com/${REPO}/archive/refs/tags/${TAG}.tar.gz" +echo "Downloading source tarball..." +SOURCE_SHA256=$(curl -sL "${SOURCE_URL}" | shasum -a 256 | awk '{print $1}') +echo " Source SHA256: ${SOURCE_SHA256}" + +# --- DMG assets (cask) --- +ARM64_DMG_URL="https://github.com/${REPO}/releases/download/${TAG}/mcpproxy-${VERSION}-darwin-arm64-installer.dmg" +AMD64_DMG_URL="https://github.com/${REPO}/releases/download/${TAG}/mcpproxy-${VERSION}-darwin-amd64-installer.dmg" + +echo "Downloading arm64 DMG..." +ARM64_SHA256=$(curl -sL "${ARM64_DMG_URL}" | shasum -a 256 | awk '{print $1}') +echo " arm64 DMG SHA256: ${ARM64_SHA256}" + +echo "Downloading amd64 DMG..." +AMD64_SHA256=$(curl -sL "${AMD64_DMG_URL}" | shasum -a 256 | awk '{print $1}') +echo " amd64 DMG SHA256: ${AMD64_SHA256}" + +# --- Update formula --- +echo "Updating formula..." + +# Update URL +sed -i '' "s|url \"https://github.com/${REPO}/archive/refs/tags/v[^\"]*\.tar\.gz\"|url \"https://github.com/${REPO}/archive/refs/tags/${TAG}.tar.gz\"|" "${FORMULA}" + +# Update SHA256 +sed -i '' "/^ url.*archive\/refs\/tags/{ n; s/sha256 \"[a-f0-9]*\"/sha256 \"${SOURCE_SHA256}\"/; }" "${FORMULA}" + +echo " Formula updated." + +# --- Update cask --- +echo "Updating cask..." + +# Update version +sed -i '' "s/version \"[^\"]*\"/version \"${VERSION}\"/" "${CASK}" + +# Update arm64 SHA256 (first sha256 in the file, inside on_arm block) +# Use awk for precise block-aware replacement +awk -v arm_sha="${ARM64_SHA256}" -v amd_sha="${AMD64_SHA256}" ' + /on_arm do/ { in_arm=1 } + /on_intel do/ { in_arm=0; in_intel=1 } + /^ end$/ { in_arm=0; in_intel=0 } + in_arm && /sha256/ { sub(/sha256 "[a-f0-9]*"/, "sha256 \"" arm_sha "\"") } + in_intel && /sha256/ { sub(/sha256 "[a-f0-9]*"/, "sha256 \"" amd_sha "\"") } + { print } +' "${CASK}" > "${CASK}.tmp" && mv "${CASK}.tmp" "${CASK}" + +echo " Cask updated." + +# --- Summary --- +echo "" +echo "=== Update Summary ===" +echo "Version: ${VERSION}" +echo "Source SHA256: ${SOURCE_SHA256}" +echo "arm64 DMG SHA256: ${ARM64_SHA256}" +echo "amd64 DMG SHA256: ${AMD64_SHA256}" +echo "" +echo "Files updated:" +echo " ${FORMULA}" +echo " ${CASK}" +echo "" +echo "Next steps:" +echo " 1. Review changes: git diff homebrew/" +echo " 2. Test formula: brew install --build-from-source homebrew/Formula/mcpproxy.rb" +echo " 3. Test cask: brew install --cask homebrew/Casks/mcpproxy.rb" +echo " 4. Commit: git add homebrew/ && git commit -m 'homebrew: update to ${VERSION}'"