@adhisang/minecraft-modding-mcp 3.2.0 → 4.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/CHANGELOG.md +25 -0
- package/README.md +25 -18
- package/dist/cache-registry.d.ts +1 -1
- package/dist/cache-registry.js +10 -2
- package/dist/config.d.ts +10 -1
- package/dist/config.js +52 -1
- package/dist/entry-tools/analyze-symbol-service.d.ts +2 -2
- package/dist/entry-tools/analyze-symbol-service.js +13 -2
- package/dist/entry-tools/inspect-minecraft-service.d.ts +20 -20
- package/dist/entry-tools/inspect-minecraft-service.js +8 -2
- package/dist/entry-tools/manage-cache-service.d.ts +4 -4
- package/dist/entry-tools/validate-project-service.js +84 -4
- package/dist/index.js +99 -33
- package/dist/lru-list.d.ts +31 -0
- package/dist/lru-list.js +102 -0
- package/dist/mapping-pipeline-service.d.ts +10 -1
- package/dist/mapping-pipeline-service.js +13 -1
- package/dist/mapping-service.d.ts +12 -0
- package/dist/mapping-service.js +252 -10
- package/dist/mixin-validator.js +22 -7
- package/dist/observability.d.ts +18 -1
- package/dist/observability.js +44 -1
- package/dist/response-utils.d.ts +44 -10
- package/dist/response-utils.js +131 -17
- package/dist/source-resolver.d.ts +9 -1
- package/dist/source-resolver.js +14 -6
- package/dist/source-service.d.ts +97 -1
- package/dist/source-service.js +922 -113
- package/dist/storage/artifacts-repo.d.ts +4 -1
- package/dist/storage/artifacts-repo.js +33 -5
- package/dist/storage/files-repo.d.ts +0 -2
- package/dist/storage/files-repo.js +0 -11
- package/dist/storage/migrations.d.ts +1 -1
- package/dist/storage/migrations.js +10 -2
- package/dist/storage/schema.d.ts +2 -0
- package/dist/storage/schema.js +25 -0
- package/dist/types.d.ts +3 -0
- package/package.json +3 -1
|
@@ -3,6 +3,7 @@ import type { ArtifactProvenance, ArtifactRow, SourceMapping, SourceOrigin } fro
|
|
|
3
3
|
type SqliteDatabase = InstanceType<typeof Database>;
|
|
4
4
|
interface UpsertArtifactInput {
|
|
5
5
|
artifactId: string;
|
|
6
|
+
alias?: string;
|
|
6
7
|
origin: SourceOrigin;
|
|
7
8
|
coordinate?: string;
|
|
8
9
|
version?: string;
|
|
@@ -27,6 +28,7 @@ export declare class ArtifactsRepo {
|
|
|
27
28
|
private readonly upsertStmt;
|
|
28
29
|
private readonly getStmt;
|
|
29
30
|
private readonly touchStmt;
|
|
31
|
+
private readonly setAliasStmt;
|
|
30
32
|
private readonly deleteStmt;
|
|
31
33
|
private readonly listStmt;
|
|
32
34
|
private readonly countStmt;
|
|
@@ -34,8 +36,9 @@ export declare class ArtifactsRepo {
|
|
|
34
36
|
private readonly listLruWithContentBytesStmt;
|
|
35
37
|
constructor(db: SqliteDatabase);
|
|
36
38
|
upsertArtifact(input: UpsertArtifactInput): void;
|
|
37
|
-
getArtifact(
|
|
39
|
+
getArtifact(artifactIdOrAlias: string): ArtifactRow | undefined;
|
|
38
40
|
touchArtifact(artifactId: string, timestamp: string): void;
|
|
41
|
+
setAlias(artifactId: string, alias: string): void;
|
|
39
42
|
deleteArtifact(artifactId: string): void;
|
|
40
43
|
listArtifactsByLru(limit: number): ArtifactRow[];
|
|
41
44
|
countArtifacts(): number;
|
|
@@ -35,6 +35,7 @@ function parseQualityFlags(value) {
|
|
|
35
35
|
function toArtifactRow(record) {
|
|
36
36
|
return {
|
|
37
37
|
artifactId: record.artifact_id,
|
|
38
|
+
alias: record.alias ?? undefined,
|
|
38
39
|
origin: record.origin,
|
|
39
40
|
coordinate: record.coordinate ?? undefined,
|
|
40
41
|
version: record.version ?? undefined,
|
|
@@ -59,6 +60,7 @@ export class ArtifactsRepo {
|
|
|
59
60
|
upsertStmt;
|
|
60
61
|
getStmt;
|
|
61
62
|
touchStmt;
|
|
63
|
+
setAliasStmt;
|
|
62
64
|
deleteStmt;
|
|
63
65
|
listStmt;
|
|
64
66
|
countStmt;
|
|
@@ -68,11 +70,12 @@ export class ArtifactsRepo {
|
|
|
68
70
|
this.db = db;
|
|
69
71
|
this.upsertStmt = this.db.prepare(`
|
|
70
72
|
INSERT INTO artifacts (
|
|
71
|
-
artifact_id, origin, coordinate, version, binary_jar_path, source_jar_path, repo_url, requested_mapping, mapping_applied, provenance_json, quality_flags_json, artifact_signature, is_decompiled, created_at, updated_at
|
|
73
|
+
artifact_id, alias, origin, coordinate, version, binary_jar_path, source_jar_path, repo_url, requested_mapping, mapping_applied, provenance_json, quality_flags_json, artifact_signature, is_decompiled, created_at, updated_at
|
|
72
74
|
) VALUES (
|
|
73
|
-
@artifact_id, @origin, @coordinate, @version, @binary_jar_path, @source_jar_path, @repo_url, @requested_mapping, @mapping_applied, @provenance_json, @quality_flags_json, @artifact_signature, @is_decompiled, @created_at, @updated_at
|
|
75
|
+
@artifact_id, @alias, @origin, @coordinate, @version, @binary_jar_path, @source_jar_path, @repo_url, @requested_mapping, @mapping_applied, @provenance_json, @quality_flags_json, @artifact_signature, @is_decompiled, @created_at, @updated_at
|
|
74
76
|
)
|
|
75
77
|
ON CONFLICT(artifact_id) DO UPDATE SET
|
|
78
|
+
alias = excluded.alias,
|
|
76
79
|
origin = excluded.origin,
|
|
77
80
|
coordinate = excluded.coordinate,
|
|
78
81
|
version = excluded.version,
|
|
@@ -87,9 +90,12 @@ export class ArtifactsRepo {
|
|
|
87
90
|
is_decompiled = excluded.is_decompiled,
|
|
88
91
|
updated_at = excluded.updated_at
|
|
89
92
|
`);
|
|
93
|
+
// artifact_id is a 64-char SHA hex; alias is `<type>-<...>-<6charhex>` containing
|
|
94
|
+
// dashes/letters. The two namespaces cannot collide, so OR-matching is unambiguous.
|
|
90
95
|
this.getStmt = this.db.prepare(`
|
|
91
96
|
SELECT
|
|
92
97
|
artifact_id,
|
|
98
|
+
alias,
|
|
93
99
|
origin,
|
|
94
100
|
coordinate,
|
|
95
101
|
version,
|
|
@@ -105,17 +111,28 @@ export class ArtifactsRepo {
|
|
|
105
111
|
created_at,
|
|
106
112
|
updated_at
|
|
107
113
|
FROM artifacts
|
|
108
|
-
WHERE artifact_id = ?
|
|
114
|
+
WHERE artifact_id = ? OR alias = ?
|
|
115
|
+
LIMIT 1
|
|
109
116
|
`);
|
|
110
117
|
this.touchStmt = this.db.prepare(`
|
|
111
118
|
UPDATE artifacts
|
|
112
119
|
SET updated_at = ?
|
|
113
120
|
WHERE artifact_id = ?
|
|
121
|
+
`);
|
|
122
|
+
// Persists alias on cache-hit / migrated-row paths where upsertArtifact would
|
|
123
|
+
// otherwise be skipped. Conditional WHERE keeps it idempotent and avoids a
|
|
124
|
+
// pointless write when alias is already correct.
|
|
125
|
+
this.setAliasStmt = this.db.prepare(`
|
|
126
|
+
UPDATE artifacts
|
|
127
|
+
SET alias = ?
|
|
128
|
+
WHERE artifact_id = ?
|
|
129
|
+
AND (alias IS NULL OR alias <> ?)
|
|
114
130
|
`);
|
|
115
131
|
this.deleteStmt = this.db.prepare(`DELETE FROM artifacts WHERE artifact_id = ?`);
|
|
116
132
|
this.listStmt = this.db.prepare(`
|
|
117
133
|
SELECT
|
|
118
134
|
artifact_id,
|
|
135
|
+
alias,
|
|
119
136
|
origin,
|
|
120
137
|
coordinate,
|
|
121
138
|
version,
|
|
@@ -157,6 +174,7 @@ export class ArtifactsRepo {
|
|
|
157
174
|
upsertArtifact(input) {
|
|
158
175
|
this.upsertStmt.run({
|
|
159
176
|
artifact_id: input.artifactId,
|
|
177
|
+
alias: input.alias ?? null,
|
|
160
178
|
origin: input.origin,
|
|
161
179
|
coordinate: input.coordinate ?? null,
|
|
162
180
|
version: input.version ?? null,
|
|
@@ -173,8 +191,11 @@ export class ArtifactsRepo {
|
|
|
173
191
|
updated_at: input.timestamp
|
|
174
192
|
});
|
|
175
193
|
}
|
|
176
|
-
|
|
177
|
-
|
|
194
|
+
// Accepts either an artifact_id (64-char SHA hex) or a human-readable alias
|
|
195
|
+
// (e.g. `mc-1.21.10-mojang-merged-5ad2e7`). The two key namespaces cannot
|
|
196
|
+
// collide because aliases always contain `-` and a non-hex prefix.
|
|
197
|
+
getArtifact(artifactIdOrAlias) {
|
|
198
|
+
const row = this.getStmt.get([artifactIdOrAlias, artifactIdOrAlias]);
|
|
178
199
|
if (!row) {
|
|
179
200
|
return undefined;
|
|
180
201
|
}
|
|
@@ -183,6 +204,13 @@ export class ArtifactsRepo {
|
|
|
183
204
|
touchArtifact(artifactId, timestamp) {
|
|
184
205
|
this.touchStmt.run([timestamp, artifactId]);
|
|
185
206
|
}
|
|
207
|
+
// Backfills or rotates the alias for an existing row. Used by warm-cache
|
|
208
|
+
// resolveArtifact paths where upsertArtifact is skipped, so a freshly
|
|
209
|
+
// computed alias still reaches the DB and stays in sync with the value the
|
|
210
|
+
// caller just received in the response.
|
|
211
|
+
setAlias(artifactId, alias) {
|
|
212
|
+
this.setAliasStmt.run([alias, artifactId, alias]);
|
|
213
|
+
}
|
|
186
214
|
deleteArtifact(artifactId) {
|
|
187
215
|
this.deleteStmt.run([artifactId]);
|
|
188
216
|
}
|
|
@@ -43,8 +43,6 @@ export declare class FilesRepo {
|
|
|
43
43
|
private readonly db;
|
|
44
44
|
private readonly deleteStmt;
|
|
45
45
|
private readonly insertFilesStmt;
|
|
46
|
-
private readonly insertFtsStmt;
|
|
47
|
-
private readonly deleteFtsStmt;
|
|
48
46
|
private readonly getContentStmt;
|
|
49
47
|
private readonly listStmt;
|
|
50
48
|
private readonly listRowsStmt;
|
|
@@ -125,8 +125,6 @@ export class FilesRepo {
|
|
|
125
125
|
db;
|
|
126
126
|
deleteStmt;
|
|
127
127
|
insertFilesStmt;
|
|
128
|
-
insertFtsStmt;
|
|
129
|
-
deleteFtsStmt;
|
|
130
128
|
getContentStmt;
|
|
131
129
|
listStmt;
|
|
132
130
|
listRowsStmt;
|
|
@@ -138,17 +136,10 @@ export class FilesRepo {
|
|
|
138
136
|
this.db = db;
|
|
139
137
|
this.deleteStmt = this.db.prepare(`
|
|
140
138
|
DELETE FROM files WHERE artifact_id = ?
|
|
141
|
-
`);
|
|
142
|
-
this.deleteFtsStmt = this.db.prepare(`
|
|
143
|
-
DELETE FROM files_fts WHERE artifact_id = ?
|
|
144
139
|
`);
|
|
145
140
|
this.insertFilesStmt = this.db.prepare(`
|
|
146
141
|
INSERT INTO files (artifact_id, file_path, content, content_bytes, content_hash)
|
|
147
142
|
VALUES (?, ?, ?, ?, ?)
|
|
148
|
-
`);
|
|
149
|
-
this.insertFtsStmt = this.db.prepare(`
|
|
150
|
-
INSERT INTO files_fts (artifact_id, file_path, content)
|
|
151
|
-
VALUES (?, ?, ?)
|
|
152
143
|
`);
|
|
153
144
|
this.getContentStmt = this.db.prepare(`
|
|
154
145
|
SELECT artifact_id, file_path, content, content_bytes, content_hash
|
|
@@ -187,7 +178,6 @@ export class FilesRepo {
|
|
|
187
178
|
}
|
|
188
179
|
clearFilesForArtifact(artifactId) {
|
|
189
180
|
this.deleteStmt.run([artifactId]);
|
|
190
|
-
this.deleteFtsStmt.run([artifactId]);
|
|
191
181
|
}
|
|
192
182
|
insertFilesForArtifact(artifactId, files) {
|
|
193
183
|
for (const file of files) {
|
|
@@ -199,7 +189,6 @@ export class FilesRepo {
|
|
|
199
189
|
file.contentBytes,
|
|
200
190
|
contentHash
|
|
201
191
|
]);
|
|
202
|
-
this.insertFtsStmt.run([artifactId, file.filePath, file.content]);
|
|
203
192
|
}
|
|
204
193
|
}
|
|
205
194
|
replaceFilesForArtifact(artifactId, files) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createError, ERROR_CODES } from "../errors.js";
|
|
2
|
-
import { SCHEMA_V1_STATEMENTS, SCHEMA_V2_STATEMENTS } from "./schema.js";
|
|
3
|
-
export const LATEST_SCHEMA_VERSION =
|
|
2
|
+
import { SCHEMA_V1_STATEMENTS, SCHEMA_V2_STATEMENTS, SCHEMA_V3_STATEMENTS, SCHEMA_V4_STATEMENTS } from "./schema.js";
|
|
3
|
+
export const LATEST_SCHEMA_VERSION = 4;
|
|
4
4
|
const migrations = [
|
|
5
5
|
{
|
|
6
6
|
version: 1,
|
|
@@ -9,6 +9,14 @@ const migrations = [
|
|
|
9
9
|
{
|
|
10
10
|
version: 2,
|
|
11
11
|
statements: SCHEMA_V2_STATEMENTS
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
version: 3,
|
|
15
|
+
statements: SCHEMA_V3_STATEMENTS
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
version: 4,
|
|
19
|
+
statements: SCHEMA_V4_STATEMENTS
|
|
12
20
|
}
|
|
13
21
|
];
|
|
14
22
|
function selectSchemaVersion(tx) {
|
package/dist/storage/schema.d.ts
CHANGED
package/dist/storage/schema.js
CHANGED
|
@@ -164,4 +164,29 @@ export const SCHEMA_V2_STATEMENTS = [
|
|
|
164
164
|
`DELETE FROM artifact_index_meta`,
|
|
165
165
|
`DELETE FROM artifacts`
|
|
166
166
|
];
|
|
167
|
+
export const SCHEMA_V4_STATEMENTS = [
|
|
168
|
+
`ALTER TABLE artifacts ADD COLUMN alias TEXT`,
|
|
169
|
+
`CREATE UNIQUE INDEX IF NOT EXISTS idx_artifacts_alias ON artifacts(alias) WHERE alias IS NOT NULL`
|
|
170
|
+
];
|
|
171
|
+
export const SCHEMA_V3_STATEMENTS = [
|
|
172
|
+
`DROP TABLE IF EXISTS files_fts`,
|
|
173
|
+
`CREATE VIRTUAL TABLE files_fts USING fts5(
|
|
174
|
+
artifact_id UNINDEXED,
|
|
175
|
+
file_path,
|
|
176
|
+
content,
|
|
177
|
+
content='files',
|
|
178
|
+
content_rowid='rowid',
|
|
179
|
+
tokenize = 'unicode61 separators ''._$'''
|
|
180
|
+
)`,
|
|
181
|
+
`CREATE TRIGGER IF NOT EXISTS trg_fts_insert AFTER INSERT ON files BEGIN
|
|
182
|
+
INSERT INTO files_fts(rowid, artifact_id, file_path, content)
|
|
183
|
+
VALUES (NEW.rowid, NEW.artifact_id, NEW.file_path, NEW.content);
|
|
184
|
+
END`,
|
|
185
|
+
// No UPDATE trigger: FilesRepo uses delete+insert, never updates rows in place.
|
|
186
|
+
`CREATE TRIGGER IF NOT EXISTS trg_fts_delete BEFORE DELETE ON files BEGIN
|
|
187
|
+
INSERT INTO files_fts(files_fts, rowid, artifact_id, file_path, content)
|
|
188
|
+
VALUES ('delete', OLD.rowid, OLD.artifact_id, OLD.file_path, OLD.content);
|
|
189
|
+
END`,
|
|
190
|
+
`INSERT INTO files_fts(files_fts) VALUES('rebuild')`
|
|
191
|
+
];
|
|
167
192
|
//# sourceMappingURL=schema.js.map
|
package/dist/types.d.ts
CHANGED
|
@@ -5,12 +5,14 @@ export type RuntimeValidationNamespace = SourceMapping | AccessTransformerNamesp
|
|
|
5
5
|
export type MappingSourcePriority = "loom-first" | "maven-first";
|
|
6
6
|
export type ArtifactTargetKind = "version" | "jar" | "coordinate";
|
|
7
7
|
export type ArtifactScope = "vanilla" | "merged" | "loader";
|
|
8
|
+
export type MappingVariant = "pass" | "mojang-remapped";
|
|
8
9
|
export interface SourceTargetInput {
|
|
9
10
|
kind: ArtifactTargetKind;
|
|
10
11
|
value: string;
|
|
11
12
|
}
|
|
12
13
|
export interface ResolvedSourceArtifact {
|
|
13
14
|
artifactId: string;
|
|
15
|
+
artifactAlias?: string;
|
|
14
16
|
artifactSignature: string;
|
|
15
17
|
origin: SourceOrigin;
|
|
16
18
|
binaryJarPath?: string;
|
|
@@ -102,6 +104,7 @@ export interface SourceSearchHit {
|
|
|
102
104
|
}
|
|
103
105
|
export interface ArtifactRow {
|
|
104
106
|
artifactId: string;
|
|
107
|
+
alias: string | undefined;
|
|
105
108
|
origin: SourceOrigin;
|
|
106
109
|
coordinate: string | undefined;
|
|
107
110
|
version: string | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adhisang/minecraft-modding-mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "MCP server with utilities for Minecraft modding workflows",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
"start": "node dist/cli.js",
|
|
24
24
|
"check": "tsc --noEmit -p tsconfig.json",
|
|
25
25
|
"test": "node --test --import tsx tests/*.test.ts",
|
|
26
|
+
"test:file": "node --test --import tsx",
|
|
27
|
+
"test:grep": "node --test --import tsx --test-name-pattern",
|
|
26
28
|
"test:coverage": "node --test --import tsx --experimental-test-coverage --test-coverage-lines=80 --test-coverage-branches=70 --test-coverage-functions=80 tests/*.test.ts",
|
|
27
29
|
"test:coverage:lcov": "node --input-type=module -e \"import { mkdirSync } from 'node:fs'; mkdirSync('coverage', { recursive: true });\" && node --test --import tsx --experimental-test-coverage --test-reporter=lcov --test-reporter-destination=coverage/lcov.info --test-coverage-lines=80 --test-coverage-branches=70 --test-coverage-functions=80 tests/*.test.ts",
|
|
28
30
|
"test:perf": "node --test --test-concurrency=1 --import tsx tests/perf/*.perf.ts",
|