import { createHash } from "node:crypto"; import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs"; import { homedir, hostname as hostHostname, arch as hostArch, platform as hostPlatform } from "node:os"; import { dirname, join } from "node:path"; import { errMessage, shellQuote } from "agent-relay-sdk"; import type { CallmuxConfig } from "callmux"; import { agentRelayHome } from "./config"; // #1331 — A Callmux descriptor is not portable just because it is JSON. The old // shared config named launch scripts and package installs from the authoring host. // Keep the source registry declarative, then materialize the small set of host-owned // executables here under AGENT_RELAY_HOME. Nothing below points at a developer repo // or ~/.npm-global, and all package/git inputs are immutable pins. const PACKAGES = { tokenlean: { pkg: "tokenlean", version: "0.50.11", bin: "tl-mcp" }, reasoning: { pkg: "@modelcontextprotocol/server-sequential-thinking", version: "2026.7.4", bin: "mcp-server-sequential-thinking" }, searxng: { pkg: "mcp-searxng", version: "1.11.0", bin: "mcp-searxng" }, } as const; // This is the official github/github-mcp-server release used by macmini's // working shared callmux. Do not replace it with the unrelated npm package // that happens to publish the same binary name (#1331). const GITHUB_MCP_VERSION = "1.0.3"; const GITHUB_MCP_RELEASES: Record = { "darwin-arm64": { asset: "github-mcp-server_Darwin_arm64.tar.gz", sha256: "c7e910537553d59e2e9a7b07bb7da856985c0c0a63c5ecbee4633807d42e420c" }, "darwin-x64": { asset: "github-mcp-server_Darwin_x86_64.tar.gz", sha256: "8c4f37ac37f8d05792615f6091f9a42ac433ff503709da17707a565016aacbc9" }, "linux-arm64": { asset: "github-mcp-server_Linux_arm64.tar.gz", sha256: "e53535f3af758f75b0dbe304d98dd7b26937f68e7e21b722665db7d9d53cc263" }, "linux-x64": { asset: "github-mcp-server_Linux_x86_64.tar.gz", sha256: "6da1c42167306357587cecb2f937bd8fd1842d3e81a44eb57e85f386c0a8bd85" }, }; // qmd runs on the macOS host named `macmini`; macmini2 is the Linux // orchestrator host and has no /opt/homebrew/bin/qmd. Host keys are public and // pinning the live ed25519 key makes first provisioning strict without TOFU. const QMD_HOST = "macmini"; const QMD_USER = "admin"; const QMD_ED25519_KEY = "AAAAC3NzaC1lZDI1NTE5AAAAICJJ9IngZt0zJFyYz9L0v2qPdmCW2raIw70oNQOnObg5"; const SEARXNG_SSH_HOST = "macmini2"; const SEARXNG_SSH_USER = "edimuj"; const SEARXNG_ED25519_KEY = "AAAAC3NzaC1lZDI1NTE5AAAAIBDgu73ZWFejv7U8aBsqCMA9oJo6tcVQvSaFYYfEeIbN"; export const PORTABLE_SHARED_MCP_SERVER_NAMES = ["tokenlean", "github", "reasoning", "searxng", "vent", "qmd"] as const; const VENT_REPO = "https://github.com/edimuj/mcp-vent.git"; const VENT_REF = "da604f7776de8ff115ea5967bca57d4e665b8e5e"; export interface PortableMcpProvisioningDeps { existsSync(path: string): boolean; mkdirSync(path: string, options?: { recursive?: boolean }): void; writeFileSync(path: string, content: string, options?: { mode?: number }): void; chmodSync(path: string, mode: number): void; statSync(path: string): { mode: number }; renameSync(oldPath: string, newPath: string): void; sha256(path: string): string; platform(): string; arch(): string; hostname(): string; run(command: string, args: string[]): { exitCode: number; stdout: string; stderr: string }; } export interface PortableMcpProvisioningResult { servers: CallmuxConfig["servers"]; failures: Array<{ name: string; reason: string }>; } interface PortableMcpProvisioningOptions { home?: string; env?: Record; /** Registry-selected server names. Absent means bootstrap the complete core surface. */ names?: Iterable; deps?: PortableMcpProvisioningDeps; } export function provisionPortableSharedMcpServers(opts: PortableMcpProvisioningOptions = {}): PortableMcpProvisioningResult { const home = opts.home ?? agentRelayHome(); const env = opts.env ?? process.env; const deps = opts.deps ?? defaultDeps(); const root = join(home, "mcp"); const requested = opts.names ? new Set(opts.names) : undefined; const servers: CallmuxConfig["servers"] = {}; const failures: PortableMcpProvisioningResult["failures"] = []; const add = (name: string, build: () => CallmuxConfig["servers"][string]) => { if (requested && !requested.has(name)) return; try { servers[name] = build(); } catch (error) { failures.push({ name, reason: errMessage(error) }); } }; add("tokenlean", () => ({ command: ensureNpmBinary("tokenlean", PACKAGES.tokenlean, root, deps), prefix: "", alwaysLoad: ["tl_symbols", "tl_snippet", "tl_pack", "tl_run", "tl_guard", "tl_lookup"], requireSessionCwd: true, })); add("github", () => ({ command: writeGithubLauncher(root, ensureOfficialGithubBinary(root, deps), deps), prefix: "gh", tools: ["issue_read", "issue_write", "list_issues", "add_issue_comment", "search_issues", "search_code", "get_file_contents", "sub_issue_write"], cachePolicy: { allowTools: ["issue_read", "list_issues", "search_issues", "search_code", "get_file_contents"] }, })); add("reasoning", () => ({ command: ensureNpmBinary("reasoning", PACKAGES.reasoning, root, deps) })); add("searxng", () => searxngDescriptor(root, env, deps)); add("vent", () => ({ command: writeVentLauncher(root, ensureVent(root, env, deps), deps), env: { VENT_REPO: env.AGENT_RELAY_VENT_REPO ?? "edimuj/exelerus-agent-vent", VENT_HOST: env.AGENT_RELAY_VENT_HOST ?? env.HOSTNAME ?? "agent-relay", }, })); add("qmd", () => qmdDescriptor(root, env, deps)); return { servers, failures }; } function ensureOfficialGithubBinary(root: string, deps: PortableMcpProvisioningDeps): string { const release = GITHUB_MCP_RELEASES[`${deps.platform()}-${deps.arch()}`]; if (!release) throw new Error(`official GitHub MCP has no pinned release for ${deps.platform()}-${deps.arch()}`); const installRoot = join(root, "packages", "github", GITHUB_MCP_VERSION, `${deps.platform()}-${deps.arch()}`); const binary = join(installRoot, "github-mcp-server"); if (deps.existsSync(binary)) return binary; const staging = `${installRoot}.staging-${process.pid}`; const archive = join(staging, release.asset); deps.mkdirSync(staging, { recursive: true }); const url = `https://github.com/github/github-mcp-server/releases/download/v${GITHUB_MCP_VERSION}/${release.asset}`; runOrThrow(deps, "curl", ["--fail", "--silent", "--show-error", "--location", "--output", archive, url], "download official GitHub MCP"); const digest = deps.sha256(archive); if (digest !== release.sha256) throw new Error(`official GitHub MCP checksum mismatch: expected ${release.sha256}, got ${digest}`); runOrThrow(deps, "tar", ["-xzf", archive, "-C", staging], "extract official GitHub MCP"); if (!deps.existsSync(join(staging, "github-mcp-server"))) throw new Error("official GitHub MCP archive has no github-mcp-server binary"); deps.chmodSync(join(staging, "github-mcp-server"), 0o700); deps.mkdirSync(dirname(installRoot), { recursive: true }); deps.renameSync(staging, installRoot); if (!deps.existsSync(binary)) throw new Error("installed official GitHub MCP binary is missing"); return binary; } function ensureNpmBinary(name: string, spec: { pkg: string; version: string; bin: string }, root: string, deps: PortableMcpProvisioningDeps): string { const installRoot = join(root, "packages", name, spec.version); const binary = join(installRoot, "node_modules", ".bin", spec.bin); if (!deps.existsSync(binary)) { deps.mkdirSync(installRoot, { recursive: true }); runOrThrow(deps, "npm", ["install", "--prefix", installRoot, "--omit=dev", "--no-package-lock", `${spec.pkg}@${spec.version}`], `install ${spec.pkg}@${spec.version}`); } if (!deps.existsSync(binary)) throw new Error(`installed ${spec.pkg}@${spec.version}, but ${spec.bin} is missing`); return binary; } function ensureVent(root: string, env: Record, deps: PortableMcpProvisioningDeps): string { const ref = env.AGENT_RELAY_VENT_REF ?? VENT_REF; if (!/^[a-f0-9]{40}$/i.test(ref)) throw new Error("AGENT_RELAY_VENT_REF must be a full immutable git SHA"); const repo = env.AGENT_RELAY_VENT_REPO_URL ?? VENT_REPO; if (!/^https:\/\//.test(repo)) throw new Error("AGENT_RELAY_VENT_REPO_URL must be an https git URL"); const target = join(root, "checkouts", "mcp-vent", ref); const entry = join(target, "dist", "index.js"); if (deps.existsSync(entry)) return entry; const staging = `${target}.staging-${process.pid}`; if (deps.existsSync(target)) throw new Error(`incomplete mcp-vent checkout at ${target}; remove it only after inspection`); runOrThrow(deps, "git", ["clone", "--no-checkout", repo, staging], "clone mcp-vent"); runOrThrow(deps, "git", ["-C", staging, "checkout", "--detach", ref], "checkout mcp-vent pin"); runOrThrow(deps, "npm", ["install", "--prefix", staging, "--include=dev", "--no-package-lock"], "install mcp-vent dependencies"); runOrThrow(deps, "npm", ["run", "build", "--prefix", staging], "build mcp-vent"); deps.mkdirSync(dirname(target), { recursive: true }); deps.renameSync(staging, target); if (!deps.existsSync(entry)) throw new Error("built mcp-vent checkout has no dist/index.js"); return entry; } function writeGithubLauncher(root: string, binary: string, deps: PortableMcpProvisioningDeps): string { return writeLauncher(join(root, "launchers", "github"), `#!/usr/bin/env bash set -euo pipefail if [ -z "\${GITHUB_PERSONAL_ACCESS_TOKEN:-}" ] && command -v gh >/dev/null 2>&1; then GITHUB_PERSONAL_ACCESS_TOKEN="$(gh auth token 2>/dev/null || true)" export GITHUB_PERSONAL_ACCESS_TOKEN fi if [ -z "\${GITHUB_PERSONAL_ACCESS_TOKEN:-}" ]; then echo "agent-relay github MCP: configure gh auth or GITHUB_PERSONAL_ACCESS_TOKEN on this host" >&2 exit 1 fi exec ${shellQuote(binary)} stdio `, deps); } function writeVentLauncher(root: string, entry: string, deps: PortableMcpProvisioningDeps): string { return writeLauncher(join(root, "launchers", "vent"), `#!/usr/bin/env bash set -euo pipefail if [ -z "\${GITHUB_TOKEN:-}" ] && [ -z "\${VENT_GITHUB_TOKEN:-}" ] && command -v gh >/dev/null 2>&1; then GITHUB_TOKEN="$(gh auth token 2>/dev/null || true)" export GITHUB_TOKEN fi if [ -z "\${GITHUB_TOKEN:-}" ] && [ -z "\${VENT_GITHUB_TOKEN:-}" ]; then echo "agent-relay vent MCP: configure gh auth or GITHUB_TOKEN on this host" >&2 exit 1 fi exec node ${shellQuote(entry)} `, deps); } function writeLauncher(path: string, content: string, deps: PortableMcpProvisioningDeps): string { deps.mkdirSync(dirname(path), { recursive: true }); deps.writeFileSync(path, content, { mode: 0o700 }); deps.chmodSync(path, 0o700); return path; } function searxngDescriptor(root: string, env: Record, deps: PortableMcpProvisioningDeps): CallmuxConfig["servers"][string] { const directUrl = env.AGENT_RELAY_SEARXNG_URL?.trim(); const sshHost = env.AGENT_RELAY_SEARXNG_SSH_HOST?.trim() || SEARXNG_SSH_HOST; const binary = ensureNpmBinary("searxng", PACKAGES.searxng, root, deps); if (directUrl || isLocalHost(deps.hostname(), sshHost)) { return { command: binary, env: { SEARXNG_URL: directUrl || "http://127.0.0.1:8888" }, cachePolicy: { allowTools: ["searxng_web_search"] }, }; } const identityFile = resolveSshIdentity("searxng", sshHost, "AGENT_RELAY_SEARXNG_IDENTITY_FILE", env, deps, "~/.ssh/id_ed25519"); const configuredHostKey = env.AGENT_RELAY_SEARXNG_HOST_KEY?.trim(); const hostKey = configuredHostKey || (sshHost === SEARXNG_SSH_HOST ? `ssh-ed25519 ${SEARXNG_ED25519_KEY}` : undefined); if (!hostKey) throw new Error(`searxng requires AGENT_RELAY_SEARXNG_HOST_KEY for SSH host ${sshHost}`); const knownHosts = writePinnedKnownHosts(root, "searxng", sshHost, hostKey, deps); const user = env.AGENT_RELAY_SEARXNG_SSH_USER?.trim() || SEARXNG_SSH_USER; const sshArgs = sharedSshArgs(knownHosts, identityFile).concat([ "-o", "ExitOnForwardFailure=yes", "-N", ]); const launcher = writeSearxngTunnelLauncher(root, binary, sshArgs, user, sshHost, deps); return { command: launcher, cachePolicy: { allowTools: ["searxng_web_search"] }, }; } function writeSearxngTunnelLauncher(root: string, binary: string, sshArgs: string[], user: string, host: string, deps: PortableMcpProvisioningDeps): string { const allocatePort = `const net = require("node:net"); const server = net.createServer(); server.on("error", (error) => { console.error(error.message); process.exit(1); }); server.listen({ host: "127.0.0.1", port: 0, exclusive: true }, () => { const address = server.address(); if (!address || typeof address === "string") process.exit(1); process.stdout.write(String(address.port)); server.close(); });`; return writeLauncher(join(root, "launchers", "searxng"), `#!/usr/bin/env bash set -u tunnel_pid="" mcp_pid="" tunnel_target=${shellQuote(`${user}@${host}`)} cleanup() { [ -z "$mcp_pid" ] || kill "$mcp_pid" 2>/dev/null || true [ -z "$tunnel_pid" ] || kill "$tunnel_pid" 2>/dev/null || true [ -z "$mcp_pid" ] || wait "$mcp_pid" 2>/dev/null || true [ -z "$tunnel_pid" ] || wait "$tunnel_pid" 2>/dev/null || true } trap cleanup EXIT INT TERM HUP exec 3<&0 local_port="$(node -e ${shellQuote(allocatePort)})" || { echo "agent-relay searxng could not allocate a local SSH-forward port" >&2 exit 1 } case "$local_port" in ""|*[!0-9]*) echo "agent-relay searxng received an invalid local SSH-forward port" >&2; exit 1 ;; esac export SEARXNG_URL="http://127.0.0.1:\${local_port}" ssh ${sshArgs.map(shellQuote).join(" ")} '-L' "127.0.0.1:\${local_port}:127.0.0.1:8888" '-l' ${shellQuote(user)} ${shellQuote(host)} & tunnel_pid=$! sleep 0.25 if ! kill -0 "$tunnel_pid" 2>/dev/null; then wait "$tunnel_pid" status=$? [ "$status" -ne 0 ] || status=1 echo "agent-relay searxng SSH tunnel to \${tunnel_target} failed (exit $status); Callmux will reconnect with backoff" >&2 exit "$status" fi ${shellQuote(binary)} <&3 & mcp_pid=$! while kill -0 "$tunnel_pid" 2>/dev/null && kill -0 "$mcp_pid" 2>/dev/null; do sleep 1 done if ! kill -0 "$tunnel_pid" 2>/dev/null; then wait "$tunnel_pid" status=$? [ "$status" -ne 0 ] || status=1 echo "agent-relay searxng SSH tunnel to \${tunnel_target} closed (exit $status); Callmux will reconnect with backoff" >&2 exit "$status" fi wait "$mcp_pid" exit $? `, deps); } function qmdDescriptor(root: string, env: Record, deps: PortableMcpProvisioningDeps): CallmuxConfig["servers"][string] { const host = env.AGENT_RELAY_QMD_HOST ?? QMD_HOST; const identityFile = resolveSshIdentity("qmd", host, "AGENT_RELAY_QMD_IDENTITY_FILE", env, deps); const pinnedHostKey = env.AGENT_RELAY_QMD_KNOWN_HOSTS; const hostKeyLine = pinnedHostKey?.trim() || `${host} ssh-ed25519 ${QMD_ED25519_KEY}`; const knownHosts = writePinnedKnownHosts(root, "qmd", host, hostKeyLine, deps); const user = env.AGENT_RELAY_QMD_USER ?? QMD_USER; return { command: "ssh", args: ["-T", ...sharedSshArgs(knownHosts, identityFile), "-l", user, host, "/opt/homebrew/bin/qmd", "mcp"], cachePolicy: { allowTools: ["query", "get", "multi_get", "status"] }, }; } function resolveSshIdentity(label: string, host: string, envName: string, env: Record, deps: PortableMcpProvisioningDeps, defaultIdentity?: string): string { const configured = env[envName] ?? defaultIdentity; if (configured) { const path = expandHome(configured, env.HOME ?? homedir()); if (!deps.existsSync(path)) throw new Error(`${label} identity file is missing: ${path}`); return path; } const sshConfig = deps.run("ssh", ["-G", host]); if (sshConfig.exitCode !== 0) throw new Error(`cannot resolve ${label} SSH config for ${host}: ${sshConfig.stderr.trim().slice(0, 300)}`); const identities = sshConfig.stdout.split(/\r?\n/) .filter((line) => line.startsWith("identityfile ")) .map((line) => expandHome(line.slice("identityfile ".length).trim(), env.HOME ?? homedir())); const identity = identities.find((path) => deps.existsSync(path)); if (!identity) throw new Error(`${label} requires an existing SSH IdentityFile for ${host} (or ${envName})`); return identity; } function sharedSshArgs(knownHosts: string, identityFile: string): string[] { return [ "-o", "BatchMode=yes", "-o", "StrictHostKeyChecking=yes", "-o", `UserKnownHostsFile=${knownHosts}`, "-o", `IdentityFile=${identityFile}`, "-o", "IdentitiesOnly=yes", "-o", "ServerAliveInterval=30", "-o", "ServerAliveCountMax=3", ]; } function writePinnedKnownHosts(root: string, label: string, host: string, hostKey: string, deps: PortableMcpProvisioningDeps): string { const knownHosts = join(root, label, "known_hosts"); const trimmed = hostKey.trim(); const line = trimmed.startsWith(`${host} `) || trimmed.startsWith(`[${host}]`) ? trimmed : trimmed.startsWith("ssh-") ? `${host} ${trimmed}` : `${host} ssh-ed25519 ${trimmed}`; deps.mkdirSync(dirname(knownHosts), { recursive: true }); deps.writeFileSync(knownHosts, `${line}\n`, { mode: 0o600 }); deps.chmodSync(knownHosts, 0o600); if ((deps.statSync(knownHosts).mode & 0o077) !== 0) throw new Error(`${label} known_hosts must not be group/world readable`); return knownHosts; } function isLocalHost(localHostname: string, targetHostname: string): boolean { const local = localHostname.toLowerCase().split(".")[0]; const target = targetHostname.toLowerCase().split(".")[0]; return Boolean(local && local === target); } function expandHome(path: string, home: string): string { return path === "~" ? home : path.startsWith("~/") ? join(home, path.slice(2)) : path; } function runOrThrow(deps: PortableMcpProvisioningDeps, command: string, args: string[], action: string): void { const result = deps.run(command, args); if (result.exitCode !== 0) throw new Error(`${action} failed (${result.exitCode}): ${result.stderr.trim().slice(0, 300)}`); } function defaultDeps(): PortableMcpProvisioningDeps { return { existsSync, mkdirSync, writeFileSync, chmodSync, statSync, renameSync, sha256: (path) => createHash("sha256").update(readFileSync(path)).digest("hex"), platform: hostPlatform, arch: hostArch, hostname: hostHostname, run(command, args) { const result = Bun.spawnSync([command, ...args], { stdout: "pipe", stderr: "pipe" }); return { exitCode: result.exitCode, stdout: result.stdout.toString(), stderr: result.stderr.toString() }; }, }; }