@creeperhost/modlens-mcp 1.6.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/README.md +938 -0
- package/dist/access-flags.d.ts +58 -0
- package/dist/access-flags.d.ts.map +1 -0
- package/dist/access-flags.js +79 -0
- package/dist/access-flags.js.map +1 -0
- package/dist/access-flags.test.d.ts +2 -0
- package/dist/access-flags.test.d.ts.map +1 -0
- package/dist/access-flags.test.js +189 -0
- package/dist/access-flags.test.js.map +1 -0
- package/dist/batch-ingest.d.ts +2 -0
- package/dist/batch-ingest.d.ts.map +1 -0
- package/dist/batch-ingest.js +54 -0
- package/dist/batch-ingest.js.map +1 -0
- package/dist/batch-resolve-mixins.d.ts +2 -0
- package/dist/batch-resolve-mixins.d.ts.map +1 -0
- package/dist/batch-resolve-mixins.js +36 -0
- package/dist/batch-resolve-mixins.js.map +1 -0
- package/dist/cache.d.ts +16 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +36 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1318 -0
- package/dist/cli.js.map +1 -0
- package/dist/curseforge.d.ts +53 -0
- package/dist/curseforge.d.ts.map +1 -0
- package/dist/curseforge.js +104 -0
- package/dist/curseforge.js.map +1 -0
- package/dist/db-backend.d.ts +17 -0
- package/dist/db-backend.d.ts.map +1 -0
- package/dist/db-backend.js +40 -0
- package/dist/db-backend.js.map +1 -0
- package/dist/db.d.ts +11 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +55 -0
- package/dist/db.js.map +1 -0
- package/dist/embed-queue.d.ts +12 -0
- package/dist/embed-queue.d.ts.map +1 -0
- package/dist/embed-queue.js +103 -0
- package/dist/embed-queue.js.map +1 -0
- package/dist/embeddings.d.ts +21 -0
- package/dist/embeddings.d.ts.map +1 -0
- package/dist/embeddings.js +55 -0
- package/dist/embeddings.js.map +1 -0
- package/dist/env-path.d.ts +19 -0
- package/dist/env-path.d.ts.map +1 -0
- package/dist/env-path.js +40 -0
- package/dist/env-path.js.map +1 -0
- package/dist/fetch-utils.d.ts +24 -0
- package/dist/fetch-utils.d.ts.map +1 -0
- package/dist/fetch-utils.js +73 -0
- package/dist/fetch-utils.js.map +1 -0
- package/dist/fetch-utils.test.d.ts +2 -0
- package/dist/fetch-utils.test.d.ts.map +1 -0
- package/dist/fetch-utils.test.js +133 -0
- package/dist/fetch-utils.test.js.map +1 -0
- package/dist/generated/sqlite/browser.d.ts +55 -0
- package/dist/generated/sqlite/browser.d.ts.map +1 -0
- package/dist/generated/sqlite/browser.js +18 -0
- package/dist/generated/sqlite/browser.js.map +1 -0
- package/dist/generated/sqlite/client.d.ts +72 -0
- package/dist/generated/sqlite/client.d.ts.map +1 -0
- package/dist/generated/sqlite/client.js +33 -0
- package/dist/generated/sqlite/client.js.map +1 -0
- package/dist/generated/sqlite/commonInputTypes.d.ts +387 -0
- package/dist/generated/sqlite/commonInputTypes.d.ts.map +1 -0
- package/dist/generated/sqlite/commonInputTypes.js +11 -0
- package/dist/generated/sqlite/commonInputTypes.js.map +1 -0
- package/dist/generated/sqlite/enums.d.ts +2 -0
- package/dist/generated/sqlite/enums.d.ts.map +1 -0
- package/dist/generated/sqlite/enums.js +11 -0
- package/dist/generated/sqlite/enums.js.map +1 -0
- package/dist/generated/sqlite/internal/class.d.ts +226 -0
- package/dist/generated/sqlite/internal/class.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/class.js +84 -0
- package/dist/generated/sqlite/internal/class.js.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespace.d.ts +1344 -0
- package/dist/generated/sqlite/internal/prismaNamespace.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespace.js +220 -0
- package/dist/generated/sqlite/internal/prismaNamespace.js.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts +204 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.d.ts.map +1 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js +191 -0
- package/dist/generated/sqlite/internal/prismaNamespaceBrowser.js.map +1 -0
- package/dist/generated/sqlite/models/DocEntry.d.ts +1223 -0
- package/dist/generated/sqlite/models/DocEntry.d.ts.map +1 -0
- package/dist/generated/sqlite/models/DocEntry.js +2 -0
- package/dist/generated/sqlite/models/DocEntry.js.map +1 -0
- package/dist/generated/sqlite/models/McSourceFile.d.ts +1289 -0
- package/dist/generated/sqlite/models/McSourceFile.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McSourceFile.js +2 -0
- package/dist/generated/sqlite/models/McSourceFile.js.map +1 -0
- package/dist/generated/sqlite/models/McVersion.d.ts +1315 -0
- package/dist/generated/sqlite/models/McVersion.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McVersion.js +2 -0
- package/dist/generated/sqlite/models/McVersion.js.map +1 -0
- package/dist/generated/sqlite/models/McVersionDiff.d.ts +1061 -0
- package/dist/generated/sqlite/models/McVersionDiff.d.ts.map +1 -0
- package/dist/generated/sqlite/models/McVersionDiff.js +2 -0
- package/dist/generated/sqlite/models/McVersionDiff.js.map +1 -0
- package/dist/generated/sqlite/models/Mod.d.ts +2396 -0
- package/dist/generated/sqlite/models/Mod.d.ts.map +1 -0
- package/dist/generated/sqlite/models/Mod.js +2 -0
- package/dist/generated/sqlite/models/Mod.js.map +1 -0
- package/dist/generated/sqlite/models/ModClass.d.ts +1254 -0
- package/dist/generated/sqlite/models/ModClass.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModClass.js +2 -0
- package/dist/generated/sqlite/models/ModClass.js.map +1 -0
- package/dist/generated/sqlite/models/ModSourceFile.d.ts +1283 -0
- package/dist/generated/sqlite/models/ModSourceFile.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModSourceFile.js +2 -0
- package/dist/generated/sqlite/models/ModSourceFile.js.map +1 -0
- package/dist/generated/sqlite/models/ModTag.d.ts +1278 -0
- package/dist/generated/sqlite/models/ModTag.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModTag.js +2 -0
- package/dist/generated/sqlite/models/ModTag.js.map +1 -0
- package/dist/generated/sqlite/models/ModVersionDiff.d.ts +1073 -0
- package/dist/generated/sqlite/models/ModVersionDiff.d.ts.map +1 -0
- package/dist/generated/sqlite/models/ModVersionDiff.js +2 -0
- package/dist/generated/sqlite/models/ModVersionDiff.js.map +1 -0
- package/dist/generated/sqlite/models/Primer.d.ts +1319 -0
- package/dist/generated/sqlite/models/Primer.d.ts.map +1 -0
- package/dist/generated/sqlite/models/Primer.js +2 -0
- package/dist/generated/sqlite/models/Primer.js.map +1 -0
- package/dist/generated/sqlite/models.d.ts +12 -0
- package/dist/generated/sqlite/models.d.ts.map +1 -0
- package/dist/generated/sqlite/models.js +2 -0
- package/dist/generated/sqlite/models.js.map +1 -0
- package/dist/jar.d.ts +7 -0
- package/dist/jar.d.ts.map +1 -0
- package/dist/jar.js +52 -0
- package/dist/jar.js.map +1 -0
- package/dist/java-tools.d.ts +46 -0
- package/dist/java-tools.d.ts.map +1 -0
- package/dist/java-tools.js +263 -0
- package/dist/java-tools.js.map +1 -0
- package/dist/launcher.d.ts +3 -0
- package/dist/launcher.d.ts.map +1 -0
- package/dist/launcher.js +105 -0
- package/dist/launcher.js.map +1 -0
- package/dist/mappings.d.ts +95 -0
- package/dist/mappings.d.ts.map +1 -0
- package/dist/mappings.js +1094 -0
- package/dist/mappings.js.map +1 -0
- package/dist/mappings.test.d.ts +2 -0
- package/dist/mappings.test.d.ts.map +1 -0
- package/dist/mappings.test.js +161 -0
- package/dist/mappings.test.js.map +1 -0
- package/dist/minecraft.d.ts +21 -0
- package/dist/minecraft.d.ts.map +1 -0
- package/dist/minecraft.js +70 -0
- package/dist/minecraft.js.map +1 -0
- package/dist/modpacks-ch.d.ts +224 -0
- package/dist/modpacks-ch.d.ts.map +1 -0
- package/dist/modpacks-ch.js +146 -0
- package/dist/modpacks-ch.js.map +1 -0
- package/dist/modrinth.d.ts +67 -0
- package/dist/modrinth.d.ts.map +1 -0
- package/dist/modrinth.js +88 -0
- package/dist/modrinth.js.map +1 -0
- package/dist/platform-adapter.d.ts +23 -0
- package/dist/platform-adapter.d.ts.map +1 -0
- package/dist/platform-adapter.js +2 -0
- package/dist/platform-adapter.js.map +1 -0
- package/dist/platform.d.ts +47 -0
- package/dist/platform.d.ts.map +1 -0
- package/dist/platform.js +175 -0
- package/dist/platform.js.map +1 -0
- package/dist/processor.d.ts +51 -0
- package/dist/processor.d.ts.map +1 -0
- package/dist/processor.js +655 -0
- package/dist/processor.js.map +1 -0
- package/dist/processor.test.d.ts +2 -0
- package/dist/processor.test.d.ts.map +1 -0
- package/dist/processor.test.js +629 -0
- package/dist/processor.test.js.map +1 -0
- package/dist/repositories/embeddings-sqlite.d.ts +38 -0
- package/dist/repositories/embeddings-sqlite.d.ts.map +1 -0
- package/dist/repositories/embeddings-sqlite.js +208 -0
- package/dist/repositories/embeddings-sqlite.js.map +1 -0
- package/dist/repositories/embeddings.d.ts +40 -0
- package/dist/repositories/embeddings.d.ts.map +1 -0
- package/dist/repositories/embeddings.js +136 -0
- package/dist/repositories/embeddings.js.map +1 -0
- package/dist/repositories/index.d.ts +2 -0
- package/dist/repositories/index.d.ts.map +1 -0
- package/dist/repositories/index.js +15 -0
- package/dist/repositories/index.js.map +1 -0
- package/dist/repositories/mcVersion.d.ts +16 -0
- package/dist/repositories/mcVersion.d.ts.map +1 -0
- package/dist/repositories/mcVersion.js +52 -0
- package/dist/repositories/mcVersion.js.map +1 -0
- package/dist/repositories/mod.d.ts +241 -0
- package/dist/repositories/mod.d.ts.map +1 -0
- package/dist/repositories/mod.js +342 -0
- package/dist/repositories/mod.js.map +1 -0
- package/dist/repositories/packs.d.ts +44 -0
- package/dist/repositories/packs.d.ts.map +1 -0
- package/dist/repositories/packs.js +138 -0
- package/dist/repositories/packs.js.map +1 -0
- package/dist/search-adapter.d.ts +34 -0
- package/dist/search-adapter.d.ts.map +1 -0
- package/dist/search-adapter.js +195 -0
- package/dist/search-adapter.js.map +1 -0
- package/dist/search.d.ts +6 -0
- package/dist/search.d.ts.map +1 -0
- package/dist/search.js +54 -0
- package/dist/search.js.map +1 -0
- package/dist/search.test.d.ts +2 -0
- package/dist/search.test.d.ts.map +1 -0
- package/dist/search.test.js +71 -0
- package/dist/search.test.js.map +1 -0
- package/dist/security.d.ts +98 -0
- package/dist/security.d.ts.map +1 -0
- package/dist/security.js +446 -0
- package/dist/security.js.map +1 -0
- package/dist/security.test.d.ts +2 -0
- package/dist/security.test.d.ts.map +1 -0
- package/dist/security.test.js +687 -0
- package/dist/security.test.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1465 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +2 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +1113 -0
- package/dist/setup.js.map +1 -0
- package/dist/tools/bytecode.d.ts +129 -0
- package/dist/tools/bytecode.d.ts.map +1 -0
- package/dist/tools/bytecode.js +679 -0
- package/dist/tools/bytecode.js.map +1 -0
- package/dist/tools/catalog.d.ts +54 -0
- package/dist/tools/catalog.d.ts.map +1 -0
- package/dist/tools/catalog.js +249 -0
- package/dist/tools/catalog.js.map +1 -0
- package/dist/tools/compat-check.d.ts +23 -0
- package/dist/tools/compat-check.d.ts.map +1 -0
- package/dist/tools/compat-check.js +250 -0
- package/dist/tools/compat-check.js.map +1 -0
- package/dist/tools/compat-check.test.d.ts +2 -0
- package/dist/tools/compat-check.test.d.ts.map +1 -0
- package/dist/tools/compat-check.test.js +179 -0
- package/dist/tools/compat-check.test.js.map +1 -0
- package/dist/tools/diagnostics.d.ts +10 -0
- package/dist/tools/diagnostics.d.ts.map +1 -0
- package/dist/tools/diagnostics.js +133 -0
- package/dist/tools/diagnostics.js.map +1 -0
- package/dist/tools/diagnostics.test.d.ts +2 -0
- package/dist/tools/diagnostics.test.d.ts.map +1 -0
- package/dist/tools/diagnostics.test.js +168 -0
- package/dist/tools/diagnostics.test.js.map +1 -0
- package/dist/tools/docs.d.ts +19 -0
- package/dist/tools/docs.d.ts.map +1 -0
- package/dist/tools/docs.js +230 -0
- package/dist/tools/docs.js.map +1 -0
- package/dist/tools/embed-registry.d.ts +125 -0
- package/dist/tools/embed-registry.d.ts.map +1 -0
- package/dist/tools/embed-registry.js +560 -0
- package/dist/tools/embed-registry.js.map +1 -0
- package/dist/tools/gradle.d.ts +16 -0
- package/dist/tools/gradle.d.ts.map +1 -0
- package/dist/tools/gradle.js +182 -0
- package/dist/tools/gradle.js.map +1 -0
- package/dist/tools/graphify.d.ts +144 -0
- package/dist/tools/graphify.d.ts.map +1 -0
- package/dist/tools/graphify.js +798 -0
- package/dist/tools/graphify.js.map +1 -0
- package/dist/tools/graphify.test.d.ts +2 -0
- package/dist/tools/graphify.test.d.ts.map +1 -0
- package/dist/tools/graphify.test.js +39 -0
- package/dist/tools/graphify.test.js.map +1 -0
- package/dist/tools/ingest.d.ts +85 -0
- package/dist/tools/ingest.d.ts.map +1 -0
- package/dist/tools/ingest.js +477 -0
- package/dist/tools/ingest.js.map +1 -0
- package/dist/tools/ingest.test.d.ts +2 -0
- package/dist/tools/ingest.test.d.ts.map +1 -0
- package/dist/tools/ingest.test.js +340 -0
- package/dist/tools/ingest.test.js.map +1 -0
- package/dist/tools/kubejs.d.ts +37 -0
- package/dist/tools/kubejs.d.ts.map +1 -0
- package/dist/tools/kubejs.js +245 -0
- package/dist/tools/kubejs.js.map +1 -0
- package/dist/tools/mappings.d.ts +17 -0
- package/dist/tools/mappings.d.ts.map +1 -0
- package/dist/tools/mappings.js +85 -0
- package/dist/tools/mappings.js.map +1 -0
- package/dist/tools/mc-fts.d.ts +101 -0
- package/dist/tools/mc-fts.d.ts.map +1 -0
- package/dist/tools/mc-fts.js +279 -0
- package/dist/tools/mc-fts.js.map +1 -0
- package/dist/tools/mcmeta.d.ts +41 -0
- package/dist/tools/mcmeta.d.ts.map +1 -0
- package/dist/tools/mcmeta.js +365 -0
- package/dist/tools/mcmeta.js.map +1 -0
- package/dist/tools/mixin-scan.d.ts +113 -0
- package/dist/tools/mixin-scan.d.ts.map +1 -0
- package/dist/tools/mixin-scan.js +433 -0
- package/dist/tools/mixin-scan.js.map +1 -0
- package/dist/tools/mod-data.d.ts +83 -0
- package/dist/tools/mod-data.d.ts.map +1 -0
- package/dist/tools/mod-data.js +630 -0
- package/dist/tools/mod-data.js.map +1 -0
- package/dist/tools/mod-tags.d.ts +59 -0
- package/dist/tools/mod-tags.d.ts.map +1 -0
- package/dist/tools/mod-tags.js +279 -0
- package/dist/tools/mod-tags.js.map +1 -0
- package/dist/tools/mod-tags.test.d.ts +2 -0
- package/dist/tools/mod-tags.test.d.ts.map +1 -0
- package/dist/tools/mod-tags.test.js +75 -0
- package/dist/tools/mod-tags.test.js.map +1 -0
- package/dist/tools/modpacks-ch.d.ts +257 -0
- package/dist/tools/modpacks-ch.d.ts.map +1 -0
- package/dist/tools/modpacks-ch.js +516 -0
- package/dist/tools/modpacks-ch.js.map +1 -0
- package/dist/tools/packtools.d.ts +113 -0
- package/dist/tools/packtools.d.ts.map +1 -0
- package/dist/tools/packtools.js +809 -0
- package/dist/tools/packtools.js.map +1 -0
- package/dist/tools/packtools.test.d.ts +2 -0
- package/dist/tools/packtools.test.d.ts.map +1 -0
- package/dist/tools/packtools.test.js +99 -0
- package/dist/tools/packtools.test.js.map +1 -0
- package/dist/tools/platform.d.ts +68 -0
- package/dist/tools/platform.d.ts.map +1 -0
- package/dist/tools/platform.js +452 -0
- package/dist/tools/platform.js.map +1 -0
- package/dist/tools/primers.d.ts +36 -0
- package/dist/tools/primers.d.ts.map +1 -0
- package/dist/tools/primers.js +451 -0
- package/dist/tools/primers.js.map +1 -0
- package/dist/tools/reports.d.ts +19 -0
- package/dist/tools/reports.d.ts.map +1 -0
- package/dist/tools/reports.js +573 -0
- package/dist/tools/reports.js.map +1 -0
- package/dist/tools/source.d.ts +10 -0
- package/dist/tools/source.d.ts.map +1 -0
- package/dist/tools/source.js +101 -0
- package/dist/tools/source.js.map +1 -0
- package/dist/tools/vanilla-data.d.ts +127 -0
- package/dist/tools/vanilla-data.d.ts.map +1 -0
- package/dist/tools/vanilla-data.js +824 -0
- package/dist/tools/vanilla-data.js.map +1 -0
- package/dist/tools/vanilla.d.ts +171 -0
- package/dist/tools/vanilla.d.ts.map +1 -0
- package/dist/tools/vanilla.js +612 -0
- package/dist/tools/vanilla.js.map +1 -0
- package/dist/tools/version-diff.d.ts +113 -0
- package/dist/tools/version-diff.d.ts.map +1 -0
- package/dist/tools/version-diff.js +519 -0
- package/dist/tools/version-diff.js.map +1 -0
- package/dist/tools/version-diff.test.d.ts +2 -0
- package/dist/tools/version-diff.test.d.ts.map +1 -0
- package/dist/tools/version-diff.test.js +102 -0
- package/dist/tools/version-diff.test.js.map +1 -0
- package/dist/validate.d.ts +19 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +54 -0
- package/dist/validate.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +73 -0
- package/dist/validate.test.js.map +1 -0
- package/package.json +62 -0
- package/prisma/backends/schema.sqlite.prisma +215 -0
- package/prisma/backends/template.db +0 -0
- package/prisma/schema.prisma +303 -0
- package/scripts/backup.mjs +151 -0
- package/scripts/build-template-db.mjs +38 -0
- package/scripts/check-atm10.mjs +27 -0
- package/scripts/check-sync.mjs +13 -0
- package/scripts/create-fts-index.mjs +50 -0
- package/scripts/enable-pgvector.mjs +90 -0
- package/scripts/enable-sqlite-vec.mjs +158 -0
- package/scripts/migrate-backend.mjs +188 -0
- package/scripts/migrate-embed-provenance.mjs +80 -0
- package/scripts/migrate-metadata-source.mjs +62 -0
- package/scripts/migrate-mod-source-files.mjs +67 -0
- package/scripts/post-update.mjs +77 -0
- package/scripts/verify-atm10.mjs +63 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,1465 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { createServer as createHttpServer } from "node:http";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { startupEmbedScan } from "./embed-queue.js";
|
|
8
|
+
import { ingestMod, decompileMod, decompileModStatus, reindexClasses, batchIngest, batchDecompileMods, deleteModFull, refreshDegradedMetadata } from "./tools/ingest.js";
|
|
9
|
+
import { listMods, getModDetails, searchMods, getDbStats, getDependencies, findVersionConflicts, getDependencyGraph, listModSourceUrls, listModRegistryEntries } from "./tools/catalog.js";
|
|
10
|
+
import { listModJarFiles, getModJarFile, getModModel, getModLang, getModSounds, listModDataTags, getModDataTag, getModAtlas, getModManifest, listModConfigs, getModConfig, diffModData, listModData, getModData, } from "./tools/mod-data.js";
|
|
11
|
+
import { traceRecipeChain } from "./tools/mod-data.js";
|
|
12
|
+
import { getModSource, searchSource, decompileModClass } from "./tools/source.js";
|
|
13
|
+
import { buildModGraph, graphBuildStatus, queryModGraph, getGraphReport, enrichNextChunk, submitEnrichment, downloadGraph, exportGraph, submitGraph } from "./tools/graphify.js";
|
|
14
|
+
import { exportEmbeddings, downloadEmbeddings, downloadPackEmbeddings, getEmbedStatus, submitEmbeddings } from "./tools/embed-registry.js";
|
|
15
|
+
import { searchModClass, getModClassMembers, getModClassBytecode, findModReferences, getModInheritance, diffModVersions, findImplementors, scanModRegistrations, findAnnotatedClasses, crossModRefs, findEventListeners, findOptionalIntegrations, findNetworkPayloads, extractConfigSchema, } from "./tools/bytecode.js";
|
|
16
|
+
import { getMixinTargets, getMixinConflicts, getAtEntries, getAwEntries, resolveMixinTargets, getMixinsTargetingPackage, findAtAwConflicts } from "./tools/mixin-scan.js";
|
|
17
|
+
import { syncModrinth, syncCurseforge, checkUpdates, downloadSource, batchSyncSources, searchPlatforms, batchCheckUpdates } from "./tools/platform.js";
|
|
18
|
+
import { listMcVersions, listNeoForgeVersions, listFabricApiVersions, listForgeVersions, downloadNeoForge, downloadFabricApi, downloadForge } from "./platform.js";
|
|
19
|
+
import { searchMinecraftClass, getMinecraftSource, getMcClassBytecode, getMcClassMembers, findMcReferences, getMcInheritance, diffMcVersions, decompileMcVersion, decompileMcVersionStatus, searchMcCode, validateAccessWidener, analyzeMixin, searchEvents, } from "./tools/vanilla.js";
|
|
20
|
+
import { diffMcVersionsDetailed, diffModVersionsDetailed } from "./tools/version-diff.js";
|
|
21
|
+
import { indexMcVersion, searchMcIndexed, indexMcSourceSemantic, searchMcSourceSemantic, indexModSourceFts, indexModSourceSemantic, searchModSourceIndexed, searchModSourceSemantic } from "./tools/mc-fts.js";
|
|
22
|
+
import { findMapping, remapModJar, getParchment, listParchmentVersions, getParchmentSummary } from "./tools/mappings.js";
|
|
23
|
+
import { ingestDocumentation, getDocumentation, searchDocumentation, listDocumentation, deleteDocumentation, seedDefaultDocumentation, semanticSearchDocumentation, backfillDocEmbeddings } from "./tools/docs.js";
|
|
24
|
+
import { getMcmetaVersions, getMcBlocks, getMcCommands, getMcRegistries, getMcSounds, getMcItemComponents, getMcDataFile, getMcAssetFile, listMcDataFiles, diffMcData, getMcAtlas, getMcmetaRaw, getRegistryEntries, compareVersions, getVersionChangelog, } from "./tools/mcmeta.js";
|
|
25
|
+
import { ingestPrimer, getPrimer, getPrimersByVersionRange, searchPrimers, listPrimers, deletePrimer, seedDefaultPrimers, semanticSearchPrimers, backfillPrimerEmbeddings, } from "./tools/primers.js";
|
|
26
|
+
import { getMcTags, findTagsForEntry, listRecipes, getRecipe, findRecipesForItem, listLootTables, getLootTable, getLangEntries, getBlockstate, getMcModel, getModelTree, listBiomes, getBiome, listDamageTypes, listEnchantments, getEnchantment, listAdvancements, getAdvancement, listStructures, getStructureData, getMcParticles, getParticleData, getEntityAttributes, } from "./tools/vanilla-data.js";
|
|
27
|
+
import { indexModTags, indexAllModTags, listTagNamespaces, getTagContributors, getModTagList, findTagConflicts, searchModTags, expandTag, } from "./tools/mod-tags.js";
|
|
28
|
+
import { listModsWithMixins, getMixinConflictMatrix, getMixinClassDetail, getMixinHotspots, batchResolveMixins, } from "./tools/mixin-scan.js";
|
|
29
|
+
import { getModGradleFiles, searchGradleFiles, compareGradleDeps, } from "./tools/gradle.js";
|
|
30
|
+
import { generateReport } from "./tools/reports.js";
|
|
31
|
+
import { findAssetConflicts, findVanillaOverrides, analyzeModSidedness, analyzePackSidedness, computeModComplexity, computePackChangelog, findDataConflicts, buildPackGraph, diffPackConfigs, packHealth } from "./tools/packtools.js";
|
|
32
|
+
import { indexKubeJsScripts, searchKubeJsScripts, semanticSearchKubeJsScripts } from "./tools/kubejs.js";
|
|
33
|
+
import { searchPacksAction, featuredPacksAction, packInfoAction, packManifestAction, syncPackModsAction, searchFtbModsAction, ftbModInfoAction, downloadModAction, downloadOverridesAction, listPackVersionsAction, listPackFilesAction, findModInPacksAction } from "./tools/modpacks-ch.js";
|
|
34
|
+
import { analyzeCrashLog, findMissingDeps } from "./tools/diagnostics.js";
|
|
35
|
+
import { checkModCompat } from "./tools/compat-check.js";
|
|
36
|
+
import { disconnect } from "./db.js";
|
|
37
|
+
import { mcPaths } from "./minecraft.js";
|
|
38
|
+
import { findModById, resolveModRefSlim } from "./repositories/mod.js";
|
|
39
|
+
import { CACHE_ROOT } from "./cache.js";
|
|
40
|
+
// Load .env — try ~/.modlens/.env first (npx/installed users), then local .env (git-clone users)
|
|
41
|
+
import { readFileSync, existsSync } from "fs";
|
|
42
|
+
import { join, dirname } from "path";
|
|
43
|
+
import { fileURLToPath } from "url";
|
|
44
|
+
import { ENV_PATH } from "./env-path.js";
|
|
45
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
46
|
+
// Always attempt to load the resolved ENV_PATH (covers both modes)
|
|
47
|
+
const localEnvPath = join(__dirname, "..", ".env");
|
|
48
|
+
for (const ep of [ENV_PATH, localEnvPath]) {
|
|
49
|
+
if (existsSync(ep)) {
|
|
50
|
+
for (const line of readFileSync(ep, "utf8").split("\n")) {
|
|
51
|
+
const m = line.match(/^([^#=]+)=(.*)$/);
|
|
52
|
+
if (m)
|
|
53
|
+
process.env[m[1].trim()] ??= m[2].trim().replace(/^["']|["']$/g, "");
|
|
54
|
+
}
|
|
55
|
+
break; // only load the first one found
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Version is sourced from package.json so it stays in sync with releases.
|
|
59
|
+
// package.json sits one level up from both src/ (dev) and dist/ (built).
|
|
60
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8"));
|
|
61
|
+
const server = new McpServer({ name: "modlens", version: pkg.version });
|
|
62
|
+
/** Serialize any result to MCP text content. */
|
|
63
|
+
function out(result) {
|
|
64
|
+
const text = typeof result === "string" ? result
|
|
65
|
+
: Array.isArray(result) && result.every(x => typeof x === "string") ? result.join("\n")
|
|
66
|
+
: JSON.stringify(result, null, 2);
|
|
67
|
+
return { content: [{ type: "text", text }] };
|
|
68
|
+
}
|
|
69
|
+
/** Coerce modId to dbId when dbId wasn't explicitly provided. */
|
|
70
|
+
function resolveDbId(dbId, modId) {
|
|
71
|
+
if (dbId != null)
|
|
72
|
+
return dbId;
|
|
73
|
+
if (modId == null)
|
|
74
|
+
return undefined;
|
|
75
|
+
const n = typeof modId === "number" ? modId : Number(modId);
|
|
76
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Like resolveDbId, but for actions that need a valid DB id — will look up
|
|
80
|
+
* string modIds via the database so agents can pass e.g. modId:"polylib".
|
|
81
|
+
*/
|
|
82
|
+
async function resolveDbIdAsync(dbId, modId) {
|
|
83
|
+
if (dbId != null)
|
|
84
|
+
return dbId;
|
|
85
|
+
if (modId == null)
|
|
86
|
+
return undefined;
|
|
87
|
+
const n = typeof modId === "number" ? modId : Number(modId);
|
|
88
|
+
if (Number.isFinite(n) && n > 0)
|
|
89
|
+
return n;
|
|
90
|
+
// String modId — resolve via DB lookup
|
|
91
|
+
if (typeof modId === "string") {
|
|
92
|
+
const mod = await resolveModRefSlim(modId);
|
|
93
|
+
if (mod)
|
|
94
|
+
return mod.id;
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
/** Wrap tool handler so thrown errors return an MCP error response instead of hanging. */
|
|
99
|
+
function safe(fn) {
|
|
100
|
+
return async (...args) => {
|
|
101
|
+
try {
|
|
102
|
+
return await fn(...args);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
106
|
+
const stack = err instanceof Error && err.stack
|
|
107
|
+
? "\n" + err.stack.split("\n").slice(1, 4).join("\n")
|
|
108
|
+
: "";
|
|
109
|
+
return { content: [{ type: "text", text: msg + stack }], isError: true };
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// ── 1. mod ────────────────────────────────────────────────────────────────────
|
|
114
|
+
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: (stub) community graph submission — not yet functional. 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: (stub) community embedding submission — not yet functional. Auto-behaviors: decompile_status auto-queues embedding (MODLENS_AUTO_EMBED) and graph build (MODLENS_AUTO_GRAPH). Pass autoEmbed/autoGraph to override per-call.", {
|
|
115
|
+
action: z.enum([
|
|
116
|
+
"ingest", "list", "get", "search", "stats", "dependencies", "dep_graph",
|
|
117
|
+
"version_conflicts", "source_urls", "decompile", "decompile_status",
|
|
118
|
+
"decompile_class", "source", "search_source", "reindex", "batch_ingest",
|
|
119
|
+
"batch_decompile", "refresh_metadata", "index_fts", "search_indexed", "index_semantic", "search_semantic", "get_paths", "delete",
|
|
120
|
+
"graph_build", "graph_status", "graph_query", "graph_report",
|
|
121
|
+
"graph_enrich_next", "graph_enrich_submit", "graph_download", "graph_export", "graph_submit",
|
|
122
|
+
"embed_export", "embed_download", "embed_download_all", "embed_status", "embed_submit",
|
|
123
|
+
]),
|
|
124
|
+
jarPath: z.string().optional(),
|
|
125
|
+
modId: z.union([z.string(), z.number()]).optional().describe("mod ID or DB id"),
|
|
126
|
+
dbId: z.number().optional().describe("DB id"),
|
|
127
|
+
query: z.string().optional(),
|
|
128
|
+
path: z.string().optional(),
|
|
129
|
+
className: z.string().optional(),
|
|
130
|
+
loader: z.string().optional().describe("fabric|neoforge|forge|quilt"),
|
|
131
|
+
mcVersion: z.string().optional(),
|
|
132
|
+
hasMixins: z.boolean().optional(),
|
|
133
|
+
decompiled: z.boolean().optional(),
|
|
134
|
+
recursive: z.boolean().optional(),
|
|
135
|
+
skipSource: z.boolean().optional(),
|
|
136
|
+
isRegex: z.boolean().optional(),
|
|
137
|
+
force: z.boolean().optional(),
|
|
138
|
+
limit: z.number().optional(),
|
|
139
|
+
directory: z.string().optional().describe("Directory of JARs (batch_ingest)"),
|
|
140
|
+
indexClasses: z.boolean().optional(),
|
|
141
|
+
replace: z.boolean().optional().describe("Replace existing mod with same modId (batch_ingest, ingest)"),
|
|
142
|
+
budget: z.number().optional().describe("Token budget for graph_query (default 2000)"),
|
|
143
|
+
backend: z.string().optional().describe("Graph backend override: ollama|gemini|deepseek|openai|claude|kimi|custom|ast-only"),
|
|
144
|
+
chunkIndex: z.number().optional().describe("Chunk index for graph_enrich_submit"),
|
|
145
|
+
nodes: z.array(z.record(z.unknown())).optional().describe("Enriched nodes for graph_enrich_submit"),
|
|
146
|
+
edges: z.array(z.record(z.unknown())).optional().describe("Enriched edges for graph_enrich_submit"),
|
|
147
|
+
outputDir: z.string().optional().describe("Output directory for embed_export"),
|
|
148
|
+
modVersion: z.string().optional().describe("Mod version for embed_download"),
|
|
149
|
+
targetType: z.enum(["mod", "vanilla", "modloader"]).optional().describe("Target scope for graph/embed actions"),
|
|
150
|
+
targetId: z.string().optional().describe("Target identifier (mod id, minecraft, loader id)"),
|
|
151
|
+
targetVersion: z.string().optional().describe("Target version (mod version, MC version, loader version)"),
|
|
152
|
+
targetLoader: z.string().optional().describe("Optional loader hint for registry matching"),
|
|
153
|
+
targetMcVersion: z.string().optional().describe("Optional MC version hint for registry matching"),
|
|
154
|
+
model: z.string().optional().describe("Embedding model override for embed_download"),
|
|
155
|
+
autoEmbed: z.boolean().optional().describe("Override auto-embed on decompile completion (default: env MODLENS_AUTO_EMBED, true)"),
|
|
156
|
+
autoGraph: z.boolean().optional().describe("Override auto-graph-build on decompile/source-download (default: env MODLENS_AUTO_GRAPH, true)"),
|
|
157
|
+
provenance: z.enum(["local", "registry", "community"]).optional().describe("Filter search_semantic results by embedding source (default: all sources)"),
|
|
158
|
+
}, 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 }) => {
|
|
159
|
+
const dbId = await resolveDbIdAsync(rawDbId, modId);
|
|
160
|
+
const resolvedTargetType = targetType ?? "mod";
|
|
161
|
+
const resolvedTargetId = targetId ?? (typeof modId === "string" ? modId : undefined) ?? (resolvedTargetType === "vanilla" ? "minecraft" : undefined);
|
|
162
|
+
const resolvedTargetVersion = targetVersion ?? modVersion ?? (resolvedTargetType === "vanilla" ? mcVersion : undefined);
|
|
163
|
+
let result;
|
|
164
|
+
switch (action) {
|
|
165
|
+
case "ingest":
|
|
166
|
+
result = await ingestMod(jarPath, skipSource ?? false, replace ?? false);
|
|
167
|
+
break;
|
|
168
|
+
case "list":
|
|
169
|
+
result = await listMods({ loader: loader, mcVersion, hasMixins, decompiled, limit: limit ?? 50 });
|
|
170
|
+
break;
|
|
171
|
+
case "get":
|
|
172
|
+
result = await getModDetails(modId);
|
|
173
|
+
break;
|
|
174
|
+
case "search":
|
|
175
|
+
result = await searchMods(query, { loader, mcVersion, limit: limit ?? 20 });
|
|
176
|
+
break;
|
|
177
|
+
case "stats":
|
|
178
|
+
result = await getDbStats();
|
|
179
|
+
break;
|
|
180
|
+
case "dependencies":
|
|
181
|
+
result = await getDependencies(modId, recursive ?? false);
|
|
182
|
+
break;
|
|
183
|
+
case "dep_graph":
|
|
184
|
+
result = await getDependencyGraph(mcVersion);
|
|
185
|
+
break;
|
|
186
|
+
case "version_conflicts":
|
|
187
|
+
result = await findVersionConflicts();
|
|
188
|
+
break;
|
|
189
|
+
case "source_urls":
|
|
190
|
+
result = await listModSourceUrls(query);
|
|
191
|
+
break;
|
|
192
|
+
case "decompile":
|
|
193
|
+
result = await decompileMod(dbId);
|
|
194
|
+
break;
|
|
195
|
+
case "decompile_status":
|
|
196
|
+
result = await decompileModStatus(dbId, { autoEmbed, autoGraph });
|
|
197
|
+
break;
|
|
198
|
+
case "decompile_class":
|
|
199
|
+
result = await decompileModClass(dbId, className);
|
|
200
|
+
break;
|
|
201
|
+
case "source":
|
|
202
|
+
result = await getModSource(dbId, path);
|
|
203
|
+
break;
|
|
204
|
+
case "search_source":
|
|
205
|
+
result = await searchSource(query, dbId, isRegex ?? false, limit ?? 50);
|
|
206
|
+
break;
|
|
207
|
+
case "reindex":
|
|
208
|
+
result = await reindexClasses(dbId);
|
|
209
|
+
break;
|
|
210
|
+
case "batch_ingest":
|
|
211
|
+
result = await batchIngest(directory, skipSource ?? true, indexClasses ?? false, replace ?? false);
|
|
212
|
+
break;
|
|
213
|
+
case "batch_decompile":
|
|
214
|
+
result = await batchDecompileMods({ concurrency: (limit ?? 2), autoEmbed, autoGraph });
|
|
215
|
+
break;
|
|
216
|
+
case "refresh_metadata":
|
|
217
|
+
result = await refreshDegradedMetadata({ loader: loader, mcVersion });
|
|
218
|
+
break;
|
|
219
|
+
case "index_semantic":
|
|
220
|
+
result = await indexModSourceSemantic(dbId, 50, limit);
|
|
221
|
+
break;
|
|
222
|
+
case "search_semantic":
|
|
223
|
+
result = await searchModSourceSemantic(query, dbId, limit ?? 10, provenance);
|
|
224
|
+
break;
|
|
225
|
+
case "index_fts":
|
|
226
|
+
result = await indexModSourceFts(dbId);
|
|
227
|
+
break;
|
|
228
|
+
case "search_indexed":
|
|
229
|
+
result = await searchModSourceIndexed(dbId, query, limit ?? 20);
|
|
230
|
+
break;
|
|
231
|
+
case "get_paths": {
|
|
232
|
+
const mod = await findModById(dbId);
|
|
233
|
+
if (!mod)
|
|
234
|
+
throw new Error(`Mod #${dbId} not found`);
|
|
235
|
+
result = {
|
|
236
|
+
dbId: mod.id,
|
|
237
|
+
modId: mod.modId,
|
|
238
|
+
version: mod.version,
|
|
239
|
+
jarPath: mod.jarPath,
|
|
240
|
+
decompPath: mod.decompPath ?? null,
|
|
241
|
+
graphPath: mod.graphPath ?? null,
|
|
242
|
+
cacheRoot: CACHE_ROOT,
|
|
243
|
+
};
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
case "delete":
|
|
247
|
+
result = await deleteModFull(dbId);
|
|
248
|
+
break;
|
|
249
|
+
case "graph_build":
|
|
250
|
+
result = await buildModGraph(dbId, force ?? false, backend);
|
|
251
|
+
break;
|
|
252
|
+
case "graph_status":
|
|
253
|
+
result = await graphBuildStatus(dbId);
|
|
254
|
+
break;
|
|
255
|
+
case "graph_query":
|
|
256
|
+
result = await queryModGraph(dbId, query, budget);
|
|
257
|
+
break;
|
|
258
|
+
case "graph_report":
|
|
259
|
+
result = await getGraphReport(dbId);
|
|
260
|
+
break;
|
|
261
|
+
case "graph_enrich_next":
|
|
262
|
+
result = await enrichNextChunk(dbId);
|
|
263
|
+
break;
|
|
264
|
+
case "graph_enrich_submit":
|
|
265
|
+
result = await submitEnrichment(dbId, chunkIndex, nodes, edges);
|
|
266
|
+
break;
|
|
267
|
+
case "graph_download":
|
|
268
|
+
result = await downloadGraph({
|
|
269
|
+
targetType: resolvedTargetType,
|
|
270
|
+
dbId,
|
|
271
|
+
targetId: resolvedTargetId,
|
|
272
|
+
targetVersion: resolvedTargetVersion,
|
|
273
|
+
loader: targetLoader ?? loader,
|
|
274
|
+
mcVersion: targetMcVersion ?? mcVersion,
|
|
275
|
+
});
|
|
276
|
+
break;
|
|
277
|
+
case "graph_export":
|
|
278
|
+
result = await exportGraph(dbId, outputDir);
|
|
279
|
+
break;
|
|
280
|
+
case "graph_submit":
|
|
281
|
+
result = await submitGraph({ bundlePath: outputDir ?? "" });
|
|
282
|
+
break;
|
|
283
|
+
case "embed_export":
|
|
284
|
+
result = await exportEmbeddings(resolvedTargetType, outputDir, { dbId, mcVersion: resolvedTargetVersion ?? mcVersion, targetId: resolvedTargetId });
|
|
285
|
+
break;
|
|
286
|
+
case "embed_download":
|
|
287
|
+
result = await downloadEmbeddings({
|
|
288
|
+
targetType: resolvedTargetType,
|
|
289
|
+
targetId: resolvedTargetId,
|
|
290
|
+
targetVersion: resolvedTargetVersion,
|
|
291
|
+
dbId,
|
|
292
|
+
model,
|
|
293
|
+
force: force ?? false,
|
|
294
|
+
});
|
|
295
|
+
break;
|
|
296
|
+
case "embed_download_all":
|
|
297
|
+
result = await downloadPackEmbeddings();
|
|
298
|
+
break;
|
|
299
|
+
case "embed_status":
|
|
300
|
+
result = await getEmbedStatus({
|
|
301
|
+
targetType: resolvedTargetType,
|
|
302
|
+
dbId,
|
|
303
|
+
mcVersion: resolvedTargetVersion ?? mcVersion,
|
|
304
|
+
});
|
|
305
|
+
break;
|
|
306
|
+
case "embed_submit":
|
|
307
|
+
result = await submitEmbeddings({ bundlePath: outputDir ?? "" });
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
return out(result);
|
|
311
|
+
}));
|
|
312
|
+
// ── 2. mod_bytecode ───────────────────────────────────────────────────────────
|
|
313
|
+
server.tool("mod_bytecode", "Mod JAR bytecode and class analysis — for INGESTED MODS only (requires dbId or modId of an ingested mod). For vanilla Minecraft classes use mc_source instead. action=search_class|class_members|bytecode|find_refs|cross_refs|inheritance|diff|diff_detailed|cache_diff|find_implementors|scan_registrations|annotated_by|event_listeners|optional_integrations|network_payloads|config_schema. Most actions require dbId or modId. cross_refs/find_implementors/annotated_by/event_listeners can omit modId to search all mods. diff/diff_detailed/cache_diff require dbIdA+dbIdB. diff_detailed gives AST-level method/field changes with breaking-change flags (add semantic=true for cosine similarity, requires mod index_semantic). cache_diff forces a (re)compute and writes to DB. Set env AUTO_CACHE_MOD_DIFFS=1 to auto-cache all diff_detailed calls.", {
|
|
314
|
+
action: z.enum(["search_class", "class_members", "bytecode", "find_refs", "cross_refs", "inheritance", "diff", "diff_detailed", "cache_diff", "find_implementors", "scan_registrations", "annotated_by", "event_listeners", "optional_integrations", "network_payloads", "config_schema"]),
|
|
315
|
+
dbId: z.number().optional().describe("DB id"),
|
|
316
|
+
dbIdA: z.number().optional().describe("older DB id"),
|
|
317
|
+
dbIdB: z.number().optional().describe("newer DB id"),
|
|
318
|
+
query: z.string().optional(),
|
|
319
|
+
className: z.string().optional(),
|
|
320
|
+
target: z.string().optional().describe("slash/dot class/method/field, e.g. 'Cls:meth:(I)V'"),
|
|
321
|
+
annotation: z.string().optional().describe("slash/dot annotation class"),
|
|
322
|
+
event: z.string().optional().describe("slash/dot event class"),
|
|
323
|
+
modId: z.union([z.string(), z.number()]).optional().describe("Mod ID string (e.g. 'polylib') or numeric DB id. Resolved via DB lookup."),
|
|
324
|
+
transitive: z.boolean().optional(),
|
|
325
|
+
mcVersion: z.string().optional(),
|
|
326
|
+
loader: z.string().optional(),
|
|
327
|
+
limit: z.number().optional(),
|
|
328
|
+
packages: z.array(z.string()).optional().describe("Slash-prefix filter for diff_detailed / cache_diff"),
|
|
329
|
+
semantic: z.boolean().optional().describe("Enrich diff_detailed with Ollama cosine similarity"),
|
|
330
|
+
cache: z.boolean().optional().describe("Read from / write to DB cache for diff_detailed"),
|
|
331
|
+
force: z.boolean().optional().describe("Recompute and overwrite DB cache (implies cache=true) for diff_detailed / cache_diff"),
|
|
332
|
+
}, safe(async ({ action, dbId: rawDbId, dbIdA, dbIdB, query, className, target, annotation, event, modId, transitive, mcVersion, loader, limit, packages, semantic, cache, force }) => {
|
|
333
|
+
const dbId = await resolveDbIdAsync(rawDbId, modId);
|
|
334
|
+
let result;
|
|
335
|
+
const autoCache = !!(process.env.AUTO_CACHE_MOD_DIFFS);
|
|
336
|
+
switch (action) {
|
|
337
|
+
case "search_class":
|
|
338
|
+
result = await searchModClass(dbId, query);
|
|
339
|
+
break;
|
|
340
|
+
case "class_members":
|
|
341
|
+
result = await getModClassMembers(dbId, className);
|
|
342
|
+
break;
|
|
343
|
+
case "bytecode":
|
|
344
|
+
result = await getModClassBytecode(dbId, className);
|
|
345
|
+
break;
|
|
346
|
+
case "find_refs":
|
|
347
|
+
result = await findModReferences(dbId, target);
|
|
348
|
+
break;
|
|
349
|
+
case "cross_refs":
|
|
350
|
+
result = await crossModRefs(target, mcVersion, loader, limit);
|
|
351
|
+
break;
|
|
352
|
+
case "inheritance":
|
|
353
|
+
result = await getModInheritance(dbId, className);
|
|
354
|
+
break;
|
|
355
|
+
case "diff":
|
|
356
|
+
result = await diffModVersions(dbIdA, dbIdB);
|
|
357
|
+
break;
|
|
358
|
+
case "diff_detailed":
|
|
359
|
+
result = await diffModVersionsDetailed(dbIdA, dbIdB, packages, limit ?? 200, semantic ?? false, (cache ?? autoCache), force ?? false);
|
|
360
|
+
break;
|
|
361
|
+
case "cache_diff":
|
|
362
|
+
result = await diffModVersionsDetailed(dbIdA, dbIdB, packages, limit ?? 200, semantic ?? false, true, force ?? true);
|
|
363
|
+
break;
|
|
364
|
+
case "find_implementors":
|
|
365
|
+
result = await findImplementors(target, modId, limit, transitive);
|
|
366
|
+
break;
|
|
367
|
+
case "scan_registrations":
|
|
368
|
+
result = await scanModRegistrations(dbId);
|
|
369
|
+
break;
|
|
370
|
+
case "annotated_by":
|
|
371
|
+
result = await findAnnotatedClasses(annotation, modId, limit);
|
|
372
|
+
break;
|
|
373
|
+
case "event_listeners":
|
|
374
|
+
result = await findEventListeners(event, modId, limit);
|
|
375
|
+
break;
|
|
376
|
+
case "optional_integrations":
|
|
377
|
+
result = await findOptionalIntegrations(dbId);
|
|
378
|
+
break;
|
|
379
|
+
case "network_payloads":
|
|
380
|
+
result = await findNetworkPayloads(dbId);
|
|
381
|
+
break;
|
|
382
|
+
case "config_schema":
|
|
383
|
+
result = await extractConfigSchema(dbId);
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
return out(result);
|
|
387
|
+
}));
|
|
388
|
+
// ── 3. mod_mixins ─────────────────────────────────────────────────────────────
|
|
389
|
+
server.tool("mod_mixins", "Mod mixin and access transformer analysis. action=targets|resolve|conflicts|targets_in_package|at_conflicts|at_entries|aw_entries.", {
|
|
390
|
+
action: z.enum(["targets", "resolve", "conflicts", "targets_in_package", "at_conflicts", "at_entries", "aw_entries"]),
|
|
391
|
+
modId: z.union([z.string(), z.number()]).optional().describe("mod ID or DB id"),
|
|
392
|
+
dbId: z.number().optional().describe("DB id"),
|
|
393
|
+
targetClass: z.string().optional().describe("slash/dot class name"),
|
|
394
|
+
packagePrefix: z.string().optional().describe("slash/dot package prefix"),
|
|
395
|
+
mcVersion: z.string().optional(),
|
|
396
|
+
loader: z.string().optional(),
|
|
397
|
+
}, safe(async ({ action, modId, dbId: rawDbId, targetClass, packagePrefix, mcVersion, loader }) => {
|
|
398
|
+
const dbId = await resolveDbIdAsync(rawDbId, modId);
|
|
399
|
+
let result;
|
|
400
|
+
switch (action) {
|
|
401
|
+
case "targets":
|
|
402
|
+
result = await getMixinTargets(modId);
|
|
403
|
+
break;
|
|
404
|
+
case "resolve":
|
|
405
|
+
result = await resolveMixinTargets(dbId);
|
|
406
|
+
break;
|
|
407
|
+
case "conflicts":
|
|
408
|
+
result = await getMixinConflicts(targetClass);
|
|
409
|
+
break;
|
|
410
|
+
case "targets_in_package":
|
|
411
|
+
result = await getMixinsTargetingPackage(packagePrefix, mcVersion);
|
|
412
|
+
break;
|
|
413
|
+
case "at_conflicts":
|
|
414
|
+
result = await findAtAwConflicts(mcVersion, loader);
|
|
415
|
+
break;
|
|
416
|
+
case "at_entries":
|
|
417
|
+
result = await getAtEntries(dbId);
|
|
418
|
+
break;
|
|
419
|
+
case "aw_entries":
|
|
420
|
+
result = await getAwEntries(dbId);
|
|
421
|
+
break;
|
|
422
|
+
}
|
|
423
|
+
return out(result);
|
|
424
|
+
}));
|
|
425
|
+
// ── 4. platform ───────────────────────────────────────────────────────────────
|
|
426
|
+
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. " +
|
|
427
|
+
"action=search: unified mod search across both Modrinth and CurseForge — deduplicated, sorted by downloads (query required, loader, mcVersion, limit). " +
|
|
428
|
+
"action=batch_check_updates: check ALL linked mods for newer versions on Modrinth/CurseForge in one pass (mcVersion, loader, modIdFilter, limit). " +
|
|
429
|
+
"download_source downloads GitHub source without decompilation — auto-queues graph build (override with autoGraph). Env: MODLENS_AUTO_GRAPH (default: 1).", {
|
|
430
|
+
action: z.enum(["sync_modrinth", "sync_curseforge", "check_updates", "batch_sync", "download_source", "search", "batch_check_updates"]),
|
|
431
|
+
dbId: z.number().optional().describe("DB id"),
|
|
432
|
+
query: z.string().optional().describe("Search query (required for action=search)"),
|
|
433
|
+
syncModrinth: z.boolean().optional(),
|
|
434
|
+
syncCurseforge: z.boolean().optional(),
|
|
435
|
+
downloadSources: z.boolean().optional(),
|
|
436
|
+
modIdFilter: z.string().optional(),
|
|
437
|
+
limit: z.number().optional(),
|
|
438
|
+
loader: z.string().optional().describe("Loader filter for search (e.g. neoforge, fabric)"),
|
|
439
|
+
mcVersion: z.string().optional().describe("MC version filter for search"),
|
|
440
|
+
autoGraph: z.boolean().optional().describe("Override auto-graph-build on source download (default: env MODLENS_AUTO_GRAPH, true)"),
|
|
441
|
+
}, safe(async ({ action, dbId: rawDbId, query, syncModrinth: sm, syncCurseforge: sc, downloadSources, modIdFilter, limit, loader, mcVersion, autoGraph }) => {
|
|
442
|
+
const dbId = await resolveDbIdAsync(rawDbId, undefined);
|
|
443
|
+
let result;
|
|
444
|
+
switch (action) {
|
|
445
|
+
case "sync_modrinth":
|
|
446
|
+
result = await syncModrinth(dbId);
|
|
447
|
+
break;
|
|
448
|
+
case "sync_curseforge":
|
|
449
|
+
result = await syncCurseforge(dbId);
|
|
450
|
+
break;
|
|
451
|
+
case "check_updates":
|
|
452
|
+
result = await checkUpdates(dbId);
|
|
453
|
+
break;
|
|
454
|
+
case "batch_sync":
|
|
455
|
+
result = await batchSyncSources({ syncModrinth: sm, syncCurseforge: sc, downloadSources, modIdFilter, limit });
|
|
456
|
+
break;
|
|
457
|
+
case "download_source": {
|
|
458
|
+
const dir = await downloadSource(dbId, { autoGraph });
|
|
459
|
+
result = `Source downloaded to: ${dir}`;
|
|
460
|
+
break;
|
|
461
|
+
}
|
|
462
|
+
case "search":
|
|
463
|
+
result = await searchPlatforms(query, { loader, mcVersion, limit });
|
|
464
|
+
break;
|
|
465
|
+
case "batch_check_updates":
|
|
466
|
+
result = await batchCheckUpdates({ mcVersion, loader, modIdFilter, limit });
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
return out(result);
|
|
470
|
+
}));
|
|
471
|
+
// ── 5. modpacks_ch ───────────────────────────────────────────────────────────
|
|
472
|
+
server.tool("modpacks_ch", "Search and sync modpacks from the modpacks.ch API (a CreeperHost service) — covers FTB and CurseForge namespaces, no API key required. " +
|
|
473
|
+
"action=search|featured|info|manifest|sync_pack_mods|search_ftb_mods|ftb_mod_info|download_mod|download_overrides|list_pack_versions|list_pack_files|find_mod_in_packs. " +
|
|
474
|
+
"namespace=ftb|curseforge (default: ftb). " +
|
|
475
|
+
"User-Agent is set per modpacks.ch (CreeperHost) team request for usage tracking.", {
|
|
476
|
+
action: z.enum(["search", "featured", "info", "manifest", "sync_pack_mods", "search_ftb_mods", "ftb_mod_info", "download_mod", "download_overrides", "list_pack_versions", "list_pack_files", "find_mod_in_packs"]),
|
|
477
|
+
namespace: z.enum(["ftb", "curseforge"]).optional().describe("ftb or curseforge (default: ftb)"),
|
|
478
|
+
packId: z.number().optional().describe("Pack ID (required for info, manifest, sync_pack_mods, list_pack_files)"),
|
|
479
|
+
versionId: z.number().optional().describe("Version ID (required for manifest, sync_pack_mods, list_pack_files)"),
|
|
480
|
+
packVersionDbId: z.number().optional().describe("Pack version DB id returned by sync_pack_mods (shortcut for list_pack_files)"),
|
|
481
|
+
query: z.string().optional().describe("Search query (required for search, search_ftb_mods)"),
|
|
482
|
+
modId: z.union([z.number(), z.string()]).optional().describe("FTB mod ID (required for ftb_mod_info, download_mod)"),
|
|
483
|
+
fileId: z.number().optional().describe("Exact CF file ID to download (download_mod; skips mcVersion/loader filter)"),
|
|
484
|
+
loader: z.string().optional().describe("Loader filter for ftb_mod_info/download_mod (e.g. neoforge, fabric)"),
|
|
485
|
+
mcVersionFilter: z.string().optional().describe("MC version filter for ftb_mod_info/download_mod (e.g. 26.1.2)"),
|
|
486
|
+
force: z.boolean().optional().describe("Force re-download even if cached (download_mod)"),
|
|
487
|
+
modDbId: z.number().optional().describe("ModLens DB mod id (for find_mod_in_packs)"),
|
|
488
|
+
cfProject: z.number().optional().describe("CurseForge project id (for find_mod_in_packs)"),
|
|
489
|
+
fileType: z.string().optional().describe("Filter list_pack_files by file type (e.g. mod, resource, config)"),
|
|
490
|
+
limit: z.number().optional().describe("Max results for search/featured (default: 20)"),
|
|
491
|
+
fileTypes: z.array(z.string()).optional().describe("File types to ingest (default: ['mod','resource'])"),
|
|
492
|
+
skipServer: z.boolean().optional().describe("Skip server-only files (default: false)"),
|
|
493
|
+
skipOptional: z.boolean().optional().describe("Skip optional files (default: false)"),
|
|
494
|
+
concurrency: z.number().optional().describe("Parallel downloads for sync_pack_mods (default: 3)"),
|
|
495
|
+
}, safe(async ({ action, namespace, packId, versionId, packVersionDbId, query, modId, fileId, loader, mcVersionFilter, force, modDbId, cfProject, fileType, limit, fileTypes, skipServer, skipOptional, concurrency }) => {
|
|
496
|
+
const ns = namespace ?? "ftb";
|
|
497
|
+
let result;
|
|
498
|
+
switch (action) {
|
|
499
|
+
case "search":
|
|
500
|
+
if (!query)
|
|
501
|
+
throw new Error("query is required for action=search");
|
|
502
|
+
result = await searchPacksAction(query, ns, limit ?? 20);
|
|
503
|
+
break;
|
|
504
|
+
case "featured":
|
|
505
|
+
result = await featuredPacksAction(limit ?? 20);
|
|
506
|
+
break;
|
|
507
|
+
case "info":
|
|
508
|
+
if (!packId)
|
|
509
|
+
throw new Error("packId is required for action=info");
|
|
510
|
+
result = await packInfoAction(packId, ns);
|
|
511
|
+
break;
|
|
512
|
+
case "manifest":
|
|
513
|
+
if (!packId)
|
|
514
|
+
throw new Error("packId is required for action=manifest");
|
|
515
|
+
if (!versionId)
|
|
516
|
+
throw new Error("versionId is required for action=manifest");
|
|
517
|
+
result = await packManifestAction(packId, versionId, ns);
|
|
518
|
+
break;
|
|
519
|
+
case "sync_pack_mods":
|
|
520
|
+
if (!packId)
|
|
521
|
+
throw new Error("packId is required for action=sync_pack_mods");
|
|
522
|
+
if (!versionId)
|
|
523
|
+
throw new Error("versionId is required for action=sync_pack_mods");
|
|
524
|
+
result = await syncPackModsAction({ packId, versionId, namespace: ns, fileTypes, skipServer, skipOptional, concurrency });
|
|
525
|
+
break;
|
|
526
|
+
case "search_ftb_mods":
|
|
527
|
+
if (!query)
|
|
528
|
+
throw new Error("query is required for action=search_ftb_mods");
|
|
529
|
+
result = await searchFtbModsAction(query, limit ?? 20);
|
|
530
|
+
break;
|
|
531
|
+
case "ftb_mod_info":
|
|
532
|
+
if (modId === undefined)
|
|
533
|
+
throw new Error("modId is required for action=ftb_mod_info");
|
|
534
|
+
result = await ftbModInfoAction(modId, { mcVersion: mcVersionFilter, loader });
|
|
535
|
+
break;
|
|
536
|
+
case "download_mod":
|
|
537
|
+
if (modId === undefined)
|
|
538
|
+
throw new Error("modId is required for action=download_mod");
|
|
539
|
+
result = await downloadModAction(modId, { mcVersion: mcVersionFilter, loader, fileId, force });
|
|
540
|
+
break;
|
|
541
|
+
case "list_pack_versions":
|
|
542
|
+
result = await listPackVersionsAction(namespace, packId);
|
|
543
|
+
break;
|
|
544
|
+
case "list_pack_files":
|
|
545
|
+
result = await listPackFilesAction({ packVersionDbId, namespace, packId, versionId, fileType });
|
|
546
|
+
break;
|
|
547
|
+
case "download_overrides":
|
|
548
|
+
if (!packId)
|
|
549
|
+
throw new Error("packId is required for action=download_overrides");
|
|
550
|
+
if (!versionId)
|
|
551
|
+
throw new Error("versionId is required for action=download_overrides");
|
|
552
|
+
result = await downloadOverridesAction({ namespace: ns, packId, versionId, force });
|
|
553
|
+
break;
|
|
554
|
+
case "find_mod_in_packs":
|
|
555
|
+
if (modDbId === undefined && cfProject === undefined)
|
|
556
|
+
throw new Error("Provide modDbId or cfProject for action=find_mod_in_packs");
|
|
557
|
+
result = await findModInPacksAction({ modDbId, cfProject });
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
return out(result);
|
|
561
|
+
}));
|
|
562
|
+
// ── 6. mc_versions ────────────────────────────────────────────────────────────
|
|
563
|
+
server.tool("mc_versions", "Minecraft and mod loader version management. action=list_mc|list_neoforge|list_forge|list_fabric|ingest_neoforge|ingest_forge|ingest_fabric.", {
|
|
564
|
+
action: z.enum(["list_mc", "list_neoforge", "list_forge", "list_fabric", "ingest_neoforge", "ingest_forge", "ingest_fabric"]),
|
|
565
|
+
type: z.enum(["release", "snapshot", "all"]).optional(),
|
|
566
|
+
mcVersion: z.string().optional(),
|
|
567
|
+
version: z.string().optional(),
|
|
568
|
+
skipIndex: z.boolean().optional(),
|
|
569
|
+
limit: z.number().optional(),
|
|
570
|
+
}, safe(async ({ action, type, mcVersion, version, skipIndex, limit }) => {
|
|
571
|
+
let result;
|
|
572
|
+
switch (action) {
|
|
573
|
+
case "list_mc":
|
|
574
|
+
result = await listMcVersions(type ?? "release");
|
|
575
|
+
break;
|
|
576
|
+
case "list_neoforge":
|
|
577
|
+
result = await listNeoForgeVersions(mcVersion, limit ?? 20);
|
|
578
|
+
break;
|
|
579
|
+
case "list_forge":
|
|
580
|
+
result = await listForgeVersions(mcVersion, limit ?? 20);
|
|
581
|
+
break;
|
|
582
|
+
case "list_fabric":
|
|
583
|
+
result = await listFabricApiVersions(mcVersion, limit ?? 20);
|
|
584
|
+
break;
|
|
585
|
+
case "ingest_neoforge": {
|
|
586
|
+
const jarPath = await downloadNeoForge(version);
|
|
587
|
+
const r = await ingestMod(jarPath, true);
|
|
588
|
+
if (r.status === "already_ingested") {
|
|
589
|
+
result = `Already ingested. DB id: ${r.mod.id}`;
|
|
590
|
+
break;
|
|
591
|
+
}
|
|
592
|
+
if (!skipIndex)
|
|
593
|
+
await reindexClasses(r.mod.id);
|
|
594
|
+
result = { ...r, jarPath };
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
case "ingest_fabric": {
|
|
598
|
+
const jarPath = await downloadFabricApi(version);
|
|
599
|
+
const r = await ingestMod(jarPath, true);
|
|
600
|
+
if (r.status === "already_ingested") {
|
|
601
|
+
result = `Already ingested. DB id: ${r.mod.id}`;
|
|
602
|
+
break;
|
|
603
|
+
}
|
|
604
|
+
if (!skipIndex)
|
|
605
|
+
await reindexClasses(r.mod.id);
|
|
606
|
+
result = { ...r, jarPath };
|
|
607
|
+
break;
|
|
608
|
+
}
|
|
609
|
+
case "ingest_forge": {
|
|
610
|
+
const jarPath = await downloadForge(version, mcVersion);
|
|
611
|
+
const r = await ingestMod(jarPath, true);
|
|
612
|
+
if (r.status === "already_ingested") {
|
|
613
|
+
result = `Already ingested. DB id: ${r.mod.id}`;
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
if (!skipIndex)
|
|
617
|
+
await reindexClasses(r.mod.id);
|
|
618
|
+
result = { ...r, jarPath };
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return out(result);
|
|
623
|
+
}));
|
|
624
|
+
// ── 6. mc_source ─────────────────────────────────────────────────────────────
|
|
625
|
+
server.tool("mc_source", "Vanilla Minecraft source code, decompilation, and validation — use this for vanilla MC classes (net/minecraft/...), NOT mod_bytecode. Requires version param. action=search_class|get_source|bytecode|class_members|find_refs|inheritance|diff|diff_detailed|decompile|decompile_status|search_code|index|search_indexed|search_events|validate_aw|analyze_mixin|index_semantic|search_semantic|get_paths. get_paths returns the on-disk jar, decompiled source directory, and index paths so the agent can grep/search files natively. diff_detailed gives AST-level method/field changes with breaking-change flags per class; add semantic=true for cosine similarity scoring (requires Ollama + index_semantic run first). index_semantic/search_semantic require Ollama running.", {
|
|
626
|
+
action: z.enum(["search_class", "get_source", "bytecode", "class_members", "find_refs", "inheritance", "diff", "diff_detailed", "decompile", "decompile_status", "search_code", "index", "search_indexed", "search_events", "validate_aw", "analyze_mixin", "index_semantic", "search_semantic", "get_paths"]),
|
|
627
|
+
version: z.string().optional(),
|
|
628
|
+
versionA: z.string().optional(),
|
|
629
|
+
versionB: z.string().optional(),
|
|
630
|
+
mcVersion: z.string().optional(),
|
|
631
|
+
query: z.string().optional(),
|
|
632
|
+
className: z.string().optional(),
|
|
633
|
+
target: z.string().optional().describe("slash/dot class/method/field, e.g. 'Cls:meth:(I)V'"),
|
|
634
|
+
searchType: z.enum(["class", "method", "field", "content", "all"]).optional(),
|
|
635
|
+
isRegex: z.boolean().optional(),
|
|
636
|
+
force: z.boolean().optional(),
|
|
637
|
+
packages: z.array(z.string()).optional().describe("Slash-prefix filter for diff_detailed, e.g. ['net/minecraft/world/entity']"),
|
|
638
|
+
semantic: z.boolean().optional().describe("Enrich diff_detailed with Ollama cosine similarity (requires Ollama + index_semantic)"),
|
|
639
|
+
cache: z.boolean().optional().describe("Read/write DB cache for diff_detailed (default true; set false to skip cache)"),
|
|
640
|
+
modloader: z.enum(["minecraft", "neoforge", "forge", "fabric", "fabric-api"]).optional(),
|
|
641
|
+
content: z.string().optional().describe("Full .accesswidener file text (validate_aw)"),
|
|
642
|
+
source: z.string().optional().describe("Full mixin Java source code (analyze_mixin)"),
|
|
643
|
+
startLine: z.number().optional().describe("1-based start line"),
|
|
644
|
+
endLine: z.number().optional().describe("1-based end line"),
|
|
645
|
+
maxLines: z.number().optional(),
|
|
646
|
+
limit: z.number().optional(),
|
|
647
|
+
provenance: z.enum(["local", "registry", "community"]).optional().describe("Filter search_semantic results by embedding source (default: all sources)"),
|
|
648
|
+
}, safe(async ({ action, version, versionA, versionB, mcVersion, query, className, target, searchType, isRegex, force, packages, semantic, cache, modloader, content, source, startLine, endLine, maxLines, limit, provenance }) => {
|
|
649
|
+
const v = version ?? mcVersion;
|
|
650
|
+
let result;
|
|
651
|
+
switch (action) {
|
|
652
|
+
case "search_class":
|
|
653
|
+
result = await searchMinecraftClass(v, query);
|
|
654
|
+
break;
|
|
655
|
+
case "get_source":
|
|
656
|
+
result = await getMinecraftSource(v, className, startLine, endLine, maxLines);
|
|
657
|
+
break;
|
|
658
|
+
case "bytecode":
|
|
659
|
+
result = await getMcClassBytecode(v, className);
|
|
660
|
+
break;
|
|
661
|
+
case "class_members":
|
|
662
|
+
result = await getMcClassMembers(v, className);
|
|
663
|
+
break;
|
|
664
|
+
case "find_refs":
|
|
665
|
+
result = await findMcReferences(v, target);
|
|
666
|
+
break;
|
|
667
|
+
case "inheritance":
|
|
668
|
+
result = await getMcInheritance(v, className);
|
|
669
|
+
break;
|
|
670
|
+
case "diff":
|
|
671
|
+
result = await diffMcVersions(versionA, versionB);
|
|
672
|
+
break;
|
|
673
|
+
case "diff_detailed":
|
|
674
|
+
result = await diffMcVersionsDetailed(versionA, versionB, packages, limit ?? 200, force ?? false, semantic ?? false, cache ?? true);
|
|
675
|
+
break;
|
|
676
|
+
case "decompile":
|
|
677
|
+
result = await decompileMcVersion(v, force ?? false);
|
|
678
|
+
break;
|
|
679
|
+
case "decompile_status":
|
|
680
|
+
result = await decompileMcVersionStatus(v);
|
|
681
|
+
break;
|
|
682
|
+
case "search_code":
|
|
683
|
+
result = await searchMcCode(v, query, searchType ?? "content", isRegex ?? false, limit ?? 50);
|
|
684
|
+
break;
|
|
685
|
+
case "index":
|
|
686
|
+
result = await indexMcVersion(v, force ?? false);
|
|
687
|
+
break;
|
|
688
|
+
case "search_indexed":
|
|
689
|
+
result = await searchMcIndexed(query, v, limit ?? 20);
|
|
690
|
+
break;
|
|
691
|
+
case "search_events":
|
|
692
|
+
result = await searchEvents(v, query, modloader);
|
|
693
|
+
break;
|
|
694
|
+
case "validate_aw":
|
|
695
|
+
result = await validateAccessWidener(content, v);
|
|
696
|
+
break;
|
|
697
|
+
case "analyze_mixin":
|
|
698
|
+
result = await analyzeMixin(source, v);
|
|
699
|
+
break;
|
|
700
|
+
case "index_semantic":
|
|
701
|
+
result = await indexMcSourceSemantic(v, limit ?? 50);
|
|
702
|
+
break;
|
|
703
|
+
case "search_semantic":
|
|
704
|
+
result = await searchMcSourceSemantic(query, v, limit ?? 10, provenance);
|
|
705
|
+
break;
|
|
706
|
+
case "get_paths": {
|
|
707
|
+
const ver = v;
|
|
708
|
+
result = {
|
|
709
|
+
version: ver,
|
|
710
|
+
jarPath: mcPaths.jar(ver),
|
|
711
|
+
decompPath: mcPaths.decompiled(ver),
|
|
712
|
+
indexPath: mcPaths.index(ver),
|
|
713
|
+
cacheRoot: CACHE_ROOT,
|
|
714
|
+
};
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return out(result);
|
|
719
|
+
}));
|
|
720
|
+
// ── 7. mappings ───────────────────────────────────────────────────────────────
|
|
721
|
+
server.tool("mappings", "Minecraft name mappings (official/intermediary/yarn/mojmap) and Parchment parameter docs. action=find|remap|parchment|list_parchment|parchment_summary.", {
|
|
722
|
+
action: z.enum(["find", "remap", "parchment", "list_parchment", "parchment_summary"]),
|
|
723
|
+
symbol: z.string().optional().describe("symbol to translate"),
|
|
724
|
+
version: z.string().optional(),
|
|
725
|
+
sourceNs: z.enum(["official", "intermediary", "yarn", "mojmap", "srg", "mcp"]).optional().describe("Allowed: official, intermediary, yarn, mojmap, srg, mcp"),
|
|
726
|
+
targetNs: z.enum(["official", "intermediary", "yarn", "mojmap", "srg", "mcp"]).optional().describe("Allowed: official, intermediary, yarn, mojmap, srg, mcp"),
|
|
727
|
+
inputJar: z.string().optional(),
|
|
728
|
+
outputJar: z.string().optional(),
|
|
729
|
+
toMapping: z.enum(["yarn", "mojmap"]).optional(),
|
|
730
|
+
className: z.string().optional(),
|
|
731
|
+
mcVersion: z.string().optional(),
|
|
732
|
+
}, safe(async ({ action, symbol, version, sourceNs, targetNs, inputJar, outputJar, toMapping, className, mcVersion }) => {
|
|
733
|
+
const v = version ?? mcVersion;
|
|
734
|
+
const mv = mcVersion ?? version;
|
|
735
|
+
let result;
|
|
736
|
+
switch (action) {
|
|
737
|
+
case "find":
|
|
738
|
+
result = await findMapping(symbol, v, sourceNs, targetNs);
|
|
739
|
+
break;
|
|
740
|
+
case "remap":
|
|
741
|
+
result = await remapModJar(inputJar, outputJar, v, toMapping);
|
|
742
|
+
break;
|
|
743
|
+
case "parchment":
|
|
744
|
+
result = await getParchment(className, mv);
|
|
745
|
+
break;
|
|
746
|
+
case "list_parchment":
|
|
747
|
+
result = await listParchmentVersions(mv);
|
|
748
|
+
break;
|
|
749
|
+
case "parchment_summary":
|
|
750
|
+
result = await getParchmentSummary(mv);
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
return out(result);
|
|
754
|
+
}));
|
|
755
|
+
// ── 8. docs ───────────────────────────────────────────────────────────────────
|
|
756
|
+
server.tool("docs", "Minecraft modding documentation database. action=ingest|seed|get|search|list|delete|semantic_search|backfill_embeddings. semantic_search requires Ollama running.", {
|
|
757
|
+
action: z.enum(["ingest", "seed", "get", "search", "list", "delete", "semantic_search", "backfill_embeddings"]),
|
|
758
|
+
entries: z.array(z.object({
|
|
759
|
+
className: z.string().optional(),
|
|
760
|
+
title: z.string(),
|
|
761
|
+
summary: z.string().optional(),
|
|
762
|
+
url: z.string(),
|
|
763
|
+
category: z.enum(["minecraft", "neoforge", "fabric", "forge", "quilt", "mod", "other"]).optional().default("minecraft"),
|
|
764
|
+
namespace: z.string().optional().default("vanilla"),
|
|
765
|
+
tags: z.array(z.string()).optional().default([]),
|
|
766
|
+
source: z.string().optional().default("manual"),
|
|
767
|
+
})).optional().describe("Doc entries to add/update (ingest)"),
|
|
768
|
+
query: z.string().optional(),
|
|
769
|
+
category: z.string().optional().describe("minecraft|neoforge|fabric|forge|quilt|mod|other"),
|
|
770
|
+
namespace: z.string().optional(),
|
|
771
|
+
tag: z.string().optional(),
|
|
772
|
+
id: z.number().optional(),
|
|
773
|
+
limit: z.number().optional(),
|
|
774
|
+
}, safe(async ({ action, entries, query, category, namespace, tag, id, limit }) => {
|
|
775
|
+
let result;
|
|
776
|
+
switch (action) {
|
|
777
|
+
case "ingest":
|
|
778
|
+
result = await ingestDocumentation(entries);
|
|
779
|
+
break;
|
|
780
|
+
case "seed":
|
|
781
|
+
result = await seedDefaultDocumentation();
|
|
782
|
+
break;
|
|
783
|
+
case "get":
|
|
784
|
+
result = await getDocumentation(query);
|
|
785
|
+
break;
|
|
786
|
+
case "search":
|
|
787
|
+
result = await searchDocumentation(query, category, namespace);
|
|
788
|
+
break;
|
|
789
|
+
case "list":
|
|
790
|
+
result = await listDocumentation(category, namespace, tag, limit ?? 100);
|
|
791
|
+
break;
|
|
792
|
+
case "delete":
|
|
793
|
+
result = await deleteDocumentation(id);
|
|
794
|
+
break;
|
|
795
|
+
case "semantic_search":
|
|
796
|
+
result = await semanticSearchDocumentation(query, limit ?? 10);
|
|
797
|
+
break;
|
|
798
|
+
case "backfill_embeddings":
|
|
799
|
+
result = await backfillDocEmbeddings();
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
return out(result);
|
|
803
|
+
}));
|
|
804
|
+
// ── 9. primers ────────────────────────────────────────────────────────────────
|
|
805
|
+
server.tool("primers", "Minecraft version migration primers and porting guides. action=ingest|seed|get|by_version|search|list|delete|semantic_search|backfill_embeddings. semantic_search requires Ollama running.", {
|
|
806
|
+
action: z.enum(["ingest", "seed", "get", "by_version", "search", "list", "delete", "semantic_search", "backfill_embeddings"]),
|
|
807
|
+
entries: z.array(z.object({
|
|
808
|
+
fromVersion: z.string(),
|
|
809
|
+
toVersion: z.string(),
|
|
810
|
+
modloader: z.string().optional(),
|
|
811
|
+
title: z.string(),
|
|
812
|
+
summary: z.string().optional(),
|
|
813
|
+
url: z.string(),
|
|
814
|
+
content: z.string().optional(),
|
|
815
|
+
tags: z.array(z.string()).optional(),
|
|
816
|
+
source: z.string().optional(),
|
|
817
|
+
fetchContent: z.boolean().optional(),
|
|
818
|
+
})).optional().describe("Primer entries to ingest (ingest)"),
|
|
819
|
+
id: z.number().optional(),
|
|
820
|
+
fromVersion: z.string().optional(),
|
|
821
|
+
toVersion: z.string().optional(),
|
|
822
|
+
modloader: z.string().optional().describe("neoforge|forge|fabric|quilt"),
|
|
823
|
+
query: z.string().optional(),
|
|
824
|
+
limit: z.number().optional(),
|
|
825
|
+
}, safe(async ({ action, entries, id, fromVersion, toVersion, modloader, query, limit }) => {
|
|
826
|
+
let result;
|
|
827
|
+
switch (action) {
|
|
828
|
+
case "ingest":
|
|
829
|
+
result = await ingestPrimer(entries);
|
|
830
|
+
break;
|
|
831
|
+
case "seed":
|
|
832
|
+
result = await seedDefaultPrimers();
|
|
833
|
+
break;
|
|
834
|
+
case "get":
|
|
835
|
+
result = await getPrimer(id);
|
|
836
|
+
break;
|
|
837
|
+
case "by_version":
|
|
838
|
+
result = await getPrimersByVersionRange(fromVersion, toVersion, modloader);
|
|
839
|
+
break;
|
|
840
|
+
case "search":
|
|
841
|
+
result = await searchPrimers(query, modloader, fromVersion, toVersion, limit);
|
|
842
|
+
break;
|
|
843
|
+
case "list":
|
|
844
|
+
result = await listPrimers(modloader, limit);
|
|
845
|
+
break;
|
|
846
|
+
case "delete":
|
|
847
|
+
result = await deletePrimer(id);
|
|
848
|
+
break;
|
|
849
|
+
case "semantic_search":
|
|
850
|
+
result = await semanticSearchPrimers(query, limit ?? 10);
|
|
851
|
+
break;
|
|
852
|
+
case "backfill_embeddings":
|
|
853
|
+
result = await backfillPrimerEmbeddings();
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
return out(result);
|
|
857
|
+
}));
|
|
858
|
+
// ── 10. mc_registry ───────────────────────────────────────────────────────────
|
|
859
|
+
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.", {
|
|
860
|
+
action: z.enum(["blocks", "commands", "registries", "sounds", "item_components", "registry_entries", "mcmeta_versions"]),
|
|
861
|
+
version: z.string().optional(),
|
|
862
|
+
registry: z.string().optional().describe("registry key e.g. 'block','item','entity_type'"),
|
|
863
|
+
filter: z.enum(["release", "snapshot", "all"]).optional().describe("Version filter for mcmeta_versions (default all)"),
|
|
864
|
+
}, safe(async ({ action, version, registry, filter }) => {
|
|
865
|
+
let result;
|
|
866
|
+
switch (action) {
|
|
867
|
+
case "blocks":
|
|
868
|
+
result = await getMcBlocks(version);
|
|
869
|
+
break;
|
|
870
|
+
case "commands":
|
|
871
|
+
result = await getMcCommands(version);
|
|
872
|
+
break;
|
|
873
|
+
case "registries":
|
|
874
|
+
result = await getMcRegistries(version, registry);
|
|
875
|
+
break;
|
|
876
|
+
case "sounds":
|
|
877
|
+
result = await getMcSounds(version);
|
|
878
|
+
break;
|
|
879
|
+
case "item_components":
|
|
880
|
+
result = await getMcItemComponents(version);
|
|
881
|
+
break;
|
|
882
|
+
case "registry_entries":
|
|
883
|
+
result = await getRegistryEntries(registry, version);
|
|
884
|
+
break;
|
|
885
|
+
case "mcmeta_versions":
|
|
886
|
+
result = await getMcmetaVersions(filter ?? "all");
|
|
887
|
+
break;
|
|
888
|
+
}
|
|
889
|
+
return out(result);
|
|
890
|
+
}));
|
|
891
|
+
// ── 11. mc_data ───────────────────────────────────────────────────────────────
|
|
892
|
+
server.tool("mc_data", "Vanilla MC data browser — tags, recipes, loot tables, lang, blockstates, models, biomes, enchantments, advancements, structures, particles, entity attributes. action=tags|find_tags_for|recipes|get_recipe|find_recipes_for|loot_tables|get_loot_table|lang|blockstate|model|model_tree|biomes|get_biome|damage_types|enchantments|get_enchantment|advancements|get_advancement|structures|get_structure|particles|get_particle|entity_attributes.", {
|
|
893
|
+
action: z.enum([
|
|
894
|
+
"tags", "find_tags_for", "recipes", "get_recipe", "find_recipes_for",
|
|
895
|
+
"loot_tables", "get_loot_table", "lang", "blockstate", "model", "model_tree",
|
|
896
|
+
"biomes", "get_biome", "damage_types", "enchantments", "get_enchantment",
|
|
897
|
+
"advancements", "get_advancement", "structures", "get_structure",
|
|
898
|
+
"particles", "get_particle", "entity_attributes",
|
|
899
|
+
]),
|
|
900
|
+
version: z.string().optional(),
|
|
901
|
+
registry: z.string().optional().describe("tag registry e.g. block|item|entity_type"),
|
|
902
|
+
tagId: z.string().optional(),
|
|
903
|
+
namespace: z.string().optional(),
|
|
904
|
+
entry: z.string().optional().describe("Entry to find tags for (find_tags_for), e.g. 'minecraft:iron_ore'"),
|
|
905
|
+
type: z.string().optional().describe("recipe type e.g. 'crafting_shaped'"),
|
|
906
|
+
outputItem: z.string().optional(),
|
|
907
|
+
recipeId: z.string().optional().describe("recipe id"),
|
|
908
|
+
item: z.string().optional().describe("item id"),
|
|
909
|
+
category: z.string().optional().describe("loot: blocks|entities|chests|gameplay; advancements: story|nether|end|adventure"),
|
|
910
|
+
path: z.string().optional().describe("loot table path e.g. 'blocks/iron_ore'"),
|
|
911
|
+
filter: z.string().optional(),
|
|
912
|
+
block: z.string().optional().describe("block id"),
|
|
913
|
+
modelPath: z.string().optional().describe("model path e.g. 'block/stone'"),
|
|
914
|
+
resolveParents: z.boolean().optional(),
|
|
915
|
+
biomeId: z.string().optional().describe("biome id"),
|
|
916
|
+
id: z.string().optional(),
|
|
917
|
+
entity: z.string().optional().describe("entity id e.g. 'player'"),
|
|
918
|
+
modId: z.union([z.string(), z.number()]).optional().describe("mod ID or DB id"),
|
|
919
|
+
limit: z.number().optional().describe("Max results (lang)"),
|
|
920
|
+
}, safe(async ({ action, version, registry, tagId, namespace, entry, type, outputItem, recipeId, item, category, path, filter, block, modelPath, resolveParents, biomeId, id, entity, modId, limit }) => {
|
|
921
|
+
let result;
|
|
922
|
+
switch (action) {
|
|
923
|
+
case "tags":
|
|
924
|
+
result = await getMcTags(version, registry, tagId, namespace);
|
|
925
|
+
break;
|
|
926
|
+
case "find_tags_for":
|
|
927
|
+
result = await findTagsForEntry(entry, registry, version, namespace);
|
|
928
|
+
break;
|
|
929
|
+
case "recipes":
|
|
930
|
+
result = await listRecipes(version, type, outputItem);
|
|
931
|
+
break;
|
|
932
|
+
case "get_recipe":
|
|
933
|
+
result = await getRecipe(version, recipeId);
|
|
934
|
+
break;
|
|
935
|
+
case "find_recipes_for":
|
|
936
|
+
result = await findRecipesForItem(item, version);
|
|
937
|
+
break;
|
|
938
|
+
case "loot_tables":
|
|
939
|
+
result = await listLootTables(version, category);
|
|
940
|
+
break;
|
|
941
|
+
case "get_loot_table":
|
|
942
|
+
result = await getLootTable(version, path);
|
|
943
|
+
break;
|
|
944
|
+
case "lang":
|
|
945
|
+
result = await getLangEntries(version, filter, limit);
|
|
946
|
+
break;
|
|
947
|
+
case "blockstate":
|
|
948
|
+
result = await getBlockstate(version, block);
|
|
949
|
+
break;
|
|
950
|
+
case "model":
|
|
951
|
+
result = await getMcModel(version, modelPath, resolveParents);
|
|
952
|
+
break;
|
|
953
|
+
case "model_tree":
|
|
954
|
+
result = await getModelTree(modelPath, version);
|
|
955
|
+
break;
|
|
956
|
+
case "biomes":
|
|
957
|
+
result = await listBiomes(version);
|
|
958
|
+
break;
|
|
959
|
+
case "get_biome":
|
|
960
|
+
result = await getBiome(version, biomeId);
|
|
961
|
+
break;
|
|
962
|
+
case "damage_types":
|
|
963
|
+
result = await listDamageTypes(version);
|
|
964
|
+
break;
|
|
965
|
+
case "enchantments":
|
|
966
|
+
result = await listEnchantments(version);
|
|
967
|
+
break;
|
|
968
|
+
case "get_enchantment":
|
|
969
|
+
result = await getEnchantment(version, id);
|
|
970
|
+
break;
|
|
971
|
+
case "advancements":
|
|
972
|
+
result = await listAdvancements(version, category);
|
|
973
|
+
break;
|
|
974
|
+
case "get_advancement":
|
|
975
|
+
result = await getAdvancement(version, id);
|
|
976
|
+
break;
|
|
977
|
+
case "structures":
|
|
978
|
+
result = await listStructures(version);
|
|
979
|
+
break;
|
|
980
|
+
case "get_structure":
|
|
981
|
+
result = await getStructureData(id, version);
|
|
982
|
+
break;
|
|
983
|
+
case "particles":
|
|
984
|
+
result = await getMcParticles(version);
|
|
985
|
+
break;
|
|
986
|
+
case "get_particle":
|
|
987
|
+
result = await getParticleData(id, version);
|
|
988
|
+
break;
|
|
989
|
+
case "entity_attributes":
|
|
990
|
+
result = await getEntityAttributes(entity, version, modId);
|
|
991
|
+
break;
|
|
992
|
+
}
|
|
993
|
+
return out(result);
|
|
994
|
+
}));
|
|
995
|
+
// ── 12. mc_files ─────────────────────────────────────────────────────────────
|
|
996
|
+
server.tool("mc_files", "Vanilla MC file access via misode/mcmeta (data packs, assets, atlases, diffs, changelogs). action=get_data|get_asset|list_files|diff|atlas|raw|compare|changelog. list_files requires dirPath+version+branch. diff requires filePath+versionA+versionB. get_data/get_asset require filePath.", {
|
|
997
|
+
action: z.enum(["get_data", "get_asset", "list_files", "diff", "atlas", "raw", "compare", "changelog"]),
|
|
998
|
+
filePath: z.string().optional().describe("relative path in branch"),
|
|
999
|
+
version: z.string().optional(),
|
|
1000
|
+
versionA: z.string().optional(),
|
|
1001
|
+
versionB: z.string().optional(),
|
|
1002
|
+
branch: z.string().optional().describe("data|assets|registries|summary|diff|atlas"),
|
|
1003
|
+
dirPath: z.string().optional(),
|
|
1004
|
+
jsonOnly: z.boolean().optional(),
|
|
1005
|
+
atlas: z.string().optional().describe("atlas name e.g. 'blocks'"),
|
|
1006
|
+
ref: z.string().optional().describe("git ref e.g. '26.1.2-data'"),
|
|
1007
|
+
}, safe(async ({ action, filePath, version, versionA, versionB, branch, dirPath, jsonOnly, atlas, ref }) => {
|
|
1008
|
+
let result;
|
|
1009
|
+
switch (action) {
|
|
1010
|
+
case "get_data":
|
|
1011
|
+
result = await getMcDataFile(filePath, version, jsonOnly ?? false);
|
|
1012
|
+
break;
|
|
1013
|
+
case "get_asset":
|
|
1014
|
+
result = await getMcAssetFile(filePath, version, jsonOnly ?? false);
|
|
1015
|
+
break;
|
|
1016
|
+
case "list_files":
|
|
1017
|
+
result = await listMcDataFiles(dirPath, version, branch);
|
|
1018
|
+
break;
|
|
1019
|
+
case "diff":
|
|
1020
|
+
result = await diffMcData(filePath, versionA, versionB, branch ?? "data");
|
|
1021
|
+
break;
|
|
1022
|
+
case "atlas":
|
|
1023
|
+
result = await getMcAtlas(version, atlas);
|
|
1024
|
+
break;
|
|
1025
|
+
case "raw":
|
|
1026
|
+
result = await getMcmetaRaw(ref, filePath);
|
|
1027
|
+
break;
|
|
1028
|
+
case "compare":
|
|
1029
|
+
result = await compareVersions(versionA, versionB, branch);
|
|
1030
|
+
break;
|
|
1031
|
+
case "changelog":
|
|
1032
|
+
result = await getVersionChangelog(version, branch);
|
|
1033
|
+
break;
|
|
1034
|
+
}
|
|
1035
|
+
return out(result);
|
|
1036
|
+
}));
|
|
1037
|
+
// ── 13. mod_jar ───────────────────────────────────────────────────────────────
|
|
1038
|
+
server.tool("mod_jar", "Mod JAR file access and registry discovery (lang, sounds, atlases, configs, manifests). action=list_files|get_file|lang|sounds|atlas|registry_entries|manifest|list_configs|get_config.", {
|
|
1039
|
+
action: z.enum(["list_files", "get_file", "lang", "sounds", "atlas", "registry_entries", "manifest", "list_configs", "get_config"]),
|
|
1040
|
+
modId: z.union([z.string(), z.number()]).describe("mod ID or DB id"),
|
|
1041
|
+
prefix: z.string().optional(),
|
|
1042
|
+
path: z.string().optional().describe("internal JAR path"),
|
|
1043
|
+
filter: z.string().optional(),
|
|
1044
|
+
namespace: z.string().optional(),
|
|
1045
|
+
atlas: z.string().optional().describe("Atlas name e.g. 'blocks' (atlas)"),
|
|
1046
|
+
type: z.enum(["item", "block", "entity_type", "enchantment", "effect", "biome", "container", "sound", "potion", "banner_pattern", "painting", "attribute", "trim_material", "trim_pattern", "creative_tab", "jukebox_song", "death_message", "fluid", "all"]).optional().describe("Registry type filter (registry_entries, default all)"),
|
|
1047
|
+
limit: z.number().optional(),
|
|
1048
|
+
}, safe(async ({ action, modId, prefix, path, filter, namespace, atlas, type, limit }) => {
|
|
1049
|
+
let result;
|
|
1050
|
+
switch (action) {
|
|
1051
|
+
case "list_files":
|
|
1052
|
+
result = await listModJarFiles(modId, prefix);
|
|
1053
|
+
break;
|
|
1054
|
+
case "get_file":
|
|
1055
|
+
result = await getModJarFile(modId, path);
|
|
1056
|
+
break;
|
|
1057
|
+
case "lang":
|
|
1058
|
+
result = await getModLang(modId, filter, limit);
|
|
1059
|
+
break;
|
|
1060
|
+
case "sounds":
|
|
1061
|
+
result = await getModSounds(modId, namespace);
|
|
1062
|
+
break;
|
|
1063
|
+
case "atlas":
|
|
1064
|
+
result = await getModAtlas(modId, atlas, namespace);
|
|
1065
|
+
break;
|
|
1066
|
+
case "registry_entries":
|
|
1067
|
+
result = await listModRegistryEntries(modId, type, filter, limit);
|
|
1068
|
+
break;
|
|
1069
|
+
case "manifest":
|
|
1070
|
+
result = await getModManifest(modId);
|
|
1071
|
+
break;
|
|
1072
|
+
case "list_configs":
|
|
1073
|
+
result = await listModConfigs(modId);
|
|
1074
|
+
break;
|
|
1075
|
+
case "get_config":
|
|
1076
|
+
result = await getModConfig(modId, path);
|
|
1077
|
+
break;
|
|
1078
|
+
}
|
|
1079
|
+
return out(result);
|
|
1080
|
+
}));
|
|
1081
|
+
// ── 14. mod_data ─────────────────────────────────────────────────────────────
|
|
1082
|
+
server.tool("mod_data", "Mod JAR structured data — list/get/diff JSON (recipes, loot tables, advancements, blockstates, models, biomes, tags, etc.) or trace a crafting dependency tree. action=list|get|diff|trace_item.", {
|
|
1083
|
+
action: z.enum(["list", "get", "diff", "trace_item"]).describe("list/get: JAR data operations; diff: compare two mod versions; trace_item: recipe chain"),
|
|
1084
|
+
type: z.enum(["recipe", "loot_table", "advancement", "blockstate", "model", "biome", "structure", "data_tag", "particle", "damage_type", "enchantment", "configured_feature", "placed_feature", "structure_set", "noise", "density_function", "processor_list", "template_pool", "dimension_type", "dimension", "trim_material", "trim_pattern", "painting_variant", "wolf_variant", "cat_variant", "chat_type"]).describe("Data type to query"),
|
|
1085
|
+
modId: z.union([z.string(), z.number()]).optional().describe("mod ID or DB id"),
|
|
1086
|
+
dbIdA: z.number().optional().describe("older DB id"),
|
|
1087
|
+
dbIdB: z.number().optional().describe("newer DB id"),
|
|
1088
|
+
itemId: z.string().optional().describe("resource id e.g. 'mekanism:steel_ingot'"),
|
|
1089
|
+
maxDepth: z.number().optional(),
|
|
1090
|
+
id: z.string().optional().describe("resource id"),
|
|
1091
|
+
modelPath: z.string().optional().describe("model path"),
|
|
1092
|
+
namespace: z.string().optional(),
|
|
1093
|
+
filter: z.string().optional().describe("Substring filter on path (list, diff)"),
|
|
1094
|
+
registry: z.string().optional().describe("tag registry e.g. item|block|entity_type"),
|
|
1095
|
+
dataType: z.string().optional(),
|
|
1096
|
+
}, safe(async ({ action, type, modId, dbIdA, dbIdB, itemId, maxDepth, id, modelPath, namespace, filter, registry, dataType }) => {
|
|
1097
|
+
let result;
|
|
1098
|
+
if (action === "diff") {
|
|
1099
|
+
result = await diffModData(dbIdA, dbIdB, dataType);
|
|
1100
|
+
}
|
|
1101
|
+
else if (action === "trace_item") {
|
|
1102
|
+
result = await traceRecipeChain(itemId, maxDepth);
|
|
1103
|
+
}
|
|
1104
|
+
else if (action === "list") {
|
|
1105
|
+
switch (type) {
|
|
1106
|
+
case "data_tag":
|
|
1107
|
+
result = await listModDataTags(modId, registry, namespace, filter);
|
|
1108
|
+
break;
|
|
1109
|
+
default:
|
|
1110
|
+
result = await listModData(modId, type, { namespace, filter });
|
|
1111
|
+
break;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
else {
|
|
1115
|
+
switch (type) {
|
|
1116
|
+
case "model":
|
|
1117
|
+
result = await getModModel(modId, modelPath ?? id, namespace);
|
|
1118
|
+
break;
|
|
1119
|
+
case "data_tag":
|
|
1120
|
+
result = await getModDataTag(modId, registry, id, namespace);
|
|
1121
|
+
break;
|
|
1122
|
+
default:
|
|
1123
|
+
result = await getModData(modId, type, id, { namespace });
|
|
1124
|
+
break;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
return out(result);
|
|
1128
|
+
}));
|
|
1129
|
+
// ── 15. mod_tags ──────────────────────────────────────────────────────────────
|
|
1130
|
+
server.tool("mod_tags", "Cross-mod data-pack tag indexing — index, browse, expand, find contributors, and detect replace:true conflicts. action=index|index_all|namespaces|contributors|expand|mod_list|find_conflicts|search.", {
|
|
1131
|
+
action: z.enum(["index", "index_all", "namespaces", "contributors", "expand", "mod_list", "find_conflicts", "search"]),
|
|
1132
|
+
modId: z.union([z.string(), z.number()]).optional().describe("Mod ID string or DB id (index, mod_list)"),
|
|
1133
|
+
tagPath: z.string().optional().describe("Tag path to look up contributors or expand (contributors, expand), e.g. 'c:ores/iron', '#forge:storage_blocks'"),
|
|
1134
|
+
registry: z.string().optional().describe("block|item|entity_type|fluid|..."),
|
|
1135
|
+
query: z.string().optional(),
|
|
1136
|
+
maxDepth: z.number().optional(),
|
|
1137
|
+
limit: z.number().optional().describe("Max results (search, default 50)"),
|
|
1138
|
+
}, safe(async ({ action, modId, tagPath, registry, query, maxDepth, limit }) => {
|
|
1139
|
+
let result;
|
|
1140
|
+
switch (action) {
|
|
1141
|
+
case "index":
|
|
1142
|
+
result = await indexModTags(modId);
|
|
1143
|
+
break;
|
|
1144
|
+
case "index_all":
|
|
1145
|
+
result = await indexAllModTags();
|
|
1146
|
+
break;
|
|
1147
|
+
case "namespaces":
|
|
1148
|
+
result = await listTagNamespaces();
|
|
1149
|
+
break;
|
|
1150
|
+
case "contributors":
|
|
1151
|
+
result = await getTagContributors(tagPath, registry);
|
|
1152
|
+
break;
|
|
1153
|
+
case "expand":
|
|
1154
|
+
result = await expandTag(tagPath, registry, maxDepth);
|
|
1155
|
+
break;
|
|
1156
|
+
case "mod_list":
|
|
1157
|
+
result = await getModTagList(modId, registry);
|
|
1158
|
+
break;
|
|
1159
|
+
case "find_conflicts":
|
|
1160
|
+
result = await findTagConflicts(registry);
|
|
1161
|
+
break;
|
|
1162
|
+
case "search":
|
|
1163
|
+
result = await searchModTags(query, registry, limit);
|
|
1164
|
+
break;
|
|
1165
|
+
}
|
|
1166
|
+
return out(result);
|
|
1167
|
+
}));
|
|
1168
|
+
// ── 16. mixin_scan ────────────────────────────────────────────────────────────
|
|
1169
|
+
server.tool("mixin_scan", "Cross-mod mixin conflict analysis. action=list_mods|conflict_matrix|class_detail|hotspots|batch_resolve.", {
|
|
1170
|
+
action: z.enum(["list_mods", "conflict_matrix", "class_detail", "hotspots", "batch_resolve"]),
|
|
1171
|
+
loader: z.string().optional().describe("fabric|neoforge|forge|quilt"),
|
|
1172
|
+
mcVersion: z.string().optional(),
|
|
1173
|
+
minConflicts: z.number().optional(),
|
|
1174
|
+
targetClass: z.string().optional().describe("slash/dot class name"),
|
|
1175
|
+
top: z.number().optional(),
|
|
1176
|
+
}, safe(async ({ action, loader, mcVersion, minConflicts, targetClass, top }) => {
|
|
1177
|
+
let result;
|
|
1178
|
+
switch (action) {
|
|
1179
|
+
case "list_mods":
|
|
1180
|
+
result = await listModsWithMixins(loader, mcVersion);
|
|
1181
|
+
break;
|
|
1182
|
+
case "conflict_matrix":
|
|
1183
|
+
result = await getMixinConflictMatrix(loader, mcVersion, minConflicts);
|
|
1184
|
+
break;
|
|
1185
|
+
case "class_detail":
|
|
1186
|
+
result = await getMixinClassDetail(targetClass);
|
|
1187
|
+
break;
|
|
1188
|
+
case "hotspots":
|
|
1189
|
+
result = await getMixinHotspots(top, loader);
|
|
1190
|
+
break;
|
|
1191
|
+
case "batch_resolve":
|
|
1192
|
+
result = await batchResolveMixins(loader, mcVersion);
|
|
1193
|
+
break;
|
|
1194
|
+
}
|
|
1195
|
+
return out(result);
|
|
1196
|
+
}));
|
|
1197
|
+
// ── 17. gradle ────────────────────────────────────────────────────────────────
|
|
1198
|
+
server.tool("gradle", "Gradle build file analysis — extract deps/plugins, search across mods, compare dependency versions. action=get_files|search|compare_deps.", {
|
|
1199
|
+
action: z.enum(["get_files", "search", "compare_deps"]),
|
|
1200
|
+
modId: z.union([z.string(), z.number()]).optional().describe("Mod ID string or DB id (get_files)"),
|
|
1201
|
+
query: z.string().optional(),
|
|
1202
|
+
modIdFilter: z.string().optional().describe("modId substring filter"),
|
|
1203
|
+
groupFilter: z.string().optional().describe("group:artifact substring"),
|
|
1204
|
+
limit: z.number().optional().describe("Max results (search, default 20)"),
|
|
1205
|
+
}, safe(async ({ action, modId, query, modIdFilter, groupFilter, limit }) => {
|
|
1206
|
+
let result;
|
|
1207
|
+
switch (action) {
|
|
1208
|
+
case "get_files":
|
|
1209
|
+
result = await getModGradleFiles(modId);
|
|
1210
|
+
break;
|
|
1211
|
+
case "search":
|
|
1212
|
+
result = await searchGradleFiles(query, modIdFilter, limit);
|
|
1213
|
+
break;
|
|
1214
|
+
case "compare_deps":
|
|
1215
|
+
result = await compareGradleDeps(groupFilter, modIdFilter);
|
|
1216
|
+
break;
|
|
1217
|
+
}
|
|
1218
|
+
return out(result);
|
|
1219
|
+
}));
|
|
1220
|
+
// ── 18. reports ───────────────────────────────────────────────────────────────
|
|
1221
|
+
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.", {
|
|
1222
|
+
report: z.enum(["mixin_conflicts", "tag_conflicts", "version_conflicts", "mod_overview", "gradle_deps", "pack_compat", "dep_graph", "sidedness", "mod_complexity", "pack_changelog"]),
|
|
1223
|
+
savePath: z.string().optional().describe("Absolute path to save the .md file, e.g. 'C:/reports/mixin_conflicts.md'"),
|
|
1224
|
+
modId: z.union([z.string(), z.number()]).optional(),
|
|
1225
|
+
loader: z.string().optional(),
|
|
1226
|
+
mcVersion: z.string().optional(),
|
|
1227
|
+
registry: z.string().optional(),
|
|
1228
|
+
minConflicts: z.number().optional(),
|
|
1229
|
+
groupFilter: z.string().optional(),
|
|
1230
|
+
modIdFilter: z.string().optional(),
|
|
1231
|
+
dbIds: z.array(z.number()).optional(),
|
|
1232
|
+
oldIds: z.array(z.number()).optional(),
|
|
1233
|
+
newIds: z.array(z.number()).optional(),
|
|
1234
|
+
}, safe(async ({ report, savePath, modId, loader, mcVersion, registry, minConflicts, groupFilter, modIdFilter, dbIds, oldIds, newIds }) => {
|
|
1235
|
+
const result = await generateReport({ report, savePath, modId, loader, mcVersion, registry, minConflicts, groupFilter, modIdFilter, dbIds, oldIds, newIds });
|
|
1236
|
+
return out(result.savedTo ? `Saved to: ${result.savedTo}\n\n${result.markdown}` : result.markdown);
|
|
1237
|
+
}));
|
|
1238
|
+
// ── 20. pack_tools ────────────────────────────────────────────────────────────
|
|
1239
|
+
server.tool("pack_tools", "Modpack-specific analysis tools. " +
|
|
1240
|
+
"action=asset_conflicts: find assets/ paths shipped by 2+ mods \u2014 last-loaded mod silently wins, causing visual/audio corruption (assetType, mcVersion, loader, limit). " +
|
|
1241
|
+
"action=vanilla_overrides: find mods overriding data/minecraft/ or assets/minecraft/ \u2014 affects vanilla recipes, loot tables, textures (overrideType, dataSubtype, mcVersion, loader). " +
|
|
1242
|
+
"action=sidedness: determine whether a single mod is client_only / client_optional / common / server_only (modId). " +
|
|
1243
|
+
"action=pack_sidedness: classify ALL mods in the DB by sidedness in one pass (mcVersion, loader). " +
|
|
1244
|
+
"action=complexity: compute class/mixin/AT/AW complexity score per mod \u2014 ranked list for perf/crash triage (mcVersion, loader). " +
|
|
1245
|
+
"action=pack_changelog: diff two pack snapshots by DB id lists \u2014 added/removed/updated mods (oldIds, newIds). " +
|
|
1246
|
+
"action=data_conflicts: find data/ paths (recipes, loot_tables, advancements, etc.) shipped by 2+ mods \u2014 last-loaded mod silently wins (dataType, mcVersion, loader, limit). " +
|
|
1247
|
+
"action=pack_graph: build a composite knowledge graph of the entire modpack \u2014 nodes (mods, tags, MC classes, KubeJS scripts) and edges (depends_on, mixes_into, contributes_tag, tag_conflict, script_modifies). Includes hub detection and mixin hotspots (mcVersion, loader, scriptsDir). " +
|
|
1248
|
+
"action=config_diff: compare a modpack's on-disk config/ directory against default configs bundled in mod JARs \u2014 identifies modified, unchanged, custom, and missing configs (configDir required, mcVersion, loader). " +
|
|
1249
|
+
"action=health: one-shot modpack health overview \u2014 mod count, loader breakdown, missing deps, mixin hotspots, tag conflicts, platform link stats (mcVersion, loader).", {
|
|
1250
|
+
action: z.enum(["asset_conflicts", "vanilla_overrides", "sidedness", "pack_sidedness", "complexity", "pack_changelog", "data_conflicts", "pack_graph", "config_diff", "health"]),
|
|
1251
|
+
modId: z.union([z.string(), z.number()]).optional(),
|
|
1252
|
+
assetType: z.enum(["textures", "models", "sounds", "blockstates", "shaders", "all"]).optional().describe("Asset sub-folder filter (asset_conflicts)"),
|
|
1253
|
+
dataType: z.enum(["recipe", "loot_tables", "advancements", "tags", "structures", "all"]).optional().describe("Data sub-folder filter (data_conflicts)"),
|
|
1254
|
+
overrideType: z.enum(["data", "assets", "all"]).optional().describe("Override type filter (vanilla_overrides)"),
|
|
1255
|
+
dataSubtype: z.string().optional().describe("Data subfolder, e.g. 'recipes', 'loot_tables', 'advancements' (vanilla_overrides)"),
|
|
1256
|
+
scriptsDir: z.string().optional().describe("Absolute path to kubejs/ folder for pack_graph KubeJS integration"),
|
|
1257
|
+
configDir: z.string().optional().describe("Absolute path to the modpack's config/ directory (config_diff)"),
|
|
1258
|
+
mcVersion: z.string().optional(),
|
|
1259
|
+
loader: z.string().optional(),
|
|
1260
|
+
oldIds: z.array(z.number()).optional(),
|
|
1261
|
+
newIds: z.array(z.number()).optional(),
|
|
1262
|
+
limit: z.number().optional(),
|
|
1263
|
+
}, safe(async ({ action, modId, assetType, dataType, overrideType, dataSubtype, scriptsDir, configDir, mcVersion, loader, oldIds, newIds, limit }) => {
|
|
1264
|
+
let result;
|
|
1265
|
+
switch (action) {
|
|
1266
|
+
case "asset_conflicts":
|
|
1267
|
+
result = await findAssetConflicts(assetType, mcVersion, loader, limit);
|
|
1268
|
+
break;
|
|
1269
|
+
case "vanilla_overrides":
|
|
1270
|
+
result = await findVanillaOverrides(overrideType, dataSubtype, mcVersion, loader);
|
|
1271
|
+
break;
|
|
1272
|
+
case "sidedness":
|
|
1273
|
+
result = await analyzeModSidedness(modId);
|
|
1274
|
+
break;
|
|
1275
|
+
case "pack_sidedness":
|
|
1276
|
+
result = await analyzePackSidedness(mcVersion, loader);
|
|
1277
|
+
break;
|
|
1278
|
+
case "complexity":
|
|
1279
|
+
result = await computeModComplexity(mcVersion, loader);
|
|
1280
|
+
break;
|
|
1281
|
+
case "pack_changelog":
|
|
1282
|
+
result = await computePackChangelog(oldIds, newIds);
|
|
1283
|
+
break;
|
|
1284
|
+
case "data_conflicts":
|
|
1285
|
+
result = await findDataConflicts(dataType, mcVersion, loader, limit);
|
|
1286
|
+
break;
|
|
1287
|
+
case "pack_graph":
|
|
1288
|
+
result = await buildPackGraph(mcVersion, loader, scriptsDir);
|
|
1289
|
+
break;
|
|
1290
|
+
case "config_diff":
|
|
1291
|
+
result = await diffPackConfigs(configDir, mcVersion, loader);
|
|
1292
|
+
break;
|
|
1293
|
+
case "health":
|
|
1294
|
+
result = await packHealth(mcVersion, loader);
|
|
1295
|
+
break;
|
|
1296
|
+
}
|
|
1297
|
+
return out(result);
|
|
1298
|
+
}));
|
|
1299
|
+
// ── 21. kubejs ────────────────────────────────────────────────────────────────
|
|
1300
|
+
server.tool("kubejs", "KubeJS script analysis \u2014 index a kubejs/ directory or text-search scripts. action=index|search|semantic_search. semantic_search requires Ollama running.", {
|
|
1301
|
+
action: z.enum(["index", "search", "semantic_search"]),
|
|
1302
|
+
scriptsDir: z.string().describe("Absolute path to the kubejs/ folder or a subfolder like kubejs/server_scripts/"),
|
|
1303
|
+
query: z.string().optional(),
|
|
1304
|
+
limit: z.number().optional(),
|
|
1305
|
+
}, safe(async ({ action, scriptsDir, query, limit }) => {
|
|
1306
|
+
let result;
|
|
1307
|
+
switch (action) {
|
|
1308
|
+
case "index":
|
|
1309
|
+
result = await indexKubeJsScripts(scriptsDir);
|
|
1310
|
+
break;
|
|
1311
|
+
case "search":
|
|
1312
|
+
result = await searchKubeJsScripts(scriptsDir, query, limit);
|
|
1313
|
+
break;
|
|
1314
|
+
case "semantic_search":
|
|
1315
|
+
result = await semanticSearchKubeJsScripts(scriptsDir, query, limit);
|
|
1316
|
+
break;
|
|
1317
|
+
}
|
|
1318
|
+
return out(result);
|
|
1319
|
+
}));
|
|
1320
|
+
// ── 22. analyze_crash_log ────────────────────────────────────────────────────
|
|
1321
|
+
server.tool("analyze_crash_log", "Parse a NeoForge/Forge/Fabric crash log and return suspect mods ranked by stack-frame hits against the indexed class DB.", {
|
|
1322
|
+
logText: z.string().describe("full crash log text"),
|
|
1323
|
+
}, safe(async ({ logText }) => {
|
|
1324
|
+
const result = await analyzeCrashLog(logText);
|
|
1325
|
+
return out(result);
|
|
1326
|
+
}));
|
|
1327
|
+
// ── 23. find_missing_deps ────────────────────────────────────────────────────
|
|
1328
|
+
server.tool("find_missing_deps", "Find declared mod dependencies not satisfied by any ingested mod in the DB. Skips loader pseudo-deps (minecraft, neoforge, java, etc.).", {
|
|
1329
|
+
mcVersion: z.string().optional(),
|
|
1330
|
+
loader: z.string().optional().describe("neoforge|forge|fabric|quilt"),
|
|
1331
|
+
}, safe(async ({ mcVersion, loader }) => {
|
|
1332
|
+
const result = await findMissingDeps(mcVersion, loader);
|
|
1333
|
+
return out(result);
|
|
1334
|
+
}));
|
|
1335
|
+
// ── 24. check_mod_compat ──────────────────────────────────────────────────────
|
|
1336
|
+
server.tool("check_mod_compat", "Pre-flight check a candidate JAR against the DB — mixin conflicts, AT/AW overlaps, asset conflicts, missing deps, sidedness. Does not require prior ingestion.", {
|
|
1337
|
+
jarPath: z.string().describe("Absolute path to the candidate mod JAR"),
|
|
1338
|
+
mcVersion: z.string().optional(),
|
|
1339
|
+
loader: z.string().optional().describe("neoforge|forge|fabric|quilt"),
|
|
1340
|
+
}, safe(async ({ jarPath, mcVersion, loader }) => {
|
|
1341
|
+
const result = await checkModCompat(jarPath, mcVersion, loader);
|
|
1342
|
+
return out(result);
|
|
1343
|
+
}));
|
|
1344
|
+
// ── Start ─────────────────────────────────────────────────────────────────────
|
|
1345
|
+
process.on("SIGINT", async () => { await disconnect(); process.exit(0); });
|
|
1346
|
+
process.on("SIGTERM", async () => { await disconnect(); process.exit(0); });
|
|
1347
|
+
// Transport selection:
|
|
1348
|
+
// • MCP_PORT set → Streamable HTTP transport (for k8s / remote deployments).
|
|
1349
|
+
// • otherwise → stdio transport (default, local CLI use).
|
|
1350
|
+
const httpPort = process.env.MCP_PORT ? parseInt(process.env.MCP_PORT, 10) : null;
|
|
1351
|
+
if (httpPort && !Number.isNaN(httpPort)) {
|
|
1352
|
+
await startHttpServer(httpPort);
|
|
1353
|
+
}
|
|
1354
|
+
else {
|
|
1355
|
+
const transport = new StdioServerTransport();
|
|
1356
|
+
await server.connect(transport);
|
|
1357
|
+
}
|
|
1358
|
+
// Background: re-queue any mods that were partially embedded before this start
|
|
1359
|
+
// Set MODLENS_AUTO_EMBED=0 to disable.
|
|
1360
|
+
if (process.env.MODLENS_AUTO_EMBED !== "0") {
|
|
1361
|
+
startupEmbedScan().catch(() => { });
|
|
1362
|
+
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Streamable HTTP transport with per-session state.
|
|
1365
|
+
*
|
|
1366
|
+
* - POST /mcp → client→server messages. A request with no session id
|
|
1367
|
+
* and an `initialize` body opens a new session.
|
|
1368
|
+
* - GET /mcp → server→client SSE stream for an existing session.
|
|
1369
|
+
* - DELETE /mcp → explicit session teardown.
|
|
1370
|
+
* - GET /healthz → liveness/readiness probe (k8s).
|
|
1371
|
+
*
|
|
1372
|
+
* Bind path is configurable via MCP_PATH (default "/mcp"). Set MCP_HOST to
|
|
1373
|
+
* change the bind address (default 0.0.0.0).
|
|
1374
|
+
*/
|
|
1375
|
+
async function startHttpServer(port) {
|
|
1376
|
+
const mcpPath = process.env.MCP_PATH ?? "/mcp";
|
|
1377
|
+
const host = process.env.MCP_HOST ?? "0.0.0.0";
|
|
1378
|
+
// Active sessions keyed by Mcp-Session-Id header.
|
|
1379
|
+
const transports = new Map();
|
|
1380
|
+
const readBody = (req) => new Promise((resolve, reject) => {
|
|
1381
|
+
const chunks = [];
|
|
1382
|
+
req.on("data", (c) => chunks.push(c));
|
|
1383
|
+
req.on("end", () => {
|
|
1384
|
+
const raw = Buffer.concat(chunks).toString("utf8");
|
|
1385
|
+
if (!raw)
|
|
1386
|
+
return resolve(undefined);
|
|
1387
|
+
try {
|
|
1388
|
+
resolve(JSON.parse(raw));
|
|
1389
|
+
}
|
|
1390
|
+
catch (e) {
|
|
1391
|
+
reject(e);
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
req.on("error", reject);
|
|
1395
|
+
});
|
|
1396
|
+
const sendJson = (res, status, body) => {
|
|
1397
|
+
const payload = JSON.stringify(body);
|
|
1398
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
1399
|
+
res.end(payload);
|
|
1400
|
+
};
|
|
1401
|
+
const isInitialize = (body) => !!body && typeof body === "object" && body.method === "initialize";
|
|
1402
|
+
const httpServer = createHttpServer(async (req, res) => {
|
|
1403
|
+
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? host}`);
|
|
1404
|
+
if (url.pathname === "/healthz") {
|
|
1405
|
+
return sendJson(res, 200, { status: "ok" });
|
|
1406
|
+
}
|
|
1407
|
+
if (url.pathname !== mcpPath) {
|
|
1408
|
+
return sendJson(res, 404, { error: "not found" });
|
|
1409
|
+
}
|
|
1410
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
1411
|
+
try {
|
|
1412
|
+
// Existing session — route to its transport.
|
|
1413
|
+
if (sessionId && transports.has(sessionId)) {
|
|
1414
|
+
const transport = transports.get(sessionId);
|
|
1415
|
+
const body = req.method === "POST" ? await readBody(req) : undefined;
|
|
1416
|
+
await transport.handleRequest(req, res, body);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
// New session — must be an initialize POST.
|
|
1420
|
+
if (req.method === "POST") {
|
|
1421
|
+
const body = await readBody(req);
|
|
1422
|
+
if (!isInitialize(body)) {
|
|
1423
|
+
return sendJson(res, 400, {
|
|
1424
|
+
jsonrpc: "2.0",
|
|
1425
|
+
error: { code: -32000, message: "No valid session ID provided" },
|
|
1426
|
+
id: null,
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
const transport = new StreamableHTTPServerTransport({
|
|
1430
|
+
sessionIdGenerator: () => randomUUID(),
|
|
1431
|
+
onsessioninitialized: (id) => { transports.set(id, transport); },
|
|
1432
|
+
});
|
|
1433
|
+
transport.onclose = () => {
|
|
1434
|
+
if (transport.sessionId)
|
|
1435
|
+
transports.delete(transport.sessionId);
|
|
1436
|
+
};
|
|
1437
|
+
await server.connect(transport);
|
|
1438
|
+
await transport.handleRequest(req, res, body);
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
return sendJson(res, 400, {
|
|
1442
|
+
jsonrpc: "2.0",
|
|
1443
|
+
error: { code: -32000, message: "No valid session ID provided" },
|
|
1444
|
+
id: null,
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
catch (err) {
|
|
1448
|
+
if (!res.headersSent) {
|
|
1449
|
+
sendJson(res, 500, {
|
|
1450
|
+
jsonrpc: "2.0",
|
|
1451
|
+
error: { code: -32603, message: "Internal server error" },
|
|
1452
|
+
id: null,
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
console.error("[mcp-http] request error:", err);
|
|
1456
|
+
}
|
|
1457
|
+
});
|
|
1458
|
+
await new Promise((resolve) => {
|
|
1459
|
+
httpServer.listen(port, host, () => {
|
|
1460
|
+
console.error(`modlens-mcp listening on http://${host}:${port}${mcpPath}`);
|
|
1461
|
+
resolve();
|
|
1462
|
+
});
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
//# sourceMappingURL=server.js.map
|