From 7d58ed23a477fb9e786a3e73ea5703b33277b746 Mon Sep 17 00:00:00 2001 From: Oleks Date: Thu, 14 May 2026 23:55:22 +0300 Subject: [PATCH] metamcp: package v2.4.22 as a NixOS-deployable service MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Single-derivation pnpm/Turbo monorepo build producing: - metamcp full orchestrator (waits for PG, runs drizzle migrations, launches backend :12009 + frontend :12008) - metamcp-backend bare backend launcher - metamcp-frontend Next.js standalone server.js launcher Notes: - Upstream pins packageManager: pnpm@9.0.0; rewrite to match the nixpkgs pnpm (Turbo requires the field but won't fight a matching version). - Frontend uses next.config 'output: standalone'; we copy .next/static and public/ into the standalone tree since Next doesn't. - HOSTNAME defaults to 0.0.0.0 (override with METAMCP_HOSTNAME) — Next standalone otherwise inherits the system hostname and is unreachable on 127.0.0.1. --- flake.nix | 1 + packages/metamcp.nix | 151 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 152 insertions(+) create mode 100644 packages/metamcp.nix diff --git a/flake.nix b/flake.nix index b56eee9..72ed57e 100644 --- a/flake.nix +++ b/flake.nix @@ -57,6 +57,7 @@ { hello-world = pkgs.callPackage ./packages/hello-world.nix { }; geesefs = pkgs.callPackage ./packages/geesefs.nix { }; + metamcp = pkgs.callPackage ./packages/metamcp.nix { }; xonsh = pkgs.callPackage ./packages/xonsh.nix { xonsh-unwrapped = import ./packages/xonsh-unwrapped.nix { inherit (pkgs) lib python3Packages fetchFromGitHub; diff --git a/packages/metamcp.nix b/packages/metamcp.nix new file mode 100644 index 0000000..82fa8d7 --- /dev/null +++ b/packages/metamcp.nix @@ -0,0 +1,151 @@ +{ + lib, + stdenv, + fetchFromGitHub, + makeWrapper, + nodejs_20, + pnpm_10, + fetchPnpmDeps, + pnpmConfigHook, + postgresql, +}: + +# MetaMCP — MCP aggregator/orchestrator. +# +# Upstream is a pnpm monorepo (Turbo) with two apps: +# - apps/backend : Express/tRPC API, built with tsup -> dist/index.js +# port 12009 (internal), runs drizzle-kit migrations at boot +# - apps/frontend : Next.js 15, port 12008 (public), launched via `next start` +# +# Both require runtime node_modules (Next.js non-standalone; drizzle-kit is +# a devDep invoked at runtime by docker-entrypoint.sh). We ship the whole +# built tree under $out/lib/metamcp and provide launcher scripts. +# +# The upstream Dockerfile patches Next.js' proxy timeout (30s -> 600s) by +# sed-editing two files inside node_modules/.pnpm/next@.../...; we replicate +# that in postBuild so the behaviour matches the official image. +# +# Build is single-derivation: pnpm fetch -> pnpm build -> install. First +# build prints the right pnpmDeps hash; paste it back here and rebuild. + +let + pname = "metamcp"; + version = "2.4.22"; + + src = fetchFromGitHub { + owner = "metatool-ai"; + repo = "metamcp"; + rev = "v${version}"; + hash = lib.fakeHash; # nix build will print the right one + }; +in +stdenv.mkDerivation (finalAttrs: { + inherit pname version src; + + pnpmDeps = fetchPnpmDeps { + inherit pname version src; + fetcherVersion = 3; + hash = lib.fakeHash; # nix build will print the right one + }; + + nativeBuildInputs = [ + nodejs_20 + pnpm_10 + pnpmConfigHook + makeWrapper + ]; + + # pnpmConfigHook places node_modules; build the workspace with Turbo. + buildPhase = '' + runHook preBuild + + # Match the upstream Dockerfile sed-patch on Next.js proxy timeout. + # Files live under the pnpm virtual store; glob to tolerate minor + # next/react version bumps on later tags. + for f in \ + node_modules/.pnpm/next@*/node_modules/next/dist/server/lib/router-utils/proxy-request.js \ + node_modules/.pnpm/next@*/node_modules/next/dist/esm/server/lib/router-utils/proxy-request.js; do + if [ -f "$f" ]; then + sed -i -e "s/30000/600000/" "$f" + fi + done + + pnpm build + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out/lib/metamcp $out/bin + + # Ship the whole built workspace. This is bulky but reliable: + # `next start` needs .next + node_modules + package.json side-by-side, + # and the backend's runtime invokes `pnpm exec drizzle-kit migrate`. + cp -r \ + apps \ + packages \ + node_modules \ + package.json \ + pnpm-workspace.yaml \ + pnpm-lock.yaml \ + turbo.json \ + docker-entrypoint.sh \ + $out/lib/metamcp/ + + # Sanitise the entrypoint: drop the hard-coded `cd /app/...` paths, + # set our launch root via $METAMCP_ROOT, keep the orchestration logic. + substituteInPlace $out/lib/metamcp/docker-entrypoint.sh \ + --replace-fail "/app" "$out/lib/metamcp" + + # Launcher: identical sequence to docker-entrypoint.sh, with PATH + # carrying pg_isready (for the readiness wait) and node. + cat > $out/bin/metamcp < $out/bin/metamcp-frontend <