The in-server agent behind MineUI's enriched metrics.
A Forge mod / Paper plugin that exposes live Minecraft server data over
a small, authenticated HTTP API — because it runs inside the JVM, it sees
what a detached process can't.
Why · Install · Features · Development · License
MineUI can already read TPS and player
data from outside the server — an RCON tps command, a status ping — but
that view is limited: no true per-window MSPT, no per-dimension chunk/entity
counts, no accurate loaded-mod list, and no way to see a container's own
cgroup resource usage from inside it.
MineUI Server Utils closes that gap by running inside the server JVM as a
mod or plugin and exposing what it sees over a small HTTP API, so MineUI (or
anything else) can read it. It is not a standalone jar — there's no main()
and java -jar won't work; it has to be loaded by a Forge or Paper server.
- Forge: drop the built jar into the server's
mods/directory. - Paper/Bukkit: drop the built jar into the server's
plugins/directory.
Restart the server — the API comes up when the server finishes starting and shuts down with it. There's no packaged release yet; build the jars yourself — see Development.
A Gradle multi-project split into a loader-agnostic core (HTTP API, auth,
JSON, config, system/cgroup stats — no Minecraft/loader imports) and thin
per-platform adapters implementing a shared ServerRuntime interface:
| Module | Output | Target |
|---|---|---|
core |
(bundled into the adapters) | Shared logic, no MC/loader imports |
forge |
Forge mod jar | Forge 1.20.1 |
paper |
Bukkit plugin jar | Paper/Spigot/Purpur/Folia and Bukkit-compatible hybrids (Mohist, Arclight, …) |
Adding a new platform (Fabric, NeoForge) means a new adapter module
implementing ServerRuntime; the core never changes.
All responses are JSON except /health. All except /health require the
bearer token when one is configured.
| Endpoint | Returns |
|---|---|
GET /health |
ok (plain text, unauthenticated) |
GET /properties |
Filtered server.properties key/values |
GET /players |
{ ok, players[] } — online player names |
GET /tps |
{ ok, one, five, fifteen, raw } — in-JVM tick rate |
GET /metrics |
TPS, MSPT, chunks, entities, per-dimension counts, system/container stats |
GET /mods |
{ ok, mods[] } — loaded mods (Forge) or plugins (Paper) as id/name/version |
GET /status |
Aggregate of properties, players, tps, metrics, and system stats |
curl -H "Authorization: Bearer $MINEUI_SERVER_UTILS_TOKEN" \
http://127.0.0.1:8787/metrics| Variable | Default | Purpose |
|---|---|---|
HTTP_HOST |
127.0.0.1 |
Bind address. Binding non-loopback (e.g. 0.0.0.0) requires MINEUI_SERVER_UTILS_TOKEN — the API refuses to start without it. |
HTTP_PORT |
8787 |
Listen port. |
MINEUI_SERVER_UTILS_TOKEN |
(unset) | Shared secret. When set, every endpoint except /health requires Authorization: Bearer <token>, compared in constant time. |
SERVER_PROPERTIES_PATH |
<serverDir>/server.properties |
Path to server.properties. |
Out of the box the API is reachable only from the same machine, so a local
MineUI instance works with zero config. To let another machine (e.g. MineUI
on a laptop, server on a desktop) reach it, set both HTTP_HOST=0.0.0.0 and
a strong token — the server won't bind a public interface without one.
rcon.password, rcon.port, and query.port are always stripped from
/properties and /status.
CPU/memory stats are read directly from the kernel cgroup interface
(/sys/fs/cgroup), so they work from inside a Podman/Docker container with
no podman/docker CLI present. They're only reported when the process is
actually containerized; a bare-metal server gets host-level metrics instead.
Paper reports Bukkit.getAverageTickTime(), so the 1m/5m/15m fields in
/tps and /metrics all report that same rolling value — Bukkit has no
per-window MSPT breakdown. The Forge adapter reports true per-window MSPT
from its own tick tracker.
Requires JDK 17 (auto-provisioned by the Gradle toolchain's Foojay resolver if you don't have one). The Gradle wrapper is committed, so no local Gradle install is needed.
./gradlew build # builds and tests all modules
./gradlew :forge:build # Forge mod only
./gradlew :paper:build # Paper plugin onlyOutput jars: forge/build/libs/forge-<version>.jar and
paper/build/libs/paper-<version>.jar. The first Forge build downloads
Forge's userdev artifacts and Minecraft mappings and can take several
minutes.
CI (.github/workflows/build.yml, workflow name build) runs
./gradlew build --no-daemon --stacktrace on every push and PR, plus a
weekly job that flags any pinned GitHub Action that's fallen behind a major
version. See CONTRIBUTING.md for module boundaries and PR
conventions.
AGPL-3.0-only — see LICENSE.