feat(v0.18): kei-mcp-server single-binary compile — 5-platform via bun

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-<os>-<arch>[.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) <noreply@anthropic.com>
This commit is contained in:
Parfii-bot 2026-04-22 15:52:36 +08:00
parent bcc199a62e
commit e67ade47c8
6 changed files with 217 additions and 4 deletions

View file

@ -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:

View file

@ -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-<os>-<arch>` 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.

View file

@ -112,6 +112,32 @@ Profile resolution lives in `_primitives/MANIFEST.toml` — one `[primitive.<nam
> **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 `<dir>.bak-TIMESTAMP/` (or, for shared hook files, to `<file>.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/<your-fork>/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 `<name>.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-<os>-<arch>[.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`:

View file

@ -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 ~8595 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.

View file

@ -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",

View file

@ -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-<os>-<arch>[.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
}