# Modrinth 新建版本 API 使用文档 本文档详细描述如何通过 Modrinth API 创建一个新版本,包括上传模组主文件(`.jar`)和一个 `-sources.jar` 附属文件。 --- ## 基本信息 | 项目 | 内容 | |------|------| | **端点** | `POST /version` | | **生产环境** | `https://api.modrinth.com/v2/version` | | **测试环境** | `https://staging-api.modrinth.com/v2/version` | | **Content-Type** | `multipart/form-data` | | **认证** | 必需 — Personal Access Token (PAT) | | **所需 Scope** | `VERSION_CREATE` | | **成功响应码** | `200` | | **成功响应体** | [Version](#响应格式) 对象 | --- ## 认证 所有创建数据的请求都需要认证。你需要一个 **Personal Access Token**(PAT),在 [Modrinth 用户设置](https://modrinth.com/settings/account) 中生成。 创建版本需要 PAT 具有 `VERSION_CREATE` 权限范围。 将 Token 放在 `Authorization` 请求头中: ``` Authorization: mrp_RNtLRSPmGj2pd1v1ubi52nX7TJJM9sznrmwhAuj511oe4t1jAqAQ3D6Wc8Ic ``` > **注意:** 需要在 `upload.md` 中提供有效的 Token。 同时,Modrinth **要求**提供一个可唯一标识的 `User-Agent` 请求头: ``` User-Agent: github_username/project_name/1.0.0 (contact@example.com) ``` --- ## 请求格式 请求是一个 **multipart/form-data** 请求,必须包含至少两个表单字段: - **`data`**:一个 JSON 字符串,包含版本元数据(见下方 [data JSON 结构](#data-json-结构))。 - **一个或多个文件字段**:包含要上传的 `.jar`、`.mrpack`、`.zip` 或 `.litemod` 文件。文件字段名可自定义。 ### 重要概念区分:multipart 字段名 vs 文件名 在 multipart 请求中,每个文件字段都有**两个不同的名字**: ``` -F "main-file=@./build/libs/my-mod-1.0.0.jar" ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^ multipart 字段名 (field name) 文件名 (filename) ``` - **multipart 字段名**(如 `main-file`):在请求体中标识这一部分数据的 key,由你自定义。 - **文件名**(如 `my-mod-1.0.0.jar`):被上传文件的原始名称。 > **关键规则:`file_parts`、`primary_file`、`file_types` 的 key 全部使用 multipart 字段名,而不是文件名。** ### `file_parts` — 声明有哪些文件 在 `data` JSON 的 `file_parts` 数组中列出所有承载文件的 **multipart 字段名**,并在 `primary_file` 中指定哪个是主文件。 ```json { "file_parts": ["main-file", "sources-file", "dev-file"], "primary_file": "main-file" } ``` ### `file_types` — 标记附属文件类型 `file_types` 是一个 **对象(map)**,**key 是 multipart 字段名**(与 `file_parts` 中的值一致),value 是该文件的类型枚举值。用于标记 `-sources.jar`、`-dev.jar` 等附属文件。 可用的文件类型(`FileTypeEnum`): | 值 | 含义 | |----|------| | `required-resource-pack` | 必需资源包 | | `optional-resource-pack` | 可选资源包 | | `sources-jar` | 源代码 JAR | | `dev-jar` | 开发版 JAR(包含未混淆的类) | | `javadoc-jar` | Javadoc JAR | | `unknown` | 未知类型 | | `signature` | 签名文件 | **标记 sources.jar 的示例:** ```json { "file_types": { "sources-file": "sources-jar" } } ``` > 上例中 `"sources-file"` 是 multipart 字段名,需要在 `file_parts` 中同步出现。 --- ## data JSON 结构 `data` 字段的值是一个 JSON 字符串,对应 `CreatableVersion` schema,它扩展了 `BaseVersion`。 ### 顶层结构(`CreateVersionBody`) ```json { "data": "{ ... CreatableVersion JSON ... }" } ``` ### CreatableVersion 字段一览 #### 必填字段 | 字段 | 类型 | 说明 | 示例 | |------|------|------|------| | `project_id` | `string` | 目标项目的 ID(8 位 base62) | `"AABBCCDD"` | | `file_parts` | `string[]` | 所有承载文件的 **multipart 字段名** 列表 | `["main-file", "sources-file"]` | | `name` | `string` | 版本名称 | `"Version 1.0.0"` | | `version_number` | `string` | 版本号,建议遵循语义化版本 | `"1.0.0"` | | `game_versions` | `string[]` | 支持的 Minecraft 版本列表 | `["1.20.1", "1.20.4"]` | | `version_type` | `string` | 发布渠道:`release`、`beta`、`alpha` | `"release"` | | `loaders` | `string[]` | 支持的模组加载器列表 | `["fabric", "forge"]` | | `featured` | `boolean` | 是否为精选版本 | `false` | | `dependencies` | `object[]` | 依赖列表(可为空数组 `[]`) | 见下方 [依赖结构](#依赖结构) | #### 可选字段 | 字段 | 类型 | 说明 | 示例 | |------|------|------|------| | `primary_file` | `string` | 主文件的 **multipart 字段名**。不提供时推断第一个文件为主文件 | `"main-file"` | | `changelog` | `string \| null` | 更新日志(Markdown/纯文本) | `"修复了若干 Bug"` | | `status` | `string` | 版本状态:`listed`、`archived`、`draft`、`unlisted`、`scheduled`、`unknown`。默认为 `listed` | `"listed"` | | `requested_status` | `string \| null` | 请求的状态(用于审核流程):`listed`、`archived`、`draft`、`unlisted` | `"listed"` | | `environment` | `string` | 目标环境,见下方枚举 | `"client_and_server"` | | `file_types` | `object` | **multipart 字段名** → 文件类型的映射 | `{"sources-file": "sources-jar"}` | #### `environment` 枚举值 所有值来自 Modrinth 前端源码 [environments.ts](https://github.com/modrinth/code/blob/main/packages/ui/src/components/project/settings/environment/environments.ts)。以下按前端 UI 的层级结构排列,只有 **Server-side only** 和 **Client and server** 是带子选项的大类,其余为单选: | 大类(前端 UI) | 子项标题 | API 值 | 说明 | |----------------|---------|--------|------| | *(无分类,单选)* | Unknown environment | `unknown` | 未指定或无法确定环境 | | Client-side only | — | `client_only` | 所有功能在客户端运行,兼容原版服务端 | | **Server-side only** | Works in singleplayer too | `server_only` | 所有功能在服务端运行,兼容原版客户端;也支持单人模式的内置服务端 | | | Dedicated server only | `dedicated_server_only` | 所有功能在服务端运行,兼容原版客户端;仅在专用服务器上工作 | | **Client and server** | Required on both | `client_and_server` | 客户端和服务端都必须安装 | | | Optional on client | `server_only_client_optional` | 主要为服务端功能,客户端安装可增强体验 | | | Optional on server | `client_only_server_optional` | 主要为客户端功能,服务端安装可增强体验 | | | Optional on both, works best when installed on both sides | `client_or_server_prefers_both` | 双方都装体验最佳 | | | Optional on both, works the same if installed on either side | `client_or_server` | 单独安装任一侧效果相同 | | Singleplayer only | — | `singleplayer_only` | 仅在单人模式或未连接多人服务器时可用 | ### 依赖结构 `dependencies` 数组中每个依赖对象至少需要 `dependency_type` 字段: | 字段 | 类型 | 必填 | 说明 | 示例 | |------|------|------|------|------| | `dependency_type` | `string` | ✅ | 依赖类型:`required`、`optional`、`incompatible`、`embedded` | `"required"` | | `project_id` | `string \| null` | ❌ | 依赖的项目 ID | `"P7dR8mSH"` (Fabric API) | | `version_id` | `string \| null` | ❌ | 依赖的具体版本 ID;为 `null` 时匹配最新版 | `"IIJJKKLL"` | | `file_name` | `string \| null` | ❌ | 依赖文件名,主要用于整合包中的外部依赖 | `"fabric-api-0.92.0.jar"` | > **注意:** `project_id` 和 `version_id` 二者至少提供一个,否则依赖关系无法解析。 **Fabric API 依赖示例:** ```json { "dependency_type": "required", "project_id": "P7dR8mSH" } ``` --- ## 完整请求示例 以下是一个使用 cURL 上传一个主 jar 和一个 sources.jar 的完整请求。 ### 准备 假设: - 项目 ID 为 `AABBCCDD` - 主文件为 `my-mod-1.0.0.jar` - 源代码文件为 `my-mod-1.0.0-sources.jar` ### cURL 命令 ```bash curl -X POST "https://api.modrinth.com/v2/version" \ -H "Authorization: YOUR_PAT_TOKEN" \ -H "User-Agent: your_username/your_project/1.0.0" \ -F "data={ \"project_id\": \"AABBCCDD\", \"file_parts\": [\"main-file\", \"sources-file\"], \"primary_file\": \"main-file\", \"name\": \"Version 1.0.0\", \"version_number\": \"1.0.0\", \"changelog\": \"## 更新内容\n\n- 新增了某某功能\n- 修复了某某 Bug\", \"dependencies\": [ { \"dependency_type\": \"required\", \"project_id\": \"P7dR8mSH\" } ], \"game_versions\": [\"1.20.1\", \"1.20.4\"], \"version_type\": \"release\", \"loaders\": [\"fabric\"], \"featured\": false, \"status\": \"listed\", \"environment\": \"client_and_server\", \"file_types\": { \"sources-file\": \"sources-jar\" } }" \ -F "main-file=@./build/libs/my-mod-1.0.0.jar" \ -F "sources-file=@./build/libs/my-mod-1.0.0-sources.jar" ``` ### Node.js (fetch) 示例 ```js const fs = require("fs"); const TOKEN = "YOUR_PAT_TOKEN"; const PROJECT_ID = "AABBCCDD"; const form = new FormData(); const metadata = { project_id: PROJECT_ID, file_parts: ["main-file", "sources-file"], primary_file: "main-file", name: "Version 1.0.0", version_number: "1.0.0", changelog: "## 更新内容\n\n- 新增了某某功能\n- 修复了某某 Bug", dependencies: [ { dependency_type: "required", project_id: "P7dR8mSH" } ], game_versions: ["1.20.1", "1.20.4"], version_type: "release", loaders: ["fabric"], featured: false, status: "listed", environment: "client_and_server", file_types: { "sources-file": "sources-jar" } }; form.append("data", JSON.stringify(metadata)); form.append("main-file", new Blob([fs.readFileSync("./build/libs/my-mod-1.0.0.jar")]), "my-mod-1.0.0.jar"); form.append("sources-file", new Blob([fs.readFileSync("./build/libs/my-mod-1.0.0-sources.jar")]), "my-mod-1.0.0-sources.jar"); const response = await fetch("https://api.modrinth.com/v2/version", { method: "POST", headers: { "Authorization": TOKEN, "User-Agent": "your_username/your_project/1.0.0" }, body: form }); const result = await response.json(); console.log(result); ``` --- ## 响应格式 成功创建后返回一个 [Version](#version-对象) 对象,HTTP 状态码 `200`。 ### Version 对象 ```json { "id": "IIJJKKLL", "project_id": "AABBCCDD", "author_id": "EEFFGGHH", "name": "Version 1.0.0", "version_number": "1.0.0", "changelog": "## 更新内容\n\n- 新增了某某功能\n- 修复了某某 Bug", "date_published": "2025-01-01T00:00:00Z", "downloads": 0, "version_type": "release", "status": "listed", "requested_status": null, "game_versions": ["1.20.1", "1.20.4"], "loaders": ["fabric"], "featured": false, "files": [ { "hashes": { "sha512": "93ecf5fe...", "sha1": "c84dd4b3..." }, "url": "https://cdn.modrinth.com/data/AABBCCDD/versions/1.0.0/my-mod-1.0.0.jar", "filename": "my-mod-1.0.0.jar", "primary": true, "size": 1097270, "file_type": null }, { "hashes": { "sha512": "ab12cd34...", "sha1": "ef56gh78..." }, "url": "https://cdn.modrinth.com/data/AABBCCDD/versions/1.0.0/my-mod-1.0.0-sources.jar", "filename": "my-mod-1.0.0-sources.jar", "primary": false, "size": 543210, "file_type": "sources-jar" } ] } ``` 响应中 `files` 数组的每个文件对象包含: | 字段 | 说明 | |------|------| | `hashes` | 文件哈希,包含 `sha512` 和 `sha1` | | `url` | 文件直链 | | `filename` | 文件名 | | `primary` | 是否为主文件 | | `size` | 文件大小(字节) | | `file_type` | 文件类型(对于 sources.jar 为 `"sources-jar"`) | --- ## 错误码 | 状态码 | 错误类型 | 说明 | |--------|----------|------| | `400` | `InvalidInputError` | 请求参数无效,详见响应中的 `description`。常见原因:缺少必填字段、`game_versions` 或 `loaders` 值不合法、文件格式不被接受 | | `401` | `AuthError` | Token 无效、未提供 Token,或 Token 不包含 `VERSION_CREATE` 权限范围 | | `404` | — | 目标项目 `project_id` 不存在 | --- ## 注意事项与最佳实践 ### 1. 必须至少上传一个文件 除非版本 `status` 设置为 `draft`,否则每个新版本必须附带至少一个文件。接受的格式:`.mrpack`、`.jar`、`.zip`、`.litemod`。 ### 2. `file_parts` / `file_types` / `primary_file` 全部使用 multipart 字段名 这三处字符串和实际表单字段名必须**严格匹配**,且它们指向的都是 **multipart 字段名**(如 `"main-file"`),不是文件名(如 `"my-mod-1.0.0.jar"`)。否则上传的文件不会被正确关联。 ### 3. sources.jar 的正确做法 - 给 sources 文件随意命名 multipart 字段名(如 `"sources-file"`),将其列入 `file_parts`。 - 在 `file_types` 中映射该 **multipart 字段名** → `"sources-jar"`。 - **不要**将 sources 文件设为主文件(`primary_file` 应指向主 jar 的 multipart 字段名)。 ### 4. `dependencies` 可以是空数组 如果该模组没有依赖(极少数情况),直接传 `[]` 即可。但 `dependencies` 字段本身是必填的。 ### 5. 文件大小限制 Modrinth 对上传文件有大小限制。如果文件过大,可能收到 `400` 错误。建议保持在合理范围内(一般单个文件不超过 100MB)。 ### 6. 版本号建议 `version_number` 理想上应遵循[语义化版本规范](https://semver.org/lang/zh-CN/),便于 Modrinth 进行版本比较和用户理解。 ### 7. 草稿版本 如果你想先上传版本而不立即公开,可将 `status` 设为 `draft`。草稿版本可以不附带文件。后续可通过 PATCH `/version/{id}` 修改状态为 `listed` 来发布。 ### 8. 已有版本追加文件 如果版本已创建但需要追加文件,应使用 `POST /version/{id}/file` 端点(需要 `VERSION_WRITE` 权限范围)。 ### 9. 环境与加载器的校验 `loaders` 和 `game_versions` 必须使用 Modrinth 认可的值。你可以通过以下端口查询可用值: - `GET /tag/loader` — 可用加载器 - `GET /tag/game_version` — 可用 Minecraft 版本 ### 10. Rate Limit Modrinth API 有频率限制:每 IP 每分钟最多 **300** 个请求。响应头中包含 `X-Ratelimit-Limit`、`X-Ratelimit-Remaining`、`X-Ratelimit-Reset`。 --- ## 参考 - [Modrinth API 文档](https://docs.modrinth.com) - [Modrinth 创建 PAT](https://modrinth.com/settings/account) - [OpenAPI 规范文件](openapi.yml) - [Modrinth Labrinth 源码](https://github.com/modrinth/labrinth) - [语义化版本规范](https://semver.org)