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.
MCReport is published by MCHaagenti and is distributed for commercial sale. See the LICENSE file distributed with MCReport for terms.
How it works
- Submitting. A player runs
/report <message>. The command also works from the server console, in which case the report is stored under the reporter UUIDconsole. - Vetting. Each player submission fires a cancellable
ReportCreateEventbefore 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. - Storing. The report is written through
MCReportProvider. Every method is asynchronous and returns aCompletableFuture, so nothing blocks the server. - 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.
- 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 theconsoleUUID./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 askand/report embedare disabled and hidden from tab completion.token— the shared OpenRouter API key. While left asyour_api_keyor blank, the AI features stay disabled.
Semantic search
/report ask <prompt> answers a natural-language question about the stored reports using Retrieval-Augmented Generation:
- Every report and every stored embedding for the configured model are loaded.
- Only the question is embedded.
- Reports that already have a stored vector are ranked by cosine similarity to the question, and the most relevant are kept.
- Those reports are given to the chat model as grounding context, and its answer is returned.
/report ask is strictly read-only. It never embeds or persists reports; it only embeds the question. Reports without a stored vector for the configured model are simply ignored, which is what /report embed exists to fix.
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, asplatform/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 sharedAbstractSqlReportDatabaseplus the SQLite, MySQL, and PostgreSQL backends, a native MongoDB backend, the native OpenRouter client, the semantic search logic, and theMCReportProviderentry point.bukkit— SpigotMC-API-only core: the/reportcommand, the cancellableReportCreateEvent, theSyncExecutorscheduling abstraction, andAbstractMCReportPlugin, 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
- Drop the
MCReportjar intoplugins/. - Start the server once to generate
plugins/MCReport/config.yml, then setenable: true(and pick adatabase.typeif not using SQLite). - Players can now run
/report <message>. - Optional: set
openrouter.tokenandopenrouter.models.chatto enable the AI assistant, andopenrouter.models.embedto 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.