Skip to main content
The ModrinthPlatform class implements the Platform interface to upload mod files to Modrinth using the Modrinth API v2.

Package

me.hsgamer.mcreleaser.modrinth.ModrinthPlatform

Class Declaration

public class ModrinthPlatform implements Platform {
    private final Logger logger = LoggerFactory.getLogger(getClass());
}

Constructor

ModrinthPlatform()

Creates a new Modrinth platform instance and inherits game version configuration from common properties if not explicitly set.
public ModrinthPlatform() {
    if (ModrinthPropertyKey.GAME_VERSIONS.isAbsent() && 
        CommonPropertyKey.GAME_VERSIONS.isPresent()) {
        ModrinthPropertyKey.GAME_VERSIONS.setValue(
            CommonPropertyKey.GAME_VERSIONS.getValue()
        );
    }
    if (ModrinthPropertyKey.GAME_VERSION_TYPE.isAbsent() && 
        CommonPropertyKey.GAME_VERSION_TYPE.isPresent()) {
        ModrinthPropertyKey.GAME_VERSION_TYPE.setValue(
            CommonPropertyKey.GAME_VERSION_TYPE.getValue()
        );
    }
}

Methods

createUploadRunnable

Creates a batch runnable that uploads files to Modrinth.
fileBundle
FileBundle
required
The bundle of files to upload as version files
runnable
Optional<BatchRunnable>
Returns a BatchRunnable if all required properties are present, otherwise empty
@Override
public Optional<BatchRunnable> createUploadRunnable(FileBundle fileBundle) {
    if (PropertyKeyUtil.isAbsentAndAnnounce(logger, 
        ModrinthPropertyKey.TOKEN, 
        ModrinthPropertyKey.PROJECT, 
        ModrinthPropertyKey.LOADERS, 
        ModrinthPropertyKey.GAME_VERSIONS)) {
        return Optional.empty();
    }
    // Create upload tasks...
}

Upload Process

The upload is executed in two phases:

Phase 1: Preparation

  1. Fetch Minecraft Versions
    List<String> gameVersionFilters = Arrays.asList(
        StringUtil.splitSpace(ModrinthPropertyKey.GAME_VERSIONS.getValue())
    );
    VersionTypeFilter gameVersionTypeFilter = VersionTypeFilter.RELEASE;
    
    MinecraftVersionFetcher.fetchVersionIds(
        gameVersionFilters, 
        gameVersionTypeFilter
    ).whenComplete((versionIds, throwable) -> {
        // Use normalized version IDs
    });
    
  2. Build Request JSON
    JsonObject request = new JsonObject();
    request.addProperty("project_id", ModrinthPropertyKey.PROJECT.getValue());
    request.addProperty("featured", ModrinthPropertyKey.FEATURED.asBoolean(true));
    request.addProperty("name", CommonPropertyKey.NAME.getValue());
    request.addProperty("changelog", CommonPropertyKey.DESCRIPTION.getValue());
    request.addProperty("version_number", CommonPropertyKey.VERSION.getValue());
    request.addProperty("version_type", ModrinthPropertyKey.VERSION_TYPE.getValue("release"));
    
    request.add("loaders", gson.toJsonTree(loaders));
    request.add("game_versions", gson.toJsonTree(gameVersions));
    request.add("dependencies", dependencies);
    request.add("file_parts", gson.toJsonTree(fileParts));
    

Phase 3: Upload

Uploads the version to Modrinth:
String url = baseUrl + "/v2/version";
HttpPost post = new HttpPost(url);
post.setHeader("Authorization", token);
post.setHeader("User-Agent", userAgent);
post.setHeader("Accept", "application/json");

MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.addTextBody("data", gson.toJson(request), ContentType.APPLICATION_JSON);
for (File file : fileBundle.allFiles()) {
    builder.addBinaryBody(file.getName(), file);
}

Required Properties

MODRINTH_TOKEN
String
required
Modrinth API token (PAT)
MODRINTH_PROJECT
String
required
Modrinth project ID or slug
MODRINTH_LOADERS
String
required
Space-separated list of mod loaders (e.g., “fabric forge paper”)
MODRINTH_GAME_VERSIONS
String
required
Space-separated Minecraft version patterns (e.g., “1.20 1.20.1”)

Optional Properties

MODRINTH_VERSION_TYPE
String
default:"release"
Version type: “release”, “beta”, or “alpha”
MODRINTH_GAME_VERSION_TYPE
String
default:"RELEASE"
Filter for Minecraft versions: “RELEASE”, “SNAPSHOT”, or “ALL”
Mark version as featured
MODRINTH_DEPENDENCIES
String
JSON array of dependencies (see examples below)
MODRINTH_ENDPOINT
String
default:"production"
API endpoint: “production” or “staging”

Common Properties

NAME
String
required
Version name/title
VERSION
String
required
Version number
DESCRIPTION
String
required
Version changelog (supports Markdown)

Configuration Examples

Basic Fabric Mod Release

export MODRINTH_TOKEN="mrp_1234567890abcdef"
export MODRINTH_PROJECT="my-mod"
export MODRINTH_LOADERS="fabric"
export MODRINTH_GAME_VERSIONS="1.20 1.20.1"

export NAME="My Mod v1.0.0"
export VERSION="1.0.0"
export DESCRIPTION="Initial release with cool features"
export FILES="build/libs/*.jar"

java -jar mcreleaser-cli.jar

Multi-Loader Support

export MODRINTH_LOADERS="fabric forge"
export MODRINTH_GAME_VERSIONS="1.19 1.20"
export MODRINTH_VERSION_TYPE="beta"

With Dependencies

export MODRINTH_DEPENDENCIES='[
  {
    "project_id": "fabric-api",
    "dependency_type": "required"
  },
  {
    "project_id": "cloth-config",
    "dependency_type": "optional"
  }
]'
Dependency types:
  • required - Must be installed
  • optional - Recommended but not required
  • incompatible - Conflicts with this version
  • embedded - Included in the mod file

Version Patterns

# Specific versions
export MODRINTH_GAME_VERSIONS="1.20.1 1.20.2"

# Version ranges (resolved automatically)
export MODRINTH_GAME_VERSIONS="1.20"
export MODRINTH_GAME_VERSION_TYPE="RELEASE"

# Include snapshots
export MODRINTH_GAME_VERSION_TYPE="ALL"

Staging Environment

export MODRINTH_ENDPOINT="staging"
# Uses https://staging-api.modrinth.com instead of production

Paper Plugin

export MODRINTH_LOADERS="paper spigot purpur"
export MODRINTH_GAME_VERSIONS="1.20 1.20.1 1.20.2"

Property Keys Source

Defined in ModrinthPropertyKey:
public interface ModrinthPropertyKey {
    PropertyPrefix MODRINTH = new PropertyPrefix("modrinth");
    PropertyKey TOKEN = MODRINTH.key("token");
    PropertyKey PROJECT = MODRINTH.key("project");
    PropertyKey VERSION_TYPE = MODRINTH.key("versionType");
    PropertyKey GAME_VERSIONS = MODRINTH.key("gameVersions");
    PropertyKey GAME_VERSION_TYPE = MODRINTH.key("gameVersionType");
    PropertyKey FEATURED = MODRINTH.key("featured");
    PropertyKey LOADERS = MODRINTH.key("loaders");
    PropertyKey DEPENDENCIES = MODRINTH.key("dependencies");
    PropertyKey ENDPOINT = MODRINTH.key("endpoint");
}

Supported Loaders

  • fabric - Fabric mod loader
  • forge - Forge mod loader
  • quilt - Quilt mod loader
  • neoforge - NeoForge mod loader
  • paper - Paper server
  • spigot - Spigot server
  • purpur - Purpur server
  • bukkit - Bukkit server
  • folia - Folia server

Version Type Values

  • release - Stable release
  • beta - Beta version
  • alpha - Alpha/development version

File Handling

Modrinth uploads all files from the FileBundle:
// Primary file is marked explicitly
if (fileBundle.primaryFile() != null) {
    request.addProperty("primary_file", fileBundle.primaryFile().getName());
}

// All files are uploaded
for (File file : fileBundle.allFiles()) {
    builder.addBinaryBody(file.getName(), file);
}

Error Handling

Invalid Version Type

try {
    gameVersionTypeFilter = VersionTypeFilter.valueOf(
        ModrinthPropertyKey.GAME_VERSION_TYPE.getValue().toUpperCase()
    );
} catch (IllegalArgumentException e) {
    logger.error("Invalid version type: {}", value, e);
    process.complete();
}

Invalid Dependencies JSON

try {
    JsonArray dependencies = gson.fromJson(
        ModrinthPropertyKey.DEPENDENCIES.getValue(), 
        JsonArray.class
    );
} catch (Exception e) {
    logger.error("Invalid dependencies", e);
    process.complete();
}

API Response

Successful upload logs the version ID:
JsonObject versionDto = client.execute(post, response -> {
    if (response.getCode() != 200) {
        throw new HttpException("Execute not successful. Got: %d %s", 
            response.getCode(), EntityUtils.toString(response.getEntity()));
    }
    String body = EntityUtils.toString(response.getEntity());
    return gson.fromJson(body, JsonObject.class);
});
logger.info("Uploaded the version: {}", versionDto.get("id").getAsString());

Build docs developers (and LLMs) love