mcdev-mcp 1.0.0
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/LICENSE +77 -0
- package/README.md +365 -0
- package/dist/callgraph/index.d.ts +14 -0
- package/dist/callgraph/index.d.ts.map +1 -0
- package/dist/callgraph/index.js +201 -0
- package/dist/callgraph/index.js.map +1 -0
- package/dist/callgraph/query.d.ts +20 -0
- package/dist/callgraph/query.d.ts.map +1 -0
- package/dist/callgraph/query.js +95 -0
- package/dist/callgraph/query.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +318 -0
- package/dist/cli.js.map +1 -0
- package/dist/decompiler/download.d.ts +31 -0
- package/dist/decompiler/download.d.ts.map +1 -0
- package/dist/decompiler/download.js +145 -0
- package/dist/decompiler/download.js.map +1 -0
- package/dist/decompiler/downloader.d.ts +7 -0
- package/dist/decompiler/downloader.d.ts.map +1 -0
- package/dist/decompiler/downloader.js +4 -0
- package/dist/decompiler/downloader.js.map +1 -0
- package/dist/decompiler/index.d.ts +16 -0
- package/dist/decompiler/index.d.ts.map +1 -0
- package/dist/decompiler/index.js +74 -0
- package/dist/decompiler/index.js.map +1 -0
- package/dist/decompiler/remapper.d.ts +9 -0
- package/dist/decompiler/remapper.d.ts.map +1 -0
- package/dist/decompiler/remapper.js +174 -0
- package/dist/decompiler/remapper.js.map +1 -0
- package/dist/decompiler/tools.d.ts +3 -0
- package/dist/decompiler/tools.d.ts.map +1 -0
- package/dist/decompiler/tools.js +79 -0
- package/dist/decompiler/tools.js.map +1 -0
- package/dist/decompiler/vineflower.d.ts +3 -0
- package/dist/decompiler/vineflower.d.ts.map +1 -0
- package/dist/decompiler/vineflower.js +37 -0
- package/dist/decompiler/vineflower.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer/index.d.ts +17 -0
- package/dist/indexer/index.d.ts.map +1 -0
- package/dist/indexer/index.js +134 -0
- package/dist/indexer/index.js.map +1 -0
- package/dist/indexer/parser.d.ts +11 -0
- package/dist/indexer/parser.d.ts.map +1 -0
- package/dist/indexer/parser.js +188 -0
- package/dist/indexer/parser.js.map +1 -0
- package/dist/indexer/types.d.ts +14 -0
- package/dist/indexer/types.d.ts.map +1 -0
- package/dist/indexer/types.js +2 -0
- package/dist/indexer/types.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/source-store.d.ts +43 -0
- package/dist/storage/source-store.d.ts.map +1 -0
- package/dist/storage/source-store.js +279 -0
- package/dist/storage/source-store.js.map +1 -0
- package/dist/tools/index.d.ts +503 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +535 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/utils/config.d.ts +13 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +12 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/paths.d.ts +30 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +131 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/types.d.ts +79 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +2 -0
- package/dist/utils/types.js.map +1 -0
- package/dist/utils/version-manifest.d.ts +10 -0
- package/dist/utils/version-manifest.d.ts.map +1 -0
- package/dist/utils/version-manifest.js +64 -0
- package/dist/utils/version-manifest.js.map +1 -0
- package/dist/version-manager.d.ts +11 -0
- package/dist/version-manager.d.ts.map +1 -0
- package/dist/version-manager.js +29 -0
- package/dist/version-manager.js.map +1 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 mcdev-mcp contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
--------------------------------------------------------------------------------
|
|
24
|
+
|
|
25
|
+
## Third-Party Licenses
|
|
26
|
+
|
|
27
|
+
This project includes or uses third-party software under the following licenses:
|
|
28
|
+
|
|
29
|
+
### DecompilerMC
|
|
30
|
+
|
|
31
|
+
The decompiler integration logic in `src/decompiler/` is adapted and translated from Python to TypeScript based on DecompilerMC's architecture.
|
|
32
|
+
|
|
33
|
+
Source: https://github.com/hube12/DecompilerMC
|
|
34
|
+
|
|
35
|
+
MIT License
|
|
36
|
+
|
|
37
|
+
Copyright (c) 2020 Neil
|
|
38
|
+
|
|
39
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
40
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
41
|
+
in the Software without restriction, including without limitation the rights
|
|
42
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
43
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
44
|
+
furnished to do so, subject to the following conditions:
|
|
45
|
+
|
|
46
|
+
The above copyright notice and this permission notice shall be included in all
|
|
47
|
+
copies or substantial portions of the Software.
|
|
48
|
+
|
|
49
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
50
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
51
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
52
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
53
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
54
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
55
|
+
SOFTWARE.
|
|
56
|
+
|
|
57
|
+
### Vineflower
|
|
58
|
+
|
|
59
|
+
This project uses Vineflower for decompiling Minecraft JAR files.
|
|
60
|
+
|
|
61
|
+
Source: https://github.com/Vineflower/vineflower
|
|
62
|
+
|
|
63
|
+
Apache License 2.0
|
|
64
|
+
|
|
65
|
+
Copyright (c) 2021-2023 Vineflower contributors
|
|
66
|
+
|
|
67
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
68
|
+
you may not use this file except in compliance with the License.
|
|
69
|
+
You may obtain a copy of the License at
|
|
70
|
+
|
|
71
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
72
|
+
|
|
73
|
+
Unless required by applicable law or agreed to in writing, software
|
|
74
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
75
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
76
|
+
See the License for the specific language governing permissions and
|
|
77
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
# mcdev-mcp
|
|
2
|
+
|
|
3
|
+
[](https://github.com/weikengchen/mcdev-mcp/actions/workflows/ci.yml)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
An **MCP (Model Context Protocol) server** that empowers AI coding agents to work effectively with Minecraft mod development. Provides accurate, up-to-date access to decompiled Minecraft source code using official Mojang mappings.
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- **Decompiled Source Access** — Auto-downloads and decompiles Minecraft client using [Vineflower](https://github.com/Vineflower/vineflower)
|
|
11
|
+
- **Dev Snapshot Support** — Works with development snapshots (e.g., `26.1-snapshot-10`) that lack ProGuard mappings
|
|
12
|
+
- **Symbol Search** — Search for classes, methods, and fields by name
|
|
13
|
+
- **Source Retrieval** — Get full class source or individual methods with context
|
|
14
|
+
- **Package Exploration** — List all classes under a package path or discover available packages
|
|
15
|
+
- **Class Hierarchy** — Find subclasses and interface implementors
|
|
16
|
+
- **Call Graph Analysis** — Find method callers and callees across the entire codebase
|
|
17
|
+
- **Zero Configuration** — Auto-initializes on first use if sources are cached
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
> **Security note — `init` is intentionally terminal-only.** The MCP server only exposes read/query tools. Downloading and decompiling Minecraft sources must be triggered by you in the terminal; an AI agent connected to the server has no tool surface to trigger `init`, `rebuild`, `clean`, or `callgraph`.
|
|
22
|
+
|
|
23
|
+
### 1. Initialize in your terminal
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Download, decompile, and index Minecraft sources (~2-5 minutes)
|
|
27
|
+
npx mcdev-mcp init -v 1.21.11
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This command:
|
|
31
|
+
1. Downloads the Minecraft client JAR
|
|
32
|
+
2. Decompiles using Vineflower (pure Java, 8 threads)
|
|
33
|
+
3. Builds the symbol index (classes, methods, fields, inheritance)
|
|
34
|
+
4. Generates call graph for `mc_find_refs`
|
|
35
|
+
|
|
36
|
+
Data is stored under `~/.mcdev-mcp/`, so it persists across `npx` invocations.
|
|
37
|
+
|
|
38
|
+
### 2. Add to your MCP client
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"mcdev": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["-y", "mcdev-mcp", "serve"]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The `serve` subcommand starts the MCP server over stdio. Your MCP client (Claude Desktop, Cursor, etc.) launches it automatically — you never run `serve` directly.
|
|
52
|
+
|
|
53
|
+
### Supported Versions
|
|
54
|
+
|
|
55
|
+
| Version Type | Example | Notes |
|
|
56
|
+
|--------------|---------|-------|
|
|
57
|
+
| Dev snapshots | `26.1-snapshot-10` | Already unobfuscated, no mappings needed |
|
|
58
|
+
| Release (>= 1.21.11) | `1.21.11` | Uses pre-unobfuscated JAR when available |
|
|
59
|
+
| Old versions | `< 1.21.11` | Not supported |
|
|
60
|
+
|
|
61
|
+
> **Note:** Minecraft is now using a new versioning scheme (26.x). Versions before 1.21.11 are not supported.
|
|
62
|
+
|
|
63
|
+
### (Optional) Skip Call Graph
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Skip callgraph generation if you don't need mc_find_refs
|
|
67
|
+
npx mcdev-mcp init -v 1.21.11 --skip-callgraph
|
|
68
|
+
|
|
69
|
+
# Generate callgraph later
|
|
70
|
+
npx mcdev-mcp callgraph -v 1.21.11
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Verify Installation
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npx mcdev-mcp status
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> **Note:** The `mc_set_version` tool must be called before using any other MCP tools. If the version isn't initialized, the AI will be instructed to ask you to run `init`.
|
|
80
|
+
|
|
81
|
+
### Install from source (development)
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
git clone https://github.com/weikengchen/mcdev-mcp.git
|
|
85
|
+
cd mcdev-mcp
|
|
86
|
+
npm install
|
|
87
|
+
npm run build
|
|
88
|
+
|
|
89
|
+
# Use the local build instead of npx
|
|
90
|
+
node dist/cli.js init -v 1.21.11
|
|
91
|
+
node dist/cli.js serve # stdio MCP server; MCP clients launch this
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
> **Upgrading from an older version?** If you have a previous installation using DecompilerMC, run `npx mcdev-mcp clean --all` first to remove old cached data.
|
|
95
|
+
|
|
96
|
+
## MCP Tools
|
|
97
|
+
|
|
98
|
+
### Version Management
|
|
99
|
+
|
|
100
|
+
Before using any other tools, set the active Minecraft version:
|
|
101
|
+
|
|
102
|
+
### `mc_set_version`
|
|
103
|
+
Set the active Minecraft version for this session. Must be called before other tools.
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"version": "1.21.11"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `mc_list_versions`
|
|
112
|
+
List all Minecraft versions that have been initialized.
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Tool Requirements
|
|
119
|
+
|
|
120
|
+
| Tool | Requires `init` | Requires `callgraph` |
|
|
121
|
+
|------|-----------------|---------------------|
|
|
122
|
+
| `mc_set_version` | - | - |
|
|
123
|
+
| `mc_list_versions` | - | - |
|
|
124
|
+
| `mc_search` | ✓ | - |
|
|
125
|
+
| `mc_get_class` | ✓ | - |
|
|
126
|
+
| `mc_get_method` | ✓ | - |
|
|
127
|
+
| `mc_list_classes` | ✓ | - |
|
|
128
|
+
| `mc_list_packages` | ✓ | - |
|
|
129
|
+
| `mc_find_hierarchy` | ✓ | - |
|
|
130
|
+
| `mc_find_refs` | ✓ | ✓ |
|
|
131
|
+
|
|
132
|
+
### `mc_search`
|
|
133
|
+
Search for Minecraft classes, methods, or fields by name pattern.
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"query": "Minecraft",
|
|
138
|
+
"type": "class"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### `mc_get_class`
|
|
143
|
+
Get the full decompiled source code for a class.
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
{
|
|
147
|
+
"className": "net.minecraft.client.Minecraft"
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### `mc_get_method`
|
|
152
|
+
Get source code for a specific method with context.
|
|
153
|
+
|
|
154
|
+
```json
|
|
155
|
+
{
|
|
156
|
+
"className": "net.minecraft.client.Minecraft",
|
|
157
|
+
"methodName": "tick"
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### `mc_find_refs`
|
|
162
|
+
Find who calls a method (callers) or what it calls (callees).
|
|
163
|
+
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"className": "net.minecraft.client.MouseHandler",
|
|
167
|
+
"methodName": "setup",
|
|
168
|
+
"direction": "callers"
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
| Direction | Description |
|
|
173
|
+
|-----------|-------------|
|
|
174
|
+
| `callers` | Find methods that call this method |
|
|
175
|
+
| `callees` | Find methods this method calls |
|
|
176
|
+
|
|
177
|
+
> **Note:** Requires callgraph to be generated (included in `init` by default).
|
|
178
|
+
|
|
179
|
+
### `mc_list_classes`
|
|
180
|
+
List all classes under a specific package path (includes subpackages).
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"packagePath": "net.minecraft.client.gui.screens"
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### `mc_list_packages`
|
|
189
|
+
List all available packages. Optionally filter by namespace.
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"namespace": "minecraft"
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
| Namespace | Description |
|
|
198
|
+
|-----------|-------------|
|
|
199
|
+
| `minecraft` | Minecraft client classes |
|
|
200
|
+
| `fabric` | Fabric API classes (if indexed) |
|
|
201
|
+
|
|
202
|
+
### `mc_find_hierarchy`
|
|
203
|
+
Find classes that extend or implement a given class or interface.
|
|
204
|
+
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"className": "net.minecraft.world.entity.Entity",
|
|
208
|
+
"direction": "subclasses"
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
| Direction | Description |
|
|
213
|
+
|-----------|-------------|
|
|
214
|
+
| `subclasses` | Classes that extend this class |
|
|
215
|
+
| `implementors` | Classes that implement this interface |
|
|
216
|
+
|
|
217
|
+
## Requirements
|
|
218
|
+
|
|
219
|
+
| Dependency | Version | Purpose |
|
|
220
|
+
|------------|---------|---------|
|
|
221
|
+
| Node.js | 18+ | Runtime |
|
|
222
|
+
| Java | 8+ | Decompilation (Vineflower) & callgraph |
|
|
223
|
+
| ~2GB | disk | Decompiled sources + cache |
|
|
224
|
+
|
|
225
|
+
> **Note:** Java 17+ is recommended for the `callgraph` command due to Gradle compatibility.
|
|
226
|
+
|
|
227
|
+
## CLI Commands
|
|
228
|
+
|
|
229
|
+
Invoke via `npx mcdev-mcp <command>` (or `node dist/cli.js <command>` from a source checkout).
|
|
230
|
+
|
|
231
|
+
| Command | Description |
|
|
232
|
+
|---------|-------------|
|
|
233
|
+
| `serve` | Start the MCP server over stdio (launched by MCP clients — not run by humans) |
|
|
234
|
+
| `init -v <version>` | Download, decompile, index Minecraft sources, and generate callgraph |
|
|
235
|
+
| `callgraph -v <version>` | Generate call graph for `mc_find_refs` |
|
|
236
|
+
| `status` | Show all initialized versions |
|
|
237
|
+
| `rebuild -v <version>` | Rebuild the symbol index from cached sources |
|
|
238
|
+
| `clean --all` | Clean all cached data |
|
|
239
|
+
|
|
240
|
+
### Re-indexing
|
|
241
|
+
|
|
242
|
+
To re-index a version:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Clean existing data for a version
|
|
246
|
+
npx mcdev-mcp clean -v 1.21.11 --all
|
|
247
|
+
|
|
248
|
+
# Re-initialize
|
|
249
|
+
npx mcdev-mcp init -v 1.21.11
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Architecture
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
mcdev-mcp/
|
|
256
|
+
├── src/
|
|
257
|
+
│ ├── index.ts # MCP server entry point
|
|
258
|
+
│ ├── cli.ts # CLI commands
|
|
259
|
+
│ ├── tools/ # MCP tool implementations
|
|
260
|
+
│ ├── decompiler/ # Vineflower integration (pure TypeScript/Java)
|
|
261
|
+
│ ├── indexer/ # Symbol index builder
|
|
262
|
+
│ ├── callgraph/ # Call graph generation & queries
|
|
263
|
+
│ └── storage/ # Source & index storage
|
|
264
|
+
└── dist/ # Compiled output
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### How It Works
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
271
|
+
│ MCP Client (AI Agent) │
|
|
272
|
+
└─────────────────────────────────────────────────────────────┘
|
|
273
|
+
│
|
|
274
|
+
▼
|
|
275
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
276
|
+
│ mcdev-mcp Server │
|
|
277
|
+
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ │
|
|
278
|
+
│ │ search │ │get_class │ │get_method│ │ find_refs │ │
|
|
279
|
+
│ └────┬────┘ └────┬─────┘ └────┬─────┘ └─────┬─────┘ │
|
|
280
|
+
│ ┌─────────────┐ ┌───────────────┐ ┌─────────────────┐ │
|
|
281
|
+
│ │list_classes │ │list_packages │ │ find_hierarchy │ │
|
|
282
|
+
│ └──────┬──────┘ └───────┬───────┘ └────────┬────────┘ │
|
|
283
|
+
│ └────────────────┼──────────────────┘ │
|
|
284
|
+
│ │ │
|
|
285
|
+
│ ┌──────────────────┴───────────────────┐ │
|
|
286
|
+
│ ▼ ▼ │
|
|
287
|
+
│ ┌──────────┐ ┌─────────────┐ │
|
|
288
|
+
│ │ Index │ │ Callgraph │ │
|
|
289
|
+
│ │ (JSON) │ │ (SQLite) │ │
|
|
290
|
+
│ └────┬─────┘ └──────┬──────┘ │
|
|
291
|
+
└───────┼────────────────────────────────────────┼────────────┘
|
|
292
|
+
▼ ▼
|
|
293
|
+
┌───────────────────┐ ┌─────────────────────┐
|
|
294
|
+
│ Decompiled Src │ │ java-callgraph2 │
|
|
295
|
+
│ (Vineflower) │ │ (static analysis) │
|
|
296
|
+
└───────────────────┘ └─────────────────────┘
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for detailed design documentation.
|
|
300
|
+
|
|
301
|
+
## Directory Structure
|
|
302
|
+
|
|
303
|
+
```
|
|
304
|
+
~/.mcdev-mcp/
|
|
305
|
+
├── tools/
|
|
306
|
+
│ └── vineflower.jar # Downloaded once
|
|
307
|
+
├── java-callgraph2/ # Call graph tool
|
|
308
|
+
├── cache/
|
|
309
|
+
│ └── {version}/
|
|
310
|
+
│ ├── jars/ # Downloaded JARs
|
|
311
|
+
│ └── client/ # Decompiled Minecraft sources
|
|
312
|
+
├── index/
|
|
313
|
+
│ └── {version}/
|
|
314
|
+
│ ├── manifest.json # Index metadata
|
|
315
|
+
│ └── minecraft/ # Per-package symbol indices
|
|
316
|
+
└── tmp/ # Temporary files (cleaned by --all)
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Development
|
|
320
|
+
|
|
321
|
+
```bash
|
|
322
|
+
npm run build # Compile TypeScript
|
|
323
|
+
npm test # Run tests
|
|
324
|
+
npm run lint # Lint code
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Limitations
|
|
328
|
+
|
|
329
|
+
- **Static Analysis Only**: `mc_find_refs` cannot trace calls through reflection, JNI callbacks, or lambda/method references created dynamically
|
|
330
|
+
- **Client Only**: Server-side classes are not included
|
|
331
|
+
|
|
332
|
+
## Legal Notice
|
|
333
|
+
|
|
334
|
+
This tool decompiles Minecraft source code for development reference purposes. Please respect Mojang's intellectual property:
|
|
335
|
+
|
|
336
|
+
**You MAY:**
|
|
337
|
+
- Decompile and study the code for understanding and learning
|
|
338
|
+
- Use the knowledge to develop mods that don't contain substantial Mojang code
|
|
339
|
+
- Reference class/method names for mod development
|
|
340
|
+
|
|
341
|
+
**You may NOT:**
|
|
342
|
+
- Distribute decompiled source code
|
|
343
|
+
- Distribute modified versions of Minecraft
|
|
344
|
+
- Use decompiled code commercially without permission
|
|
345
|
+
|
|
346
|
+
Per the [Minecraft EULA](https://www.minecraft.net/eula): *"You may not distribute any Modded Versions of our game or software"* and *"Mods are okay to distribute; hacked versions or Modded Versions of the game client or server software are not okay to distribute."*
|
|
347
|
+
|
|
348
|
+
This tool is for **reference only** — do not copy decompiled code directly into your projects.
|
|
349
|
+
|
|
350
|
+
## Third-Party Components
|
|
351
|
+
|
|
352
|
+
This project includes or uses third-party software under the following licenses:
|
|
353
|
+
|
|
354
|
+
- **[DecompilerMC](https://github.com/hube12/DecompilerMC)** (MIT) — Decompiler logic adapted and translated from Python to TypeScript in `src/decompiler/`
|
|
355
|
+
- **[Vineflower](https://github.com/Vineflower/vineflower)** (Apache-2.0) — Java decompiler used for source generation
|
|
356
|
+
- **[java-callgraph2](https://github.com/Adrninistrator/java-callgraph2)** — Cloned at runtime for static call graph generation
|
|
357
|
+
|
|
358
|
+
Additional runtime dependencies (downloaded/used):
|
|
359
|
+
- **[Mojang](https://www.minecraft.net/)** — Official ProGuard mappings and Minecraft client JAR
|
|
360
|
+
|
|
361
|
+
See [LICENSE](LICENSE) for full license text and third-party attributions.
|
|
362
|
+
|
|
363
|
+
## License
|
|
364
|
+
|
|
365
|
+
[MIT](LICENSE) — Copyright (c) 2025 mcdev-mcp contributors
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function getJavaCGDir(): string;
|
|
2
|
+
export declare function getJavaCGJarPath(): string;
|
|
3
|
+
export declare function getJavaCGLibDir(): string;
|
|
4
|
+
export declare function getDecompilerMCDir(): string;
|
|
5
|
+
export declare function getRemappedJarPath(version: string): string;
|
|
6
|
+
export type ProgressCallback = (stage: string, progress: number, message: string) => void;
|
|
7
|
+
export declare function ensureJavaCG(progressCb?: ProgressCallback): Promise<string>;
|
|
8
|
+
export declare function ensureRemappedJar(version: string, progressCb?: ProgressCallback): Promise<string>;
|
|
9
|
+
export declare function generateCallgraph(version: string, progressCb?: ProgressCallback): Promise<string>;
|
|
10
|
+
export declare function hasCallgraphDb(version: string): boolean;
|
|
11
|
+
export declare function parseCallgraphAndCreateDb(version: string, callgraphFile: string, progressCb?: ProgressCallback): number;
|
|
12
|
+
export declare function ensureCallgraph(version: string, progressCb?: ProgressCallback): Promise<void>;
|
|
13
|
+
export { openDb, closeDb, findCallers, findCallees, searchMethods, getCallgraphStats } from './query.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/callgraph/index.ts"],"names":[],"mappings":"AASA,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAGzC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAE1F,wBAAsB,YAAY,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAwDjF;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0BvG;AAED,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+BvG;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAqDvH;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAInG;AAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import Database from 'better-sqlite3';
|
|
5
|
+
import { ensureDir, getHomeDir, getMinecraftJarPath } from '../utils/paths.js';
|
|
6
|
+
import { getCallgraphDir, getCallgraphDbPath } from './query.js';
|
|
7
|
+
const SPECIAL_SOURCE_VERSION = '1.11.4';
|
|
8
|
+
export function getJavaCGDir() {
|
|
9
|
+
return path.join(getHomeDir(), 'java-callgraph2');
|
|
10
|
+
}
|
|
11
|
+
export function getJavaCGJarPath() {
|
|
12
|
+
// gen_run_jar creates jar_output_dir/jar/run_javacg2.jar
|
|
13
|
+
return path.join(getJavaCGDir(), 'jar_output_dir', 'jar', 'run_javacg2.jar');
|
|
14
|
+
}
|
|
15
|
+
export function getJavaCGLibDir() {
|
|
16
|
+
return path.join(getJavaCGDir(), 'jar_output_dir', 'lib');
|
|
17
|
+
}
|
|
18
|
+
export function getDecompilerMCDir() {
|
|
19
|
+
return path.join(getHomeDir(), 'DecompilerMC');
|
|
20
|
+
}
|
|
21
|
+
export function getRemappedJarPath(version) {
|
|
22
|
+
return path.join(getCallgraphDir(version), 'client-remapped.jar');
|
|
23
|
+
}
|
|
24
|
+
export async function ensureJavaCG(progressCb) {
|
|
25
|
+
const jarPath = getJavaCGJarPath();
|
|
26
|
+
if (fs.existsSync(jarPath))
|
|
27
|
+
return jarPath;
|
|
28
|
+
const jcDir = getJavaCGDir();
|
|
29
|
+
const gradlewPath = path.join(jcDir, 'gradlew');
|
|
30
|
+
const gradlewBatPath = path.join(jcDir, 'gradlew.bat');
|
|
31
|
+
const isWindows = process.platform === 'win32';
|
|
32
|
+
const gradlewCmd = isWindows ? 'gradlew.bat' : 'gradlew';
|
|
33
|
+
const gradlewExists = isWindows ? fs.existsSync(gradlewBatPath) : fs.existsSync(gradlewPath);
|
|
34
|
+
if (progressCb)
|
|
35
|
+
progressCb('javacg', 0, 'Cloning java-callgraph2...');
|
|
36
|
+
// Clone if gradlew doesn't exist (covers incomplete/corrupted clones)
|
|
37
|
+
if (!gradlewExists) {
|
|
38
|
+
if (fs.existsSync(jcDir)) {
|
|
39
|
+
fs.rmSync(jcDir, { recursive: true, force: true });
|
|
40
|
+
}
|
|
41
|
+
await new Promise((resolve, reject) => {
|
|
42
|
+
const proc = spawn('git', ['clone', 'https://github.com/Adrninistrator/java-callgraph2.git', jcDir], { stdio: 'inherit' });
|
|
43
|
+
proc.on('error', (err) => reject(new Error(`git clone failed: ${err.message}`)));
|
|
44
|
+
proc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`git clone failed: ${code}`)));
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// Update gradle wrapper to Gradle 9.3.1 (supports Java 25)
|
|
48
|
+
const gradleWrapperProps = path.join(jcDir, 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
|
49
|
+
if (fs.existsSync(gradleWrapperProps)) {
|
|
50
|
+
fs.writeFileSync(gradleWrapperProps, 'distributionUrl=https\\://services.gradle.org/distributions/gradle-9.3.1-bin.zip\n');
|
|
51
|
+
}
|
|
52
|
+
// Make gradlew executable (Unix only)
|
|
53
|
+
if (!isWindows && fs.existsSync(gradlewPath)) {
|
|
54
|
+
fs.chmodSync(gradlewPath, 0o755);
|
|
55
|
+
}
|
|
56
|
+
// Patch build.gradle for Gradle 9.x compatibility (remove deprecated properties)
|
|
57
|
+
const buildGradlePath = path.join(jcDir, 'build.gradle');
|
|
58
|
+
if (fs.existsSync(buildGradlePath)) {
|
|
59
|
+
let buildGradle = fs.readFileSync(buildGradlePath, 'utf-8');
|
|
60
|
+
buildGradle = buildGradle.replace(/sourceCompatibility\s*=\s*1\.8\s*\n?/g, '');
|
|
61
|
+
buildGradle = buildGradle.replace(/targetCompatibility\s*=\s*1\.8\s*\n?/g, '');
|
|
62
|
+
fs.writeFileSync(buildGradlePath, buildGradle);
|
|
63
|
+
}
|
|
64
|
+
if (progressCb)
|
|
65
|
+
progressCb('javacg', 50, 'Building java-callgraph2 with Gradle 9.3...');
|
|
66
|
+
// Use gen_run_jar task instead of shadowJar (creates run_javacg2.jar with lib/ folder)
|
|
67
|
+
await new Promise((resolve, reject) => {
|
|
68
|
+
const proc = spawn(gradlewCmd, ['gen_run_jar'], { cwd: jcDir, stdio: 'inherit', shell: isWindows });
|
|
69
|
+
proc.on('error', (err) => reject(new Error(`gradle build failed: ${err.message}`)));
|
|
70
|
+
proc.on('close', (code) => code === 0 ? resolve() : reject(new Error(`gradle build failed: ${code}`)));
|
|
71
|
+
});
|
|
72
|
+
if (progressCb)
|
|
73
|
+
progressCb('javacg', 100, 'java-callgraph2 ready.');
|
|
74
|
+
return jarPath;
|
|
75
|
+
}
|
|
76
|
+
export async function ensureRemappedJar(version, progressCb) {
|
|
77
|
+
const remappedJar = getRemappedJarPath(version);
|
|
78
|
+
if (fs.existsSync(remappedJar))
|
|
79
|
+
return remappedJar;
|
|
80
|
+
const clientJar = getMinecraftJarPath(version);
|
|
81
|
+
const dmcdDir = getDecompilerMCDir();
|
|
82
|
+
const tsrgMappings = path.join(dmcdDir, 'mappings', version, 'client.tsrg');
|
|
83
|
+
const specialSource = path.join(dmcdDir, 'lib', `SpecialSource-${SPECIAL_SOURCE_VERSION}.jar`);
|
|
84
|
+
// For dev snapshots (no mappings), use client.jar directly
|
|
85
|
+
if (!fs.existsSync(tsrgMappings)) {
|
|
86
|
+
if (progressCb)
|
|
87
|
+
progressCb('remap', 0, 'No mappings found, using unobfuscated jar directly...');
|
|
88
|
+
ensureDir(path.dirname(remappedJar));
|
|
89
|
+
fs.copyFileSync(clientJar, remappedJar);
|
|
90
|
+
if (progressCb)
|
|
91
|
+
progressCb('remap', 100, 'Jar ready (no remapping needed).');
|
|
92
|
+
return remappedJar;
|
|
93
|
+
}
|
|
94
|
+
if (progressCb)
|
|
95
|
+
progressCb('remap', 0, 'Creating remapped jar...');
|
|
96
|
+
ensureDir(path.dirname(remappedJar));
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
const proc = spawn('java', ['-Xmx2g', '-jar', specialSource, '--in-jar', clientJar, '--out-jar', remappedJar, '--srg-in', tsrgMappings, '--kill-lvt'], { stdio: 'inherit' });
|
|
99
|
+
proc.on('error', (err) => reject(new Error(`SpecialSource failed: ${err.message}`)));
|
|
100
|
+
proc.on('close', (code) => code === 0 && fs.existsSync(remappedJar) ? (progressCb?.('remap', 100, 'Remapped jar created.'), resolve(remappedJar)) : reject(new Error(`SpecialSource failed: ${code}`)));
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
export async function generateCallgraph(version, progressCb) {
|
|
104
|
+
const javacgPath = await ensureJavaCG(progressCb);
|
|
105
|
+
const libDir = getJavaCGLibDir();
|
|
106
|
+
const outputDir = getCallgraphDir(version);
|
|
107
|
+
const remappedJar = await ensureRemappedJar(version, progressCb);
|
|
108
|
+
// java-callgraph2 outputs to {jar}-output_javacg2/method_call.txt
|
|
109
|
+
const expectedOutput = path.join(outputDir, `${path.basename(remappedJar)}-output_javacg2`, 'method_call.txt');
|
|
110
|
+
if (fs.existsSync(expectedOutput))
|
|
111
|
+
return expectedOutput;
|
|
112
|
+
ensureDir(outputDir);
|
|
113
|
+
if (progressCb)
|
|
114
|
+
progressCb('callgraph', 0, 'Generating callgraph...');
|
|
115
|
+
// java-callgraph2 expects config in _javacg2_config/ directory
|
|
116
|
+
const configDir = path.join(outputDir, '_javacg2_config');
|
|
117
|
+
ensureDir(configDir);
|
|
118
|
+
fs.writeFileSync(path.join(configDir, 'jar_dir.properties'), remappedJar);
|
|
119
|
+
fs.writeFileSync(path.join(configDir, 'config.properties'), `output.dir=${outputDir}\nparse.method.call.info=1\n`);
|
|
120
|
+
// Build classpath: jar + all libs
|
|
121
|
+
const libJars = fs.existsSync(libDir)
|
|
122
|
+
? fs.readdirSync(libDir).filter(f => f.endsWith('.jar')).map(f => path.join(libDir, f))
|
|
123
|
+
: [];
|
|
124
|
+
const classpathSep = process.platform === 'win32' ? ';' : ':';
|
|
125
|
+
const classpath = [javacgPath, ...libJars].join(classpathSep);
|
|
126
|
+
return new Promise((resolve, reject) => {
|
|
127
|
+
const proc = spawn('java', ['-Xmx4g', '-cp', classpath, 'com.adrninistrator.javacg2.entry.JavaCG2Entry', configDir], { cwd: outputDir, stdio: 'inherit' });
|
|
128
|
+
proc.on('error', (err) => reject(new Error(`java-callgraph2 failed: ${err.message}`)));
|
|
129
|
+
proc.on('close', (code) => code === 0 || fs.existsSync(expectedOutput) ? (progressCb?.('callgraph', 100, 'Callgraph generated.'), resolve(expectedOutput)) : reject(new Error(`java-callgraph2 failed: ${code}`)));
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export function hasCallgraphDb(version) {
|
|
133
|
+
return fs.existsSync(getCallgraphDbPath(version));
|
|
134
|
+
}
|
|
135
|
+
export function parseCallgraphAndCreateDb(version, callgraphFile, progressCb) {
|
|
136
|
+
if (progressCb)
|
|
137
|
+
progressCb('index', 0, 'Parsing callgraph...');
|
|
138
|
+
const content = fs.readFileSync(callgraphFile, 'utf-8');
|
|
139
|
+
const lines = content.split('\n');
|
|
140
|
+
const dbPath = getCallgraphDbPath(version);
|
|
141
|
+
if (fs.existsSync(dbPath))
|
|
142
|
+
fs.unlinkSync(dbPath);
|
|
143
|
+
const db = new Database(dbPath);
|
|
144
|
+
db.exec(`CREATE TABLE calls (id INTEGER PRIMARY KEY, caller_class TEXT, caller_method TEXT, caller_desc TEXT, callee_class TEXT, callee_method TEXT, callee_desc TEXT, line_number INTEGER); CREATE INDEX idx_callee ON calls(callee_class, callee_method); CREATE INDEX idx_caller ON calls(caller_class, caller_method);`);
|
|
145
|
+
const insert = db.prepare('INSERT INTO calls VALUES (NULL, ?, ?, ?, ?, ?, ?, ?)');
|
|
146
|
+
const insertMany = db.transaction((items) => { for (const item of items)
|
|
147
|
+
insert.run(...item); });
|
|
148
|
+
let count = 0;
|
|
149
|
+
const batch = [];
|
|
150
|
+
// Format: seq num caller callee line return_type ...
|
|
151
|
+
// caller format: class:method(args)
|
|
152
|
+
// callee format: (TYPE)class:method(args) where TYPE is VIR/STA/SPE
|
|
153
|
+
for (const line of lines) {
|
|
154
|
+
if (!line.trim() || line.startsWith('#'))
|
|
155
|
+
continue;
|
|
156
|
+
const parts = line.split('\t');
|
|
157
|
+
if (parts.length < 5)
|
|
158
|
+
continue;
|
|
159
|
+
const callerRaw = parts[2] || '';
|
|
160
|
+
const calleeRaw = parts[3] || '';
|
|
161
|
+
const lineNumber = parts[4] ? parseInt(parts[4], 10) : null;
|
|
162
|
+
// Parse caller: class:method(args)
|
|
163
|
+
const callerMatch = callerRaw.match(/^(.+):(.+)(\([^)]*\))$/);
|
|
164
|
+
// Parse callee: (TYPE)class:method(args)
|
|
165
|
+
const calleeMatch = calleeRaw.match(/^\([A-Z]+\)(.+):(.+)(\([^)]*\))$/);
|
|
166
|
+
if (callerMatch && calleeMatch) {
|
|
167
|
+
batch.push([
|
|
168
|
+
callerMatch[1], callerMatch[2], callerMatch[3],
|
|
169
|
+
calleeMatch[1], calleeMatch[2], calleeMatch[3],
|
|
170
|
+
lineNumber
|
|
171
|
+
]);
|
|
172
|
+
if (batch.length >= 10000) {
|
|
173
|
+
insertMany(batch);
|
|
174
|
+
count += batch.length;
|
|
175
|
+
batch.length = 0;
|
|
176
|
+
if (progressCb && count % 100000 === 0)
|
|
177
|
+
progressCb('index', 50, `Indexed ${count}...`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (batch.length > 0) {
|
|
182
|
+
insertMany(batch);
|
|
183
|
+
count += batch.length;
|
|
184
|
+
}
|
|
185
|
+
db.pragma('optimize');
|
|
186
|
+
db.close();
|
|
187
|
+
if (progressCb)
|
|
188
|
+
progressCb('index', 100, `Indexed ${count} call references.`);
|
|
189
|
+
return count;
|
|
190
|
+
}
|
|
191
|
+
export async function ensureCallgraph(version, progressCb) {
|
|
192
|
+
if (hasCallgraphDb(version)) {
|
|
193
|
+
if (progressCb)
|
|
194
|
+
progressCb('callgraph', 100, 'Callgraph database ready.');
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const callgraphFile = await generateCallgraph(version, progressCb);
|
|
198
|
+
parseCallgraphAndCreateDb(version, callgraphFile, progressCb);
|
|
199
|
+
}
|
|
200
|
+
export { openDb, closeDb, findCallers, findCallees, searchMethods, getCallgraphStats } from './query.js';
|
|
201
|
+
//# sourceMappingURL=index.js.map
|