From e67ade47c8bc5e6850911b180b4c2de0f0671c7e Mon Sep 17 00:00:00 2001 From: Parfii-bot Date: Wed, 22 Apr 2026 15:52:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(v0.18):=20kei-mcp-server=20single-binary?= =?UTF-8?q?=20compile=20=E2=80=94=205-platform=20via=20bun?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 of exobrain architecture. Ships TS MCP server as a static binary so users on machines without Node can run KeiSeiKit (USB / flashdrive / air-gapped scenarios). .github/workflows/release.yml (+62 LOC) — new build-mcp-binary job: - 5-target matrix: darwin arm64/x64, linux arm64/x64, windows x64 - bun build --compile, linux arm64 continue-on-error (ARM runners less reliable) - Artifact kei-mcp-server--[.exe] + sha256 - release job now needs [build-release, build-mcp-binary] install/lib-rust.sh (+50 LOC) — have_prebuilt_mcp_server() + report_mcp_server_binary_status(); KEI_SKIP_MCP_BUILD=1 env flag skips bun/npm install when a prebuilt binary is present. File 165 LOC (<200 limit). _ts_packages/packages/mcp-server/package.json — scripts.build:native + 5 per-target aliases (macos-arm, macos-x64, linux-x64, linux-arm, win-x64) for local dev. _ts_packages/packages/mcp-server/BUILD.md (NEW, 52 LOC) — local compile guide per platform + Gatekeeper/code-sign notes + cites bun docs [VERIFIED: https://bun.sh/docs/bundler/executables]. README.md pre-built-binaries section gains 'MCP server binary' subsection (download, chmod +x, xattr -d com.apple.quarantine for macOS, UAC note for Windows). CHANGELOG.md [Unreleased] bullet added. Output size: ~90 MB per binary (bundled bun runtime). Acceptable trade for zero-dep USB distribution. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/release.yml | 83 ++++++++++++++++++- CHANGELOG.md | 1 + README.md | 26 ++++++ _ts_packages/packages/mcp-server/BUILD.md | 52 ++++++++++++ _ts_packages/packages/mcp-server/package.json | 8 +- install/lib-rust.sh | 51 ++++++++++++ 6 files changed, 217 insertions(+), 4 deletions(-) create mode 100644 _ts_packages/packages/mcp-server/BUILD.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1460be0..d4a3dbd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -98,9 +98,77 @@ jobs: keisei-${{ matrix.target }}.tar.gz.sha256 if-no-files-found: error + # v0.18 Phase 1 (exobrain): compile @keisei/mcp-server to a single static + # binary for 5 platforms via `bun build --compile`. Runs in parallel with + # build-release; the release job below `needs:` both. Linux arm64 is kept + # `continue-on-error` because the ubuntu arm runner pool is newer and + # occasionally flaky — a missing linux-arm64 asset must NOT block release. + build-mcp-binary: + name: Build mcp-server ${{ matrix.target.platform }}-${{ matrix.target.arch }} + runs-on: ${{ matrix.target.runner }} + continue-on-error: ${{ matrix.target.arch == 'arm64' && matrix.target.platform == 'linux' }} + strategy: + fail-fast: false + matrix: + target: + - { platform: linux, arch: x64, runner: ubuntu-latest, bun_target: bun-linux-x64, ext: '' } + - { platform: linux, arch: arm64, runner: ubuntu-24.04-arm, bun_target: bun-linux-arm64, ext: '' } + - { platform: darwin, arch: x64, runner: macos-13, bun_target: bun-darwin-x64, ext: '' } + - { platform: darwin, arch: arm64, runner: macos-latest, bun_target: bun-darwin-arm64, ext: '' } + - { platform: windows, arch: x64, runner: windows-latest, bun_target: bun-windows-x64, ext: '.exe' } + steps: + - uses: actions/checkout@v4 + + - name: Install bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + + - name: Install mcp-server deps + shell: bash + working-directory: _ts_packages/packages/mcp-server + run: bun install --frozen-lockfile || bun install + + - name: Compile single-binary + shell: bash + env: + BIN_NAME: kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }} + run: | + set -euo pipefail + mkdir -p dist + bun build \ + --compile \ + --target=${{ matrix.target.bun_target }} \ + _ts_packages/packages/mcp-server/src/index.ts \ + --outfile "dist/${BIN_NAME}" + ls -la "dist/${BIN_NAME}" + + - name: Compute sha256 + shell: bash + env: + BIN_NAME: kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }} + run: | + set -euo pipefail + cd dist + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "${BIN_NAME}" > "${BIN_NAME}.sha256" + else + shasum -a 256 "${BIN_NAME}" > "${BIN_NAME}.sha256" + fi + cat "${BIN_NAME}.sha256" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }} + path: | + dist/kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }} + dist/kei-mcp-server-${{ matrix.target.platform }}-${{ matrix.target.arch }}${{ matrix.target.ext }}.sha256 + if-no-files-found: error + release: name: Publish GitHub Release - needs: build-release + needs: [build-release, build-mcp-binary] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -126,8 +194,16 @@ jobs: run: | set -euo pipefail mkdir -p release-assets - find dist -type f \( -name '*.tar.gz' -o -name '*.sha256' \) \ - -exec mv {} release-assets/ \; + # Rust tarballs + sha256 sums from build-release matrix. + # MCP-server bare binaries (+ .exe on windows) + sha256 sums from + # build-mcp-binary matrix. Bare binaries need a stable name to stay + # USB-drive-droppable, so no archive — we ship them raw alongside + # the tarballs. + find dist -type f \( \ + -name '*.tar.gz' \ + -o -name '*.sha256' \ + -o -name 'kei-mcp-server-*' \ + \) -exec mv {} release-assets/ \; ls -la release-assets - name: Generate release notes (kei-changelog) @@ -163,6 +239,7 @@ jobs: files: | release-assets/*.tar.gz release-assets/*.sha256 + release-assets/kei-mcp-server-* fail_on_unmatched_files: false npm-publish: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ed38cd..ed90fc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ _primitives/_rust/target/release/kei-changelog \ - Placeholder: CHANGELOG.md generation wired through `kei-changelog` (this file). - Placeholder: `.github/workflows/release.yml` — tag-driven multi-platform release. - Placeholder: pre-built-binary install path in `install.sh` (`KEI_SKIP_RUST_BUILD=1`). +- added: `kei-mcp-server` single-binary compile for 5 platforms (linux/darwin/windows × x64/arm64 where available) via `bun build --compile` — v0.18 Phase 1 of the exobrain distribution architecture. Ships as bare binaries + `.sha256` sums on every GitHub release; `install.sh` detects a dropped binary at `_primitives/_rust/target/release/kei-mcp-server--` and skips bun/npm build. Opt-out via `KEI_SKIP_MCP_BUILD=1`. See `_ts_packages/packages/mcp-server/BUILD.md`. ### Changed - Placeholder: plugin / block format refresh targeted for v0.16.0. diff --git a/README.md b/README.md index 64c0f65..2dd9813 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,32 @@ Profile resolution lives in `_primitives/MANIFEST.toml` — one `[primitive. **Re-install disclaimer:** `install.sh` is idempotent for clean state but **overwrites kit-owned `_blocks/`, `_primitives/`, `_bridges/`, `_templates/`, `_assembler/`, `hooks/`, and `skills/` on re-run** — local modifications under those directories are backed up to `.bak-TIMESTAMP/` (or, for shared hook files, to `.bak-TIMESTAMP`). User-owned `_manifests/*.toml` are never overwritten. +### MCP server binary (zero-install path, v0.18) + +From v0.18 each GitHub release ships a **single static binary** of the `@keisei/mcp-server` package for five platforms — no Node, no `npm install`. Drop the binary anywhere (USB stick, S3 bucket, Downloads folder) and run it. This is Phase 1 of the "exobrain" distribution goal: any MCP-capable client can mount KeiSeiKit from read-only media. + +| Platform | Asset name | +|---|---| +| Linux x64 | `kei-mcp-server-linux-x64` | +| Linux arm64 | `kei-mcp-server-linux-arm64` | +| macOS x64 | `kei-mcp-server-darwin-x64` | +| macOS arm64 | `kei-mcp-server-darwin-arm64` | +| Windows x64 | `kei-mcp-server-windows-x64.exe` | + +```bash +# Linux / macOS +curl -L -o kei-mcp-server \ + https://github.com//KeiSeiKit/releases/latest/download/kei-mcp-server-darwin-arm64 +chmod +x kei-mcp-server +# macOS only — clear Gatekeeper quarantine on the downloaded binary: +xattr -d com.apple.quarantine ./kei-mcp-server 2>/dev/null || true +./kei-mcp-server --stdio +``` + +Every asset has a matching `.sha256` for integrity verification. Build details and local cross-compile recipes: `_ts_packages/packages/mcp-server/BUILD.md`. + +If you drop the binary at `~/.claude/agents/_primitives/_rust/target/release/kei-mcp-server--[.exe]` (the same layout `install.sh` uses for Rust primitives), re-running `install.sh` will detect it and skip any bun/npm build step. Set `KEI_SKIP_MCP_BUILD=1` to force-skip that step regardless of detection. + ## Runtime hook controls Every kit-shipped hook (v0.14.2+) honours two env vars so you can silence noise or isolate a failure without editing `~/.claude/settings.json`: diff --git a/_ts_packages/packages/mcp-server/BUILD.md b/_ts_packages/packages/mcp-server/BUILD.md new file mode 100644 index 0000000..955164f --- /dev/null +++ b/_ts_packages/packages/mcp-server/BUILD.md @@ -0,0 +1,52 @@ +# Building a single-binary `kei-mcp-server` + +> KeiSeiKit v0.18 Phase 1 (exobrain) — ship the MCP server as a portable +> static binary so any machine without Node can run it off a USB drive. + +## Tooling + +Compile via **bun** (`bun build --compile`). Bundles the Bun runtime + JS +into one static executable — no Node, no `node_modules/` at runtime. +Requires bun `>= 1.0`. Docs (target list + flags): +[VERIFIED: https://bun.sh/docs/bundler/executables] + +## Supported targets + +| Platform | Arch | `--target=` | Output name | +|----------|-------|------------------------|---------------------------------------| +| Linux | x64 | `bun-linux-x64` | `kei-mcp-server-linux-x64` | +| Linux | arm64 | `bun-linux-arm64` | `kei-mcp-server-linux-arm64` | +| macOS | x64 | `bun-darwin-x64` | `kei-mcp-server-darwin-x64` | +| macOS | arm64 | `bun-darwin-arm64` | `kei-mcp-server-darwin-arm64` | +| Windows | x64 | `bun-windows-x64` | `kei-mcp-server-windows-x64.exe` | + +## Local build + +```bash +cd _ts_packages/packages/mcp-server +bun install +bun run build:native # host-native +bun run build:native:darwin-arm64 # explicit cross-target +``` + +Output lands in `dist/`. Size ~85–95 MB per binary (bundled runtime). + +## Release build (CI) + +`.github/workflows/release.yml` → job `build-mcp-binary` runs the 5-target +matrix on tag push (`v*`) and attaches binaries + `.sha256` sums to the +GitHub release. Runtime requirement: **none** (static). + +## Troubleshooting + +- **macOS Gatekeeper (“cannot be opened because Apple cannot check it for + malicious software”)** — remove the quarantine attribute: + `xattr -d com.apple.quarantine ./kei-mcp-server-darwin-arm64` +- **Windows SmartScreen / AV flags** — not signed; right-click → + Properties → Unblock, or add an AV exclusion for the binary path. +- **Missing symbol at startup** — usually a native-only dep that resolved + at runtime on Node but cannot be bundled. Re-run `bun install`, then + `bun build --compile ... --smol` to surface the resolution error. +- **`.js` ESM imports fail** — the mcp-server source imports via `.js` + suffix (ESM canonical). Bun resolves these from the sibling `.ts` + file automatically; no `tsc` pre-step needed. diff --git a/_ts_packages/packages/mcp-server/package.json b/_ts_packages/packages/mcp-server/package.json index 04f6adc..ec286bb 100644 --- a/_ts_packages/packages/mcp-server/package.json +++ b/_ts_packages/packages/mcp-server/package.json @@ -20,7 +20,13 @@ "scripts": { "build": "tsc -b", "test": "vitest run", - "dev": "tsx src/index.ts --stdio" + "dev": "tsx src/index.ts --stdio", + "build:native": "bun build --compile src/index.ts --outfile dist/kei-mcp-server", + "build:native:linux-x64": "bun build --compile --target=bun-linux-x64 src/index.ts --outfile dist/kei-mcp-server-linux-x64", + "build:native:linux-arm64": "bun build --compile --target=bun-linux-arm64 src/index.ts --outfile dist/kei-mcp-server-linux-arm64", + "build:native:darwin-x64": "bun build --compile --target=bun-darwin-x64 src/index.ts --outfile dist/kei-mcp-server-darwin-x64", + "build:native:darwin-arm64": "bun build --compile --target=bun-darwin-arm64 src/index.ts --outfile dist/kei-mcp-server-darwin-arm64", + "build:native:windows-x64": "bun build --compile --target=bun-windows-x64 src/index.ts --outfile dist/kei-mcp-server-windows-x64.exe" }, "dependencies": { "@modelcontextprotocol/sdk": "^1.0.0", diff --git a/install/lib-rust.sh b/install/lib-rust.sh index fefc5cf..b8f2ffe 100644 --- a/install/lib-rust.sh +++ b/install/lib-rust.sh @@ -13,6 +13,10 @@ # Requires: say / warn from lib-log.sh. # Reads globals: $AGENTS_DIR, $KIT_DIR. # Honours env: $KEI_SKIP_RUST_BUILD (1 = force-skip cargo build). +# Honours env: $KEI_SKIP_MCP_BUILD (1 = force-skip mcp-server bun compile; +# also set automatically when a prebuilt +# single-binary is detected via +# have_prebuilt_mcp_server). # Echo rust crates currently installed (by scanning .installed + MANIFEST). installed_rust_crates() { @@ -112,3 +116,50 @@ regenerate_rust_workspace() { done <<< "$members_nl" say " $built / $n Rust primitive binaries available" } + +# --- mcp-server single-binary detection (v0.18 Phase 1 / exobrain) ---------- +# Analog of have_prebuilt_binaries for the TS @keisei/mcp-server package, +# which is distributable as a `bun build --compile` single binary. +# +# Contract: returns 0 iff a matching pre-built binary is present at +# $AGENTS_DIR/_primitives/_rust/target/release/kei-mcp-server--[.exe] +# (reusing the target/release directory so install.sh only has to lay down +# one staging dir from the release tarball). Release workflow puts the bare +# binary there; install.sh can then skip any bun/npm install entirely. +# +# Host classification: linux | darwin | windows vs x64 | arm64. +# Unsupported combos (e.g. freebsd, x86) return 1 — no attempt made. +have_prebuilt_mcp_server() { + local target_dir="$AGENTS_DIR/_primitives/_rust/target/release" + local uname_s uname_m os arch ext bin + uname_s="$(uname -s 2>/dev/null || echo unknown)" + uname_m="$(uname -m 2>/dev/null || echo unknown)" + case "$uname_s" in + Linux) os=linux; ext='' ;; + Darwin) os=darwin; ext='' ;; + MINGW*|MSYS*|CYGWIN*) os=windows; ext='.exe' ;; + *) return 1 ;; + esac + case "$uname_m" in + x86_64|amd64) arch=x64 ;; + arm64|aarch64) arch=arm64 ;; + *) return 1 ;; + esac + bin="$target_dir/kei-mcp-server-${os}-${arch}${ext}" + [ -x "$bin" ] || return 1 + echo "$bin" +} + +# Consult KEI_SKIP_MCP_BUILD + pre-built detection; emit a one-line status. +# Intentionally does NOT run bun/npm — install.sh has no TS build step today; +# this is the hook to grow into one later without touching the call sites. +report_mcp_server_binary_status() { + if [ "${KEI_SKIP_MCP_BUILD:-0}" = "1" ]; then + say " KEI_SKIP_MCP_BUILD=1 — skipping mcp-server single-binary build" + return 0 + fi + local bin + if bin="$(have_prebuilt_mcp_server)"; then + say " pre-built mcp-server binary detected: $bin" + fi +}