feat(inventory): expose NBT/component data in inventory MCP tools

Add a nullable `Nbt` field to `MccInventorySnapshotSlot`,
`MccInventorySearchMatch`, and `MccItemStackSnapshot` so that callers
can access the full item metadata beyond just material and count.

For 1.20.6+ servers the field is a map of component name
(e.g. "minecraft:custom_data") to the serialized component object.
For legacy servers it is the raw NBT dictionary. The field is omitted
entirely for vanilla items that carry no extra data, so there is no
noise for plain items like Stone or Dirt.

Implementation:
- `StructuredComponent`: add `ComponentName` property (set by the
  registry during parse) and mark both it and `TypeId` with
  `[JsonIgnore]` so they are excluded from component serialization.
- `StructuredComponentRegistry.ParseComponent`: assign `ComponentName`
  after instantiation.
- `MccGameCommon.BuildNbt`: new helper that serializes components by
  runtime type (fixing the polymorphism issue with System.Text.Json)
  keyed by component name, falling back to the legacy NBT dictionary.
- Snapshot and search query builders updated to call `BuildNbt`.
This commit is contained in:
Ítalo Seara 2026-06-27 14:41:41 -03:00
parent 95031338bf
commit f34b8ba989
5 changed files with 33 additions and 3 deletions

View file

@ -795,7 +795,8 @@ public sealed class MccGameApi
{
Slot = item.Key,
Type = item.Value.Type.ToString(),
Count = item.Value.Count
Count = item.Value.Count,
Nbt = MccGameCommon.BuildNbt(item.Value)
})
.ToArray();
@ -856,7 +857,8 @@ public sealed class MccGameApi
TypeLabel = pair.Value.GetTypeString(),
Count = pair.Value.Count,
IsPlayerInventory = entry.Key == 0,
HotbarSlot = isHotbar ? hotbar + 1 : null
HotbarSlot = isHotbar ? hotbar + 1 : null,
Nbt = MccGameCommon.BuildNbt(pair.Value)
};
});
})

View file

@ -35,6 +35,7 @@ public sealed class MccItemStackSnapshot
{
public required string Type { get; init; }
public required int Count { get; init; }
public Dictionary<string, object>? Nbt { get; init; }
}
/// <summary>
@ -177,10 +178,26 @@ public static class MccGameCommon
return new MccItemStackSnapshot
{
Type = item.Type.ToString(),
Count = item.Count
Count = item.Count,
Nbt = BuildNbt(item)
};
}
public static Dictionary<string, object>? BuildNbt(Item item)
{
if (item.Components is { Count: > 0 })
{
return item.Components
.Where(c => !string.IsNullOrEmpty(c.ComponentName))
.ToDictionary(
c => c.ComponentName,
c => (object)System.Text.Json.JsonSerializer.SerializeToElement(c, c.GetType())
);
}
return item.NBT is { Count: > 0 } ? item.NBT : null;
}
public static bool TryParseBlockQuery(string? query, out int? blockId, out int? blockMeta)
{
blockId = null;

View file

@ -216,6 +216,7 @@ public sealed class MccInventorySnapshotSlot
public required int Slot { get; init; }
public required string Type { get; init; }
public required int Count { get; init; }
public Dictionary<string, object>? Nbt { get; init; }
}
public sealed class MccInventorySnapshotResult
@ -239,6 +240,7 @@ public sealed class MccInventorySearchMatch
public required int Count { get; init; }
public required bool IsPlayerInventory { get; init; }
public int? HotbarSlot { get; init; }
public Dictionary<string, object>? Nbt { get; init; }
}
public sealed class MccInventorySearchResult