mcp-dataverse 0.3.9 → 0.4.5
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.
- package/CAPABILITIES.md +281 -16
- package/README.md +28 -5
- package/dist/chunk-MPWVUZBX.js +35 -0
- package/dist/dataverse-client-advanced-ZG4OPCGR.js +1 -0
- package/dist/doctor.js +1 -1
- package/dist/http-server.js +4 -3
- package/dist/server.js +9 -9
- package/package.json +2 -2
- package/server.json +2 -2
- package/dist/chunk-OXKMMPM3.js +0 -37
- package/dist/dataverse-client-advanced-T5ZJMRLK.js +0 -1
package/CAPABILITIES.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# MCP Dataverse Server — Complete Capabilities Reference
|
|
2
2
|
|
|
3
|
-
> **Version**: 0.
|
|
3
|
+
> **Version**: 0.4.5 | **API Version**: Dataverse Web API v9.2 | **Transport**: stdio · HTTP/SSE
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
67 tools across 25 categories for full Dataverse lifecycle: schema, CRUD, FetchXML, solutions, plugins, audit, files, users, teams, RBAC, attribute management, environment variables, workflows, and more.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
- [19. Views (1)](#19-views-1-tool)
|
|
33
33
|
- [20. Files (2)](#20-files-2-tools)
|
|
34
34
|
- [21. Org (1)](#21-org-1-tool)
|
|
35
|
+
- [22. RBAC (3)](#22-rbac-3-tools)
|
|
36
|
+
- [23. Workflows (2)](#23-workflows-2-tools)
|
|
37
|
+
- [24. Assistance (4)](#24-assistance-4-tools)
|
|
38
|
+
- [25. Attributes (4)](#25-attributes-4-tools)
|
|
35
39
|
- [Error Handling & Retry Behavior](#error-handling--retry-behavior)
|
|
36
40
|
- [Security](#security)
|
|
37
41
|
- [Limitations & Known Constraints](#limitations--known-constraints)
|
|
@@ -91,7 +95,7 @@ Server communicates over **stdio** (MCP SDK `StdioServerTransport`). Connect fro
|
|
|
91
95
|
|
|
92
96
|
```mermaid
|
|
93
97
|
graph LR
|
|
94
|
-
MCP["MCP Dataverse Server<br/><i>
|
|
98
|
+
MCP["MCP Dataverse Server<br/><i>67 tools · 25 categories</i>"]
|
|
95
99
|
|
|
96
100
|
MCP --> AUTH["🔑 Auth (1)"]
|
|
97
101
|
MCP --> META["📋 Metadata (8)"]
|
|
@@ -104,25 +108,27 @@ graph LR
|
|
|
104
108
|
MCP --> SOL["🧩 Solutions (3)"]
|
|
105
109
|
MCP --> IMP["👤 Impersonation (1)"]
|
|
106
110
|
MCP --> CUST["🔧 Customization (3)"]
|
|
107
|
-
MCP --> ENV["⚙️ Environment (
|
|
111
|
+
MCP --> ENV["⚙️ Environment (3)"]
|
|
108
112
|
MCP --> TRACE["🔎 Trace (2)"]
|
|
109
113
|
MCP --> SRCH["🔍 Search (1)"]
|
|
110
114
|
MCP --> AUDIT["📜 Audit (1)"]
|
|
111
115
|
MCP --> QUAL["✅ Quality (1)"]
|
|
112
116
|
MCP --> NOTE["📝 Annotations (2)"]
|
|
113
117
|
MCP --> USR["👥 Users (2)"]
|
|
118
|
+
MCP --> RBAC["🛡️ RBAC (3)"]
|
|
114
119
|
MCP --> VIEWS["👁️ Views (1)"]
|
|
115
120
|
MCP --> FILES["📁 Files (2)"]
|
|
116
|
-
MCP --> ORG["🏢 Org (
|
|
117
|
-
MCP -->
|
|
121
|
+
MCP --> ORG["🏢 Org (2)"]
|
|
122
|
+
MCP --> WF["⚙️ Workflows (2)"]
|
|
118
123
|
MCP --> ASSIST["🤖 Assistance (4)"]
|
|
124
|
+
MCP --> ATTR["🏗️ Attributes (4)"]
|
|
119
125
|
```
|
|
120
126
|
|
|
121
127
|
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.
|
|
122
128
|
|
|
123
129
|
---
|
|
124
130
|
|
|
125
|
-
## Tool Reference (
|
|
131
|
+
## Tool Reference (67 tools)
|
|
126
132
|
|
|
127
133
|
### 1. Auth (1 tool)
|
|
128
134
|
|
|
@@ -724,7 +730,7 @@ Retrieves AsyncOperation records for background/classic workflow executions. Not
|
|
|
724
730
|
|
|
725
731
|
#### `dataverse_search`
|
|
726
732
|
|
|
727
|
-
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.
|
|
733
|
+
Full-text Relevance Search across all configured Dataverse tables. Returns ranked results with entity name, record ID, score, highlights, and matched fields. Uses the **Search API v2.0** endpoint. Requires **Relevance Search** enabled in Dataverse admin. If Relevance Search is disabled or no entities are configured for search, returns a structured error with `errorCategory: "ENV_LIMITATION"` and setup instructions.
|
|
728
734
|
|
|
729
735
|
| Parameter | Type | Req | Notes |
|
|
730
736
|
| ------------ | ------------------ | --- | --------------------------------------------------- |
|
|
@@ -801,7 +807,7 @@ Retrieves notes and file attachments linked to a record. Set `includeContent=tru
|
|
|
801
807
|
| Parameter | Type | Req | Notes |
|
|
802
808
|
| ---------------- | --------------- | --- | ---------------------------------------- |
|
|
803
809
|
| `recordId` | `string (UUID)` | ✓ | Parent record GUID |
|
|
804
|
-
| `includeContent` | `boolean` | — | Include `
|
|
810
|
+
| `includeContent` | `boolean` | — | Include `documentBody` (default `false`) |
|
|
805
811
|
| `top` | `number` | — | Default `20`, max `100` |
|
|
806
812
|
| `mimeTypeFilter` | `string` | — | e.g. `"application/pdf"` |
|
|
807
813
|
|
|
@@ -811,17 +817,17 @@ Retrieves notes and file attachments linked to a record. Set `includeContent=tru
|
|
|
811
817
|
|
|
812
818
|
#### `dataverse_create_annotation`
|
|
813
819
|
|
|
814
|
-
Creates a note or file attachment linked to a Dataverse record. Provide `
|
|
820
|
+
Creates a note or file attachment linked to a Dataverse record. Provide `noteText` for a text note, `documentBody` (base64) for a file, or both.
|
|
815
821
|
|
|
816
822
|
| Parameter | Type | Req | Notes |
|
|
817
823
|
| --------------- | --------------- | --- | ------------------------------------------------------- |
|
|
818
824
|
| `recordId` | `string (UUID)` | ✓ | Parent record GUID |
|
|
819
825
|
| `entitySetName` | `string` | ✓ | Parent entity set, e.g. `"accounts"` |
|
|
820
|
-
| `
|
|
826
|
+
| `noteText` | `string` | — | Text content (required if no `documentBody`) |
|
|
821
827
|
| `subject` | `string` | — | Note subject/title |
|
|
822
|
-
| `
|
|
823
|
-
| `
|
|
824
|
-
| `
|
|
828
|
+
| `fileName` | `string` | — | File name (for attachments) |
|
|
829
|
+
| `mimeType` | `string` | — | MIME type, e.g. `"application/pdf"` |
|
|
830
|
+
| `documentBody` | `string` | — | Base64-encoded file content (required if no `noteText`) |
|
|
825
831
|
|
|
826
832
|
> "Attach a PDF report to account a1b2c3d4"
|
|
827
833
|
|
|
@@ -942,7 +948,7 @@ Downloads a file from a Dataverse file-type column. Returns the file as a base64
|
|
|
942
948
|
|
|
943
949
|
---
|
|
944
950
|
|
|
945
|
-
### 21. Org (
|
|
951
|
+
### 21. Org (2 tools)
|
|
946
952
|
|
|
947
953
|
#### `dataverse_list_business_units`
|
|
948
954
|
|
|
@@ -957,6 +963,255 @@ Lists business units in the environment with name, ID, parent BU ID, disabled st
|
|
|
957
963
|
|
|
958
964
|
---
|
|
959
965
|
|
|
966
|
+
#### `dataverse_list_teams`
|
|
967
|
+
|
|
968
|
+
Lists Dataverse teams (owner teams and access teams) within one or all business units.
|
|
969
|
+
|
|
970
|
+
| Parameter | Type | Req | Notes |
|
|
971
|
+
| ---------------- | ------------------------- | --- | ---------------------------------------------- |
|
|
972
|
+
| `top` | `number` | — | Default `50`, max `200` |
|
|
973
|
+
| `teamType` | `"Owner"\|"Access"\|"AAD"` | — | Filter by team type; omit for all |
|
|
974
|
+
| `businessUnitId` | `string (UUID)` | — | Filter by business unit |
|
|
975
|
+
|
|
976
|
+
> "List all owner teams in the environment"
|
|
977
|
+
|
|
978
|
+
---
|
|
979
|
+
|
|
980
|
+
### 22. RBAC (3 tools)
|
|
981
|
+
|
|
982
|
+
#### `dataverse_list_roles`
|
|
983
|
+
|
|
984
|
+
Lists security roles in the environment, optionally filtered by name.
|
|
985
|
+
|
|
986
|
+
| Parameter | Type | Req | Notes |
|
|
987
|
+
| ------------ | -------- | --- | ---------------------------- |
|
|
988
|
+
| `nameFilter` | `string` | — | Substring match on role name |
|
|
989
|
+
| `top` | `number` | — | Default `50`, max `200` |
|
|
990
|
+
|
|
991
|
+
> "List all security roles that contain 'Sales'"
|
|
992
|
+
|
|
993
|
+
```json
|
|
994
|
+
{
|
|
995
|
+
"total": 3,
|
|
996
|
+
"roles": [{ "roleId": "...", "name": "Salesperson", "businessUnitId": "..." }]
|
|
997
|
+
}
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
---
|
|
1001
|
+
|
|
1002
|
+
#### `dataverse_assign_role_to_user`
|
|
1003
|
+
|
|
1004
|
+
Assigns a security role to a system user (idempotent — returns `"already_assigned"` if the role is already assigned).
|
|
1005
|
+
|
|
1006
|
+
| Parameter | Type | Req | Notes |
|
|
1007
|
+
| --------- | --------------- | --- | ----------------- |
|
|
1008
|
+
| `userId` | `string (UUID)` | ✓ | System user GUID |
|
|
1009
|
+
| `roleId` | `string (UUID)` | ✓ | Security role ID |
|
|
1010
|
+
|
|
1011
|
+
> "Assign role r1s2t3u4 to user u1v2w3x4"
|
|
1012
|
+
|
|
1013
|
+
---
|
|
1014
|
+
|
|
1015
|
+
#### `dataverse_remove_role_from_user`
|
|
1016
|
+
|
|
1017
|
+
Removes a security role from a system user.
|
|
1018
|
+
|
|
1019
|
+
| Parameter | Type | Req | Notes |
|
|
1020
|
+
| --------- | --------------- | --- | ---------------- |
|
|
1021
|
+
| `userId` | `string (UUID)` | ✓ | System user GUID |
|
|
1022
|
+
| `roleId` | `string (UUID)` | ✓ | Security role ID |
|
|
1023
|
+
|
|
1024
|
+
> "Remove role r1s2t3u4 from user u1v2w3x4"
|
|
1025
|
+
|
|
1026
|
+
---
|
|
1027
|
+
|
|
1028
|
+
### 23. Workflows (2 tools)
|
|
1029
|
+
|
|
1030
|
+
#### `dataverse_list_workflows`
|
|
1031
|
+
|
|
1032
|
+
Lists classic Dataverse workflows and modern cloud flows registered in the environment.
|
|
1033
|
+
|
|
1034
|
+
| Parameter | Type | Req | Notes |
|
|
1035
|
+
| ------------- | --------- | --- | -------------------------------------------- |
|
|
1036
|
+
| `top` | `number` | — | Default `50`, max `200` |
|
|
1037
|
+
| `activeOnly` | `boolean` | — | Return only activated workflows (default `false`) |
|
|
1038
|
+
| `nameFilter` | `string` | — | Substring match on workflow name |
|
|
1039
|
+
|
|
1040
|
+
> "List all active workflows on the account table"
|
|
1041
|
+
|
|
1042
|
+
---
|
|
1043
|
+
|
|
1044
|
+
#### `dataverse_get_workflow`
|
|
1045
|
+
|
|
1046
|
+
Retrieves a single workflow definition by ID, including its trigger, steps, and current state.
|
|
1047
|
+
|
|
1048
|
+
| Parameter | Type | Req | Notes |
|
|
1049
|
+
| ------------ | --------------- | --- | ------------- |
|
|
1050
|
+
| `workflowId` | `string (UUID)` | ✓ | Workflow GUID |
|
|
1051
|
+
|
|
1052
|
+
> "Get the definition of workflow w1x2y3z4"
|
|
1053
|
+
|
|
1054
|
+
---
|
|
1055
|
+
|
|
1056
|
+
### 24. Assistance (4 tools)
|
|
1057
|
+
|
|
1058
|
+
#### `dataverse_suggest_tools`
|
|
1059
|
+
|
|
1060
|
+
Returns a ranked list of MCP tools relevant to a natural-language task description. Use this when unsure which tool to call — it uses tag-based matching to surface the right tools.
|
|
1061
|
+
|
|
1062
|
+
| Parameter | Type | Req | Notes |
|
|
1063
|
+
| ------------- | -------- | --- | ---------------------------------- |
|
|
1064
|
+
| `task` | `string` | ✓ | Natural-language description of the task |
|
|
1065
|
+
| `top` | `number` | — | Max results (default `5`) |
|
|
1066
|
+
|
|
1067
|
+
> "Which tool should I use to create a new lookup column?"
|
|
1068
|
+
|
|
1069
|
+
---
|
|
1070
|
+
|
|
1071
|
+
#### `dataverse_list_guides`
|
|
1072
|
+
|
|
1073
|
+
Lists available built-in guides that walk through common multi-step Dataverse tasks.
|
|
1074
|
+
|
|
1075
|
+
| Parameter | Type | Req | Notes |
|
|
1076
|
+
| --------- | ---- | --- | ----- |
|
|
1077
|
+
| — | — | — | No parameters |
|
|
1078
|
+
|
|
1079
|
+
> "What guides are available in this MCP server?"
|
|
1080
|
+
|
|
1081
|
+
---
|
|
1082
|
+
|
|
1083
|
+
#### `dataverse_get_guide`
|
|
1084
|
+
|
|
1085
|
+
Retrieves the full step-by-step content for a specific guide by name.
|
|
1086
|
+
|
|
1087
|
+
| Parameter | Type | Req | Notes |
|
|
1088
|
+
| ----------- | -------- | --- | ------------------- |
|
|
1089
|
+
| `guideName` | `string` | ✓ | Guide name from `list_guides` |
|
|
1090
|
+
|
|
1091
|
+
> "Show me the steps for the entity-audit guide"
|
|
1092
|
+
|
|
1093
|
+
---
|
|
1094
|
+
|
|
1095
|
+
#### `dataverse_list_connection_references`
|
|
1096
|
+
|
|
1097
|
+
Lists connection references used in solutions (Power Automate connectors).
|
|
1098
|
+
|
|
1099
|
+
| Parameter | Type | Req | Notes |
|
|
1100
|
+
| -------------- | -------- | --- | -------------------------------- |
|
|
1101
|
+
| `top` | `number` | — | Default `50`, max `200` |
|
|
1102
|
+
| `nameFilter` | `string` | — | Substring match on display name |
|
|
1103
|
+
|
|
1104
|
+
> "List all SharePoint connection references in my environment"
|
|
1105
|
+
|
|
1106
|
+
---
|
|
1107
|
+
|
|
1108
|
+
### 25. Attributes (4 tools)
|
|
1109
|
+
|
|
1110
|
+
Attribute tools manage **column-level schema** in Dataverse tables. All write operations require `confirm: true` and auto-publish the entity definition by default.
|
|
1111
|
+
|
|
1112
|
+
#### `dataverse_create_attribute`
|
|
1113
|
+
|
|
1114
|
+
Creates a new column on an existing Dataverse table. Supports 11 attribute types with type-specific parameters.
|
|
1115
|
+
|
|
1116
|
+
| Parameter | Type | Req | Notes |
|
|
1117
|
+
| -------------------- | ---------------------------------------------------------------------------------------------------------------- | --- | --------------------------------------------------------------- |
|
|
1118
|
+
| `entityLogicalName` | `string` | ✓ | Target table (e.g. `"account"`) |
|
|
1119
|
+
| `schemaName` | `string` | ✓ | Must include publisher prefix (e.g. `"new_CustomField"`) |
|
|
1120
|
+
| `attributeType` | `"String"\|"Memo"\|"Integer"\|"Decimal"\|"Money"\|"DateTime"\|"Boolean"\|"Picklist"\|"MultiSelectPicklist"\|"AutoNumber"\|"Image"` | ✓ | Column type |
|
|
1121
|
+
| `displayName` | `string` | ✓ | Human-readable label |
|
|
1122
|
+
| `description` | `string` | — | Column description |
|
|
1123
|
+
| `requiredLevel` | `"None"\|"ApplicationRequired"\|"Recommended"` | — | Requirement level (default `"None"`) |
|
|
1124
|
+
| `maxLength` | `number` | — | String/Memo max chars |
|
|
1125
|
+
| `minValue`/`maxValue`| `number` | — | Integer/Decimal range bounds |
|
|
1126
|
+
| `precision` | `number` | — | Decimal/Money decimal places |
|
|
1127
|
+
| `dateTimeFormat` | `"DateOnly"\|"DateAndTime"` | — | DateTime display format |
|
|
1128
|
+
| `defaultBooleanValue`| `boolean` | — | Default for Boolean columns |
|
|
1129
|
+
| `picklistOptions` | `{value: number, label: string}[]` | — | Option values for Picklist/MultiSelectPicklist |
|
|
1130
|
+
| `autoNumberFormat` | `string` | — | Format string for AutoNumber, e.g. `"INV-{SEQNUM:5}"` |
|
|
1131
|
+
| `languageCode` | `number` | — | Label language code (default `1033` = English) |
|
|
1132
|
+
| `autoPublish` | `boolean` | — | Publish after create (default `true`) |
|
|
1133
|
+
| `confirm` | `true` | ✓ | Must be `true` |
|
|
1134
|
+
|
|
1135
|
+
> "Add a text column 'new_ExternalId' to the account table with max 100 characters"
|
|
1136
|
+
|
|
1137
|
+
```json
|
|
1138
|
+
{
|
|
1139
|
+
"logicalName": "new_externalid",
|
|
1140
|
+
"schemaName": "new_ExternalId",
|
|
1141
|
+
"attributeType": "String",
|
|
1142
|
+
"entityLogicalName": "account",
|
|
1143
|
+
"published": true
|
|
1144
|
+
}
|
|
1145
|
+
```
|
|
1146
|
+
|
|
1147
|
+
---
|
|
1148
|
+
|
|
1149
|
+
#### `dataverse_update_attribute`
|
|
1150
|
+
|
|
1151
|
+
Updates properties of an existing column. Only columns with `IsCustomizable = true` can be modified.
|
|
1152
|
+
|
|
1153
|
+
| Parameter | Type | Req | Notes |
|
|
1154
|
+
| --------------------- | ----------------------------------------------- | --- | ------------------------------------------- |
|
|
1155
|
+
| `entityLogicalName` | `string` | ✓ | Table containing the column |
|
|
1156
|
+
| `attributeLogicalName`| `string` | ✓ | Column logical name (e.g. `"new_myfield"`) |
|
|
1157
|
+
| `displayName` | `string` | — | New display label |
|
|
1158
|
+
| `description` | `string` | — | New description |
|
|
1159
|
+
| `requiredLevel` | `"None"\|"ApplicationRequired"\|"Recommended"` | — | New requirement level |
|
|
1160
|
+
| `maxLength` | `number` | — | Increase only (cannot decrease) |
|
|
1161
|
+
| `isSearchable` | `boolean` | — | Include in Quick Find views |
|
|
1162
|
+
| `languageCode` | `number` | — | Default `1033` |
|
|
1163
|
+
| `autoPublish` | `boolean` | — | Default `true` |
|
|
1164
|
+
| `confirm` | `true` | ✓ | Must be `true` |
|
|
1165
|
+
|
|
1166
|
+
> "Update new_externalid on account to be required"
|
|
1167
|
+
|
|
1168
|
+
---
|
|
1169
|
+
|
|
1170
|
+
#### `dataverse_delete_attribute`
|
|
1171
|
+
|
|
1172
|
+
⚠️ **DESTRUCTIVE** — permanently deletes a custom column and all its data from all records.
|
|
1173
|
+
|
|
1174
|
+
| Parameter | Type | Req | Notes |
|
|
1175
|
+
| --------------------- | -------- | --- | ------------------------------------------------ |
|
|
1176
|
+
| `entityLogicalName` | `string` | ✓ | Table containing the column |
|
|
1177
|
+
| `attributeLogicalName`| `string` | ✓ | Column logical name (must be a custom column) |
|
|
1178
|
+
| `autoPublish` | `boolean`| — | Publish after delete (default `true`) |
|
|
1179
|
+
| `confirm` | `true` | ✓ | Must be `true` |
|
|
1180
|
+
|
|
1181
|
+
> "Delete the column new_externalid from account"
|
|
1182
|
+
|
|
1183
|
+
---
|
|
1184
|
+
|
|
1185
|
+
#### `dataverse_create_lookup_attribute`
|
|
1186
|
+
|
|
1187
|
+
Creates a lookup (N:1) column on a table, simultaneously defining a 1:N relationship between two tables. This is the correct way to add a foreign-key-style reference to another table.
|
|
1188
|
+
|
|
1189
|
+
| Parameter | Type | Req | Notes |
|
|
1190
|
+
| --------------------- | -------- | --- | --------------------------------------------------------------------------- |
|
|
1191
|
+
| `entityLogicalName` | `string` | ✓ | Table that will contain the lookup column (the "many" side) |
|
|
1192
|
+
| `schemaName` | `string` | ✓ | Must include publisher prefix (e.g. `"new_ParentAccount"`) |
|
|
1193
|
+
| `displayName` | `string` | ✓ | Human-readable label |
|
|
1194
|
+
| `referencedEntity` | `string` | ✓ | Table being looked up (the "one" side, e.g. `"account"`) |
|
|
1195
|
+
| `description` | `string` | — | Column description |
|
|
1196
|
+
| `requiredLevel` | `"None"\|"ApplicationRequired"\|"Recommended"` | — | Default `"None"` |
|
|
1197
|
+
| `languageCode` | `number` | — | Default `1033` |
|
|
1198
|
+
| `autoPublish` | `boolean`| — | Default `true` |
|
|
1199
|
+
| `confirm` | `true` | ✓ | Must be `true` |
|
|
1200
|
+
|
|
1201
|
+
> "Add a lookup to 'account' on the contact table called new_PrimaryAccount"
|
|
1202
|
+
|
|
1203
|
+
```json
|
|
1204
|
+
{
|
|
1205
|
+
"lookupLogicalName": "new_primaryaccount",
|
|
1206
|
+
"relationshipSchemaName": "new_contact_primaryaccount",
|
|
1207
|
+
"referencedEntity": "account",
|
|
1208
|
+
"referencingEntity": "contact",
|
|
1209
|
+
"published": true
|
|
1210
|
+
}
|
|
1211
|
+
```
|
|
1212
|
+
|
|
1213
|
+
---
|
|
1214
|
+
|
|
960
1215
|
## Error Handling & Retry Behavior
|
|
961
1216
|
|
|
962
1217
|
All tool handlers return `{ isError: true, content: [{ type: "text", text: "Error: ..." }] }` on failure. Zod input validation runs before any network call.
|
|
@@ -971,6 +1226,16 @@ All tool handlers return `{ isError: true, content: [{ type: "text", text: "Erro
|
|
|
971
1226
|
|
|
972
1227
|
Dataverse error bodies are formatted as `Dataverse error <code>: <message>`. Timeouts (`ECONNABORTED`) produce `Request timed out. Check your Dataverse environment URL.`
|
|
973
1228
|
|
|
1229
|
+
### Error Categories
|
|
1230
|
+
|
|
1231
|
+
Certain tools include an `errorCategory` field in the error text when the failure has a well-known cause:
|
|
1232
|
+
|
|
1233
|
+
| `errorCategory` | Meaning | Example |
|
|
1234
|
+
| ----------------- | ------------------------------------------------------------ | --------------------------------------------------------- |
|
|
1235
|
+
| `ENV_LIMITATION` | Feature not enabled or unavailable in this environment | `dataverse_search` when Relevance Search is disabled |
|
|
1236
|
+
| `PERMISSIONS` | Operation denied due to insufficient privileges | Restricted table or action |
|
|
1237
|
+
| `SCHEMA_MISMATCH` | Supplied data conflicts with the table's metadata schema | Wrong attribute type in `dataverse_create_attribute` |
|
|
1238
|
+
|
|
974
1239
|
---
|
|
975
1240
|
|
|
976
1241
|
## Security
|
|
@@ -1036,4 +1301,4 @@ Dataverse error bodies are formatted as `Dataverse error <code>: <message>`. Tim
|
|
|
1036
1301
|
|
|
1037
1302
|
---
|
|
1038
1303
|
|
|
1039
|
-
_This document reflects the MCP Dataverse server codebase as of v0.
|
|
1304
|
+
_This document reflects the MCP Dataverse server codebase as of v0.4.0 — 67 tools across 25 categories._
|
package/README.md
CHANGED
|
@@ -6,9 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**The most complete MCP server for Microsoft Dataverse.**
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
63 tools · 4 resources · 10 guided workflows · Zero config auth
|
|
9
|
+
67 tools · 4 resources · 10 guided workflows · Zero config auth
|
|
12
10
|
|
|
13
11
|
[](https://www.npmjs.com/package/mcp-dataverse)
|
|
14
12
|
[](https://www.npmjs.com/package/mcp-dataverse)
|
|
@@ -80,6 +78,31 @@ Re-authenticate after ~90 days of inactivity: `npx mcp-dataverse-auth`
|
|
|
80
78
|
|
|
81
79
|
---
|
|
82
80
|
|
|
81
|
+
## HTTP Transport
|
|
82
|
+
|
|
83
|
+
Run as an HTTP server for multi-client use:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
MCP_TRANSPORT=http MCP_HTTP_PORT=3000 MCP_HTTP_SECRET=mysecret node dist/server.js
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Connect using VS Code / Copilot with:
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"servers": {
|
|
93
|
+
"dataverse": {
|
|
94
|
+
"type": "http",
|
|
95
|
+
"url": "http://localhost:3000/mcp",
|
|
96
|
+
"headers": {
|
|
97
|
+
"Authorization": "Bearer mysecret"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
83
106
|
## Troubleshooting
|
|
84
107
|
|
|
85
108
|
| Symptom | Fix |
|
|
@@ -102,8 +125,8 @@ MCP Dataverse is designed to be comprehensive, but most AI models work best with
|
|
|
102
125
|
|
|
103
126
|
| Version | Feature | Status |
|
|
104
127
|
| ------- | ------- | ------ |
|
|
105
|
-
| **v0.4** | HTTP transport +
|
|
106
|
-
| **v0.5** |
|
|
128
|
+
| **v0.4** | HTTP transport + attribute management + schema consistency | ✅ Released |
|
|
129
|
+
| **v0.5** | Enterprise auth (Client Credentials, Managed Identity) + MCP Prompts | 🔴 Planned |
|
|
107
130
|
|
|
108
131
|
[→ Full Roadmap](https://codeurali.github.io/mcp-dataverse/roadmap)
|
|
109
132
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var y=class extends Error{constructor(e,n,s,r,i={}){super(e);this.status=n;this.data=s;this.code=r;this.responseHeaders=i;this.name="HttpError"}},R=class{baseURL;timeoutMs;defaultHeaders;tokenProvider;constructor(t){this.baseURL=t.baseURL.endsWith("/")?t.baseURL:t.baseURL+"/",this.timeoutMs=t.timeout??3e4,this.defaultHeaders={...t.headers},this.tokenProvider=t.tokenProvider??void 0}async get(t,e){return this.request("GET",t,void 0,e)}async post(t,e,n){return this.request("POST",t,e,n)}async patch(t,e,n){return this.request("PATCH",t,e,n)}async put(t,e,n){return this.request("PUT",t,e,n)}async delete(t,e){return this.request("DELETE",t,void 0,e)}resolveUrl(t){if(!t.startsWith("http"))return this.baseURL+t;let e=new URL(t),n=new URL(this.baseURL);if(e.origin!==n.origin)throw new y(`SSRF protection: request to '${e.origin}' blocked; only '${n.origin}' is permitted`,0,void 0,"SSRF_BLOCKED");return t}async request(t,e,n,s){let r=this.resolveUrl(e),i={...this.defaultHeaders,...s?.headers};this.tokenProvider&&(i.Authorization=`Bearer ${await this.tokenProvider()}`);let a=s?.timeoutMs??this.timeoutMs,c=new AbortController,u=setTimeout(()=>c.abort(),a);try{let o={method:t,headers:i,signal:c.signal};n!==void 0&&(o.body=typeof n=="string"?n:JSON.stringify(n));let d=await fetch(r,o),p={};if(d.headers.forEach((g,f)=>{p[f]=g}),!d.ok){let g=await d.text(),f;try{f=JSON.parse(g)}catch{f=g||void 0}throw new y(`Request failed with status ${d.status}`,d.status,f,void 0,p)}let m;if(s?.responseType==="text")m=await d.text();else{let g=await d.text();m=g?JSON.parse(g):{}}return{data:m,status:d.status,headers:p}}catch(o){throw o instanceof y?o:o instanceof DOMException&&o.name==="AbortError"?new y("Request timed out",0,void 0,"ECONNABORTED"):o}finally{clearTimeout(u)}}};function O(h){let t=h.indexOf(`\r
|
|
2
|
+
\r
|
|
3
|
+
`),e=h.indexOf(`
|
|
4
|
+
|
|
5
|
+
`);return t!==-1&&(e===-1||t<=e)?t+4:e!==-1?e+2:-1}function q(h,t){let e=[],n=h.split(`--${t}`);for(let s of n){let r=s.trim();if(!r||r==="--")continue;let i=O(s);if(i===-1)continue;let a=s.slice(i),c=O(a);if(c===-1)continue;let o=(a.trimStart().split(/\r?\n/)[0]??"").match(/^HTTP\/\d+\.\d+\s+(\d{3})/),d=o?parseInt(o[1],10):0,p=d>=200&&d<300,m=a.slice(c).trim();if(!m){e.push(p?null:{error:"Empty response body",status:d});continue}try{let g=JSON.parse(m);e.push(p?g:{error:g,status:d})}catch{e.push({error:m,status:d})}}return e}function l(h){return h.replace(/'/g,"''")}var P="9.2",v={opportunities:"opportunityid",territories:"territoryid",categories:"categoryid",activityparties:"activitypartyid",activitymimeattachments:"activitymimeattachmentid",queues:"queueid",queueitems:"queueitemid"},$=class{http;authProvider;maxRetries;constructor(t,e=3,n=3e4){this.authProvider=t,this.maxRetries=e,this.http=new R({baseURL:`${t.environmentUrl}/api/data/v${P}/`,timeout:n,headers:{"OData-MaxVersion":"4.0","OData-Version":"4.0",Accept:"application/json","Content-Type":"application/json; charset=utf-8"},tokenProvider:()=>t.getToken()})}async requestWithRetry(t,e=0){try{return await t()}catch(n){if(n instanceof y){if(n.status===401&&e===0)return this.authProvider.invalidateToken(),this.requestWithRetry(t,e+1);if([429,503,504].includes(n.status)&&e<this.maxRetries){let r=n.responseHeaders["retry-after"],i=r?parseInt(r,10)*1e3:Math.pow(2,e)*1e3;return await new Promise(a=>setTimeout(a,i)),this.requestWithRetry(t,e+1)}if(n.status===400&&n.data?.error?.code==="0x80071151"&&e<5){let i=Math.pow(2,e)*5e3;return await new Promise(a=>setTimeout(a,i)),this.requestWithRetry(t,e+1)}}throw this.formatError(n)}}formatError(t){if(t instanceof y){let e=t.data?.error;return e?new Error(`Dataverse error ${e.code??""}: ${e.message??"Unknown error"}`):t.code==="ECONNABORTED"?new Error("Request timed out. Check your Dataverse environment URL."):t}return t instanceof Error?t:new Error(String(t))}async whoAmI(){return this.requestWithRetry(async()=>{let t=await this.http.get("WhoAmI"),{UserId:e,BusinessUnitId:n,OrganizationId:s}=t.data,r="";try{r=(await this.http.get(`organizations(${s})?$select=name`)).data.name??""}catch{r=""}let i=this.authProvider.environmentUrl;return{UserId:e,BusinessUnitId:n,OrganizationId:s,OrganizationName:r,EnvironmentUrl:i}})}async listTables(t=!1){let s=["$select=LogicalName,SchemaName,DisplayName,EntitySetName,PrimaryIdAttribute,PrimaryNameAttribute,IsCustomEntity",t?"$filter=IsCustomEntity eq true":""].filter(Boolean).join("&");return this.requestWithRetry(()=>this.http.get(`EntityDefinitions?${s}`).then(r=>r.data.value))}async getTableMetadata(t,e=!0){let n=e?"$expand=Attributes":"",s=`EntityDefinitions(LogicalName='${l(t)}')${n?"?"+n:""}`;return this.requestWithRetry(()=>this.http.get(s).then(r=>r.data))}async fetchAllPagesOData(t){let e=[],n=t;for(;n;){let s=await this.requestWithRetry(()=>this.http.get(n).then(r=>r.data));e.push(...s.value??[]),n=s["@odata.nextLink"]}return e}async getRelationships(t){let e=l(t),[n,s,r]=await Promise.all([this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${e}')/OneToManyRelationships`),this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${e}')/ManyToOneRelationships`),this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${e}')/ManyToManyRelationships`)]);return[...n,...s,...r]}async query(t,e={}){let n=a=>encodeURIComponent(a).replace(/%28/g,"(").replace(/%29/g,")").replace(/%2C/g,",").replace(/%27/g,"'").replace(/%40/g,"@"),s=[];e.select?.length&&s.push(`$select=${e.select.join(",")}`),e.filter&&s.push(`$filter=${n(e.filter)}`),e.orderby&&s.push(`$orderby=${n(e.orderby)}`),e.top&&s.push(`$top=${e.top}`),e.expand&&s.push(`$expand=${e.expand}`),e.count&&s.push("$count=true"),e.apply&&s.push(`$apply=${n(e.apply)}`);let r=`${t}${s.length?"?"+s.join("&"):""}`,i=e.formattedValues?{headers:{Prefer:'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'}}:void 0;return this.requestWithRetry(()=>this.http.get(r,i).then(a=>a.data))}async executeFetchXml(t,e,n){let s=encodeURIComponent(e),r=n?{headers:{Prefer:'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'}}:void 0;return this.requestWithRetry(()=>this.http.get(`${t}?fetchXml=${s}`,r).then(i=>i.data))}async getRecord(t,e,n,s){let r=[];n?.length&&r.push(`$select=${n.join(",")}`),s&&r.push(`$expand=${s}`);let i=r.length?`?${r.join("&")}`:"";return this.requestWithRetry(async()=>{let a=await this.http.get(`${t}(${e})${i}`,{headers:{Prefer:'odata.include-annotations="*"'}}),c=a.headers["odata-etag"]??a.data["@odata.etag"]??null;return{record:a.data,etag:c}})}async createRecord(t,e){return this.requestWithRetry(async()=>{let n=await this.http.post(t,e,{headers:{Prefer:"return=representation"}}),r=n.headers["odata-entityid"]?.match(/\(([^)]+)\)/)?.[1];if(r)return r;let i=n.data,a=i["@odata.id"]?.match(/\(([^)]+)\)/)?.[1];if(a)return a;let c=v[t]??t.replace(/s$/,"")+"id",u=i[c];return u||(n.headers.location?.match(/\(([^)]+)\)/)?.[1]??"")})}async updateRecord(t,e,n,s){await this.requestWithRetry(()=>this.http.patch(`${t}(${e})`,n,{headers:{"If-Match":s??"*"}}))}async deleteRecord(t,e){await this.requestWithRetry(()=>this.http.delete(`${t}(${e})`))}async upsertRecord(t,e,n,s,r="upsert",i){return this.requestWithRetry(async()=>{let a=i?`${t}(${i})`:`${t}(${l(e)}='${l(n)}')`,c={Prefer:"return=representation"};r==="createOnly"&&(c["If-None-Match"]="*"),r==="updateOnly"&&(c["If-Match"]="*");try{let u=await this.http.put(a,s,{headers:c}),o=u.status===201?"created":"updated",p=u.headers["odata-entityid"]?.match(/\(([^)]+)\)/)?.[1],m=u.data,g=v[t]??t.replace(/s$/,"")+"id",f=p??m?.[g]??n;return{operation:o,id:f}}catch(u){if(u instanceof y&&u.status===412){if(r==="createOnly")throw new Error("Record already exists");if(r==="updateOnly")throw new Error("Record not found")}throw u}})}async associate(t,e,n,s,r){let i=`${this.authProvider.environmentUrl}/api/data/v${P}/${s}(${r})`;await this.requestWithRetry(()=>this.http.post(`${t}(${e})/${n}/$ref`,{"@odata.id":i}))}async disassociate(t,e,n,s,r){let i=s?`?$id=${this.authProvider.environmentUrl}/api/data/v${P}/${r??t}(${s})`:"";await this.requestWithRetry(()=>this.http.delete(`${t}(${e})/${n}/$ref${i}`))}};var w=class extends ${async executeAction(t,e={}){return this.requestWithRetry(()=>this.http.post(t,e).then(n=>n.data))}async executeFunction(t,e={}){let n=[],s=[];Object.entries(e).forEach(([a,c])=>{if(typeof c=="object"&&c!==null){let u=`@${a}`;s.push(`${l(a)}=${u}`),n.push(`${u}=${encodeURIComponent(JSON.stringify(c))}`)}else typeof c=="string"?s.push(`${l(a)}='${l(c)}'`):s.push(`${l(a)}=${String(c)}`)});let r=s.length?`${t}(${s.join(",")})`:`${t}()`,i=n.length?`${r}?${n.join("&")}`:r;return this.requestWithRetry(()=>this.http.get(i).then(a=>a.data))}async search(t){let n=`${this.http.baseURL.replace(/\/api\/data\/v[\d.]+\/?$/,"")}/api/search/v2.0/query`;return this.requestWithRetry(()=>this.http.post(n,t).then(s=>s.data))}async executeBoundAction(t,e,n,s={}){return this.requestWithRetry(()=>this.http.post(`${t}(${e})/Microsoft.Dynamics.CRM.${n}`,s).then(r=>r.data))}};var b=class extends w{async listDependencies(t,e){return this.requestWithRetry(()=>this.http.get(`RetrieveDependenciesForDelete(ComponentType=${t},ObjectId=${e})`).then(n=>n.data.value))}async listTableDependencies(t,e){let n={0:"Workflow",1:"Dialog",2:"BusinessRule",3:"Action",4:"BusinessProcessFlow",5:"Flow"},s={0:"Draft",1:"Active",2:"Inactive"},i=(await this.requestWithRetry(()=>this.http.get(`workflows?$filter=primaryentity eq '${l(t)}' and statecode ne 2&$select=name,workflowid,statecode,category,triggeroncreate,triggerondelete,triggeronupdateattributelist`).then(o=>o.data))).value.map(o=>{let d=[];return o.triggeroncreate&&d.push("Create"),o.triggerondelete&&d.push("Delete"),o.triggeronupdateattributelist&&d.push("Update"),{componentType:n[o.category]??`Category${o.category}`,name:o.name,id:o.workflowid,state:s[o.statecode]??`State${o.statecode}`,triggerEvent:d.length?d.join(","):null,solutionName:null}}),a=e?.length?i.filter(o=>e.includes(o.componentType)):i,u=e?.some(o=>o==="Plugin"||o==="CustomAPI")?"Plugin and CustomAPI types require additional SDK message queries and are not yet implemented. Results show Workflow/BusinessRule/Flow/Action dependencies only.":null;return{tableName:t,dependencies:a,count:a.length,warning:u}}async listGlobalOptionSets(){return this.requestWithRetry(()=>this.http.get("GlobalOptionSetDefinitions").then(t=>t.data.value))}async getOptionSet(t){return this.requestWithRetry(()=>this.http.get(`GlobalOptionSetDefinitions(Name='${l(t)}')`).then(e=>e.data))}async getAttributeOptionSet(t,e){let n=["PicklistAttributeMetadata","MultiSelectPicklistAttributeMetadata","StatusAttributeMetadata","StateAttributeMetadata"];for(let s of n)try{let r=`EntityDefinitions(LogicalName='${l(t)}')/Attributes(LogicalName='${l(e)}')/Microsoft.Dynamics.CRM.${s}?$select=LogicalName,DisplayName&$expand=OptionSet`,u=((await this.requestWithRetry(()=>this.http.get(r).then(o=>o.data))).OptionSet?.Options??[]).map(o=>({label:o.Label?.UserLocalizedLabel?.Label??"",value:o.Value}));return{entityLogicalName:t,attributeLogicalName:e,attributeType:s.replace("AttributeMetadata",""),options:u}}catch{continue}throw new Error(`Attribute '${e}' on entity '${t}' is not a Picklist, MultiSelectPicklist, Status, or State attribute, or does not exist.`)}async getEntityKeys(t){return this.requestWithRetry(async()=>(await this.http.get(`EntityDefinitions(LogicalName='${l(t)}')/Keys?$select=SchemaName,LogicalName,KeyAttributes,IsCustomizable,EntityKeyIndexStatus`)).data.value.map(n=>({schemaName:n.SchemaName,logicalName:n.LogicalName,keyAttributes:n.KeyAttributes,isCustomizable:n.IsCustomizable?.Value??!1,indexStatus:n.EntityKeyIndexStatus})))}async updateEntityDefinition(t,e){await this.requestWithRetry(()=>this.http.patch(`EntityDefinitions(LogicalName='${l(t)}')`,e,{headers:{"MSCRM.MergeLabels":"true"}}))}async createAttribute(t,e){return this.requestWithRetry(async()=>(await this.http.post(`EntityDefinitions(LogicalName='${l(t)}')/Attributes`,e,{headers:{Prefer:"return=representation"}})).data.MetadataId??"")}async updateAttribute(t,e,n){await this.requestWithRetry(()=>this.http.put(`EntityDefinitions(LogicalName='${l(t)}')/Attributes(LogicalName='${l(e)}')`,n,{headers:{"MSCRM.MergeLabels":"true"}}))}async deleteAttribute(t,e){await this.requestWithRetry(()=>this.http.delete(`EntityDefinitions(LogicalName='${l(t)}')/Attributes(LogicalName='${l(e)}')`))}async createRelationship(t){return this.requestWithRetry(async()=>(await this.http.post("RelationshipDefinitions",t)).headers["odata-entityid"]?.match(/\(([^)]+)\)$/)?.[1]??"")}};var T=class extends b{async batchExecute(t,e=!1){let n=`batch_${Date.now()}`,s="";if(e){let i=`changeset_${Date.now()+1}`,a=t.filter(u=>u.method==="GET"),c=t.filter(u=>u.method!=="GET");for(let u of a)s+=`--${n}\r
|
|
6
|
+
`,s+=`Content-Type: application/http\r
|
|
7
|
+
`,s+=`Content-Transfer-Encoding: binary\r
|
|
8
|
+
\r
|
|
9
|
+
`,s+=`${u.method} ${this.http.baseURL}${u.url} HTTP/1.1\r
|
|
10
|
+
`,s+=`Accept: application/json\r
|
|
11
|
+
\r
|
|
12
|
+
`;if(c.length>0){s+=`--${n}\r
|
|
13
|
+
`,s+=`Content-Type: multipart/mixed; boundary=${i}\r
|
|
14
|
+
\r
|
|
15
|
+
`;let u=1;for(let o of c)s+=`--${i}\r
|
|
16
|
+
`,s+=`Content-Type: application/http\r
|
|
17
|
+
`,s+=`Content-Transfer-Encoding: binary\r
|
|
18
|
+
`,s+=`Content-ID: ${o.contentId??u++}\r
|
|
19
|
+
\r
|
|
20
|
+
`,s+=`${o.method} ${this.http.baseURL}${o.url} HTTP/1.1\r
|
|
21
|
+
`,s+=`Content-Type: application/json\r
|
|
22
|
+
\r
|
|
23
|
+
`,o.body&&(s+=JSON.stringify(o.body)),s+=`\r
|
|
24
|
+
`;s+=`--${i}--\r
|
|
25
|
+
`}}else t.forEach(i=>{s+=`--${n}\r
|
|
26
|
+
`,s+=`Content-Type: application/http\r
|
|
27
|
+
`,s+=`Content-Transfer-Encoding: binary\r
|
|
28
|
+
\r
|
|
29
|
+
`,s+=`${i.method} ${this.http.baseURL}${i.url} HTTP/1.1\r
|
|
30
|
+
`,s+=`Content-Type: application/json\r
|
|
31
|
+
\r
|
|
32
|
+
`,i.body&&(s+=JSON.stringify(i.body)),s+=`\r
|
|
33
|
+
`});s+=`--${n}--`;let r=await this.requestWithRetry(()=>this.http.post("$batch",s,{headers:{"Content-Type":`multipart/mixed;boundary=${n}`},responseType:"text"}));try{let a=(r.headers["content-type"]??"").match(/boundary=(?:"([^"]+)"|([^;"\s]+))/),c=a?.[1]??a?.[2];return c?q(r.data,c):(process.stderr.write(`[batchExecute] No multipart boundary in response Content-Type; returning raw data.
|
|
34
|
+
`),[r.data])}catch(i){return process.stderr.write(`[batchExecute] Failed to parse multipart response; returning raw data. ${String(i)}
|
|
35
|
+
`),[r.data]}}};var x={1:"Entity",2:"Attribute",3:"Relationship",9:"OptionSet",29:"Workflow",61:"SystemForm",71:"SiteMap",90:"PluginAssembly",92:"PluginType",97:"WebResource",95:"ServiceEndpoint",79:"ConnectionRole"};function k(h){return h.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var E=class extends T{async executeBoundFunction(t,e,n,s={}){let r=[],i=[];Object.entries(s).forEach(([o,d])=>{if(typeof d=="object"&&d!==null){let p=`@${o}`;i.push(`${l(o)}=${p}`),r.push(`${p}=${encodeURIComponent(JSON.stringify(d))}`)}else typeof d=="string"?i.push(`${l(o)}='${l(d)}'`):i.push(`${l(o)}=${String(d)}`)});let a=i.join(","),c=`${t}(${e})/Microsoft.Dynamics.CRM.${n}(${a})`,u=r.length?`${c}?${r.join("&")}`:c;return this.requestWithRetry(()=>this.http.get(u).then(o=>o.data))}async queryWithPaging(t,e={}){let n=Math.min(e.maxTotal??5e3,5e4),s=[],r=0,i={};e.select!==void 0&&(i.select=e.select),e.filter!==void 0&&(i.filter=e.filter),e.orderby!==void 0&&(i.orderby=e.orderby),e.expand!==void 0&&(i.expand=e.expand);let a=await this.query(t,i);for(s.push(...a.value),r++;a["@odata.nextLink"]&&s.length<n;){let u=a["@odata.nextLink"];a=await this.requestWithRetry(()=>this.http.get(u).then(o=>o.data)),s.push(...a.value),r++}let c=s.slice(0,n);return{records:c,totalRetrieved:c.length,pageCount:r}}async getChangedRecords(t,e,n){let s,r={};if(e===null){let p=n?.length?`?$select=${n.join(",")}`:"";s=`${t}${p}`,r.Prefer="odata.track-changes"}else{let p=n?.length?`&$select=${n.join(",")}`:"";s=`${t}?$deltatoken=${e}${p}`}let i=await this.requestWithRetry(()=>this.http.get(s,{headers:r}).then(p=>p.data)),a=i.value??[],c=[],u=[];for(let p of a)if("@removed"in p){let m=String(p["@id"]??""),g=m.match(/\(([^)]+)\)$/);u.push({id:g?g[1]:m})}else c.push(p);let o=i["@odata.deltaLink"],d=null;if(o){let p=o.match(/\$deltatoken=([^&]+)/);d=p?decodeURIComponent(p[1]):null}return{newAndModified:c,deleted:u,nextDeltaToken:d}}async getSolutionComponents(t,e,n=200){return this.requestWithRetry(async()=>{let r=(await this.http.get(`solutions?$filter=uniquename eq '${l(t)}'&$select=solutionid,uniquename,friendlyname,version&$top=1`)).data.value;if(!r.length)throw new Error(`Solution '${t}' not found`);let i=r[0],a=i.solutionid,c=`_solutionid_value eq ${a}`;e!==void 0&&(c+=` and componenttype eq ${e}`);let o=(await this.http.get(`solutioncomponents?$filter=${c}&$select=componenttype,objectid&$top=${n}&$orderby=componenttype`)).data.value.map(d=>({componentType:d.componenttype,componentTypeName:x[d.componenttype]??`Type${d.componenttype}`,objectId:d.objectid}));return{solutionName:i.uniquename,solutionId:a,friendlyName:i.friendlyname,version:i.version,components:o,count:o.length}})}async publishCustomizations(t){return this.requestWithRetry(async()=>{if(!(t&&((t.entities?.length??0)>0||(t.webResources?.length??0)>0||(t.optionSets?.length??0)>0)))return await this.http.post("PublishAllXml",{},{timeoutMs:12e4}),{published:!0,message:"All customizations published successfully"};let n="<importexportxml>";return t.entities?.length&&(n+="<entities>"+t.entities.map(s=>`<entity>${k(s)}</entity>`).join("")+"</entities>"),t.webResources?.length&&(n+="<webresources>"+t.webResources.map(s=>`<webresource>${k(s)}</webresource>`).join("")+"</webresources>"),t.optionSets?.length&&(n+="<optionsets>"+t.optionSets.map(s=>`<optionset>${k(s)}</optionset>`).join("")+"</optionsets>"),n+="</importexportxml>",await this.http.post("PublishXml",{ParameterXml:n},{timeoutMs:12e4}),{published:!0,message:"Selected customizations published successfully"}})}async listDataverseWorkflows(t){return this.requestWithRetry(async()=>{let e=[];t.category!==void 0&&e.push(`category eq ${t.category}`),t.nameContains&&e.push(`contains(name,'${l(t.nameContains)}')`);let n=`workflows?$select=workflowid,name,description,category,statecode,statuscode,type,modifiedon&$orderby=name asc&$top=${t.top??50}`;return e.length>0&&(n+=`&$filter=${e.join(" and ")}`),(await this.http.get(n)).data.value??[]})}async getDataverseWorkflow(t){return this.requestWithRetry(()=>this.http.get(`workflows(${t})?$select=workflowid,name,description,category,statecode,statuscode,type,modifiedon`).then(e=>e.data))}};export{l as a,E as b};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as a}from"./chunk-MPWVUZBX.js";export{a as DataverseAdvancedClient};
|
package/dist/doctor.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
async function f(){let t=e=>process.stdout.write(e+`
|
|
3
|
-
`),o=e=>t(` \u2705 ${e}`),n=e=>t(` \u274C ${e}`);t(""),t("\u{1F3E5} mcp-dataverse doctor"),t("\u2500".repeat(50)),t("");let r=!0;t("\u{1F4CB} Environment");let c=process.version;parseInt(c.slice(1).split(".")[0],10)>=20?o(`Node.js ${c}`):(n(`Node.js ${c} \u2014 requires v20+`),r=!1),t(""),t("\u2699\uFE0F Configuration");try{let{loadConfig:e}=await import("./config.loader-VTIKUDN7.js"),s=e();o(`Environment URL: ${s.environmentUrl}`),o(`Timeout: ${s.requestTimeoutMs}ms, Retries: ${s.maxRetries}`)}catch(e){let s=e instanceof Error?e.message:String(e);n(`Configuration error: ${s}`),r=!1,t(""),t(r?"\u2705 All checks passed!":"\u274C Some checks failed."),process.exit(r?0:1)}t(""),t("\u{1F511} Authentication");try{let{loadConfig:e}=await import("./config.loader-VTIKUDN7.js"),s=e(),{createAuthProvider:l}=await import("./auth-provider.factory-MSMLSOX3.js"),i=await l(s).getToken();i?(o("Token acquired successfully"),i.split(".").length===3&&o("Valid JWT token format")):(n("No token returned"),r=!1)}catch(e){let s=e instanceof Error?e.message:String(e);n(`Auth failed: ${s}`),r=!1}t(""),t("\u{1F310} Dataverse API");try{let{loadConfig:e}=await import("./config.loader-VTIKUDN7.js"),s=e(),{createAuthProvider:l}=await import("./auth-provider.factory-MSMLSOX3.js"),{DataverseAdvancedClient:m}=await import("./dataverse-client-advanced-
|
|
3
|
+
`),o=e=>t(` \u2705 ${e}`),n=e=>t(` \u274C ${e}`);t(""),t("\u{1F3E5} mcp-dataverse doctor"),t("\u2500".repeat(50)),t("");let r=!0;t("\u{1F4CB} Environment");let c=process.version;parseInt(c.slice(1).split(".")[0],10)>=20?o(`Node.js ${c}`):(n(`Node.js ${c} \u2014 requires v20+`),r=!1),t(""),t("\u2699\uFE0F Configuration");try{let{loadConfig:e}=await import("./config.loader-VTIKUDN7.js"),s=e();o(`Environment URL: ${s.environmentUrl}`),o(`Timeout: ${s.requestTimeoutMs}ms, Retries: ${s.maxRetries}`)}catch(e){let s=e instanceof Error?e.message:String(e);n(`Configuration error: ${s}`),r=!1,t(""),t(r?"\u2705 All checks passed!":"\u274C Some checks failed."),process.exit(r?0:1)}t(""),t("\u{1F511} Authentication");try{let{loadConfig:e}=await import("./config.loader-VTIKUDN7.js"),s=e(),{createAuthProvider:l}=await import("./auth-provider.factory-MSMLSOX3.js"),i=await l(s).getToken();i?(o("Token acquired successfully"),i.split(".").length===3&&o("Valid JWT token format")):(n("No token returned"),r=!1)}catch(e){let s=e instanceof Error?e.message:String(e);n(`Auth failed: ${s}`),r=!1}t(""),t("\u{1F310} Dataverse API");try{let{loadConfig:e}=await import("./config.loader-VTIKUDN7.js"),s=e(),{createAuthProvider:l}=await import("./auth-provider.factory-MSMLSOX3.js"),{DataverseAdvancedClient:m}=await import("./dataverse-client-advanced-ZG4OPCGR.js"),i=l(s),a=await new m(i,s.maxRetries,s.requestTimeoutMs).whoAmI();o(`Organization: ${a.OrganizationName||"N/A"}`),o(`User ID: ${a.UserId||"N/A"}`),o(`Business Unit: ${a.BusinessUnitId||"N/A"}`),o(`Environment: ${a.EnvironmentUrl||s.environmentUrl}`)}catch(e){let s=e instanceof Error?e.message:String(e);n(`API call failed: ${s}`),r=!1}t(""),t("\u2500".repeat(50)),t(r?"\u2705 All checks passed! Your mcp-dataverse setup is healthy.":"\u274C Some checks failed. Review the errors above."),t(""),process.exit(r?0:1)}export{f as runDoctor};
|
package/dist/http-server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
import{createServer as
|
|
2
|
-
`),
|
|
3
|
-
`)
|
|
1
|
+
import{createServer as S}from"http";import{timingSafeEqual as u,createHmac as m,randomUUID as v}from"crypto";import{StreamableHTTPServerTransport as w}from"@modelcontextprotocol/sdk/server/streamableHttp.js";function g(a,r){try{let i=Buffer.from("mcp-token-verify-constant"),c=m("sha256",i).update(a).digest(),d=m("sha256",i).update(r).digest();return u(c,d)}catch{return!1}}async function O(a,r,i,c){let d=process.env.MCP_HTTP_JSON_RESPONSE!=="false",l=process.env.MCP_HTTP_SECRET??process.env.BEARER_TOKEN,h=process.env.MCP_HTTP_CORS_ORIGIN??"*",n=new Map,p=S(async(s,e)=>{if(e.setHeader("Access-Control-Allow-Origin",h),e.setHeader("Access-Control-Allow-Methods","GET, POST, DELETE, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, mcp-session-id, Authorization"),s.method==="OPTIONS"){e.writeHead(204),e.end();return}let T=new URL(s.url??"/",`http://localhost:${r}`);if(T.pathname==="/health"&&s.method==="GET"){e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({status:"ok",version:i,tools:c}));return}if(T.pathname==="/mcp"){if(l){let o=s.headers.authorization??"",t=o.startsWith("Bearer ")?o.slice(7):"";if(!g(t,l)){e.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":'Bearer realm="MCP Dataverse"'}),e.end(JSON.stringify({error:"Unauthorized"}));return}}try{let o=s.headers["mcp-session-id"];if(o){let t=n.get(o);if(!t){e.writeHead(404,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"Session not found. Please reinitialize."}));return}await t.handleRequest(s,e)}else{if(s.method!=="POST"){e.writeHead(400,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"mcp-session-id header required for GET and DELETE"}));return}let t=new w({sessionIdGenerator:()=>v(),enableJsonResponse:d});t.sessionId&&(n.set(t.sessionId,t),t.onclose=()=>{t.sessionId&&n.delete(t.sessionId)}),await a().connect(t),await t.handleRequest(s,e),t.sessionId&&(n.has(t.sessionId)||(n.set(t.sessionId,t),t.onclose=()=>{t.sessionId&&n.delete(t.sessionId)}))}}catch(o){process.stderr.write(`[http-server] Unhandled error: ${String(o)}
|
|
2
|
+
`),e.headersSent||(e.writeHead(500,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"Internal server error"})))}return}e.writeHead(404,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"Not found"}))});await new Promise(s=>{p.listen(r,()=>{process.stderr.write(`MCP Dataverse HTTP server listening on http://localhost:${r}/mcp
|
|
3
|
+
`),s()})});let f=async()=>{process.stderr.write(`Shutting down HTTP server...
|
|
4
|
+
`);for(let s of n.values())await s.close();p.close(),process.exit(0)};process.on("SIGINT",f),process.on("SIGTERM",f),await new Promise(s=>{p.on("close",s)})}export{O as startHttpTransport};
|