mcp-dataverse 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CAPABILITIES.md +1049 -992
  2. package/README.md +80 -78
  3. package/config.example.json +0 -1
  4. package/dist/auth/auth-provider.factory.d.ts +2 -2
  5. package/dist/auth/auth-provider.factory.js +4 -4
  6. package/dist/auth/msal-auth-provider.d.ts +2 -2
  7. package/dist/auth/msal-auth-provider.d.ts.map +1 -1
  8. package/dist/auth/msal-auth-provider.js +9 -7
  9. package/dist/auth/msal-auth-provider.js.map +1 -1
  10. package/dist/auth/pac-auth-provider.d.ts +1 -1
  11. package/dist/auth/pac-auth-provider.d.ts.map +1 -1
  12. package/dist/auth/pac-auth-provider.js +39 -35
  13. package/dist/auth/pac-auth-provider.js.map +1 -1
  14. package/dist/config/config.loader.d.ts +1 -1
  15. package/dist/config/config.loader.d.ts.map +1 -1
  16. package/dist/config/config.loader.js +22 -25
  17. package/dist/config/config.loader.js.map +1 -1
  18. package/dist/config/config.schema.d.ts +1 -4
  19. package/dist/config/config.schema.d.ts.map +1 -1
  20. package/dist/config/config.schema.js +11 -11
  21. package/dist/config/config.schema.js.map +1 -1
  22. package/dist/dataverse/dataverse-client-advanced.d.ts +1 -1
  23. package/dist/dataverse/dataverse-client-advanced.d.ts.map +1 -1
  24. package/dist/dataverse/dataverse-client-advanced.js +70 -39
  25. package/dist/dataverse/dataverse-client-advanced.js.map +1 -1
  26. package/dist/dataverse/dataverse-client.actions.d.ts +11 -0
  27. package/dist/dataverse/dataverse-client.actions.d.ts.map +1 -0
  28. package/dist/dataverse/dataverse-client.actions.js +25 -0
  29. package/dist/dataverse/dataverse-client.actions.js.map +1 -0
  30. package/dist/dataverse/dataverse-client.batch.d.ts +2 -2
  31. package/dist/dataverse/dataverse-client.batch.d.ts.map +1 -1
  32. package/dist/dataverse/dataverse-client.batch.js +12 -12
  33. package/dist/dataverse/dataverse-client.batch.js.map +1 -1
  34. package/dist/dataverse/dataverse-client.d.ts +5 -8
  35. package/dist/dataverse/dataverse-client.d.ts.map +1 -1
  36. package/dist/dataverse/dataverse-client.js +81 -81
  37. package/dist/dataverse/dataverse-client.js.map +1 -1
  38. package/dist/dataverse/dataverse-client.metadata.d.ts +3 -3
  39. package/dist/dataverse/dataverse-client.metadata.d.ts.map +1 -1
  40. package/dist/dataverse/dataverse-client.metadata.js +45 -45
  41. package/dist/dataverse/dataverse-client.metadata.js.map +1 -1
  42. package/dist/dataverse/dataverse-client.utils.d.ts.map +1 -1
  43. package/dist/dataverse/dataverse-client.utils.js +5 -5
  44. package/dist/dataverse/dataverse-client.utils.js.map +1 -1
  45. package/dist/dataverse/http-client.d.ts +1 -1
  46. package/dist/dataverse/http-client.d.ts.map +1 -1
  47. package/dist/dataverse/http-client.js +25 -18
  48. package/dist/dataverse/http-client.js.map +1 -1
  49. package/dist/dataverse/types.d.ts +5 -5
  50. package/dist/server.js +57 -57
  51. package/dist/server.js.map +1 -1
  52. package/dist/setup-auth.js +8 -8
  53. package/dist/setup-auth.js.map +1 -1
  54. package/dist/tools/actions.tools.d.ts +2 -2
  55. package/dist/tools/actions.tools.d.ts.map +1 -1
  56. package/dist/tools/actions.tools.js +108 -65
  57. package/dist/tools/actions.tools.js.map +1 -1
  58. package/dist/tools/annotations.tools.d.ts +2 -2
  59. package/dist/tools/annotations.tools.d.ts.map +1 -1
  60. package/dist/tools/annotations.tools.js +79 -70
  61. package/dist/tools/annotations.tools.js.map +1 -1
  62. package/dist/tools/audit.tools.d.ts +2 -2
  63. package/dist/tools/audit.tools.d.ts.map +1 -1
  64. package/dist/tools/audit.tools.js +51 -48
  65. package/dist/tools/audit.tools.js.map +1 -1
  66. package/dist/tools/auth.tools.d.ts +2 -2
  67. package/dist/tools/auth.tools.d.ts.map +1 -1
  68. package/dist/tools/auth.tools.js +9 -7
  69. package/dist/tools/auth.tools.js.map +1 -1
  70. package/dist/tools/batch.tools.d.ts +2 -2
  71. package/dist/tools/batch.tools.d.ts.map +1 -1
  72. package/dist/tools/batch.tools.js +39 -26
  73. package/dist/tools/batch.tools.js.map +1 -1
  74. package/dist/tools/crud.tools.d.ts +2 -2
  75. package/dist/tools/crud.tools.d.ts.map +1 -1
  76. package/dist/tools/crud.tools.js +140 -83
  77. package/dist/tools/crud.tools.js.map +1 -1
  78. package/dist/tools/customization.tools.d.ts +2 -2
  79. package/dist/tools/customization.tools.d.ts.map +1 -1
  80. package/dist/tools/customization.tools.js +72 -62
  81. package/dist/tools/customization.tools.js.map +1 -1
  82. package/dist/tools/environment.tools.d.ts +2 -2
  83. package/dist/tools/environment.tools.d.ts.map +1 -1
  84. package/dist/tools/environment.tools.js +62 -55
  85. package/dist/tools/environment.tools.js.map +1 -1
  86. package/dist/tools/file.tools.d.ts +2 -2
  87. package/dist/tools/file.tools.d.ts.map +1 -1
  88. package/dist/tools/file.tools.js +63 -44
  89. package/dist/tools/file.tools.js.map +1 -1
  90. package/dist/tools/impersonate.tools.d.ts +3 -3
  91. package/dist/tools/impersonate.tools.d.ts.map +1 -1
  92. package/dist/tools/impersonate.tools.js +22 -20
  93. package/dist/tools/impersonate.tools.js.map +1 -1
  94. package/dist/tools/metadata.tools.d.ts +2 -2
  95. package/dist/tools/metadata.tools.d.ts.map +1 -1
  96. package/dist/tools/metadata.tools.js +110 -75
  97. package/dist/tools/metadata.tools.js.map +1 -1
  98. package/dist/tools/org.tools.d.ts +2 -2
  99. package/dist/tools/org.tools.d.ts.map +1 -1
  100. package/dist/tools/org.tools.js +21 -15
  101. package/dist/tools/org.tools.js.map +1 -1
  102. package/dist/tools/quality.tools.d.ts +2 -2
  103. package/dist/tools/quality.tools.d.ts.map +1 -1
  104. package/dist/tools/quality.tools.js +19 -17
  105. package/dist/tools/quality.tools.js.map +1 -1
  106. package/dist/tools/query.tools.d.ts +2 -2
  107. package/dist/tools/query.tools.d.ts.map +1 -1
  108. package/dist/tools/query.tools.js +81 -58
  109. package/dist/tools/query.tools.js.map +1 -1
  110. package/dist/tools/relations.tools.d.ts +2 -2
  111. package/dist/tools/relations.tools.d.ts.map +1 -1
  112. package/dist/tools/relations.tools.js +62 -26
  113. package/dist/tools/relations.tools.js.map +1 -1
  114. package/dist/tools/search.tools.d.ts +2 -2
  115. package/dist/tools/search.tools.d.ts.map +1 -1
  116. package/dist/tools/search.tools.js +45 -45
  117. package/dist/tools/search.tools.js.map +1 -1
  118. package/dist/tools/solution.tools.d.ts +2 -2
  119. package/dist/tools/solution.tools.d.ts.map +1 -1
  120. package/dist/tools/solution.tools.js +89 -55
  121. package/dist/tools/solution.tools.js.map +1 -1
  122. package/dist/tools/teams.tools.d.ts +2 -2
  123. package/dist/tools/teams.tools.d.ts.map +1 -1
  124. package/dist/tools/teams.tools.js +33 -29
  125. package/dist/tools/teams.tools.js.map +1 -1
  126. package/dist/tools/trace.tools.d.ts +2 -2
  127. package/dist/tools/trace.tools.d.ts.map +1 -1
  128. package/dist/tools/trace.tools.js +111 -101
  129. package/dist/tools/trace.tools.js.map +1 -1
  130. package/dist/tools/tracking.tools.d.ts +2 -2
  131. package/dist/tools/tracking.tools.d.ts.map +1 -1
  132. package/dist/tools/tracking.tools.js +16 -14
  133. package/dist/tools/tracking.tools.js.map +1 -1
  134. package/dist/tools/users.tools.d.ts +2 -2
  135. package/dist/tools/users.tools.d.ts.map +1 -1
  136. package/dist/tools/users.tools.js +62 -57
  137. package/dist/tools/users.tools.js.map +1 -1
  138. package/dist/tools/validation.utils.d.ts +1 -1
  139. package/dist/tools/validation.utils.d.ts.map +1 -1
  140. package/dist/tools/validation.utils.js +9 -3
  141. package/dist/tools/validation.utils.js.map +1 -1
  142. package/dist/tools/views.tools.d.ts +2 -2
  143. package/dist/tools/views.tools.d.ts.map +1 -1
  144. package/dist/tools/views.tools.js +32 -24
  145. package/dist/tools/views.tools.js.map +1 -1
  146. package/package.json +81 -81
  147. package/server.json +51 -4
package/CAPABILITIES.md CHANGED
@@ -1,992 +1,1049 @@
1
- # MCP Dataverse Server — Complete Capabilities Reference
2
-
3
- > **Version**: 0.2.0 | **API Version**: Dataverse Web API v9.2 | **Transport**: MCP SDK over stdio
4
-
5
- 48 tools across 21 categories for full Dataverse lifecycle: schema, CRUD, FetchXML, solutions, plugins, audit, files, users, environment variables, and more.
6
-
7
- ---
8
-
9
- ## Table of Contents
10
-
11
- - [Quick Start](#quick-start)
12
- - [Architecture Overview](#architecture-overview)
13
- - [Tool Reference (48 tools)](#tool-reference-48-tools)
14
- - [1. Auth (1)](#1-auth-1-tool)
15
- - [2. Metadata (8)](#2-metadata-8-tools)
16
- - [3. Query (3)](#3-query-3-tools)
17
- - [4. CRUD (5)](#4-crud-5-tools)
18
- - [5. Relations (2)](#5-relations-2-tools)
19
- - [6. Actions & Functions (6)](#6-actions--functions-6-tools)
20
- - [7. Batch (1)](#7-batch-1-tool)
21
- - [8. Change Tracking (1)](#8-change-tracking-1-tool)
22
- - [9. Solutions (3)](#9-solutions-3-tools)
23
- - [10. Impersonation (1)](#10-impersonation-1-tool)
24
- - [11. Customization (3)](#11-customization-3-tools)
25
- - [12. Environment (2)](#12-environment-2-tools)
26
- - [13. Trace (2)](#13-trace-2-tools)
27
- - [14. Search (1)](#14-search-1-tool)
28
- - [15. Audit (1)](#15-audit-1-tool)
29
- - [16. Quality (1)](#16-quality-1-tool)
30
- - [17. Annotations (2)](#17-annotations-2-tools)
31
- - [18. Users (2)](#18-users-2-tools)
32
- - [19. Views (1)](#19-views-1-tool)
33
- - [20. Files (2)](#20-files-2-tools)
34
- - [21. Org (1)](#21-org-1-tool)
35
- - [Error Handling & Retry Behavior](#error-handling--retry-behavior)
36
- - [Security](#security)
37
- - [Limitations & Known Constraints](#limitations--known-constraints)
38
-
39
- ---
40
-
41
- ## Quick Start
42
-
43
- ### Prerequisites
44
-
45
- | Requirement | Details |
46
- |---|---|
47
- | **Node.js** | v20+ |
48
- | **Dataverse Environment** | Active URL (`https://<org>.crm<N>.dynamics.com`) |
49
- | **Authentication** | PAC CLI (interactive) or Entra ID App Registration (headless) |
50
-
51
- ### Installation & Configuration
52
-
53
- ```bash
54
- npm install && npm run build
55
- ```
56
-
57
- Create `config.json` (see `config.example.json`):
58
-
59
- ```json
60
- {
61
- "environmentUrl": "https://yourorg.crm.dynamics.com",
62
- "authMode": "pac",
63
- "pacProfileName": "default",
64
- "tenantId": "",
65
- "clientId": "",
66
- "clientSecret": "",
67
- "requestTimeoutMs": 30000,
68
- "maxRetries": 3
69
- }
70
- ```
71
-
72
- | Field | Type | Description |
73
- |---|---|---|
74
- | `environmentUrl` | `string` | Dataverse environment URL (**required**, must be HTTPS) |
75
- | `authMode` | `"pac"\|"msal"` | Auth strategy (default: `"pac"`) |
76
- | `pacProfileName` | `string` | PAC CLI profile (default: `"default"`) |
77
- | `tenantId` / `clientId` / `clientSecret` | `string` | Required for `authMode: "msal"` |
78
- | `requestTimeoutMs` | `number` | HTTP timeout ms (default: `30000`) |
79
- | `maxRetries` | `number` | Max retry attempts 0–10 (default: `3`) |
80
-
81
- Env vars override config: `DATAVERSE_ENV_URL`, `AUTH_MODE`, `PAC_PROFILE_NAME`, `TENANT_ID`, `CLIENT_ID`, `REQUEST_TIMEOUT_MS`, `MAX_RETRIES`.
82
-
83
- ### Authentication
84
-
85
- **PAC (interactive):** `npm run auth:setup` then `npm run start`. Uses Microsoft's public Platform CLI App ID — no registration required. Tokens cached in `.msal-cache.json`.
86
-
87
- **MSAL (headless/CI):** Set `authMode: "msal"` with `tenantId`, `clientId`, `clientSecret`. Tokens acquired via OAuth2 `client_credentials`.
88
-
89
- ### Running
90
-
91
- ```bash
92
- npm run start # production
93
- npm run dev # watch mode
94
- ```
95
-
96
- Server communicates over **stdio** (MCP SDK `StdioServerTransport`). Connect from any MCP host (VS Code Copilot, Claude Desktop, etc.) by configuring it to spawn the process.
97
-
98
- ---
99
-
100
- ## Architecture Overview
101
-
102
- ```mermaid
103
- graph LR
104
- MCP["MCP Dataverse Server<br/><i>48 tools · 21 categories</i>"]
105
-
106
- MCP --> AUTH["🔑 Auth (1)"]
107
- MCP --> META["📋 Metadata (8)"]
108
- MCP --> QUERY["🔍 Query (3)"]
109
- MCP --> CRUD["✏️ CRUD (5)"]
110
- MCP --> REL["🔗 Relations (2)"]
111
- MCP --> ACT["⚡ Actions & Functions (6)"]
112
- MCP --> BATCH["📦 Batch (1)"]
113
- MCP --> TRACK["🔄 Change Tracking (1)"]
114
- MCP --> SOL["🧩 Solutions (3)"]
115
- MCP --> IMP["👤 Impersonation (1)"]
116
- MCP --> CUST["🔧 Customization (3)"]
117
- MCP --> ENV["⚙️ Environment (2)"]
118
- MCP --> TRACE["🔎 Trace (2)"]
119
- MCP --> SRCH["🔍 Search (1)"]
120
- MCP --> AUDIT["📜 Audit (1)"]
121
- MCP --> QUAL["✅ Quality (1)"]
122
- MCP --> NOTE["📝 Annotations (2)"]
123
- MCP --> USR["👥 Users (2)"]
124
- MCP --> VIEWS["👁️ Views (1)"]
125
- MCP --> FILES["📁 Files (2)"]
126
- MCP --> ORG["🏢 Org (1)"]
127
- ```
128
-
129
- All tool handlers validate inputs with **Zod** before calling the `DataverseAdvancedClient`. Auth tokens are cached and refreshed proactively; transient errors (429, 503, 504) are retried with exponential backoff.
130
-
131
- ---
132
-
133
- ## Tool Reference (48 tools)
134
-
135
- ### 1. Auth (1 tool)
136
-
137
- #### `dataverse_whoami`
138
-
139
- Returns the current authenticated user context (userId, businessUnitId, organizationId, organizationName, environmentUrl). No parameters required.
140
-
141
- > "Who am I in Dataverse?"
142
-
143
- ---
144
-
145
- ### 2. Metadata (8 tools)
146
-
147
- #### `dataverse_list_tables`
148
-
149
- Lists all Dataverse tables. Defaults to custom tables only; set `includeSystemTables=true` for all ~1700+ system tables.
150
-
151
- | Parameter | Type | Req | Notes |
152
- |---|---|---|---|
153
- | `includeSystemTables` | `boolean` | — | Default `false` |
154
-
155
- > "List all custom tables in my environment"
156
-
157
- ---
158
-
159
- #### `dataverse_get_table_metadata`
160
-
161
- Returns full schema for a table: column logical names, display names, data types, required levels, lookup targets. Set `includeAttributes=false` for table-level info only.
162
-
163
- | Parameter | Type | Req | Notes |
164
- |---|---|---|---|
165
- | `logicalName` | `string` | ✓ | e.g. `"account"` |
166
- | `includeAttributes` | `boolean` | — | Default `true` |
167
-
168
- > "Show me the schema for the contact table"
169
-
170
- ---
171
-
172
- #### `dataverse_get_relationships`
173
-
174
- Returns 1:N, N:1, and N:N relationships for a table. Use to find the correct `relationshipName` for associate/disassociate.
175
-
176
- | Parameter | Type | Req | Notes |
177
- |---|---|---|---|
178
- | `logicalName` | `string` | ✓ | Table logical name |
179
- | `relationshipType` | `"OneToMany"\|"ManyToOne"\|"ManyToMany"\|"All"` | — | Default `"All"` |
180
-
181
- > "What relationships does the account table have?"
182
-
183
- ---
184
-
185
- #### `dataverse_list_global_option_sets`
186
-
187
- Lists all global (shared) option sets in the environment. No parameters required.
188
-
189
- > "List all global option sets in my environment"
190
-
191
- ---
192
-
193
- #### `dataverse_get_option_set`
194
-
195
- Returns all labels and integer values for a named global option set.
196
-
197
- | Parameter | Type | Req | Notes |
198
- |---|---|---|---|
199
- | `name` | `string` | ✓ | Global option set name |
200
-
201
- > "Show me the values for the budgetstatus option set"
202
-
203
- ---
204
-
205
- #### `dataverse_get_entity_key`
206
-
207
- Returns alternate key definitions for a table: key attributes, index status (Active/InProgress/Failed), and customizability.
208
-
209
- | Parameter | Type | Req | Notes |
210
- |---|---|---|---|
211
- | `tableName` | `string` | ✓ | Table logical name |
212
-
213
- > "What alternate keys are defined on the account table?"
214
-
215
- ---
216
-
217
- #### `dataverse_get_attribute_option_set`
218
-
219
- Returns all labels and integer values for a table-specific Picklist, Status, or State attribute. Use instead of `dataverse_get_option_set` for column-local choices.
220
-
221
- | Parameter | Type | Req | Notes |
222
- |---|---|---|---|
223
- | `entityLogicalName` | `string` | ✓ | e.g. `"account"` |
224
- | `attributeLogicalName` | `string` | ✓ | e.g. `"statuscode"`, `"industrycode"` |
225
-
226
- > "What are the valid statuscode values for opportunity?"
227
-
228
- ---
229
-
230
- ### 3. Query (3 tools)
231
-
232
- #### `dataverse_query`
233
-
234
- OData query with `$filter`, `$select`, `$orderby`, `$top`, `$expand`, `$count`, and `$apply` (aggregations).
235
-
236
- | Parameter | Type | Req | Notes |
237
- |---|---|---|---|
238
- | `entitySetName` | `string` | ✓ | e.g. `"accounts"` |
239
- | `select` | `string[]` | — | Recommended — minimizes payload |
240
- | `filter` | `string` | — | OData `$filter` expression |
241
- | `orderby` | `string` | — | OData `$orderby` expression |
242
- | `top` | `number` | — | Default `50`, max `5000` |
243
- | `expand` | `string` | — | OData `$expand` |
244
- | `count` | `boolean` | — | Include `@odata.count` |
245
-
246
- > "Show me the first 10 active accounts sorted by name"
247
-
248
- ---
249
-
250
- #### `dataverse_execute_fetchxml`
251
-
252
- Executes a FetchXML query — use for aggregations, complex joins, many-to-many traversal, and filters not expressible in OData.
253
-
254
- | Parameter | Type | Req | Notes |
255
- |---|---|---|---|
256
- | `fetchXml` | `string` | ✓ | Complete FetchXML string |
257
- | `entitySetName` | `string` | — | Auto-extracted from `<entity name="...">` if omitted |
258
-
259
- > "Run a FetchXML query to count opportunities grouped by status"
260
-
261
- ---
262
-
263
- #### `dataverse_retrieve_multiple_with_paging`
264
-
265
- Retrieves ALL matching records by auto-following `@odata.nextLink` pages. Use when > 5000 records are needed.
266
-
267
- | Parameter | Type | Req | Notes |
268
- |---|---|---|---|
269
- | `entitySetName` | `string` | ✓ | OData entity set name |
270
- | `select` | `string[]` | — | Columns to return |
271
- | `filter` | `string` | — | OData `$filter` |
272
- | `orderby` | `string` | — | OData `$orderby` |
273
- | `expand` | `string` | — | OData `$expand` |
274
- | `maxTotal` | `number` | — | Default `5000`, max `50000` |
275
-
276
- > "Get all contacts with email addresses, up to 20000 records"
277
-
278
- ```json
279
- { "records": [...], "totalRetrieved": 12345, "pageCount": 3 }
280
- ```
281
-
282
- ---
283
-
284
- ### 4. CRUD (5 tools)
285
-
286
- #### `dataverse_get`
287
-
288
- Retrieves a single record by GUID. Returns the record plus `@odata.etag`.
289
-
290
- | Parameter | Type | Req | Notes |
291
- |---|---|---|---|
292
- | `entitySetName` | `string` | ✓ | OData entity set name |
293
- | `id` | `string (UUID)` | ✓ | Record GUID |
294
- | `select` | `string[]` | — | Columns to return |
295
-
296
- > "Get the account record with ID a1b2c3d4-..."
297
-
298
- ---
299
-
300
- #### `dataverse_create`
301
-
302
- Creates a new record and returns its GUID. Use `@odata.bind` for lookup fields.
303
-
304
- | Parameter | Type | Req | Notes |
305
- |---|---|---|---|
306
- | `entitySetName` | `string` | ✓ | OData entity set name |
307
- | `data` | `object` | ✓ | Field key-value pairs using logical names |
308
-
309
- > "Create a new account called 'Fabrikam Inc.'"
310
-
311
- ---
312
-
313
- #### `dataverse_update`
314
-
315
- PATCH-updates an existing record (only supplied fields change). Sends `If-Match: *`.
316
-
317
- | Parameter | Type | Req | Notes |
318
- |---|---|---|---|
319
- | `entitySetName` | `string` | ✓ | OData entity set name |
320
- | `id` | `string (UUID)` | ✓ | Record GUID |
321
- | `data` | `object` | ✓ | Fields to update |
322
-
323
- > "Update account a1b2c3d4 — set revenue to 5000000"
324
-
325
- ---
326
-
327
- #### `dataverse_delete`
328
-
329
- Permanently deletes a record. **Irreversible.** `confirm` must be `true`.
330
-
331
- | Parameter | Type | Req | Notes |
332
- |---|---|---|---|
333
- | `entitySetName` | `string` | ✓ | OData entity set name |
334
- | `id` | `string (UUID)` | ✓ | Record GUID |
335
- | `confirm` | `boolean` | ✓ | Must be `true` to proceed |
336
-
337
- > "Delete account record a1b2c3d4-..."
338
-
339
- ---
340
-
341
- #### `dataverse_upsert`
342
-
343
- Create-or-update via an alternate key (no GUID needed). Returns `"created"` or `"updated"`.
344
-
345
- | Parameter | Type | Req | Notes |
346
- |---|---|---|---|
347
- | `entitySetName` | `string` | ✓ | OData entity set name |
348
- | `alternateKey` | `string` | ✓ | Alternate key attribute name |
349
- | `alternateKeyValue` | `string` | ✓ | Value for the alternate key |
350
- | `data` | `object` | ✓ | Record data |
351
- | `mode` | `"upsert"\|"createOnly"\|"updateOnly"` | — | Default `"upsert"` |
352
-
353
- > "Upsert account with external ID 'EXT-001', set name to 'Contoso'"
354
-
355
- ```json
356
- { "operation": "created", "id": "d4e5f6a7-...", "message": "Record created successfully" }
357
- ```
358
-
359
- ---
360
-
361
- ### 5. Relations (2 tools)
362
-
363
- #### `dataverse_associate`
364
-
365
- Creates an N:N or 1:N association between two records via a named relationship.
366
-
367
- | Parameter | Type | Req | Notes |
368
- |---|---|---|---|
369
- | `entitySetName` | `string` | | Source entity set |
370
- | `id` | `string (UUID)` | ✓ | Source record GUID |
371
- | `relationshipName` | `string` | | Relationship schema name |
372
- | `relatedEntitySetName` | `string` | | Related entity set |
373
- | `relatedId` | `string (UUID)` | ✓ | Related record GUID |
374
-
375
- > "Associate contact c1d2e3f4 with account a1b2c3d4"
376
-
377
- ---
378
-
379
- #### `dataverse_disassociate`
380
-
381
- Removes an existing association. `relatedId` / `relatedEntitySetName` required for N:N.
382
-
383
- | Parameter | Type | Req | Notes |
384
- |---|---|---|---|
385
- | `entitySetName` | `string` | | Source entity set |
386
- | `id` | `string (UUID)` | ✓ | Source record GUID |
387
- | `relationshipName` | `string` | | Relationship schema name |
388
- | `relatedId` | `string (UUID)` | | Required for N:N |
389
- | `relatedEntitySetName` | `string` | | Required for N:N |
390
-
391
- > "Remove the association between account a1b2c3d4 and contact c1d2e3f4"
392
-
393
- ---
394
-
395
- ### 6. Actions & Functions (6 tools)
396
-
397
- #### `dataverse_execute_action`
398
-
399
- Executes a global (unbound) Dataverse action — state-changing, not tied to a specific record.
400
-
401
- | Parameter | Type | Req | Notes |
402
- |---|---|---|---|
403
- | `actionName` | `string` | | Action logical name (e.g. `"WinOpportunity"`) |
404
- | `parameters` | `object` | — | Action parameters (default `{}`) |
405
-
406
- > "Execute the WinOpportunity action"
407
-
408
- ---
409
-
410
- #### `dataverse_execute_function`
411
-
412
- Executes a global (unbound) OData function — read-only, no side effects.
413
-
414
- | Parameter | Type | Req | Notes |
415
- |---|---|---|---|
416
- | `functionName` | `string` | | e.g. `"RetrieveTotalRecordCount"` |
417
- | `parameters` | `object` | — | Key-value pairs serialized into URL |
418
-
419
- > "Get total record count for the account entity"
420
-
421
- ---
422
-
423
- #### `dataverse_execute_bound_action`
424
-
425
- Executes an action bound to a specific record. Do **not** include the `Microsoft.Dynamics.CRM.` namespace prefix.
426
-
427
- | Parameter | Type | Req | Notes |
428
- |---|---|---|---|
429
- | `entitySetName` | `string` | | OData entity set name |
430
- | `id` | `string (UUID)` | ✓ | Record GUID |
431
- | `actionName` | `string` | | Action name (no namespace prefix) |
432
- | `parameters` | `object` | | Action parameters |
433
-
434
- > "Qualify the lead l1m2n3o4 in Dataverse"
435
-
436
- ---
437
-
438
- #### `dataverse_execute_bound_function`
439
-
440
- Executes a read-only function bound to a specific record (e.g. `CalculateRollupField`).
441
-
442
- | Parameter | Type | Req | Notes |
443
- |---|---|---|---|
444
- | `entitySetName` | `string` | | OData entity set name |
445
- | `id` | `string (UUID)` | ✓ | Record GUID |
446
- | `functionName` | `string` | | Bound function name |
447
- | `parameters` | `object` | | Key-value pairs serialized into URL |
448
-
449
- > "Calculate the rollup field 'totalrevenue' on account a1b2c3d4"
450
-
451
- ---
452
-
453
- #### `dataverse_list_dependencies`
454
-
455
- Lists workflows, flows, business rules, and custom actions that reference a table. Use before modifying or removing a table.
456
-
457
- | Parameter | Type | Req | Notes |
458
- |---|---|---|---|
459
- | `tableName` | `string` | | Table logical name |
460
- | `componentType` | `string[]` | — | Filter: `Workflow`, `Flow`, `BusinessRule`, `Action`, `BusinessProcessFlow`, `Plugin`, `CustomAPI` |
461
-
462
- > "What workflows and flows reference the account table?"
463
-
464
- ---
465
-
466
- #### `dataverse_retrieve_dependencies_for_delete`
467
-
468
- Returns solution components that would block deletion of a component. Use before deleting customizations.
469
-
470
- | Parameter | Type | Req | Notes |
471
- |---|---|---|---|
472
- | `componentType` | `number` | | Type code (1=Entity, 2=Attribute, 29=Workflow, 90=PluginAssembly…) |
473
- | `objectId` | `string (UUID)` | ✓ | Component GUID |
474
-
475
- > "Check dependencies blocking deletion of workflow w1x2y3z4"
476
-
477
- ---
478
-
479
- ### 7. Batch (1 tool)
480
-
481
- #### `dataverse_batch_execute`
482
-
483
- Executes up to 1000 operations in a single `$batch` request. Set `useChangeset=true` to wrap mutations atomically (all-or-nothing rollback).
484
-
485
- | Parameter | Type | Req | Notes |
486
- |---|---|---|---|
487
- | `requests` | `array` | | 1–1000 operation objects |
488
- | `requests[].method` | `"GET"\|"POST"\|"PATCH"\|"DELETE"` | ✓ | HTTP method |
489
- | `requests[].url` | `string` | | Relative URL, e.g. `"accounts(guid)"` |
490
- | `requests[].body` | `object` | | Body for POST/PATCH |
491
- | `useChangeset` | `boolean` | | Atomic changeset (default `false`) |
492
-
493
- > "Create 3 contacts in a single atomic batch"
494
-
495
- ```json
496
- { "results": [...], "count": 3 }
497
- ```
498
-
499
- ---
500
-
501
- ### 8. Change Tracking (1 tool)
502
-
503
- #### `dataverse_change_detection`
504
-
505
- Delta-query for incremental sync. Pass `deltaToken: null` for initial snapshot; use returned `nextDeltaToken` for subsequent calls. Requires change tracking enabled on the table.
506
-
507
- | Parameter | Type | Req | Notes |
508
- |---|---|---|---|
509
- | `entitySetName` | `string` | | OData entity set name |
510
- | `deltaToken` | `string\|null` | ✓ | `null` for initial sync |
511
- | `select` | `string[]` | | Columns to return |
512
-
513
- > "Get all changes to accounts since my last sync (token: abc123)"
514
-
515
- ```json
516
- { "newAndModified": [...], "deleted": [{"id":"..."}], "nextDeltaToken": "12345!..." }
517
- ```
518
-
519
- ---
520
-
521
- ### 9. Solutions (3 tools)
522
-
523
- #### `dataverse_list_solutions`
524
-
525
- Lists solutions in the environment. By default returns only **unmanaged** solutions.
526
-
527
- | Parameter | Type | Req | Notes |
528
- |---|---|---|---|
529
- | `includeManaged` | `boolean` | | Include managed solutions (default `false`) |
530
- | `nameFilter` | `string` | — | Contains-match on unique name |
531
- | `top` | `number` | | Default `50`, max `200` |
532
-
533
- > "List all unmanaged solutions in my environment"
534
-
535
- ```json
536
- { "solutions": [{ "uniqueName": "MySolution", "version": "1.0.0.0", "isManaged": false }], "count": 1 }
537
- ```
538
-
539
- ---
540
-
541
- #### `dataverse_solution_components`
542
-
543
- Lists all components in a named solution. Use the **unique** solution name, not the display name.
544
-
545
- | Parameter | Type | Req | Notes |
546
- |---|---|---|---|
547
- | `solutionName` | `string` | ✓ | Unique solution name |
548
- | `componentType` | `number` | — | Type code filter (1=Entity, 29=Workflow, 90=PluginAssembly, 97=WebResource…) |
549
- | `top` | `number` | — | Default `200`, max `5000` |
550
-
551
- > "List all entities in the 'MySolution' solution"
552
-
553
- ---
554
-
555
- #### `dataverse_publish_customizations`
556
-
557
- Publishes unpublished customizations. Omit `components` to publish all (equivalent to "Publish All" in maker portal). **Can take 30–120 s in large environments.**
558
-
559
- | Parameter | Type | Req | Notes |
560
- |---|---|---|---|
561
- | `components.entities` | `string[]` | — | Entity logical names |
562
- | `components.webResources` | `string[]` | — | Web resource names |
563
- | `components.optionSets` | `string[]` | — | Global option set names |
564
-
565
- > "Publish all pending customizations"
566
-
567
- ---
568
-
569
- ### 10. Impersonation (1 tool)
570
-
571
- #### `dataverse_impersonate`
572
-
573
- Executes any other tool on behalf of a different Dataverse user by injecting `MSCRMCallerId`. Applies only to the single wrapped call; cleaned up in `finally`. Requires `prvActOnBehalfOfAnotherUser`.
574
-
575
- | Parameter | Type | Req | Notes |
576
- |---|---|---|---|
577
- | `callerId` | `string (UUID)` | ✓ | Azure AD Object ID of the user to impersonate |
578
- | `toolName` | `string` | ✓ | MCP tool name (e.g. `"dataverse_create"`) |
579
- | `toolArgs` | `object` | ✓ | Arguments for the wrapped tool |
580
-
581
- > "Create a contact as user john@contoso.com (ID: u1v2w3x4-...)"
582
-
583
- ```json
584
- { "impersonatedAs": "u1v2w3x4-...", "tool": "dataverse_create", "result": { "id": "..." } }
585
- ```
586
-
587
- ---
588
-
589
- ### 11. Customization (3 tools)
590
-
591
- #### `dataverse_list_custom_actions`
592
-
593
- Lists all public SDK messages (custom APIs) registered in the environment.
594
-
595
- | Parameter | Type | Req | Notes |
596
- |---|---|---|---|
597
- | `top` | `number` | — | Default `100`, max `500` |
598
- | `nameFilter` | `string` | — | Substring match on message name |
599
-
600
- > "List all custom actions registered in the environment"
601
-
602
- ```json
603
- { "total": 5, "messages": [{ "name": "new_MyAction", "category": "", "asyncSupported": true }] }
604
- ```
605
-
606
- ---
607
-
608
- #### `dataverse_list_plugin_steps`
609
-
610
- Lists plugin step registrations (SdkMessageProcessingStep): assembly, message, entity, stage (pre/post), mode (sync/async), and enabled state.
611
-
612
- | Parameter | Type | Req | Notes |
613
- |---|---|---|---|
614
- | `top` | `number` | — | Default `100`, max `500` |
615
- | `activeOnly` | `boolean` | — | Default `true` |
616
- | `entityLogicalName` | `string` | — | Filter by entity |
617
-
618
- > "Show me all active plugin steps for the account entity"
619
-
620
- ---
621
-
622
- #### `dataverse_set_workflow_state`
623
-
624
- Activates or deactivates a classic Dataverse workflow (statecode/statuscode update).
625
-
626
- | Parameter | Type | Req | Notes |
627
- |---|---|---|---|
628
- | `workflowId` | `string (UUID)` | | Workflow GUID |
629
- | `activate` | `boolean` | ✓ | `true` = Activated, `false` = Draft |
630
-
631
- > "Activate workflow w1x2y3z4"
632
-
633
- ```json
634
- { "workflowId": "...", "newState": "Activated", "statecode": 1, "statuscode": 2 }
635
- ```
636
-
637
- ---
638
-
639
- ### 12. Environment (2 tools)
640
-
641
- #### `dataverse_get_environment_variable`
642
-
643
- Retrieves an environment variable's definition (type, default value) and current override value.
644
-
645
- | Parameter | Type | Req | Notes |
646
- |---|---|---|---|
647
- | `schemaName` | `string` | ✓ | Schema name, e.g. `"new_MyConfig"` |
648
-
649
- > "What is the current value of environment variable new_ApiEndpoint?"
650
-
651
- ```json
652
- { "schemaName": "new_ApiEndpoint", "typeName": "String", "defaultValue": "https://...", "currentValue": "https://override...", "effectiveValue": "https://override..." }
653
- ```
654
-
655
- ---
656
-
657
- #### `dataverse_set_environment_variable`
658
-
659
- Sets or updates an environment variable's current value (creates or updates the value record).
660
-
661
- | Parameter | Type | Req | Notes |
662
- |---|---|---|---|
663
- | `schemaName` | `string` | ✓ | Schema name of the environment variable |
664
- | `value` | `string` | ✓ | New value to set |
665
-
666
- > "Set environment variable new_FeatureFlag to 'true'"
667
-
668
- ---
669
-
670
- ### 13. Trace (2 tools)
671
-
672
- #### `dataverse_get_plugin_trace_logs`
673
-
674
- Retrieves plugin execution trace logs: type name, triggering message, entity, duration, trace output, and exception details. Requires Plugin Trace Log enabled in Dataverse settings.
675
-
676
- | Parameter | Type | Req | Notes |
677
- |---|---|---|---|
678
- | `top` | `number` | — | Default `50`, max `200` |
679
- | `pluginTypeFilter` | `string` | — | Substring match on type name |
680
- | `messageFilter` | `string` | — | e.g. `"Create"`, `"Update"` |
681
- | `entityFilter` | `string` | — | Entity logical name |
682
- | `exceptionsOnly` | `boolean` | — | Only traces with exceptions (default `false`) |
683
-
684
- > "Show me recent plugin failures on the account table"
685
-
686
- ---
687
-
688
- #### `dataverse_get_workflow_trace_logs`
689
-
690
- Retrieves AsyncOperation records for background/classic workflow executions. Not for modern cloud flows (use Power Automate portal for those).
691
-
692
- | Parameter | Type | Req | Notes |
693
- |---|---|---|---|
694
- | `top` | `number` | — | Default `50`, max `200` |
695
- | `failedOnly` | `boolean` | | Only failed executions (default `false`) |
696
- | `entityFilter` | `string` | — | Filter by regarding entity type |
697
-
698
- > "Show me failed classic workflows for the last 50 executions"
699
-
700
- ---
701
-
702
- ### 14. Search (1 tool)
703
-
704
- #### `dataverse_search`
705
-
706
- Full-text Relevance Search across all configured Dataverse tables. Returns ranked results with entity name, record ID, score, highlights, and matched fields. Requires **Relevance Search** enabled in Dataverse admin.
707
-
708
- | Parameter | Type | Req | Notes |
709
- |---|---|---|---|
710
- | `query` | `string` | | Search string; Lucene syntax with `searchType=full` |
711
- | `entities` | `string[]` | — | Restrict to specific tables |
712
- | `top` | `number` | — | Default `10`, max `50` |
713
- | `searchMode` | `"any"\|"all"` | | Match any/all terms (default `"any"`) |
714
- | `searchType` | `"simple"\|"full"` | — | Default `"simple"` |
715
- | `filter` | `string` | — | OData `$filter` on results |
716
- | `facets` | `string[]` | — | Fields for faceted counts |
717
- | `orderby` | `string[]` | — | Sort fields, e.g. `["@search.score desc"]` |
718
- | `select` | `string[]` | — | Fields to return per result |
719
-
720
- > "Find all records mentioning 'Contoso' across accounts and contacts"
721
-
722
- ```json
723
- { "totalRecordCount": 12, "results": [{ "entityName": "account", "objectId": "...", "score": 0.9 }] }
724
- ```
725
-
726
- ---
727
-
728
- ### 15. Audit (1 tool)
729
-
730
- #### `dataverse_get_audit_log`
731
-
732
- Retrieves audit log entries with operation type, user info, and parsed change data. At least one filter is recommended. Requires auditing enabled on the environment and table.
733
-
734
- | Parameter | Type | Req | Notes |
735
- |---|---|---|---|
736
- | `recordId` | `string (UUID)` | — | Specific record GUID |
737
- | `entityLogicalName` | `string` | | Filter by entity type |
738
- | `userId` | `string (UUID)` | | Filter by user |
739
- | `fromDate` | `string` | | ISO 8601 date (entries on/after) |
740
- | `top` | `number` | — | Default `50`, max `500` |
741
- | `operations` | `string[]` | — | `"Create"`, `"Update"`, `"Delete"`, `"Activate"`, `"Deactivate"`, `"Share"`, `"Assign"`, `"Access"` |
742
-
743
- > "Show me all updates to account a1b2c3d4 in the last week"
744
-
745
- ---
746
-
747
- ### 16. Quality (1 tool)
748
-
749
- #### `dataverse_detect_duplicates`
750
-
751
- Checks prospective record fields against existing records using Dataverse built-in duplicate detection rules.
752
-
753
- | Parameter | Type | Req | Notes |
754
- |---|---|---|---|
755
- | `entityLogicalName` | `string` | ✓ | Table to check, e.g. `"account"` |
756
- | `record` | `object` | ✓ | Field values of the prospective record |
757
- | `top` | `number` | — | Default `5`, max `20` |
758
-
759
- > "Check if there's already an account named 'Contoso Ltd'"
760
-
761
- ```json
762
- { "hasDuplicates": true, "duplicateCount": 1, "duplicates": [{ "accountid": "...", "name": "Contoso Ltd" }] }
763
- ```
764
-
765
- ---
766
-
767
- ### 17. Annotations (2 tools)
768
-
769
- #### `dataverse_get_annotations`
770
-
771
- Retrieves notes and file attachments linked to a record. Set `includeContent=true` to fetch base64 file bodies (can be very large).
772
-
773
- | Parameter | Type | Req | Notes |
774
- |---|---|---|---|
775
- | `recordId` | `string (UUID)` | | Parent record GUID |
776
- | `includeContent` | `boolean` | — | Include `documentbody` (default `false`) |
777
- | `top` | `number` | — | Default `20`, max `100` |
778
- | `mimeTypeFilter` | `string` | — | e.g. `"application/pdf"` |
779
-
780
- > "Get all notes attached to account a1b2c3d4"
781
-
782
- ---
783
-
784
- #### `dataverse_create_annotation`
785
-
786
- Creates a note or file attachment linked to a Dataverse record. Provide `notetext` for a text note, `documentbody` (base64) for a file, or both.
787
-
788
- | Parameter | Type | Req | Notes |
789
- |---|---|---|---|
790
- | `recordId` | `string (UUID)` | ✓ | Parent record GUID |
791
- | `entitySetName` | `string` | | Parent entity set, e.g. `"accounts"` |
792
- | `notetext` | `string` | — | Text content (required if no `documentbody`) |
793
- | `subject` | `string` | — | Note subject/title |
794
- | `filename` | `string` | — | File name (for attachments) |
795
- | `mimetype` | `string` | — | MIME type, e.g. `"application/pdf"` |
796
- | `documentbody` | `string` | — | Base64-encoded file content (required if no `notetext`) |
797
-
798
- > "Attach a PDF report to account a1b2c3d4"
799
-
800
- ---
801
-
802
- ### 18. Users (2 tools)
803
-
804
- #### `dataverse_list_users`
805
-
806
- Searches Dataverse system users by name or email. At least one of `search` or `businessUnitId` is required. Excludes application users and disabled users by default.
807
-
808
- | Parameter | Type | Req | Notes |
809
- |---|---|---|---|
810
- | `search` | `string` | ✓* | Full-name or email contains-search (*one of these required*) |
811
- | `businessUnitId` | `string (UUID)` | ✓* | Restrict to a business unit |
812
- | `includeDisabled` | `boolean` | — | Default `false` |
813
- | `includeApplicationUsers` | `boolean` | — | Default `false` |
814
- | `top` | `number` | — | Default `20`, max `100` |
815
-
816
- > "Find all users named 'John' in my environment"
817
-
818
- ---
819
-
820
- #### `dataverse_get_user_roles`
821
-
822
- Returns all security roles assigned to a system user.
823
-
824
- | Parameter | Type | Req | Notes |
825
- |---|---|---|---|
826
- | `userId` | `string (UUID)` | ✓ | System user GUID |
827
-
828
- > "What security roles does user u1v2w3x4 have?"
829
-
830
- ```json
831
- { "userId": "...", "fullname": "John Doe", "roles": [{ "name": "Sales Manager", "roleId": "..." }], "roleCount": 1 }
832
- ```
833
-
834
- ---
835
-
836
- ### 19. Views (1 tool)
837
-
838
- #### `dataverse_list_views`
839
-
840
- Lists saved (system) and optionally personal views for a Dataverse table, including view name, ID, default flag, query type, and description.
841
-
842
- | Parameter | Type | Req | Notes |
843
- |---|---|---|---|
844
- | `entityLogicalName` | `string` | | Table logical name, e.g. `"account"` |
845
- | `includePersonal` | `boolean` | | Include personal views (default `false`) |
846
- | `top` | `number` | | Max per category, default `20`, max `100` |
847
-
848
- > "List all system views for the account table"
849
-
850
- ```json
851
- { "entityLogicalName": "account", "systemViews": [{ "id": "...", "name": "Active Accounts", "isDefault": true }], "systemViewCount": 5 }
852
- ```
853
-
854
- ---
855
-
856
- ### 20. Files (2 tools)
857
-
858
- #### `dataverse_upload_file_column`
859
-
860
- Uploads a file to a Dataverse **file-type column** on a record. File content must be base64-encoded.
861
-
862
- | Parameter | Type | Req | Notes |
863
- |---|---|---|---|
864
- | `entitySetName` | `string` | | OData entity set name |
865
- | `recordId` | `string (UUID)` | ✓ | Record GUID |
866
- | `columnName` | `string` | ✓ | File column logical name (alphanumeric/underscore only) |
867
- | `fileContent` | `string` | ✓ | Base64-encoded file content |
868
- | `fileName` | `string` | ✓ | File name including extension, e.g. `"report.pdf"` |
869
-
870
- > "Upload report.pdf to the attachment column on account a1b2c3d4"
871
-
872
- ```json
873
- { "success": true, "recordId": "...", "columnName": "new_report", "fileName": "report.pdf", "sizeBytes": 204800 }
874
- ```
875
-
876
- ---
877
-
878
- #### `dataverse_download_file_column`
879
-
880
- Downloads a file from a Dataverse file-type column. Returns the file as a base64-encoded string with its name and size.
881
-
882
- | Parameter | Type | Req | Notes |
883
- |---|---|---|---|
884
- | `entitySetName` | `string` | | OData entity set name |
885
- | `recordId` | `string (UUID)` | ✓ | Record GUID |
886
- | `columnName` | `string` | | File column logical name |
887
-
888
- > "Download the attachment from account a1b2c3d4 column new_report"
889
-
890
- ```json
891
- { "fileName": "report.pdf", "sizeBytes": 204800, "contentBase64": "JVBERi0x..." }
892
- ```
893
-
894
- ---
895
-
896
- ### 21. Org (1 tool)
897
-
898
- #### `dataverse_list_business_units`
899
-
900
- Lists business units in the environment with name, ID, parent BU ID, disabled status, and creation date.
901
-
902
- | Parameter | Type | Req | Notes |
903
- |---|---|---|---|
904
- | `top` | `number` | — | Default `50`, max `200` |
905
- | `includeDisabled` | `boolean` | — | Include disabled BUs (default `false`) |
906
-
907
- > "List all active business units in my Dataverse environment"
908
-
909
- ---
910
-
911
- ## Error Handling & Retry Behavior
912
-
913
- All tool handlers return `{ isError: true, content: [{ type: "text", text: "Error: ..." }] }` on failure. Zod input validation runs before any network call.
914
-
915
- ### Retry Strategy
916
-
917
- | Status | Behavior | Attempts |
918
- |---|---|---|
919
- | **401** | Invalidate token, retry once with fresh token | 1 |
920
- | **429 / 503 / 504** | Exponential backoff (`2^attempt × 1000ms`) | `maxRetries` (default 3) |
921
- | **Other** | Throw immediately | 0 |
922
-
923
- Dataverse error bodies are formatted as `Dataverse error <code>: <message>`. Timeouts (`ECONNABORTED`) produce `Request timed out. Check your Dataverse environment URL.`
924
-
925
- ---
926
-
927
- ## Security
928
-
929
- | Mode | Flow | Use Case |
930
- |---|---|---|
931
- | **pac** | MSAL Public Client → device code + silent refresh | Local dev, interactive |
932
- | **msal** | MSAL Confidential Client → `client_credentials` | CI/CD, headless |
933
-
934
- - `clientSecret` is never logged or returned in tool responses.
935
- - OData path segments use `esc()` (single-quote doubling) to prevent OData injection.
936
- - `columnName` in file tools is validated against `/^[a-zA-Z0-9_]+$/` to prevent path traversal.
937
- - `MSCRMCallerId` for impersonation is set per-call and cleaned up in a `finally` block regardless of outcome.
938
- - `.msal-cache.json` should be in `.gitignore`. No HTTP endpoints are exposed (stdio only).
939
-
940
- ---
941
-
942
- ## Limitations & Known Constraints
943
-
944
- ### General
945
-
946
- | Limitation | Details |
947
- |---|---|
948
- | **Transport** | stdio only. Server must be spawned as a child process by the MCP host. |
949
- | **Single environment** | One Dataverse environment per server instance. Restart to switch. |
950
- | **No streaming** | Responses are complete JSON. Very large result sets may exceed AI model context limits. |
951
- | **No real-time subscriptions** | Use `dataverse_change_detection` for polling-based incremental sync. |
952
-
953
- ### Query
954
-
955
- | Limitation | Details |
956
- |---|---|
957
- | **`$top` max** | `dataverse_query` caps at 5000 per call. Use `dataverse_retrieve_multiple_with_paging` for more. |
958
- | **Paging max** | `dataverse_retrieve_multiple_with_paging` caps at 50,000 records total. |
959
- | **FetchXML entity set** | Auto-extraction appends `"s"` to the logical name. Non-standard entity set names require explicit `entitySetName`. |
960
-
961
- ### CRUD
962
-
963
- | Limitation | Details |
964
- |---|---|
965
- | **UUID required for get/update/delete** | Alternate-key retrieval via `dataverse_get` is not supported; use `dataverse_upsert` or `dataverse_query` instead. |
966
- | **No ETag conditional update** | `dataverse_update` sends `If-Match: *`. ETag-based optimistic concurrency is not exposed. |
967
-
968
- ### Authentication
969
-
970
- | Limitation | Details |
971
- |---|---|
972
- | **No certificate auth** | MSAL `clientSecret` only; certificate credentials not supported. |
973
- | **PAC token expiry** | If the refresh token expires, re-run `npm run auth:setup`. |
974
- | **MSAL multi-tenant** | Requires explicit `tenantId`; no `common` authority support. |
975
-
976
- ### Dependencies & Solutions
977
-
978
- | Limitation | Details |
979
- |---|---|
980
- | **Plugin/CustomAPI in `list_dependencies`** | Only Workflow/BusinessRule/Flow types fully supported. Plugin step queries require separate SDK message lookups not yet implemented. |
981
- | **`solutionName` in dependency results** | Always `null` — solution join not yet implemented. |
982
-
983
- ### Batch
984
-
985
- | Limitation | Details |
986
- |---|---|
987
- | **Max operations** | 1,000 per batch (Zod-enforced). |
988
- | **No `$<Content-ID>` references** | Cross-referencing created entities within a changeset is not supported at the tool level. |
989
-
990
- ---
991
-
992
- *This document reflects the MCP Dataverse server codebase as of v0.2.0 — 48 tools across 21 categories.*
1
+ # MCP Dataverse Server — Complete Capabilities Reference
2
+
3
+ > **Version**: 0.2.0 | **API Version**: Dataverse Web API v9.2 | **Transport**: MCP SDK over stdio
4
+
5
+ 50 tools across 22 categories for full Dataverse lifecycle: schema, CRUD, FetchXML, solutions, plugins, audit, files, users, teams, environment variables, and more.
6
+
7
+ ---
8
+
9
+ ## Table of Contents
10
+
11
+ - [Quick Start](#quick-start)
12
+ - [Architecture Overview](#architecture-overview)
13
+ - [Tool Reference (50 tools)](#tool-reference-50-tools)
14
+ - [1. Auth (1)](#1-auth-1-tool)
15
+ - [2. Metadata (8)](#2-metadata-8-tools)
16
+ - [3. Query (3)](#3-query-3-tools)
17
+ - [4. CRUD (5)](#4-crud-5-tools)
18
+ - [5. Relations (2)](#5-relations-2-tools)
19
+ - [6. Actions & Functions (6)](#6-actions--functions-6-tools)
20
+ - [7. Batch (1)](#7-batch-1-tool)
21
+ - [8. Change Tracking (1)](#8-change-tracking-1-tool)
22
+ - [9. Solutions (3)](#9-solutions-3-tools)
23
+ - [10. Impersonation (1)](#10-impersonation-1-tool)
24
+ - [11. Customization (3)](#11-customization-3-tools)
25
+ - [12. Environment (2)](#12-environment-2-tools)
26
+ - [13. Trace (2)](#13-trace-2-tools)
27
+ - [14. Search (1)](#14-search-1-tool)
28
+ - [15. Audit (1)](#15-audit-1-tool)
29
+ - [16. Quality (1)](#16-quality-1-tool)
30
+ - [17. Annotations (2)](#17-annotations-2-tools)
31
+ - [18. Users (2)](#18-users-2-tools)
32
+ - [19. Views (1)](#19-views-1-tool)
33
+ - [20. Files (2)](#20-files-2-tools)
34
+ - [21. Org (1)](#21-org-1-tool)
35
+ - [Error Handling & Retry Behavior](#error-handling--retry-behavior)
36
+ - [Security](#security)
37
+ - [Limitations & Known Constraints](#limitations--known-constraints)
38
+
39
+ ---
40
+
41
+ ## Quick Start
42
+
43
+ ### Prerequisites
44
+
45
+ | Requirement | Details |
46
+ | ------------------------- | ------------------------------------------------------------- |
47
+ | **Node.js** | v20+ |
48
+ | **Dataverse Environment** | Active URL (`https://<org>.crm<N>.dynamics.com`) |
49
+ | **Authentication** | PAC CLI (interactive) or Entra ID App Registration (headless) |
50
+
51
+ ### Installation & Configuration
52
+
53
+ ```bash
54
+ npm install && npm run build
55
+ ```
56
+
57
+ Create `config.json` (see `config.example.json`):
58
+
59
+ ```json
60
+ {
61
+ "environmentUrl": "https://yourorg.crm.dynamics.com",
62
+ "authMode": "pac",
63
+ "pacProfileName": "default",
64
+ "tenantId": "",
65
+ "clientId": "",
66
+ "clientSecret": "",
67
+ "requestTimeoutMs": 30000,
68
+ "maxRetries": 3
69
+ }
70
+ ```
71
+
72
+ | Field | Type | Description |
73
+ | ---------------------------------------- | --------------- | ------------------------------------------------------- |
74
+ | `environmentUrl` | `string` | Dataverse environment URL (**required**, must be HTTPS) |
75
+ | `authMode` | `"pac"\|"msal"` | Auth strategy (default: `"pac"`) |
76
+ | `pacProfileName` | `string` | PAC CLI profile (default: `"default"`) |
77
+ | `tenantId` / `clientId` / `clientSecret` | `string` | Required for `authMode: "msal"` |
78
+ | `requestTimeoutMs` | `number` | HTTP timeout ms (default: `30000`) |
79
+ | `maxRetries` | `number` | Max retry attempts 0–10 (default: `3`) |
80
+
81
+ Env vars override config: `DATAVERSE_ENV_URL`, `AUTH_MODE`, `PAC_PROFILE_NAME`, `TENANT_ID`, `CLIENT_ID`, `REQUEST_TIMEOUT_MS`, `MAX_RETRIES`.
82
+
83
+ ### Authentication
84
+
85
+ **PAC (interactive):** `npm run auth:setup` then `npm run start`. Uses Microsoft's public Platform CLI App ID — no registration required. Tokens cached in `.msal-cache.json`.
86
+
87
+ **MSAL (headless/CI):** Set `authMode: "msal"` with `tenantId`, `clientId`, `clientSecret`. Tokens acquired via OAuth2 `client_credentials`.
88
+
89
+ ### Running
90
+
91
+ ```bash
92
+ npm run start # production
93
+ npm run dev # watch mode
94
+ ```
95
+
96
+ Server communicates over **stdio** (MCP SDK `StdioServerTransport`). Connect from any MCP host (VS Code Copilot, Claude Desktop, etc.) by configuring it to spawn the process.
97
+
98
+ ---
99
+
100
+ ## Architecture Overview
101
+
102
+ ```mermaid
103
+ graph LR
104
+ MCP["MCP Dataverse Server<br/><i>50 tools · 22 categories</i>"]
105
+
106
+ MCP --> AUTH["🔑 Auth (1)"]
107
+ MCP --> META["📋 Metadata (8)"]
108
+ MCP --> QUERY["🔍 Query (3)"]
109
+ MCP --> CRUD["✏️ CRUD (5)"]
110
+ MCP --> REL["🔗 Relations (2)"]
111
+ MCP --> ACT["⚡ Actions & Functions (6)"]
112
+ MCP --> BATCH["📦 Batch (1)"]
113
+ MCP --> TRACK["🔄 Change Tracking (1)"]
114
+ MCP --> SOL["🧩 Solutions (3)"]
115
+ MCP --> IMP["👤 Impersonation (1)"]
116
+ MCP --> CUST["🔧 Customization (3)"]
117
+ MCP --> ENV["⚙️ Environment (2)"]
118
+ MCP --> TRACE["🔎 Trace (2)"]
119
+ MCP --> SRCH["🔍 Search (1)"]
120
+ MCP --> AUDIT["📜 Audit (1)"]
121
+ MCP --> QUAL["✅ Quality (1)"]
122
+ MCP --> NOTE["📝 Annotations (2)"]
123
+ MCP --> USR["👥 Users (2)"]
124
+ MCP --> VIEWS["👁️ Views (1)"]
125
+ MCP --> FILES["📁 Files (2)"]
126
+ MCP --> ORG["🏢 Org (1)"]
127
+ ```
128
+
129
+ All tool handlers validate inputs with **Zod** before calling the `DataverseAdvancedClient`. Auth tokens are cached and refreshed proactively; transient errors (429, 503, 504) are retried with exponential backoff.
130
+
131
+ ---
132
+
133
+ ## Tool Reference (50 tools)
134
+
135
+ ### 1. Auth (1 tool)
136
+
137
+ #### `dataverse_whoami`
138
+
139
+ Returns the current authenticated user context (userId, businessUnitId, organizationId, organizationName, environmentUrl). No parameters required.
140
+
141
+ > "Who am I in Dataverse?"
142
+
143
+ ---
144
+
145
+ ### 2. Metadata (8 tools)
146
+
147
+ #### `dataverse_list_tables`
148
+
149
+ Lists all Dataverse tables. Defaults to custom tables only; set `includeSystemTables=true` for all ~1700+ system tables.
150
+
151
+ | Parameter | Type | Req | Notes |
152
+ | --------------------- | --------- | --- | --------------- |
153
+ | `includeSystemTables` | `boolean` | — | Default `false` |
154
+
155
+ > "List all custom tables in my environment"
156
+
157
+ ---
158
+
159
+ #### `dataverse_get_table_metadata`
160
+
161
+ Returns full schema for a table: column logical names, display names, data types, required levels, lookup targets. Set `includeAttributes=false` for table-level info only.
162
+
163
+ | Parameter | Type | Req | Notes |
164
+ | ------------------- | --------- | --- | ---------------- |
165
+ | `logicalName` | `string` | ✓ | e.g. `"account"` |
166
+ | `includeAttributes` | `boolean` | — | Default `true` |
167
+
168
+ > "Show me the schema for the contact table"
169
+
170
+ ---
171
+
172
+ #### `dataverse_get_relationships`
173
+
174
+ Returns 1:N, N:1, and N:N relationships for a table. Use to find the correct `relationshipName` for associate/disassociate.
175
+
176
+ | Parameter | Type | Req | Notes |
177
+ | ------------------ | ----------------------------------------------- | --- | ------------------ |
178
+ | `logicalName` | `string` | ✓ | Table logical name |
179
+ | `relationshipType` | `"OneToMany"\|"ManyToOne"\|"ManyToMany"\|"All"` | — | Default `"All"` |
180
+
181
+ > "What relationships does the account table have?"
182
+
183
+ ---
184
+
185
+ #### `dataverse_list_global_option_sets`
186
+
187
+ Lists all global (shared) option sets in the environment. No parameters required.
188
+
189
+ > "List all global option sets in my environment"
190
+
191
+ ---
192
+
193
+ #### `dataverse_get_option_set`
194
+
195
+ Returns all labels and integer values for a named global option set.
196
+
197
+ | Parameter | Type | Req | Notes |
198
+ | --------- | -------- | --- | ---------------------- |
199
+ | `name` | `string` | ✓ | Global option set name |
200
+
201
+ > "Show me the values for the budgetstatus option set"
202
+
203
+ ---
204
+
205
+ #### `dataverse_get_entity_key`
206
+
207
+ Returns alternate key definitions for a table: key attributes, index status (Active/InProgress/Failed), and customizability.
208
+
209
+ | Parameter | Type | Req | Notes |
210
+ | ----------- | -------- | --- | ------------------ |
211
+ | `tableName` | `string` | ✓ | Table logical name |
212
+
213
+ > "What alternate keys are defined on the account table?"
214
+
215
+ ---
216
+
217
+ #### `dataverse_get_attribute_option_set`
218
+
219
+ Returns all labels and integer values for a table-specific Picklist, Status, or State attribute. Use instead of `dataverse_get_option_set` for column-local choices.
220
+
221
+ | Parameter | Type | Req | Notes |
222
+ | ---------------------- | -------- | --- | ------------------------------------- |
223
+ | `entityLogicalName` | `string` | ✓ | e.g. `"account"` |
224
+ | `attributeLogicalName` | `string` | ✓ | e.g. `"statuscode"`, `"industrycode"` |
225
+
226
+ > "What are the valid statuscode values for opportunity?"
227
+
228
+ ---
229
+
230
+ ### 3. Query (3 tools)
231
+
232
+ #### `dataverse_query`
233
+
234
+ OData query with `$filter`, `$select`, `$orderby`, `$top`, `$expand`, `$count`, and `$apply` (aggregations).
235
+
236
+ | Parameter | Type | Req | Notes |
237
+ | --------------- | ---------- | --- | ------------------------------- |
238
+ | `entitySetName` | `string` | ✓ | e.g. `"accounts"` |
239
+ | `select` | `string[]` | — | Recommended — minimizes payload |
240
+ | `filter` | `string` | — | OData `$filter` expression |
241
+ | `orderby` | `string` | — | OData `$orderby` expression |
242
+ | `top` | `number` | — | Default `50`, max `5000` |
243
+ | `expand` | `string` | — | OData `$expand` |
244
+ | `count` | `boolean` | — | Include `@odata.count` |
245
+
246
+ > "Show me the first 10 active accounts sorted by name"
247
+
248
+ ---
249
+
250
+ #### `dataverse_execute_fetchxml`
251
+
252
+ Executes a FetchXML query — use for aggregations, complex joins, many-to-many traversal, and filters not expressible in OData.
253
+
254
+ | Parameter | Type | Req | Notes |
255
+ | --------------- | -------- | --- | ---------------------------------------------------- |
256
+ | `fetchXml` | `string` | ✓ | Complete FetchXML string |
257
+ | `entitySetName` | `string` | — | Auto-extracted from `<entity name="...">` if omitted |
258
+
259
+ > "Run a FetchXML query to count opportunities grouped by status"
260
+
261
+ ---
262
+
263
+ #### `dataverse_retrieve_multiple_with_paging`
264
+
265
+ Retrieves ALL matching records by auto-following `@odata.nextLink` pages. Use when > 5000 records are needed.
266
+
267
+ | Parameter | Type | Req | Notes |
268
+ | --------------- | ---------- | --- | --------------------------- |
269
+ | `entitySetName` | `string` | ✓ | OData entity set name |
270
+ | `select` | `string[]` | — | Columns to return |
271
+ | `filter` | `string` | — | OData `$filter` |
272
+ | `orderby` | `string` | — | OData `$orderby` |
273
+ | `expand` | `string` | — | OData `$expand` |
274
+ | `maxTotal` | `number` | — | Default `5000`, max `50000` |
275
+
276
+ > "Get all contacts with email addresses, up to 20000 records"
277
+
278
+ ```json
279
+ { "records": [...], "totalRetrieved": 12345, "pageCount": 3 }
280
+ ```
281
+
282
+ ---
283
+
284
+ ### 4. CRUD (5 tools)
285
+
286
+ #### `dataverse_get`
287
+
288
+ Retrieves a single record by GUID. Returns the record plus `@odata.etag`.
289
+
290
+ | Parameter | Type | Req | Notes |
291
+ | --------------- | --------------- | --- | --------------------- |
292
+ | `entitySetName` | `string` | ✓ | OData entity set name |
293
+ | `id` | `string (UUID)` | ✓ | Record GUID |
294
+ | `select` | `string[]` | — | Columns to return |
295
+
296
+ > "Get the account record with ID a1b2c3d4-..."
297
+
298
+ ---
299
+
300
+ #### `dataverse_create`
301
+
302
+ Creates a new record and returns its GUID. Use `@odata.bind` for lookup fields.
303
+
304
+ | Parameter | Type | Req | Notes |
305
+ | --------------- | -------- | --- | ----------------------------------------- |
306
+ | `entitySetName` | `string` | ✓ | OData entity set name |
307
+ | `data` | `object` | ✓ | Field key-value pairs using logical names |
308
+
309
+ > "Create a new account called 'Fabrikam Inc.'"
310
+
311
+ ---
312
+
313
+ #### `dataverse_update`
314
+
315
+ PATCH-updates an existing record (only supplied fields change). Sends `If-Match: *`.
316
+
317
+ | Parameter | Type | Req | Notes |
318
+ | --------------- | --------------- | --- | --------------------- |
319
+ | `entitySetName` | `string` | ✓ | OData entity set name |
320
+ | `id` | `string (UUID)` | ✓ | Record GUID |
321
+ | `data` | `object` | ✓ | Fields to update |
322
+
323
+ > "Update account a1b2c3d4 — set revenue to 5000000"
324
+
325
+ ---
326
+
327
+ #### `dataverse_delete`
328
+
329
+ Permanently deletes a record. **Irreversible.** `confirm` must be `true`.
330
+
331
+ | Parameter | Type | Req | Notes |
332
+ | --------------- | --------------- | --- | ------------------------- |
333
+ | `entitySetName` | `string` | ✓ | OData entity set name |
334
+ | `id` | `string (UUID)` | ✓ | Record GUID |
335
+ | `confirm` | `boolean` | ✓ | Must be `true` to proceed |
336
+
337
+ > "Delete account record a1b2c3d4-..."
338
+
339
+ ---
340
+
341
+ #### `dataverse_upsert`
342
+
343
+ Create-or-update via an alternate key (no GUID needed). Returns `"created"` or `"updated"`.
344
+
345
+ | Parameter | Type | Req | Notes |
346
+ | ------------------- | -------------------------------------- | --- | ---------------------------- |
347
+ | `entitySetName` | `string` | ✓ | OData entity set name |
348
+ | `alternateKey` | `string` | ✓ | Alternate key attribute name |
349
+ | `alternateKeyValue` | `string` | ✓ | Value for the alternate key |
350
+ | `data` | `object` | ✓ | Record data |
351
+ | `mode` | `"upsert"\|"createOnly"\|"updateOnly"` | — | Default `"upsert"` |
352
+
353
+ > "Upsert account with external ID 'EXT-001', set name to 'Contoso'"
354
+
355
+ ```json
356
+ {
357
+ "operation": "created",
358
+ "id": "d4e5f6a7-...",
359
+ "message": "Record created successfully"
360
+ }
361
+ ```
362
+
363
+ ---
364
+
365
+ ### 5. Relations (2 tools)
366
+
367
+ #### `dataverse_associate`
368
+
369
+ Creates an N:N or 1:N association between two records via a named relationship.
370
+
371
+ | Parameter | Type | Req | Notes |
372
+ | ---------------------- | --------------- | --- | ------------------------ |
373
+ | `entitySetName` | `string` | ✓ | Source entity set |
374
+ | `id` | `string (UUID)` | ✓ | Source record GUID |
375
+ | `relationshipName` | `string` | ✓ | Relationship schema name |
376
+ | `relatedEntitySetName` | `string` | ✓ | Related entity set |
377
+ | `relatedId` | `string (UUID)` | ✓ | Related record GUID |
378
+
379
+ > "Associate contact c1d2e3f4 with account a1b2c3d4"
380
+
381
+ ---
382
+
383
+ #### `dataverse_disassociate`
384
+
385
+ Removes an existing association. `relatedId` / `relatedEntitySetName` required for N:N.
386
+
387
+ | Parameter | Type | Req | Notes |
388
+ | ---------------------- | --------------- | --- | ------------------------ |
389
+ | `entitySetName` | `string` | | Source entity set |
390
+ | `id` | `string (UUID)` | ✓ | Source record GUID |
391
+ | `relationshipName` | `string` | ✓ | Relationship schema name |
392
+ | `relatedId` | `string (UUID)` | — | Required for N:N |
393
+ | `relatedEntitySetName` | `string` | — | Required for N:N |
394
+
395
+ > "Remove the association between account a1b2c3d4 and contact c1d2e3f4"
396
+
397
+ ---
398
+
399
+ ### 6. Actions & Functions (6 tools)
400
+
401
+ #### `dataverse_execute_action`
402
+
403
+ Executes a global (unbound) Dataverse action state-changing, not tied to a specific record.
404
+
405
+ | Parameter | Type | Req | Notes |
406
+ | ------------ | -------- | --- | --------------------------------------------- |
407
+ | `actionName` | `string` | ✓ | Action logical name (e.g. `"WinOpportunity"`) |
408
+ | `parameters` | `object` | — | Action parameters (default `{}`) |
409
+
410
+ > "Execute the WinOpportunity action"
411
+
412
+ ---
413
+
414
+ #### `dataverse_execute_function`
415
+
416
+ Executes a global (unbound) OData function read-only, no side effects.
417
+
418
+ | Parameter | Type | Req | Notes |
419
+ | -------------- | -------- | --- | ----------------------------------- |
420
+ | `functionName` | `string` | ✓ | e.g. `"RetrieveTotalRecordCount"` |
421
+ | `parameters` | `object` | — | Key-value pairs serialized into URL |
422
+
423
+ > "Get total record count for the account entity"
424
+
425
+ ---
426
+
427
+ #### `dataverse_execute_bound_action`
428
+
429
+ Executes an action bound to a specific record. Do **not** include the `Microsoft.Dynamics.CRM.` namespace prefix.
430
+
431
+ | Parameter | Type | Req | Notes |
432
+ | --------------- | --------------- | --- | --------------------------------- |
433
+ | `entitySetName` | `string` | ✓ | OData entity set name |
434
+ | `id` | `string (UUID)` | ✓ | Record GUID |
435
+ | `actionName` | `string` | ✓ | Action name (no namespace prefix) |
436
+ | `parameters` | `object` | — | Action parameters |
437
+
438
+ > "Qualify the lead l1m2n3o4 in Dataverse"
439
+
440
+ ---
441
+
442
+ #### `dataverse_execute_bound_function`
443
+
444
+ Executes a read-only function bound to a specific record (e.g. `CalculateRollupField`).
445
+
446
+ | Parameter | Type | Req | Notes |
447
+ | --------------- | --------------- | --- | ----------------------------------- |
448
+ | `entitySetName` | `string` | ✓ | OData entity set name |
449
+ | `id` | `string (UUID)` | ✓ | Record GUID |
450
+ | `functionName` | `string` | ✓ | Bound function name |
451
+ | `parameters` | `object` | — | Key-value pairs serialized into URL |
452
+
453
+ > "Calculate the rollup field 'totalrevenue' on account a1b2c3d4"
454
+
455
+ ---
456
+
457
+ #### `dataverse_list_dependencies`
458
+
459
+ Lists workflows, flows, business rules, and custom actions that reference a table. Use before modifying or removing a table.
460
+
461
+ | Parameter | Type | Req | Notes |
462
+ | --------------- | ---------- | --- | -------------------------------------------------------------------------------------------------- |
463
+ | `tableName` | `string` | ✓ | Table logical name |
464
+ | `componentType` | `string[]` | — | Filter: `Workflow`, `Flow`, `BusinessRule`, `Action`, `BusinessProcessFlow`, `Plugin`, `CustomAPI` |
465
+
466
+ > "What workflows and flows reference the account table?"
467
+
468
+ ---
469
+
470
+ #### `dataverse_retrieve_dependencies_for_delete`
471
+
472
+ Returns solution components that would block deletion of a component. Use before deleting customizations.
473
+
474
+ | Parameter | Type | Req | Notes |
475
+ | --------------- | --------------- | --- | ------------------------------------------------------------------ |
476
+ | `componentType` | `number` | ✓ | Type code (1=Entity, 2=Attribute, 29=Workflow, 90=PluginAssembly…) |
477
+ | `objectId` | `string (UUID)` | ✓ | Component GUID |
478
+
479
+ > "Check dependencies blocking deletion of workflow w1x2y3z4"
480
+
481
+ ---
482
+
483
+ ### 7. Batch (1 tool)
484
+
485
+ #### `dataverse_batch_execute`
486
+
487
+ Executes up to 1000 operations in a single `$batch` request. Set `useChangeset=true` to wrap mutations atomically (all-or-nothing rollback).
488
+
489
+ | Parameter | Type | Req | Notes |
490
+ | ------------------- | ---------------------------------- | --- | ------------------------------------- |
491
+ | `requests` | `array` | | 1–1000 operation objects |
492
+ | `requests[].method` | `"GET"\|"POST"\|"PATCH"\|"DELETE"` | ✓ | HTTP method |
493
+ | `requests[].url` | `string` | ✓ | Relative URL, e.g. `"accounts(guid)"` |
494
+ | `requests[].body` | `object` | — | Body for POST/PATCH |
495
+ | `useChangeset` | `boolean` | — | Atomic changeset (default `false`) |
496
+
497
+ > "Create 3 contacts in a single atomic batch"
498
+
499
+ ```json
500
+ { "results": [...], "count": 3 }
501
+ ```
502
+
503
+ ---
504
+
505
+ ### 8. Change Tracking (1 tool)
506
+
507
+ #### `dataverse_change_detection`
508
+
509
+ Delta-query for incremental sync. Pass `deltaToken: null` for initial snapshot; use returned `nextDeltaToken` for subsequent calls. Requires change tracking enabled on the table.
510
+
511
+ | Parameter | Type | Req | Notes |
512
+ | --------------- | -------------- | --- | ----------------------- |
513
+ | `entitySetName` | `string` | ✓ | OData entity set name |
514
+ | `deltaToken` | `string\|null` | ✓ | `null` for initial sync |
515
+ | `select` | `string[]` | — | Columns to return |
516
+
517
+ > "Get all changes to accounts since my last sync (token: abc123)"
518
+
519
+ ```json
520
+ { "newAndModified": [...], "deleted": [{"id":"..."}], "nextDeltaToken": "12345!..." }
521
+ ```
522
+
523
+ ---
524
+
525
+ ### 9. Solutions (3 tools)
526
+
527
+ #### `dataverse_list_solutions`
528
+
529
+ Lists solutions in the environment. By default returns only **unmanaged** solutions.
530
+
531
+ | Parameter | Type | Req | Notes |
532
+ | ---------------- | --------- | --- | ------------------------------------------- |
533
+ | `includeManaged` | `boolean` | — | Include managed solutions (default `false`) |
534
+ | `nameFilter` | `string` | — | Contains-match on unique name |
535
+ | `top` | `number` | — | Default `50`, max `200` |
536
+
537
+ > "List all unmanaged solutions in my environment"
538
+
539
+ ```json
540
+ {
541
+ "solutions": [
542
+ { "uniqueName": "MySolution", "version": "1.0.0.0", "isManaged": false }
543
+ ],
544
+ "count": 1
545
+ }
546
+ ```
547
+
548
+ ---
549
+
550
+ #### `dataverse_solution_components`
551
+
552
+ Lists all components in a named solution. Use the **unique** solution name, not the display name.
553
+
554
+ | Parameter | Type | Req | Notes |
555
+ | --------------- | -------- | --- | ---------------------------------------------------------------------------- |
556
+ | `solutionName` | `string` | ✓ | Unique solution name |
557
+ | `componentType` | `number` | — | Type code filter (1=Entity, 29=Workflow, 90=PluginAssembly, 97=WebResource…) |
558
+ | `top` | `number` | — | Default `200`, max `5000` |
559
+
560
+ > "List all entities in the 'MySolution' solution"
561
+
562
+ ---
563
+
564
+ #### `dataverse_publish_customizations`
565
+
566
+ Publishes unpublished customizations. Omit `components` to publish all (equivalent to "Publish All" in maker portal). **Can take 30–120 s in large environments.**
567
+
568
+ | Parameter | Type | Req | Notes |
569
+ | ------------------------- | ---------- | --- | ----------------------- |
570
+ | `components.entities` | `string[]` | — | Entity logical names |
571
+ | `components.webResources` | `string[]` | — | Web resource names |
572
+ | `components.optionSets` | `string[]` | — | Global option set names |
573
+
574
+ > "Publish all pending customizations"
575
+
576
+ ---
577
+
578
+ ### 10. Impersonation (1 tool)
579
+
580
+ #### `dataverse_impersonate`
581
+
582
+ Executes any other tool on behalf of a different Dataverse user by injecting `MSCRMCallerId`. Applies only to the single wrapped call; cleaned up in `finally`. Requires `prvActOnBehalfOfAnotherUser`.
583
+
584
+ | Parameter | Type | Req | Notes |
585
+ | ---------- | --------------- | --- | --------------------------------------------- |
586
+ | `callerId` | `string (UUID)` | ✓ | Azure AD Object ID of the user to impersonate |
587
+ | `toolName` | `string` | ✓ | MCP tool name (e.g. `"dataverse_create"`) |
588
+ | `toolArgs` | `object` | ✓ | Arguments for the wrapped tool |
589
+
590
+ > "Create a contact as user john@contoso.com (ID: u1v2w3x4-...)"
591
+
592
+ ```json
593
+ {
594
+ "impersonatedAs": "u1v2w3x4-...",
595
+ "tool": "dataverse_create",
596
+ "result": { "id": "..." }
597
+ }
598
+ ```
599
+
600
+ ---
601
+
602
+ ### 11. Customization (3 tools)
603
+
604
+ #### `dataverse_list_custom_actions`
605
+
606
+ Lists all public SDK messages (custom APIs) registered in the environment.
607
+
608
+ | Parameter | Type | Req | Notes |
609
+ | ------------ | -------- | --- | ------------------------------- |
610
+ | `top` | `number` | — | Default `100`, max `500` |
611
+ | `nameFilter` | `string` | — | Substring match on message name |
612
+
613
+ > "List all custom actions registered in the environment"
614
+
615
+ ```json
616
+ {
617
+ "total": 5,
618
+ "messages": [
619
+ { "name": "new_MyAction", "category": "", "asyncSupported": true }
620
+ ]
621
+ }
622
+ ```
623
+
624
+ ---
625
+
626
+ #### `dataverse_list_plugin_steps`
627
+
628
+ Lists plugin step registrations (SdkMessageProcessingStep): assembly, message, entity, stage (pre/post), mode (sync/async), and enabled state.
629
+
630
+ | Parameter | Type | Req | Notes |
631
+ | ------------------- | --------- | --- | ------------------------ |
632
+ | `top` | `number` | — | Default `100`, max `500` |
633
+ | `activeOnly` | `boolean` | — | Default `true` |
634
+ | `entityLogicalName` | `string` | — | Filter by entity |
635
+
636
+ > "Show me all active plugin steps for the account entity"
637
+
638
+ ---
639
+
640
+ #### `dataverse_set_workflow_state`
641
+
642
+ Activates or deactivates a classic Dataverse workflow (statecode/statuscode update).
643
+
644
+ | Parameter | Type | Req | Notes |
645
+ | ------------ | --------------- | --- | ----------------------------------- |
646
+ | `workflowId` | `string (UUID)` | ✓ | Workflow GUID |
647
+ | `activate` | `boolean` | ✓ | `true` = Activated, `false` = Draft |
648
+
649
+ > "Activate workflow w1x2y3z4"
650
+
651
+ ```json
652
+ {
653
+ "workflowId": "...",
654
+ "newState": "Activated",
655
+ "statecode": 1,
656
+ "statuscode": 2
657
+ }
658
+ ```
659
+
660
+ ---
661
+
662
+ ### 12. Environment (2 tools)
663
+
664
+ #### `dataverse_get_environment_variable`
665
+
666
+ Retrieves an environment variable's definition (type, default value) and current override value.
667
+
668
+ | Parameter | Type | Req | Notes |
669
+ | ------------ | -------- | --- | ---------------------------------- |
670
+ | `schemaName` | `string` | ✓ | Schema name, e.g. `"new_MyConfig"` |
671
+
672
+ > "What is the current value of environment variable new_ApiEndpoint?"
673
+
674
+ ```json
675
+ {
676
+ "schemaName": "new_ApiEndpoint",
677
+ "typeName": "String",
678
+ "defaultValue": "https://...",
679
+ "currentValue": "https://override...",
680
+ "effectiveValue": "https://override..."
681
+ }
682
+ ```
683
+
684
+ ---
685
+
686
+ #### `dataverse_set_environment_variable`
687
+
688
+ Sets or updates an environment variable's current value (creates or updates the value record).
689
+
690
+ | Parameter | Type | Req | Notes |
691
+ | ------------ | -------- | --- | --------------------------------------- |
692
+ | `schemaName` | `string` | | Schema name of the environment variable |
693
+ | `value` | `string` | ✓ | New value to set |
694
+
695
+ > "Set environment variable new_FeatureFlag to 'true'"
696
+
697
+ ---
698
+
699
+ ### 13. Trace (2 tools)
700
+
701
+ #### `dataverse_get_plugin_trace_logs`
702
+
703
+ Retrieves plugin execution trace logs: type name, triggering message, entity, duration, trace output, and exception details. Requires Plugin Trace Log enabled in Dataverse settings.
704
+
705
+ | Parameter | Type | Req | Notes |
706
+ | ------------------ | --------- | --- | --------------------------------------------- |
707
+ | `top` | `number` | — | Default `50`, max `200` |
708
+ | `pluginTypeFilter` | `string` | | Substring match on type name |
709
+ | `messageFilter` | `string` | — | e.g. `"Create"`, `"Update"` |
710
+ | `entityFilter` | `string` | | Entity logical name |
711
+ | `exceptionsOnly` | `boolean` | — | Only traces with exceptions (default `false`) |
712
+
713
+ > "Show me recent plugin failures on the account table"
714
+
715
+ ---
716
+
717
+ #### `dataverse_get_workflow_trace_logs`
718
+
719
+ Retrieves AsyncOperation records for background/classic workflow executions. Not for modern cloud flows (use Power Automate portal for those).
720
+
721
+ | Parameter | Type | Req | Notes |
722
+ | -------------- | --------- | --- | ---------------------------------------- |
723
+ | `top` | `number` | — | Default `50`, max `200` |
724
+ | `failedOnly` | `boolean` | — | Only failed executions (default `false`) |
725
+ | `entityFilter` | `string` | — | Filter by regarding entity type |
726
+
727
+ > "Show me failed classic workflows for the last 50 executions"
728
+
729
+ ---
730
+
731
+ ### 14. Search (1 tool)
732
+
733
+ #### `dataverse_search`
734
+
735
+ Full-text Relevance Search across all configured Dataverse tables. Returns ranked results with entity name, record ID, score, highlights, and matched fields. Requires **Relevance Search** enabled in Dataverse admin.
736
+
737
+ | Parameter | Type | Req | Notes |
738
+ | ------------ | ------------------ | --- | --------------------------------------------------- |
739
+ | `query` | `string` | | Search string; Lucene syntax with `searchType=full` |
740
+ | `entities` | `string[]` | — | Restrict to specific tables |
741
+ | `top` | `number` | — | Default `10`, max `50` |
742
+ | `searchMode` | `"any"\|"all"` | — | Match any/all terms (default `"any"`) |
743
+ | `searchType` | `"simple"\|"full"` | — | Default `"simple"` |
744
+ | `filter` | `string` | — | OData `$filter` on results |
745
+ | `facets` | `string[]` | — | Fields for faceted counts |
746
+ | `orderby` | `string[]` | — | Sort fields, e.g. `["@search.score desc"]` |
747
+ | `select` | `string[]` | — | Fields to return per result |
748
+
749
+ > "Find all records mentioning 'Contoso' across accounts and contacts"
750
+
751
+ ```json
752
+ {
753
+ "totalRecordCount": 12,
754
+ "results": [{ "entityName": "account", "objectId": "...", "score": 0.9 }]
755
+ }
756
+ ```
757
+
758
+ ---
759
+
760
+ ### 15. Audit (1 tool)
761
+
762
+ #### `dataverse_get_audit_log`
763
+
764
+ Retrieves audit log entries with operation type, user info, and parsed change data. At least one filter is recommended. Requires auditing enabled on the environment and table.
765
+
766
+ | Parameter | Type | Req | Notes |
767
+ | ------------------- | --------------- | --- | --------------------------------------------------------------------------------------------------- |
768
+ | `recordId` | `string (UUID)` | — | Specific record GUID |
769
+ | `entityLogicalName` | `string` | — | Filter by entity type |
770
+ | `userId` | `string (UUID)` | — | Filter by user |
771
+ | `fromDate` | `string` | — | ISO 8601 date (entries on/after) |
772
+ | `top` | `number` | — | Default `50`, max `500` |
773
+ | `operations` | `string[]` | | `"Create"`, `"Update"`, `"Delete"`, `"Activate"`, `"Deactivate"`, `"Share"`, `"Assign"`, `"Access"` |
774
+
775
+ > "Show me all updates to account a1b2c3d4 in the last week"
776
+
777
+ ---
778
+
779
+ ### 16. Quality (1 tool)
780
+
781
+ #### `dataverse_detect_duplicates`
782
+
783
+ Checks prospective record fields against existing records using Dataverse built-in duplicate detection rules.
784
+
785
+ | Parameter | Type | Req | Notes |
786
+ | ------------------- | -------- | --- | -------------------------------------- |
787
+ | `entityLogicalName` | `string` | ✓ | Table to check, e.g. `"account"` |
788
+ | `record` | `object` | | Field values of the prospective record |
789
+ | `top` | `number` | — | Default `5`, max `20` |
790
+
791
+ > "Check if there's already an account named 'Contoso Ltd'"
792
+
793
+ ```json
794
+ {
795
+ "hasDuplicates": true,
796
+ "duplicateCount": 1,
797
+ "duplicates": [{ "accountid": "...", "name": "Contoso Ltd" }]
798
+ }
799
+ ```
800
+
801
+ ---
802
+
803
+ ### 17. Annotations (2 tools)
804
+
805
+ #### `dataverse_get_annotations`
806
+
807
+ Retrieves notes and file attachments linked to a record. Set `includeContent=true` to fetch base64 file bodies (can be very large).
808
+
809
+ | Parameter | Type | Req | Notes |
810
+ | ---------------- | --------------- | --- | ---------------------------------------- |
811
+ | `recordId` | `string (UUID)` | | Parent record GUID |
812
+ | `includeContent` | `boolean` | — | Include `documentbody` (default `false`) |
813
+ | `top` | `number` | — | Default `20`, max `100` |
814
+ | `mimeTypeFilter` | `string` | — | e.g. `"application/pdf"` |
815
+
816
+ > "Get all notes attached to account a1b2c3d4"
817
+
818
+ ---
819
+
820
+ #### `dataverse_create_annotation`
821
+
822
+ Creates a note or file attachment linked to a Dataverse record. Provide `notetext` for a text note, `documentbody` (base64) for a file, or both.
823
+
824
+ | Parameter | Type | Req | Notes |
825
+ | --------------- | --------------- | --- | ------------------------------------------------------- |
826
+ | `recordId` | `string (UUID)` | ✓ | Parent record GUID |
827
+ | `entitySetName` | `string` | ✓ | Parent entity set, e.g. `"accounts"` |
828
+ | `notetext` | `string` | — | Text content (required if no `documentbody`) |
829
+ | `subject` | `string` | — | Note subject/title |
830
+ | `filename` | `string` | — | File name (for attachments) |
831
+ | `mimetype` | `string` | — | MIME type, e.g. `"application/pdf"` |
832
+ | `documentbody` | `string` | — | Base64-encoded file content (required if no `notetext`) |
833
+
834
+ > "Attach a PDF report to account a1b2c3d4"
835
+
836
+ ---
837
+
838
+ ### 18. Users (2 tools)
839
+
840
+ #### `dataverse_list_users`
841
+
842
+ Searches Dataverse system users by name or email. At least one of `search` or `businessUnitId` is required. Excludes application users and disabled users by default.
843
+
844
+ | Parameter | Type | Req | Notes |
845
+ | ------------------------- | --------------- | --- | ------------------------------------------------------------ |
846
+ | `search` | `string` | ✓\* | Full-name or email contains-search (_one of these required_) |
847
+ | `businessUnitId` | `string (UUID)` | ✓\* | Restrict to a business unit |
848
+ | `includeDisabled` | `boolean` | — | Default `false` |
849
+ | `includeApplicationUsers` | `boolean` | — | Default `false` |
850
+ | `top` | `number` | — | Default `20`, max `100` |
851
+
852
+ > "Find all users named 'John' in my environment"
853
+
854
+ ---
855
+
856
+ #### `dataverse_get_user_roles`
857
+
858
+ Returns all security roles assigned to a system user.
859
+
860
+ | Parameter | Type | Req | Notes |
861
+ | --------- | --------------- | --- | ---------------- |
862
+ | `userId` | `string (UUID)` | | System user GUID |
863
+
864
+ > "What security roles does user u1v2w3x4 have?"
865
+
866
+ ```json
867
+ {
868
+ "userId": "...",
869
+ "fullname": "John Doe",
870
+ "roles": [{ "name": "Sales Manager", "roleId": "..." }],
871
+ "roleCount": 1
872
+ }
873
+ ```
874
+
875
+ ---
876
+
877
+ ### 19. Views (1 tool)
878
+
879
+ #### `dataverse_list_views`
880
+
881
+ Lists saved (system) and optionally personal views for a Dataverse table, including view name, ID, default flag, query type, and description.
882
+
883
+ | Parameter | Type | Req | Notes |
884
+ | ------------------- | --------- | --- | ----------------------------------------- |
885
+ | `entityLogicalName` | `string` | ✓ | Table logical name, e.g. `"account"` |
886
+ | `includePersonal` | `boolean` | | Include personal views (default `false`) |
887
+ | `top` | `number` | — | Max per category, default `20`, max `100` |
888
+
889
+ > "List all system views for the account table"
890
+
891
+ ```json
892
+ {
893
+ "entityLogicalName": "account",
894
+ "systemViews": [
895
+ { "id": "...", "name": "Active Accounts", "isDefault": true }
896
+ ],
897
+ "systemViewCount": 5
898
+ }
899
+ ```
900
+
901
+ ---
902
+
903
+ ### 20. Files (2 tools)
904
+
905
+ #### `dataverse_upload_file_column`
906
+
907
+ Uploads a file to a Dataverse **file-type column** on a record. File content must be base64-encoded.
908
+
909
+ | Parameter | Type | Req | Notes |
910
+ | --------------- | --------------- | --- | ------------------------------------------------------- |
911
+ | `entitySetName` | `string` | ✓ | OData entity set name |
912
+ | `recordId` | `string (UUID)` | ✓ | Record GUID |
913
+ | `columnName` | `string` | ✓ | File column logical name (alphanumeric/underscore only) |
914
+ | `fileContent` | `string` | ✓ | Base64-encoded file content |
915
+ | `fileName` | `string` | ✓ | File name including extension, e.g. `"report.pdf"` |
916
+
917
+ > "Upload report.pdf to the attachment column on account a1b2c3d4"
918
+
919
+ ```json
920
+ {
921
+ "success": true,
922
+ "recordId": "...",
923
+ "columnName": "new_report",
924
+ "fileName": "report.pdf",
925
+ "sizeBytes": 204800
926
+ }
927
+ ```
928
+
929
+ ---
930
+
931
+ #### `dataverse_download_file_column`
932
+
933
+ Downloads a file from a Dataverse file-type column. Returns the file as a base64-encoded string with its name and size.
934
+
935
+ | Parameter | Type | Req | Notes |
936
+ | --------------- | --------------- | --- | ------------------------ |
937
+ | `entitySetName` | `string` | ✓ | OData entity set name |
938
+ | `recordId` | `string (UUID)` | ✓ | Record GUID |
939
+ | `columnName` | `string` | ✓ | File column logical name |
940
+
941
+ > "Download the attachment from account a1b2c3d4 column new_report"
942
+
943
+ ```json
944
+ {
945
+ "fileName": "report.pdf",
946
+ "sizeBytes": 204800,
947
+ "contentBase64": "JVBERi0x..."
948
+ }
949
+ ```
950
+
951
+ ---
952
+
953
+ ### 21. Org (1 tool)
954
+
955
+ #### `dataverse_list_business_units`
956
+
957
+ Lists business units in the environment with name, ID, parent BU ID, disabled status, and creation date.
958
+
959
+ | Parameter | Type | Req | Notes |
960
+ | ----------------- | --------- | --- | -------------------------------------- |
961
+ | `top` | `number` | — | Default `50`, max `200` |
962
+ | `includeDisabled` | `boolean` | — | Include disabled BUs (default `false`) |
963
+
964
+ > "List all active business units in my Dataverse environment"
965
+
966
+ ---
967
+
968
+ ## Error Handling & Retry Behavior
969
+
970
+ All tool handlers return `{ isError: true, content: [{ type: "text", text: "Error: ..." }] }` on failure. Zod input validation runs before any network call.
971
+
972
+ ### Retry Strategy
973
+
974
+ | Status | Behavior | Attempts |
975
+ | ------------------- | --------------------------------------------- | ------------------------ |
976
+ | **401** | Invalidate token, retry once with fresh token | 1 |
977
+ | **429 / 503 / 504** | Exponential backoff (`2^attempt × 1000ms`) | `maxRetries` (default 3) |
978
+ | **Other** | Throw immediately | 0 |
979
+
980
+ Dataverse error bodies are formatted as `Dataverse error <code>: <message>`. Timeouts (`ECONNABORTED`) produce `Request timed out. Check your Dataverse environment URL.`
981
+
982
+ ---
983
+
984
+ ## Security
985
+
986
+ | Mode | Flow | Use Case |
987
+ | -------- | ------------------------------------------------- | ---------------------- |
988
+ | **pac** | MSAL Public Client device code + silent refresh | Local dev, interactive |
989
+ | **msal** | MSAL Confidential Client → `client_credentials` | CI/CD, headless |
990
+
991
+ - `clientSecret` is never logged or returned in tool responses.
992
+ - OData path segments use `esc()` (single-quote doubling) to prevent OData injection.
993
+ - `columnName` in file tools is validated against `/^[a-zA-Z0-9_]+$/` to prevent path traversal.
994
+ - `MSCRMCallerId` for impersonation is set per-call and cleaned up in a `finally` block regardless of outcome.
995
+ - `.msal-cache.json` should be in `.gitignore`. No HTTP endpoints are exposed (stdio only).
996
+
997
+ ---
998
+
999
+ ## Limitations & Known Constraints
1000
+
1001
+ ### General
1002
+
1003
+ | Limitation | Details |
1004
+ | ------------------------------ | --------------------------------------------------------------------------------------- |
1005
+ | **Transport** | stdio only. Server must be spawned as a child process by the MCP host. |
1006
+ | **Single environment** | One Dataverse environment per server instance. Restart to switch. |
1007
+ | **No streaming** | Responses are complete JSON. Very large result sets may exceed AI model context limits. |
1008
+ | **No real-time subscriptions** | Use `dataverse_change_detection` for polling-based incremental sync. |
1009
+
1010
+ ### Query
1011
+
1012
+ | Limitation | Details |
1013
+ | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
1014
+ | **`$top` max** | `dataverse_query` caps at 5000 per call. Use `dataverse_retrieve_multiple_with_paging` for more. |
1015
+ | **Paging max** | `dataverse_retrieve_multiple_with_paging` caps at 50,000 records total. |
1016
+ | **FetchXML entity set** | Auto-extraction appends `"s"` to the logical name. Non-standard entity set names require explicit `entitySetName`. |
1017
+
1018
+ ### CRUD
1019
+
1020
+ | Limitation | Details |
1021
+ | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
1022
+ | **UUID required for get/update/delete** | Alternate-key retrieval via `dataverse_get` is not supported; use `dataverse_upsert` or `dataverse_query` instead. |
1023
+ | **No ETag conditional update** | `dataverse_update` sends `If-Match: *`. ETag-based optimistic concurrency is not exposed. |
1024
+
1025
+ ### Authentication
1026
+
1027
+ | Limitation | Details |
1028
+ | ----------------------- | ---------------------------------------------------------------- |
1029
+ | **No certificate auth** | MSAL `clientSecret` only; certificate credentials not supported. |
1030
+ | **PAC token expiry** | If the refresh token expires, re-run `npm run auth:setup`. |
1031
+ | **MSAL multi-tenant** | Requires explicit `tenantId`; no `common` authority support. |
1032
+
1033
+ ### Dependencies & Solutions
1034
+
1035
+ | Limitation | Details |
1036
+ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
1037
+ | **Plugin/CustomAPI in `list_dependencies`** | Only Workflow/BusinessRule/Flow types fully supported. Plugin step queries require separate SDK message lookups not yet implemented. |
1038
+ | **`solutionName` in dependency results** | Always `null` — solution join not yet implemented. |
1039
+
1040
+ ### Batch
1041
+
1042
+ | Limitation | Details |
1043
+ | --------------------------------- | ----------------------------------------------------------------------------------------- |
1044
+ | **Max operations** | 1,000 per batch (Zod-enforced). |
1045
+ | **No `$<Content-ID>` references** | Cross-referencing created entities within a changeset is not supported at the tool level. |
1046
+
1047
+ ---
1048
+
1049
+ _This document reflects the MCP Dataverse server codebase as of v0.2.0 — 50 tools across 22 categories._