# Foreman: the documentation > A multi-tool for Minecraft server owners, admins and plugin developers. --- # Foreman A multi-tool for Minecraft server owners, admins and plugin developers. See what your server sends and draws, debug your plugins on a running server, and work with an AI that can do the same: in game, or from your terminal. Foreman is a **Fabric client mod** and an optional **Paper plugin** for Minecraft 26.3. *A plugin's bug, found and fixed from inside the game: the chat reads the server's log, fixes the config, reloads the plugin and tries the command again. More in [Examples](ai/examples.md).*
- **Packet viewer** Record what the server and your client send each other, then find what floods the connection. [→ Packet viewer](client/packets.md) - **HUD inspector** See how the client lays out action bars, titles, boss bars and the tab list, glyph by glyph. [→ HUD inspector](client/hud-inspector.md) - **Plugin locations** Plugins send their NPCs, spawns and regions, and the mod draws them. Move them with a gizmo. [→ Locations](client/locations.md) - **Dev-server tools** Commands with everything they printed, the log since the last reload, stale jars, a profiler, event listeners. [→ Dev-server tools](server/dev-tools.md) - **AI chats** Your own Claude Code, opencode or OpenRouter, in game, with your position, selection and plugin folders. [→ Chats](ai/chats.md) - **Outside agents** Claude Code in a terminal uses the client through MCP to test your plugin end to end in the running game. [→ Outside agents](ai/outside-agents.md) - **Your plugins, understood** Skills teach the AI WorldEdit, WorldGuard, MythicMobs, LuckPerms and more; add yours as a markdown file. [→ Plugin integrations](server/integrations.md) - **Dupe testing** Count every item, attack a plugin's menus and shops, and catch what duplicates — before your players do. [→ Dupe testing](server/dupe-testing.md) - **For your AI** Prompts to connect your agent to Foreman or add Foreman support to your plugin; the docs in one file. [→ Ask your AI](ai/ask-your-ai.md)
## What runs where | | Client mod alone | With the server plugin | |---|---|---| | Packet viewer, HUD inspector, local location sets | ✓ | ✓ | | Plugin locations | when a plugin sends them (`foreman-paper`) | when a plugin sends them | | AI chats with the client's tools (screenshots, commands, files, screens) | ✓ | ✓ | | Dev-server tools (`server_command`, `server_log`, `server_plugins`…) | | ✓ | | World tools (render, edit, terrain, buildings, check, undo) | | ✓ | | Dupe testing (item ledger, inventory watch, action bursts) | | ✓ | | Tools other plugins publish (`ForemanTools`) | | ✓ | ## Cheap when unused Foreman is meant to stay installed. A packet recording runs only while you record, overlays are drawn only when they're on, and the client's MCP server only answers outside agents when you allow them. !!! note "Server requirements" The server plugin runs on Paper alone. Add FastAsyncWorldEdit for the world tools: they're built on it, and they're off without it. --- # Getting started 1. [Install the client mod](install-client.md). That's all you need on any server. 2. [Install the server plugin](install-server.md) on servers where you want the server tools. 3. [First run](first-run.md): the hub, a first packet recording, a first chat. 4. Using a Flatpak launcher? [Let it start Claude Code or opencode](flatpak.md). --- # Install the client Put `foreman-1.0.0.jar` in your instance's `mods/` folder, with: - Fabric Loader 0.19.3 or later, for Minecraft 26.3 - [Fabric API](https://modrinth.com/mod/fabric-api) 0.160.5 or later - [Fabric Language Kotlin](https://modrinth.com/mod/fabric-language-kotlin) 1.13.12 or later Any later version works: the mod asks for the oldest ones it was built against. The mod keeps its files in `config/foreman/`: | Path | What | |---|---| | `settings.json` | Every setting (readable by you only: it holds tokens and keys) | | `chats//` | Your chats, in the folder of the server each started on (every chat is listed on every server) | | `local-locations/` | Your local location sets | | `packets/` | Packet recordings written by `/packets export` | | `client-mcp.json` | How outside agents connect, while you allow them | Download it from the [releases](https://github.com/qore-games/foreman/releases): each release has the mod (`foreman-.jar`) and the server plugin (`foreman-server-.jar`). --- # Install the server plugin The plugin is optional: the client mod works on any server. Install it where you want the [dev-server tools](../server/dev-tools.md), the [world tools](../server/world-tools.md) or tools other plugins publish. 1. Put `foreman-server-1.0.0.jar` in `plugins/`. For the world tools, add [FastAsyncWorldEdit](https://modrinth.com/plugin/fastasyncworldedit) too: without it the plugin has the dev-server tools and other plugins' tools, and none that edit the world (the log says so at start). 2. Start the server. The log shows the MCP address, `http://127.0.0.1:25599/mcp` by default; other MCP clients send the token Foreman made in `plugins/Foreman/mcp-token.txt`. 3. Join with the client mod. Players with `foreman.client` (ops by default) are connected to the server's tools. Then read [Setup and configuration](../server/setup.md) to let players on other machines connect, and [Permissions and tokens](../server/permissions.md) for who can use what. !!! warning The world tools edit the world, and the dev-server tools run commands as the console. Give `foreman.client` and `foreman.admin` only to people you trust with that, and keep the MCP server on localhost unless you need more. --- # First run ## Open the hub Press ++h++. The hub has everything on one screen: a rail of sections on the left, grouped by what they're for (AI, World, Debug), with Keys and Settings at the bottom. ++l++ opens it on Locations. See [The hub](../client/hub.md). ## Record the traffic 1. Stand where the game stutters and run `/packets start 20`. 2. When it stops, run `/packets` (or open the hub's Packets section). 3. Start with *Suspects*: the traffic grouped by what probably sends it. See [Packet viewer](../client/packets.md). ## Look at the HUD `/hud` opens the HUD inspector, and `/hud guides action_bar` draws guides over the action bar. See [HUD inspector](../client/hud-inspector.md). ## Ask the AI Press ++y++, start a chat and ask. By default the chat uses Claude Code with your own login; pick another backend in the hub's AI section. Give the chat your plugin's folder through a [workspace](../ai/workspaces.md) so it can read and fix the code. See [Chats](../ai/chats.md). ## Let Claude Code in a terminal drive the game In the hub's AI section, allow outside sessions and copy the `claude mcp add` command. Run it once in the folder Claude Code works in. See [Outside agents](../ai/outside-agents.md). --- # Flatpak launchers If your launcher is a Flatpak (Prism Launcher from Flathub, for example), it can't start programs on your computer by default, so chats can't start Claude Code or opencode. Let it, once, then restart the launcher: ```sh flatpak override --user --talk-name=org.freedesktop.Flatpak org.prismlauncher.PrismLauncher ``` The OpenRouter backend doesn't need this: it talks to OpenRouter from the game itself. Folders outside the launcher's sandbox may not be visible from the game. Claude Code and opencode still use them, because they run outside the sandbox. The hub warns about this when you add such a folder. --- # Client The mod works on any server. Everything it adds starts from the [hub](hub.md) (++h++): - [Packet viewer](packets.md): what the server and the client send each other. - [HUD inspector](hud-inspector.md): how the client lays out the HUD's texts. - [Locations and local sets](locations.md): plugin locations drawn in the world, and your own lists of points. - [Selection](selection.md): a box with a gizmo, for the AI or your own commands. - [Plugin messages and helpers](plugin-messages.md): a log of plugin channels, copying your position. - [Keys and commands](keys-commands.md): every key and client command. The AI chats are described in [AI](../ai/index.md). --- # The hub ++h++ opens the hub, ++l++ opens it on Locations. A rail of sections on the left, grouped by what they're for, with each feature's settings on its own page: | Group | Sections | |---|---| | | **Home**: is the Foreman plugin linked, the running chats, the packet recording, the overlays and your selection, your keys | | **AI** | **Chats**, **Workspaces**, **Tools** (every tool the AI can use, by provider), **Agents** (the backend chats run on, agents outside the game) | | **World** | **Overlays** (what's drawn in the world), **Locations** (sources and categories, the location editor and its snap), **Selection** (your selection tool, the WorldEdit selection and its tool) | | **Debug** | **Packets** (the packet viewer and its settings), **HUD** (the HUD inspector), **Plugin messages** (a log of what plugins send on their channels) | | | **Keys** (by group; click a key, press the new one), **Settings** (where each setting is, the config folder, links) | The rail shows badges: the number of chats at work, a red dot while packets are being recorded, and a dot on the sections whose tool or log is on (the selection tool, the location editor, HUD guides, the plugin message log, outside agents). ![The hub's Home: the grouped rail, the server, chats, debugging and the world at a glance](../assets/screenshots/hub.png) ## Overlays Each layer is **off**, **nearby** (around you only) or **always** (everything in range): | Layer | What | Needs | |---|---|---| | Locations | What plugins send | a plugin using the [location API](../developers/locations-api.md) | | Areas | Named places, space reserved by builds, roads, zones | the Foreman plugin | | Points | Spots builds recorded (containers, spawns…) | the Foreman plugin | | Check issues | What the last `check` found, numbered | the Foreman plugin | | Last AI change | The box the AI just changed, fading out | the Foreman plugin | The section also has the master switch, drawing through blocks, a notice when you enter or leave an area, the areas you're in on the HUD, and the distances (nearby from, labels within). --- # Packet viewer Records what the server and your client send each other, and helps find what floods the connection or stutters the client. Nothing runs while it isn't recording. ![The packet viewer's Suspects: one NPC's ModelEngine parts resend their transformations every tick](../assets/screenshots/packets.png) ## Recording | | | |---|---| | `/packets start [seconds]` | Start (until `/packets stop`, or for that long) | | `/packets stop` | Stop | | `/packets` | Open the viewer (the hub's Packets section) | | `/packets clear` | Drop the recording | | `/packets export` | Write everything to `config/foreman/packets/` | | `/packets ask` | A new chat looks for what lags in the recording | A key to start and stop a recording is in the hub's Keys section, unbound by default. While recording, the HUD shows the time and the bytes per second. ## What you see - **Each packet:** type, direction, size on the wire after compression, what it's about (an entity, a chunk, a plugin channel), its fields, and how long the client took to handle it. - **Suspects:** the traffic grouped by what likely sends it (a model's parts, a boss bar, the action bar, a plugin channel…), how often it's resent, how much of it repeats unchanged, which entity fields change, and what to search for in your plugins: item models, custom data keys, translation keys, texts. - **Totals:** the types, entities and chunks that get the most, and the bytes per second. ## Settings In the Packets section: whether to keep each packet's fields (not only type and size), and how many packets a recording keeps one by one (20,000 to 500,000; the oldest go first, the totals always cover the whole recording). ## With the AI *Ask the AI* (`/packets ask`) starts a chat that reads the recording with the `packets_*` tools, searches your workspace folders (plugin sources, server configs, language files) for the clues, and proposes the change. Give the chat a [workspace](../ai/workspaces.md) with your plugins' sources first. Outside agents get the same tools ([reference](../ai/tools.md#packets)). --- # HUD inspector Shows how the client lays out your server's HUD texts, glyph by glyph: what you need to build HUDs out of font glyphs without them shifting when the text changes. ![The HUD inspector: the action bar's parts, where each starts and ends](../assets/screenshots/hud-inspector.png) Elements: `action_bar`, `title`, `subtitle`, `boss_bar`, `tab_header`, `tab_footer`, `screen_title`. ## In game ![Guides over the action bar: the screen's center and each glyph's left edge](../assets/screenshots/hud-guides.png) - `/hud` opens the hub's HUD section: each element's parts (the root text, then each child, the way HUD plugins compose a line), where each part starts and ends, and the part that doesn't come back to where it started. - `/hud guides [element]` draws guides over one element: the screen's center, where each part starts and where it leaves the cursor (in red when it doesn't come back), and the left edge of each glyph. ## For the AI and outside agents | Tool | Does | |---|---| | `ui_capture` | An element's layout: parts, glyphs by texture with their real advance and position, the problems found, and a zoomed image | | `ui_watch` | Watches an element for a few seconds and reports every glyph that moved, and the part whose text changed; it can run a command to trigger the change | | `ui_measure` | The same report for a text you give (a text component's JSON, or text with a font), without showing it | | `ui_glyphs` | The glyphs the loaded resource packs declare, by font: character, texture, height, ascent, advance | | `ui_check_widths` | Checks the widths your server declares (glyph definitions, a font width table) against what the client draws | !!! tip A part that leaves the cursor off moves everything after it, and a centered line moves by half its width change. `ui_capture` names both. --- # Locations and local sets Plugins send their locations (NPCs, spawns, regions, waypoints…) to players with the permission, and the mod draws them in the world: each **source** (a plugin) has **categories**, each shown or hidden. Staff can teleport to them and move them; the plugin receives the move and saves it. To send locations from your plugin, see the [location API](../developers/locations-api.md). ![An NPC plugin's locations drawn in the world: the NPC's box and its data](../assets/screenshots/locations.jpg) ## Viewing The hub's Locations section (++l++) lists the sources and their categories: show or hide each, show only one source, teleport to the nearest location of a category. The Locations layer of the [overlays](hub.md#overlays) sets how far they're drawn. ## Editing Turn on the location editor (`/locations editor`, or its key, unbound by default): left click a location to select it, drag the gizmo's arrows, right click a block to place it there. Moves snap to a step (the gizmo snap setting in the Locations section; hold ++ctrl++ to move freely). | Key | Moves the selected location | |---|---| | ++k++ | to your feet | | ++u++ | to your eyes | | ++j++ | to your block | | Command | Does | |---|---| | `/locations list [source]` | Sources, or a source's categories | | `/locations toggle [category]` | Show or hide | | `/locations add [k=v…]` | Ask the plugin to add one where you stand | | `/locations relocate [x y z]` | Move the selection (default: your block) | | `/locations rename ` | Rename it; `rename source `, `rename category ` move it to another source or category | | `/locations delete` | Delete it (for plugin locations: on this client only, the plugin keeps it) | | `/locations gizmo` | Show or hide the arrows | | `/locations clear`, `status` | Forget every received location (they come back with the next update), count them | ## Local sets Your own lists of points, kept on your computer in `config/foreman/local-locations/.yaml`. The active set is shown and edited like a source named `local:`, and every change is saved. They need no plugin and follow you from server to server. What they're for: - **Playtest notes.** Walk a map and drop a point wherever something's wrong (`/local add bugs door won't open`, `/local add todo needs a light`), then fix them one by one with the points drawn in the world, or give the file to a chat: each point has its world, position and label. - **Positions for a config, before the plugin exists.** Place the spawns, loot spots or waypoints of a feature you're writing, move them with the gizmo until they're right, then copy them from the YAML (`world;x;y;z;yaw;pitch`) into your plugin's config. - **Servers you can't add a plugin to.** Building on someone else's server, keep your own markers: the build's corners, where the next pieces go. - **Camera points.** Exact positions and angles for screenshots, trailers or a cinematic plugin's keyframes. | Command | Does | |---|---| | `/local create \| delete \| use ` | Manage sets, pick the active one | | `/local add [category] [label]` | Add your position to the active set | | `/local rename ` | Rename the active set | | `/local save \| load ` | Write or read a set file | | `/local list \| saved \| active`, `/local stop` | List them, hide the active set | --- # Selection ## Your selection A box in the world, for the AI or your own commands. Press ++b++ (or use the hub's Selection section), then left click and right click the two corners, and drag the gizmo to adjust it. The AI chats are told about it and about the WorldEdit selection: "here", "this" and "the selection" mean them. The hub's *Ask a chat* button, or the *Ask the AI about the selection* key (unbound by default), starts a chat about the selected area. `/ai selection clear` clears it. ## The WorldEdit selection On a server with WorldEdit or FastAsyncWorldEdit, the mod shows your WorldEdit selection in the world, whatever its shape: cuboid, sphere, ellipsoid, cylinder, polygon, convex. It's what WorldEdit tells a CUI client, so it works without the Foreman plugin. Turn it off in the hub's **World → Selection** (*Show the WorldEdit selection in the world*). ![WorldEdit selections in four shapes](../assets/screenshots/worldedit-shapes.jpg) The **WorldEdit tool** (hub's Selection section, or its key, unbound by default) edits it: - **Left / right click a block** for `//pos1` / `//pos2`, however far the block is. What they mean depends on the selector, as with the wand: two corners, a center and a radius, the points of a polygon. - **Drag an arrow** to move the selection (`//shift`), **a knob** to resize it. A sphere, an ellipsoid or a cylinder grows on both sides of its center; a polygon or a convex shape is stretched. The mod selects it again with the commands that make it, paced so the server's spam check never kicks you. Off when the WorldEdit CUI mod (or WorldEdit itself) is installed: it draws the selection then. --- # Plugin messages and helpers ## Plugin messages Logs the plugin messages (custom payloads) your client sends and receives, both ways: in the hub's **Debug → Plugin messages**, the last 500 as a row each — time, direction, a colour per channel, size and a preview. The header counts them (in / out / channels), **Pause** freezes the list so a fast stream holds still while you read, and you can filter by channel or text. Nothing is kept while logging is off. **Click a message** to open it: the decoded text (readable for the JSON and strings most plugins send) and a hex + ASCII dump of the raw bytes, so a binary payload is legible too. They can also be printed (**Print**) to chat, a line each as the hub's rows read (direction, the channel in its colour, size, a preview; hover for the whole text, click to copy it), or to the game's log, as lines like `Plugin message S→C B ""`. ![The plugin message log: WorldEdit's selection and Foreman's bridge](../assets/screenshots/plugin-messages.png) The [packet viewer](packets.md) counts plugin channels too, by size and rate, to find the one that floods the connection. ## Copy your position | Key | Copies | |---|---| | ++f6++ | Your exact position (`x y z`) | | ++f7++ | Your block position | --- # Keys and commands ## Keys Change any of them in the hub's Keys section (click the key, then press the new one: ++esc++ cancels, ++backspace++ unbinds) or in Minecraft's controls. | Key | Does | |---|---| | ++h++ | The hub | | ++y++ | AI chats | | ++b++ | Selection tool | | ++l++ | Plugin locations | | ++k++ / ++u++ / ++j++ | Location editor: move the selected location to your feet / eyes / block | | ++f6++ / ++f7++ | Copy your exact / block position | | unbound | Location editor, WorldEdit tool, packet recording, ask the AI about the selection, overlays on/off, each overlay layer | ## Client commands | Command | Does | |---|---| | `/packets [start [seconds] \| stop \| clear \| export \| ask]` | [Packet viewer](packets.md) | | `/hud [guides [element]]` | [HUD inspector](hud-inspector.md) | | `/locations …` | [Plugin locations](locations.md) and the location editor | | `/local …` | [Local sets](locations.md#local-sets) | | `/ai` | Open the chats | | `/ai ask ` | Send a prompt in a new chat | | `/ai new`, `/ai cancel` | A new chat; stop every running chat | | `/ai tools` | Every tool the AI can use, by provider | | `/ai selection clear` | Clear the selection | The server plugin's `/foreman` command is described in [Setup and configuration](../server/setup.md#commands). --- # AI Foreman doesn't ship a model or an account. The AI runs on your computer with **your own** Claude Code, opencode or OpenRouter account, and Foreman gives it tools: - **The client's tools**, on any server: screenshots, commands as you, the camera, the open screen (clicks, typing), entities, items, resource packs, the client log, the packet viewer, the HUD inspector, and the folders you give it. - **The server's tools**, where the Foreman plugin runs: [dev-server tools](../server/dev-tools.md), [world tools](../server/world-tools.md), and tools other plugins publish. You can use them in two ways: - [Chats](chats.md) in game (++y++): ask in plain words. Several chats can run at once. - [Outside agents](outside-agents.md): Claude Code in a terminal connects to the client's tools through MCP, so it can implement a change in your editor, deploy it, and test it in the running game. See the [tool reference](tools.md) for every tool. --- # Chats Press ++y++ (or `/ai`) to open the chats. Each chat has its own transcript and backend, and runs on its own: start one, switch to another while it works. The hub's Chats section lists them all, whatever the server, and the HUD shows the ones at work. === "A build's report" ![A chat's report after a build: what it made, what the checks found](../assets/screenshots/ai-build-chat.png) === "A server's health" ![A chat checking a dev server's health with the server tools](../assets/screenshots/chat.png) ## What a chat knows Each prompt carries your context: your position and facing, the block or entity you're looking at, your [selection](../client/selection.md) and your WorldEdit selection. "Here", "this" and "where I look" mean them. On a server without the Foreman plugin, the chat is told which server tools are missing, so it says so in one line instead of looking for a workaround. ## Across servers Every chat is listed on every server: a conversation about a plugin can start on your dev server and continue on staging. Each chat remembers the server it was last used on (shown next to it when it's another one), and when you continue it elsewhere the AI is told: the positions, areas and edits earlier in the chat are in the other server's world, and its tools now act on this one. ## What a chat may do - **Folders:** the folders of its [workspaces](workspaces.md) and any folder added to this chat only. The AI reads and edits files only there, and runs in the first one. - **Commands** (switch at the top of the chat): run commands as you, with your permissions. - **Vision** (switch at the top of the chat): take screenshots of your view. ## Starting points - `/ai ask ` sends a prompt in a new chat from the chat bar. - `/packets ask` starts a chat that looks for what lags in the [packet recording](../client/packets.md). - *Ask a chat* on the hub's Home page, or the *Ask the AI about the selection* key, starts a chat about the selected area. - On a server with the plugin, its [prompts](../server/lessons-prompts.md#prompts) are ready-made tasks. `/ai cancel` stops every running chat. Chats are saved in `config/foreman/chats//`. --- # Examples Real runs, recorded in game and sped up. Each is one prompt in a chat (++y++) with Claude Code, on a dev server with the Foreman plugin; the transcript shows every tool the AI calls. ## Fix a plugin's bug > /shop buy sword says "An internal error occurred". Find out why and fix it, then check that it works. The chat was given the plugin's folder (its workspace switches, then *Folders of this chat only*). It read the server's log with the stack trace (`server_log`), found the typo in the plugin's `config.yml` (`DIAMOND_SWORDD`), fixed it, marked the log (`server_log_mark`), reloaded the plugin through PlugMan (`server_reload_plugin`), ran `/shop buy sword` as the player, and checked that nothing new was logged. 11 turns. ## Build a place > Build a small market square on the hill around x -30, z 140: a level cobbled plaza with a well in the middle, three > market stalls with colored awnings around it, benches and lanterns, and a path leading down the slope. Medieval > style, about 30 by 30 blocks. Don't ask me anything: decide yourself, check it, and tell me when it's done. It measured the site, graded the hill into a pad with banks, built in labelled steps (each one an undo step, shown in chat to ops), laid the path down the slope, then ran `check`, which walks the build like a player, and fixed what it reported (a well you couldn't climb out of). It looked at the result in first person before saying it was done, and named and claimed the area. About four minutes. ![The market square](../assets/screenshots/ai-build-result.jpg) ## Find what floods the connection Record the packets, then *Ask the AI* in the packet viewer. The chat read the recording (`packets_suspects`, `packets_list`), followed the clues into the plugins' sources in its workspace, and named the code behind each: an NPC's model parts resending their transformations every tick, an action bar and boss bars resent unchanged. Then it proposed the changes, with the code. ## Do something in game > What am I looking at? Then make it sunset, and give me a spyglass and 16 lanterns. It took a screenshot to see what you see, described it, and ran the commands as you (`client_run_command`). Turn off *Commands* or *Vision* in a chat's header to forbid either. ## More to ask - *Replace all the stone in this box with 50% stone and 50% cobblestone.* One `worldedit` call (`//replace stone 50%stone,50%cobblestone`); a run measured 3 turns. See [Plugin integrations](../server/integrations.md). - *Why is the server lagging?* `server_status`, `server_profile` (the server thread by plugin) and the log. - *Did my deploy work?* `server_plugins` says whether each plugin runs the jar on disk or an older one. - *Which listeners run on `PlayerInteractEvent`, in what order?* `server_listeners`. - *My boss bar text jumps when the number changes: why?* The HUD inspector's tools (`ui_capture`, `ui_watch`) measure each glyph. - *Open the settings menu and check that every button works.* Screen tools: `client_screen`, `client_click`. - *Give the tavern a second floor, in the style of the houses around it.* World tools, with the [world's profile and templates](../server/building-templates.md). --- # Ask your AI to use Foreman Your AI (Claude Code, opencode, Cursor, any agent with MCP) can set Foreman up and build on it for you. These prompts are written for it: paste one into a session in your plugin's or server's folder. The documentation is available for AIs in one file, , and as an index, . The repository's [AGENTS.md](https://github.com/qore-games/foreman/blob/main/AGENTS.md) explains the code to an AI working on Foreman itself. ## Connect to your server and your game ```text Read https://qore-games.github.io/foreman/llms-full.txt to learn Foreman. Then connect yourself to it: - my dev server runs the Foreman plugin; its MCP endpoint is http://127.0.0.1:25599/mcp (mcp.port in plugins/Foreman/config.yml), with the token in plugins/Foreman/mcp-token.txt (or mcp.token) sent as "Authorization: Bearer ": add it as an MCP server named "minecraft". - my game runs the Foreman mod: I allowed outside agents in the hub (AI → Agents); the address and token are in config/foreman/client-mcp.json of my Minecraft instance: add it as an MCP server named "foreman-client". List the tools and skills you get, and tell me what you can now do for me. ``` ## Add Foreman support to my plugin ```text Read https://qore-games.github.io/foreman/llms-full.txt, sections "Skills for your plugin", "ForemanTools" and "Add Foreman to your plugin". Then make this plugin easy for an AI to use through Foreman: 1. Write skills in src/main/resources/foreman/skills/ for its main tasks (its commands, config files, how to test a change, pitfalls), one per task, each with a description that says when to read it. Only commands that exist in this code. 2. Where a command can't give what an AI needs (structured data, an action without a command), add a tool with ForemanTools, in a class loaded only when Foreman is enabled (softdepend: Foreman). 3. Build, then on my dev server (Foreman's MCP) reload the plugin, check that /foreman skills and /foreman tools list them, and try each tool once. ``` ## Write a skill for my server ```text Using Foreman's tools on my server, work out how we (look at the plugins' configs and commands involved, try them), then save it as a skill with save_skill so later sessions know it: a short description that says when to read it, the exact commands and steps, and the pitfalls you hit. ``` ## Test my plugin end to end ```text I changed in this plugin. Build it, deploy the jar to my dev server's plugins folder, then with Foreman: mark the server log, reload the plugin (server_reload_plugin), check server_plugins says it runs the new jar, and read the log since the mark. Then in my game (foreman-client tools): , take screenshots, and tell me what works and what doesn't, with the log lines and screenshots that show it. ``` ## Tips - Give the AI the folders it may edit (a chat's workspace in game, the session's folder outside). - Ask it to `learn` rules and `save_skill` workflows that worked: the next session starts from there. - The tool reference ([Tools](tools.md)) and the skills of your server (`/foreman skills`) are what it can do here. --- # Workspaces A workspace is a named set of folders that chats work in: a plugin's configs, its sources, a datapack, a resource pack. Make them in the hub's Workspaces section: 1. Name a workspace and create it. 2. Add its folders (absolute paths). 3. Optionally, write its instructions: how the AI should work there (conventions, what to edit, what not to touch). They're added to the prompt of every chat using it. 4. Mark it **default** to give it to every new chat. A chat can use several workspaces, and can have extra folders of its own (in the chat's folder panel). !!! tip For the packet viewer's *Ask the AI*, make a default workspace with your plugins' sources and your server's configs and language files. That's where the AI searches for what sends the traffic. !!! note "Sandboxed launchers" A folder the game can't see (Flatpak launcher) is shown in yellow. Claude Code and opencode still use it, because they run outside the sandbox. See [Flatpak launchers](../getting-started/flatpak.md). --- # Backends ![The hub's Agents page](../assets/screenshots/agents.png) Pick the backend of new chats in the hub's **AI → Agents**, with its settings under it. Each chat keeps the backend it started with. === "Claude Code" Runs `claude` on your computer with your own login, in print mode, with two MCP servers: `client` (this client's tools) and `minecraft` (the Foreman server, when there is one). Only their tools are allowed, plus Claude Code's own file tools on the chat's folders. Settings: the command (default `claude`) and the model (empty: Claude Code's default). === "opencode" Runs `opencode run` on your computer with the same two MCP servers, and its file tools on the chat's folders. Settings: the command (default `opencode`) and the model. === "OpenRouter" Talks to OpenRouter from the game, with your API key. No program to install, and no Flatpak permission needed. Settings: the API key and the model (default `anthropic/claude-sonnet-4.5`). ## Other settings - **Instructions:** added to every prompt (house rules, language…). - **Server MCP URL:** the address of the Foreman plugin's MCP endpoint, when the one the server announces is wrong for your computer (see the server's `mcp.public-url`). Empty: the server's. All of them are stored in `config/foreman/settings.json`, readable by you only. --- # Outside agents (client MCP) The client runs a small MCP server on your computer (loopback only). Chats use it, and when you allow it, so can **AI sessions outside the game**: Claude Code in a terminal, or another agent on this computer. That's how you implement fast and test end to end: the agent edits your plugin in your editor, deploys it to your dev server, then plays the change in the running client (screens, commands, entities, items, packets, HUD) and reads what happened. ## Turning it on 1. In the hub's **AI → Agents**, allow agents outside the game. 2. Optionally forbid them to run commands as you, or to take screenshots. 3. Copy the `claude mcp add` command and run it once in the folder Claude Code works in. The address and a token are also written to `config/foreman/client-mcp.json` (readable by you only), for other agents: ```json {"url": "http://127.0.0.1:25601/mcp/external", "token": "…", "claude": "claude mcp add --transport http foreman-client …"} ``` The port stays the same from one launch to the next (`25601`, or another free one when it's taken). ## While an agent works While a session uses the tools (a call in the last 5 minutes), the game doesn't pause when its window loses focus, so screenshots and clicks keep working while you're in the terminal. Idle again, it pauses as usual. Outside sessions get no workspace folders: they have their own file tools. ## Example ```text > Open /shop as me, click the second item, and tell me whether the price in the lore matches the config. ``` The agent calls `client_run_command`, `client_screen`, `client_click`, `client_item`, then reads your plugin's config with its own tools. --- # Tool reference Every tool the AI can use, by provider. In game, the hub's AI section (Tools) lists them with their full descriptions; `/ai tools` prints them to chat, and `/foreman tools [filter]` on the server lists the server's. ## Client tools Available on any server, to chats and to [outside agents](outside-agents.md). ### Seeing and acting | Tool | Does | |---|---| | `client_context` | Where the player is and looks, the block or entity at the crosshair, their selection, dimension, the last chat lines | | `client_screenshot` | What the player sees, as an image (needs Vision) | | `client_run_command` | Runs a command as the player and returns what the server printed (needs Commands) | | `client_teleport` | Teleports the player (a `/tp` as them) | | `client_look` | Turns the camera to a point, or a yaw and pitch | | `client_chat_log` | The last chat and system messages | | `client_log` | The client's recent log lines, filtered by level and text | ### Screens | Tool | Does | |---|---| | `client_screen` | The open screen: its class and title, the GUI size, each widget with its text and bounds | | `client_click`, `client_hover`, `client_scroll` | Use the open screen like the mouse | | `client_drag` | Hold a mouse button and move: across a screen (scrollbars, sliders), or turning the camera in the world (gizmos, mining) | | `client_type`, `client_key` | Type into the focused field; press a key binding | | `client_dialog_preview` | Opens a dialog on the client alone from its JSON, as the server would send it | | `inventory_snapshot`, `inventory_actions` | Read the open container, and click it like a player (bursts in one tick, optionally under a network profile or inbound packet drop, which need `foreman.test`) — [dupe testing](../server/dupe-testing.md) | ### Entities, items, resources | Tool | Does | |---|---| | `client_entities` | The entities around the player or along the look, with their synced data | | `client_item` | An item the player has, with its components, as the client sees it | | `client_resource_packs` | The loaded resource packs, bottom to top | | `client_post_effects` | The post effects (full-screen shaders) in use | ### Packets | Tool | Does | |---|---| | `packets_record` | Starts or stops a recording | | `packets_summary` | Totals each way, the busiest second, the types, entities and chunks that get the most | | `packets_suspects` | The traffic grouped by what likely sends it, with clues to search for | | `packets_list` | Kept packets one by one, filtered | | `packets_timeline` | Packets and bytes per time bucket, with the peaks | | `packets_export` | Writes the recording to a file | ### HUD `ui_capture`, `ui_watch`, `ui_measure`, `ui_glyphs`, `ui_check_widths`: see [HUD inspector](../client/hud-inspector.md). ### Files (chats only) `client_list_files`, `client_read_file`, `client_write_file`, `client_search_files`: the chat's workspace folders. Claude Code and opencode also have their own file tools on the same folders; OpenRouter chats rely on these. ## Server tools Available where the Foreman plugin runs. ### Dev server `server_command`, `server_log`, `server_log_mark`, `server_plugins`, `server_status`, `server_profile`, `server_listeners`, `server_reload_plugin` (with PlugMan or PlugManX). See [Dev-server tools](../server/dev-tools.md). ### Skills `read_skill` (one skill, or the list), `save_skill` (a skill for the server, from a chat). See [Skills](../server/skills.md). ### Testing (dupe hunting) `item_ledger` (count an item everywhere, with the change since last time), `inventory_watch` (the inventory events a test fires and who handles them), `test_restore` (save/restore inventories). Need `foreman.test`. See [Dupe testing](../server/dupe-testing.md). ### Other plugins | Plugin | Tools | |---|---| | ModelEngine | `modelengine_models`: the loaded models, their bones, hitboxes and animations | Most plugins need no tool of their own: a [skill](../server/integrations.md) teaches the AI their commands, which it runs with `server_command` (or `worldedit`). ### World | Group | Tools | |---|---| | Inspect | `get_context`, `read_style_guide`, `scan_area`, `read_blocks`, `find_blocks`, `render`, `world_profile`, `set_mark` | | Quality | `check`, `look`, `measure_site`, `claim` | | Edit | `edit`, `worldedit` (any WorldEdit command on a box, undoable), `transform_region`, `undo` | | Terrain | `foundation`, `blend_terrain`, `smooth_terrain`, `make_ground`, `grade`, `build_path`, `make_fields` | | Buildings | `build_structure`, `demolish`, `list_styles`, `points` | | Repair | `connect_doors`, `repair_terrain` | | Assets | `list_assets`, `index_assets`, `preview_asset`, `scatter_assets`, `paste_asset`, `save_asset` | | Models | `preview_model` | | Furniture | `list_furniture`, `place_furniture`, `remove_furniture` (with a [furniture provider](../developers/furniture.md)) | | Lessons | `learn`, `lessons` | See [World tools](../server/world-tools.md). ### Your plugins' tools Other plugins publish their own with [`ForemanTools`](../developers/foreman-tools.md). They're listed with their plugin as the provider. --- # Server plugin The Paper plugin runs an MCP server inside your Minecraft server. The client mod's chats connect to it, and so can any MCP client (Claude Code, opencode…). It brings: - [Dev-server tools](dev-tools.md): commands with everything they printed, the log, stale jars, a profiler, event listeners, plugin reloads. - [World tools](world-tools.md): read and edit the world, terrain and roads, buildings from [templates](building-templates.md), checks, undo. - [Dupe testing](dupe-testing.md): count an item everywhere, run inventory attacks against a plugin, and catch the ones that duplicate items (needs `foreman.test`). - [World context](world-context.md): each world's profile, named areas and points, kept in the world. - Tools other plugins publish through [`ForemanTools`](../developers/foreman-tools.md). Start with [Setup and configuration](setup.md) and [Permissions and tokens](permissions.md). --- # Setup and configuration 1. Put `foreman-server-1.0.0.jar` in `plugins/` (with FastAsyncWorldEdit for the world tools) and start the server. The log shows the MCP address, `http://127.0.0.1:25599/mcp` by default. 2. Join with the client mod and press ++y++, or connect another MCP client with the token Foreman made in `plugins/Foreman/mcp-token.txt` (from the server's folder): ```sh claude mcp add --transport http minecraft http://127.0.0.1:25599/mcp \ --header "Authorization: Bearer $(cat plugins/Foreman/mcp-token.txt)" ``` 3. For players on other machines, set `mcp.host: 0.0.0.0`. Each player with `foreman.client` gets their own token through the mod. The MCP server speaks plain HTTP: beyond a network you trust, put it behind TLS (a reverse proxy) or a VPN. ## config.yml ```yaml mcp: # Bind to localhost unless the AI client runs on another machine. The server speaks plain HTTP: beyond this # machine, put it behind TLS (a reverse proxy) or a VPN. host: 127.0.0.1 port: 25599 # Clients send "Authorization: Bearer ". Empty: the token in mcp-token.txt (this folder), made at the first # start. Players with foreman.client also get their own through the mod (tokens.yml), accepted besides this one. token: "" # The URL the mod gives its AI. Default: http://:/mcp, with the server's IP when host is 0.0.0.0. # Set it when players reach the server through another address. public-url: "" # World used when a tool call names none. Empty: the world of the first online player. default-world: "" limits: max-edit-blocks: 1000000 max-read-blocks: 20000 max-render-blocks: 800000 # Assets for the textured renders (look, model previews). The client jar is downloaded from Mojang into cache/ # unless client-jar is set; packs are the server's resource packs (zips or folders), later ones winning. resources: client-jar: "" packs: [] ``` ## The plugin's folder | Path | What | |---|---| | `config.yml` | The settings above | | `mcp-token.txt` | The MCP token, when `mcp.token` isn't set (readable by the server's user only) | | `tokens.yml` | Players' MCP tokens (readable by the server's user only) | | `catalog/` | [Building templates](building-templates.md): `rooms.yml`, `buildings.yml`, `furniture.yml` | | `styles/` | [Style guides](building-templates.md#style-guides) for the AI | | `assets/` | The [schematic library](assets.md) | | `prompts/` | Ready-made tasks ([prompts](lessons-prompts.md#prompts)) | | `lessons.md` | What the AI learned on this server ([lessons](lessons-prompts.md#lessons)) | | `worlds/` | World data exported with `/foreman world export` | | `cache/` | The client jar used for textured renders | ## Commands `/foreman` (alias `/fm`, permission `foreman.use`): | Command | Does | |---|---| | `/foreman status` | The MCP address, tools, assets, areas and undoable changes | | `/foreman tools [filter]` | Every MCP tool by provider (Foreman's and other plugins'), those that edit in gold | | `/foreman mark [note]` | Name your WorldEdit selection (or where you stand) as an area | | `/foreman unmark `, `/foreman marks` | Remove an area, list them | | `/foreman undo [count]` | Revert the last AI changes | | `/foreman asset save [tags…]` | Save your WorldEdit selection to the schematic library | | `/foreman token revoke ` | Revoke a player's token (a new one comes on their next join) | | `/foreman world show \| export \| import [world]` | A world's profile and areas, as YAML in `worlds/` | --- # Permissions and tokens | Permission | Gives | Default | |---|---|---| | `foreman.use` | The `/foreman` command | ops | | `foreman.client` | Being connected to the server's tools when joining with the mod (a personal token) | ops | | `foreman.admin` | With a personal token: the [dev-server tools](dev-tools.md), `learn` and removing lessons, `save_skill`, `worldedit` commands other than block edits (WorldGuard, schematics), plugins' tools that run console commands | ops | | `foreman.test` | The [dupe-testing](dupe-testing.md) tools (item ledger, inventory watch and restore), and the mod's packet options for them (`network`, `drop`) | ops | | `foreman.watch` | A chat line for each change an AI makes to the world, with who asked | ops | ## Tokens - **Personal tokens:** each player with `foreman.client` gets one through the mod when they join, kept in `tokens.yml`. Calls with it act as that player: the tools above then need `foreman.admin`. A player who loses `foreman.client` can't use theirs any more, and it's revoked when they next join. `/foreman token revoke ` revokes one; a new one comes on their next join. - **The server's token** (`mcp.token`, or `mcp-token.txt` that Foreman makes when it's empty): for other MCP clients (Claude Code in a terminal, scripts). It needs no permission: it's the console's. Every request needs one of them, on localhost too. The MCP server refuses requests from web pages (with an `Origin` header) so that a site open in your browser can't reach it. !!! warning `server_command` runs commands as the console. Treat `foreman.admin` and the server's token like console access. More in [Security and privacy](../security.md). --- # Dev-server tools For an AI (or you) working on plugins against a dev server: they replace RCON, log files and guesses. With a personal token, the player needs `foreman.admin` (ops have it). *A chat fixing a plugin's bug with them: `server_log` for the stack trace, `server_log_mark` and `server_reload_plugin` after the fix, then the command again as the player ([the run](../ai/examples.md#fix-a-plugins-bug)).* | Tool | Does | |---|---| | `server_command` | Runs a command on the server thread and returns everything it printed: the replies, the log lines logged meanwhile (exceptions with their frames), that it's unknown (with close names) or its syntax is wrong. As the console, or `as_player`. `wait_for` a text when it finishes later | | `server_log` | The log kept in memory (the last 10,000 lines): by `level`, `plugin`, `contains`, `since` start, the last reload or a mark. Repeated lines are folded, exceptions show their causes and the frame they came from | | `server_log_mark` | Marks the log now, to read only what follows (before a deploy or a test) | | `server_plugins` | Each plugin's version, jar and dependencies, and whether the server runs the jar on disk: **STALE** (with the classes that differ) or **GONE**. Then the jars nothing loaded | | `server_status` | Version, uptime, TPS, MSPT, memory, CPU, GC, players, worlds (chunks, entities, block entities), tasks by plugin | | `server_profile` | A sampling profiler of the server thread (or other threads): each plugin's share, how the server entered it, its hottest lines | | `server_listeners` | An event's listeners in call order (priority, plugin, method, ignores cancelled), or the events a plugin handles | | `server_reload_plugin` | Reloads a plugin through PlugMan or PlugManX and reports how it went (published only while one of them runs) | ## A deploy loop 1. `server_log_mark name=before-deploy` 2. Build, copy the jar, `server_reload_plugin` (or restart). 3. `server_plugins stale_only=true`: is the server running the new jar? 4. `server_command` the feature, `server_log since=before-deploy` for what went wrong. ## Stale plugin jars `server_plugins` compares the jar each plugin's class loader opened (its entries as they were when it loaded, even after the file was replaced) with the file on disk now, and names the classes that differ. When the class loader can't be read, it falls back to the size, time and hash recorded when Foreman saw the plugin enable. !!! tip Replace a jar by writing the new one beside it and moving it over: copying onto a loaded jar breaks the running plugin. --- # Dupe testing Item duplication is the bug that wrecks an economy, and it almost always comes from a plugin mishandling an inventory. Foreman turns a dupe hunt into something you run on purpose: **count every item on the server, do the suspect action, count again.** A bigger total is a dupe, and the tools tell you which action and which plugin caused it. It's the same method an attacker uses to find a dupe — run on your own server, before they do. ## The tools | Tool | Where | Does | |---|---|---| | `item_ledger` | server | Counts an item everywhere — every player's inventory, ender chest and cursor, containers, item frames, armor stands, dropped items and entities with inventories (mules, chest/hopper minecarts) — and reports the total, a per-place breakdown, and the change since you last ran it | | `inventory_watch` | server | Records the inventory events over a few seconds and whether each ended cancelled, with the plugins whose listeners are in the chain | | `test_restore` | server | Saves and restores players' inventories, so each attempt starts the same | | `inventory_snapshot` | client | The open container exactly as the client holds it: slots, cursor, sync state id | | `inventory_actions` | client | Performs a sequence of inventory clicks like a player — shift-click, number/offhand swap, middle-click clone, drop, double-click collect — including several in the same client tick | ## The loop An AI chat with the `dupe-testing` skill does this for you; the prompt **find_dupes** kicks it off (`/foreman` → prompts, or ask a chat "find dupes in ``"). By hand: 1. `item_ledger` the item (`held`, or a material), `reset: true` — the baseline. 2. `test_restore` `save` a label. 3. `inventory_watch` on, then do the suspect action with `inventory_actions` (e.g. two `pickup` on one slot, `per_tick: 2` — grab and replace in one tick). 4. `item_ledger` again: `+n` is duplication, and the breakdown says where the copies went. 5. `test_restore` `restore` and repeat to confirm. The dupe classes to try — same-tick in/out, shift-spam, number/offhand swaps in menus, double-click collect, drop-while- reacting, mules and minecarts, reconnect with a cursor item, sell-twice — are in the skill. ## Desync (lag) dupes The strongest dupes need the client and server to disagree for a tick, the way added latency does. The mod ships two kinds of [network profile](../developers/network-profiles.md): `burst` holds the client's sends and sends them together when the burst ends, and `ping_` (`ping_100`, `ping_300`, `ping_500`, `ping_1000`) adds n ms to each. Name one as `network` on `inventory_actions` and it is held around the burst; without one, the bursts still run at normal timing and catch every logic dupe above. A `drop` list on `inventory_actions` ignores those server packets for the burst's duration (`setslot`, `containerset`): the client keeps acting on the slots it has while the server has already changed them. The drop stays armed for `drop_ms` after the last click (default 400) so the server's reply, which arrives a round-trip later, is caught. `network` and `drop` need the server's opt-in, like the server tools: the Foreman plugin and `foreman.test`. Elsewhere `inventory_actions` refuses them, and its plain bursts still run. ## Scope - The server tools need the Foreman plugin and `foreman.test` (ops by default); they read and restore inventories and count items. - The client tools replay the clicks a player already makes, through the normal click path, on any server. Their packet options (`network`, `drop`) only run where the Foreman plugin gives you `foreman.test`. - The desync layer is a [`NetworkProfile`](../developers/network-profiles.md): the mod ships `burst` and `ping_`, and a dev build can register its own. ## Keep the wins Each dupe you patch becomes a test you keep: save the steps as a chat prompt or a note, and re-run them after every plugin update. The ledger is a regression guard, not a one-off. --- # Skills A skill teaches the AI how to do one kind of task on your server: a plugin's commands and conventions, a workflow of yours ("open the market for the day", "set up a new arena"). It's a markdown file. The AI sees every skill's name and one-line description in its instructions, and reads the whole skill (`read_skill`) only when a request matches it. A skill costs one line until it's needed, where a tool's definition is sent with every request. That's how Foreman supports many plugins without slowing the AI down or using more tokens: the skill explains the commands, and the AI runs them with `server_command` (or `worldedit` for WorldEdit). `/foreman skills` lists the skills of your server; the hub's AI → Tools page shows them too. ## Where skills come from Later ones win on a name, so you can replace any skill with your own version. | Source | What | Who | |---|---|---| | Foreman | Skills for [popular plugins](integrations.md), offered while their plugin runs | Foreman | | A plugin's jar | `foreman/skills/.md` files in the plugin, offered while it runs: no code | [Plugin authors](../developers/skills.md) | | A plugin's code | `ForemanTools.registerSkill(…)` | [Plugin authors](../developers/skills.md) | | Your server | `plugins/Foreman/skills/.md` | You, or the AI with `save_skill` | ## Writing one Drop a file in `plugins/Foreman/skills/`, for example `arena-setup.md`: ```markdown --- name: arena-setup description: Set up a new PvP arena (region, spawns, kit, signs). Read when asked to create or reset an arena. plugin: WorldGuard --- # Arena setup 1. Define the region with the worldedit tool on the arena's box: `/rg define arena-`. 2. Flags: `/rg flag -w arena- pvp allow`, `... build deny`, `... item-drop deny`. 3. Spawns: two points 30 blocks apart on the arena floor, saved with `/arena setspawn 1|2` as a player. 4. Kit: `/kit arena ` gives the default kit; never give armor above iron. 5. Check: walk it with `check`, then `look` from both spawns. ``` - **`name`:** lowercase letters, digits and `-`; the file name when it's missing. - **`description`:** when to read it. It's what the AI chooses by, so say what it's for and when it applies. - **`plugin`** (optional): the skill is offered only while that plugin is enabled. - **The body:** the how. Commands with their exact syntax, the order of steps, what to check, the pitfalls. Short and concrete beats long and general. The next AI session sees it; there's nothing to reload. ## Letting the AI write them Tell a chat what you want it to remember ("save how we set up arenas as a skill"): it writes the file with `save_skill` (players need `foreman.admin`). Use `learn` instead for one-sentence rules. ## Skills or tools? A skill is enough when the plugin has commands for what the AI needs, and most plugins do. A tool is worth writing when commands fall short: reading structured data no command prints (ModelEngine's models), an action that needs code, or an answer the AI would otherwise have to piece together from many commands. See [ForemanTools](../developers/foreman-tools.md). --- # Plugin integrations Foreman works with other plugins through [skills](skills.md): for each plugin below, a skill teaches the AI its commands, files and pitfalls, and the AI runs them with `server_command`. Each skill is offered only while its plugin is enabled, and costs one line of the AI's instructions until a request needs it. A few plugins also get code tools, where their commands fall short. Nothing to configure: install the plugin, and its skill is there the next time the AI connects. | Plugin | What the AI can do | How | |---|---|---| | [FastAsyncWorldEdit](https://modrinth.com/plugin/fastasyncworldedit) | Any WorldEdit command on a box in one call: replace with mixes, overlay, smooth, naturalize, set biomes, regenerate. Undoable | `worldedit` tool + skill. It also powers the [world tools](world-tools.md) | | [WorldGuard](https://enginehub.org/worldguard) | Define and redefine regions, flags, priorities, members, the global region; find what blocks a player | Skill (`/rg`, regions defined on a box through `worldedit`) | | [MythicMobs](https://mythiccraft.io) | Write mobs, skills and items, reload, spawn and test them, read config errors | Skill (`/mm`, the YAML files) | | [ModelEngine](https://mythiccraft.io) | List models with their bones, hitboxes and animations; put them on mobs; test them | `modelengine_models` tool + skill | | [LuckPerms](https://luckperms.net) | Find why a player can or can't do something, give and remove permissions, groups, prefixes | Skill (`/lp`, verbose checks) | | [PlaceholderAPI](https://placeholderapi.com) | Check what a placeholder returns for a player, install expansions, fix placeholders shown raw | Skill (`/papi`) | | [CoreProtect](https://www.spigotmc.org/resources/coreprotect.8631/) | Find who changed, broke or took what, roll back and restore | Skill (`/co`). CoreProtect 24.1 doesn't run on 26.3 yet | | [EssentialsX](https://essentialsx.net) | Warps, spawn, kits, economy, its configs | Skill | | [Citizens](https://citizensnpcs.co) | Create, move, skin and script NPCs from the console, text above them | Skill (`/npc … --id`) | | [TAB](https://modrinth.com/plugin/tab-was-taken) | Tab list, name tags, prefixes, header/footer; why a value applies | Skill (`/tab parse`, `/tab group`) | | [Skript](https://modrinth.com/plugin/skript) | Write and fix scripts, read their errors on reload | Skill (`/sk reload`) | | [PlugMan / PlugManX](https://modrinth.com/plugin/plugmanx) | Reload a plugin and report how it went | `server_reload_plugin` tool | ## An example: replacing blocks > In the box from -60,60,160 to -50,95,170, replace all grass with 70% moss and 30% grass. The AI makes one call, `worldedit` with `//replace grass_block 70%moss_block,30%grass_block` on that box, and reports the count. `undo` puts the box back. Without WorldEdit it would have to read and rewrite the blocks itself. ## Your plugin, or one that's missing - **Your own plugin:** ship a skill in its jar, or register tools and skills from code: [Skills for your plugin](../developers/skills.md), [ForemanTools](../developers/foreman-tools.md). - **A plugin you use:** write a skill for it in `plugins/Foreman/skills/` ([Skills](skills.md)), or ask a chat to write it after it worked something out ("save how to do this as a skill"). - **For everyone:** skills for more plugins are welcome as pull requests: a markdown file in `server/src/main/resources/skills/`, tested on a server with that plugin ([Contributing](https://github.com/qore-games/foreman/blob/main/CONTRIBUTING.md)). --- # World tools With the plugin and [FastAsyncWorldEdit](https://modrinth.com/plugin/fastasyncworldedit), the AI can read and edit the world. It looks before it builds, works in steps, checks its work the way a player would walk it, and every change can be undone. Without FastAsyncWorldEdit these tools are off (`/foreman status` says so), and so are the areas, overlays and furniture that go with them. | | Tools | |---|---| | **Looking** | `get_context` (players, selections, areas, the world's profile, limits), `scan_area`, `read_blocks`, `find_blocks`, `render` (top, iso, plan, slope views), `look` (first person, textured) | | **Editing** | `edit` (block operations: fills, shapes, roofs, doors, stairs, weathering, plants, trees…, all undone together), `transform_region` (move or copy, rotated) | | **Terrain** | `make_ground`, `grade`, `foundation`, `blend_terrain`, `smooth_terrain`, `make_fields`, `build_path` (walkable roads over the relief) | | **Buildings** | `build_structure` (a whole building from [templates](building-templates.md)), `demolish` (puts the land back), `list_styles` | | **Library** | `list_assets`, `preview_asset`, `paste_asset`, `scatter_assets`, `save_asset` ([schematic library](assets.md)) | | **Checking** | `check`, `measure_site`, `repair_terrain`, `connect_doors` | | **Context** | `set_mark`, `claim`, `world_profile`, `points` ([world context](world-context.md)) | | **Undo** | `undo` reverts AI changes, furniture included; `/foreman undo [count]` does too | ## check `check` walks an area like a player and reports, with coordinates: unreachable rooms, seats and containers, steps that need a jump, blocked doors, wrong double-door hinges, furniture in front of doors or inside walls, sunk or floating builds, cliffs, steep or broken roads, floating blocks and overlapping claims. The client draws what it found as the *Check issues* overlay. ## Watching the AI Ops with `foreman.watch` see each change in chat (the tool, who asked, how long it took). The client draws the box the AI just changed as the *Last AI change* overlay. ## Furniture Foreman has no furniture of its own. A furniture plugin can provide it by registering a [`FurnitureProvider`](../developers/furniture.md): the AI then gets `list_furniture`, `place_furniture` and `remove_furniture`, and generated buildings are furnished with it. Notes in `catalog/furniture.yml` are shown to the AI next to each furniture id. ## Limits `limits` in `config.yml` caps the blocks one call may edit, read or render; `get_context` tells the AI, which splits large edits. *A market square built from one prompt, with the overlays of the AI's changes ([the run](../ai/examples.md#build-a-place)).* ![The square with Foreman's overlays: its area, the path's claim, the last change](../assets/screenshots/ai-build-overlays.jpg) === "`render` iso" ![render view=iso_se of a town square](../assets/screenshots/world-render-iso.png) === "`look`" ![look: a textured first-person view, drawn by the server](../assets/screenshots/world-look.jpg) === "`render` top" ![render view=top: the same square from above, 11 px per block](../assets/screenshots/world-render-top.png) The AI sees what it builds without a game client: `render` draws the blocks of a box from above, in plan or in isometric views, `look` draws a first-person view with the game's textures. --- # Building templates `build_structure` builds a whole building in one call: site levelling, foundation, walls, storeys, rooms and doors, walkable stairs, roof, furniture by room type, then a `check` of the result. Its layouts are built in (the archetypes); what goes into them comes from templates you can edit: | Template | What | Where | |---|---|---| | **Styles** | Material presets: wall, trim, roof, doors… | `catalog/buildings.yml` `styles:` | | **Uses** | The room types of a building use, in order | `catalog/buildings.yml` `uses:` | | **Rooms** | What each room type gets: furniture, blocks, points | `catalog/rooms.yml` | | **Style guides** | Free text the AI follows in a zone | `styles/.md` | `list_styles` shows the result in a world: every style, use and room type with what it gets. ## Where they're defined Later ones win, by name: 1. Foreman's bundled defaults, neutral on purpose (styles `brick`, `wood`, `stone`, `modern`, `industrial`; uses `office`, `school`, `hotel`, `apartment`, `dormitory`, `shop`, `workshop`, `basement`). 2. The server's `plugins/Foreman/catalog/buildings.yml` and `catalog/rooms.yml`. Foreman writes them with commented examples on first start. 3. A world's profile (`world_profile`: `styles`, `uses`, `rooms`), kept in the world. See [World context](world-context.md). 4. One building's arguments: `style` + `materials`, `use` or `rooms`, `catalog`. You can also ask the AI to write them: "add a timber style and a library use to the catalog". ## Archetypes | Archetype | Layout | |---|---| | `corridor` | Rooms on both sides of a 3-wide corridor, stair halls at the ends (depth 19 or more) | | `sections` | An apartment block: `sections` stair cores, each between two flats of `flat_width` | | `hall` | One large room | | `house` | Rooms of 6 or more around the stairs (13 × 13 or more) | Other arguments: `floors`, `floor_height`, `roof` (flat, gable, hip), `basement`, `room_width`, `furnish`, `connect` (paths to the nearest roads), and the optional `decay` (wear, broken windows, vines), `dark` (boarded windows) and `clutter` (debris that breaks sightlines, a path kept free). ## Styles ```yaml title="plugins/Foreman/catalog/buildings.yml" styles: timber: extends: wood # start from wood, give only what changes wall: white_terracotta trim: dark_oak_log pillar: dark_oak_log roof_stairs: dark_oak_stairs sandstone: extends: stone wall: smooth_sandstone trim: cut_sandstone base: sandstone roof_stairs: sandstone_stairs parapet: smooth_sandstone_slab[type=bottom] ``` Keys: `wall`, `trim` (floor edges, corners), `base` (foundation, bottom row), `floor`, `slab` (between storeys, flat roofs), `inner` (inner walls), `paint` (bottom row of the rooms' walls), `roof`, `roof_stairs` (gable and hip roofs), `parapet` (flat roofs, optional), `window`, `door`, `entrance` (front doors), `stairs`, `pillar` (corners, optional), `railing`. Values are block states; `none` removes an optional one. A style without `extends` starts from `brick`. ## Uses ```yaml title="plugins/Foreman/catalog/buildings.yml" uses: library: [reading, reading, office, storage] tavern: [dining, kitchen, storage, bedroom, bedroom] ``` Rooms get the types in order, repeating. `basement` is the use of basements. ## Rooms ```yaml title="plugins/Foreman/catalog/rooms.yml" reading: - {item: "block:bookshelf", count: 3-5} - {item: "block:lectern[facing={facing}]"} - {item: "block:oak_stairs[facing={back}]", count: 2-3, place: center} - {item: "block:lantern", chance: 0.5} guardroom: - {item: "block:barrel[facing={facing}]", count: 1-2, point: "container:supplies"} - {point: "spawn:guard", count: 1-2} clutter: - {item: "block:barrel[facing=up]"} - {item: "block:hay_block"} ``` | Key | | |---|---| | `item` | `furniture:` or `block:`. `{facing}` is the side its front looks to, `{back}` the wall side. `a\|b\|c` after the prefix picks one per piece | | `count` | `n` or `a-b` (default 1), for a room of about 50 cells: bigger rooms get more | | `chance` | 0..1 for each one (default 1) | | `place` | `wall` (default: back against a wall, clear of doors and windows) or `center` | | `point` | `:`: records a [point](world-context.md#points) there for gameplay plugins. Without `item`, on a free floor cell | Room types are free names. The archetypes use `living`, `kitchen`, `lobby`, `corridor`, `stairwell` and `hall`; uses name the others. `clutter`, when defined, lists the blocks `clutter` scatters. ## Style guides `styles/.md` is free text the AI reads (`read_style_guide`) before building where it applies: the mood, the palette as block ids, sizes in blocks, rules, and the assets and furniture that belong there. Foreman writes `styles/example.md` as a template on first start. Name the guide in a world's profile or a zone's theme so the AI knows when to read it. ```markdown title="plugins/Foreman/styles/harbor.md" # Harbor district ## Palette - Foundations: `cobblestone`, `mossy_cobblestone` - Walls: `spruce_planks` with `stripped_spruce_log` frames - Roofs: `dark_oak_stairs`, 1 block overhang ## Rules - Houses 5–9 blocks wide, 1–2 floors, raised 1 block on a stone base - Every door has a path to the quay ``` --- # World context Each world carries its own settings, saved in the world itself (its persistent data), so they travel with the world file. The AI reads them with `get_context`; your plugins read them with [`ForemanWorlds`](../developers/foreman-worlds.md). ## Profile The look and rules the AI follows in a world, and the defaults of generated buildings. Without one it builds neutrally. Set it with the `world_profile` tool (ask the AI), which merges fields in: | Field | What | |---|---| | `theme` | Text: the look, palette, era, level-design rules | | `build` | Defaults of `build_structure` (`style`, `roof`, `decay`, `clutter`, `dark`, `furnish`, `room_width`, `floor_height`, `basement`…) | | `styles`, `uses`, `rooms` | [Building templates](building-templates.md) for this world only | ```yaml theme: |- A coastal trading town, late medieval. Timber-framed houses on stone bases, narrow streets that follow the relief, a market square by the harbor. Read the style guide `harbor` before building near the water. build: style: timber roof: gable ``` ## Areas Named places ("town_hall", "east_gate"), made with `set_mark`, a tool's `mark` argument or `/foreman mark `: - **Claims** reserve space for a build, a road or a yard (`claim`, or the `claim` argument of the tools that build), so later edits keep off it. - **Zones** are areas with a `theme`: their own building rules, on top of the world's profile. The client draws them as the *Areas* overlay, and can tell you when you enter or leave one. ## Points Gameplay spots that generated buildings leave behind, from the `point` entries of the [room templates](building-templates.md#rooms): containers, spawns… with free type and kind names. The `points` tool lists, exports (JSON), adds and removes them; the client draws them as the *Points* overlay; your plugins read them (fill containers, spawn NPCs) with `ForemanWorlds`. ## Export and import `/foreman world export [world]` writes the profile, areas and points to `plugins/Foreman/worlds/` as YAML, and `/foreman world import [world]` loads them back. `/foreman world show` prints the profile. --- # Schematic library `plugins/Foreman/assets/` holds your schematics for the AI: houses, stalls, trees, rocks, ruins… WorldEdit's schematics (`FastAsyncWorldEdit/schematics`) are listed too. | | | |---|---| | `/foreman asset save [tags…]` | Saves your WorldEdit selection | | `save_asset` | The AI saves a box of the world | | `index_assets` | Reads new or changed schematics: size, main blocks, a guessed category (tree, plant, rock, ruin, structure) | | `list_assets`, `preview_asset` | Browse them, render one from four sides | | `paste_asset` | Paste one, its bottom center at a position | | `scatter_assets` | Scatter some over an area, each on the ground, spaced and turned | Schematics can sit in subfolders (`houses/small_cottage`); folders starting with `_` are left out of listings but can still be pasted by name. WorldEdit's own schematics are listed as `fawe/`, read only. A `.yml` next to a schematic describes it for the AI: ```yaml title="plugins/Foreman/assets/houses/small_cottage.yml" description: A one-room cottage with a chimney, door to the south tags: [house, rural] category: structure ``` --- # Lessons and prompts ## Lessons `plugins/Foreman/lessons.md` holds what the AI learned on your server, one `- ` line each: a mistake the checks missed, a rule of a zone, a tool trick. The AI records them with `learn` when you correct it, reads them at the start of every session, and `lessons` lists or removes them. Edit the file by hand too. Every later session follows them, the console's too, so writing and removing lessons needs `foreman.admin` (or the server's token). A lesson is one sentence of at most 300 characters, and a server keeps at most 100. ## Prompts MCP prompts are ready-made tasks with arguments. Foreman ships `build_place` (build something at a place, end to end, until `check` reports no errors), `review_area` (review an area like a player and a level designer, changing nothing) and `repair_area` (find and fix what's wrong, checking again after each round). Add yours, or replace these, as `.md` files in `plugins/Foreman/prompts/`: a YAML header between `---` lines, then the text. `{{argument}}` is replaced by the argument, `{{lessons}}` by the server's lessons. ```markdown title="plugins/Foreman/prompts/test_command.md" --- description: Test a plugin command end to end and report what broke arguments: - {name: command, description: "The command to test, without the slash", required: true} --- Mark the server log, run `{{command}}` with server_command, then read the log since the mark. Report the replies, every warning and error with the plugin and line it came from, and a fix. Lessons learned on this server: {{lessons}} ``` --- # For developers | You want to | Use | Needs the Foreman plugin | |---|---|---| | Teach the AI your plugin (its commands, configs) | [Skills](skills.md): markdown in your jar, no code | yes (to be read) | | Give the AI your plugin's tools and commands | [`ForemanTools`](foreman-tools.md) (`foreman-server-api`) | yes | | Show your plugin's locations in the mod and receive moves | [Location API](locations-api.md) (`foreman-paper`) | no | | Read the areas and points Foreman keeps in a world | [`ForemanWorlds`](foreman-worlds.md) (`foreman-server-api`) | yes | | Offer your furniture to the world tools | [`FurnitureProvider`](furniture.md) (`foreman-server-api`) | yes | Start with [Add Foreman to your plugin](install.md): the repository, the dependency for Gradle (Kotlin or Groovy) or Maven, and a first tool or location in Java or Kotlin. --- # Add Foreman to your plugin Foreman publishes three artifacts. Most plugins need one of the first two: | Artifact | Gives you | How you depend on it | Needs the Foreman plugin | |---|---|---|---| | `games.qore:foreman-server-api` | [`ForemanTools`](foreman-tools.md), [`ForemanWorlds`](foreman-worlds.md), [`FurnitureProvider`](furniture.md) | provided (`compileOnly`): the Foreman plugin has the classes | yes | | `games.qore:foreman-paper` | The [location API](locations-api.md): your locations in the mod, staff's edits back as events | shaded into your plugin | no | | `games.qore:foreman-api` | The location protocol (packets, codecs, builders) | comes with `foreman-paper` | no | They're in Foreman's Maven repository, `https://qore-games.github.io/foreman/maven/`. The examples use version `1.0.0`: the latest is on the [releases page](https://github.com/qore-games/foreman/releases). ## The server API ### 1. The dependency === "Gradle (Kotlin)" ```kotlin title="build.gradle.kts" repositories { mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") maven("https://qore-games.github.io/foreman/maven/") } dependencies { compileOnly("io.papermc.paper:paper-api:26.3.build.+") compileOnly("games.qore:foreman-server-api:1.0.0") } ``` === "Gradle (Groovy)" ```groovy title="build.gradle" repositories { mavenCentral() maven { url = 'https://repo.papermc.io/repository/maven-public/' } maven { url = 'https://qore-games.github.io/foreman/maven/' } } dependencies { compileOnly 'io.papermc.paper:paper-api:26.3.build.+' compileOnly 'games.qore:foreman-server-api:1.0.0' } ``` === "Maven" ```xml title="pom.xml" papermc https://repo.papermc.io/repository/maven-public/ foreman https://qore-games.github.io/foreman/maven/ games.qore foreman-server-api 1.0.0 provided ``` Never shade `foreman-server-api`: the Foreman plugin provides its classes, and a copy in your jar would be a different class from the one Foreman registered. ### 2. A soft dependency on Foreman Your plugin keeps working without Foreman; with it, Foreman enables first. === "plugin.yml" ```yaml softdepend: [Foreman] ``` === "paper-plugin.yml" ```yaml dependencies: server: Foreman: load: BEFORE required: false join-classpath: true # lets your plugin see Foreman's classes ``` ### 3. Register your tools Get the service in `onEnable`. It's `null` when Foreman isn't installed: keep everything that touches Foreman's classes behind that check (or in a class you only load then). === "Java" ```java import games.qore.foreman.server.api.ExternalTool; import games.qore.foreman.server.api.ForemanTools; import games.qore.foreman.server.api.ToolReply; public final class MyPlugin extends JavaPlugin { @Override public void onEnable() { if (getServer().getPluginManager().isPluginEnabled("Foreman")) ForemanHook.register(this); } } final class ForemanHook { static void register(MyPlugin plugin) { ForemanTools tools = plugin.getServer().getServicesManager().load(ForemanTools.class); if (tools == null) return; // One of your commands as a tool: the AI gets everything it printed. tools.registerCommand(plugin, "arena_reset", "Resets an arena to its saved state", "arena reset {arena}", Map.of("arena", "Arena id"), false); // A tool of your own. tools.register(plugin, new ExternalTool( "arena_list", "Lists the arenas with their state and player count. Use it before resetting one.", "{\"type\": \"object\", \"properties\": {}}", false, call -> ToolReply.ok(tools.onMainThread(() -> plugin.describeArenas())))); } } ``` === "Kotlin" ```kotlin import games.qore.foreman.server.api.ExternalTool import games.qore.foreman.server.api.ForemanTools import games.qore.foreman.server.api.ToolReply class MyPlugin : JavaPlugin() { override fun onEnable() { if (server.pluginManager.isPluginEnabled("Foreman")) ForemanHook.register(this) } } object ForemanHook { fun register(plugin: MyPlugin) { val tools = plugin.server.servicesManager.load(ForemanTools::class.java) ?: return // One of your commands as a tool: the AI gets everything it printed. tools.registerCommand(plugin, "arena_reset", "Resets an arena to its saved state", "arena reset {arena}", mapOf("arena" to "Arena id"), false) // A tool of your own. tools.register(plugin, ExternalTool( "arena_list", "Lists the arenas with their state and player count. Use it before resetting one.", """{"type": "object", "properties": {}}""", false, ) { ToolReply.ok(tools.onMainThread { plugin.describeArenas() }) }) } } ``` Then `/foreman tools` lists them, and every chat on the server can use them. The rest of the API: [ForemanTools](foreman-tools.md), [ForemanWorlds](foreman-worlds.md), [FurnitureProvider](furniture.md). ## The location API `foreman-paper` runs inside your plugin, so it works on servers without Foreman: shade it into your jar. ### 1. Shade it === "Gradle (Kotlin)" ```kotlin title="build.gradle.kts" plugins { java id("com.gradleup.shadow") version "9.6.1" } repositories { mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") maven("https://qore-games.github.io/foreman/maven/") } dependencies { compileOnly("io.papermc.paper:paper-api:26.3.build.+") implementation("games.qore:foreman-paper:1.0.0") } tasks.shadowJar { // Kotlin's standard library comes from the server (plugin.yml `libraries`), not your jar. dependencies { exclude(dependency("org.jetbrains.kotlin:.*")) } relocate("games.qore.foreman", "com.example.myplugin.foreman") } ``` === "Gradle (Groovy)" ```groovy title="build.gradle" plugins { id 'java' id 'com.gradleup.shadow' version '9.6.1' } repositories { mavenCentral() maven { url = 'https://repo.papermc.io/repository/maven-public/' } maven { url = 'https://qore-games.github.io/foreman/maven/' } } dependencies { compileOnly 'io.papermc.paper:paper-api:26.3.build.+' implementation 'games.qore:foreman-paper:1.0.0' } shadowJar { // Kotlin's standard library comes from the server (plugin.yml `libraries`), not your jar. dependencies { exclude(dependency('org.jetbrains.kotlin:.*')) } relocate 'games.qore.foreman', 'com.example.myplugin.foreman' } ``` === "Maven" ```xml title="pom.xml" games.qore foreman-paper 1.0.0 org.apache.maven.plugins maven-shade-plugin 3.6.2 package shade org.jetbrains.kotlin:* games.qore.foreman com.example.myplugin.foreman ``` `foreman-paper` is written in Kotlin. A Java plugin asks the server for Kotlin's standard library in its `plugin.yml` (Paper downloads it once); a Kotlin plugin already has it. ```yaml title="plugin.yml" libraries: - org.jetbrains.kotlin:kotlin-stdlib:2.4.0 ``` Relocating keeps your copy apart from other plugins' (each plugin registers its own). The relocation is optional. ### 2. Send your locations, receive the edits === "Java" ```java import games.qore.foreman.api.ForemanAPI; import games.qore.foreman.api.data.LocationAction; import games.qore.foreman.api.data.RenderShape; import games.qore.foreman.paper.ForemanPaper; import games.qore.foreman.paper.event.LocationMoveRequestEvent; public final class MyPlugin extends JavaPlugin implements Listener { private final ForemanPaper foreman = new ForemanPaper(); @Override public void onEnable() { foreman.register(this); getServer().getPluginManager().registerEvents(this, this); } /** Sends the spawns to a staff member (on join, and after each change). */ void showSpawns(Player staff) { var builder = ForemanAPI.builder("myplugin", "spawns") .setAction(LocationAction.REPLACE) .setSourceColor(0x55FF55) .setDefaultDisplay(ForemanAPI.displayConfig().shape(RenderShape.FLAG).glow(true).build()); for (Spawn spawn : spawns) builder.addPreciseLocation(spawn.id(), spawn.world(), spawn.x(), spawn.y(), spawn.z()); foreman.sendLocationData(staff, builder.build()); } @EventHandler public void onMove(LocationMoveRequestEvent event) { if (!event.getSource().equals("myplugin")) return; if (!event.getPlayer().hasPermission("myplugin.edit")) { event.setCancelled(true); return; } moveSpawn(event.getLocationId(), event.getWorld(), event.getX(), event.getY(), event.getZ()); showSpawns(event.getPlayer()); } } ``` === "Kotlin" ```kotlin import games.qore.foreman.api.ForemanAPI import games.qore.foreman.api.data.RenderShape import games.qore.foreman.paper.ForemanPaper import games.qore.foreman.paper.event.LocationMoveRequestEvent class MyPlugin : JavaPlugin(), Listener { private val foreman = ForemanPaper() override fun onEnable() { foreman.register(this) server.pluginManager.registerEvents(this, this) } /** Sends the spawns to a staff member (on join, and after each change). */ fun showSpawns(staff: Player) { val packet = ForemanAPI.createReplacePacket("myplugin", "spawns") { setSourceColor(0x55FF55) setDefaultDisplay { shape(RenderShape.FLAG); glow(true) } for (spawn in spawns) addPreciseLocation(spawn.id, spawn.world, spawn.x, spawn.y, spawn.z) } foreman.sendLocationData(staff, packet) } @EventHandler fun onMove(event: LocationMoveRequestEvent) { if (event.source != "myplugin") return if (!event.player.hasPermission("myplugin.edit")) return event.setCancelled(true) moveSpawn(event.locationId, event.world, event.x, event.y, event.z) showSpawns(event.player) } } ``` Every plugin that shades the location API receives every edit: check `source` before acting on one. The details: [Location API](locations-api.md). ## Before a release: your local build To try a change to Foreman's APIs in your plugin, publish them to your local Maven repository and add `mavenLocal()` first in your repositories: ```sh git clone https://github.com/qore-games/foreman && cd foreman ./gradlew publishToMavenLocal ``` --- # Skills for your plugin Teach the AI your plugin: its commands, its config files, how to test a change. A [skill](../server/skills.md) is markdown the AI reads when a request is about your plugin; it costs one line of the AI's instructions until then. ## Without code: files in your jar Put markdown files under `foreman/skills/` in your plugin's resources: ``` src/main/resources/ plugin.yml foreman/skills/ myshop.md myshop-prices.md ``` ```markdown title="foreman/skills/myshop.md" --- name: myshop description: Add, change or price items in MyShop (its config.yml and /shop commands). Read before touching the shop. --- # MyShop Items are in `plugins/MyShop/config.yml` under `items.`: `material`, `amount` (default 1), `price`. After editing: `/shop reload`, then `/shop list` to see what players see. `/shop buy ` as a player tests a purchase. ``` Foreman reads them while your plugin is enabled (no dependency on Foreman, nothing to register), and offers them with your plugin as their source. `name` is lowercase letters, digits and `-`; `plugin` defaults to yours. ## From code: `registerSkill` When the skill depends on the server (the arenas that exist, a config value), build it at runtime. You need `foreman-server-api` ([Add Foreman to your plugin](install.md#the-server-api)). === "Java" ```java ForemanTools foreman = Bukkit.getServicesManager().load(ForemanTools.class); if (foreman != null) foreman.registerSkill(this, "myshop", "Add, change or price items in MyShop. Read before touching the shop.", "# MyShop\n\nItems: " + String.join(", ", shop.itemIds()) + "\n\nAfter editing config.yml: `/shop reload`."); ``` === "Kotlin" ```kotlin Bukkit.getServicesManager().load(ForemanTools::class.java)?.registerSkill( this, "myshop", "Add, change or price items in MyShop. Read before touching the shop.", "# MyShop\n\nItems: ${shop.itemIds().joinToString()}\n\nAfter editing config.yml: `/shop reload`.", ) ``` Registered skills are removed when your plugin disables; register again to update one. ## Writing a good skill - **The description is the trigger.** The AI picks skills by it: say what the skill is for and when to read it ("Read before changing a region or when something is blocked by protection"). - **Exact commands**, with their arguments and which must run as a player. Say which work from the console: the AI runs commands as the console unless told otherwise. - **How to check:** the command that shows the result, where errors are logged (`/myplugin reload` then the log). - **The pitfalls** you'd tell a new admin. - Short. A long skill costs tokens every time it's read: split it by task (`myshop`, `myshop-prices`). When commands can't do what the AI needs (structured data, an action without a command), add a tool: [ForemanTools](foreman-tools.md). --- # ForemanTools Publish tools the AI can call, next to Foreman's own, with your plugin as their provider. They show up in `/foreman tools`, in the client's hub and in every chat on the server. ## Setup `compileOnly` on `games.qore:foreman-server-api` and a soft dependency on Foreman: [Add Foreman to your plugin](install.md#the-server-api). Register in `onEnable` (Foreman enables first). Your tools are removed when your plugin disables, so a reload registers them again. ## A command as a tool The simplest tool runs one of your commands. `{argument}` placeholders are filled from the call, and the AI gets back everything the command printed (like `server_command`). === "Java" ```java ForemanTools tools = Bukkit.getServicesManager().load(ForemanTools.class); // null without Foreman if (tools != null) tools.registerCommand(this, "npc_reload", "Reloads the NPCs of a zone", "npcs reload {zone}", Map.of("zone", "Zone id"), false); ``` === "Kotlin" ```kotlin val tools = Bukkit.getServicesManager().load(ForemanTools::class.java) // null without Foreman tools?.registerCommand(this, "npc_reload", "Reloads the NPCs of a zone", "npcs reload {zone}", mapOf("zone" to "Zone id"), false) ``` The last argument runs it as the calling player (who must be online), with their permissions. Otherwise it runs from the console, and players calling it need `foreman.admin`, like `server_command`. Each value is one word, so a call can't add arguments to the command, except the one whose placeholder ends it (`broadcast {message}`). ## A tool of your own === "Java" ```java tools.register(this, new ExternalTool( "npc_list", "Lists the NPCs of a world with their positions. Use it before moving or editing one.", """ {"type": "object", "properties": {"world": {"type": "string", "description": "World name"}}} """, false, // modifies: changes are announced to ops with foreman.watch call -> { World world = Bukkit.getWorld(call.string("world")); if (world == null) return ToolReply.error("No such world"); String text = tools.onMainThread(() -> describeNpcs(world)); return ToolReply.ok(text); })); ``` === "Kotlin" ```kotlin tools.register(this, ExternalTool( "npc_list", "Lists the NPCs of a world with their positions. Use it before moving or editing one.", """{"type": "object", "properties": {"world": {"type": "string", "description": "World name"}}}""", false, // modifies: changes are announced to ops with foreman.watch ) { call -> val world = call.string("world")?.let { Bukkit.getWorld(it) } if (world == null) ToolReply.error("No such world") else ToolReply.ok(tools.onMainThread { describeNpcs(world) }) }) ``` - **Names:** lowercase letters, digits and `_`. Foreman's own names and other plugins' are refused. - **Descriptions:** the AI reads them to choose a tool. Say what it does and when to use it. - **Handlers** run off the main thread: use `onMainThread` for the world. - **Replies:** `ToolReply.ok(text)`, `ToolReply.error(text)`, or `new ToolReply(text, false, images)` with PNG bytes the AI can look at. - **Calls:** `call.argument(name)` (String, Double, Boolean, List, Map or null), `call.string`, `call.number`, `call.getPlayer()` (who asked, when online), `call.getArguments()` (the JSON). `tools.tools()` lists every tool with its provider; `unregister(owner, name)` removes one. --- # Location API Show your plugin's locations (NPCs, spawns, regions, waypoints…) in the client mod, by **source** (your plugin) and **category**, and receive what staff do with them in the location editor: moves, renames, new locations. It works on any Paper server, with or without the Foreman plugin. ## Setup Shade `games.qore:foreman-paper` (it brings `foreman-api`) into your plugin, see [Add Foreman to your plugin](install.md#the-location-api), and register it once: === "Java" ```java ForemanPaper foreman = new ForemanPaper(); foreman.register(this); // false when it's registered already: use ForemanPaper.getInstance() ``` === "Kotlin" ```kotlin val foreman = ForemanPaper() foreman.register(this) // false when it's registered already: use ForemanPaper.instance ``` ## Sending locations === "Java" ```java var builder = ForemanAPI.builder("myplugin", "spawns") .setAction(LocationAction.REPLACE) .setSourceColor(0x55FF55) .setDefaultDisplay(ForemanAPI.displayConfig().shape(RenderShape.FLAG).glow(true).build()); for (Spawn spawn : spawns) builder.addPreciseLocation(spawn.id(), spawn.world(), spawn.x(), spawn.y(), spawn.z()); ForemanPaper.getInstance().sendLocationData(player, builder.build()); ``` === "Kotlin" ```kotlin val packet = ForemanAPI.createReplacePacket("myplugin", "spawns") { setSourceColor(0x55FF55) setDefaultDisplay { shape(RenderShape.FLAG); glow(true) } for (spawn in spawns) addPreciseLocation(spawn.id, spawn.world, spawn.x, spawn.y, spawn.z) } ForemanPaper.instance?.sendLocationData(player, packet) ``` - **Actions:** `createAddPacket` adds to a category, `createReplacePacket` replaces it, `createRemovePacket` removes it (from Java: `builder(…).setAction(LocationAction.ADD | REPLACE)`). `sendClearLocations(player, source)` clears a source (or everything). - **Locations:** `addBlockLocation` (block coordinates) or `addPreciseLocation`, each with an id unique in its category. - **Display:** `color`, `name`, `glow`, `shape` (`CUBE`, `SPHERE`, `DIAMOND`, `FLAG`, `CROSS`, `BEACON`), `size`, `visibleDistance`, per location or as the category's default. Send them to the players who should see them (staff), when they join and when the locations change. ## Receiving edits Listen to the events, check the player's permission, apply the change, save, and send the updated locations back: | Event | When | |---|---| | `LocationMoveRequestEvent` | A location was moved, or moved to another source or category (`newSource`, `newCategory`) | | `LocationUpdateDisplayEvent` | A location was renamed | | `LocationAddRequestEvent` | A player asked to add one where they stand (`/locations add [k=v…]`); fill `resultEntries` to answer | All three are cancellable. Every plugin that shades the location API receives every edit: check `source` first. ## Channels For other server platforms: `foreman:location_data` and `foreman:location_clear` (server to client), `foreman:location_add_request`, `foreman:location_update_display` and `foreman:location_relocate` (client to server). The codecs are in `foreman-api` (`LocationCodecs`). --- # ForemanWorlds Read what Foreman keeps in each world: its [areas, points and profile](../server/world-context.md). Use them to fill containers with loot, spawn NPCs at the spawns builds recorded, or turn areas into regions. The service is there when the Foreman plugin runs with FastAsyncWorldEdit (the world tools); otherwise `load` returns `null`. `compileOnly` on `games.qore:foreman-server-api`: [Add Foreman to your plugin](install.md#the-server-api). === "Java" ```java ForemanWorlds worlds = Bukkit.getServicesManager().load(ForemanWorlds.class); // null without Foreman for (WorldPoint p : worlds.points(world)) if (p.getType().equals("container")) fillContainer(world.getBlockAt(p.getX(), p.getY(), p.getZ()), p.getKind()); ``` === "Kotlin" ```kotlin val worlds = Bukkit.getServicesManager().load(ForemanWorlds::class.java) // null without Foreman worlds?.points(world)?.filter { it.type == "container" }?.forEach { fillContainer(world.getBlockAt(it.x, it.y, it.z), it.kind) } ``` | Method | Returns | |---|---| | `areas(world)`, `area(world, name)` | `WorldArea`: name, boxes (`BoundingBox`, max corners exclusive), `kind` when it reserves space (`build`, `road`, `yard`…), note, `theme` when it's a zone | | `points(world)` | `WorldPoint`: block position, `type` and `kind` (`container`/`chest`), the `build` and `room` it belongs to | | `profile(world)` | The world's profile as JSON (`{}` without one) | Reads are thread-safe and return snapshots. Listen to `WorldDataChangeEvent` (`AREAS`, `POINTS`, `PROFILE`), fired on the main thread after a change is saved, to follow changes. --- # FurnitureProvider Foreman has no furniture of its own, and its furniture tools are world tools: they need FastAsyncWorldEdit on the server. A furniture plugin offers its furniture (models shown by display entities, with collision cells) by registering a `FurnitureProvider`. The AI then gets `list_furniture`, `place_furniture`, `remove_furniture` and furniture previews; `build_structure` furnishes rooms with `furniture:` entries of the [room templates](../server/building-templates.md#rooms); `check`, `transform_region` and undo take the furniture into account. `compileOnly` on `games.qore:foreman-server-api`: [Add Foreman to your plugin](install.md#the-server-api). === "Java" ```java Bukkit.getServicesManager().register(FurnitureProvider.class, new MyFurniture(), this, ServicePriority.Normal); ``` === "Kotlin" ```kotlin Bukkit.getServicesManager().register(FurnitureProvider::class.java, MyFurniture(), this, ServicePriority.Normal) ``` The highest priority provider is used. Everything is called on the main thread. | Method | Does | |---|---| | `types()`, `type(id)` | Your furniture types | | `find(world, box)` | The furniture whose root block is in a box | | `spawn(type, root, yaw, state)` | Places floor furniture; null when refused | | `place(type, player, against, face)` | Places wall, ceiling or hanging furniture as a player would | | `remove(handle)` | Removes one without drops | A `FurnitureType` gives its `id`, the `surfaces` it goes on (`floor`, `wall`, `ceiling`, `hanging`), its `rotations` (4 or 8), its `footprint` (collision cells relative to the root, front facing north), whether it's `interactive` (seats, containers: players must reach it), and its item `model` and `pose` so renders can draw it. --- # Network profiles (desync dupe testing) The hardest item dupes need the client and server to disagree for a tick. Foreman's [dupe testing](../server/dupe-testing.md) runs the input side of that (action bursts in one tick), and two stock network profiles run the network side: `burst` holds the client's sends and sends them together at the end, and `ping_` adds n ms to each. A test names one as `network` on `inventory_actions`, and the profile is held around the burst only. The hook is open too: implement another condition against the client connection and register it. ## Stock profiles | id | What it does while armed | |---|---| | `burst` | Every outbound packet is held; on release they are sent in order, in one go. The client goes silent, then blinks. | | `ping_100`, `ping_300`, `ping_500`, `ping_1000` | Each outbound packet is delayed by that many ms. Packets already scheduled when the burst ends keep their delay; the ones after it go normally. | Keepalive, ping, pong, disconnect, login, kick, chat-session and configuration packets always pass untouched, so a test is not kicked for lagging. Without a `network` argument the burst runs at normal timing. ## The interface In the client mod (`games.qore.foreman.client.link`): ```kotlin interface NetworkProfile { val id: String // what a test names it by: "burst", "ping_300" fun apply() // start the condition (delay/withhold/queue packets) fun release() // stop and flush: the connection returns to normal } ``` Register another one once, for example from your own companion mod or a dev build: ```kotlin NetworkProfiles.register(object : NetworkProfile { override val id = "my_condition" override fun apply() { /* start the condition */ } override fun release() { /* stop it and send anything held */ } }) ``` Then a test burst holds it around the actions: ```json { "name": "inventory_actions", "arguments": { "actions": [ {"slot": 36, "input": "pickup"}, {"slot": 36, "input": "pickup"} ], "per_tick": 2, "network": "burst" } } ``` `NetworkProfiles.around(id) { … }` applies the profile, runs the block, and releases it even if the block throws. With no profile of that name registered, `inventory_actions` reports it clearly and does nothing; without a `network` argument the burst runs at normal timing. Leaving the server releases every profile. ## Keeping it to your server Packet timing applies to whatever server you're on, so it needs the server's opt-in: `inventory_actions` arms a profile, or a `drop`, only when the server runs the Foreman plugin and gives the player `foreman.test` (the bridge's hello says so), as the rest of the testing tools need. A profile you register goes through the same check. ## Inbound drops (ignore a server packet) The same idea on the receive side: `PacketFilter` (in `client/link/`) holds which inbound packet types a test is dropping and answers `shouldDrop(packet)` (it never drops keepalive/ping/disconnect/login/chat-session). The inbound hook (`ConnectionMixin.foreman$received`) cancels a packet when `shouldDrop` is true, so the client never handles it and keeps acting on the state it had. `inventory_actions` takes a `drop` list (type-name fragments like `setslot`, `containerset`) and arms it around the burst; leaving the server clears it. Packet bundles are dispatched as one packet, so a type inside a `ClientboundBundlePacket` is not matched by `drop`. --- # Building from source Requirements: JDK 25. ```sh ./gradlew build ./gradlew publishToMavenLocal ``` | Module | Artifact | What | |---|---|---| | `client` | `client/build/libs/foreman-.jar` | The Fabric mod | | `server` | `server/build/libs/foreman-server-.jar` | The Paper plugin | | `server-api` | `games.qore:foreman-server-api` | What other plugins compile against (`ForemanTools`, `ForemanWorlds`, `FurnitureProvider`) | | `paper` | `games.qore:foreman-paper` | The location API's Paper side, shaded into your plugin | | `api` | `games.qore:foreman-api` | The location protocol, and Foreman's tool names shared by the client and the server | `./gradlew :client:runClient` starts a development client. ## Documentation The site is built with [Zensical](https://zensical.org) from `docs/` and `zensical.toml`: ```sh pip install -r docs/requirements.txt zensical serve # http://localhost:8000, rebuilt as you edit ``` It's published to GitHub Pages by `.github/workflows/docs.yml` on every push to `main` that changes the docs, with the Maven repository (`maven/`) next to it. --- # Security and privacy Foreman gives an AI the hands of an admin: it runs commands, edits the world and reads logs. This page lists what it can reach, what leaves your computer and your server, and how to keep it to the people you trust. ## What leaves your computer Foreman collects nothing and has no telemetry. It connects to: | From | To | What | |---|---|---| | The mod | The AI you chose: Claude Code and opencode (their own provider), or OpenRouter | Your prompts and what the AI's tools return: screenshots, chat lines, packets, logs, files of the chat's folders | | The mod | The server's Foreman plugin | Tool calls, with your personal token | | The plugin | Mojang (`piston-meta.mojang.com`), once | The client jar used for textured renders, kept in `cache/` (unless `resources.client-jar` is set) | **Ask the AI** in the packet viewer sends the captured packets to your AI: they hold chat, player names and UUIDs, coordinates and inventories. Record only what you mean to share. ## What reaches the AI The AI reads what its tools return, and some of that is written by other people: chat lines, signs, books, item and entity names, log lines, packets, skills shipped in plugins. Text in any of them can try to steer it ("ignore your instructions and…"), so: - **Give a chat only what it needs.** Commands run as you: as an op, a chat can do anything you can. Turn off a chat's **Commands** or **Vision** switch when it doesn't need them, and add only folders you trust to its workspace. - **Agents read their workspace's own settings.** Claude Code loads `.claude/settings.json` from the folders it works in (hooks, permissions), opencode loads `opencode.json`. A folder from someone else can change what they may do. - **Read before you confirm.** The world tools can be undone (`/foreman undo`); commands and file edits can't. ## The server - **Every MCP request needs a token**, on localhost too: the server's (`mcp.token`, or `mcp-token.txt` that Foreman makes) or a player's personal one. Requests from web pages are refused, so a site open in your browser can't reach it. - **The server's token and `foreman.admin` are console access:** `server_command` runs commands as the console. Keep them to people you'd give the console to. - **`foreman.client`** lets a player's AI use the world tools and plugins' tools as them. The tools that act as the console need `foreman.admin` ([Permissions and tokens](server/permissions.md)). - **The MCP server speaks plain HTTP.** It listens on `127.0.0.1` by default. To reach it from other machines, put it behind TLS (a reverse proxy) or a VPN, and don't open the port to the internet. - `mcp-token.txt` and `tokens.yml` are readable by the server's user only. ## Your computer - The mod's tokens and keys (`config/foreman/settings.json`, `client-mcp.json`) are readable by your user only. On Windows they get your profile folder's permissions, private by default. - The client's MCP server listens on `127.0.0.1` only, refuses web pages, and needs a token: a new one each launch for chats, a saved one for [outside agents](ai/outside-agents.md), which are off until you allow them. - A chat's file tools stay inside its folders, links included. ## Reporting a vulnerability Report it privately through the repository's [Security tab](https://github.com/qore-games/foreman/security/advisories/new), not in a public issue. --- # FAQ and known limits ## Does Foreman cost anything when I don't use it? No. Packet recordings run only while you record, overlays are drawn only when they're on, and the client's MCP server only answers outside agents when you allow them. On the server, the dev-server tools only do work when called. ## Do I need the server plugin? No. The packet viewer, the HUD inspector, local location sets and AI chats with the client's tools work on any server. Plugin locations need a plugin that sends them ([location API](developers/locations-api.md)). The dev-server tools, the world tools and other plugins' tools need the plugin. ## Which AI does it use? Yours: Claude Code or opencode installed on your computer with your own login, or OpenRouter with your own key. See [Backends](ai/backends.md). ## Can I use it on a production server? Yes. The client mod does nothing on a server until you use it. The server plugin runs without FastAsyncWorldEdit: then it has the dev-server tools and your plugins' tools, and none that edit the world. With FastAsyncWorldEdit, the world tools are on too. Either way, keep `foreman.client` and `foreman.admin` to staff you trust, and the MCP server on localhost unless you need more ([Permissions and tokens](server/permissions.md)). ## Chats can't start Claude Code If your launcher is a Flatpak, see [Flatpak launchers](getting-started/flatpak.md). Otherwise check the command in the hub's AI settings (default `claude`) and that it runs in a terminal. ## Can I use it on my commercial server, or in a paid plugin? Yes. Foreman is under the [MIT License](https://github.com/qore-games/foreman/blob/main/LICENSE): use it, change it, share it and build on it for any purpose, as long as the license's notice stays with the copies. ## Known limits - The undo history is kept in memory and is lost on restart. - `check` tells terrain from builds by block type, so a build made of plain stone or dirt looks like terrain. - Paths don't bridge water. - `server_reload_plugin` needs PlugMan or PlugManX. Plugins that depend on the reloaded one keep its old classes: reload them too, or restart.