@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.
Files changed (38) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -18
  3. package/dist/cache-registry.d.ts +1 -1
  4. package/dist/cache-registry.js +10 -2
  5. package/dist/config.d.ts +10 -1
  6. package/dist/config.js +52 -1
  7. package/dist/entry-tools/analyze-symbol-service.d.ts +2 -2
  8. package/dist/entry-tools/analyze-symbol-service.js +13 -2
  9. package/dist/entry-tools/inspect-minecraft-service.d.ts +20 -20
  10. package/dist/entry-tools/inspect-minecraft-service.js +8 -2
  11. package/dist/entry-tools/manage-cache-service.d.ts +4 -4
  12. package/dist/entry-tools/validate-project-service.js +84 -4
  13. package/dist/index.js +99 -33
  14. package/dist/lru-list.d.ts +31 -0
  15. package/dist/lru-list.js +102 -0
  16. package/dist/mapping-pipeline-service.d.ts +10 -1
  17. package/dist/mapping-pipeline-service.js +13 -1
  18. package/dist/mapping-service.d.ts +12 -0
  19. package/dist/mapping-service.js +252 -10
  20. package/dist/mixin-validator.js +22 -7
  21. package/dist/observability.d.ts +18 -1
  22. package/dist/observability.js +44 -1
  23. package/dist/response-utils.d.ts +44 -10
  24. package/dist/response-utils.js +131 -17
  25. package/dist/source-resolver.d.ts +9 -1
  26. package/dist/source-resolver.js +14 -6
  27. package/dist/source-service.d.ts +97 -1
  28. package/dist/source-service.js +922 -113
  29. package/dist/storage/artifacts-repo.d.ts +4 -1
  30. package/dist/storage/artifacts-repo.js +33 -5
  31. package/dist/storage/files-repo.d.ts +0 -2
  32. package/dist/storage/files-repo.js +0 -11
  33. package/dist/storage/migrations.d.ts +1 -1
  34. package/dist/storage/migrations.js +10 -2
  35. package/dist/storage/schema.d.ts +2 -0
  36. package/dist/storage/schema.js +25 -0
  37. package/dist/types.d.ts +3 -0
  38. 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(artifactId: string): ArtifactRow | undefined;
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
- getArtifact(artifactId) {
177
- const row = this.getStmt.get([artifactId]);
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) {
@@ -6,6 +6,6 @@ type MigrationRunner = {
6
6
  };
7
7
  transaction<T>(fn: () => T): () => T;
8
8
  };
9
- export declare const LATEST_SCHEMA_VERSION = 2;
9
+ export declare const LATEST_SCHEMA_VERSION = 4;
10
10
  export declare function runMigrations(db: MigrationRunner): number;
11
11
  export {};
@@ -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;
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) {
@@ -1,2 +1,4 @@
1
1
  export declare const SCHEMA_V1_STATEMENTS: string[];
2
2
  export declare const SCHEMA_V2_STATEMENTS: string[];
3
+ export declare const SCHEMA_V4_STATEMENTS: string[];
4
+ export declare const SCHEMA_V3_STATEMENTS: string[];
@@ -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.2.0",
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",