MCReport

MCReport

A commercial reporting plugin for Minecraft servers. Players submit reports with one command; staff read them, and can ask a built-in AI assistant to summarize a report or answer questions across every report ever filed.

Commercial Software

MCReport is published by MCHaagenti and is distributed for commercial sale. See the LICENSE file distributed with MCReport for terms.

How it works

  1. Submitting. A player runs /report <message>. The command also works from the server console, in which case the report is stored under the reporter UUID console.
  2. Vetting. Each player submission fires a cancellable ReportCreateEvent before the report is persisted, so other plugins can veto a report or rewrite its contents. Console submissions have no associated player, so they skip the event.
  3. Storing. The report is written through MCReportProvider. Every method is asynchronous and returns a CompletableFuture, so nothing blocks the server.
  4. Indexing. If an embedding model is configured, the report's contents are embedded in the background and the vector is stored. This is best-effort: it never blocks or fails a submission.
  5. Reviewing. Staff read reports, mark them reviewed, or hand them to the AI assistant for a summary or a question across the whole set.

Commands

  • /report <message> — submit a new report. Any player or console; console submissions are stored under the console UUID.
  • /report test ai [tool] — let the built-in AI self-test the plugin commands. With no tool name it tests every tool; with a tool name (tab-completed) it tests only that one. Operators only.
  • /report summary <report id> — ask the AI to summarize a stored report: who reported it, when, and what it is about. Operators only.
  • /report ask <prompt> — ask a natural-language question about the stored reports using semantic search. Operators only; disabled unless an embedding model is configured.
  • /report embed <amount|all> — backfill embeddings for reports stored without one, newest first, ten at a time. Operators only; disabled unless an embedding model is configured.

AI assistant

MCReport ships with a native OpenRouter integration. The model is called directly over HTTP from the JDK HttpClient with no third-party AI SDK, and it sits behind the IAIService interface so the backend can be swapped without touching the rest of the plugin.

Every tool description sent to the model explicitly states it comes from the MCReport plugin running on a Minecraft server, so the model never assumes it is using its own built-in tooling.

Add the following to config.yml. Every player on the server shares this single API key, and the AI sub-commands are restricted to operators:

openrouter:
  models:
    embed: ""
    chat: "deepseek/deepseek-v4-flash"
  token: "your_api_key"
  • models.chat — the chat model used for /report summary, /report test ai, and the answer step of /report ask.
  • models.embed — the embedding model used for semantic search. Leave empty to disable it; while empty, /report ask and /report embed are disabled and hidden from tab completion.
  • token — the shared OpenRouter API key. While left as your_api_key or blank, the AI features stay disabled.

Embeddings are stored per model

Embeddings do not live on the report. They live in their own report_embeddings table, keyed by both the report and the model that produced the vector:

  • report_id — the report the vector describes. A foreign key, cascading on delete.
  • model_name — the model that produced it, as platform/model.
  • embedding — the vector, serialized as a JSON array.
  • created_at — when the vector was stored.

The primary key is the pair (report_id, model_name), so a report can hold one vector per model at the same time.

This is what makes changing embedding model safe. Server owners run different models, and a model that looks better on paper may turn out worse in practice, or simply cost too much. Point openrouter.models.embed at a different model and the new vectors are written alongside the old ones rather than over them. If the new model does not work out, switch back and the previous vectors are still there — semantic search keeps working immediately, nothing has to be re-embedded, and nothing is paid for twice.

Vectors from different models are never mixed. They describe the same reports in different vector spaces and are not comparable, so /report ask only ever reads the vectors belonging to the currently configured model, and /report embed only counts a report as missing if it has no vector for that model.

Model identifiers are stored as {platform}/{model} and lowercased before they are written, so one model can never appear under two spellings. A configured model not in that shape is rejected at startup rather than stored — a name that does not match the convention could never be matched again, which would strand every vector written under it.

Database

The storage backend is selected via database.type in config.yml. Supported values are sqlite (default, zero-configuration), mysql, postgresql, and mongodb. For SQLite the file name is set under database.sqlite.file (default mcreport.db).

Two tables (or collections) are created automatically on first start: reports and report_embeddings.

Architecture

MCReport follows the MCHaagenti sub-module architecture; the storage core is pure Java and never touches the Bukkit API, and all storage operations are asynchronous and return a CompletableFuture.

  • api — interfaces and immutable models only (IReportDatabase, IAIService, AITool, Report).
  • common — storage implementations: a shared AbstractSqlReportDatabase plus the SQLite, MySQL, and PostgreSQL backends, a native MongoDB backend, the native OpenRouter client, the semantic search logic, and the MCReportProvider entry point.
  • bukkit — SpigotMC-API-only core: the /report command, the cancellable ReportCreateEvent, the SyncExecutor scheduling abstraction, and AbstractMCReportPlugin, which holds the whole bootstrap.
  • platforms — thin entry points per platform:
    • spigotmc, papermc — use the regular server scheduler.
    • foliamc — uses a region/entity-aware scheduler so all Bukkit access respects Folia's threading model.
    • engine — the universal jar; see below.

Thread-safety

Database and AI work runs off the server thread. Any result that touches the Bukkit API — including every reply a command sends — is scheduled back through a platform-specific SyncExecutor: the main thread on SpigotMC and PaperMC, or the owning region or entity thread on FoliaMC. On Folia there is no main thread at all, so a reply sent straight from a completion callback would be touching a player from a thread that does not own them.

One jar for every platform

MCReport ships as a single universal jar. There is no separate SpigotMC, PaperMC, or FoliaMC download to choose between.

The jar bundles all three platform modules and detects the running server at enable time, probing for Folia's regionised server class first, then Paper's configuration class, falling back to Spigot. It then installs the matching scheduler and writes the platform it found to the server log.

Building

This project uses Gradle with the Java 25 toolchain. Building with the shadowJar task on platforms:engine produces the universal MCReport jar. The per-platform modules still build, but they are inputs to that jar rather than artifacts to distribute.

Getting started

  1. Drop the MCReport jar into plugins/.
  2. Start the server once to generate plugins/MCReport/config.yml, then set enable: true (and pick a database.type if not using SQLite).
  3. Players can now run /report <message>.
  4. Optional: set openrouter.token and openrouter.models.chat to enable the AI assistant, and openrouter.models.embed to enable semantic search.

License

Copyright (c) 2026 MCHaagenti. All rights reserved.
Please see the LICENSE file distributed with MCReport for details regarding usage and distribution.

Explore