@creeperhost/modlens-mcp 1.6.18 → 1.6.20
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/README.md +112 -15
- package/TESTING.md +69 -0
- package/dist/cli.js +49 -13
- package/dist/cli.js.map +1 -1
- package/dist/curseforge.d.ts +9 -7
- package/dist/curseforge.d.ts.map +1 -1
- package/dist/curseforge.js +48 -55
- package/dist/curseforge.js.map +1 -1
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +31 -1
- package/dist/db.js.map +1 -1
- package/dist/embeddings.d.ts.map +1 -1
- package/dist/embeddings.js +1 -0
- package/dist/embeddings.js.map +1 -1
- package/dist/feed-the-beast.d.ts +5 -5
- package/dist/feed-the-beast.d.ts.map +1 -1
- package/dist/feed-the-beast.js +5 -21
- package/dist/feed-the-beast.js.map +1 -1
- package/dist/java-tools.d.ts +1 -0
- package/dist/java-tools.d.ts.map +1 -1
- package/dist/java-tools.js +31 -7
- package/dist/java-tools.js.map +1 -1
- package/dist/launcher.js +15 -0
- package/dist/launcher.js.map +1 -1
- package/dist/mappings.d.ts +4 -1
- package/dist/mappings.d.ts.map +1 -1
- package/dist/mappings.js +120 -52
- package/dist/mappings.js.map +1 -1
- package/dist/minecraft.d.ts +0 -4
- package/dist/minecraft.d.ts.map +1 -1
- package/dist/minecraft.js +7 -23
- package/dist/minecraft.js.map +1 -1
- package/dist/mod-artifacts.d.ts +4 -0
- package/dist/mod-artifacts.d.ts.map +1 -0
- package/dist/mod-artifacts.js +58 -0
- package/dist/mod-artifacts.js.map +1 -0
- package/dist/modpacks-ch.d.ts +72 -46
- package/dist/modpacks-ch.d.ts.map +1 -1
- package/dist/modpacks-ch.js +134 -20
- package/dist/modpacks-ch.js.map +1 -1
- package/dist/modrinth.d.ts +14 -6
- package/dist/modrinth.d.ts.map +1 -1
- package/dist/modrinth.js +70 -55
- package/dist/modrinth.js.map +1 -1
- package/dist/platform-adapter.d.ts +1 -0
- package/dist/platform-adapter.d.ts.map +1 -1
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +20 -44
- package/dist/platform.js.map +1 -1
- 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/processor.d.ts +5 -0
- package/dist/processor.d.ts.map +1 -1
- package/dist/processor.js +166 -46
- package/dist/processor.js.map +1 -1
- package/dist/registry-submission.d.ts +34 -0
- package/dist/registry-submission.d.ts.map +1 -0
- package/dist/registry-submission.js +128 -0
- package/dist/registry-submission.js.map +1 -0
- package/dist/repositories/array-fields.d.ts +3 -0
- package/dist/repositories/array-fields.d.ts.map +1 -0
- package/dist/repositories/array-fields.js +8 -0
- package/dist/repositories/array-fields.js.map +1 -0
- package/dist/repositories/embeddings-sqlite.d.ts +1 -0
- package/dist/repositories/embeddings-sqlite.d.ts.map +1 -1
- package/dist/repositories/embeddings-sqlite.js +26 -58
- package/dist/repositories/embeddings-sqlite.js.map +1 -1
- package/dist/repositories/embeddings.d.ts.map +1 -1
- package/dist/repositories/embeddings.js +26 -3
- package/dist/repositories/embeddings.js.map +1 -1
- package/dist/repositories/index.d.ts +1 -1
- package/dist/repositories/index.d.ts.map +1 -1
- package/dist/repositories/mod.d.ts +8 -5
- package/dist/repositories/mod.d.ts.map +1 -1
- package/dist/repositories/mod.js +25 -11
- package/dist/repositories/mod.js.map +1 -1
- package/dist/repositories/packs.d.ts.map +1 -1
- package/dist/repositories/packs.js +10 -2
- package/dist/repositories/packs.js.map +1 -1
- package/dist/search-adapter.d.ts +2 -0
- package/dist/search-adapter.d.ts.map +1 -1
- package/dist/search-adapter.js +21 -9
- package/dist/search-adapter.js.map +1 -1
- package/dist/security.d.ts +2 -0
- package/dist/security.d.ts.map +1 -1
- package/dist/security.js +5 -1
- package/dist/security.js.map +1 -1
- package/dist/server.js +80 -35
- package/dist/server.js.map +1 -1
- package/dist/setup.js +9 -4
- package/dist/setup.js.map +1 -1
- package/dist/sqlite-json.d.ts +3 -0
- package/dist/sqlite-json.d.ts.map +1 -0
- package/dist/sqlite-json.js +78 -0
- package/dist/sqlite-json.js.map +1 -0
- package/dist/sqlite-parameters.d.ts +3 -0
- package/dist/sqlite-parameters.d.ts.map +1 -0
- package/dist/sqlite-parameters.js +19 -0
- package/dist/sqlite-parameters.js.map +1 -0
- package/dist/sqlite-schema.d.ts +4 -0
- package/dist/sqlite-schema.d.ts.map +1 -0
- package/dist/sqlite-schema.js +76 -0
- package/dist/sqlite-schema.js.map +1 -0
- package/dist/tools/catalog.d.ts +1 -6
- package/dist/tools/catalog.d.ts.map +1 -1
- package/dist/tools/catalog.js +10 -14
- package/dist/tools/catalog.js.map +1 -1
- package/dist/tools/compat-check.d.ts.map +1 -1
- package/dist/tools/compat-check.js +18 -30
- package/dist/tools/compat-check.js.map +1 -1
- package/dist/tools/diagnostics.js.map +1 -1
- package/dist/tools/docs.d.ts.map +1 -1
- package/dist/tools/docs.js +8 -3
- package/dist/tools/docs.js.map +1 -1
- package/dist/tools/embed-registry.d.ts +20 -4
- package/dist/tools/embed-registry.d.ts.map +1 -1
- package/dist/tools/embed-registry.js +30 -19
- package/dist/tools/embed-registry.js.map +1 -1
- package/dist/tools/graphify.d.ts +19 -4
- package/dist/tools/graphify.d.ts.map +1 -1
- package/dist/tools/graphify.js +61 -13
- package/dist/tools/graphify.js.map +1 -1
- package/dist/tools/ingest.d.ts.map +1 -1
- package/dist/tools/ingest.js +4 -4
- package/dist/tools/ingest.js.map +1 -1
- package/dist/tools/kubejs.js +1 -1
- package/dist/tools/kubejs.js.map +1 -1
- package/dist/tools/mc-fts.d.ts.map +1 -1
- package/dist/tools/mc-fts.js +9 -4
- package/dist/tools/mc-fts.js.map +1 -1
- package/dist/tools/mcmeta.d.ts +10 -1
- package/dist/tools/mcmeta.d.ts.map +1 -1
- package/dist/tools/mcmeta.js +43 -29
- package/dist/tools/mcmeta.js.map +1 -1
- package/dist/tools/mixin-scan.d.ts +12 -6
- package/dist/tools/mixin-scan.d.ts.map +1 -1
- package/dist/tools/mixin-scan.js +12 -7
- package/dist/tools/mixin-scan.js.map +1 -1
- package/dist/tools/mod-data.d.ts.map +1 -1
- package/dist/tools/mod-data.js +40 -27
- package/dist/tools/mod-data.js.map +1 -1
- package/dist/tools/modpacks-ch.d.ts +9 -5
- package/dist/tools/modpacks-ch.d.ts.map +1 -1
- package/dist/tools/modpacks-ch.js +54 -33
- package/dist/tools/modpacks-ch.js.map +1 -1
- package/dist/tools/packtools.js +3 -3
- package/dist/tools/packtools.js.map +1 -1
- package/dist/tools/platform.d.ts +2 -2
- package/dist/tools/platform.d.ts.map +1 -1
- package/dist/tools/platform.js +33 -23
- package/dist/tools/platform.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 +342 -0
- package/dist/tools/project.d.ts.map +1 -0
- package/dist/tools/project.js +519 -0
- package/dist/tools/project.js.map +1 -0
- package/dist/tools/reports.d.ts.map +1 -1
- package/dist/tools/reports.js +5 -3
- package/dist/tools/reports.js.map +1 -1
- package/dist/tools/vanilla-data.d.ts.map +1 -1
- package/dist/tools/vanilla-data.js +3 -33
- package/dist/tools/vanilla-data.js.map +1 -1
- package/dist/tools/vanilla.d.ts +3 -1
- package/dist/tools/vanilla.d.ts.map +1 -1
- package/dist/tools/vanilla.js +26 -12
- package/dist/tools/vanilla.js.map +1 -1
- package/dist/tools/version-diff.d.ts.map +1 -1
- package/dist/tools/version-diff.js +4 -24
- package/dist/tools/version-diff.js.map +1 -1
- package/dist/version-ranges.d.ts +5 -0
- package/dist/version-ranges.d.ts.map +1 -0
- package/dist/version-ranges.js +93 -0
- package/dist/version-ranges.js.map +1 -0
- package/package.json +9 -1
- package/prisma/backends/template.db +0 -0
- package/scripts/build-template-db.mjs +3 -1
- package/scripts/enable-sqlite-vec.mjs +2 -106
- package/scripts/gradle/modlens.init.gradle +154 -0
- package/scripts/project-upload.mjs +74 -0
- package/scripts/test-minecraft-eras.mjs +202 -0
- package/scripts/test-package.mjs +105 -5
- package/scripts/test-primers-live.mjs +93 -0
- package/scripts/test-project-gradle.mjs +76 -0
- package/scripts/test-project-http.mjs +98 -0
- package/scripts/test-setup.mjs +18 -0
package/dist/server.js
CHANGED
|
@@ -31,13 +31,14 @@ import { getModGradleFiles, searchGradleFiles, compareGradleDeps, } from "./tool
|
|
|
31
31
|
import { generateReport } from "./tools/reports.js";
|
|
32
32
|
import { findAssetConflicts, findVanillaOverrides, analyzeModSidedness, analyzePackSidedness, computeModComplexity, computePackChangelog, findDataConflicts, buildPackGraph, diffPackConfigs, packHealth } from "./tools/packtools.js";
|
|
33
33
|
import { indexKubeJsScripts, searchKubeJsScripts, semanticSearchKubeJsScripts } from "./tools/kubejs.js";
|
|
34
|
-
import { searchPacksAction, featuredPacksAction, packInfoAction, packManifestAction, syncPackModsAction, resolvePackAction, listRemotePackVersionsAction, ingestPackAction,
|
|
34
|
+
import { searchPacksAction, featuredPacksAction, packInfoAction, packManifestAction, syncPackModsAction, resolvePackAction, listRemotePackVersionsAction, ingestPackAction, searchModsAction, modInfoAction, downloadModAction, downloadOverridesAction, listPackVersionsAction, listPackFilesAction, findModInPacksAction } from "./tools/modpacks-ch.js";
|
|
35
35
|
import { analyzeCrashLog, findMissingDeps } from "./tools/diagnostics.js";
|
|
36
36
|
import { checkModCompat } from "./tools/compat-check.js";
|
|
37
37
|
import { disconnect } from "./db.js";
|
|
38
38
|
import { mcPaths } from "./minecraft.js";
|
|
39
39
|
import { findModById, resolveModRefSlim } from "./repositories/mod.js";
|
|
40
40
|
import { CACHE_ROOT } from "./cache.js";
|
|
41
|
+
import { projectAction, projectToolSchema } from "./tools/project.js";
|
|
41
42
|
// Load .env — try ~/.modlens/.env first (npx/installed users), then local .env (git-clone users)
|
|
42
43
|
import { readFileSync, existsSync } from "fs";
|
|
43
44
|
import { join, dirname } from "path";
|
|
@@ -72,7 +73,7 @@ const VERBOSE = process.env.MODLENS_VERBOSE
|
|
|
72
73
|
/** Best-effort, bounded JSON for logging tool params (handles circular refs). */
|
|
73
74
|
function previewArgs(value) {
|
|
74
75
|
try {
|
|
75
|
-
const json = JSON.stringify(value);
|
|
76
|
+
const json = JSON.stringify(value, (key, item) => ["projectKey", "data"].includes(key) ? "[redacted]" : item);
|
|
76
77
|
if (json == null)
|
|
77
78
|
return String(value);
|
|
78
79
|
return json.length > 2000 ? json.slice(0, 2000) + "…(truncated)" : json;
|
|
@@ -105,6 +106,12 @@ function resolveDbId(dbId, modId) {
|
|
|
105
106
|
const n = typeof modId === "number" ? modId : Number(modId);
|
|
106
107
|
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
107
108
|
}
|
|
109
|
+
function requireModRef(modId, dbId) {
|
|
110
|
+
const ref = dbId ?? modId;
|
|
111
|
+
if (ref === undefined || ref === "")
|
|
112
|
+
throw new Error("modId or dbId is required");
|
|
113
|
+
return ref;
|
|
114
|
+
}
|
|
108
115
|
/**
|
|
109
116
|
* Like resolveDbId, but for actions that need a valid DB id — will look up
|
|
110
117
|
* string modIds via the database so agents can pass e.g. modId:"polylib".
|
|
@@ -149,10 +156,12 @@ function safe(fn) {
|
|
|
149
156
|
}
|
|
150
157
|
class JsonBodyParseError extends Error {
|
|
151
158
|
}
|
|
159
|
+
class HttpBodyTooLargeError extends Error {
|
|
160
|
+
}
|
|
152
161
|
function createMcpServer() {
|
|
153
162
|
const server = new McpServer({ name: "modlens", version: pkg.version });
|
|
154
163
|
// ── 1. mod ────────────────────────────────────────────────────────────────────
|
|
155
|
-
server.tool("mod", "Mod database, decompile, and source browser. action=ingest|list|get|search|stats|dependencies|dep_graph|version_conflicts|source_urls|decompile|decompile_status|decompile_class|source|search_source|reindex|batch_ingest|batch_decompile|refresh_metadata|index_fts|search_indexed|index_semantic|search_semantic|get_paths|delete|graph_build|graph_status|graph_query|graph_report|graph_enrich_next|graph_enrich_submit|graph_download|graph_export|graph_submit|embed_export|embed_download|embed_download_all|embed_status|embed_submit. index_fts/search_indexed: BM25-ranked FTS over source code. index_semantic/search_semantic: vector search (requires Ollama). refresh_metadata: re-parse all mods with degraded metadata (filename/@Mod annotation) and upgrade if a higher-quality manifest is found. graph_enrich_next: get next un-enriched chunk for chat enrichment. graph_enrich_submit: submit enriched nodes/edges (chunkIndex, nodes, edges). graph_download: download pre-built graph from registry (targetType=mod|vanilla|modloader with targetId/targetVersion). graph_export: export a mod's local graph as a shareable gzipped bundle. graph_submit:
|
|
164
|
+
server.tool("mod", "Mod database, decompile, and source browser. action=ingest|list|get|search|stats|dependencies|dep_graph|version_conflicts|source_urls|decompile|decompile_status|decompile_class|source|search_source|reindex|batch_ingest|batch_decompile|refresh_metadata|index_fts|search_indexed|index_semantic|search_semantic|get_paths|delete|graph_build|graph_status|graph_query|graph_report|graph_enrich_next|graph_enrich_submit|graph_download|graph_export|graph_submit|embed_export|embed_download|embed_download_all|embed_status|embed_submit. index_fts/search_indexed: BM25-ranked FTS over source code. index_semantic/search_semantic: vector search (requires Ollama). refresh_metadata: re-parse all mods with degraded metadata (filename/@Mod annotation) and upgrade if a higher-quality manifest is found. graph_enrich_next: get next un-enriched chunk for chat enrichment. graph_enrich_submit: submit enriched nodes/edges (chunkIndex, nodes, edges). graph_download: download pre-built graph from registry (targetType=mod|vanilla|modloader with targetId/targetVersion). graph_export: export a mod's local graph as a shareable gzipped bundle. graph_submit: validate an exported bundle and open a draft GitHub registry PR (bundlePath required). embed_export/embed_download/embed_status: targetType-aware embeddings actions for mod/vanilla/modloader. embed_download: downloads registry embeddings; protects local embeddings by default (use force=true to overwrite). embed_download_all: download embeddings for all mods. embed_submit: validate an exported bundle and open a draft GitHub registry PR (bundlePath required). Auto-behaviors: decompile_status auto-queues embedding (MODLENS_AUTO_EMBED) and graph build (MODLENS_AUTO_GRAPH). Pass autoEmbed/autoGraph to override per-call.", {
|
|
156
165
|
action: z.enum([
|
|
157
166
|
"ingest", "list", "get", "search", "stats", "dependencies", "dep_graph",
|
|
158
167
|
"version_conflicts", "source_urls", "decompile", "decompile_status",
|
|
@@ -193,10 +202,13 @@ function createMcpServer() {
|
|
|
193
202
|
targetLoader: z.string().optional().describe("Optional loader hint for registry matching"),
|
|
194
203
|
targetMcVersion: z.string().optional().describe("Optional MC version hint for registry matching"),
|
|
195
204
|
model: z.string().optional().describe("Embedding model override for embed_download"),
|
|
205
|
+
bundlePath: z.string().optional().describe("Exported bundle file for graph_submit or embed_submit"),
|
|
206
|
+
contributor: z.string().optional().describe("Contributor alias for a registry submission"),
|
|
207
|
+
note: z.string().optional().describe("Note for the registry pull request"),
|
|
196
208
|
autoEmbed: z.boolean().optional().describe("Override auto-embed on decompile completion (default: env MODLENS_AUTO_EMBED, true)"),
|
|
197
209
|
autoGraph: z.boolean().optional().describe("Override auto-graph-build on decompile/source-download (default: env MODLENS_AUTO_GRAPH, true)"),
|
|
198
210
|
provenance: z.enum(["local", "registry", "community"]).optional().describe("Filter search_semantic results by embedding source (default: all sources)"),
|
|
199
|
-
}, safe(async ({ action, jarPath, modId, dbId: rawDbId, query, path, className, loader, mcVersion, hasMixins, decompiled, recursive, skipSource, isRegex, force, limit, directory, indexClasses, replace, budget, backend, chunkIndex, nodes, edges, outputDir, modVersion, targetType, targetId, targetVersion, targetLoader, targetMcVersion, model, autoEmbed, autoGraph, provenance }) => {
|
|
211
|
+
}, safe(async ({ action, jarPath, modId, dbId: rawDbId, query, path, className, loader, mcVersion, hasMixins, decompiled, recursive, skipSource, isRegex, force, limit, directory, indexClasses, replace, budget, backend, chunkIndex, nodes, edges, outputDir, modVersion, targetType, targetId, targetVersion, targetLoader, targetMcVersion, model, autoEmbed, autoGraph, provenance, bundlePath, contributor, note }) => {
|
|
200
212
|
const dbId = await resolveDbIdAsync(rawDbId, modId);
|
|
201
213
|
const resolvedTargetType = targetType ?? "mod";
|
|
202
214
|
const resolvedTargetId = targetId ?? (typeof modId === "string" ? modId : undefined) ?? (resolvedTargetType === "vanilla" ? "minecraft" : undefined);
|
|
@@ -210,7 +222,7 @@ function createMcpServer() {
|
|
|
210
222
|
result = await listMods({ loader: loader, mcVersion, hasMixins, decompiled, limit: limit ?? 50 });
|
|
211
223
|
break;
|
|
212
224
|
case "get":
|
|
213
|
-
result = await getModDetails(modId);
|
|
225
|
+
result = await getModDetails(requireModRef(modId, rawDbId));
|
|
214
226
|
break;
|
|
215
227
|
case "search":
|
|
216
228
|
result = await searchMods(query, { loader, mcVersion, limit: limit ?? 20 });
|
|
@@ -219,7 +231,7 @@ function createMcpServer() {
|
|
|
219
231
|
result = await getDbStats();
|
|
220
232
|
break;
|
|
221
233
|
case "dependencies":
|
|
222
|
-
result = await getDependencies(modId, recursive ?? false);
|
|
234
|
+
result = await getDependencies(requireModRef(modId, rawDbId), recursive ?? false);
|
|
223
235
|
break;
|
|
224
236
|
case "dep_graph":
|
|
225
237
|
result = await getDependencyGraph(mcVersion);
|
|
@@ -337,7 +349,7 @@ function createMcpServer() {
|
|
|
337
349
|
result = await exportGraph(dbId, outputDir);
|
|
338
350
|
break;
|
|
339
351
|
case "graph_submit":
|
|
340
|
-
result = await submitGraph({ bundlePath: outputDir ?? "" });
|
|
352
|
+
result = await submitGraph({ bundlePath: bundlePath ?? outputDir ?? "", contributor, note });
|
|
341
353
|
break;
|
|
342
354
|
case "embed_export":
|
|
343
355
|
result = await exportEmbeddings(resolvedTargetType, outputDir, { dbId, mcVersion: resolvedTargetVersion ?? mcVersion, targetId: resolvedTargetId });
|
|
@@ -363,7 +375,7 @@ function createMcpServer() {
|
|
|
363
375
|
});
|
|
364
376
|
break;
|
|
365
377
|
case "embed_submit":
|
|
366
|
-
result = await submitEmbeddings({ bundlePath: outputDir ?? "" });
|
|
378
|
+
result = await submitEmbeddings({ bundlePath: bundlePath ?? outputDir ?? "", contributor, note });
|
|
367
379
|
break;
|
|
368
380
|
}
|
|
369
381
|
return out(result);
|
|
@@ -458,7 +470,7 @@ function createMcpServer() {
|
|
|
458
470
|
let result;
|
|
459
471
|
switch (action) {
|
|
460
472
|
case "targets":
|
|
461
|
-
result = await getMixinTargets(modId);
|
|
473
|
+
result = await getMixinTargets(requireModRef(modId, rawDbId));
|
|
462
474
|
break;
|
|
463
475
|
case "resolve":
|
|
464
476
|
result = await resolveMixinTargets(dbId);
|
|
@@ -483,7 +495,7 @@ function createMcpServer() {
|
|
|
483
495
|
}));
|
|
484
496
|
// ── 4. platform ───────────────────────────────────────────────────────────────
|
|
485
497
|
server.tool("platform", "Modrinth/CurseForge platform sync and source download. action=sync_modrinth|sync_curseforge|check_updates|batch_sync|download_source|search|batch_check_updates. " +
|
|
486
|
-
"action=search: unified mod search across both Modrinth and CurseForge — deduplicated, sorted by downloads (query required, loader, mcVersion, limit). " +
|
|
498
|
+
"action=search: unified mod search across both Modrinth and CurseForge — deduplicated, sorted by downloads (query required, loader, mcVersion, limit). Missing modpacks.ch loader labels are recovered by inspecting cached/downloaded JARs. " +
|
|
487
499
|
"action=batch_check_updates: check ALL linked mods for newer versions on Modrinth/CurseForge in one pass (mcVersion, loader, modIdFilter, limit). " +
|
|
488
500
|
"download_source downloads GitHub source without decompilation — auto-queues graph build (override with autoGraph). Env: MODLENS_AUTO_GRAPH (default: 1).", {
|
|
489
501
|
action: z.enum(["sync_modrinth", "sync_curseforge", "check_updates", "batch_sync", "download_source", "search", "batch_check_updates"]),
|
|
@@ -529,22 +541,22 @@ function createMcpServer() {
|
|
|
529
541
|
}));
|
|
530
542
|
// ── 5. modpacks_ch ───────────────────────────────────────────────────────────
|
|
531
543
|
server.tool("modpacks_ch", "Search, resolve, list versions for, and ingest arbitrary modpacks so other ModLens tools can analyze the exact pack contents. General workflow: when the user names a modpack and possibly a fuzzy version like 7.1, call action=list_versions with packRef/versionRef, choose the best matching version, then call action=ingest_pack before using analyze_crash_log, reports, pack_tools, dependency checks, mixin scans, source search, or other pack-aware analysis. Crash triage is one common use, not the only use. " +
|
|
532
|
-
"
|
|
533
|
-
"action=search|featured|info|manifest|resolve_pack|list_versions|ingest_pack|sync_pack_mods|
|
|
534
|
-
"namespace=ftb|curseforge|modrinth|feedthebeast (default: ftb).
|
|
544
|
+
"Metadata comes from modpacks.ch for all providers; downloads use the supplied URLs and Modrinth .mrpack archives. Loader-filtered mod queries automatically download and inspect unlabeled JARs; recovered labels report loaderSource=jar. No CurseForge API key is required. Modrinth packRef may be a slug/project ID, a modrinth.com modpack/version URL, an api.modrinth.com project/version URL, a cdn.modrinth.com .mrpack URL, or any direct HTTPS .mrpack URL. " +
|
|
545
|
+
"action=search|featured|info|manifest|resolve_pack|list_versions|ingest_pack|sync_pack_mods|search_mods|mod_info|download_mod|download_overrides|list_pack_versions|list_pack_files|find_mod_in_packs. mod_info returns up to limit versions (default 20). search_ftb_mods and ftb_mod_info remain compatibility aliases. " +
|
|
546
|
+
"namespace=ftb|curseforge|modrinth|feedthebeast (default: ftb). feedthebeast uses the modpacks.ch /ftb catalog; ftb uses /modpack. resolve_pack/list_versions/ingest_pack accept packRef=name/slug/id and versionRef=version id/name/number/partial text. list_versions returns matches for fuzzy refs; list_pack_versions is DB-backed synced-pack history. " +
|
|
535
547
|
"User-Agent is set per modpacks.ch (CreeperHost) team request for usage tracking.", {
|
|
536
|
-
action: z.enum(["search", "featured", "info", "manifest", "resolve_pack", "list_versions", "ingest_pack", "sync_pack_mods", "search_ftb_mods", "ftb_mod_info", "download_mod", "download_overrides", "list_pack_versions", "list_pack_files", "find_mod_in_packs"]),
|
|
537
|
-
namespace: z.enum(["ftb", "curseforge", "modrinth", "feedthebeast"]).optional().describe("
|
|
548
|
+
action: z.enum(["search", "featured", "info", "manifest", "resolve_pack", "list_versions", "ingest_pack", "sync_pack_mods", "search_mods", "mod_info", "search_ftb_mods", "ftb_mod_info", "download_mod", "download_overrides", "list_pack_versions", "list_pack_files", "find_mod_in_packs"]),
|
|
549
|
+
namespace: z.enum(["ftb", "curseforge", "modrinth", "feedthebeast"]).optional().describe("Provider catalog on modpacks.ch (default: ftb). feedthebeast selects /ftb; ftb selects /modpack."),
|
|
538
550
|
packId: z.number().optional().describe("Numeric FTB/CurseForge pack ID or internal Modrinth pack key returned by resolve/list actions"),
|
|
539
551
|
versionId: z.number().optional().describe("Numeric FTB/CurseForge version ID or internal Modrinth version key returned by resolve/list actions"),
|
|
540
552
|
packRef: z.union([z.number(), z.string()]).optional().describe("Pack name, slug, Modrinth project ID, CurseForge pack ID, FTB/feedthebeast pack ID, Modrinth web/API/CDN URL, or direct HTTPS .mrpack URL. Use for resolve_pack/list_versions/ingest_pack."),
|
|
541
553
|
versionRef: z.union([z.number(), z.string()]).optional().describe("Version ID, full version name, version number, or fuzzy partial text such as 7.1. Use list_versions first if ambiguous."),
|
|
542
554
|
packVersionDbId: z.number().optional().describe("Pack version DB id returned by sync_pack_mods (shortcut for list_pack_files)"),
|
|
543
|
-
query: z.string().optional().describe("Search query (required for search,
|
|
544
|
-
modId: z.union([z.number(), z.string()]).optional().describe("
|
|
545
|
-
fileId: z.number().optional().describe("Exact
|
|
546
|
-
loader: z.string().optional().describe("Loader filter for
|
|
547
|
-
mcVersionFilter: z.string().optional().describe("MC version filter for
|
|
555
|
+
query: z.string().optional().describe("Search query (required for search, search_mods)"),
|
|
556
|
+
modId: z.union([z.number(), z.string()]).optional().describe("CurseForge project ID or Modrinth project ID/slug (required for mod_info, download_mod)"),
|
|
557
|
+
fileId: z.union([z.number(), z.string()]).optional().describe("Exact CurseForge file ID or Modrinth version ID to download (download_mod; skips mcVersion/loader filter)"),
|
|
558
|
+
loader: z.string().optional().describe("Loader filter for mod_info/download_mod (e.g. forge, fabric, neoforge)"),
|
|
559
|
+
mcVersionFilter: z.string().optional().describe("MC version filter for mod_info/download_mod (e.g. 1.7.10, 1.12.2, 1.21.1)"),
|
|
548
560
|
force: z.boolean().optional().describe("Force re-download even if cached (download_mod)"),
|
|
549
561
|
modDbId: z.number().optional().describe("ModLens DB mod id (for find_mod_in_packs)"),
|
|
550
562
|
cfProject: z.number().optional().describe("CurseForge project id (for find_mod_in_packs)"),
|
|
@@ -597,15 +609,17 @@ function createMcpServer() {
|
|
|
597
609
|
throw new Error("Use action=ingest_pack for Modrinth packs");
|
|
598
610
|
result = await syncPackModsAction({ packId, versionId, namespace: ns, fileTypes, skipServer, skipOptional, concurrency, maxFiles });
|
|
599
611
|
break;
|
|
612
|
+
case "search_mods":
|
|
600
613
|
case "search_ftb_mods":
|
|
601
614
|
if (!query)
|
|
602
|
-
throw new Error("query is required for action=
|
|
603
|
-
result = await
|
|
615
|
+
throw new Error("query is required for action=search_mods");
|
|
616
|
+
result = await searchModsAction(query, limit ?? 20);
|
|
604
617
|
break;
|
|
618
|
+
case "mod_info":
|
|
605
619
|
case "ftb_mod_info":
|
|
606
620
|
if (modId === undefined)
|
|
607
|
-
throw new Error("modId is required for action=
|
|
608
|
-
result = await
|
|
621
|
+
throw new Error("modId is required for action=mod_info");
|
|
622
|
+
result = await modInfoAction(modId, { mcVersion: mcVersionFilter, loader, limit });
|
|
609
623
|
break;
|
|
610
624
|
case "download_mod":
|
|
611
625
|
if (modId === undefined)
|
|
@@ -889,7 +903,7 @@ function createMcpServer() {
|
|
|
889
903
|
return out(result);
|
|
890
904
|
}));
|
|
891
905
|
// ── 9. primers ────────────────────────────────────────────────────────────────
|
|
892
|
-
server.tool("primers", "Minecraft
|
|
906
|
+
server.tool("primers", "Minecraft migration primers. by_version finds each migration step, including vanilla changes for the selected loader; includeContent=true bundles the Markdown with a shared maxChars budget. Continue with nextCursor until null, keeping the same range and loader. get reads one guide (follow nextStartLine). Missing content is fetched and cached unless fetchContent=false. seed populates the catalogue. Bundles report per-guide failures; retry failed IDs with get. Published guides may leave coverage gaps. semantic_search requires Ollama.", {
|
|
893
907
|
action: z.enum(["ingest", "seed", "get", "by_version", "search", "list", "delete", "semantic_search", "backfill_embeddings"]),
|
|
894
908
|
entries: z.array(z.object({
|
|
895
909
|
fromVersion: z.string(),
|
|
@@ -902,27 +916,34 @@ function createMcpServer() {
|
|
|
902
916
|
tags: z.array(z.string()).optional(),
|
|
903
917
|
source: z.string().optional(),
|
|
904
918
|
fetchContent: z.boolean().optional(),
|
|
905
|
-
})).optional().describe("Primer entries to ingest
|
|
906
|
-
id: z.number().optional(),
|
|
919
|
+
})).min(1).optional().describe("Primer entries to ingest; set entries[].fetchContent=true to fetch before saving"),
|
|
920
|
+
id: z.number().int().positive().optional(),
|
|
907
921
|
fromVersion: z.string().optional(),
|
|
908
922
|
toVersion: z.string().optional(),
|
|
909
|
-
modloader: z.string().optional().describe("neoforge|forge|fabric|quilt"),
|
|
923
|
+
modloader: z.string().optional().describe("neoforge|forge|fabric|quilt|vanilla; discovery includes vanilla guides"),
|
|
910
924
|
query: z.string().optional(),
|
|
911
|
-
limit: z.number().optional(),
|
|
912
|
-
|
|
925
|
+
limit: z.number().int().min(1).max(200).optional(),
|
|
926
|
+
fetchContent: z.boolean().optional().describe("get/seed/by_version: fetch missing content (default true); false permits cached/offline reads"),
|
|
927
|
+
includeContent: z.boolean().optional().describe("by_version: bundle guide bodies (default false); includes vanilla guides for the selected loader"),
|
|
928
|
+
maxChars: z.number().int().min(1000).max(200000).optional().describe("by_version with includeContent: combined text budget (default 60000); up to 20 guides per page"),
|
|
929
|
+
cursor: z.string().min(1).max(2048).optional().describe("by_version with includeContent: nextCursor from the previous page; retain the same range and loader"),
|
|
930
|
+
refresh: z.boolean().optional().describe("get: re-fetch even if cached; failed refresh preserves the cached guide"),
|
|
931
|
+
startLine: z.number().int().positive().optional().describe("get: first line, 1-based (default 1)"),
|
|
932
|
+
maxLines: z.number().int().min(1).max(2000).optional().describe("get: page size (default 400); follow nextStartLine until null"),
|
|
933
|
+
}, safe(async ({ action, entries, id, fromVersion, toVersion, modloader, query, limit, fetchContent, includeContent, maxChars, cursor, refresh, startLine, maxLines }) => {
|
|
913
934
|
let result;
|
|
914
935
|
switch (action) {
|
|
915
936
|
case "ingest":
|
|
916
937
|
result = await ingestPrimer(entries);
|
|
917
938
|
break;
|
|
918
939
|
case "seed":
|
|
919
|
-
result = await seedDefaultPrimers();
|
|
940
|
+
result = await seedDefaultPrimers(fetchContent);
|
|
920
941
|
break;
|
|
921
942
|
case "get":
|
|
922
|
-
result = await getPrimer(id);
|
|
943
|
+
result = await getPrimer(id, { fetchContent, refresh, startLine, maxLines });
|
|
923
944
|
break;
|
|
924
945
|
case "by_version":
|
|
925
|
-
result = await getPrimersByVersionRange(fromVersion, toVersion, modloader);
|
|
946
|
+
result = await getPrimersByVersionRange(fromVersion, toVersion, modloader, { includeContent, fetchContent, maxChars, cursor });
|
|
926
947
|
break;
|
|
927
948
|
case "search":
|
|
928
949
|
result = await searchPrimers(query, modloader, fromVersion, toVersion, limit);
|
|
@@ -940,7 +961,7 @@ function createMcpServer() {
|
|
|
940
961
|
result = await backfillPrimerEmbeddings();
|
|
941
962
|
break;
|
|
942
963
|
}
|
|
943
|
-
return out(result);
|
|
964
|
+
return { ...out(result), ...(result && typeof result === "object" && "failed" in result && Number(result.failed) > 0 ? { isError: true } : {}) };
|
|
944
965
|
}));
|
|
945
966
|
// ── 10. mc_registry ───────────────────────────────────────────────────────────
|
|
946
967
|
server.tool("mc_registry", "Vanilla MC registry and meta data (blocks, commands, registries, sounds, item components). action=blocks|commands|registries|sounds|item_components|registry_entries|mcmeta_versions.", {
|
|
@@ -1305,6 +1326,12 @@ function createMcpServer() {
|
|
|
1305
1326
|
return out(result);
|
|
1306
1327
|
}));
|
|
1307
1328
|
// ── 18. reports ───────────────────────────────────────────────────────────────
|
|
1329
|
+
server.tool("project", "Import and query a private Gradle compile environment, including prepared ATs, mappings and loader patches. " +
|
|
1330
|
+
"Use this instead of mc_source/mod source or member tools when answering about an imported project. " +
|
|
1331
|
+
"Every query requires projectKey and immutable environmentId; no shared vanilla caches are modified. " +
|
|
1332
|
+
"Export with scripts/gradle/modlens.init.gradle; use scripts/project-upload.mjs for remote imports. " +
|
|
1333
|
+
"action=import_local|upload_begin|upload_chunk|upload_finish|upload_abort|list|info|classes|source|search|members|bytecode. " +
|
|
1334
|
+
"list is restricted to the supplied private key. Search covers provided sources and cached decompiles, not all runtime transformations.", projectToolSchema, safe(async (params) => out(await projectAction(params))));
|
|
1308
1335
|
server.tool("reports", "Generate Markdown reports. report=mixin_conflicts|tag_conflicts|version_conflicts|mod_overview|gradle_deps|pack_compat|dep_graph|sidedness|mod_complexity|pack_changelog. savePath to write to disk.", {
|
|
1309
1336
|
report: z.enum(["mixin_conflicts", "tag_conflicts", "version_conflicts", "mod_overview", "gradle_deps", "pack_compat", "dep_graph", "sidedness", "mod_complexity", "pack_changelog"]),
|
|
1310
1337
|
savePath: z.string().optional().describe("Absolute path to save the .md file, e.g. 'C:/reports/mixin_conflicts.md'"),
|
|
@@ -1469,8 +1496,23 @@ async function startHttpServer(port) {
|
|
|
1469
1496
|
const transports = new Map();
|
|
1470
1497
|
const readBody = (req) => new Promise((resolve, reject) => {
|
|
1471
1498
|
const chunks = [];
|
|
1472
|
-
|
|
1499
|
+
let size = 0;
|
|
1500
|
+
let oversized = false;
|
|
1501
|
+
req.on("data", (c) => {
|
|
1502
|
+
size += c.length;
|
|
1503
|
+
if (size > 8 * 1024 * 1024) {
|
|
1504
|
+
if (!oversized) {
|
|
1505
|
+
oversized = true;
|
|
1506
|
+
chunks.length = 0;
|
|
1507
|
+
reject(new HttpBodyTooLargeError("Request body exceeds 8 MiB; use project upload chunks"));
|
|
1508
|
+
}
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
chunks.push(c);
|
|
1512
|
+
});
|
|
1473
1513
|
req.on("end", () => {
|
|
1514
|
+
if (oversized)
|
|
1515
|
+
return;
|
|
1474
1516
|
const raw = Buffer.concat(chunks).toString("utf8");
|
|
1475
1517
|
if (!raw)
|
|
1476
1518
|
return resolve(undefined);
|
|
@@ -1482,6 +1524,7 @@ async function startHttpServer(port) {
|
|
|
1482
1524
|
}
|
|
1483
1525
|
});
|
|
1484
1526
|
req.on("error", reject);
|
|
1527
|
+
req.on("aborted", () => reject(new Error("Request aborted")));
|
|
1485
1528
|
});
|
|
1486
1529
|
const sendJson = (res, status, body) => {
|
|
1487
1530
|
const payload = JSON.stringify(body);
|
|
@@ -1551,6 +1594,8 @@ async function startHttpServer(port) {
|
|
|
1551
1594
|
}
|
|
1552
1595
|
catch (err) {
|
|
1553
1596
|
if (!res.headersSent) {
|
|
1597
|
+
if (err instanceof HttpBodyTooLargeError)
|
|
1598
|
+
return sendJson(res, 413, { error: err.message });
|
|
1554
1599
|
if (err instanceof JsonBodyParseError) {
|
|
1555
1600
|
return sendJson(res, 400, {
|
|
1556
1601
|
jsonrpc: "2.0",
|