Files
twenty/packages/twenty-shared
Etienne 5ebcce0a51 feat(ai-tool): resolve and default icons in AI metadata tools (#23480)
## Context

Objects and fields created through the AI chat / MCP metadata tools
almost never get an icon, so they all render with the meaningless `123`
fallback icon. Two causes:

- The `icon` tool input was described only as `"Icon name"`, so the
model had no idea what the value space is and mostly skipped an optional
field it couldn't fill confidently.
- Any invalid name is silently swapped for `Icon123` by
`useIcons.getIcon` on the frontend, so near-misses were
indistinguishable from unset.

## What this PR does

**Guide the model** (icon names are Tabler names, which LLMs know well):
- `icon` / `targetFieldIcon` schema descriptions now state the
convention with examples (`IconBuildingSkyscraper`, `IconPaw`, …) and
ask for one to always be set
- The `metadata-building` skill gains an "Icons" section; the MCP server
instructions gain a one-line reminder

**Normalize server-side** (new `resolveIconName` util, used by all
create/update/batch metadata tool executes incl.
`relationCreationPayload.targetFieldIcon`):
- Fixes shape mistakes: raw tabler slugs (`"building-skyscraper"`),
separators, missing or lowercased `Icon` prefix
- Deliberately does NOT validate existence against the full ~4.2k icon
registry — an unknown name is harmless since the frontend falls back to
its default icon, exactly as for icons stored via the API today
- Unusable input (empty/garbage) resolves to nothing: creates fall back
to a default, updates keep the existing icon

**Fall back sensibly for fields**:
- New `FIELD_TYPE_DEFAULT_ICONS` in `twenty-shared/constants` maps every
`FieldMetadataType` to a sensible icon (mirroring the settings UI type
illustrations), applied when the model provides no usable icon — an
AI-created field always gets a meaningful icon
- Lives in twenty-shared so the frontend can reuse it later (e.g. as
`getIcon`'s custom default for fields)

The REST/GraphQL metadata APIs are untouched — this only affects the AI
tool layer.

## Test plan

- `resolve-icon-name.util.spec.ts` — canonical pass-through,
slug/prefix/separator fixes, unknown-name pass-through (FE fallback
contract), unusable inputs, icon-key dropping on updates
- `FieldTypeDefaultIcons.test.ts` — every field type mapped, all values
canonically shaped (values hand-checked against the twenty-ui
`ALL_ICONS` registry)
- `nx typecheck` twenty-server + twenty-shared, oxlint/oxfmt clean on
changed files

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23480?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-07-29 09:39:55 +00:00
..