mcp-dataverse 0.5.0 → 0.7.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 +270 -171
- package/README.md +33 -29
- package/dist/chunk-6KQIRQNP.js +35 -0
- package/dist/chunk-IA4YDPXM.js +35 -0
- package/dist/dataverse-client-advanced-54FU3FKK.js +1 -0
- package/dist/dataverse-client-advanced-CON7DMNS.js +1 -0
- package/dist/doctor.js +1 -1
- package/dist/http-server.js +6 -6
- package/dist/server.js +151 -15
- package/package.json +2 -2
- package/server.json +3 -3
- package/dist/chunk-FSM3J3WD.js +0 -35
- package/dist/dataverse-client-advanced-EASNSX3M.js +0 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
**The most complete MCP server for Microsoft Dataverse.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
79 tools · 4 resources · 10 guided workflows · Three auth modes
|
|
10
10
|
|
|
11
11
|
[](https://www.npmjs.com/package/mcp-dataverse)
|
|
12
12
|
[](https://www.npmjs.com/package/mcp-dataverse)
|
|
@@ -50,11 +50,11 @@ The interactive wizard configures your environment, registers the server in VS C
|
|
|
50
50
|
|
|
51
51
|
Three modes — choose based on where the server runs:
|
|
52
52
|
|
|
53
|
-
| Mode
|
|
54
|
-
|
|
55
|
-
| **Device Code** (default) | Local development — interactive Microsoft login, token cached on disk
|
|
56
|
-
| **Client Credentials**
|
|
57
|
-
| **Managed Identity**
|
|
53
|
+
| Mode | When to use |
|
|
54
|
+
| :------------------------ | :------------------------------------------------------------------------------------------------ |
|
|
55
|
+
| **Device Code** (default) | Local development — interactive Microsoft login, token cached on disk |
|
|
56
|
+
| **Client Credentials** | Unattended: CI/CD, Docker, Azure services — `authMethod: "client-credentials"` + App Registration |
|
|
57
|
+
| **Managed Identity** | Azure-hosted (App Service, Container Apps) — zero secrets, `authMethod: "managed-identity"` |
|
|
58
58
|
|
|
59
59
|
**Device code quick start:** authentication triggers on the first tool call.
|
|
60
60
|
|
|
@@ -68,25 +68,27 @@ For client credentials and managed identity setup, see [Authentication docs](htt
|
|
|
68
68
|
|
|
69
69
|
## Capabilities
|
|
70
70
|
|
|
71
|
-
| Category
|
|
72
|
-
| ----------------------- | ----- |
|
|
73
|
-
| **Metadata**
|
|
74
|
-
| **Query**
|
|
75
|
-
| **CRUD**
|
|
76
|
-
| **Relations**
|
|
77
|
-
| **Actions & Functions** | 6
|
|
78
|
-
| **Batch**
|
|
79
|
-
| **Solutions**
|
|
80
|
-
| **Search**
|
|
81
|
-
| **Users & Teams**
|
|
82
|
-
| **RBAC**
|
|
83
|
-
| **Files**
|
|
84
|
-
| **Audit & Trace**
|
|
85
|
-
| **Annotations**
|
|
86
|
-
| **Customization**
|
|
87
|
-
| **Attributes**
|
|
88
|
-
| **
|
|
89
|
-
|
|
|
71
|
+
| Category | Count | Description |
|
|
72
|
+
| ----------------------- | ----- | --------------------------------------------------------------------- |
|
|
73
|
+
| **Metadata** | 9 | Tables, schema, relationships, option sets, entity keys |
|
|
74
|
+
| **Query** | 3 | OData, FetchXML, paginated retrieval |
|
|
75
|
+
| **CRUD** | 6 | Get, create, update, delete, upsert, assign |
|
|
76
|
+
| **Relations** | 4 | Associate, associate bulk, disassociate, query associations |
|
|
77
|
+
| **Actions & Functions** | 6 | Bound/unbound Dataverse actions and functions |
|
|
78
|
+
| **Batch** | 1 | Up to 1000 operations atomically |
|
|
79
|
+
| **Solutions** | 2 | Publish customizations, create sitemap |
|
|
80
|
+
| **Search** | 1 | Full-text Relevance Search |
|
|
81
|
+
| **Users & Teams** | 4 | Users, roles, teams, role assignment |
|
|
82
|
+
| **RBAC** | 7 | Role privileges: list, assign, remove, add, replace, get, team |
|
|
83
|
+
| **Files** | 2 | Upload/download file and image columns |
|
|
84
|
+
| **Audit & Trace** | 3 | Audit log, plugin trace logs, workflow trace logs |
|
|
85
|
+
| **Annotations** | 2 | Notes and file attachments |
|
|
86
|
+
| **Customization** | 4 | Custom actions, plugins, env variables, connection references |
|
|
87
|
+
| **Attributes** | 4 | Create, update, delete columns; lookup column type |
|
|
88
|
+
| **Schema (write)** | 2 | Create custom tables and relationships |
|
|
89
|
+
| **Record Access** | 4 | Check, grant, revoke record sharing; merge records |
|
|
90
|
+
| **Assistance** | 2 | Tool router, tool tags |
|
|
91
|
+
| **+ more** | … | Delta sync, impersonation, views, business units, duplicate detection |
|
|
90
92
|
|
|
91
93
|
[→ Full Capabilities Reference](https://codeurali.github.io/mcp-dataverse/capabilities)
|
|
92
94
|
|
|
@@ -101,6 +103,7 @@ MCP_TRANSPORT=http MCP_HTTP_PORT=3000 MCP_HTTP_SECRET=mysecret node dist/server.
|
|
|
101
103
|
```
|
|
102
104
|
|
|
103
105
|
Connect using VS Code / Copilot with:
|
|
106
|
+
|
|
104
107
|
```json
|
|
105
108
|
{
|
|
106
109
|
"servers": {
|
|
@@ -137,11 +140,12 @@ MCP Dataverse is designed to be comprehensive, but most AI models work best with
|
|
|
137
140
|
|
|
138
141
|
## Roadmap
|
|
139
142
|
|
|
140
|
-
| Version
|
|
141
|
-
|
|
|
142
|
-
| **v0.4** | HTTP transport + attribute management + schema consistency
|
|
143
|
+
| Version | Feature | Status |
|
|
144
|
+
| -------- | ----------------------------------------------------------------- | ----------- |
|
|
145
|
+
| **v0.4** | HTTP transport + attribute management + schema consistency | ✅ Released |
|
|
143
146
|
| **v0.5** | Enterprise auth (Client Credentials, Managed Identity, Entra JWT) | ✅ Released |
|
|
144
|
-
| **v0.6** | MCP Prompts
|
|
147
|
+
| **v0.6** | MCP Prompts (5 templates) + MCP Resources (4) | ✅ Released |
|
|
148
|
+
| **v0.7** | Schema write (create table/relationship) + Record Access (share, merge) | ✅ Released (v0.7.5) |
|
|
145
149
|
|
|
146
150
|
[→ Full Roadmap](https://codeurali.github.io/mcp-dataverse/roadmap)
|
|
147
151
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var y=class extends Error{constructor(t,n,s,r,i={}){super(t);this.status=n;this.data=s;this.code=r;this.responseHeaders=i;this.name="HttpError"}},R=class{baseURL;timeoutMs;defaultHeaders;tokenProvider;constructor(e){this.baseURL=e.baseURL.endsWith("/")?e.baseURL:e.baseURL+"/",this.timeoutMs=e.timeout??3e4,this.defaultHeaders={...e.headers},this.tokenProvider=e.tokenProvider??void 0}async get(e,t){return this.request("GET",e,void 0,t)}async post(e,t,n){return this.request("POST",e,t,n)}async patch(e,t,n){return this.request("PATCH",e,t,n)}async put(e,t,n){return this.request("PUT",e,t,n)}async delete(e,t){return this.request("DELETE",e,void 0,t)}resolveUrl(e){if(!e.startsWith("http"))return this.baseURL+e;let t=new URL(e),n=new URL(this.baseURL);if(t.origin!==n.origin)throw new y(`SSRF protection: request to '${t.origin}' blocked; only '${n.origin}' is permitted`,0,void 0,"SSRF_BLOCKED");return e}async request(e,t,n,s){let r=this.resolveUrl(t),i={...this.defaultHeaders,...s?.headers};this.tokenProvider&&(i.Authorization=`Bearer ${await this.tokenProvider()}`);let u=s?.timeoutMs??this.timeoutMs,c=new AbortController,a=setTimeout(()=>c.abort(),u);try{let o={method:e,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(a)}}};function O(h){let e=h.indexOf(`\r
|
|
2
|
+
\r
|
|
3
|
+
`),t=h.indexOf(`
|
|
4
|
+
|
|
5
|
+
`);return e!==-1&&(t===-1||e<=t)?e+4:t!==-1?t+2:-1}function E(h,e){let t=[],n=h.split(`--${e}`);for(let s of n){let r=s.trim();if(!r||r==="--")continue;let i=O(s);if(i===-1)continue;let u=s.slice(i),c=O(u);if(c===-1)continue;let o=(u.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=u.slice(c).trim();if(!m){t.push(p?null:{error:"Empty response body",status:d});continue}try{let g=JSON.parse(m);t.push(p?g:{error:g,status:d})}catch{t.push({error:m,status:d})}}return t}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(e,t=3,n=3e4){this.authProvider=e,this.maxRetries=t,this.http=new R({baseURL:`${e.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:()=>e.getToken()})}get serviceRootUrl(){return this.http.baseURL}async requestWithRetry(e,t=0){try{return await e()}catch(n){if(n instanceof y){if(n.status===401&&t===0)return this.authProvider.invalidateToken(),this.requestWithRetry(e,t+1);if([429,503,504].includes(n.status)&&t<this.maxRetries){let r=n.responseHeaders["retry-after"],i=r?parseInt(r,10)*1e3:Math.pow(2,t)*1e3;return await new Promise(u=>setTimeout(u,i)),this.requestWithRetry(e,t+1)}if(n.status===400){let r=n.data?.error?.code;if(r==="0x80071151"&&t<5){let c=Math.pow(2,t)*5e3;return await new Promise(a=>setTimeout(a,c)),this.requestWithRetry(e,t+1)}if(r==="0x80071151"&&t>=5)throw new Error(`[TRANSIENT_LOCK_EXHAUSTED] Dataverse customization lock (0x80071151) still held after ${t} retries (~155s total). A publish operation is running server-side. WAIT at least 60 seconds, then retry the operation ONCE manually. Do NOT loop-retry. Inform the user that Dataverse is processing a previous schema change.`);let i=n.data?.error?.message?.toLowerCase()??"",u=r==="0x80044181"||i.includes("publish")&&(i.includes("already")||i.includes("in progress")||i.includes("another user"));if(u&&t<4)return await new Promise(c=>setTimeout(c,3e4)),this.requestWithRetry(e,t+1);if(u&&t>=4)throw new Error(`[TRANSIENT_LOCK_EXHAUSTED] Dataverse publish lock still held after ${t} retries (~120s total). A PublishAllXml operation is in progress server-side. WAIT at least 60 seconds before retrying ONCE. Do NOT call dataverse_publish_customizations again immediately. Inform the user that Dataverse is serialising a background publish job.`)}}throw this.formatError(n)}}formatError(e){if(e instanceof y){let t=e.data?.error;return t?.code==="0x80060888"?new Error(`Dataverse error 0x80060888: The Web API endpoint does not exist in this organization. This means the action/function name is wrong OR the entity set name is incorrect \u2014 NOT necessarily a permissions error. Original message: ${t.message??""}. Tip: verify action names via $metadata or use dataverse_resolve_entity_name for entity names.`):t?new Error(`Dataverse error ${t.code??""}: ${t.message??"Unknown error"}`):e.code==="ECONNABORTED"?new Error("Request timed out. Check your Dataverse environment URL."):e}return e instanceof Error?e:new Error(String(e))}async whoAmI(){return this.requestWithRetry(async()=>{let e=await this.http.get("WhoAmI"),{UserId:t,BusinessUnitId:n,OrganizationId:s}=e.data,r="";try{r=(await this.http.get(`organizations(${s})?$select=name`)).data.name??""}catch{r=""}let i=this.authProvider.environmentUrl;return{UserId:t,BusinessUnitId:n,OrganizationId:s,OrganizationName:r,EnvironmentUrl:i}})}async listTables(e=!1){let s=["$select=LogicalName,SchemaName,DisplayName,EntitySetName,PrimaryIdAttribute,PrimaryNameAttribute,IsCustomEntity",e?"$filter=IsCustomEntity eq true":""].filter(Boolean).join("&");return this.requestWithRetry(()=>this.http.get(`EntityDefinitions?${s}`).then(r=>r.data.value))}async getTableMetadata(e,t=!0){let n=t?"$expand=Attributes":"",s=`EntityDefinitions(LogicalName='${l(e)}')${n?"?"+n:""}`;return this.requestWithRetry(()=>this.http.get(s).then(r=>r.data))}async fetchAllPagesOData(e){let t=[],n=e;for(;n;){let s=await this.requestWithRetry(()=>this.http.get(n).then(r=>r.data));t.push(...s.value??[]),n=s["@odata.nextLink"]}return t}async getRelationships(e){let t=l(e),[n,s,r]=await Promise.all([this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${t}')/OneToManyRelationships`),this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${t}')/ManyToOneRelationships`),this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${t}')/ManyToManyRelationships`)]);return[...n,...s,...r]}async query(e,t={}){let n=u=>encodeURIComponent(u).replace(/%28/g,"(").replace(/%29/g,")").replace(/%2C/g,",").replace(/%27/g,"'").replace(/%40/g,"@"),s=[];t.select?.length&&s.push(`$select=${t.select.join(",")}`),t.filter&&s.push(`$filter=${n(t.filter)}`),t.orderby&&s.push(`$orderby=${n(t.orderby)}`),t.top&&s.push(`$top=${t.top}`),t.expand&&s.push(`$expand=${t.expand}`),t.count&&s.push("$count=true"),t.apply&&s.push(`$apply=${n(t.apply)}`);let r=`${e}${s.length?"?"+s.join("&"):""}`,i=t.formattedValues?{headers:{Prefer:'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'}}:void 0;return this.requestWithRetry(()=>this.http.get(r,i).then(u=>u.data))}async executeFetchXml(e,t,n){let s=encodeURIComponent(t),r=n?{headers:{Prefer:'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'}}:void 0;return this.requestWithRetry(()=>this.http.get(`${e}?fetchXml=${s}`,r).then(i=>i.data))}async getRecord(e,t,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 u=await this.http.get(`${e}(${t})${i}`,{headers:{Prefer:'odata.include-annotations="*"'}}),c=u.headers["odata-etag"]??u.data["@odata.etag"]??null;return{record:u.data,etag:c}})}async createRecord(e,t){return this.requestWithRetry(async()=>{let n=await this.http.post(e,t,{headers:{Prefer:"return=representation"}}),r=n.headers["odata-entityid"]?.match(/\(([^)]+)\)/)?.[1];if(r)return r;let i=n.data,u=i["@odata.id"]?.match(/\(([^)]+)\)/)?.[1];if(u)return u;let c=v[e]??e.replace(/s$/,"")+"id",a=i[c];return a||(n.headers.location?.match(/\(([^)]+)\)/)?.[1]??"")})}async updateRecord(e,t,n,s){await this.requestWithRetry(()=>this.http.patch(`${e}(${t})`,n,{headers:{"If-Match":s??"*"}}))}async deleteRecord(e,t){await this.requestWithRetry(()=>this.http.delete(`${e}(${t})`))}async upsertRecord(e,t,n,s,r="upsert",i){return this.requestWithRetry(async()=>{let u=i?`${e}(${i})`:`${e}(${l(t)}='${l(n)}')`,c={Prefer:"return=representation"};r==="createOnly"&&(c["If-None-Match"]="*"),r==="updateOnly"&&(c["If-Match"]="*");try{let a=await this.http.put(u,s,{headers:c}),o=a.status===201?"created":"updated",p=a.headers["odata-entityid"]?.match(/\(([^)]+)\)/)?.[1],m=a.data,g=v[e]??e.replace(/s$/,"")+"id",f=p??m?.[g]??n;return{operation:o,id:f}}catch(a){if(a instanceof y&&a.status===412){if(r==="createOnly")throw new Error("Record already exists");if(r==="updateOnly")throw new Error("Record not found")}throw a}})}async associate(e,t,n,s,r){let i=`${this.authProvider.environmentUrl}/api/data/v${P}/${s}(${r})`;await this.requestWithRetry(()=>this.http.post(`${e}(${t})/${n}/$ref`,{"@odata.id":i}))}async disassociate(e,t,n,s,r){let i=s?`?$id=${this.authProvider.environmentUrl}/api/data/v${P}/${r??e}(${s})`:"";await this.requestWithRetry(()=>this.http.delete(`${e}(${t})/${n}/$ref${i}`))}};var w=class extends ${async executeAction(e,t={}){return this.requestWithRetry(()=>this.http.post(e,t).then(n=>n.data))}async executeFunction(e,t={}){let n=[],s=[],r=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;Object.entries(t).forEach(([c,a])=>{if(typeof a=="object"&&a!==null){let o=`@${c}`;s.push(`${l(c)}=${o}`),n.push(`${o}=${encodeURIComponent(JSON.stringify(a))}`)}else typeof a=="string"&&r.test(a)?s.push(`${l(c)}=${a}`):typeof a=="string"?s.push(`${l(c)}='${l(a)}'`):s.push(`${l(c)}=${String(a)}`)});let i=s.length?`${e}(${s.join(",")})`:`${e}()`,u=n.length?`${i}?${n.join("&")}`:i;return this.requestWithRetry(()=>this.http.get(u).then(c=>c.data))}async search(e){let n=`${this.http.baseURL.replace(/\/api\/data\/v[\d.]+\/?$/,"")}/api/search/v2.0/query`;return this.requestWithRetry(()=>this.http.post(n,e).then(s=>s.data))}async executeBoundAction(e,t,n,s={}){return this.requestWithRetry(()=>this.http.post(`${e}(${t})/Microsoft.Dynamics.CRM.${n}`,s).then(r=>r.data))}};var b=class extends w{async listDependencies(e,t){return this.requestWithRetry(()=>this.http.get(`RetrieveDependenciesForDelete(ComponentType=${e},ObjectId=${t})`).then(n=>n.data.value))}async listTableDependencies(e,t){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(e)}' 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}}),u=t?.length?i.filter(o=>t.includes(o.componentType)):i,a=t?.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:e,dependencies:u,count:u.length,warning:a}}async listGlobalOptionSets(){return this.requestWithRetry(()=>this.http.get("GlobalOptionSetDefinitions").then(e=>e.data.value))}async getOptionSet(e){return this.requestWithRetry(()=>this.http.get(`GlobalOptionSetDefinitions(Name='${l(e)}')`).then(t=>t.data))}async getAttributeOptionSet(e,t){let n=["PicklistAttributeMetadata","MultiSelectPicklistAttributeMetadata","StatusAttributeMetadata","StateAttributeMetadata"];for(let s of n)try{let r=`EntityDefinitions(LogicalName='${l(e)}')/Attributes(LogicalName='${l(t)}')/Microsoft.Dynamics.CRM.${s}?$select=LogicalName,DisplayName&$expand=OptionSet`,a=((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:e,attributeLogicalName:t,attributeType:s.replace("AttributeMetadata",""),options:a}}catch{continue}throw new Error(`Attribute '${t}' on entity '${e}' is not a Picklist, MultiSelectPicklist, Status, or State attribute, or does not exist.`)}async getEntityKeys(e){return this.requestWithRetry(async()=>(await this.http.get(`EntityDefinitions(LogicalName='${l(e)}')/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(e,t){await this.requestWithRetry(()=>this.http.patch(`EntityDefinitions(LogicalName='${l(e)}')`,t,{headers:{"MSCRM.MergeLabels":"true"}}))}async createEntityDefinition(e){return this.requestWithRetry(async()=>(await this.http.post("EntityDefinitions",e)).headers["odata-entityid"]?.match(/\(([^)]+)\)$/)?.[1]??"")}async createAttribute(e,t){return this.requestWithRetry(async()=>(await this.http.post(`EntityDefinitions(LogicalName='${l(e)}')/Attributes`,t,{headers:{Prefer:"return=representation"}})).data.MetadataId??"")}async updateAttribute(e,t,n){await this.requestWithRetry(()=>this.http.put(`EntityDefinitions(LogicalName='${l(e)}')/Attributes(LogicalName='${l(t)}')`,n,{headers:{"MSCRM.MergeLabels":"true"}}))}async deleteAttribute(e,t){await this.requestWithRetry(()=>this.http.delete(`EntityDefinitions(LogicalName='${l(e)}')/Attributes(LogicalName='${l(t)}')`))}async createRelationship(e){return this.requestWithRetry(async()=>(await this.http.post("RelationshipDefinitions",e)).headers["odata-entityid"]?.match(/\(([^)]+)\)$/)?.[1]??"")}};var T=class extends b{async batchExecute(e,t=!1){let n=`batch_${Date.now()}`,s="";if(t){let i=`changeset_${Date.now()+1}`,u=e.filter(a=>a.method==="GET"),c=e.filter(a=>a.method!=="GET");for(let a of u)s+=`--${n}\r
|
|
6
|
+
`,s+=`Content-Type: application/http\r
|
|
7
|
+
`,s+=`Content-Transfer-Encoding: binary\r
|
|
8
|
+
\r
|
|
9
|
+
`,s+=`${a.method} ${this.http.baseURL}${a.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 a=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??a++}\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 e.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 u=(r.headers["content-type"]??"").match(/boundary=(?:"([^"]+)"|([^;"\s]+))/),c=u?.[1]??u?.[2];return c?E(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 A={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 x=class extends T{async executeBoundFunction(e,t,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 u=i.join(","),c=`${e}(${t})/Microsoft.Dynamics.CRM.${n}(${u})`,a=r.length?`${c}?${r.join("&")}`:c;return this.requestWithRetry(()=>this.http.get(a).then(o=>o.data))}async executeUnboundFunction(e,t={}){let n=[],s=[];Object.entries(t).forEach(([c,a])=>{if(typeof a=="object"&&a!==null){let o=`@${c}`;s.push(`${l(c)}=${o}`),n.push(`${o}=${encodeURIComponent(JSON.stringify(a))}`)}else typeof a=="string"?s.push(`${l(c)}='${l(a)}'`):s.push(`${l(c)}=${String(a)}`)});let r=s.join(","),i=`${e}(${r})`,u=n.length?`${i}?${n.join("&")}`:i;return this.requestWithRetry(()=>this.http.get(u).then(c=>c.data))}async queryWithPaging(e,t={}){let n=Math.min(t.maxTotal??5e3,5e4),s=[],r=0,i={};t.select!==void 0&&(i.select=t.select),t.filter!==void 0&&(i.filter=t.filter),t.orderby!==void 0&&(i.orderby=t.orderby),t.expand!==void 0&&(i.expand=t.expand);let u=await this.query(e,i);for(s.push(...u.value),r++;u["@odata.nextLink"]&&s.length<n;){let a=u["@odata.nextLink"];u=await this.requestWithRetry(()=>this.http.get(a).then(o=>o.data)),s.push(...u.value),r++}let c=s.slice(0,n);return{records:c,totalRetrieved:c.length,pageCount:r}}async getChangedRecords(e,t,n){let s,r={};if(t===null){let p=n?.length?`?$select=${n.join(",")}`:"";s=`${e}${p}`,r.Prefer="odata.track-changes"}else{let p=n?.length?`&$select=${n.join(",")}`:"";s=`${e}?$deltatoken=${t}${p}`}let i=await this.requestWithRetry(()=>this.http.get(s,{headers:r}).then(p=>p.data)),u=i.value??[],c=[],a=[];for(let p of u)if("@removed"in p){let m=String(p["@id"]??""),g=m.match(/\(([^)]+)\)$/);a.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:a,nextDeltaToken:d}}async getSolutionComponents(e,t,n=200){return this.requestWithRetry(async()=>{let r=(await this.http.get(`solutions?$filter=uniquename eq '${l(e)}'&$select=solutionid,uniquename,friendlyname,version&$top=1`)).data.value;if(!r.length)throw new Error(`Solution '${e}' not found`);let i=r[0],u=i.solutionid,c=`_solutionid_value eq ${u}`;t!==void 0&&(c+=` and componenttype eq ${t}`);let o=(await this.http.get(`solutioncomponents?$filter=${c}&$select=componenttype,objectid&$top=${n}&$orderby=componenttype`)).data.value.map(d=>({componentType:d.componenttype,componentTypeName:A[d.componenttype]??`Type${d.componenttype}`,objectId:d.objectid}));return{solutionName:i.uniquename,solutionId:u,friendlyName:i.friendlyname,version:i.version,components:o,count:o.length}})}async publishCustomizations(e){try{return await this.requestWithRetry(async()=>{if(!(e&&((e.entities?.length??0)>0||(e.webResources?.length??0)>0||(e.optionSets?.length??0)>0)))return await this.http.post("PublishAllXml",{},{timeoutMs:12e4}),{published:!0,message:"All customizations published successfully"};let n="<importexportxml>";return e.entities?.length&&(n+="<entities>"+e.entities.map(s=>`<entity>${k(s)}</entity>`).join("")+"</entities>"),e.webResources?.length&&(n+="<webresources>"+e.webResources.map(s=>`<webresource>${k(s)}</webresource>`).join("")+"</webresources>"),e.optionSets?.length&&(n+="<optionsets>"+e.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"}})}catch(t){if((t instanceof Error?t.message:String(t)).includes("[TRANSIENT_LOCK_EXHAUSTED]")&&(e?.entities?.length??0)>0)return{published:!0,message:"Publish lock exhausted but a concurrent PublishAllXml has already covered these entities. Changes are live. (covered by concurrent publish)"};throw t}}async listDataverseWorkflows(e){return this.requestWithRetry(async()=>{let t=[];e.category!==void 0&&t.push(`category eq ${e.category}`),e.nameContains&&t.push(`contains(name,'${l(e.nameContains)}')`);let n=`workflows?$select=workflowid,name,description,category,statecode,statuscode,type,modifiedon&$orderby=name asc&$top=${e.top??50}`;return t.length>0&&(n+=`&$filter=${t.join(" and ")}`),(await this.http.get(n)).data.value??[]})}async getDataverseWorkflow(e){return this.requestWithRetry(()=>this.http.get(`workflows(${e})?$select=workflowid,name,description,category,statecode,statuscode,type,modifiedon`).then(t=>t.data))}async executeFetchXmlAllPages(e,t){let n=[],s=1,r;do{let i=t;s>1&&(i=i.replace(/<fetch\b([^>]*)>/i,(o,d)=>{let p=d.replace(/\s+page="[^"]*"/g,"").replace(/\s+paging-cookie="[^"]*"/g,""),m=r?` paging-cookie="${r.replace(/"/g,""").replace(/'/g,"'")}"`:"";return`<fetch${p} page="${s}"${m}>`}));let u=await this.executeFetchXml(e,i),c=u.value??[];n.push(...c);let a=u["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"];if(!a||c.length===0)break;try{let d=decodeURIComponent(decodeURIComponent(String(a))).match(/pagingcookie="([^"]+)"/);r=d?d[1]:void 0}catch{r=String(a)}if(!r||(s++,s>100))break}while(!0);return n}};export{l as a,x as b};
|
|
@@ -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 u=s?.timeoutMs??this.timeoutMs,c=new AbortController,a=setTimeout(()=>c.abort(),u);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(a)}}};function E(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 v(h,t){let e=[],n=h.split(`--${t}`);for(let s of n){let r=s.trim();if(!r||r==="--")continue;let i=E(s);if(i===-1)continue;let u=s.slice(i),c=E(u);if(c===-1)continue;let o=(u.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=u.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={opportunities:"opportunityid",territories:"territoryid",categories:"categoryid",activityparties:"activitypartyid",activitymimeattachments:"activitymimeattachmentid",queues:"queueid",queueitems:"queueitemid"};function x(h){if(h instanceof y){let t=h.data?.error;return t?.code==="0x80060888"?new Error(`Dataverse error 0x80060888: The Web API endpoint does not exist in this organization. This means the action/function name is wrong OR the entity set name is incorrect \u2014 NOT necessarily a permissions error. Original message: ${t.message??""}. Tip: verify action names via $metadata or use dataverse_resolve_entity_name for entity names.`):t?new Error(`Dataverse error ${t.code??""}: ${t.message??"Unknown error"}`):h.code==="ECONNABORTED"?new Error("Request timed out. Check your Dataverse environment URL."):h}return h instanceof Error?h:new Error(String(h))}var k="9.2",$=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${k}/`,timeout:n,headers:{"OData-MaxVersion":"4.0","OData-Version":"4.0",Accept:"application/json","Content-Type":"application/json; charset=utf-8"},tokenProvider:()=>t.getToken()})}get serviceRootUrl(){return this.http.baseURL}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(u=>setTimeout(u,i)),this.requestWithRetry(t,e+1)}if(n.status===400){let r=n.data?.error?.code;if(r==="0x80071151"&&e<5){let c=Math.pow(2,e)*5e3;return await new Promise(a=>setTimeout(a,c)),this.requestWithRetry(t,e+1)}if(r==="0x80071151"&&e>=5)throw new Error(`[TRANSIENT_LOCK_EXHAUSTED] Dataverse customization lock (0x80071151) still held after ${e} retries (~155s total). A publish operation is running server-side. WAIT at least 60 seconds, then retry the operation ONCE manually. Do NOT loop-retry. Inform the user that Dataverse is processing a previous schema change.`);let i=n.data?.error?.message?.toLowerCase()??"",u=r==="0x80044181"||i.includes("publish")&&(i.includes("already")||i.includes("in progress")||i.includes("another user"));if(u&&e<4)return await new Promise(c=>setTimeout(c,3e4)),this.requestWithRetry(t,e+1);if(u&&e>=4)throw new Error(`[TRANSIENT_LOCK_EXHAUSTED] Dataverse publish lock still held after ${e} retries (~120s total). A PublishAllXml operation is in progress server-side. WAIT at least 60 seconds before retrying ONCE. Do NOT call dataverse_publish_customizations again immediately. Inform the user that Dataverse is serialising a background publish job.`)}}throw x(n)}}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=u=>encodeURIComponent(u).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(u=>u.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 u=await this.http.get(`${t}(${e})${i}`,{headers:{Prefer:'odata.include-annotations="*"'}}),c=u.headers["odata-etag"]??u.data["@odata.etag"]??null;return{record:u.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,u=i["@odata.id"]?.match(/\(([^)]+)\)/)?.[1];if(u)return u;let c=P[t]??t.replace(/s$/,"")+"id",a=i[c];return a||(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 u=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 a=await this.http.put(u,s,{headers:c}),o=a.status===201?"created":"updated",p=a.headers["odata-entityid"]?.match(/\(([^)]+)\)/)?.[1],m=a.data,g=P[t]??t.replace(/s$/,"")+"id",f=p??m?.[g]??n;return{operation:o,id:f}}catch(a){if(a instanceof y&&a.status===412){if(r==="createOnly")throw new Error("Record already exists");if(r==="updateOnly")throw new Error("Record not found")}throw a}})}async associate(t,e,n,s,r){let i=`${this.authProvider.environmentUrl}/api/data/v${k}/${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${k}/${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=[],r=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;Object.entries(e).forEach(([c,a])=>{if(typeof a=="object"&&a!==null){let o=`@${c}`;s.push(`${l(c)}=${o}`),n.push(`${o}=${encodeURIComponent(JSON.stringify(a))}`)}else typeof a=="string"&&r.test(a)?s.push(`${l(c)}=${a}`):typeof a=="string"?s.push(`${l(c)}='${l(a)}'`):s.push(`${l(c)}=${String(a)}`)});let i=s.length?`${t}(${s.join(",")})`:`${t}()`,u=n.length?`${i}?${n.join("&")}`:i;return this.requestWithRetry(()=>this.http.get(u).then(c=>c.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}}),u=e?.length?i.filter(o=>e.includes(o.componentType)):i,a=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:u,count:u.length,warning:a}}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`,a=((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:a}}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 createEntityDefinition(t){return this.requestWithRetry(async()=>(await this.http.post("EntityDefinitions",t)).headers["odata-entityid"]?.match(/\(([^)]+)\)$/)?.[1]??"")}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}`,u=t.filter(a=>a.method==="GET"),c=t.filter(a=>a.method!=="GET");for(let a of u)s+=`--${n}\r
|
|
6
|
+
`,s+=`Content-Type: application/http\r
|
|
7
|
+
`,s+=`Content-Transfer-Encoding: binary\r
|
|
8
|
+
\r
|
|
9
|
+
`,s+=`${a.method} ${this.http.baseURL}${a.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 a=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??a++}\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 u=(r.headers["content-type"]??"").match(/boundary=(?:"([^"]+)"|([^;"\s]+))/),c=u?.[1]??u?.[2];return c?v(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 q={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 O(h){return h.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}var A=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 u=i.join(","),c=`${t}(${e})/Microsoft.Dynamics.CRM.${n}(${u})`,a=r.length?`${c}?${r.join("&")}`:c;return this.requestWithRetry(()=>this.http.get(a).then(o=>o.data))}async executeUnboundFunction(t,e={}){let n=[],s=[];Object.entries(e).forEach(([c,a])=>{if(typeof a=="object"&&a!==null){let o=`@${c}`;s.push(`${l(c)}=${o}`),n.push(`${o}=${encodeURIComponent(JSON.stringify(a))}`)}else typeof a=="string"?s.push(`${l(c)}='${l(a)}'`):s.push(`${l(c)}=${String(a)}`)});let r=s.join(","),i=`${t}(${r})`,u=n.length?`${i}?${n.join("&")}`:i;return this.requestWithRetry(()=>this.http.get(u).then(c=>c.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 u=await this.query(t,i);for(s.push(...u.value),r++;u["@odata.nextLink"]&&s.length<n;){let a=u["@odata.nextLink"];u=await this.requestWithRetry(()=>this.http.get(a).then(o=>o.data)),s.push(...u.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)),u=i.value??[],c=[],a=[];for(let p of u)if("@removed"in p){let m=String(p["@id"]??""),g=m.match(/\(([^)]+)\)$/);a.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:a,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],u=i.solutionid,c=`_solutionid_value eq ${u}`;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:q[d.componenttype]??`Type${d.componenttype}`,objectId:d.objectid}));return{solutionName:i.uniquename,solutionId:u,friendlyName:i.friendlyname,version:i.version,components:o,count:o.length}})}async publishCustomizations(t){try{return await 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>${O(s)}</entity>`).join("")+"</entities>"),t.webResources?.length&&(n+="<webresources>"+t.webResources.map(s=>`<webresource>${O(s)}</webresource>`).join("")+"</webresources>"),t.optionSets?.length&&(n+="<optionsets>"+t.optionSets.map(s=>`<optionset>${O(s)}</optionset>`).join("")+"</optionsets>"),n+="</importexportxml>",await this.http.post("PublishXml",{ParameterXml:n},{timeoutMs:12e4}),{published:!0,message:"Selected customizations published successfully"}})}catch(e){if((e instanceof Error?e.message:String(e)).includes("[TRANSIENT_LOCK_EXHAUSTED]")&&(t?.entities?.length??0)>0)return{published:!0,message:"Publish lock exhausted but a concurrent PublishAllXml has already covered these entities. Changes are live. (covered by concurrent publish)"};throw e}}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))}async executeFetchXmlAllPages(t,e){let n=[],s=1,r;do{let i=e;s>1&&(i=i.replace(/<fetch\b([^>]*)>/i,(o,d)=>{let p=d.replace(/\s+page="[^"]*"/g,"").replace(/\s+paging-cookie="[^"]*"/g,""),m=r?` paging-cookie="${r.replace(/"/g,""").replace(/'/g,"'")}"`:"";return`<fetch${p} page="${s}"${m}>`}));let u=await this.executeFetchXml(t,i),c=u.value??[];n.push(...c);let a=u["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"];if(!a||c.length===0)break;try{let d=decodeURIComponent(decodeURIComponent(String(a))).match(/pagingcookie="([^"]+)"/);r=d?d[1]:void 0}catch{r=String(a)}if(!r||(s++,s>100))break}while(!0);return n}};export{l as a,A as b};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as a}from"./chunk-6KQIRQNP.js";export{a as DataverseAdvancedClient};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{b as a}from"./chunk-IA4YDPXM.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 n=e=>process.stdout.write(e+`
|
|
3
|
-
`),s=e=>n(` \u2705 ${e}`),o=e=>n(` \u274C ${e}`);n(""),n("\u{1F3E5} mcp-dataverse doctor"),n("\u2500".repeat(50)),n("");let i=!0;n("\u{1F4CB} Environment");let c=process.version;parseInt(c.slice(1).split(".")[0],10)>=20?s(`Node.js ${c}`):(o(`Node.js ${c} \u2014 requires v20+`),i=!1),n(""),n("\u2699\uFE0F Configuration");try{let{loadConfig:e}=await import("./config.loader-YZJ7QTCV.js"),t=e();s(`Environment URL: ${t.environmentUrl}`),s(`Auth method: ${t.authMethod??"device-code"}`),s(`Timeout: ${t.requestTimeoutMs}ms, Retries: ${t.maxRetries}`)}catch(e){let t=e instanceof Error?e.message:String(e);o(`Configuration error: ${t}`),i=!1,n(""),n(i?"\u2705 All checks passed!":"\u274C Some checks failed."),process.exit(i?0:1)}n(""),n("\u{1F511} Authentication");try{let{loadConfig:e}=await import("./config.loader-YZJ7QTCV.js"),t=e(),r=t.authMethod??"device-code";s(`Auth method: ${r}`),r==="client-credentials"?(s(`Tenant ID : ${t.tenantId}`),s(`Client ID : ${t.clientId}`),t.clientSecret&&!process.env.AZURE_CLIENT_SECRET&&!process.env.clientSecret?n(" \u26A0\uFE0F clientSecret is stored in config file \u2014 prefer AZURE_CLIENT_SECRET env var for production"):s("Client Secret: supplied via env var")):r==="managed-identity"&&(t.managedIdentityClientId?s(`Identity type: user-assigned (clientId: ${t.managedIdentityClientId})`):s("Identity type: system-assigned"));let{createAuthProvider:d}=await import("./auth-provider.factory-IVYKBXVY.js"),l=await d(t).getToken();l?(s("Token acquired successfully"),l.split(".").length===3&&s("Valid JWT token format")):(o("No token returned"),i=!1)}catch(e){let t=e instanceof Error?e.message:String(e);o(`Auth failed: ${t}`),i=!1}n(""),n("\u{1F310} Dataverse API");try{let{loadConfig:e}=await import("./config.loader-YZJ7QTCV.js"),t=e(),{createAuthProvider:r}=await import("./auth-provider.factory-IVYKBXVY.js"),{DataverseAdvancedClient:d}=await import("./dataverse-client-advanced-
|
|
3
|
+
`),s=e=>n(` \u2705 ${e}`),o=e=>n(` \u274C ${e}`);n(""),n("\u{1F3E5} mcp-dataverse doctor"),n("\u2500".repeat(50)),n("");let i=!0;n("\u{1F4CB} Environment");let c=process.version;parseInt(c.slice(1).split(".")[0],10)>=20?s(`Node.js ${c}`):(o(`Node.js ${c} \u2014 requires v20+`),i=!1),n(""),n("\u2699\uFE0F Configuration");try{let{loadConfig:e}=await import("./config.loader-YZJ7QTCV.js"),t=e();s(`Environment URL: ${t.environmentUrl}`),s(`Auth method: ${t.authMethod??"device-code"}`),s(`Timeout: ${t.requestTimeoutMs}ms, Retries: ${t.maxRetries}`)}catch(e){let t=e instanceof Error?e.message:String(e);o(`Configuration error: ${t}`),i=!1,n(""),n(i?"\u2705 All checks passed!":"\u274C Some checks failed."),process.exit(i?0:1)}n(""),n("\u{1F511} Authentication");try{let{loadConfig:e}=await import("./config.loader-YZJ7QTCV.js"),t=e(),r=t.authMethod??"device-code";s(`Auth method: ${r}`),r==="client-credentials"?(s(`Tenant ID : ${t.tenantId}`),s(`Client ID : ${t.clientId}`),t.clientSecret&&!process.env.AZURE_CLIENT_SECRET&&!process.env.clientSecret?n(" \u26A0\uFE0F clientSecret is stored in config file \u2014 prefer AZURE_CLIENT_SECRET env var for production"):s("Client Secret: supplied via env var")):r==="managed-identity"&&(t.managedIdentityClientId?s(`Identity type: user-assigned (clientId: ${t.managedIdentityClientId})`):s("Identity type: system-assigned"));let{createAuthProvider:d}=await import("./auth-provider.factory-IVYKBXVY.js"),l=await d(t).getToken();l?(s("Token acquired successfully"),l.split(".").length===3&&s("Valid JWT token format")):(o("No token returned"),i=!1)}catch(e){let t=e instanceof Error?e.message:String(e);o(`Auth failed: ${t}`),i=!1}n(""),n("\u{1F310} Dataverse API");try{let{loadConfig:e}=await import("./config.loader-YZJ7QTCV.js"),t=e(),{createAuthProvider:r}=await import("./auth-provider.factory-IVYKBXVY.js"),{DataverseAdvancedClient:d}=await import("./dataverse-client-advanced-CON7DMNS.js"),m=r(t),a=await new d(m,t.maxRetries,t.requestTimeoutMs).whoAmI();s(`Organization: ${a.OrganizationName||"N/A"}`),s(`User ID: ${a.UserId||"N/A"}`),s(`Business Unit: ${a.BusinessUnitId||"N/A"}`),s(`Environment: ${a.EnvironmentUrl||t.environmentUrl}`)}catch(e){let t=e instanceof Error?e.message:String(e);o(`API call failed: ${t}`),i=!1}n(""),n("\u2500".repeat(50)),n(i?"\u2705 All checks passed! Your mcp-dataverse setup is healthy.":"\u274C Some checks failed. Review the errors above."),n(""),process.exit(i?0:1)}export{f as runDoctor};
|
package/dist/http-server.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{createServer as N}from"http";import{timingSafeEqual as y,createHmac as
|
|
2
|
-
`)}else process.stderr.write(
|
|
1
|
+
import{createServer as N}from"http";import{timingSafeEqual as y,createHmac as I,randomUUID as C}from"crypto";import{StreamableHTTPServerTransport as _}from"@modelcontextprotocol/sdk/server/streamableHttp.js";function A(p,a){try{let d=Buffer.from("mcp-token-verify-constant"),l=I("sha256",d).update(p).digest(),u=I("sha256",d).update(a).digest();return y(l,u)}catch{return!1}}async function U(p,a,d,l){let u=process.env.MCP_HTTP_JSON_RESPONSE!=="false",T=process.env.MCP_HTTP_SECRET??process.env.BEARER_TOKEN,S=process.env.MCP_HTTP_CORS_ORIGIN??"*",s=process.env.ENTRA_TENANT_ID&&process.env.ENTRA_CLIENT_ID?{tenantId:process.env.ENTRA_TENANT_ID,clientId:process.env.ENTRA_CLIENT_ID,audience:process.env.ENTRA_AUDIENCE??`api://${process.env.ENTRA_CLIENT_ID}`,...process.env.ENTRA_REQUIRED_SCOPE!==void 0?{requiredScope:process.env.ENTRA_REQUIRED_SCOPE}:{}}:null,h=null;if(s){let{createEntraJwtValidator:n}=await import("./entra-jwt-validator-DABIEBOV.js");h=n(s),process.stderr.write(`[http-server] Auth mode: entra-jwt (tenant=${s.tenantId}, audience=${s.audience})
|
|
2
|
+
`)}else process.stderr.write(T?`[http-server] Auth mode: static bearer token
|
|
3
3
|
`:`[http-server] WARNING: No auth configured \u2014 all /mcp requests accepted
|
|
4
|
-
`);let w=(process.env.MCP_PUBLIC_URL??`http://localhost:${a}`).replace(/\/$/,""),o=new Map,m=N(async(n,e)=>{if(e.setHeader("Access-Control-Allow-Origin",
|
|
5
|
-
`),e.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":v}),e.end(JSON.stringify({error:"Unauthorized"}));return}}else if(
|
|
4
|
+
`);let w=(process.env.MCP_PUBLIC_URL??`http://localhost:${a}`).replace(/\/$/,""),o=new Map,m=N(async(n,e)=>{if(e.setHeader("Access-Control-Allow-Origin",S),e.setHeader("Access-Control-Allow-Methods","GET, POST, DELETE, OPTIONS"),e.setHeader("Access-Control-Allow-Headers","Content-Type, mcp-session-id, Authorization"),n.method==="OPTIONS"){e.writeHead(204),e.end();return}let f=new URL(n.url??"/",`http://localhost:${a}`);if(f.pathname==="/health"&&n.method==="GET"){e.writeHead(200,{"Content-Type":"application/json"}),e.end(JSON.stringify({status:"ok",version:d,tools:l}));return}if(f.pathname==="/.well-known/oauth-protected-resource"&&n.method==="GET"){let c=s?.tenantId??"";e.writeHead(200,{"Content-Type":"application/json","Cache-Control":"max-age=3600"});let i=s?.clientId??"";e.end(JSON.stringify({resource:`${w}/mcp`,authorization_servers:c?[`https://login.microsoftonline.com/${c}/v2.0`]:[],bearer_methods_supported:["header"],scopes_supported:i?[`api://${i}/DataverseAccess`]:[],resource_documentation:"https://github.com/aileron-split/mcp-dataverse"}));return}if(f.pathname==="/mcp"){let c=n.headers.authorization??"",i=c.startsWith("Bearer ")?c.slice(7):"",g=`${w}/.well-known/oauth-protected-resource`,v=s?`Bearer resource_metadata="${g}", realm="MCP Dataverse"`:'Bearer realm="MCP Dataverse"';if(h){if(!i){e.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":v}),e.end(JSON.stringify({error:"Unauthorized"}));return}try{await h(i)}catch(r){let t=r instanceof Error?r.message:String(r);process.stderr.write(`[http-server] JWT rejected: ${t}
|
|
5
|
+
`),e.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":v}),e.end(JSON.stringify({error:"Unauthorized"}));return}}else if(T&&!A(i,T)){e.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":v}),e.end(JSON.stringify({error:"Unauthorized"}));return}try{let r=n.headers["mcp-session-id"];if(r){let t=o.get(r);if(!t){e.writeHead(404,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:"Session not found. Please reinitialize."}));return}await t.handleRequest(n,e)}else{if(n.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 _({sessionIdGenerator:()=>C(),enableJsonResponse:u});t.sessionId&&(o.set(t.sessionId,t),t.onclose=()=>{t.sessionId&&o.delete(t.sessionId)}),await p().connect(t),await t.handleRequest(n,e),t.sessionId&&(o.has(t.sessionId)||(o.set(t.sessionId,t),t.onclose=()=>{t.sessionId&&o.delete(t.sessionId)}))}}catch(r){process.stderr.write(`[http-server] Unhandled error: ${String(r)}
|
|
6
6
|
`),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(n=>{m.listen(a,"0.0.0.0",()=>{process.stderr.write(`MCP Dataverse HTTP server listening on http://0.0.0.0:${a}/mcp
|
|
7
|
-
`),n()})});let
|
|
8
|
-
`);for(let n of o.values())await n.close();m.close(),process.exit(0)};process.on("SIGINT",
|
|
7
|
+
`),n()})});let E=async()=>{process.stderr.write(`Shutting down HTTP server...
|
|
8
|
+
`);for(let n of o.values())await n.close();m.close(),process.exit(0)};process.on("SIGINT",E),process.on("SIGTERM",E),await new Promise(n=>{m.on("close",n)})}export{U as startHttpTransport};
|