version: "1.0" author: Klemens Stelk
⚠️ MANDATORY: Read this skill BEFORE using any MCP tools to interact with CRM/Dataverse records. Do not call get-record, query-records, create-record, update-record, or delete-record until you've reviewed this guidance.
Dataverse CRUD Operations
Field Type Reference
| Field Type | How to Set | Example |
|---|---|---|
| Lookup (single) | fieldname@odata.bind | "si_titleid@odata.bind": "/si_titles(guid)" |
| Polymorphic Lookup | fieldname_entity@odata.bind | "si_customerid_contact@odata.bind": "/contacts(guid)" |
| Option Set | Integer value | "sic_joiningreasonsource": 157430003 |
| Two Option (Boolean) | true or false | "donotemail": false |
| DateTime | ISO 8601 string | "birthdate": "1990-05-15" |
| Money | Decimal number | "revenue": 50000.00 |
| Whole/Decimal Number | Integer or Decimal | "numberofemployees": 150 |
| Text | String | "firstname": "John" |
Critical Gotchas
Lookup Fields: Logical Names Work
Both logical names (lowercase) and SchemaNames work—use field names directly from get-record output:
{ "si_titleid@odata.bind": "/si_titles(guid)" }
No need to call get-lookup-target for setting lookups.
Polymorphic Lookups Need Entity Suffix
When a lookup can point to multiple entity types:
{ "si_customerid_contact@odata.bind": "/contacts(guid)" }
{ "si_customerid_account@odata.bind": "/accounts(guid)" }
Read-Only Fields (Never Set These)
- •
createdon,modifiedon,createdby,modifiedby - •
versionnumber,@odata.etag - •Calculated/rollup fields,
fullnameon Contact - •
ownerid,owninguser,owningbusinessunit - •Fields ending in
_base(base currency) - •Fields starting with
_and ending with_value(use@odata.bindinstead)
Option Set Values Are Integers
Find values from existing records or metadata—not labels.
Efficient Workflows
Clone a Record
- •Extract record ID from URL
- •
get-recordto retrieve original - •
create-recordwith field values (exclude read-only, modify as requested) - •If related records needed, create and link via lookups
Avoid: get-entity-metadata, get-lookup-target (not needed)
Create New Record with Specific Values
- •Identify required lookups (e.g., "title Mr." = need si_title record)
- •
query-recordson reference table to find record GUID - •
create-recordwithfieldname@odata.bindsyntax
Update Existing Record
- •Extract record ID from URL
- •
update-recordwith changed fields only (partial update)
Link Records via Lookup
Update the entity with the lookup field:
{ "si_emailid@odata.bind": "/si_communicationchannels(email-guid)" }
Common Patterns
Finding Reference Data
// 1. Query reference table
query-records: si_titles, filter: "contains(si_name, 'Mr')"
// 2. Use GUID in create
{ "si_titleid@odata.bind": "/si_titles(<guid>)" }
Option Set Values
// Get existing record with correct value set, note the integer
get-record: existing contact with "Events" selected
// Use integer in create
{ "sic_joiningreasonsource": 157430003 }
Error Solutions
| Error | Solution |
|---|---|
| "Cannot insert duplicate key" | Check unique fields (email, external ID, code) |
| "Lookup attribute is invalid" | Verify entity set name and GUID format |
| "Cannot update read-only field" | Remove from payload (ownerid, createdon, etc.) |
| "Required field missing" | Check entity definition or error details |
| "Invalid type for property" | Check field type (string vs int vs boolean) |
Pre-Create/Update Checklist
- • Excluded read-only fields
- • Integer values for option sets (not labels)
- • Entity suffix for polymorphic lookups (
_contact,_account) - • Boolean fields use
true/false(not 0/1) - • DateTime in ISO 8601 format
- • Lookups use
@odata.bindsyntax