@creeperhost/modlens-mcp 1.6.19 → 1.6.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ATTRIBUTION.md +8 -1
- package/README.md +82 -5
- package/RUNTIME.md +262 -0
- package/TESTING.md +8 -0
- package/dist/cli.js +28 -8
- package/dist/cli.js.map +1 -1
- package/dist/hosted-mod-license.d.ts +36 -0
- package/dist/hosted-mod-license.d.ts.map +1 -0
- package/dist/hosted-mod-license.js +65 -0
- package/dist/hosted-mod-license.js.map +1 -0
- package/dist/hosted-policy.d.ts +32 -0
- package/dist/hosted-policy.d.ts.map +1 -0
- package/dist/hosted-policy.js +257 -0
- package/dist/hosted-policy.js.map +1 -0
- package/dist/launcher.js +104 -89
- package/dist/launcher.js.map +1 -1
- package/dist/license-notices.d.ts +8 -0
- package/dist/license-notices.d.ts.map +1 -0
- package/dist/license-notices.js +10 -0
- package/dist/license-notices.js.map +1 -0
- package/dist/license-source-references.d.ts +12 -0
- package/dist/license-source-references.d.ts.map +1 -0
- package/dist/license-source-references.js +18 -0
- package/dist/license-source-references.js.map +1 -0
- package/dist/license-templates.d.ts +6 -0
- package/dist/license-templates.d.ts.map +1 -0
- package/dist/license-templates.js +55 -0
- package/dist/license-templates.js.map +1 -0
- package/dist/local-mod.d.ts +31 -0
- package/dist/local-mod.d.ts.map +1 -0
- package/dist/local-mod.js +71 -0
- package/dist/local-mod.js.map +1 -0
- package/dist/mod-license.d.ts +53 -0
- package/dist/mod-license.d.ts.map +1 -0
- package/dist/mod-license.js +617 -0
- package/dist/mod-license.js.map +1 -0
- package/dist/primer-catalog.d.ts +15 -0
- package/dist/primer-catalog.d.ts.map +1 -0
- package/dist/primer-catalog.js +43 -0
- package/dist/primer-catalog.js.map +1 -0
- package/dist/primer-content.d.ts +7 -0
- package/dist/primer-content.d.ts.map +1 -0
- package/dist/primer-content.js +185 -0
- package/dist/primer-content.js.map +1 -0
- package/dist/runtime/cli.d.ts +2 -0
- package/dist/runtime/cli.d.ts.map +1 -0
- package/dist/runtime/cli.js +163 -0
- package/dist/runtime/cli.js.map +1 -0
- package/dist/runtime/companion.d.ts +32 -0
- package/dist/runtime/companion.d.ts.map +1 -0
- package/dist/runtime/companion.js +135 -0
- package/dist/runtime/companion.js.map +1 -0
- package/dist/runtime/guidance.d.ts +73 -0
- package/dist/runtime/guidance.d.ts.map +1 -0
- package/dist/runtime/guidance.js +40 -0
- package/dist/runtime/guidance.js.map +1 -0
- package/dist/runtime/hub.d.ts +150 -0
- package/dist/runtime/hub.d.ts.map +1 -0
- package/dist/runtime/hub.js +561 -0
- package/dist/runtime/hub.js.map +1 -0
- package/dist/runtime/modlens-agent.jar +0 -0
- package/dist/runtime/protocol.d.ts +202 -0
- package/dist/runtime/protocol.d.ts.map +1 -0
- package/dist/runtime/protocol.js +91 -0
- package/dist/runtime/protocol.js.map +1 -0
- package/dist/runtime/requests.d.ts +367 -0
- package/dist/runtime/requests.d.ts.map +1 -0
- package/dist/runtime/requests.js +57 -0
- package/dist/runtime/requests.js.map +1 -0
- package/dist/server.js +141 -45
- package/dist/server.js.map +1 -1
- package/dist/setup.js +9 -4
- package/dist/setup.js.map +1 -1
- package/dist/tools/primers.d.ts +133 -22
- package/dist/tools/primers.d.ts.map +1 -1
- package/dist/tools/primers.js +391 -386
- package/dist/tools/primers.js.map +1 -1
- package/dist/tools/project.d.ts +11 -7
- package/dist/tools/project.d.ts.map +1 -1
- package/dist/tools/project.js +45 -3
- package/dist/tools/project.js.map +1 -1
- package/dist/tools/report-issue.d.ts +77 -0
- package/dist/tools/report-issue.d.ts.map +1 -0
- package/dist/tools/report-issue.js +92 -0
- package/dist/tools/report-issue.js.map +1 -0
- package/dist/tools/runtime.d.ts +6 -0
- package/dist/tools/runtime.d.ts.map +1 -0
- package/dist/tools/runtime.js +18 -0
- package/dist/tools/runtime.js.map +1 -0
- package/dist/tools/source.d.ts +1 -1
- package/dist/tools/source.d.ts.map +1 -1
- package/dist/tools/source.js +5 -1
- package/dist/tools/source.js.map +1 -1
- package/package.json +8 -1
- package/scripts/build-runtime-agent.mjs +64 -0
- package/scripts/fixtures/mod-license-corpus.json +154 -0
- package/scripts/gradle/modlens-runtime.init.gradle +17 -0
- package/scripts/runtime-test-client.mjs +36 -0
- package/scripts/test-hosted-policy.mjs +123 -0
- package/scripts/test-mod-licenses-live.mjs +86 -0
- package/scripts/test-package.mjs +141 -5
- package/scripts/test-primers-live.mjs +93 -0
- package/scripts/test-project-http.mjs +46 -3
- package/scripts/test-runtime-gradle.mjs +107 -0
- package/scripts/test-runtime-minecraft.mjs +190 -0
- package/scripts/test-runtime-native.mjs +120 -0
- package/scripts/test-runtime-remote.mjs +209 -0
- package/scripts/update-license-templates.mjs +14 -0
package/ATTRIBUTION.md
CHANGED
|
@@ -6,4 +6,11 @@ If ModLens substantially powers an end-user-facing feature in your product or se
|
|
|
6
6
|
|
|
7
7
|
Suggested wording:
|
|
8
8
|
|
|
9
|
-
Powered by ModLens by CreeperHost
|
|
9
|
+
Powered by ModLens by CreeperHost
|
|
10
|
+
|
|
11
|
+
## Third-party license reference data
|
|
12
|
+
|
|
13
|
+
The hosted mod license matcher includes selected standard license texts from
|
|
14
|
+
[SPDX License List Data 3.27.0](https://github.com/spdx/license-list-data/tree/v3.27.0),
|
|
15
|
+
whose data is dedicated to the public domain under CC0-1.0. These reference
|
|
16
|
+
texts identify grants; they do not themselves license any Minecraft or mod code.
|
package/README.md
CHANGED
|
@@ -4,6 +4,33 @@ MCP server and CLI for browsing, decompiling, and analyzing Minecraft mod JARs.
|
|
|
4
4
|
|
|
5
5
|
Store mod metadata, class indexes, mixin targets, AT/AW entries, and decompiled source in a local database — **embedded SQLite by default** (zero setup), or PostgreSQL/PGlite if you want them. Query everything via AI (MCP) or command line (CLI).
|
|
6
6
|
|
|
7
|
+
## Optional live development client
|
|
8
|
+
|
|
9
|
+
The `runtime` MCP tool can prepare and launch a **Minecraft 26.3 / Java 25** dev
|
|
10
|
+
client, detect IntelliJ launches, monitor JVM failures and memory pressure, and
|
|
11
|
+
control input without desktop automation. It supports normal, visible watch-only,
|
|
12
|
+
and hidden windows. Ask the AI to call `runtime` with `action:"help"` or set it up
|
|
13
|
+
for your mod project. With remote MCP, Codex runs the local `--runtime` helper;
|
|
14
|
+
with local stdio MCP, the tools execute directly. No second MCP connection is needed.
|
|
15
|
+
See [runtime setup, examples, and compatibility limits](RUNTIME.md).
|
|
16
|
+
|
|
17
|
+
## Reporting an issue from your coding agent
|
|
18
|
+
|
|
19
|
+
Ask your agent to **report a ModLens issue on GitHub**. The `report_issue` MCP tool
|
|
20
|
+
works locally and remotely: `action:"help"` explains the workflow, and
|
|
21
|
+
`action:"prepare"` produces a draft from a title, summary, reproduction steps,
|
|
22
|
+
expected/actual behavior, environment and an optional sanitized diagnostic excerpt.
|
|
23
|
+
It includes the ModLens server version automatically.
|
|
24
|
+
|
|
25
|
+
The tool directs the agent to check for duplicates and submit to
|
|
26
|
+
[CreeperHost/modlens-mcp](https://github.com/CreeperHost/modlens-mcp/issues) using
|
|
27
|
+
its existing GitHub connector or `gh issue create --body-file`. Without GitHub
|
|
28
|
+
access, it returns a draft and a manual submission link. ModLens needs no GitHub
|
|
29
|
+
credentials and does not publish the report itself; `executed:false` means only
|
|
30
|
+
the draft was prepared. Remove credentials, private source and personal details
|
|
31
|
+
before supplying diagnostic excerpts. Reports are submitted when the user requests
|
|
32
|
+
it, rather than automatically for every error.
|
|
33
|
+
|
|
7
34
|
## Installation
|
|
8
35
|
|
|
9
36
|
### Option A — npx (recommended, no clone required)
|
|
@@ -718,13 +745,23 @@ All tool actions have been consolidated into **24 grouped tools** to stay within
|
|
|
718
745
|
| action | Key params | Description |
|
|
719
746
|
|--------|-----------|-------------|
|
|
720
747
|
| `ingest` | entries[] | Add migration guide entries |
|
|
721
|
-
| `seed` |
|
|
722
|
-
| `get` | id |
|
|
723
|
-
| `by_version` | fromVersion, toVersion, modloader |
|
|
748
|
+
| `seed` | fetchContent=true | Populate the official Minecraft/Forge/NeoForge primer catalogue and cache missing guide bodies |
|
|
749
|
+
| `get` | id, startLine=1, maxLines=400, fetchContent=true, refresh=false | Read cached Markdown; fetch missing content automatically |
|
|
750
|
+
| `by_version` | fromVersion, toVersion, modloader, includeContent=false, maxChars=60000, cursor, fetchContent=true | Ordered migration steps; optionally bundle their Markdown, including vanilla changes for the selected loader |
|
|
724
751
|
| `search` | query, modloader, fromVersion, toVersion, limit | Full-text search |
|
|
725
752
|
| `list` | modloader, limit | List all primers |
|
|
726
753
|
| `delete` | id | Remove by DB id |
|
|
727
754
|
|
|
755
|
+
Start with `{"action":"seed"}` on a fresh database, then use `{"action":"by_version","fromVersion":"1.21.1","toVersion":"1.21.5","modloader":"neoforge"}`. This returns the vanilla and NeoForge guides for the intervening transitions. Call `{"action":"get","id":<returned id>}` for each guide, following `nextStartLine` until it is null. Guides retain headings, tables, links and code blocks; pagination does not truncate the cached document. The catalogue contains the steps published upstream, so a result is not a guarantee of coverage for every release or loader.
|
|
756
|
+
|
|
757
|
+
To read a whole migration range, request `{"action":"by_version","fromVersion":"1.21.1","toVersion":"26.1","modloader":"neoforge","includeContent":true}`. The server fetches missing bodies and returns the original Markdown with each guide's ID, title, source URL, loader and version boundaries. Small ranges fit in one response. Larger ranges return `nextCursor`; repeat the same request with `cursor` set to that value until it is null. `count` is the total number of matching guides; `primers` contains the current page. Existing calls without `includeContent` retain their metadata-only response.
|
|
758
|
+
|
|
759
|
+
Bundled pages share a `maxChars` text budget (1,000–200,000, default 60,000) and contain at most 20 guides. Long guides may span pages: concatenate `content` chunks for the same ID directly, without inserting separators. `startOffset`, `endOffset` (exclusive), `totalChars` and `contentChars` count UTF-16 code units; pagination preserves Unicode characters. Each guide reports `contentStatus` and `truncated`; the outer `truncated` reports whether another page remains. Cursors detect changes to the selected catalogue or partly read content and ask you to restart. `fetchContent:false` reads only cached bodies and reports missing ones with `contentStatus:"missing"` and a page-level `missing` count. Fetch failures retain successful guides, report `contentStatus:"fetch_failed"` and an error per failed guide, and set the page-level `failed` count and MCP error flag. Retry those IDs with `get`. The CLI equivalent is `primers by-version 1.21.1 26.1 --modloader=neoforge --include-content`, with optional `--max-chars=`, `--cursor=` and `--fetch-content=false`; fetch failures set a nonzero exit code after printing the page.
|
|
760
|
+
|
|
761
|
+
`get` and `seed` fetch missing content on the MCP server and store it in its configured database, so this also works with a remote server. Cached reads work without Internet access. Use `fetchContent:false` for metadata-only reads/seeding, or `refresh:true` on `get` to replace cached content. Fetch failures return an error; failed refreshes preserve the previous content. `ingest` still accepts supplied content or an explicit `entries[].fetchContent:true`, and reports failed entries without saving them. The CLI equivalents are `primers seed --fetch-content=false` and `primers get <id> --refresh --start-line=401`.
|
|
762
|
+
|
|
763
|
+
Existing installations repair the old built-in placeholder URLs on the first primer operation; no database reset is needed. IDs are retained where possible, and obsolete duplicates or placeholders are marked superseded with replacement guides. Custom entries and existing content are preserved.
|
|
764
|
+
|
|
728
765
|
### 10. `mc_registry` — MC Registry & Meta Data
|
|
729
766
|
|
|
730
767
|
| action | Key params | Description |
|
|
@@ -1026,6 +1063,46 @@ node dist/cli.js check-updates 2
|
|
|
1026
1063
|
|
|
1027
1064
|
---
|
|
1028
1065
|
|
|
1066
|
+
## Hosted access limits
|
|
1067
|
+
|
|
1068
|
+
HTTP MCP (`MCP_PORT`) enables hosted limits by default. Local stdio retains its existing access. Hosted developers can browse source in pages, search, inspect bytecode and members, compare versions, inspect mixins and data, and upload their private Gradle environments. Bulk decompile/index commands, source/graph/embedding exports, raw JAR reads, host paths and filesystem administration are reserved for the local operator. KubeJS directory access and compatibility checks using host-local JAR paths are also local-only.
|
|
1069
|
+
|
|
1070
|
+
| Setting | Default | Scope |
|
|
1071
|
+
| --- | --- | --- |
|
|
1072
|
+
| `MODLENS_HOSTED_SOURCE_LINES` | 200 | Source/bytecode lines per response, shared across search snippets |
|
|
1073
|
+
| `MODLENS_HOSTED_RESPONSE_BYTES` | 131072 (128 KiB) | Serialized text content per tool response |
|
|
1074
|
+
| `MODLENS_HOSTED_DAILY_BYTES` | 5242880 (5 MiB) | Delivered tool content per account per UTC day |
|
|
1075
|
+
| `MODLENS_HOSTED_PERIOD_BYTES` | 52428800 (50 MiB) | Delivered tool content per account per fixed 30-day period |
|
|
1076
|
+
| `MODLENS_HOSTED_DAILY_REQUESTS` | 1000 | Tool calls per account per UTC day |
|
|
1077
|
+
| `MODLENS_HOSTED_MINUTE_REQUESTS` | 120 | Tool calls per account per minute |
|
|
1078
|
+
|
|
1079
|
+
The byte allowance includes all successful tool content, including metadata, search snippets and bytecode. It measures UTF-8 JSON content before transport compression. Cached files and inbound upload bytes are excluded. Each source text field has a 32 KiB cap. Searches with `limit`/`top` are capped at 50 results; responses exceeding the byte limit require a narrower query.
|
|
1080
|
+
|
|
1081
|
+
Use `startLine` (1-based) and `maxLines` for `mc_source get_source/bytecode`, `mod source/decompile_class`, `mod_bytecode bytecode`, and `project source/bytecode`. A range may start anywhere; the hosted cap bounds its length. Prepare shared indexes and ingest mods through the operator's local interface; hosted clients can upload their own Gradle environment through `project`.
|
|
1082
|
+
|
|
1083
|
+
### Mod source access
|
|
1084
|
+
|
|
1085
|
+
Hosted mod source responses include licence and attribution notices. Use `mod_license` with `action=check` and `modId`/`dbId` to check availability. For uploaded projects, supply `projectKey`, `environmentId` and `className` instead.
|
|
1086
|
+
|
|
1087
|
+
When hosted source is unavailable, `action=local_plan` provides instructions for decompiling your local JAR with your explicit consent. Run the returned request on your computer:
|
|
1088
|
+
|
|
1089
|
+
```bash
|
|
1090
|
+
npx -y @creeperhost/modlens-mcp --local-mod --request-file local-request.json
|
|
1091
|
+
```
|
|
1092
|
+
|
|
1093
|
+
### Bind allowances to authenticated accounts
|
|
1094
|
+
|
|
1095
|
+
For public HTTP access, put an authenticated HTTPS gateway in front of the server. Set `MODLENS_HOSTED_PROXY_SECRET` to a random secret of at least 32 characters. The gateway must remove caller-provided `x-modlens-*` headers and inject:
|
|
1096
|
+
|
|
1097
|
+
- `x-modlens-proxy-secret`: the server's secret, never sent to clients.
|
|
1098
|
+
- `x-modlens-user-id`: a stable, verified account identifier selected by the gateway. Reconnecting, rotating tokens, or using another client must retain this identifier.
|
|
1099
|
+
|
|
1100
|
+
Restrict network access to the origin to that gateway and protect the gateway-to-server connection. The server checks the secret and account on every MCP request and binds each session to its account. The gateway manages authentication and account access. Multiple login methods for one account must use the same identifier.
|
|
1101
|
+
|
|
1102
|
+
Without the proxy secret, callers share a single global allowance, regardless of self-asserted user IDs or tokens. This fallback provides limits, **not authentication**, and one caller can exhaust it for everyone. `MODLENS_HOSTED_LIMITS=0` disables these controls and gateway-secret checking entirely; use it only for trusted private HTTP deployments.
|
|
1103
|
+
|
|
1104
|
+
Usage persists in `hosted_usage` in the configured database. Checks and updates are atomic, including parallel calls; reconnects and server restarts do not reset usage. All replicas must use the same persistent database and account mapping. The 30-day periods align to Unix-epoch boundaries, rather than rolling with each request. Restoring an older database restores its older counters. Database failures reject hosted tool calls before releasing output.
|
|
1105
|
+
|
|
1029
1106
|
## Acknowledgements
|
|
1030
1107
|
|
|
1031
1108
|
### Services & APIs
|
|
@@ -1037,9 +1114,9 @@ node dist/cli.js check-updates 2
|
|
|
1037
1114
|
- **[Mojang](https://www.minecraft.net)** — for publishing official Mojmap mappings and the Piston Meta API used for version discovery and JAR downloads.
|
|
1038
1115
|
|
|
1039
1116
|
### Modloader teams
|
|
1040
|
-
- **[NeoForged team](https://github.com/neoforged/NeoForge)** — for NeoForge, the [NeoForge documentation](https://docs.neoforged.net) seeded into the docs database, and the migration
|
|
1117
|
+
- **[NeoForged team](https://github.com/neoforged/NeoForge)** — for NeoForge, the [NeoForge documentation](https://docs.neoforged.net) seeded into the docs database, and the [migration primer catalogue](https://docs.neoforged.net/primer/docs/) used by the primers tool.
|
|
1041
1118
|
- **[FabricMC team](https://github.com/FabricMC)** — for the [Fabric Wiki](https://fabricmc.net/wiki) and [Yarn mappings](https://github.com/FabricMC/yarn) seeded into the docs database, Intermediary mappings used by the `mappings` tool, and [mcsrc.dev](https://mcsrc.dev) whose source browsing and class analysis features inspired our `mc_source` tool.
|
|
1042
|
-
- **[MinecraftForge team](https://github.com/MinecraftForge)** — for
|
|
1119
|
+
- **[MinecraftForge team](https://github.com/MinecraftForge)** — for Forge and the API changes documented in the Forge migration primers.
|
|
1043
1120
|
|
|
1044
1121
|
### Community contributors
|
|
1045
1122
|
- **[MCPHackers](https://mcphackers.org/)** — for [RetroMCP](https://github.com/MCPHackers/RetroMCP-Java), providing the Tiny v2 mappings that enable decompilation of legacy Minecraft versions (Alpha, Beta, and pre-1.7.10 releases).
|
package/RUNTIME.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Optional live Minecraft development runtime
|
|
2
|
+
|
|
3
|
+
ModLens can launch or detect a **Minecraft Java 26.3 / Java 25** development client,
|
|
4
|
+
observe JVM health and failures, and send inputs directly through LWJGL's SDL3
|
|
5
|
+
bindings. Desktop automation and an IntelliJ plugin are not required. This is an
|
|
6
|
+
initial 26.3 adapter, not a claim of compatibility with older versions or every modpack.
|
|
7
|
+
|
|
8
|
+
## Let the AI set it up
|
|
9
|
+
|
|
10
|
+
Use your existing **remote or local** ModLens MCP connection and ask:
|
|
11
|
+
|
|
12
|
+
> Set up ModLens runtime monitoring for this 26.3 mod project. Let me launch it
|
|
13
|
+
> from IntelliJ, and keep the client visible but watch-only while you control it.
|
|
14
|
+
|
|
15
|
+
The always-discoverable `runtime` MCP tool describes this workflow. The AI calls:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{"action":"setup","projectDir":"F:/Git/my-mod","mcVersion":"26.3","mode":"observe","gradleTask":"runClient"}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
With local stdio, that tool call executes directly. With remote MCP, it returns
|
|
22
|
+
`executed:false` and a **local execution plan**. Codex writes the supplied JSON
|
|
23
|
+
request to a local file and invokes the version-matched helper through its local
|
|
24
|
+
terminal tools. A returned plan is not a completed setup or game action.
|
|
25
|
+
|
|
26
|
+
For example, save the setup JSON above as `runtime-request.json`, then run on the
|
|
27
|
+
Minecraft PC (use the package version returned by your remote MCP):
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
npx -y @creeperhost/modlens-mcp@<version> --runtime --request-file runtime-request.json
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For this unpublished source build, use the built checkout instead:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
node /path/to/modlens-mcp/dist/launcher.js --runtime --request-file runtime-request.json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The helper automatically starts an authenticated loopback companion. It stays
|
|
40
|
+
running between commands, detects configured IntelliJ launches, and collects
|
|
41
|
+
events while Codex is doing other work. It requires Node.js and the same Minecraft
|
|
42
|
+
JDK as the direct MCP workflow; it does not bootstrap a source database or modify
|
|
43
|
+
MCP configuration. There is no second local MCP connection, Cloudflare dependency,
|
|
44
|
+
public runtime endpoint, or automatic upload of diagnostics to remote ModLens.
|
|
45
|
+
Codex must have local terminal/file access on the Minecraft PC; a cloud-only shell
|
|
46
|
+
does not provide that access.
|
|
47
|
+
|
|
48
|
+
All JSON requests in this document work through the helper. Short commands are
|
|
49
|
+
also available:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
modlens-mcp --runtime help
|
|
53
|
+
modlens-mcp --runtime sessions
|
|
54
|
+
modlens-mcp --runtime status
|
|
55
|
+
modlens-mcp --runtime stop
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use the same `npx ...` or `node .../dist/launcher.js` prefix if the command is not
|
|
59
|
+
installed globally. The companion stores its private credentials and journal in
|
|
60
|
+
`<MODLENS_CACHE_ROOT>/runtime` (by default `~/.modlens-cache/runtime`). Stop ends
|
|
61
|
+
monitoring, leaving running game processes alive; held inputs expire on connection
|
|
62
|
+
loss. Switch to interactive mode first if you want to continue playing manually.
|
|
63
|
+
The next helper request starts it again. An existing local stdio bridge and the
|
|
64
|
+
helper cannot own the same runtime cache simultaneously; stop the current owner
|
|
65
|
+
before switching between these two arrangements.
|
|
66
|
+
Stop and restart the companion when switching package versions so it runs the
|
|
67
|
+
helper code and bundled agent from the selected installation.
|
|
68
|
+
|
|
69
|
+
Successful local setup copies the bundled agent into the project and creates **ModLens Client** in
|
|
70
|
+
IntelliJ's run configurations. Select that configuration and press Run. The AI can
|
|
71
|
+
also call `runtime` with `action:"launch"` and the returned `projectId`; supply
|
|
72
|
+
`javaHome` if the server's `JAVA_HOME` does not select a suitable JDK.
|
|
73
|
+
|
|
74
|
+
For multi-project builds, select the actual client task, e.g. `:fabric:runClient`.
|
|
75
|
+
The task must extend Gradle `JavaExec`. A generated init script adds the agent to
|
|
76
|
+
that task only. Custom launch plugins which don't expose a JavaExec task get an
|
|
77
|
+
actionable error. Setup also returns `vmOptions` for the actual game JVM in an
|
|
78
|
+
existing Application run configuration. Add every entry, including
|
|
79
|
+
`-XX:StackShadowPages=32`: the 26.3 client needs this setting independently of the
|
|
80
|
+
agent. The generated Gradle/IntelliJ launch supplies it automatically, only to the
|
|
81
|
+
selected client task. **Do not put these options on IntelliJ itself or the Gradle
|
|
82
|
+
daemon.** Quote each whole VM option if it contains spaces. The singular `vmOption`
|
|
83
|
+
field remains available for callers that only need the agent argument.
|
|
84
|
+
|
|
85
|
+
Setup only creates `.modlens/runtime/` and `.run/ModLens Client.run.xml`. It does
|
|
86
|
+
not edit existing run configurations or build files, and refuses to overwrite a
|
|
87
|
+
non-ModLens run configuration. `.modlens/runtime/` contains a local `.gitignore`:
|
|
88
|
+
its connection token and runtime files must stay private. Published npm releases
|
|
89
|
+
include the compiled agent; end users do not need to compile it.
|
|
90
|
+
|
|
91
|
+
## Modes
|
|
92
|
+
|
|
93
|
+
| Mode | Display | Input |
|
|
94
|
+
| --- | --- | --- |
|
|
95
|
+
| `interactive` | Visible game window | Human controls; MCP input rejected |
|
|
96
|
+
| `observe` | Visible game window | MCP controls; physical game input filtered |
|
|
97
|
+
| `hidden` | Hidden game window | MCP controls; physical game input filtered |
|
|
98
|
+
|
|
99
|
+
Switch a connected client with:
|
|
100
|
+
|
|
101
|
+
```json
|
|
102
|
+
{"action":"command","sessionId":"<id>","command":{"type":"mode","mode":"hidden"}}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Observe mode uses the actual game window as a watch-only display. OS window
|
|
106
|
+
management, including closing it, remains available. Hidden mode still uses a
|
|
107
|
+
graphics device and desktop/display environment; it is not a GPU-free server.
|
|
108
|
+
Input interception covers the normal SDL paths. Mods using another native input
|
|
109
|
+
path need a separate adapter. This is a development convenience, not an OS security
|
|
110
|
+
boundary. Switching to interactive returns control; click the game to resume mouse
|
|
111
|
+
capture normally.
|
|
112
|
+
|
|
113
|
+
## AI workflow and tool examples
|
|
114
|
+
|
|
115
|
+
1. `help` / `status`: discover setup, configured projects, and whether the JAR is packaged.
|
|
116
|
+
2. `setup`: prepare a specific project (explicitly opts it in).
|
|
117
|
+
3. `launch`, or let the developer use IntelliJ.
|
|
118
|
+
4. `sessions`: discover client session IDs, PID, connection status and capabilities.
|
|
119
|
+
5. `status` with a session ID: read metrics, mode, screen, world state and coordinates.
|
|
120
|
+
6. `events` with `afterCursor` and `waitMs:30000`: wait for new events without flooding context.
|
|
121
|
+
7. `command`: send input or request diagnostic artifacts; `artifact` reads the result.
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{"action":"command","sessionId":"<id>","command":{"type":"key","key":"W","down":true,"holdMs":500}}
|
|
125
|
+
{"action":"command","sessionId":"<id>","command":{"type":"mouse_move","x":20,"y":-5,"relative":true}}
|
|
126
|
+
{"action":"command","sessionId":"<id>","command":{"type":"mouse_button","button":1,"down":true,"holdMs":100}}
|
|
127
|
+
{"action":"command","sessionId":"<id>","command":{"type":"text","text":"Hello"}}
|
|
128
|
+
{"action":"command","sessionId":"<id>","command":{"type":"screenshot"}}
|
|
129
|
+
{"action":"artifact","sessionId":"<id>","artifactName":"<returned artifact name>"}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Key names include A–Z, 0–9, SPACE, ENTER, ESCAPE, TAB, arrows, modifiers and F1–F12.
|
|
133
|
+
Numeric strings outside single digits represent SDL scancodes. Mouse buttons are
|
|
134
|
+
1=left, 2=middle, 3=right. Coordinates are **window pixels**, not scaled GUI units;
|
|
135
|
+
relative movements are deltas. Text sends text-input events independently of keys.
|
|
136
|
+
`scroll` accepts `x` and `y`. Inputs are delivered to the event loop; they do not
|
|
137
|
+
guarantee a particular gameplay result. Observe state or take a screenshot afterward.
|
|
138
|
+
|
|
139
|
+
Held keys/buttons expire within 10 seconds, and are released when the bridge has
|
|
140
|
+
been unreachable for five seconds (as soon as the game event loop runs). Use
|
|
141
|
+
`release_all` to cancel held inputs. Commands have deadlines and aren't replayed
|
|
142
|
+
automatically after uncertain delivery. A timeout means execution is unknown;
|
|
143
|
+
inspect state before retrying an action.
|
|
144
|
+
|
|
145
|
+
Screenshots use 26.3's own renderer-independent screenshot API, after
|
|
146
|
+
`state.observation.gameLoaded` becomes true. PNGs are returned as MCP image content
|
|
147
|
+
by `artifact` over local stdio MCP. The CLI returns the local PNG path for Codex's
|
|
148
|
+
image viewer instead of base64 in terminal text. `threads` writes a thread dump including detected deadlock IDs;
|
|
149
|
+
`recording` writes the recent JFR recording. Files live under
|
|
150
|
+
`.modlens/runtime/sessions/<sessionId>/`. Diagnostic artifacts are local and may
|
|
151
|
+
contain application data; delete old session directories when finished.
|
|
152
|
+
|
|
153
|
+
## Monitoring and failure semantics
|
|
154
|
+
|
|
155
|
+
The agent samples heap/non-heap usage, thread counts and GC counters every second,
|
|
156
|
+
keeps a bounded two-minute/16 MiB JFR recording, captures uncaught exceptions while
|
|
157
|
+
chaining the previous handler, and hooks the game's fatal crash reporting path.
|
|
158
|
+
It does not intercept every logged/caught exception or exceptions consumed by a
|
|
159
|
+
custom per-thread handler. The optional Minecraft crash hook covers the normal
|
|
160
|
+
client fatal-report path independently of the default uncaught handler.
|
|
161
|
+
Memory-pressure alerts combine sustained collection activity with heap pressure.
|
|
162
|
+
Collection time can include concurrent GC work: it is **not** a stop-the-world
|
|
163
|
+
pause percentage or proof of a memory leak.
|
|
164
|
+
|
|
165
|
+
For allocation churn in your own mod, request a report and read its returned
|
|
166
|
+
artifact through MCP:
|
|
167
|
+
|
|
168
|
+
```json
|
|
169
|
+
{"action":"command","sessionId":"<id>","command":{"type":"allocations","packagePrefix":"com.example.mymod","windowSeconds":30,"limit":20}}
|
|
170
|
+
{"action":"artifact","sessionId":"<id>","artifactName":"<returned artifact name>"}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
The JSON report groups JFR allocation samples by allocated class and matching
|
|
174
|
+
caller, with sample weights and example stacks. Filtering matches callers in the
|
|
175
|
+
package, so allocations made inside Java/Minecraft libraries on behalf of the mod
|
|
176
|
+
can appear too. Omit the filter to inspect all sampled allocation sites. These are
|
|
177
|
+
statistical estimates of allocation pressure, not exact byte counts or retained
|
|
178
|
+
object sizes. Empty or sparse samples are inconclusive; reproduce the workload
|
|
179
|
+
and compare reports alongside heap/GC metrics. Reports use the bounded rolling JFR
|
|
180
|
+
recording, which may contain less history than the requested 5–120 seconds.
|
|
181
|
+
No heap dump or full-GC operation is requested. Retaining paths and GC roots are
|
|
182
|
+
outside this first version. See the [JFR sample-weight definition](https://github.com/openjdk/jdk/blob/jdk-25-ga/src/hotspot/share/jfr/metadata/metadata.xml).
|
|
183
|
+
|
|
184
|
+
The bridge keeps bounded event history with cursors, deduplicates agent replays,
|
|
185
|
+
and persists a rolling journal. A new JVM gets a new session ID. Connection loss
|
|
186
|
+
is reported separately from a crash; native failures/OOM may prevent in-process
|
|
187
|
+
delivery. The generated Gradle run redirects JVM fatal-error logs into
|
|
188
|
+
`.modlens/runtime/`. Commands and telemetry never use Minecraft's network protocol.
|
|
189
|
+
|
|
190
|
+
An active AI task can monitor using bounded event waits. Capturing an event does
|
|
191
|
+
not guarantee an idle Codex task wakes up; host scheduling is separate from MCP.
|
|
192
|
+
|
|
193
|
+
## Scope and maintenance
|
|
194
|
+
|
|
195
|
+
- Source-analysis use starts no runtime listener. An explicit local helper command
|
|
196
|
+
starts its loopback companion; only setup opts a project in, and only launch
|
|
197
|
+
starts a client process.
|
|
198
|
+
- A previously configured local project reconnects when its ModLens server starts.
|
|
199
|
+
- One local bridge owns a runtime cache at a time; a second connection reports the
|
|
200
|
+
existing owner instead of stealing active clients. Multiple game sessions are
|
|
201
|
+
supported through that bridge.
|
|
202
|
+
- HTTP MCP returns explicit local execution plans. The local helper performs
|
|
203
|
+
runtime actions on the Minecraft PC using the same request schema and dispatcher
|
|
204
|
+
as local stdio MCP. HTTP handlers never execute user-supplied local paths.
|
|
205
|
+
- The agent connects only to authenticated loopback HTTP; no browser control routes.
|
|
206
|
+
- SDL events, keyboard state and mouse state are kept consistent. Input/window code
|
|
207
|
+
lives in `SdlAdapter`; optional game APIs live in `Minecraft263`.
|
|
208
|
+
- Exact transformation descriptors live in `Transformer`. It uses Java 25's
|
|
209
|
+
standard Class-File API and an isolated bootstrap bridge, with no ASM/Gson or
|
|
210
|
+
native agent dependencies to collide with mod loaders.
|
|
211
|
+
- Setup accepts `minecraftHooks:false` to isolate compatibility problems. SDL
|
|
212
|
+
controls and JVM diagnostics remain available; screenshots and structured game
|
|
213
|
+
state require the Minecraft adapter. A GLFW backend belongs to a future backport:
|
|
214
|
+
vanilla 26.3 uses SDL3.
|
|
215
|
+
- Capability presence and validation are distinct. Tests cover selected environments;
|
|
216
|
+
Fabric/NeoForge launchers and rendering replacements still require their own runs.
|
|
217
|
+
|
|
218
|
+
## Building and testing
|
|
219
|
+
|
|
220
|
+
The initial validation ran on Windows with Java 25, LWJGL 3.4.3, Gradle 9.6 and
|
|
221
|
+
vanilla Minecraft 26.3 using OpenGL. It includes a real hidden client screenshot
|
|
222
|
+
and input delivery, native SDL controls/diagnostics, the Gradle launch path, and a
|
|
223
|
+
fresh npm-package MCP installation. World gameplay and the full mod-loader/rendering
|
|
224
|
+
matrix have not been validated yet.
|
|
225
|
+
|
|
226
|
+
```sh
|
|
227
|
+
npm ci
|
|
228
|
+
npm run build
|
|
229
|
+
# JAVA_HOME must point to JDK 25 or later:
|
|
230
|
+
npm run build:agent
|
|
231
|
+
npm test
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The optional agent build is separate so source-analysis-only development and Docker
|
|
235
|
+
builds retain their existing Java requirements. The npm release workflow builds and
|
|
236
|
+
ships the agent. `npm run test:runtime:native` exercises an actual hidden SDL window;
|
|
237
|
+
set `JAVA_HOME` and `MODLENS_SDL_CLASSPATH` to LWJGL 3.4.3 core/SDL Java and native
|
|
238
|
+
JARs for your platform. It checks input delivery, polling consistency, hardware-like
|
|
239
|
+
event suppression, key expiry, Unicode text, diagnostics and uncaught exceptions.
|
|
240
|
+
|
|
241
|
+
`node scripts/test-runtime-minecraft.mjs` validates an isolated hidden vanilla 26.3
|
|
242
|
+
demo client without reading accounts or worlds. Set `JAVA_HOME`,
|
|
243
|
+
`MODLENS_TEST_MC_MANIFEST`, `MODLENS_TEST_MC_CLIENT`, `MODLENS_TEST_MC_ASSETS`,
|
|
244
|
+
`MODLENS_TEST_MC_ASSET_INDEX` and `MODLENS_TEST_MAVEN_CACHE`. It uses supplied cached
|
|
245
|
+
artifacts and downloads missing libraries with manifest checksum verification.
|
|
246
|
+
Evidence is retained in the printed temporary directory. The test includes the
|
|
247
|
+
required `-XX:StackShadowPages=32` setting and checks a Minecraft allocation report
|
|
248
|
+
alongside screenshots and input. Use `--baseline` to compare startup without the
|
|
249
|
+
agent while retaining the required client JVM options; this baseline can show a
|
|
250
|
+
normal game window.
|
|
251
|
+
|
|
252
|
+
`node scripts/test-runtime-remote.mjs` tests an isolated HTTP MCP server returning
|
|
253
|
+
plans which local CLI processes execute against a real instrumented SDL JVM. It
|
|
254
|
+
checks setup, session persistence, input, allocation reports, crash events, helper
|
|
255
|
+
restart/reconnection, and shutdown without creating a local source database. Set the same JDK/SDL variables
|
|
256
|
+
as the native test. Add `--helper` to the Minecraft or Gradle tests to exercise
|
|
257
|
+
their complete workflows through the CLI companion too.
|
|
258
|
+
|
|
259
|
+
`node scripts/test-runtime-gradle.mjs` tests the generated launch path in a
|
|
260
|
+
disposable JavaExec project, including paths with spaces and exclusion of unrelated
|
|
261
|
+
tasks. Set `JAVA_HOME` and `MODLENS_TEST_GRADLE_HOME` (Gradle 9.6). This tests the
|
|
262
|
+
Gradle integration; it does not replace mod-loader-specific client tests.
|
package/TESTING.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Run the deterministic regressions with `npm test`. Run `npm run build` before the MCP and package checks below.
|
|
4
4
|
|
|
5
|
+
For a package that includes live runtime support, also run `npm run build:agent`
|
|
6
|
+
with JDK 25 before `npm run test:package`. The npm release workflow does this
|
|
7
|
+
automatically. Runtime protocol tests are part of `npm test`; native SDL and real
|
|
8
|
+
26.3 client checks are described in [RUNTIME.md](RUNTIME.md).
|
|
9
|
+
The packaged-consumer test also exercises the persistent `--runtime` helper.
|
|
10
|
+
`node scripts/test-runtime-remote.mjs` validates HTTP MCP guidance followed by local
|
|
11
|
+
CLI execution against a real Java agent; Minecraft and Gradle tests accept `--helper`.
|
|
12
|
+
|
|
5
13
|
`npm test` works directly after `npm ci`, before a build. Vitest generates the SQLite client before importing the tests. SQLite integration tests create a temporary template from the Prisma schema, use a fresh database for each test, and isolate their artifact cache. They do not depend on `dist/`, the packaged `template.db`, or earlier test results.
|
|
6
14
|
|
|
7
15
|
## Minecraft era matrix
|
package/dist/cli.js
CHANGED
|
@@ -212,9 +212,11 @@ DOCS
|
|
|
212
212
|
docs semantic-search <query> Semantic search (requires Ollama) [--limit=10]
|
|
213
213
|
|
|
214
214
|
PRIMERS (version migration guides)
|
|
215
|
-
primers seed Seed built-in
|
|
216
|
-
primers get <id>
|
|
215
|
+
primers seed Seed and fetch built-in primers [--fetch-content=false]
|
|
216
|
+
primers get <id> Read cached/fetched Markdown [--refresh] [--fetch-content=false]
|
|
217
|
+
[--start-line=1] [--max-lines=400]
|
|
217
218
|
primers by-version <from> <to> Get primers for a version range [--modloader=]
|
|
219
|
+
[--include-content] [--max-chars=60000] [--cursor=] [--fetch-content=false]
|
|
218
220
|
primers search <query> Keyword search [--modloader=] [--limit=]
|
|
219
221
|
primers list List all primers [--modloader=] [--limit=]
|
|
220
222
|
primers delete <id> Delete a primer by ID
|
|
@@ -867,15 +869,33 @@ try {
|
|
|
867
869
|
case "primers": {
|
|
868
870
|
const sub = requireArg(positional[0], "primers action");
|
|
869
871
|
switch (sub) {
|
|
870
|
-
case "seed":
|
|
871
|
-
|
|
872
|
+
case "seed": {
|
|
873
|
+
const seeded = await seedDefaultPrimers(flags.fetchContent !== "false" && flags.fetchContent !== false);
|
|
874
|
+
out(seeded);
|
|
875
|
+
if (seeded.failed)
|
|
876
|
+
process.exitCode = 1;
|
|
872
877
|
break;
|
|
878
|
+
}
|
|
873
879
|
case "get":
|
|
874
|
-
out(await getPrimer(numArg(positional[1], "id")
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
880
|
+
out(await getPrimer(numArg(positional[1], "id"), {
|
|
881
|
+
fetchContent: flags.fetchContent !== "false" && flags.fetchContent !== false,
|
|
882
|
+
refresh: flags.refresh === true || flags.refresh === "true",
|
|
883
|
+
startLine: flags.startLine,
|
|
884
|
+
maxLines: flags.maxLines,
|
|
885
|
+
}));
|
|
886
|
+
break;
|
|
887
|
+
case "by-version": {
|
|
888
|
+
const range = await getPrimersByVersionRange(requireArg(positional[1], "fromVersion"), requireArg(positional[2], "toVersion"), flags.modloader, {
|
|
889
|
+
includeContent: flags.includeContent === true || flags.includeContent === "true",
|
|
890
|
+
fetchContent: flags.fetchContent !== "false" && flags.fetchContent !== false,
|
|
891
|
+
maxChars: flags.maxChars,
|
|
892
|
+
cursor: flags.cursor,
|
|
893
|
+
});
|
|
894
|
+
out(range);
|
|
895
|
+
if ("failed" in range && range.failed)
|
|
896
|
+
process.exitCode = 1;
|
|
878
897
|
break;
|
|
898
|
+
}
|
|
879
899
|
case "search":
|
|
880
900
|
out(await searchPrimers(requireArg(positional[1], "query"), flags.modloader, undefined, undefined, flags.limit));
|
|
881
901
|
break;
|