platform-custom-metadata-type-generate

Use this skill when users need to create, generate, or validate Salesforce Custom Metadata Type metadata — the __mdt object, its fields, and its deployable records. Trigger when users mention custom metadata types, CMDT, __mdt objects, custom metadata records, .md-meta.xml files, or reference/config

By forcedotcom · 693 installs

npx skills add forcedotcom/sf-skills --skill platform-custom-metadata-type-generate

Source repository · Upstream listing

Salesforce Custom Metadata Type Generator and Validator When to Use This Skill Use this skill when you need to: Create a Custom Metadata Type ( mdt ) Generate or validate CMDT fields, including MetadataRelationship fields Generate CMDT records as deployable .md meta.xml files Troubleshoot CMDT deployment errors 1. Overview and Purpose A Custom Metadata Type produces two separate artifact families , and most requests need both: Artifact Path Metadata type Type definition objects/<Name mdt/<Name mdt.object meta.xml CustomObject Fields objects/<Name mdt/fields/<Field c.field meta.xml CustomField Records customMetadata/<Name .<Record .md meta.xml CustomMetadata API name suffix: mdt on the type; fields still end in c . The defining advantage over a custom setting: CMDT records are metadata and therefore deploy between orgs. When a user says configuration should "ship with the package" or "be the same in every org," CMDT is the right answer. The root element is <CustomObject , but almost none of a custom object's rules apply. sharingModel , nameField , and deploymentStatus are required or normal on a regular custom object and are hard errors here. Do not carry assumptions across from platform custom object generate . 2. Syntactic Essentials — Type Definition (Tier 1) Required and Allowed Elements Element Requirement Notes <label Required Singular UI name <pluralLabel Required Omitting it gives Must specify a non empty plural label for the CustomObject <visibility Always include Public , or Protected / PackageProtected only in dev/sandbox/scratch (Section 6) <description Always include What this type configures and who owns it <pluralLabel being required here but forbidden on a custom setting is the most commonly inverted rule between the two families. Neither failure mentions the other family's rule. Forbidden Elements Every element below produces Cannot specify: <element for Custom Metadata Type — reusing a regular custom object's skeleton (with sharingModel , deploymentStatus , a nameField block, or enableSearch ) is the usual cause: sharingModel , nameField , deploymentStatus , enableActivities , enableReports , enableHistory , enableSearch CORRECT — minimum valid mdt type: 3. Field Rules This skill owns the CMDT specific deltas only — the type allowlist, fieldManageability , and MetadataRelationship below. For generic field mechanics ( <fullName derivation, <label , <description , <inlineHelpText , precision/scale, <length , visibleLines ), follow platform custom field generate . Supported Field Types Checkbox , Date , DateTime , Email , Number , Percent , Phone , Picklist , Text , TextArea , LongTextArea , Url , plus MetadataRelationship . Unsupported Field Types Currency , AutoNumber , MasterDetail , Summary , Location , Time , EncryptedText , Html , MultiselectPicklist . Each fails with a precise, well formed error: (There is no trailing period.) Example: Type Currency of CustomMetadataField Partner Tier mdt.Discount c is not supported for the Entity Partner Tier mdt Currency is the common trap — it works on a custom setting but not here. Use Number with <precision / <scale and put the currency in the label or help text. Formula fields are unsupported , but they break the pattern above. A <formula element on an otherwise legal type gives only: Lookup is silently coerced — CRITICAL <type Lookup</type on a mdt does not fail when its referenceTo resolves to a real sObject: the deploy is green and the platform silently rewrites the field to MetadataRelationship . A later retrieve shows a field the user never wrote: Always write MetadataRelationship explicitly. Emitting Lookup produces a green deploy and a source vs org mismatch that churns in git the first time anyone retrieves. (Reproduced on a live deploy: a resolvable Lookup returns as MetadataRelationship ; a non resolvable referenceTo is cleanly rejected.) MetadataRelationship Three targets, all valid. referenceTo selects which. <referenceTo Purpose Extra requirement Another mdt type Link two custom metadata types Must be a different type EntityDefinition Point at an sObject None FieldDefinition Point at a field Requires <metadataRelationshipControllingField The controlling field must be an EntityDefinition relationship on the same type , referenced as Type mdt.Field c . Omitting it gives Metadata relationships to Field Definition require a controlling field. Self references are impossible. Pointing a MetadataRelationship at its own parent type fails with Cannot add a self lookup relationship child with cascade or restrict options to the object itself (verbatim from a live mdt deploy) — use a second type. <fieldManageability Optional. It defaults to DeveloperControlled — do not add it unless the user wants a different value. Valid values are DeveloperControlled , SubscriberControlled , and Locked . It is valid only on mdt fields. On a regular custom object field or a custom setting field: 4. CMDT Records ( .md meta.xml ) Filename convention Write customMetadata/<TypeNameWithout mdt .<RecordDeveloperName .md meta.xml . The mdt suffix in the filename also deploys and creates a real record, so it is tolerated — but Salesforce's retrieve normalizes to the no suffix form, so always write it without mdt to avoid git churn (detail in references/cmdt records.md ). Required namespaces All three are mandatory on the root element — xsi:type on values will not resolve without them. xsi:type mapping — every value needs one Field type Correct xsi:type Example value Checkbox xsd:boolean true Date xsd:date 2024 01 15 DateTime xsd:dateTime 2024 01 15T10:30:00.000Z Number, Percent xsd:double 42.0 Text, TextArea, LongTextArea xsd:string some text Email, Phone, Url xsd:string a@b.com Picklist xsd:string Beta MetadataRelationship → EntityDefinition xsd:string Account MetadataRelationship → FieldDefinition xsd:string Account.Name any type, null no xsi:type — write <value xsi:nil="true"/ A value pointing at another mdt type via MetadataRelationship was not verified — expect xsd:string with the target's DeveloperName, but confirm. xsd:picklist must not be emitted — CRITICAL Salesforce's own documentation tells you to use xsd:picklist for Picklist fields. It is wrong — it is not a valid XML Schema type. Instead of a clean validation error, the platform fails the entire deploy server side with no component level diagnostics, taking every other component down with it. Always emit xsd:string for a Picklist field's value; never xsd:picklist . See references/cmdt records.md §2. Record body rules <label is required . Omitting it gives Required fields are missing: [MasterLabel] — note it reports the sObject field name MasterLabel , not label . <protected is optional and defaults to false . Never put <fullName inside a <values block — it is a hard parse error. <value xsi:nil="true"/ works on optional fields. On a required field it is rejected exactly as if the field were absent. Omitting xsi:type on a non nil value is always fatal, including for text fields. CORRECT — a complete record: Record DeveloperName rules The name is the second segment of the filename. It must begin with a letter, contain only alphanumerics and underscores, not end with an underscore, not contain two consecutive underscores, and be at most 40 characters . Violations give: Over 40 characters gives Value too long for field: fullName maximum length is:40 (no space after the colon). Deploy ordering No ordering constraint — the type and every referenced field only need to resolve (already in the org, or present in the same deploy). Two failures tell you what is missing: Custom metadata type <Type mdt is not available in this organization. (the type is missing) and <Type mdt: could not find fields: <Field c (the type exists but the field does not). → Full record value error catalog and the xsi:type coercion asymmetry: references/cmdt records.md . Generating records from user input Record data arrives three ways; in each case emit one <Type .<Record .md meta.xml per row , mapping columns to <field / <value pairs with the right xsi:type : Inline list/table — map columns straight to fields. CSV — header = field API names (or map labels to them), each line = one record; take the DeveloperName from a key column. Prose — infer the rows ("Bronze at 5%, Gold at 15%") and confirm the field mapping before generating. Derive every DeveloperName deterministically from the key column or label — never free hand one. For each row, run scripts/sanitize developer name.sh "<LABEL " <ROW before writing its record file (same input → same valid name; empty/all symbol/non Latin labels fall back to Record <ROW ). The transform is many to one — United States and United States both yield United States , and since each record is <Type .<DeveloperName .md meta.xml , a collision silently overwrites a row. After deriving the whole batch, check duplicates before writing ; on any collision stop and report the colliding source labels rather than overwrite (append a deterministic 2 / 3 suffix only if the user asks to auto resolve). Ordered algorithm, ASCII only limitation, uniqueness check, and worked examples: references/cmdt records.md §7 . Consuming records in Apex Records are read through the generated typed class — no SOQL, no query rows consumed , safe in loops and triggers: Accessor truncation — CRITICAL for LongTextArea . getAll() / getInstance() return only the first 255 characters of any field; longer LongTextArea values are silently truncated for those callers and must be read via SOQL ( SELECT ... FROM Partner Tier mdt ). Say so whenever you put a LongTextArea on a CMDT. 5. Choosing the Right Component Before generating, confirm a CMDT is what the user needs. If the config is… Use Why Reference data that must deploy between orgs with its records CMDT (this skill) Records are metadata An admin maintained mapping / lookup / crosswalk table (field↔field, code↔code, "map A to B") CMDT (this skill) Editable reference data, one record per pair Admin editable per profile/user, or org local platform custom setting generate Values are data and stay in one org Business records users create and edit at runtime platform custom object generate CMDT records are not transactional data Translatable UI text Custom Label — not generated here Labels are the translation surface Credentials, API keys, tokens Named Credential / External Credential — not generated here See Section 6 A permission check in Apex or a flow Custom Permission — not generated here Boolean access belongs in the permission model The distinction that matters most: CMDT records deploy, custom setting values do not. If the user needs admins to edit values per profile at runtime, that is a custom setting, not a CMDT. Field mapping and lookup tables are a canonical CMDT use case. When a user asks to "map fields from A to B," maintain a code to code lookup, or keep a crosswalk admins can edit, model it as a CMDT — one record per pair (e.g. Lead Field c / Target Field c , or MetadataRelationship fields to FieldDefinition ) — never a hardcoded Apex Map , constant, or Flow decision. Phrasing like "for conversion" or "for our i