jevsnes.git / tools / mcp / launch.sh
1#!/usr/bin/env bash
2# What `.mcp.json` starts: the stdio MCP proxy, in well under a second, with
3# no build and no devshell in the way. See ./README.md.
4#
5# Bash builtins only - no dirname, sed or jq. A client may spawn this with a
6# bare environment (NixOS gives a profile-less process PATH=/usr/bin:/bin,
7# where the only programs are env, bash and sh), and an external command
8# that is not found there turned the repo root into "" (2026-09-21).
9set -euo pipefail
10
11here="${BASH_SOURCE[0]%/*}"
12[[ "$here" == "${BASH_SOURCE[0]}" ]] && here=.
13REPO_ROOT="$(cd -P "$here/../.." && pwd -P)"
14BIN="$REPO_ROOT/run/bin/native"
15ENV_FILE="$REPO_ROOT/run/bin/native.env"
16
17# No copy yet: say so as a JSON-RPC error to the client's `initialize`, which
18# the client shows, as well as on stderr, which it logs - then exit. Never
19# build here: a cold build is minutes, the client gives up after 30 s.
20# The id is a JSON number or string; anything else is answered with null.
21refuse() {  # $1 = why (no double quotes or backslashes: it is sent as-is)
22    local why="$1 Run tools/mcp/install.sh in ~/jevsnes, then reconnect." line id=null
23    printf 'jev mcp: %s\n' "$why" >&2
24    if IFS= read -r -t 5 line; then
25        if [[ "$line" =~ \"id\"[[:space:]]*:[[:space:]]*(-?[0-9]+|\"[^\"\\]*\") ]]; then
26            id="${BASH_REMATCH[1]}"
27        fi
28        printf '{"jsonrpc":"2.0","id":%s,"error":{"code":-32603,"message":"%s"}}\n' "$id" "$why"
29    fi
30    exit 1
31}
32
33[[ -x "$BIN" ]] || refuse "No proxy binary at $BIN."
34[[ -r "$ENV_FILE" ]] || refuse "No $ENV_FILE beside the proxy binary."
35
36library_path=""
37while IFS= read -r line; do
38    [[ "$line" == LD_LIBRARY_PATH=* ]] && library_path="${line#LD_LIBRARY_PATH=}"
39done <"$ENV_FILE"
40[[ -n "$library_path" ]] || refuse "$ENV_FILE names no LD_LIBRARY_PATH."
41
42LD_LIBRARY_PATH="$library_path" exec "$BIN" mcp