Skip to main content
Glama

Look up Mojang official names directly from piston-meta

mc_mojang_mappings

Fetches and searches Mojang's client mappings for Minecraft versions that linkie cannot load, such as the 26.1 lineage. Verifies class, method, or field names directly from piston-meta.mojang.com.

Instructions

Bypasses linkie and fetches Mojang's client_mappings.txt straight from piston-meta.mojang.com, parses the proguard format, and searches it. Use this for any version linkie can't load — most importantly the 26.1 lineage (26.1, 26.1.1, 26.1.2) which linkie advertises but 500s on. Also useful when you want to verify a name against Mojang directly. First call for a version downloads the mapping (multi-MB) and caches it in-process; subsequent calls are instant.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
versionYesMinecraft version id, e.g. '26.1.2', '1.21.11'
queryYesClass/method/field name (or substring) to search for
typeNoFilter. Default all.
limitNoMax hits. Default 25.

Implementation Reference

  • src/index.ts:804-865 (registration)
    Registration of the 'mc_mojang_mappings' tool via server.registerTool, including the inputSchema (version, query, type, limit) and the handler that calls getMappings and searchMojangMappings from mojang.ts.
    server.registerTool(
      "mc_mojang_mappings",
      {
        title: "Look up Mojang official names directly from piston-meta",
        description:
          "Bypasses linkie and fetches Mojang's client_mappings.txt straight from " +
          "piston-meta.mojang.com, parses the proguard format, and searches it. " +
          "Use this for any version linkie can't load — most importantly the 26.1 " +
          "lineage (26.1, 26.1.1, 26.1.2) which linkie advertises but 500s on. " +
          "Also useful when you want to verify a name against Mojang directly. " +
          "First call for a version downloads the mapping (multi-MB) and caches " +
          "it in-process; subsequent calls are instant.",
        inputSchema: {
          version: z.string().describe("Minecraft version id, e.g. '26.1.2', '1.21.11'"),
          query: z.string().describe("Class/method/field name (or substring) to search for"),
          type: z.enum(["class", "method", "field", "all"]).optional().describe("Filter. Default all."),
          limit: z.number().int().positive().max(100).optional().describe("Max hits. Default 25."),
        },
      },
      async ({ version, query, type, limit }) => {
        try {
          const parsed = await getMappings(version);
          const hits = searchMojangMappings(parsed, query, { type, limit });
          if (hits.length === 0) {
            return text(`No matches for "${query}" in Mojang mappings for ${version}.`);
          }
          const lines: string[] = [
            `# ${hits.length} hit${hits.length === 1 ? "" : "s"} in Mojang mappings for ${version} (query: "${query}")`,
            "",
          ];
          for (const h of hits) {
            if (h.type === "class") {
              lines.push(`- **class** \`${h.owner}\`  obf: \`${h.obfuscated}\``);
            } else {
              lines.push(
                `- **${h.type}** \`${h.owner}#${h.name}\`` +
                  (h.signature ? `  \`${h.signature}\`` : "") +
                  `  obf: \`${h.obfuscated}\``,
              );
            }
          }
          return text(lines.join("\n"));
        } catch (err) {
          if (err instanceof MojangVersionNotFoundError) {
            const recent = await listMojangVersions((id) => id.startsWith("26.") || id.startsWith("1.21"));
            return text(
              `${err.message}\n\nRecent 26.x / 1.21.x ids on piston-meta: ${recent.slice(0, 20).join(", ")}.`,
            );
          }
          if (err instanceof MojangNoMappingsError) {
            return text(
              `${err.message}\n\n` +
                `**Workarounds for ${err.version}:**\n` +
                `- Try mc_list_versions to see which linkie namespaces have ingested this version.\n` +
                `- For class concepts whose names rarely change, mc_lookup_class is still useful (curated).\n` +
                `- The compiled client.jar at piston-data still ships — symbol extraction would require decompilation, which this server doesn't do.`,
            );
          }
          return text(`Mojang mappings fetch failed: ${(err as Error).message}`);
        }
      },
    );
  • getMappings() - Core handler function that fetches+parses client_mappings.txt for a given Minecraft version. Uses in-process cache and delegates to fetchAndParse which downloads from piston-meta.mojang.com and parses the proguard format.
    export function getMappings(version: string): Promise<ParsedMappings> {
      const cached = mappingCache.get(version);
      if (cached) return cached;
      const promise = fetchAndParse(version).catch((err) => {
        mappingCache.delete(version); // don't poison cache on transient failure
        throw err;
      });
      mappingCache.set(version, promise);
      return promise;
    }
  • searchMojangMappings() - Search function used by the tool handler. Performs substring-and-token search across parsed mappings for classes, methods, and fields with ranking by exact/prefix/substring match.
    export function searchMojangMappings(
      parsed: ParsedMappings,
      query: string,
      opts: { limit?: number; type?: "class" | "method" | "field" | "all" } = {},
    ): MojangSearchHit[] {
      const limit = opts.limit ?? 25;
      const type = opts.type ?? "all";
      const q = query.toLowerCase();
      const hits: MojangSearchHit[] = [];
    
      if (type === "class" || type === "all") {
        for (const [mapped, obf] of parsed.classes) {
          const simple = mapped.split(".").pop() ?? mapped;
          if (mapped.toLowerCase().includes(q) || simple.toLowerCase().includes(q)) {
            hits.push({ type: "class", owner: mapped, obfuscated: obf });
            if (hits.length >= limit * 4) break;
          }
        }
      }
      if (type === "method" || type === "field" || type === "all") {
        outer: for (const [owner, bucket] of parsed.members) {
          if (type === "method" || type === "all") {
            for (const m of bucket.methods) {
              if (m.mappedName.toLowerCase().includes(q)) {
                hits.push({
                  type: "method",
                  owner,
                  name: m.mappedName,
                  obfuscated: m.obfName,
                  signature: `${m.mappedReturn} ${m.mappedName}(${m.mappedParams.join(", ")})`,
                });
                if (hits.length >= limit * 4) break outer;
              }
            }
          }
          if (type === "field" || type === "all") {
            for (const f of bucket.fields) {
              if (f.mappedName.toLowerCase().includes(q)) {
                hits.push({
                  type: "field",
                  owner,
                  name: f.mappedName,
                  obfuscated: f.obfName,
                  signature: f.mappedType,
                });
                if (hits.length >= limit * 4) break outer;
              }
            }
          }
        }
      }
    
      // Rank: exact-name > prefix > substring; classes and members interleave by score.
      const score = (h: MojangSearchHit): number => {
        const n = (h.name ?? h.owner.split(".").pop() ?? "").toLowerCase();
        if (n === q) return 1000;
        if (n.startsWith(q)) return 500;
        return 100 - Math.abs(n.length - q.length);
      };
      hits.sort((a, b) => score(b) - score(a));
      return hits.slice(0, limit);
    }
  • parseProguard() - Parses Mojang's proguard-format client_mappings.txt into ParsedMappings (classes Map and members Map). The proguard format maps Mojang names → obfuscated names.
    export function parseProguard(version: string, text: string): ParsedMappings {
      const classes = new Map<string, string>();
      const members = new Map<string, ClassMembers>();
      let currentClass: string | null = null;
      let currentBucket: ClassMembers | null = null;
    
      // We split lines once. Files are large (multi-MB) but this is fine in Node.
      const lines = text.split("\n");
      for (let i = 0; i < lines.length; i++) {
        const raw = lines[i];
        if (!raw || raw.startsWith("#")) continue;
    
        if (raw[0] !== " " && raw[0] !== "\t") {
          // Class line:  "<mapped> -> <obf>:"
          const m = raw.match(/^([^\s]+) -> ([^:]+):\s*$/);
          if (!m) continue;
          currentClass = m[1];
          const obf = m[2];
          classes.set(currentClass, obf);
          currentBucket = { fields: [], methods: [] };
          members.set(currentClass, currentBucket);
          continue;
        }
    
        if (!currentBucket || !currentClass) continue;
        const body = raw.trimStart();
        // Method:  "12:120:void name(int,int) -> a"  OR  "void name(int,int) -> a"
        const methodMatch = body.match(
          /^(?:(\d+):(\d+):)?([^\s]+) ([^\s(]+)\(([^)]*)\) -> ([^\s]+)$/,
        );
        if (methodMatch) {
          const [, start, end, retType, name, paramsRaw, obf] = methodMatch;
          const params = paramsRaw ? paramsRaw.split(",") : [];
          currentBucket.methods.push({
            mappedName: name,
            mappedReturn: retType,
            mappedParams: params,
            obfName: obf,
            lineRange: start && end ? `${start}:${end}` : undefined,
          });
          continue;
        }
        // Field:  "int experience -> bM"
        const fieldMatch = body.match(/^([^\s]+) ([^\s]+) -> ([^\s]+)$/);
        if (fieldMatch) {
          currentBucket.fields.push({
            mappedType: fieldMatch[1],
            mappedName: fieldMatch[2],
            obfName: fieldMatch[3],
          });
        }
      }
      return { version, classes, members };
    }
  • Import of types/functions used by mc_mojang_mappings: getMappings, searchMojangMappings, reverseMojangLookup, listMojangVersions, MojangVersionNotFoundError, MojangNoMappingsError from mojang.ts.
    import {
      getMappings,
      searchMojangMappings,
      reverseMojangLookup,
      listMojangVersions,
      MojangVersionNotFoundError,
      MojangNoMappingsError,
    } from "./mojang.js";
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description fully discloses key behaviors: first call downloads a multi-MB mapping file and caches it in-process, subsequent calls are instant. It also notes linkie's limitations. No contradictions.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four sentences, each adds value. Front-loaded with core function. Could be slightly more concise, but no redundancy.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Covers the complex multi-step process (download, parse, search) and caching. However, no output schema and description omits return format, which is a minor gap. Otherwise complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3. The description adds context about version-specific behavior (caching) but does not individually elaborate on each parameter. Adequate but not enhanced beyond schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool bypasses linkie to fetch and parse Mojang's client_mappings.txt from piston-meta, then searches it. It uses specific verbs ('bypasses', 'fetches', 'parses', 'searches') and distinguishes itself from the linkie alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use the tool: for versions linkie can't load (especially 26.1 lineage) and for verifying names against Mojang directly. It provides clear context and implicit alternatives.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ratph6/mc-mod-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server