Skip to main content
Glama

Search Minecraft mappings (live, via linkie)

mc_mappings_search

Search class, method, or field names across any Minecraft version and mappings namespace. Uses live full-text search from linkie's database.

Instructions

Live full-text search against linkie's mappings database. Returns class/method/field results for ANY (namespace, version) combination linkie carries — no curation gap, no stale snapshot. Use this whenever the user asks 'what is X called in ?' or 'find all methods named foo in 1.21.11 yarn'. For 26.1.x, linkie currently 500s on the mojang_raw namespace — use mc_mojang_mappings instead for those versions.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryYesClass/method/field name (or substring) to search for
namespaceYesMappings namespace. yarn/quilt-mappings for Fabric, mojang for NeoForge, mojang_raw for Mojang's shipped names, mcp/legacy-yarn/feather for 1.8.x, etc.
versionYesMinecraft version id, e.g. '1.21.11', '1.8.9', '26.1.2'
typeNoFilter by entry type. Default all.
limitNoMax hits. Default 25.

Implementation Reference

  • src/index.ts:681-757 (registration)
    Registration of the 'mc_mappings_search' tool with server.registerTool, including its inputSchema (query, namespace, version, type, limit) and the handler function that calls linkieSearch to perform full-text search against linkie's mappings API and formats results.
    server.registerTool(
      "mc_mappings_search",
      {
        title: "Search Minecraft mappings (live, via linkie)",
        description:
          "Live full-text search against linkie's mappings database. Returns " +
          "class/method/field results for ANY (namespace, version) combination " +
          "linkie carries — no curation gap, no stale snapshot. Use this " +
          "whenever the user asks 'what is X called in <specific version>?' or " +
          "'find all methods named foo in 1.21.11 yarn'. For 26.1.x, linkie " +
          "currently 500s on the mojang_raw namespace — use mc_mojang_mappings " +
          "instead for those versions.",
        inputSchema: {
          query: z.string().describe("Class/method/field name (or substring) to search for"),
          namespace: LinkieNamespaceSchema.describe(
            "Mappings namespace. yarn/quilt-mappings for Fabric, mojang for NeoForge, " +
              "mojang_raw for Mojang's shipped names, mcp/legacy-yarn/feather for 1.8.x, etc.",
          ),
          version: z.string().describe("Minecraft version id, e.g. '1.21.11', '1.8.9', '26.1.2'"),
          type: z.enum(["class", "method", "field", "all"]).optional().describe("Filter by entry type. Default all."),
          limit: z.number().int().positive().max(100).optional().describe("Max hits. Default 25."),
        },
      },
      async ({ query, namespace, version, type, limit }) => {
        let hits: LinkieHit[];
        try {
          hits = await linkieSearch({ namespace, version, query, type, limit });
        } catch (err) {
          if (err instanceof LinkieNotIngestedError) {
            const isPost1x = /^26\./.test(err.version);
            const fallback = isPost1x
              ? `**Note**: linkie's mojang_raw can't load 26.x because Mojang stopped shipping client_mappings.txt. ` +
                `Calling mc_mojang_mappings will return the same dead-end. ` +
                `Workaround: mc_list_versions to find a namespace that *has* ingested ${err.version}, ` +
                `or use mc_lookup_class for stable concept-level translations.`
              : `Try mc_mojang_mappings(version="${err.version}", query="${query}") for the Mojang official names.`;
            return text(
              `linkie advertises ${err.namespace}/${err.version} but its search backend ` +
                `hasn't ingested it yet:\n\n    ${err.detail}\n\n${fallback}`,
            );
          }
          return text(`Search failed: ${(err as Error).message}`);
        }
        if (hits.length === 0) {
          return text(
            `No hits for "${query}" in ${namespace}/${version}. ` +
              `Check the version exists in this namespace via mc_list_versions.`,
          );
        }
        const lines: string[] = [
          `# ${hits.length} hit${hits.length === 1 ? "" : "s"} in ${namespace}/${version} for "${query}"`,
          "",
        ];
        for (const h of hits) {
          if (h.type === "class") {
            lines.push(`- **class** \`${h.mapped}\``);
            if (h.intermediary && h.intermediary !== h.mapped) lines.push(`    intermediary: \`${h.intermediary}\``);
            if (h.obfuscated) lines.push(`    obfuscated: \`${h.obfuscated}\``);
          } else {
            const decoded = h.descriptor
              ? h.type === "method"
                ? decodeMethodDescriptor(h.descriptor)
                : decodeFieldDescriptor(h.descriptor)
              : null;
            const sigBits = [
              decoded ? `\`${decoded}\`` : null,
              h.params && h.params.length ? `param names \`(${h.params.join(", ")})\`` : null,
            ].filter(Boolean);
            lines.push(`- **${h.type}** \`${h.owner ?? "?"}#${h.mapped}\`${sigBits.length ? "  " + sigBits.join(" · ") : ""}`);
            if (h.descriptor) lines.push(`    raw descriptor: \`${h.descriptor}\``);
            if (h.intermediary && h.intermediary !== h.mapped) lines.push(`    intermediary: \`${h.intermediary}\``);
            if (h.obfuscated) lines.push(`    obfuscated: \`${h.obfuscated}\``);
          }
        }
        return text(lines.join("\n"));
      },
    );
  • The linkieSearch function (exported as 'search') that performs the actual HTTP request to the linkie API, parses the response, and returns structured LinkieHit objects. This is the core helper invoked by the mc_mappings_search handler.
    /** Search linkie. Returns structured hits or throws on transport errors. */
    export async function search(opts: {
      namespace: string;
      version: string;
      query: string;
      limit?: number;
      /** Filter to one of class/method/field; "all" by default. */
      type?: "class" | "method" | "field" | "all";
      /** Allow fuzzy results. */
      allowFuzzy?: boolean;
    }): Promise<LinkieHit[]> {
      const { namespace, version, query } = opts;
      const limit = opts.limit ?? 25;
      const cacheKey = `${namespace}|${version}|${query}|${opts.type ?? "all"}|${limit}|${opts.allowFuzzy ? 1 : 0}`;
      // 1-hour TTL for search results — same query + version is stable for hours
      // at a time. The model retries the same lookups across turns.
      const data = await cached<LinkieSearchResponse | { _notIngested: { namespace: string; version: string; detail: string } }>(
        "linkie-search",
        cacheKey,
        60 * 60 * 1000,
        async () => {
          const url = new URL(`${LINKIE_API_BASE}/api/search`);
          url.searchParams.set("namespace", namespace);
          url.searchParams.set("version", version);
          url.searchParams.set("query", query);
          url.searchParams.set("limit", String(limit));
          if (opts.allowFuzzy) url.searchParams.set("allowFuzzy", "true");
          const res = await fetch(url);
          if (!res.ok) {
            const body = await res.text();
            if (res.status === 500 && /Failed to load/.test(body)) {
              // Cache the not-ingested response — it stays not-ingested until linkie
              // does a backend refresh, no point pinging again every minute.
              return { _notIngested: { namespace, version, detail: body.trim() } };
            }
            throw new Error(`linkie /api/search: HTTP ${res.status} — ${body.slice(0, 160)}`);
          }
          return (await res.json()) as LinkieSearchResponse;
        },
      );
    
      if ("_notIngested" in data) {
        throw new LinkieNotIngestedError(data._notIngested.namespace, data._notIngested.version, data._notIngested.detail);
      }
      const filterT =
        opts.type === "class" ? "c" :
        opts.type === "method" ? "m" :
        opts.type === "field"  ? "f" : null;
    
      const hits: LinkieHit[] = [];
      for (const e of data.entries) {
        if (filterT && e.t !== filterT) continue;
        if (e.t === "c") {
          hits.push({
            type: "class",
            score: e.z,
            mapped: dotted(e.n ?? e.i ?? e.o ?? "?"),
            intermediary: e.i ? dotted(e.i) : undefined,
            obfuscated: e.o ? dotted(e.o) : undefined,
          });
        } else {
          hits.push({
            type: e.t === "m" ? "method" : "field",
            score: e.z,
            mapped: e.n ?? e.i ?? e.o ?? "?",
            intermediary: e.i,
            obfuscated: e.o,
            owner: e.c ? dotted(e.c) : e.b ? dotted(e.b) : e.a ? dotted(e.a) : undefined,
            descriptor: e.f ?? e.e ?? e.d,
            params: e.p ? Object.keys(e.p).sort((x, y) => Number(x) - Number(y)).map((k) => e.p![k]) : undefined,
          });
        }
      }
      hits.sort((a, b) => b.score - a.score);
      return hits;
    }
  • The LinkieHit interface definition, which is the return type schema used by the mc_mappings_search tool handler to format search results.
    export interface LinkieHit {
      type: "class" | "method" | "field";
      score: number;
      /** Mapped (named) form — what the user reads. */
      mapped: string;
      /** Intermediary (Yarn intermediary or hashed) form — runtime-stable. */
      intermediary?: string;
      /** Obfuscated form. */
      obfuscated?: string;
      /** For method/field: owner class FQN (mapped). */
      owner?: string;
      /** Method/field descriptor in mapped form. */
      descriptor?: string;
      /** Method parameter names (mapped). */
      params?: string[];
    }
  • The LinkieNamespace interface used in the tool's input validation (namespace + version list).
    export interface LinkieNamespace {
      id: string;
      versions: { version: string; stable: boolean }[];
    }
  • The LinkieNamespaceSchema (z.enum) used for input validation of the 'namespace' parameter in mc_mappings_search and other tools.
    const LinkieNamespaceSchema = z.enum([
      "yarn",
      "mojang",
      "mojang_raw",
      "mojang_hashed",
      "mojang_srg",
      "quilt-mappings",
      "mcp",
      "legacy-yarn",
      "yarrn",
      "plasma",
      "barn",
      "feather",
    ]);
Behavior4/5

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

With no annotations, description reveals it's a live search, always up-to-date, and flags a current 500 error for 26.1.x mojang_raw. Could be improved by mentioning return format or pagination, but adequately covers behavioral traits.

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

Conciseness5/5

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

Three sentences, each earning its place: purpose, usage, limitation. No redundancy, well front-loaded.

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?

Captures key aspects: search scope, namespace mapping, version specificity, and a known issue. Lacks description of response format or pagination, but sufficient for most use cases given tool purpose.

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

Parameters4/5

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

Schema already covers all params (100%). Description adds contextual mapping for namespaces (e.g., 'yarn for Fabric') and provides examples for version and type, enhancing understanding 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?

Clearly states it's a live full-text search against linkie's mappings database, returning class/method/field results. Distinguishes from siblings by emphasizing no curation gap and no stale snapshot.

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 tells when to use (specific mapping queries) and when NOT to use (26.1.x mojang_raw, directing to mc_mojang_mappings). Provides concrete example queries for clarity.

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