Step-1.5-music-api / mcp_server.py
ZeroClawAgent's picture
Upload 10 files
d984ed3 verified
Raw
History Blame Contribute Delete
11.5 kB
"""
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__)
# ---------------------------------------------------------------------------
# Server + shared state
# ---------------------------------------------------------------------------
mcp: FastMCP = FastMCP("fabric-config-oracle")
# Module-level singleton resolver. The MCP stdio server runs in a single
# process for its entire lifetime, so a single shared HTTP client + cache
# is both safe and desirable (connection pooling, fewer upstream calls).
_resolver: Optional[VersionResolver] = None
def _get_resolver() -> VersionResolver:
"""Lazily instantiate the shared VersionResolver."""
global _resolver
if _resolver is None:
_resolver = VersionResolver()
return _resolver
# ---------------------------------------------------------------------------
# Tool 1: get_optimal_fabric_config
# ---------------------------------------------------------------------------
@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
# ---------------------------------------------------------------------------
# Tool 2: generate_gradle_properties
# ---------------------------------------------------------------------------
@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)
# Derive a safe mod_id from the mod name: lowercase, replace non-alnum
# with hyphens, collapse runs of hyphens, strip leading/trailing hyphens.
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
# For unobfuscated versions (26.1+), Yarn mappings are deprecated and
# not used — Loom reads Mojang's official Mojmap directly via
# `loom.officialMojangMappings()` in build.gradle. We omit the
# `yarn_mappings` line entirely and emit a comment explaining why.
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)
# ---------------------------------------------------------------------------
# Resource: gradle_properties schema
# ---------------------------------------------------------------------------
_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
# ---------------------------------------------------------------------------
# Public entry point
# ---------------------------------------------------------------------------
def get_server() -> FastMCP:
"""Return the configured FastMCP server instance."""
return mcp