jevstrudel, built from this repo: the Astro site (Strudel's REPL with the song browser), the static site deployed from it, and the stdio MCP proxy Claude Code starts from .mcp.json.
4{ 5 stdenv, 6 nodejs, 7 pnpm_10, 8 fetchPnpmDeps, 9 pnpmConfigHook, 10 importNpmLock, 11 runCommand, 12 writeShellApplication, 13 src, 14}: 15let 16 pname = "jevstrudel"; 17 version = "0-unstable-${src.lastModifiedDate or "0"}"; 18 19 pnpmDeps = fetchPnpmDeps { 20 inherit pname version src; 21 pnpm = pnpm_10; 22 fetcherVersion = 4; 23 hash = "sha256-xdDD3pJNjPjNAHLnGncq8ofay+HMaKGoAuPVEvFzG3g="; 24 }; 25 nativeBuildInputs = [ 26 nodejs 27 pnpm_10 28 pnpmConfigHook 29 ]; 30 31 site = stdenv.mkDerivation { 32 inherit 33 pname 34 version 35 src 36 pnpmDeps 37 nativeBuildInputs 38 ; 39 40 buildPhase = '' 41 runHook preBuild 42 pnpm run build 43 runHook postBuild 44 ''; 45 46 installPhase = '' 47 runHook preInstall 48 share=$out/share/jevstrudel 49 mkdir -p $share/website 50 cp -r website/dist $share/website/dist 51 # Upstream's CNAME names strudel.cc; a host that honours it (GitHub 52 # Pages) would try to claim Strudel's domain for this build. 53 rm $share/website/dist/CNAME 54 runHook postInstall 55 ''; 56 };
jevstrudel's tests (nix flake check): Jev's core, the Worker (the
relay's rules and log, the votes, the D1 migrations), the agreement
report, the deploy's storage step, the telemetry reader, Jev's decision
history, and every song evaluated and played to its end against a stand-in Jev
(website/src/jev/allSongs.test.mjs). Upstream's own suites are
Strudel's to run.
64 tests = stdenv.mkDerivation { 65 pname = "${pname}-tests"; 66 inherit 67 version 68 src 69 pnpmDeps 70 nativeBuildInputs 71 ; 72 dontBuild = true; 73 doCheck = true; 74 checkPhase = '' 75 runHook preCheck 76 pnpm exec vitest run website/src/jev worker tools/agreement tools/deploy tools/events tools/jev-history 77 runHook postCheck 78 ''; 79 installPhase = "touch $out"; 80 }; 81 # The Worker as the deploy uploads it and the type check reads it: 82 # worker/ with its own node_modules, holding exactly its dependencies' 83 # closure (the WebAuthn library), taken from the installed workspace by 84 # worker-closure.mjs. So a copy of it anywhere bundles and type-checks 85 # without the rest of the repo. 86 worker = stdenv.mkDerivation { 87 pname = "${pname}-worker"; 88 inherit 89 version 90 src 91 pnpmDeps 92 nativeBuildInputs 93 ; 94 dontBuild = true; 95 installPhase = '' 96 runHook preInstall 97 node nix/worker-closure.mjs worker $out 98 runHook postInstall 99 ''; 100 dontFixup = true; 101 };
tools/mcp/ with its node_modules, beside the two files it reads from worker/ (the dev port, and the dev tools for when the Worker is down), at the same relative paths as in the repo.
106 mcpNodeModules = importNpmLock.buildNodeModules { 107 npmRoot = "${src}/tools/mcp"; 108 inherit nodejs; 109 }; 110 mcpTree = runCommand "jevstrudel-mcp-tree" { } '' 111 mkdir -p $out/tools/mcp $out/worker/src 112 cp ${src}/tools/mcp/*.mjs $out/tools/mcp/ 113 ln -s ${mcpNodeModules}/node_modules $out/tools/mcp/node_modules 114 cp ${src}/worker/wrangler.json $out/worker/ 115 cp ${src}/worker/src/tools.json ${src}/worker/src/shared-tools.json $out/worker/src/ 116 ''; 117in 118{ 119 inherit site tests worker;
The static site alone, ready for any static host: the REPL and song browser at /, the sample pack under /jev-samples/ and /_astro/. No MCP: the production Worker has no hub, so the page's probe finds none.
124 static = runCommand "jevstrudel-static" { } "ln -s ${site}/share/jevstrudel/website/dist $out";
The stdio MCP server in .mcp.json: a proxy with no state, forwarding
each message to the MCP hub in the jevstrudel Worker under wrangler dev
or, switched with its use_environment tool, to the public site's hosted
MCP as an OAuth client (tools/mcp/), so neither side has to restart for
the other. It needs only Node and the MCP SDK (tools/mcp/package-lock.json,
each package fetched by its integrity hash, apart from the site's pnpm
closure), not the built site.