| """ |
| MCP server definition: registers tools and resources exposed by the |
| fabric-config-oracle over stdio transport. |
| |
| Tools |
| ----- |
| * `get_optimal_fabric_config` — resolve the optimal Fabric build config for a |
| specific or the latest Minecraft version, applying snapshot fallback logic. |
| * `generate_gradle_properties` — render a complete `gradle.properties` file |
| text from a resolved config plus mod metadata. |
| |
| Resources |
| --------- |
| * `fabric://config/schema/gradle_properties` — static reference schema for a |
| well-formed Fabric `gradle.properties` file. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import logging |
| from typing import Optional |
|
|
| from mcp.server.fastmcp import FastMCP |
|
|
| from models import FabricConfig |
| from resolver import VersionResolver |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| |
| |
| |
|
|
| mcp: FastMCP = FastMCP("fabric-config-oracle") |
|
|
| |
| |
| |
| _resolver: Optional[VersionResolver] = None |
|
|
|
|
| def _get_resolver() -> VersionResolver: |
| """Lazily instantiate the shared VersionResolver.""" |
| global _resolver |
| if _resolver is None: |
| _resolver = VersionResolver() |
| return _resolver |
|
|
|
|
| |
| |
| |
|
|
|
|
| @mcp.tool() |
| async def get_optimal_fabric_config( |
| minecraft_version: str = "latest", |
| mod_id: Optional[str] = None, |
| ) -> dict: |
| """ |
| Get the optimal Fabric mod configuration for a specific or the latest |
| Minecraft version. |
| |
| When `minecraft_version` is "latest" (or omitted), the server applies |
| intelligent fallback logic: it walks the newest Minecraft snapshots and |
| returns the first one that has full Fabric ecosystem support. |
| |
| **Support is defined differently depending on the MC version:** |
| - For OBFUSCATED versions (1.21.x and earlier): the snapshot must have |
| BOTH Yarn mappings AND a Fabric API build available. |
| - For UNOBFUSCATED versions (26.1+): only a Fabric API build is required. |
| Yarn mappings are deprecated and don't exist for these versions — |
| mods use Mojang's official Mojmap via Loom's |
| `loom.officialMojangMappings()` in build.gradle. |
| |
| If no snapshots have full Fabric support yet, the server falls back to |
| the newest stable release that does. |
| |
| Args: |
| minecraft_version: Target Minecraft version. Accepts: |
| - "latest" (default): apply fallback logic. |
| - Specific releases: "1.21", "1.21.4", "26.1", "26.2". |
| - Specific snapshots: "26.3 Snapshot 2". |
| mod_id: Optional mod identifier. Currently informational only — |
| included in the response payload for caller convenience but does |
| not affect resolution. |
| |
| Returns: |
| A JSON object with the following fields: |
| minecraft_version — the resolved MC version string |
| version_type — "release" or "snapshot" |
| yarn_mappings — Yarn build string (e.g. "1.21.4+build.8"), |
| OR empty string for MC 26.1+ (Yarn |
| deprecated; use Mojmap via Loom) |
| loader_version — Fabric Loader version |
| fabric_api_version — Fabric API mod version from Modrinth |
| loom_version — Fabric Loom Gradle plugin version |
| gradle_wrapper_version — recommended Gradle wrapper version |
| message — human-readable status message |
| mod_id — echoed back if provided |
| """ |
| resolver = _get_resolver() |
| config: FabricConfig = await resolver.resolve_config(minecraft_version) |
| payload = config.model_dump() |
| if mod_id: |
| payload["mod_id"] = mod_id |
| return payload |
|
|
|
|
| |
| |
| |
|
|
|
|
| @mcp.tool() |
| async def generate_gradle_properties( |
| mod_name: str, |
| mod_version: str, |
| minecraft_version: str = "latest", |
| ) -> str: |
| """ |
| Generate the exact text content of a `gradle.properties` file for a |
| Fabric mod, based on the resolved build configuration. |
| |
| Internally calls `get_optimal_fabric_config` to resolve Yarn / Loader / |
| Fabric API versions, then renders a complete properties file with both |
| mod metadata and Fabric tooling versions. |
| |
| Args: |
| mod_name: Human-readable mod name (e.g. "Example Mod"). |
| mod_version: Mod version string (e.g. "1.0.0"). |
| minecraft_version: Target MC version, default "latest". |
| |
| Returns: |
| The full text content of a `gradle.properties` file, ready to be |
| written to disk by the AI assistant. |
| """ |
| resolver = _get_resolver() |
| config: FabricConfig = await resolver.resolve_config(minecraft_version) |
|
|
| |
| |
| raw_id = "".join(c.lower() if c.isalnum() else "-" for c in mod_name) |
| while "--" in raw_id: |
| raw_id = raw_id.replace("--", "-") |
| mod_id = raw_id.strip("-") or "mod" |
|
|
| maven_group = f"com.example.{mod_id.replace('-', '.')}" |
| archives_base_name = mod_id |
|
|
| |
| |
| |
| |
| from resolver import is_unobfuscated |
| unobf = is_unobfuscated(config.minecraft_version) |
|
|
| mc_fabric_section: list[str] = [ |
| "# Minecraft & Fabric versions", |
| f"minecraft_version = {config.minecraft_version}", |
| ] |
| if unobf: |
| mc_fabric_section.append( |
| "# yarn_mappings: not used — MC 26.1+ is unobfuscated; mods" |
| ) |
| mc_fabric_section.append( |
| "# use Mojang official Mojmap via loom.officialMojangMappings()" |
| ) |
| mc_fabric_section.append( |
| "# in build.gradle. (See https://docs.fabricmc.net/develop/porting/mappings)" |
| ) |
| else: |
| mc_fabric_section.append(f"yarn_mappings = {config.yarn_mappings}") |
| mc_fabric_section.append(f"loader_version = {config.loader_version}") |
| mc_fabric_section.append(f"fabric_version = {config.fabric_api_version}") |
|
|
| lines: list[str] = [ |
| "# Gradle properties for Fabric mod", |
| "# Generated by fabric-config-oracle MCP server", |
| "", |
| "# Mod metadata", |
| f"mod_name = {mod_name}", |
| f"mod_id = {mod_id}", |
| f"mod_version = {mod_version}", |
| f"maven_group = {maven_group}", |
| f"archives_base_name = {archives_base_name}", |
| "", |
| *mc_fabric_section, |
| "", |
| "# Build tooling", |
| f"loom_version = {config.loom_version}", |
| f"gradle_wrapper_version = {config.gradle_wrapper_version}", |
| "", |
| "# Optional: Kotlin support (uncomment if needed)", |
| "# kotlin_code_style = official", |
| "# fabric_kotlin_version = 1.10.10+kotlin.1.9.10", |
| ] |
| return "\n".join(lines) |
|
|
|
|
| |
| |
| |
|
|
|
|
| _GRADLE_PROPERTIES_SCHEMA = """\ |
| # Fabric gradle.properties Schema Reference |
| # ========================================= |
| # This document describes the expected structure of a Fabric mod's |
| # gradle.properties file. All keys are case-sensitive. |
| # |
| # IMPORTANT: Mappings selection depends on the target Minecraft version: |
| # |
| # * MC <= 1.21.11 (OBFUSCATED): Yarn mappings are REQUIRED. The mod |
| # references obfuscated class names which |
| # Yarn translates to human-readable names. |
| # Loom is configured in build.gradle via |
| # mappings "net.fabricmc:yarn:${yarn_mappings}:v2" |
| # |
| # * MC >= 26.1 (UNOBFUSCATED): Yarn mappings DO NOT EXIST and are |
| # NOT NEEDED. Mojang ships the jar |
| # unobfuscated with parameter names. |
| # Loom is configured in build.gradle via |
| # loom.officialMojangMappings() |
| # The `yarn_mappings` property line is |
| # OMITTED entirely. |
| # |
| # Source: https://docs.fabricmc.net/develop/porting/mappings |
| |
| # --- Common keys (always present) --- |
| |
| # mod_name: Human-readable mod name (string, any characters) |
| mod_name = Example Mod |
| |
| # mod_id: Lowercase identifier, alphanumeric + hyphens only, max 64 chars. |
| mod_id = example-mod |
| |
| # mod_version: SemVer-style version string for the mod itself. |
| mod_version = 1.0.0 |
| |
| # maven_group: Maven group ID for artifact publishing. Conventionally the |
| # reversed domain of the mod author. |
| maven_group = com.example.examplemod |
| |
| # archives_base_name: Base name for built jar artifacts. |
| archives_base_name = example-mod |
| |
| # minecraft_version: Exact MC version this mod targets (e.g. "1.21.4", "26.2"). |
| minecraft_version = 26.2 |
| |
| # loader_version: Fabric Loader version. The mod's minimum compatible loader. |
| loader_version = 0.16.0 |
| |
| # fabric_version: Fabric API mod version from Modrinth. Required if the mod |
| # depends on Fabric API; otherwise can be omitted. |
| fabric_version = 0.155.2+26.2 |
| |
| # --- Keys present ONLY for obfuscated versions (MC <= 1.21.11) --- |
| |
| # yarn_mappings: Yarn mappings version from meta.fabricmc.net. |
| # Format: "<mc_version>+build.<n>". |
| # OMITTED for MC 26.1+ (unobfuscated; use Mojmap instead). |
| yarn_mappings = 1.21.4+build.8 |
| |
| # --- Build tooling (always present) --- |
| |
| # loom_version: Fabric Loom Gradle plugin version (e.g. "1.7-SNAPSHOT"). |
| loom_version = 1.7-SNAPSHOT |
| |
| # gradle_wrapper_version: Recommended Gradle wrapper version. |
| gradle_wrapper_version = 8.8 |
| |
| # --- Optional: Kotlin --- |
| # kotlin_code_style = official |
| # fabric_kotlin_version = 1.10.10+kotlin.1.9.10 |
| """ |
|
|
|
|
| @mcp.resource("fabric://config/schema/gradle_properties") |
| def get_gradle_properties_schema() -> str: |
| """ |
| Static reference schema for a Fabric mod `gradle.properties` file. |
| |
| Reading this resource returns a plain-text document describing every |
| expected key, its purpose, and an example value. AI assistants can use |
| this to validate generated `gradle.properties` files or to learn the |
| expected structure before generating one. |
| """ |
| return _GRADLE_PROPERTIES_SCHEMA |
|
|
|
|
| |
| |
| |
|
|
|
|
| def get_server() -> FastMCP: |
| """Return the configured FastMCP server instance.""" |
| return mcp |
|
|