-
Notifications
You must be signed in to change notification settings - Fork 0
Tools Core
This page is generated from the code by scripts/gen_wiki_tools.py — do not edit by hand.
24 tools · category token core · enable with DATAVERSE_TOOLS=core (unset enables every category)
Write tools additionally require DATAVERSE_ALLOW_WRITE=true; delete tools require DATAVERSE_ALLOW_DELETE=true.
Read · idempotent · category core
Group and aggregate Dataverse records with an OData $apply expression.
Use this for per-group questions (e.g. count by status, sum revenue by region). For a single total count use dataverse_count_records; for raw rows use dataverse_query_table. Works on up to 50,000 records. See the apply parameter for expression examples.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| apply | str |
yes | — | See note below. |
| filter | str | None |
no | None |
OData $filter expression to narrow records before aggregation. |
- apply — OData $apply expression. Examples: "groupby((statecode),aggregate($count as total))" — count rows by status; "groupby((statecode),aggregate(accountid with countdistinct as total))" — distinct count; "aggregate(revenue with sum as total_revenue)" — sum a column; "groupby((statuscode))" — distinct values. Use 'countdistinct' not 'count'. Lookup fields cannot be used in groupby. Works on up to 50,000 records. (min_len=1)
Returns {"records": ..., "count": ...}. Errors return {"error": true, "message": "..."}.
Write · idempotent · category core
Associate two Dataverse records via a collection-valued navigation property.
Navigation property names are case-sensitive — use dataverse_list_relationships to discover the correct name. For the reverse operation use dataverse_disassociate_records. Requires DATAVERSE_ALLOW_WRITE=true.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the primary record's table (e.g., 'accounts'). Use dataverse_get_entity_sets to discover. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| record_id | str |
yes | — | GUID of the primary record. (min_len=36) |
| navigation_property | str |
yes | — | Collection-valued navigation property name on the primary entity (case-sensitive). Use dataverse_list_relationships to discover the correct name (e.g., 'contact_customer_accounts'). (min_len=1) |
| related_entity_set_name | str |
yes | — | OData collection name of the related record's table (e.g., 'contacts'). (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| related_record_id | str |
yes | — | GUID of the related record to associate. (min_len=36) |
Returns {"success": ...}. Errors return {"error": true, "message": "..."}.
Write · idempotent · category core
Upsert many records in one call using OData $batch PATCH operations.
Each record is PATCHed individually (not in a change set) so a single bad row does not roll back the rest when continue_on_error=true. Records are chunked into requests of up to chunk_size operations each.
Key detection:
- Provide key_columns to upsert by alternate key (e.g., key_columns=['accountnumber'] → PATCH accounts(accountnumber='AN001')).
- Omit key_columns to upsert by primary GUID — the tool detects the first GUID-valued field in each record and uses it as the primary key.
Returns per-row outcomes (created, updated, or failed) with row index and id. Requires DATAVERSE_ALLOW_WRITE=true.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the target table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| records | list[dict[str, Any]] |
yes | — | List of records to upsert. Each record is a dict of column→value pairs and must include the key field(s). Maximum 1,000 records per call. (min_len=1) |
| key_columns | list[str] | None |
no | None |
Alternate key column names used to build the upsert URL. When provided, the URL is built as entity_set_name(col1='val1',col2='val2') and these fields are removed from the PATCH body. When omitted, the tool looks for a GUID-valued field in each record and uses it as the primary key. (min_len=1) |
| continue_on_error | bool |
no | True |
When True, a failed row does not stop the batch — remaining rows are still attempted and each row gets its own outcome in the results. |
| chunk_size | int |
no | 100 |
Number of records per $batch request. Keep ≤1,000 (the Dataverse batch limit). Smaller values reduce timeout risk on slow environments. (ge=1, le=1000) |
Returns {"total": ..., "succeeded": ..., "failed": ..., "results": ...}. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Count records in a table (optionally filtered) and return only the integer total.
Use this instead of dataverse_query_table when you need a number, not rows. For per-group counts (e.g. count by status) use dataverse_aggregate_table. The total is capped at 5,000 by Dataverse.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| filter | str | None |
no | None |
OData $filter expression to count only matching records. Note: the count is always capped at 5,000 by Dataverse. |
Returns {"total_count": ..., "capped": ...}. Errors return {"error": true, "message": "..."}.
Write · non-idempotent · category core
Create a single record in any Dataverse table and return the new record's id.
For updating an existing record use dataverse_update_record. For bulk or atomic multi-operation writes use dataverse_execute_batch. Requires DATAVERSE_ALLOW_WRITE=true. Use dataverse_get_entity_sets to discover entity_set_name; use dataverse_list_columns to discover column names.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| data | dict[str, Any] |
yes | — | Column name/value pairs for the new record. Use logical (lowercase) column names (e.g., {'name': 'Contoso', 'telephone1': '555-0100'}). Use dataverse_list_columns to discover available columns. Must contain at least one column. (min_len=1) |
Returns {"created": ..., "id": ...}. Errors return {"error": true, "message": "..."}.
Delete · idempotent · category core
Permanently delete a single Dataverse record by GUID — this action cannot be undone.
Requires DATAVERSE_ALLOW_DELETE=true.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| record_id | str |
yes | — | The GUID of the record to delete. (min_len=1) |
Returns {"deleted": ..., "id": ...}. Errors return {"error": true, "message": "..."}.
Delete · idempotent · category core
Remove an existing association between two Dataverse records.
Navigation property names are case-sensitive — use dataverse_list_relationships to discover the correct name. Requires DATAVERSE_ALLOW_DELETE=true.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the primary record's table (e.g., 'accounts'). (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| record_id | str |
yes | — | GUID of the primary record. (min_len=36) |
| navigation_property | str |
yes | — | Collection-valued navigation property name on the primary entity (case-sensitive). Use dataverse_list_relationships to discover. (min_len=1) |
| related_record_id | str |
yes | — | GUID of the related record to disassociate. (min_len=36) |
Returns {"success": ...}. Errors return {"error": true, "message": "..."}.
Write · non-idempotent · category core
Execute bulk or atomic multi-operation reads and writes via the OData $batch endpoint.
Use this for bulk record operations or when multiple writes must succeed or fail together. For single-record writes use dataverse_create_record / dataverse_update_record / dataverse_delete_record instead. For metadata/schema changes use the dataverse_create_/update_/delete_* metadata tools.
POST/PUT/PATCH operations require DATAVERSE_ALLOW_WRITE=true; DELETE operations require DATAVERSE_ALLOW_DELETE=true. Group operations with the same change_set_id to run them atomically (all-or-nothing, up to 1,000 operations per request). Returns per-operation results [{index, status_code, body}].
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| operations | list[BatchOperationItem] |
yes | — | Ordered list of OData operations to execute in the batch. Maximum 1,000 operations per request. Operations within the same change_set_id are executed atomically. (min_len=1) |
| continue_on_error | bool |
no | False |
When True, adds 'Prefer: odata.continue-on-error' — the batch continues processing remaining operations even if one fails. When False (default), the batch stops on the first error. |
Returns {"results": ..., "count": ...}. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Execute a FetchXML query against a Dataverse table and return matching records.
FetchXML supports complex joins (link-entity), aggregation, and queries that OData $filter cannot express. Use dataverse_query_table for simple OData queries. Use dataverse_get_entity_sets to discover entity_set_name; the entity_set_name must match the root <entity name="..."> logical name's collection name.
FetchXML uses paging cookies (not @odata.nextLink). This tool returns one page plus paging metadata (has_more, paging_cookie) so the caller can page if needed.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | Entity set (collection) name matching the FetchXML root entity, e.g. 'accounts'. Use dataverse_get_entity_sets to discover the correct name. (pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| fetch_xml | str |
yes | — | The FetchXML query string |
| include_formatted_values | bool |
no | False |
When True, includes formatted (display) values for lookups, option sets, etc. Formatted values appear as 'fieldname@OData.Community.Display.V1.FormattedValue' in each record. |
Returns JSON. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
List OData EntitySet names from the Dataverse service document.
Use this to discover the correct entity_set_name for a table before querying records (e.g., 'account' → 'accounts', 'systemuser' → 'systemusers'). Faster and smaller than fetching $metadata. Filter with contains.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| contains | str | None |
no | None |
Case-insensitive substring filter applied to EntitySet names. Use this to narrow results (e.g., 'account' returns 'accounts', 'accountleads', etc.). If omitted, all entity sets are returned up to the 'top' limit. |
| top | int |
no | 50 |
Maximum number of entity sets to return (1–1000). (ge=1, le=1000) |
Returns {"entity_sets": ..., "count": ..., "has_more": ...}. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Fingerprint a Dataverse environment: server version, organization identity, endpoints.
Merges three unbound Web API functions — RetrieveVersion, RetrieveCurrentOrganization, and RetrieveOrganizationInfo. Call this before any risky operation to confirm which environment you are pointed at.
To tell a non-production environment from production, read organization_info.organizationInfo.InstanceType or current_organization.Detail.OrganizationType. Both are strings, and their values are distinct per tier — a developer-tier org reports "Developer", not "Sandbox" — so never test only for "Sandbox" when deciding whether an environment is safe to change. Identity lives alongside them: Detail.UniqueName, Detail.FriendlyName, Detail.EnvironmentId, Detail.Geo, and Detail.State.
RetrieveOrganizationInfo also returns every installed solution. That list runs to several hundred entries, so it is replaced by organization_info.organizationInfo.solutions_count. Set include_solutions=true to get the full Solutions array as well, but prefer dataverse_list_solutions for browsing solutions.
Each function is called independently. If one is unavailable or privilege-gated its failure is reported in partial_errors and the remaining data is still returned; only a failure of all three yields an error response. Apart from the solution summarization, payloads are returned as Dataverse produced them, minus the @odata envelope keys.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| access_type | Literal['Default', 'Internet', 'Intranet'] |
no | 'Default' |
EndpointAccessType member used when retrieving the current organization's service endpoints. 'Default' returns the endpoints for the caller's own access path; 'Internet' and 'Intranet' request the externally- and internally-facing endpoints. Only these three members are accepted. |
| include_solutions | bool |
no | False |
See note below. |
- include_solutions — Return the full list of installed solutions from RetrieveOrganizationInfo. Off by default because a real environment commonly has several hundred solutions, which would dominate the response of what is otherwise a small environment fingerprint. When false, only 'solutions_count' is returned. Prefer dataverse_list_solutions for browsing, filtering, or paging solutions — it is the purpose-built tool.
Returns JSON. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Retrieve a single Dataverse record by its GUID.
For multiple records with filtering use dataverse_query_table. Use dataverse_query_table first to find record IDs if you do not have one.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| record_id | str |
yes | — | The GUID of the record to retrieve (min_len=1) |
| select | list[str] | None |
no | None |
Columns to return. Omit to return a conservative default projection ('createdon','modifiedon'). Specify to reduce payload (e.g., ['name', 'telephone1']) |
| include_formatted_values | bool |
no | False |
When True, returns human-readable formatted values alongside raw values (e.g., option set labels, formatted dates). Formatted values appear as 'fieldname@OData.Community.Display.V1.FormattedValue' in the record. |
Returns {"record": ...}. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Read one setting's FINAL COMPUTED value for this environment.
Calls the unbound RetrieveSetting function, which returns the value actually in effect after the platform has applied its precedence rules, rather than a raw configuration row that only tells you what someone stored at one level. That makes it the tool for diffing configuration between environments: compare computed values, not rows.
It reads a NAMED setting from the settings framework, addressed by its unique name. It is not a general reader for the organization row: a column such as plugintracelogsetting is not a setting name, and dataverse_get_plugin_trace_log_setting (or dataverse_query_table over organizations) is what reads those.
Omit app_unique_name to read the ORGANIZATION-level value. Supply the unique name of a model-driven app to read the value as that app sees it, which can differ where an app-level override exists. The two are different requests: when app_unique_name is omitted the parameter is left out of the call entirely rather than sent empty.
THE VALUE IS NESTED. Microsoft Learn documents RetrieveSettingResponse but not its inner properties; live, v9.2 answers {"SettingDetail": {"Name": ..., "Value": "false", "DataType": 2}}. setting_value is lifted out of that container and setting_value_source says where it came from (normally SettingDetail.Value). setting_detail_name and setting_data_type carry its siblings; DataType is an INTEGER CODE passed through unmapped, since no verified code-to-type-name table exists. Note Value is a STRING — "false", not a JSON boolean — so parse it yourself rather than testing truthiness.
AN UNKNOWN SETTING NAME IS NOT AN ERROR. Dataverse answers HTTP 200 with SettingDetail: null. That is reported as setting_found: false with no setting_value, and it is a DIFFERENT answer from a setting that exists and holds "", "false" or 0 — those come back as setting_found: true with the value. Never read a missing setting_value as "the setting is off".
If the payload matches neither shape, setting_value is OMITTED rather than guessed and normalized is false. raw_response (minus the @odata.* envelope) always rides along on every path, so the extraction can be checked.
Setting names come from the settingdefinitions table (112 rows on a stock org), which dataverse_query_table can list. Both URL forms are live-verified to return HTTP 200: SettingName alone, and SettingName with AppUniqueName.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| setting_name | str |
yes | — | Unique name of the setting to read (the settingdefinition's unique name, not its display label). Letters, digits, '', '.' and '-' only. (min_len=1, max_len=100, pattern=^[A-Za-z0-9.\-]+$) |
| app_unique_name | str | None |
no | None |
Optional unique name of a model-driven app, to read the value as that app sees it (an app-level override of the org-level setting). OMIT it to read the organization-level value — the parameter is then left out of the request entirely rather than sent empty, which is a different call and can return a different value. (min_len=1, max_len=100, pattern=^[A-Za-z0-9_.\-]+$) |
Returns JSON. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Get approximate row counts for many Dataverse tables in one round trip.
Calls the unbound RetrieveTotalRecordCount function with up to 50 table logical names (singular and lowercase — 'account', not 'accounts') and returns a {logical_name: count} map.
The counts come from a snapshot Dataverse takes at most once every 24 hours, so they are approximate and can lag reality by up to a day: a table populated an hour ago may report 0, and recent deletions may still be included. Worse, on an environment where the snapshot job has not run, EVERY count comes back 0 while the tables actually hold data (observed live on an org whose real counts were in the hundreds). The response flags that case with all_counts_zero=true — read it as "unknown", not "empty". Use this tool for cheap bulk sizing (which tables hold data, rough magnitudes, migration planning), and dataverse_count_records whenever an exact, live, or filtered count matters.
Unknown names are all-or-nothing, NOT silently dropped: a single logical name Dataverse does not recognize fails the whole call with HTTP 400 ([0x80040203] "Entity X was not found in the CRM system") and no partial results come back. Pass names you have already confirmed exist — dataverse_list_tables is the cheap way to confirm them. The error message names the offending table so you can drop it and retry.
If the response is not in the expected shape it is returned unchanged under raw_response with normalized=false rather than being guessed at.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_names | list[str] |
yes | — | See note below. |
- entity_names — Logical names of the tables to count — singular and lowercase (e.g. ['account', 'contact', 'new_invoice']), NOT the plural OData entity set names used elsewhere. Between 1 and 50 names per call: the list is sent as a JSON array inside the request URL's query string, and a longer list risks exceeding the practical URL length limit, so split larger sets across multiple calls (with unusually long names, split sooner). Each name must match the Dataverse identifier grammar (letter or underscore, then letters/digits/underscores) and Dataverse's 50-character limit on table schema/logical names. Every name must exist: one unrecognized name fails the whole call with HTTP 400, with no partial results. (min_len=1, max_len=50)
Returns JSON. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
List Power Platform environments available to the authenticated user.
Uses the Power Platform admin API — no dataverse_url required. Returns instance_url for each environment, which is the dataverse_url for all other tools. Use this to discover environments before calling environment-specific Dataverse tools.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| expand_capacity | bool |
no | False |
Include capacity details for each environment |
| expand_addons | bool |
no | False |
Include add-on allocation details for each environment |
Returns {"environments": ..., "count": ...}. Errors return {"error": true, "message": "..."}.
Write · idempotent · category core
Merge a subordinate record into a target record for account, contact, lead, or incident.
The subordinate record is deactivated (not deleted) after the merge. Use update_content to carry specific field values from the subordinate to the target. Requires DATAVERSE_ALLOW_WRITE=true.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_logical_name | str |
yes | — | Logical name of the entity type to merge. Must be one of: 'account', 'contact', 'lead', 'incident'. (min_len=1) |
| target_id | str |
yes | — | GUID of the target record to keep after the merge. (min_len=36) |
| subordinate_id | str |
yes | — | GUID of the subordinate record to merge into the target. The subordinate is deactivated (not deleted) after the merge. (min_len=36) |
| update_content | dict | None |
no | None |
Optional dict of field name/value pairs from the subordinate record to carry over to the target after the merge. Example: {'telephone1': '555-1234'} |
| perform_parenting_checks | bool |
no | False |
Whether to check and reparent records during the merge. Set to True only when parenting relationships must be maintained. |
Returns {"success": ...}. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Query records from a Dataverse table with OData filtering, ordering, and expansion.
For a single record by GUID use dataverse_get_record. For just a count use dataverse_count_records. For group-by aggregation use dataverse_aggregate_table. To create, update, or delete records use dataverse_create_record, dataverse_update_record, or dataverse_delete_record.
Always specify select to limit returned columns and keep payloads small.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| select | list[str] | None |
no | None |
Columns to return. Omit to return a conservative default projection ('createdon','modifiedon'). Always specify this to reduce payload size (e.g., ['name', 'accountid', 'telephone1']) |
| filter | str | None |
no | None |
OData $filter expression. Use lowercase logical names. Examples: "statecode eq 0", "name eq 'Contoso'" , "createdon gt 2024-01-01" |
| orderby | list[str] | None |
no | None |
Sort order. Each entry is 'column_name asc' or 'column_name desc' (e.g., ['name asc', 'createdon desc']) |
| top | int |
no | 50 |
Maximum number of records to return (ge=1, le=5000) |
| expand | list[str] | None |
no | None |
Navigation properties to expand (case-sensitive!). Example: ['primarycontactid'] |
| count | bool |
no | False |
When True, includes total_count in the response with the number of matching records. Counts are capped at 5,000 by Dataverse. |
| include_formatted_values | bool |
no | False |
When True, returns human-readable formatted values alongside raw values (e.g., option set labels, formatted dates). Formatted values appear as 'fieldname@OData.Community.Display.V1.FormattedValue' in each record. |
Returns JSON. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Return the access rights a system user has to a specific Dataverse record.
Returns the AccessRights bitmask and named rights (ReadAccess, WriteAccess, DeleteAccess, etc.). Use before delegating an operation to confirm the user can act on the record.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| user_id | str |
yes | — | GUID of the system user to check access for. Use dataverse_whoami to get the current caller's UserId. (min_len=36) |
| entity_set_name | str |
yes | — | OData collection name of the target record's table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| record_id | str |
yes | — | GUID of the target record to check access against. (min_len=36) |
Returns {"access_rights": ..., "named_rights": ..., "user_id": ..., "entity_set_name": ..., "record_id": ...}. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Read the UNPUBLISHED (draft) definition of one customization record.
A normal GET — and therefore dataverse_get_form, dataverse_get_view and dataverse_get_web_resource — returns the PUBLISHED row. Writes such as dataverse_set_formxml, dataverse_add_form_control, dataverse_update_view and dataverse_add_view_column save to the draft, so after any of them the published read is stale until dataverse_publish_customizations runs. Call this tool to read back what you just wrote; reading the published row and then editing it can silently clobber your own unpublished changes.
Supported entity_set_name values: 'savedqueries' (views), 'systemforms' (forms), 'appmodules', 'webresourceset'. Dataverse accepts the RetrieveUnpublished message for only certain customization entity types, and sitemap is NOT one of them — a sitemap draft cannot be read this way, so do not go looking for it. Ordinary data tables such as 'accounts' have no unpublished layer at all.
Returns one record, not a list. By default a small projection is returned with the large XML/binary columns held back (formxml, fetchxml, layoutxml, content) — pass select to ask for them explicitly, e.g. select=['formid','name','formxml']. select is honoured and validated: an unknown column name comes back as an HTTP 400 naming the property.
IMPORTANT — the returned column set is NOT the set you asked for, and it differs in BOTH directions.
- FEWER: unlike a plain GET, RetrieveUnpublished omits a requested column whose value is NULL instead of returning it as null. A missing key means "this column is null", NOT "this column does not exist".
- MORE: the platform also returns columns you never requested. It is an
open-ended set, not one known extra — _organizationid_value comes back
on every entity set, and a narrow select on 'systemforms' also returned
objecttypecode and type. Treat any unrequested column as possible.
So never assume returned set == requested set, and do NOT diff requested
against returned to detect a mistyped select column: nulls vanish from that
diff and platform extras pollute it. The reliable signal for a bad column
name is Dataverse's own HTTP 400 "Could not find a property named '
<name>'", surfaced through the error envelope.
If the record has no unpublished changes, the draft and the published row are identical, which is the expected result rather than an error.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | Literal['savedqueries', 'systemforms', 'appmodules', 'webresourceset'] |
yes | — | See note below. |
| record_id | str |
yes | — | GUID of the record to read — savedqueryid, formid, appmoduleid or webresourceid. Use dataverse_list_views, dataverse_list_forms, dataverse_list_apps or dataverse_list_web_resources to find one. |
| select | list[str] | None |
no | None |
See note below. |
- entity_set_name — OData collection name of the customization table to read from. Only these four support the RetrieveUnpublished message: 'savedqueries' (views), 'systemforms' (forms), 'appmodules', 'webresourceset' (note the irregular plural). Ordinary data tables such as 'accounts' have no unpublished layer, and 'sitemaps' is rejected by Dataverse itself ('the RetrieveUnpublished method does not support entities of type sitemap') — neither is accepted.
- select — Columns to return. Defaults to a small per-table projection that deliberately EXCLUDES the large XML/binary columns (formxml, fetchxml, layoutxml, content) — a single systemform row can be hundreds of KB. Ask for them explicitly when you need them, e.g. ['formid','name','formxml'] or ['savedqueryid','name','fetchxml','layoutxml']. A requested column whose value is NULL is omitted from the returned record rather than returned as null. (min_len=1, max_len=100)
Returns JSON. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Retrieve all security privileges assigned to a system user via their roles.
Returns RolePrivilege objects with PrivilegeName and Depth. Use dataverse_whoami to get the caller's UserId for checking your own privileges.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| user_id | str |
yes | — | GUID of the system user whose privileges to retrieve. Use dataverse_whoami to get the current caller's UserId. (min_len=36) |
Returns {"privileges": ..., "count": ...}. Errors return {"error": true, "message": "..."}.
Write · idempotent · category core
Swap a connection reference logical name inside a cloud flow's clientdata.
Reads the flow's clientdata, replaces every literal occurrence of old_logical_name with new_logical_name, and PATCHes the result back — all server-side, so the multi-KB clientdata JSON never travels as a tool argument (which would otherwise risk truncation at the transport boundary and a "Flow clientdata is in invalid format" error from Dataverse). This is a literal string replace, not a JSON reparse, so unrelated formatting is preserved byte-for-byte. Requires DATAVERSE_ALLOW_WRITE=true.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| workflow_id | str |
yes | — | The GUID of the cloud flow (workflows entity) to update. (min_len=1) |
| old_logical_name | str |
yes | — | Connection reference logical name to replace in the flow's clientdata (e.g., 'courts_Decisions_MicrosoftDataverse'). (min_len=1) |
| new_logical_name | str |
yes | — | Replacement connection reference logical name (e.g., 'courts_Core_MicrosoftDataverse'). (min_len=1) |
Returns {"updated": ..., "workflow_id": ..., "replacements": ..., "old_logical_name": ..., "new_logical_name": ...} or {"updated": ..., "workflow_id": ..., "replacements": ..., "message": ...}. Errors return {"error": true, "message": "..."}.
Write · idempotent · category core
Partially update a single Dataverse record via PATCH — only supplied columns change.
For creating a new record use dataverse_create_record. For bulk or atomic multi-operation writes use dataverse_execute_batch. Requires DATAVERSE_ALLOW_WRITE=true. Unlike metadata tools, this is a PATCH partial update — no full definition needed.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| entity_set_name | str |
yes | — | OData collection name of the table (e.g., 'accounts', 'contacts'). Use dataverse_get_entity_sets to discover the correct name. (min_len=1, pattern=^[a-zA-Z_][a-zA-Z0-9_]*$) |
| record_id | str |
yes | — | The GUID of the record to update. (min_len=1) |
| data | dict[str, Any] |
yes | — | Partial column name/value pairs to update — only the provided columns are changed, all others are left untouched. Use logical (lowercase) column names (e.g., {'name': 'New Name', 'telephone1': '555-0200'}). Must contain at least one column. (min_len=1) |
Returns {"updated": ..., "id": ...}. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Check a FetchXML query for problems and performance warnings WITHOUT running it.
Pre-flight companion to dataverse_execute_fetchxml. Calls the unbound ValidateFetchXmlExpression function, which parses and analyses the expression server-side and reports validation errors plus performance suggestions (for example unrestricted column lists or filters that cannot use an index). No records are read and nothing is modified. Run it before executing an expensive or machine-generated query — a FetchXML query that returns results can still be a query that scans a table.
No entity set name is required: the root <entity name="..."> inside the document identifies the table.
HTTP 200 DOES NOT MEAN THE QUERY IS VALID — check has_errors / error_count. A FetchXml naming a table or attribute that does not exist comes back as a successful HTTP 200 carrying an error-severity message ("Error handling FetchXML: The entity with a name = '...' was not found in the MetadataCache"), not as an HTTP 400. Treating a non-error response as "this query works" is wrong. Read has_errors first, then errors for the error texts.
Findings are reported as: count (total messages), error_count, warning_count (count == error_count + warning_count, so nothing is dropped), has_errors, and errors (the error texts). The severity mapping is OBSERVED, NOT DOCUMENTED: live responses used 1 for performance warnings and 3 for errors, so severity >= 3 is counted as an error and < 3 as a warning. Any message whose severity is missing or not an integer is bucketed conservatively as an error rather than assumed benign, and still appears in count.
The full payload is also returned unchanged under raw_response (minus the @odata envelope): ValidationResults.Helplink, each message's LocalizedMessageText and its OptionalPropertyBag (which carries details such as AttributeCount/AttributeLimit) are worth reading. If the payload is not in the expected ValidationResults.Messages shape it is returned raw with normalized=false and no counts, rather than being guessed at.
The query is checked locally for XML well-formedness first, using a hardened parser that rejects DTDs and entity declarations, so malformed or hostile markup fails immediately with a clear message instead of costing a round trip.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
| fetch_xml | str |
yes | — | See note below. |
-
fetch_xml — The FetchXml document to validate, starting with '
<fetch ...>'. It is analysed by Dataverse, not executed: no records are read and nothing is modified. No entity set name is needed — the root<entity name='...'>inside the document identifies the table. Maximum 2000 characters, because the whole document is sent inside the request URL's query string as a percent-encoded OData string literal, which roughly doubles its length (typically at most triples it, and up to six times for a document that is almost all single quotes, since each quote is doubled before being encoded); beyond that the URL risks rejection by Dataverse or a proxy in front of it, which is reported back as an HTTP 414 with a shortening hint. If a query is too long, shrink it before validating: drop insignificant whitespace and comments, trim<attribute>elements to the columns actually needed, or validate expensive<link-entity>branches one at a time — the warnings are reported per construct, so a trimmed query still surfaces the same suggestions. The document must be well-formed XML and must not declare a DTD or XML entities; both are checked locally before the request is sent. (min_len=8, max_len=2000)
Returns JSON. Errors return {"error": true, "message": "..."}.
Read · idempotent · category core
Return the authenticated caller's identity from the Dataverse WhoAmI endpoint.
Returns UserId, BusinessUnitId, and OrganizationId. Call at session start to confirm authentication and get the caller's UserId for privilege checks.
| Param | Type | Req | Default | Notes |
|---|---|---|---|---|
| dataverse_url | str |
yes | — | Required Dataverse organization URL for this request (e.g., 'https://yourorg.crm.dynamics.com'). |
Returns {"UserId": ..., "BusinessUnitId": ..., "OrganizationId": ...}. Errors return {"error": true, "message": "..."}.
dataverse-mcp 3.9.1 — Repository · PyPI · Issues · Changelog · MIT
Get started
Configure
Tools
- Tool-Index
- Tools-Core
- Tools-Schema
- Tools-Solutions
- Tools-Plugins
- Tools-Security
- Tools-Custom-APIs
- Tools-Apps
- Tools-Variables
- Tools-Flows
- Tools-Views
- Tools-Forms
- Tools-Connections
- Tools-Web-Resources
- Tools-Jobs
Understand