mcp-dataverse 0.7.6 → 0.7.8

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/dist/server.js CHANGED
@@ -1,15 +1,147 @@
1
1
  #!/usr/bin/env node
2
- import{a as Ze}from"./chunk-OQ46VPYS.js";import{a as et}from"./chunk-E3JJTII3.js";import"./chunk-L2PQKWEU.js";import"./chunk-KJ3HM2VM.js";import{a as w,b as tt}from"./chunk-ZPKWBEXB.js";import{Server as Hi}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as $i}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as qi,ListToolsRequestSchema as ji,ListResourcesRequestSchema as Wi,ListResourceTemplatesRequestSchema as Fi,ReadResourceRequestSchema as Gi,ListPromptsRequestSchema as Bi,GetPromptRequestSchema as Vi}from"@modelcontextprotocol/sdk/types.js";import{RESOURCE_MIME_TYPE as ee}from"@modelcontextprotocol/ext-apps/server";import{readFileSync as zi,existsSync as U}from"fs";import{readFile as Re}from"node:fs/promises";import{join as Ki,dirname as Xi,resolve as x}from"path";import{fileURLToPath as Ji}from"url";function at(){let a=process.argv.slice(2),i="stdio",n=3e3;for(let e=0;e<a.length;e++){if(a[e]==="--transport"&&a[e+1]){let t=a[e+1];(t==="http"||t==="stdio")&&(i=t),e++}if(a[e]==="--port"&&a[e+1]){let t=parseInt(a[e+1],10);!isNaN(t)&&t>0&&t<65536&&(n=t),e++}}return{transport:i,port:n}}var Oe=class{map=new Map;register(i){for(let n of i.tools){if(this.map.has(n.name))throw new Error(`Duplicate tool name: ${n.name}`);this.map.set(n.name,{definition:n,handler:i.handler})}}getHandler(i){return this.map.get(i)?.handler}getAllDefinitions(){return Array.from(this.map.values()).map(i=>i.definition)}has(i){return this.map.has(i)}get size(){return this.map.size}};function nt(a){let i=new Oe;for(let n of a)i.register(n);return i}var ve=class{server;token;constructor(i,n){this.server=i??null,this.token=n??null}async report(i,n){if(!(!this.server||this.token==null))try{await this.server.notification({method:"notifications/progress",params:{progressToken:this.token,progress:i,total:n}})}catch{}}},go=new ve;function P(a){let i=a.errorCategory!==void 0,n=a.data!==null&&a.data!==void 0?Ue(a.data):a.data,e=n===a.data?a:{...a,data:n},t=i?{...e,isError:!0,content:[{type:"text",text:e.summary}]}:e,r=JSON.stringify(t);if(Buffer.byteLength(r,"utf8")>9e5){let s={summary:a.summary,data:null,warnings:[...a.warnings??[],`Result exceeded the ${Math.round(900)} KB safety limit and was dropped to avoid hitting the Claude Desktop 1 MB tool-result cap.`],suggestions:[...a.suggestions??[],"Re-run with $select to return only the columns you need.","Reduce the page size: pass $top (or `top`) \u2014 typical safe value is 25\u201350 rows.","For large result sets use dataverse_retrieve_multiple_with_paging (cursor-based) instead of a single big query.","If you need every row, page through results in a loop on the client side."],errorCategory:"ENV_LIMITATION"};return r=JSON.stringify({...s,isError:!0,content:[{type:"text",text:s.summary}]}),{content:[{type:"text",text:r}],isError:!0}}let o={content:[{type:"text",text:r}]};return i&&(o.isError=!0),o}function p(a,i,n,e){return P({summary:a,data:i,suggestions:n,warnings:e})}function re(a,i,n){return P({summary:`${i.length} ${a} found.`,data:i,suggestions:n})}function ke(a,i="UNKNOWN",n){return P({summary:a,data:null,errorCategory:i,suggestions:n})}function ge(a){let i={feature_disabled:"ENV_LIMITATION",permission_required:"PERMISSIONS",schema_missing:"SCHEMA_MISMATCH"};return P({summary:`Blocked: ${a.cannotProceedBecause}`,data:null,prerequisite:a,errorCategory:i[a.type]})}var rt=new Set(["@odata.editLink","@odata.id","@odata.type"]),it=["@Microsoft.Dynamics.CRM.lookuplogicalname","@Microsoft.Dynamics.CRM.associatednavigationproperty"];function nn(a,i){if(typeof a!="object"||a===null)return a;let n=new Set(i?.keep??["@odata.etag"]),e=a,t={},r=!1;for(let o of Object.keys(e)){if(n.has(o)){t[o]=e[o];continue}if(rt.has(o)){r=!0;continue}if(it.some(s=>o.endsWith(s))){r=!0;continue}t[o]=e[o]}return r?t:a}var rn=new Set(["@odata.context"]);function Ue(a){if(Array.isArray(a)){let r=!1,o=[];for(let s of a){let c=Ue(s);c!==s&&(r=!0),o.push(c)}return r?o:a}if(a===null||typeof a!="object"||Buffer.isBuffer(a)||a instanceof Date)return a;let i=Object.getPrototypeOf(a);if(i!==Object.prototype&&i!==null)return a;let n=a,e={},t=!1;for(let r of Object.keys(n)){if(r==="@odata.etag"){e[r]=n[r];continue}if(rt.has(r)||rn.has(r)){t=!0;continue}if(it.some(s=>r.endsWith(s))){t=!0;continue}let o=Ue(n[r]);o!==n[r]&&(t=!0),e[r]=o}return t?e:a}function ue(a,i){return a.map(n=>nn(n,i))}function be(a){if(typeof a!="object"||a===null)return a;let i=a,n={},e=!1;for(let t of Object.keys(i)){let r=i[t];if(r==null||r===""){e=!0;continue}n[t]=r}return e?n:a}function v(a){return a.map(i=>be(i))}function on(a){if(a==null||a==="")return!0;if(Array.isArray(a))return a.length===0;if(typeof a=="object"){let i=Object.getPrototypeOf(a);if(i===null||i===Object.prototype)return Object.keys(a).length===0}return!1}function Pe(a){if(Array.isArray(a))return a.map(e=>Pe(e));if(a===null||typeof a!="object")return a;let i=a;if("UserLocalizedLabel"in i||"LocalizedLabels"in i&&Array.isArray(i.LocalizedLabels)){let e=i.UserLocalizedLabel;if(e&&typeof e.Label=="string")return e.Label;let t=i.LocalizedLabels;return Array.isArray(t)&&t[0]?.Label?t[0].Label:null}let n={};for(let e of Object.keys(i)){let t=Pe(i[e]);on(t)||(n[e]=t)}return n}function Me(a){return Pe(a)}var Le="@OData.Community.Display.V1.FormattedValue";function pe(a){return a.map(i=>{if(typeof i!="object"||i===null)return i;let n=i,e={};for(let r of Object.keys(n))if(r.endsWith(Le)){let o=r.slice(0,-Le.length);e[o]=n[r]}if(Object.keys(e).length===0)return i;let t={};for(let r of Object.keys(n))r.endsWith(Le)||(Object.prototype.hasOwnProperty.call(e,r)?t[r]={value:n[r],label:e[r]}:t[r]=n[r]);return t})}var ot=[{name:"dataverse_whoami",description:"Returns the current authenticated user context from Dataverse WhoAmI: userId, businessUnitId, organizationId, organizationName, and environmentUrl. Use this to verify authentication is working, retrieve the current user context, or obtain IDs needed for subsequent operations. WHEN TO USE: Verifying authentication, getting current user/org context, or obtaining IDs for downstream operations. BEST PRACTICES: Call first to confirm connectivity before other tools. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function st(a,i,n){if(a==="dataverse_whoami"){let e=await n.whoAmI(),t={userId:e.UserId,businessUnitId:e.BusinessUnitId,organizationId:e.OrganizationId,organizationName:e.OrganizationName,environmentUrl:e.EnvironmentUrl};return p(`Authenticated as ${e.OrganizationName}`,t,["Use dataverse_list_tables to discover available tables","Use dataverse_get_table_metadata to inspect a table schema"])}throw new Error(`Unknown auth tool: ${a}`)}import{z as pt}from"zod";import{z as C}from"zod";var He=[{name:"dataverse_list_tables",description:"Lists all Dataverse tables. By default returns ONLY custom (non-system) tables. Set includeSystemTables=true to include all ~1700+ system tables.",inputSchema:{type:"object",properties:{includeSystemTables:{type:"boolean",description:"Include system tables. Default false = custom tables only."},verbose:{type:"boolean",description:"When true, returns full Dataverse metadata including all localized labels. Default false collapses DisplayName/Description objects to a single string to reduce token usage."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_table_metadata",description:"Returns full schema metadata for a Dataverse table: all attribute (column) logical names, display names, data types, required levels, and lookup target entities. Use this before writing queries or creating/updating records to confirm correct field names and types. Set includeAttributes=false if you only need table-level metadata without column details.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:'The logical name of the table (e.g., "account", "contact", "new_mytable")'},includeAttributes:{type:"boolean",description:"Include attribute (column) definitions. Default: true"},verbose:{type:"boolean",description:"When true, returns full Dataverse metadata including all localized labels. Default false collapses DisplayName/Description objects to a single string to reduce token usage."}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_relationships",description:"Returns all relationship definitions (1:N, N:1, N:N) for a Dataverse table, including relationship schema names, referenced/referencing entity names, and lookup attribute names. Use to determine the correct relationshipName for dataverse_associate/dataverse_disassociate, or to map lookup fields before building FetchXML joins. Use relationshipType to filter results.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:"The logical name of the table"},relationshipType:{type:"string",enum:["OneToMany","ManyToOne","ManyToMany","All"],description:"Filter by relationship type. Default: All"}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_global_option_sets",description:"Lists all global (shared) option sets defined in the Dataverse environment, returning their names and metadata IDs. Use this to discover available option sets before calling dataverse_get_option_set to retrieve their values. Prefer this over dataverse_get_table_metadata when you need to find option sets that are reused across multiple tables.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_option_set",description:"Returns all option labels and their integer values for a named global option set. Use this to look up the numeric code for a picklist value before filtering records (e.g., statecode or statuscode equivalents), or to populate dropdowns with correct option values.",inputSchema:{type:"object",properties:{name:{type:"string",description:"The name of the global option set"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_entity_key",description:"Returns all alternate key definitions for a Dataverse table. Useful before using dataverse_upsert to know which fields serve as alternate keys, their index status (Active/InProgress/Failed), and whether they are customizable.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:'Logical name of the table (e.g., "account", "contact")'}},required:["tableName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_attribute_option_set",description:"Returns all option labels and integer values for a table-specific attribute (Picklist, MultiSelectPicklist, Status, or State field). Use to look up the numeric codes for a column's choices before filtering or updating records.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Logical name of the table (e.g., "account", "contact")'},attributeLogicalName:{type:"string",description:'Logical name of the attribute (e.g., "statuscode", "industrycode", "new_tags")'}},required:["entityLogicalName","attributeLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],sn=C.object({logicalName:C.string().min(1),includeAttributes:C.boolean().optional().default(!0),verbose:C.boolean().optional().default(!1)}),ln=C.object({logicalName:C.string().min(1),relationshipType:C.enum(["OneToMany","ManyToOne","ManyToMany","All"]).optional()}),cn=C.object({includeSystemTables:C.boolean().default(!1).optional().describe("Include system tables. Default false = custom tables only."),verbose:C.boolean().optional().default(!1)}),dn=C.object({name:C.string().min(1)}),un=C.object({tableName:C.string().min(1).describe('Logical name of the table (e.g., "account", "contact")')}),lt=/^[a-z_][a-z0-9_]*$/,pn=C.object({entityLogicalName:C.string().min(1).regex(lt,"Invalid Dataverse logical name"),attributeLogicalName:C.string().min(1).regex(lt,"Invalid Dataverse logical name")});async function ct(a,i,n){switch(a){case"dataverse_list_tables":{let{includeSystemTables:e=!1,verbose:t=!1}=cn.parse(i??{}),r=!e,o=await n.listTables(r),s=Array.isArray(o)?o:[],c=t?s:Me(s);return re(`tables (${r?"custom only":"including system"})`,c,["Use dataverse_get_table_metadata to inspect a specific table's columns and types"])}case"dataverse_get_table_metadata":{let{logicalName:e,includeAttributes:t,verbose:r}=sn.parse(i),o=await n.getTableMetadata(e,t),s=Array.isArray(o?.Attributes)?o.Attributes.length:0,c=r?o:Me(o);return p(`Metadata for ${e}: ${s} attributes`,c,["Use dataverse_query to read records from this table","Use dataverse_get_relationships to see related tables"])}case"dataverse_get_relationships":{let{logicalName:e,relationshipType:t}=ln.parse(i),r=await n.getRelationships(e),o=e.toLowerCase(),s=r.filter(g=>g.RelationshipType==="OneToManyRelationship"&&g.ReferencedEntity?.toLowerCase()===o),c=r.filter(g=>g.RelationshipType==="OneToManyRelationship"&&g.ReferencingEntity?.toLowerCase()===o),l=r.filter(g=>g.RelationshipType==="ManyToManyRelationship"),d=!t||t==="All",u=[...d||t==="OneToMany"?s:[],...d||t==="ManyToOne"?c:[],...d||t==="ManyToMany"?l:[]];return p(`${u.length} relationships found for ${e}`,{tableName:e,oneToMany:d||t==="OneToMany"?s:void 0,manyToOne:d||t==="ManyToOne"?c:void 0,manyToMany:d||t==="ManyToMany"?l:void 0},["Use dataverse_associate or dataverse_disassociate to manage N:N relationships"])}case"dataverse_list_global_option_sets":{let e=await n.listGlobalOptionSets(),t=Array.isArray(e)?e:[];return re("global option sets",t,["Use dataverse_get_option_set with a name to see the values"])}case"dataverse_get_option_set":{let{name:e}=dn.parse(i),t=await n.getOptionSet(e),r=Array.isArray(t?.Options)?t.Options:[];return p(`Option set '${e}': ${r.length} options`,t,["Use these values in $filter expressions or when creating/updating records"])}case"dataverse_get_entity_key":{let{tableName:e}=un.parse(i),t=await n.getEntityKeys(e);return p(`${t.length} alternate keys for ${e}`,{tableName:e,keys:t,count:t.length},["Use dataverse_upsert with these keys for create-or-update operations"])}case"dataverse_get_attribute_option_set":{let{entityLogicalName:e,attributeLogicalName:t}=pn.parse(i),r=await n.getAttributeOptionSet(e,t),o=Array.isArray(r?.options)?r.options:[];return p(`Attribute '${t}' on '${e}': ${o.length} options`,r,["Use these integer values in $filter or when creating/updating records"])}default:throw new Error(`Unknown metadata-read tool: ${a}`)}}import{z as M}from"zod";var mn=new Set(["dataverse_delete","dataverse_delete_file","dataverse_update_entity","dataverse_delete_attribute"]);function $e(a){let i=[];return a.top!==void 0&&a.top>5e3&&i.push({code:"LARGE_RESULT_SET",message:`Large result set requested (top=${a.top}). This can generate very large responses that consume significant context tokens. Reduce $top to \u226450 for exploration, or add a $filter to narrow results before increasing $top.`,severity:"warning"}),(!a.select||a.select.length===0)&&i.push({code:"NO_SELECT",message:"No $select specified \u2014 all 50+ columns are being returned, many of which are null. Add select=['name','statecode','modifiedon'] (or whichever columns you need) to reduce token usage by 60-80% on typical Dataverse tables.",severity:"warning"}),a.filter||i.push({code:"NO_FILTER",message:'No $filter \u2014 returning all accessible records up to the $top limit. For targeted queries, add filter (e.g., "statecode eq 0") to reduce both token usage and API load.',severity:"info"}),i}function X(a){let i=[];return mn.has(a.toolName)&&i.push({code:"DESTRUCTIVE_OP",message:"Destructive operation \u2014 confirm with user before proceeding. This action cannot be undone.",severity:"warning"}),a.toolName==="dataverse_batch_execute"&&i.push({code:"BATCH_OP",message:"Batch operations may modify multiple records in a single transaction.",severity:"info"}),i}var dt=[{name:"dataverse_update_entity",description:"Updates configuration flags on an existing Dataverse entity definition \u2014 enables or disables Notes (HasNotes), Change Tracking, and Audit. Requires System Customizer or System Administrator privileges. WHEN TO USE: Enabling notes/attachments support, change tracking, or audit on a table. BEST PRACTICES: Leave autoPublish=false (default) when making multiple changes and call dataverse_publish_customizations once at the end. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the entity to update (e.g. 'account', 'new_mytable')"},hasNotes:{type:"boolean",description:"Enable or disable the Notes/Attachments feature for this entity"},changeTrackingEnabled:{type:"boolean",description:"Enable or disable change tracking (required for delta sync)"},isAuditEnabled:{type:"boolean",description:"Enable or disable auditing on this entity"},autoPublish:{type:"boolean",description:"Publish after update (default: false). BATCH RULE: NEVER set true if you are making multiple schema changes in the same session \u2014 causes Dataverse publish-lock contention (0x80044181). Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the very end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification to Dataverse entity metadata"}},required:["entityLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_table_icon",description:"Sets, changes, or removes the icon for a model-driven app table (entity) by assigning a web resource reference. PREREQUISITE: You MUST call dataverse_create_web_resource FIRST to upload the SVG/PNG \u2014 the name from that step is your iconVectorName here. Supports modern SVG vector icons (IconVectorName, preferred) and legacy PNG icons (IconLargeName, IconMediumName). To REMOVE an icon, pass an empty string '' for that property. Requires System Customizer or System Administrator privileges. WHEN TO USE: After upload \u2014 assigning, changing, or removing a table's icon in the model-driven app navigation. BEST PRACTICES: Use IconVectorName with an SVG web resource for sharp rendering at any size. WORKFLOW: table_icons \u2014 step 1: dataverse_create_web_resource \u2192 step 2: this tool \u2192 step 3: dataverse_publish_customizations.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the entity to update (e.g., 'account', 'new_mytable')"},iconVectorName:{type:"string",description:"SVG vector icon web resource name (e.g., 'new_icon.svg'). This must be the name returned by dataverse_create_web_resource. Pass an empty string '' to remove the icon."},iconLargeName:{type:"string",description:"Legacy 32x32 image web resource name. Must be uploaded first via dataverse_create_web_resource. Pass '' to remove."},iconMediumName:{type:"string",description:"Legacy 16x16 image web resource name. Must be uploaded first via dataverse_create_web_resource. Pass '' to remove."},autoPublish:{type:"boolean",description:"Publish after update (default: false). BATCH RULE: NEVER set true if making multiple schema changes \u2014 call dataverse_publish_customizations ONCE at the very end."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],gn=M.object({entityLogicalName:M.string().min(1).regex(/^[a-z_][a-z0-9_]*$/,"Invalid logical name"),confirm:M.literal(!0,{errorMap:()=>({message:"Set confirm: true to modify entity metadata"})}),hasNotes:M.boolean().optional(),changeTrackingEnabled:M.boolean().optional(),isAuditEnabled:M.boolean().optional(),autoPublish:M.boolean().optional().default(!1)}).refine(a=>a.hasNotes!==void 0||a.changeTrackingEnabled!==void 0||a.isAuditEnabled!==void 0,{message:"At least one of hasNotes, changeTrackingEnabled, or isAuditEnabled must be provided"}),fn=M.object({entityLogicalName:M.string().min(1).regex(/^[a-z_][a-z0-9_]*$/,"Invalid logical name"),confirm:M.literal(!0,{errorMap:()=>({message:"Set confirm: true to modify entity metadata"})}),iconVectorName:M.string().optional(),iconLargeName:M.string().optional(),iconMediumName:M.string().optional(),autoPublish:M.boolean().optional().default(!1)}).refine(a=>a.iconVectorName!==void 0||a.iconLargeName!==void 0||a.iconMediumName!==void 0,{message:"At least one of iconVectorName, iconLargeName, or iconMediumName must be provided"});async function ut(a,i,n){switch(a){case"dataverse_update_entity":{let{entityLogicalName:e,hasNotes:t,changeTrackingEnabled:r,isAuditEnabled:o,autoPublish:s}=gn.parse(i),c=X({toolName:"dataverse_update_entity",entitySetName:e}).map(m=>`[${m.severity.toUpperCase()}] ${m.code}: ${m.message}`),l={};t!==void 0&&(l.HasNotes=t),r!==void 0&&(l.ChangeTrackingEnabled=r),o!==void 0&&(l.IsAuditEnabled=o);try{await n.getTableMetadata(e,!1)}catch{return P({summary:`Entity '${e}' does not exist in this Dataverse environment.`,data:{error:"ENTITY_NOT_FOUND",entityLogicalName:e},errorCategory:"SCHEMA_MISMATCH",suggestions:["Use dataverse_list_tables to find the correct entity logical name"]})}let d={"@odata.type":"#Microsoft.Dynamics.CRM.EntityMetadata"};t!==void 0&&(d.HasNotes=t),r!==void 0&&(d.ChangeTrackingEnabled=r),o!==void 0&&(d.IsAuditEnabled={Value:o});try{await n.updateEntityDefinition(e,d)}catch(m){if((m instanceof Error?m.message:String(m)).includes("0x80060888")){let R=Object.keys(l),q={IsAuditEnabled:"IsAuditEnabled requires organization-level auditing to be enabled first: Power Platform admin center > Settings > Audit and logs > Start Auditing",ChangeTrackingEnabled:"ChangeTrackingEnabled may be blocked if the entity is part of a managed solution",HasNotes:"HasNotes=true/false should work for custom (unmanaged) entities"},h=R.map(j=>q[j]).filter(j=>j!==void 0);return P({summary:`Cannot update entity '${e}': operation not supported by Dataverse.`,data:{error:"0x80060888",entityLogicalName:e,requestedChanges:l},errorCategory:"SCHEMA_MISMATCH",suggestions:h})}throw m}let u=Object.entries(l).map(([m,y])=>`${m}=${String(y)}`).join(", "),g="autoPublish=false (skipped)";return s&&(await n.publishCustomizations({entities:[e]}),g="published successfully"),p(`Entity '${e}' updated: ${u}. ${g}.`,{entityLogicalName:e,changes:l,published:s,...c.length>0&&{warnings:c}},["Use dataverse_get_table_metadata to verify the changes","Enable HasNotes=true before using dataverse_create_annotation"])}case"dataverse_set_table_icon":{let{entityLogicalName:e,iconVectorName:t,iconLargeName:r,iconMediumName:o,autoPublish:s}=fn.parse(i),c=X({toolName:"dataverse_set_table_icon",entitySetName:e}).map(m=>`[${m.severity.toUpperCase()}] ${m.code}: ${m.message}`),l={};t!==void 0&&(l.IconVectorName=t),r!==void 0&&(l.IconLargeName=r),o!==void 0&&(l.IconMediumName=o);let d;try{d=await n.getTableMetadata(e,!1)}catch{return P({summary:`Entity '${e}' does not exist in this Dataverse environment.`,data:{error:"ENTITY_NOT_FOUND",entityLogicalName:e},errorCategory:"SCHEMA_MISMATCH",suggestions:["Use dataverse_list_tables to find the correct entity logical name"]})}delete d["@odata.context"],t!==void 0&&(d.IconVectorName=t),r!==void 0&&(d.IconLargeName=r),o!==void 0&&(d.IconMediumName=o),await n.replaceEntityDefinition(e,d);let u=Object.entries(l).map(([m,y])=>`${m}=${String(y)}`).join(", "),g="autoPublish=false (skipped)";return s&&(await n.publishCustomizations({entities:[e]}),g="published successfully"),p(`Entity '${e}' icon updated: ${u}. ${g}.`,{entityLogicalName:e,changes:l,published:s,...c.length>0&&{warnings:c}},["Use dataverse_get_table_metadata to verify the icon","Use dataverse_publish_customizations if autoPublish was left false"])}default:throw new Error(`Unknown metadata tool: ${a}`)}}var yn={name:"dataverse_resolve_entity_name",description:"Resolves entity names bidirectionally: input a logicalName (e.g. 'account') or entitySetName (e.g. 'accounts') and get both representations plus metadata. Essential for avoiding 404/0x80060888 errors caused by using logicalName in OData URLs (which require entitySetName). WHEN TO USE: Any time you're unsure whether to use logicalName vs entitySetName, or to look up PrimaryIdAttribute/PrimaryNameAttribute for new entities. EXAMPLES: 'account' \u2192 LogicalName:account, EntitySetName:accounts, PrimaryId:accountid. WORKFLOW: read_record, inspect_audit.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Entity name to resolve \u2014 either logicalName (e.g. 'account') or entitySetName (e.g. 'accounts')"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},hn=pt.object({name:pt.string().min(1)}),mt=[...He,yn,...dt];async function gt(a,i,n){if(a==="dataverse_resolve_entity_name"){let{name:t}=hn.parse(i),r=`LogicalName eq '${t}' or EntitySetName eq '${t}'`,s=(await n.query("EntityDefinitions",{filter:r,select:["LogicalName","EntitySetName","DisplayName","PrimaryIdAttribute","PrimaryNameAttribute","OwnershipType","IsCustomEntity"]})).value??[];if(s.length===0)return p(`No entity found matching '${t}'`,{name:t,matches:[]},["Try dataverse_list_tables to see available table names"]);let c=s[0],l=c.DisplayName?.UserLocalizedLabel?.Label??null;return p(`Resolved '${t}' \u2192 LogicalName: ${c.LogicalName}, EntitySetName: ${c.EntitySetName}`,{logicalName:c.LogicalName,entitySetName:c.EntitySetName,displayName:l,primaryIdAttribute:c.PrimaryIdAttribute,primaryNameAttribute:c.PrimaryNameAttribute,ownershipType:c.OwnershipType,isCustomEntity:c.IsCustomEntity},["Use entitySetName in OData Web API URLs (dataverse_query, dataverse_read_record)","Use logicalName in FetchXML and metadata API calls"])}return new Set(He.map(t=>t.name)).has(a)?ct(a,i,n):ut(a,i,n)}import{z as _}from"zod";import{z as ft}from"zod";var yt=/^[a-zA-Z_][a-zA-Z0-9_]*$/,S=ft.string().min(1).regex(yt,"entitySetName must contain only letters, digits, or underscores"),Ee=ft.string().min(1).regex(yt,"relationshipName must contain only letters, digits, or underscores");var vn={opportunity:"opportunities",territory:"territories",category:"categories",activityparty:"activityparties",activitymimeattachment:"activitymimeattachments",queue:"queues",queueitem:"queueitems",dependency:"dependencies",salesliteratureitem:"salesliteratureitems",contractdetail:"contractdetails",discounttype:"discounttypes",entitlementtemplate:"entitlementtemplates",pricelevel:"pricelevels"},ht=[{name:"dataverse_query",description:"Queries a Dataverse table using OData ($filter, $select, $orderby, $top, $expand, $count). Use for simple to moderate reads on a single table or with shallow $expand for related fields. TOKEN EFFICIENCY (critical): Always pass $select=['col1','col2'] \u2014 without it, 50+ columns are returned including many nulls. compact defaults to true, automatically dropping null/empty fields. Without $filter, top is auto-capped at 50 to prevent unexpectedly large responses. For complex aggregations (count, sum, avg), multi-entity joins, many-to-many traversal, or advanced FetchXML-only operators, use dataverse_execute_fetchxml. WHEN TO USE: Single-table reads, simple filters, shallow expands, or server-side aggregation via $apply. BEST PRACTICES: $select=['name','statecode'] + compact=true + top\u226450 for exploration. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts", "contacts")'},select:{type:"array",items:{type:"string"},description:"Columns to return. Always specify to minimize payload."},filter:{type:"string",description:'OData $filter expression (e.g., "statecode eq 0 and new_amount gt 1000")'},orderby:{type:"string",description:'OData $orderby expression (e.g., "createdon desc")'},top:{type:"number",description:"Maximum number of records to return (default: 25). Lower defaults reduce token usage; raise explicitly when more rows are needed."},expand:{type:"string",description:'OData $expand for related entities (e.g., "parentaccountid($select=name)")'},count:{type:"boolean",description:"Include total record count in response"},apply:{type:"string",description:'OData $apply for server-side aggregation (e.g., "groupby((statuscode),aggregate($count as count))")'},formattedValues:{type:"boolean",description:"Include human-readable labels for picklist fields alongside raw integer codes."},keepODataAnnotations:{type:"boolean",description:"Preserve verbose OData/CRM annotations. Default false strips them. @odata.etag is always kept."},compact:{type:"boolean",description:"Drops null/empty fields per record (default true). Reduces tokens by 60-80%. Set false only when null values are needed."}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_fetchxml",description:'Executes a FetchXML query against Dataverse \u2014 use for complex scenarios requiring aggregations (count, sum, avg, min, max with grouping), linked-entity joins across multiple tables, many-to-many relationship traversal, or advanced filtering not expressible in OData. Returns a typed array of records. entitySetName is optional \u2014 if omitted it is extracted from the <entity name="..."> element in the FetchXML. Set fetchAll=true to automatically paginate through all pages (up to 100 pages / 50,000 records for safety). Default behavior returns first page only. TOKEN EFFICIENCY: compact defaults to true (drops null/empty fields). Always specify <attribute name="..."> elements to limit columns returned \u2014 omitting them returns all columns including many nulls. WHEN TO USE: Multi-table joins, aggregations with groupby, N:N traversal, or filtering not supported by OData. BEST PRACTICES: Add page/count attributes for large result sets; prefer dataverse_query for simple reads; use fetchAll=true to retrieve all records beyond the first page. WORKFLOW: query_data.',inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name of the root entity (e.g., "accounts"). If omitted, extracted from the <entity name="..."> element in the FetchXML.'},fetchXml:{type:"string",description:"The complete FetchXML query string"},fetchAll:{type:"boolean",description:"When true, automatically paginates through all result pages (up to 100 pages / 50,000 records). Default false returns the first page only."},formattedValues:{type:"boolean",description:"When true, includes human-readable labels for picklist fields alongside raw integer codes."},keepODataAnnotations:{type:"boolean",description:"When true, preserves verbose OData/CRM annotations. Default false strips them to reduce token usage. @odata.etag is always preserved."},compact:{type:"boolean",description:"Drops null, undefined, and empty-string fields from each record (default true). Most Dataverse rows have 30-50+ null columns \u2014 keeping compact=true can reduce token usage by 60-80%."}},required:["fetchXml"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_retrieve_multiple_with_paging",description:"Retrieves ALL records matching a query by automatically following OData nextLink pages. Use instead of dataverse_query when you need more than 5000 records or all records in a table. Returns totalRetrieved count. Set maxTotal to cap retrieval (default 5000, max 50000) to avoid overwhelming the context. TOKEN EFFICIENCY: compact defaults to true (drops null/empty fields). Always pass select=['col1','col2'] \u2014 without $select, all 50+ columns are returned per record. WHEN TO USE: You need all matching records beyond the 5000-row OData page limit or a full table export. BEST PRACTICES: Always set $select; use maxTotal to cap results and avoid context overflow. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},select:{type:"array",items:{type:"string"}},filter:{type:"string"},orderby:{type:"string"},expand:{type:"string"},maxTotal:{type:"number",description:"Maximum records to retrieve (default: 5000, max: 50000)"},formattedValues:{type:"boolean",description:"Include formatted label annotations."},keepODataAnnotations:{type:"boolean",description:"When true, preserves verbose OData/CRM annotations. Default false strips them to reduce token usage. @odata.etag is always preserved."},compact:{type:"boolean",description:"Drops null, undefined, and empty-string fields from each record (default true). Most Dataverse rows have 30-50+ null columns \u2014 keeping compact=true can reduce token usage by 60-80%."}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],bn=_.object({entitySetName:S,select:_.array(_.string()).optional(),filter:_.string().optional(),orderby:_.string().optional(),top:_.number().positive().max(5e3).optional().default(25),expand:_.string().optional(),count:_.boolean().optional(),apply:_.string().optional(),formattedValues:_.boolean().optional(),keepODataAnnotations:_.boolean().optional().default(!1),compact:_.boolean().optional().default(!0)}),_n=_.object({fetchXml:_.string().min(1).describe("Complete FetchXML query string"),entitySetName:_.string().optional().describe('OData entity set name (e.g., "accounts"). If omitted, extracted from the <entity name="..."> element in the FetchXML.'),fetchAll:_.boolean().optional(),formattedValues:_.boolean().optional(),keepODataAnnotations:_.boolean().optional().default(!1),compact:_.boolean().optional().default(!0)}),wn=_.object({entitySetName:S,select:_.array(_.string()).optional(),filter:_.string().optional(),orderby:_.string().optional(),expand:_.string().optional(),maxTotal:_.number().positive().max(5e4).optional(),formattedValues:_.boolean().optional(),keepODataAnnotations:_.boolean().optional().default(!1),compact:_.boolean().optional().default(!0)});async function vt(a,i,n,e){switch(a){case"dataverse_query":{let t=bn.parse(i),r=50,o;!t.filter&&t.top!==void 0&&t.top>r&&(o=r);let s=o??t.top,c=!t.select||t.select.length===0,l=c||t.compact,d={};t.select!==void 0&&(d.select=t.select),t.filter!==void 0&&(d.filter=t.filter),t.orderby!==void 0&&(d.orderby=t.orderby),s!==void 0&&(d.top=s),t.expand!==void 0&&(d.expand=t.expand),t.count!==void 0&&(d.count=t.count),t.apply!==void 0&&(d.apply=t.apply),t.formattedValues!==void 0&&(d.formattedValues=t.formattedValues);let u;try{u=await n.query(t.entitySetName,d)}catch(D){let Qe=D,en=Qe?.data?.error?.message,tn=D instanceof Error?D.message:String(D),Ce=Qe?.status,an=Ce===403||Ce===401?"PERMISSIONS":Ce===404?"SCHEMA_MISMATCH":"UNKNOWN";return ke(`dataverse_query failed on '${t.entitySetName}': ${en??tn}`,an,["Use dataverse_list_tables to verify the entity set name","Check OData filter syntax and column names"])}let g=Array.isArray(u?.value)?u.value:[],m=u["@odata.count"],y=m!==void 0?m===g.length?" (showing all results)":` (total in dataset: ${m})`:"",R=(()=>{let D=g;return t.formattedValues&&(D=pe(D)),t.keepODataAnnotations||(D=ue(D)),l&&(D=v(D)),D})(),q=Array.isArray(u?.value)?(()=>{let D={...u,value:R};return t.keepODataAnnotations||delete D["@odata.context"],D})():u,h=$e({...s!==void 0?{top:s}:{},...t.select!==void 0?{select:t.select}:{},...t.filter!==void 0?{filter:t.filter}:{},entitySetName:t.entitySetName}).map(D=>`[${D.severity.toUpperCase()}] ${D.code}: ${D.message}`),j=[];o!==void 0&&j.push(`[INFO] AUTO_TOP_CAP: top was reduced from ${t.top} to ${r} because no $filter was specified. Add a $filter to query specific records, or pass a lower explicit top.`),c&&!t.compact&&j.push("[INFO] AUTO_COMPACT: compact=true was applied automatically because no $select was specified. Pass $select=['col1','col2'] to choose specific columns and reduce token usage further.");let Ae=[...j,...h],Ye=["Use dataverse_execute_fetchxml for complex joins or aggregations"];return(!t.select||t.select.length===0)&&Ye.push("Add $select=['name','statecode',...] to return only the columns you need \u2014 this is the single most effective way to reduce token usage."),p(`${g.length} records returned from ${t.entitySetName}${y}`,q,Ye,Ae.length>0?Ae:void 0)}case"dataverse_execute_fetchxml":{let t=_n.parse(i),r=t.entitySetName,{fetchXml:o}=t;if(!r){let d=o.match(/<entity\s+name=["']([^"']+)["']/i);if(!d)return{content:[{type:"text",text:JSON.stringify({isError:!0,error:"entitySetName is required when not present in FetchXML <entity> element"})}]};let u=d[1];r=vn[u]??u+"s"}if(t.fetchAll){let u=await n.executeFetchXmlAllPages(r,o);return t.formattedValues&&(u=pe(u)),t.keepODataAnnotations||(u=ue(u)),t.compact&&(u=v(u)),p(`${u.length} records retrieved via FetchXML (all pages)`,u,["Use dataverse_query for simple OData reads","fetchAll paginated through all available pages (up to 100)"])}let s=await n.executeFetchXml(r,o,t.formattedValues),l=Array.isArray(s)?s:Array.isArray(s?.value)?s.value:[];return t.formattedValues&&(l=pe(l)),t.keepODataAnnotations||(l=ue(l)),t.compact&&(l=v(l)),p(`${l.length} records returned via FetchXML`,Array.isArray(s?.value)?(()=>{let d={...s,value:l};return t.keepODataAnnotations||delete d["@odata.context"],d})():l,["Use dataverse_query for simple OData reads","Add page/count attributes for large result sets"])}case"dataverse_retrieve_multiple_with_paging":{let t=wn.parse(i),r={};t.select!==void 0&&(r.select=t.select),t.filter!==void 0&&(r.filter=t.filter),t.orderby!==void 0&&(r.orderby=t.orderby),t.expand!==void 0&&(r.expand=t.expand),t.maxTotal!==void 0&&(r.maxTotal=t.maxTotal),await e?.report(0,1);let o=await n.queryWithPaging(t.entitySetName,r),s=o?.totalRetrieved??(Array.isArray(o?.value)?o.value.length:0),c=o?.pageCount??1;await e?.report(1,1);let l=Array.isArray(o?.records)?o.records:[],d=(()=>{let m=l;return t.formattedValues&&m.length>0&&(m=pe(m)),!t.keepODataAnnotations&&m.length>0&&(m=ue(m)),t.compact&&m.length>0&&(m=v(m)),m})(),u=d!==l?{...o,records:d}:o,g=$e({...t.select!==void 0?{select:t.select}:{},...t.filter!==void 0?{filter:t.filter}:{},entitySetName:t.entitySetName}).map(m=>`[${m.severity.toUpperCase()}] ${m.code}: ${m.message}`);return p(`${s} records retrieved across ${c} pages from ${t.entitySetName}`,u,["Set maxTotal to limit retrieval","Use $select to minimize payload size"],g.length>0?g:void 0)}default:throw new Error(`Unknown query tool: ${a}`)}}import{z as b}from"zod";var bt=[{name:"dataverse_get",description:"Retrieves a single Dataverse record by its GUID. Use when you already know the exact record ID and want specific fields \u2014 faster and more precise than dataverse_query with a GUID filter. Specify select to limit returned columns and reduce payload size. WHEN TO USE: You have the exact record GUID and want specific fields. BEST PRACTICES: Always specify select; use the returned etag for subsequent optimistic-concurrency updates. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name"},id:{type:"string",description:"Record GUID"},select:{type:"array",items:{type:"string"},description:"Columns to return"},expand:{type:"string",description:'OData $expand for related entities (e.g., "parentaccountid($select=name)")'},formattedValues:{type:"boolean",description:"When true, annotates picklist fields with human-readable labels. Note: dataverse_get always requests all OData annotations."},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["entitySetName","id"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create",description:`Creates a new record in a Dataverse table and returns the new record's GUID. Use dataverse_get_table_metadata first to confirm correct logical field names and required fields. For setting lookup fields, use the format "_fieldname_value" with the related record GUID. For bulk creation of multiple records, consider dataverse_batch_execute to reduce HTTP round-trips. WHEN TO USE: Creating a single new record in a known table. BEST PRACTICES: Validate field names via dataverse_get_table_metadata first; use dataverse_batch_execute for bulk inserts. WORKFLOW: create_record.`,inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name"},data:{type:"object",description:"Record data as key-value pairs using logical names"}},required:["entitySetName","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_update",description:"Updates an existing Dataverse record using PATCH semantics \u2014 only the fields provided in data are changed, all other fields remain unchanged. Requires the record GUID. Use dataverse_upsert instead if you want to create-or-update using an alternate key without knowing the GUID upfront. WHEN TO USE: Modifying specific fields on an existing record with a known GUID. BEST PRACTICES: Pass etag for optimistic concurrency; include only changed fields in data. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},data:{type:"object",description:"Fields to update"},etag:{type:"string",description:"ETag value from a prior dataverse_get response. When provided, the update only succeeds if the record has not been modified since (optimistic concurrency). Prevents lost updates."}},required:["entitySetName","id","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_delete",description:"Permanently deletes a Dataverse record by its GUID. This operation is irreversible \u2014 you MUST set confirm=true to proceed. Use dataverse_list_dependencies to check if the record is referenced by workflows, plugins, or other components before deleting shared or configuration records. WHEN TO USE: Permanently removing a record you are certain should be deleted. BEST PRACTICES: Call dataverse_list_dependencies first for shared records; always set confirm=true. WORKFLOW: delete_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},confirm:{type:"boolean",description:"Must be explicitly true to proceed with deletion"}},required:["entitySetName","id","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_upsert",description:'Creates or updates a Dataverse record using an alternate key (no GUID needed). Returns operation="created" or "updated". Use mode="createOnly" to fail with an error if the record already exists, or mode="updateOnly" to fail if the record does not exist. Default mode="upsert" creates or updates. Supports compound alternate keys via the alternateKeys parameter. WHEN TO USE: Create-or-update when you have an alternate key but no GUID. BEST PRACTICES: Verify alternate keys with dataverse_get_entity_key first; use mode to control behavior. WORKFLOW: create_record.',inputSchema:{type:"object",properties:{entitySetName:{type:"string"},alternateKey:{type:"string",description:"Alternate key attribute name (for single key)"},alternateKeyValue:{type:"string",description:"Alternate key value (for single key)"},alternateKeys:{type:"object",description:'Compound alternate key as key-value map (e.g., {"key1":"val1","key2":"val2"}). Use instead of alternateKey/alternateKeyValue for multi-field keys.'},data:{type:"object",description:"Record data"},mode:{type:"string",enum:["upsert","createOnly","updateOnly"],description:"upsert=create or update (default), createOnly=fail if exists, updateOnly=fail if not found"}},required:["entitySetName","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign",description:"Assigns a Dataverse record to a different user or team owner. Sets the ownerid lookup field using the OData bind syntax. WHEN TO USE: Changing the owner of a record to a different user or team. BEST PRACTICES: Use dataverse_list_users or dataverse_list_teams to find valid owner GUIDs first. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name of the record to assign"},id:{type:"string",description:"Record GUID"},ownerType:{type:"string",enum:["systemuser","team"],description:'Type of the new owner: "systemuser" for a user, "team" for a team'},ownerId:{type:"string",description:"GUID of the user or team to assign the record to"}},required:["entitySetName","id","ownerType","ownerId"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Nn=b.object({entitySetName:S,id:b.string().uuid(),select:b.array(b.string()).optional(),expand:b.string().optional(),formattedValues:b.boolean().optional(),compact:b.boolean().optional().default(!0)}),Sn=b.object({entitySetName:S,data:b.record(b.unknown())}),Tn=b.object({entitySetName:S,id:b.string().uuid(),data:b.record(b.unknown()),etag:b.string().optional()}),Rn=b.object({entitySetName:S,id:b.string().uuid(),confirm:b.boolean()}),An=b.object({entitySetName:S,alternateKey:b.string().min(1).optional(),alternateKeyValue:b.string().min(1).optional(),alternateKeys:b.record(b.string()).optional(),data:b.record(b.unknown()),mode:b.enum(["upsert","createOnly","updateOnly"]).default("upsert").optional().describe("upsert=create or update (default), createOnly=fail if exists, updateOnly=fail if not found")}).refine(a=>a.alternateKey&&a.alternateKeyValue||a.alternateKeys&&Object.keys(a.alternateKeys).length>0,{message:"Provide either (alternateKey + alternateKeyValue) or alternateKeys"}),kn=b.object({entitySetName:S,id:b.string().uuid(),ownerType:b.enum(["systemuser","team"]),ownerId:b.string().uuid()});async function _t(a,i,n){switch(a){case"dataverse_get":{let{entitySetName:e,id:t,select:r,expand:o,formattedValues:s,compact:c}=Nn.parse(i),l,d;try{let m=await n.getRecord(e,t,r,o);l=m.record,d=m.etag}catch(m){let y=m,q=y?.data?.error?.message,h=m instanceof Error?m.message:String(m),j=y?.status,Ae=j===403||j===401?"PERMISSIONS":j===404?"SCHEMA_MISMATCH":"UNKNOWN";return ke(`dataverse_get failed for '${t}' in '${e}': ${q??h}`,Ae,["Use dataverse_list_tables to verify the entity set name","Verify the record ID exists with dataverse_query"])}let u=s?pe([l])[0]??l:l,g=c?be(u):u;return p(`Retrieved record ${t} from ${e}`,{id:t,record:g,etag:d},["Use dataverse_update to modify this record","Use dataverse_get_relationships to explore related records"])}case"dataverse_create":{let{entitySetName:e,data:t}=Sn.parse(i),r=await n.createRecord(e,t);return p(`Created record ${r} in ${e}`,{id:r,message:"Record created successfully"},["Use dataverse_get to retrieve the full record","Use dataverse_associate to link related records"])}case"dataverse_update":{let{entitySetName:e,id:t,data:r,etag:o}=Tn.parse(i);return await n.updateRecord(e,t,r,o),p(`Updated record ${t} in ${e}`,{message:"Record updated successfully"},["Use dataverse_get to verify the update","Use etag parameter for optimistic concurrency"])}case"dataverse_delete":{let{entitySetName:e,id:t,confirm:r}=Rn.parse(i);return r?(await n.deleteRecord(e,t),p(`Deleted record ${t} from ${e}`,{message:"Record deleted successfully"},["This operation is irreversible"])):{content:[{type:"text",text:JSON.stringify({message:`Deletion not performed. Set 'confirm: true' to delete record '${t}' from '${e}'.`})}]}}case"dataverse_upsert":{let e=An.parse(i),{entitySetName:t,alternateKey:r,alternateKeyValue:o,alternateKeys:s,data:c,mode:l="upsert"}=e,d;s&&Object.keys(s).length>0&&(d=Object.entries(s).map(([g,m])=>`${w(g)}='${w(m)}'`).join(","));let u=await n.upsertRecord(t,r??"",o??"",c,l,d);return p(`Upsert ${u.operation}: record in ${t}`,{operation:u.operation,id:u.id,message:`Record ${u.operation} successfully`},["Use dataverse_get_entity_key to verify alternate key definitions"])}case"dataverse_assign":{let{entitySetName:e,id:t,ownerType:r,ownerId:o}=kn.parse(i),s=r==="systemuser"?"systemusers":"teams";return await n.updateRecord(e,t,{"ownerid@odata.bind":`/${s}(${o})`}),p(`Assigned record ${t} in ${e} to new owner`,{message:"Record assigned successfully"},["Use dataverse_list_users or dataverse_list_teams to find valid owners"])}default:throw new Error(`Unknown CRUD tool: ${a}`)}}import{z as I}from"zod";var wt=[{name:"dataverse_associate",description:"Creates an association between two Dataverse records via a named N:N or 1:N relationship. Requires the relationship schema name obtainable from dataverse_get_relationships. Use for N:N relationships or to link records without modifying a lookup field directly \u2014 for simple 1:N lookups, setting the lookup field in dataverse_update is simpler.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Source record GUID"},relationshipName:{type:"string",description:"Relationship schema name"},relatedEntitySetName:{type:"string",description:"Related entity set name"},relatedId:{type:"string",description:"Related record GUID"}},required:["entitySetName","id","relationshipName","relatedEntitySetName","relatedId"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_associate_bulk",description:"Associates one source record with multiple related records at once via a named relationship, executing all associations in parallel. Unlike dataverse_associate (one pair at a time), this accepts an array of related IDs and runs them concurrently. Uses Promise.allSettled semantics \u2014 individual failures are reported per item without aborting the others.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"Source entity set name"},id:{type:"string",description:"Source record GUID"},relationshipName:{type:"string",description:"Relationship schema name"},relatedEntitySetName:{type:"string",description:"Related entity set name"},relatedIds:{type:"array",items:{type:"string"},description:"GUIDs of records to associate (1\u2013200)",minItems:1,maxItems:200}},required:["entitySetName","id","relationshipName","relatedEntitySetName","relatedIds"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_disassociate",description:"Removes an existing association between two Dataverse records on a named relationship. For N:N relationships, provide relatedId and relatedEntitySetName to build the correct $id URL. For 1:N relationships, relatedId and relatedEntitySetName are optional. Use dataverse_get_relationships to find the correct relationship schema name.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string"},relationshipName:{type:"string"},relatedId:{type:"string",description:"Required for N:N relationships"},relatedEntitySetName:{type:"string",description:'Entity set name of the related record (required for N:N). E.g., "contacts"'},confirm:{type:"boolean",const:!0,description:"Must be explicitly true to confirm removal of the association"}},required:["entitySetName","id","relationshipName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_query_associations",description:"Reads existing N:N associations for a record by querying through a navigation property. Returns the related records \u2014 use to verify what's already linked before calling dataverse_associate or dataverse_disassociate. Use $select to return only needed columns; set $top to limit results.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"Source entity set name, e.g. 'roles'"},id:{type:"string",description:"Source record GUID"},navigationProperty:{type:"string",description:"Navigation property name, e.g. 'roleprivileges_association'"},select:{type:"string",description:"Comma-separated columns to return on related records"},top:{type:"number",description:"Max records to return. Default 50, max 1000"},filter:{type:"string",description:"OData $filter expression applied to related records"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:["entitySetName","id","navigationProperty"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],En=I.object({entitySetName:S,id:I.string().uuid(),relationshipName:Ee,relatedEntitySetName:S,relatedIds:I.array(I.string().uuid()).min(1).max(200)}),xn=I.object({entitySetName:S,id:I.string().uuid(),relationshipName:Ee,relatedEntitySetName:S,relatedId:I.string().uuid()}),Dn=I.object({entitySetName:S,id:I.string().uuid(),relationshipName:Ee,relatedId:I.string().uuid().optional(),relatedEntitySetName:S.optional(),confirm:I.literal(!0)}),In=/^[A-Za-z_][A-Za-z0-9_]*$/,Cn=I.object({entitySetName:S,id:I.string().uuid(),navigationProperty:I.string().min(1).regex(In,"Invalid navigation property name"),select:I.string().optional(),top:I.number().int().min(1).max(1e3).optional(),filter:I.string().optional(),compact:I.boolean().optional().default(!0)});async function Nt(a,i,n){switch(a){case"dataverse_associate_bulk":{let{entitySetName:e,id:t,relationshipName:r,relatedEntitySetName:o,relatedIds:s}=En.parse(i),c=await Promise.allSettled(s.map(u=>n.associate(e,t,r,o,u))),l=[],d=[];return c.forEach((u,g)=>{u.status==="fulfilled"?l.push(s[g]):d.push({id:s[g],error:u.reason instanceof Error?u.reason.message:String(u.reason)})}),p(`${l.length}/${s.length} associations succeeded`,{entitySetName:e,id:t,relationshipName:r,succeeded:l,failed:d,successCount:l.length,failureCount:d.length},d.length>0?["Some associations failed \u2014 check the 'failed' array for details"]:["All associations processed (Dataverse returns 204 for both new and duplicate associations \u2014 use dataverse_query_associations to verify current state)"])}case"dataverse_associate":{let{entitySetName:e,id:t,relationshipName:r,relatedEntitySetName:o,relatedId:s}=xn.parse(i);return await n.associate(e,t,r,o,s),p(`Associated ${e}(${t}) with ${o}(${s}) via ${r}`,{message:"Records associated successfully"},["Use dataverse_get_relationships to verify relationship names"])}case"dataverse_disassociate":{let{entitySetName:e,id:t,relationshipName:r,relatedId:o,relatedEntitySetName:s}=Dn.parse(i);return await n.disassociate(e,t,r,o,s),p(`Disassociated records via ${r}`,{message:"Records disassociated successfully"},["This removes the N:N link but does not delete records"])}case"dataverse_query_associations":{let{entitySetName:e,id:t,navigationProperty:r,select:o,top:s,filter:c,compact:l}=Cn.parse(i),d=`${e}(${t})/${r}`,g=(await n.query(d,{...o?{select:o.split(",").map(m=>m.trim())}:{},top:s??50,...c?{filter:c}:{}})).value??[];return l&&(g=v(g)),p(`${g.length} associated records via ${r}`,{entitySetName:e,id:t,navigationProperty:r,count:g.length,records:g},["Use dataverse_associate to add new associations","Use dataverse_disassociate to remove existing ones"])}default:throw new Error(`Unknown relation tool: ${a}`)}}import{z as T}from"zod";var St=[{name:"dataverse_execute_action",description:"Executes a global (unbound) Dataverse action that is not tied to a specific record \u2014 for example WinOpportunity, SendEmail, or custom process actions. Use dataverse_execute_bound_action when the action must operate on a particular record. Actions differ from functions in that they are state-changing operations; for read-only operations use dataverse_execute_function.",inputSchema:{type:"object",properties:{actionName:{type:"string",description:'Action logical name (e.g., "WinOpportunity")'},parameters:{type:"object",description:"Action parameters"}},required:["actionName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_execute_function",description:"Executes a global (unbound) Dataverse OData function that is read-only and returns data without side effects \u2014 for example RetrieveTotalRecordCount or InitializeFrom. Use dataverse_execute_action for state-changing operations. Use dataverse_execute_bound_action when the function/action requires a specific record context.",inputSchema:{type:"object",properties:{functionName:{type:"string",description:'Function name (e.g., "WhoAmI", "RetrieveTotalRecordCount")'},parameters:{type:"object",description:"Function parameters: strings are quoted inline, numbers/booleans inlined as-is, objects serialized as aliased OData parameters"}},required:["functionName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_bound_action",description:"Executes a Dataverse action bound to a specific record instance, passing the entity set name and record GUID as context (e.g., QualifyLead on a lead, or a custom action scoped to an account). The actionName should not include the Microsoft.Dynamics.CRM namespace prefix. Use dataverse_execute_action for global unbound actions that do not require a record context.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},actionName:{type:"string",description:"Action name (without Microsoft.Dynamics.CRM prefix)"},parameters:{type:"object",description:"Action parameters"}},required:["entitySetName","id","actionName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_retrieve_dependencies_for_delete",description:"Checks what solution components would block deletion of a specific component. Provide the Dataverse component type code (1=Entity, 2=Attribute, 26=SavedQuery, 29=Workflow, 92=PluginAssembly) and the component GUID. Use before deleting shared Dataverse customization components.",inputSchema:{type:"object",properties:{componentType:{type:"number",description:"Dataverse component type code (1=Entity, 2=Attribute, 29=Workflow, 92=PluginAssembly)"},objectId:{type:"string",description:"Component GUID"}},required:["componentType","objectId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_bound_function",description:"Executes a Dataverse function bound to a specific record (e.g., CalculateRollupField, GetQuantityAvailable). Use for read-only computed operations on a single record. Unlike bound actions, bound functions do not modify data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name of the table (e.g., "accounts")'},id:{type:"string",description:"GUID of the record"},functionName:{type:"string",description:'Name of the bound function (e.g., "CalculateRollupField")'},parameters:{type:"object",description:"Function parameters as key-value pairs of strings",additionalProperties:{type:"string"}}},required:["entitySetName","id","functionName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_dependencies",description:"Lists workflows, Power Automate flows, Business Rules, and custom actions that reference a given table. Use to detect hidden dependencies before modifying or removing a table. Returns component name, type, state (Active/Draft), trigger event (Create/Update/Delete), and count.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:'Logical name of the table to check (e.g., "account", "contact")'},componentType:{type:"array",items:{type:"string",enum:["Workflow","Flow","BusinessRule","Action","BusinessProcessFlow","Plugin","CustomAPI"]},description:"Filter by component type. Default: all types."}},required:["tableName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],xe=/^[a-zA-Z0-9_.]+$/,On=T.object({actionName:T.string().min(1).regex(xe,"actionName must contain only letters, digits, underscores, or dots"),parameters:T.record(T.unknown()).optional().default({})}),Ln=T.object({functionName:T.string().min(1).regex(xe,"functionName must contain only letters, digits, underscores, or dots"),parameters:T.record(T.unknown()).optional().default({})}),Un=T.object({entitySetName:S,id:T.string().uuid(),actionName:T.string().min(1).regex(xe,"actionName must contain only letters, digits, underscores, or dots"),parameters:T.record(T.unknown()).optional().default({})}),Pn=T.object({componentType:T.number().int().positive(),objectId:T.string().uuid()}),Mn=["Workflow","Flow","BusinessRule","Action","BusinessProcessFlow","Plugin","CustomAPI"],Hn=T.object({tableName:T.string().min(1),componentType:T.array(T.enum(Mn)).optional()}),$n=T.object({entitySetName:S,id:T.string().uuid(),functionName:T.string().min(1).regex(xe,"functionName must contain only letters, digits, underscores, or dots"),parameters:T.record(T.unknown()).optional().default({})});async function Tt(a,i,n){switch(a){case"dataverse_execute_action":{let{actionName:e,parameters:t}=On.parse(i),r=await n.executeAction(e,t);return p(`Executed unbound action ${e}`,r,["Use dataverse_list_custom_actions to discover available actions"])}case"dataverse_execute_function":{let{functionName:e,parameters:t}=Ln.parse(i),r=await n.executeFunction(e,t);return p(`Executed function ${e}`,r,["Functions are read-only operations"])}case"dataverse_execute_bound_action":{let{entitySetName:e,id:t,actionName:r,parameters:o}=Un.parse(i),s=await n.executeBoundAction(e,t,r,o);return p(`Executed bound action ${r} on ${e}(${t})`,s)}case"dataverse_list_dependencies":{let{tableName:e,componentType:t}=Hn.parse(i),r=await n.listTableDependencies(e,t);return p(`${r.count} dependencies found for component`,r,["Use this information before deleting or modifying the component"])}case"dataverse_retrieve_dependencies_for_delete":{let{componentType:e,objectId:t}=Pn.parse(i),r=await n.listDependencies(e,t),o=Array.isArray(r)?r:r.value??[r];return p(`${o.length} dependencies found`,r,["Review dependencies before deleting the component"])}case"dataverse_execute_bound_function":{let{entitySetName:e,id:t,functionName:r,parameters:o}=$n.parse(i),s=await n.executeBoundFunction(e,t,r,o);return p(`Executed bound function ${r} on ${e}(${t})`,s)}default:throw new Error(`Unknown action tool: ${a}`)}}import{z as ie}from"zod";var Rt=[{name:"dataverse_batch_execute",description:`Executes multiple Dataverse operations in a single HTTP $batch request to reduce network round-trips and improve throughput. Accepts up to 1000 individual GET, POST, PATCH, or DELETE requests. Use for bulk creates, updates, or deletes that need to be grouped for performance. Set useChangeset=true to wrap all mutating operations (POST/PATCH/DELETE) in an atomic changeset \u2014 a failure rolls back ALL changeset operations. Individual per-operation results (status, body) are returned as an array in the same order as the input requests. $REF OPERATIONS (N:N/1:N associations): POST to a collection-valued navigation property (N:N): url='roles({id})/roleprivileges_association/$ref', body must contain { "@odata.id": "https://<org_url>/api/data/v9.2/privileges({privilegeId})" } with an ABSOLUTE URL \u2014 a relative URL will cause a 400 error. PUT to a single-valued navigation property (lookup/1:N): url='contacts({id})/account_primary_contact/$ref', body must contain { "@odata.id": "https://<org_url>/api/data/v9.2/accounts({accountId})" } with an ABSOLUTE URL. Example: { method: 'POST', url: 'roles({roleId})/roleprivileges_association/$ref', body: { '@odata.id': 'https://org.crm.dynamics.com/api/data/v9.2/privileges({privilegeId})' } }. WHEN TO USE: Bulk creates, updates, or deletes (up to 1000 ops) needing a single HTTP round-trip; batched $ref associations. BEST PRACTICES: Use useChangeset=true for atomic all-or-nothing mutations; batch GETs for parallel reads; always use absolute URLs in @odata.id for $ref bodies. WORKFLOW: bulk_operations.`,inputSchema:{type:"object",properties:{requests:{type:"array",description:"Array of batch requests to execute",items:{type:"object",properties:{method:{type:"string",enum:["GET","POST","PATCH","DELETE"],description:"HTTP method"},url:{type:"string",description:'Relative URL (e.g., "accounts(guid)" or "contacts"). For $ref operations (e.g. "roles({id})/roleprivileges_association/$ref"), the request body MUST include "@odata.id" with an ABSOLUTE URL \u2014 relative URLs in @odata.id cause 400 errors.'},body:{type:"object",description:"Request body for POST/PATCH operations"}},required:["method","url"]}},useChangeset:{type:"boolean",description:"Wrap mutating operations (POST/PATCH/DELETE) in an atomic changeset. A failure rolls back ALL operations in the changeset. Defaults to false."}},required:["requests"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}],qn=ie.object({method:ie.enum(["GET","POST","PATCH","DELETE"]),url:ie.string().min(1).refine(a=>!/[\r\n]/.test(a),{message:"Batch URL must not contain CR or LF characters"}).refine(a=>!a.startsWith("http"),{message:"Batch URL must be a relative path, not an absolute URL"}).refine(a=>!/(\.\.[\/\\])|(^\.\.$)/.test(a),{message:"Batch URL must not contain path traversal sequences"}),body:ie.record(ie.unknown()).optional()}),jn=ie.object({requests:ie.array(qn).min(1).max(1e3),useChangeset:ie.boolean().default(!1).describe("Wrap mutating operations (POST/PATCH/DELETE) in an atomic changeset. A failure rolls back ALL operations in the changeset.")});async function At(a,i,n,e){if(a==="dataverse_batch_execute"){let{requests:t,useChangeset:r}=jn.parse(i),o=t.map(l=>({method:l.method,url:l.url,body:l.body}));await e?.report(0,t.length);let s=await n.batchExecute(o,r);await e?.report(t.length,t.length);let c=s.filter(l=>l===null||!l.error).length;return p(`Batch executed: ${c}/${s.length} operations succeeded`,{results:s,count:s.length},["Use batch for bulk create/update operations to reduce HTTP round-trips"])}throw new Error(`Unknown batch tool: ${a}`)}import{z as _e}from"zod";var kt=[{name:"dataverse_change_detection",description:"Detects new, modified, and deleted records since a previous sync using Dataverse change tracking (delta queries). On first call, pass deltaToken=null to get an initial snapshot and receive a token. On subsequent calls, pass the returned token to retrieve only changes since last sync. Change tracking must be enabled on the table in Dataverse settings. Returns newAndModified records, deleted record IDs, and the nextDeltaToken for the next call. WHEN TO USE: Incremental sync \u2014 detecting records created, updated, or deleted since a previous snapshot. BEST PRACTICES: Store the deltaToken persistently; always specify $select to minimize payload. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},deltaToken:{anyOf:[{type:"string"},{type:"null"}],description:"Delta token from a previous call, or null for the initial sync"},select:{type:"array",items:{type:"string"},description:"Columns to return (recommended to minimise payload)"},compact:{type:"boolean",description:"Drop null/empty fields from each returned record (default true). Most Dataverse rows have many null columns \u2014 compact=true can reduce token usage by 60-80%."}},required:["entitySetName","deltaToken"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Wn=_e.object({entitySetName:S,deltaToken:_e.string().nullable(),select:_e.array(_e.string()).optional(),compact:_e.boolean().optional().default(!0)});async function Et(a,i,n){if(a==="dataverse_change_detection"){let e=Wn.parse(i),{entitySetName:t,deltaToken:r,select:o}=e,s;try{s=await n.getChangedRecords(t,r,o)}catch(u){let g=u instanceof Error?u.message:String(u);if(/change.?track|0x80072491/i.test(g))return ge({type:"feature_disabled",feature:"Change Tracking",cannotProceedBecause:`Change tracking is not enabled on '${t}', so delta queries cannot be executed.`,adminPortal:"Power Apps Maker Portal",steps:["Open Power Apps maker portal (make.powerapps.com)",`Navigate to Tables \u2192 search for '${t}'`,"Open the table \u2192 click the Settings (gear) icon",'Enable "Track changes"',"Save the table, then publish customizations"],fixableViaToolName:"dataverse_update_entity"});throw u}let c=s,l=Array.isArray(c?.newAndModified)?c.newAndModified:[];l=ue(l),e.compact&&(l=v(l));let d={...c,newAndModified:l};return p(`${l.length} changed records detected`,d,["Store the returned deltaToken for subsequent incremental sync"])}throw new Error(`Unknown tracking tool: ${a}`)}import{z as oe}from"zod";var Fn=oe.object({components:oe.object({entities:oe.array(oe.string()).optional().describe("Entity logical names to publish"),webResources:oe.array(oe.string()).optional().describe("Web resource names to publish"),optionSets:oe.array(oe.string()).optional().describe("Global OptionSet names to publish")}).optional().describe("Specific components to publish. If omitted, ALL unpublished customizations are published.")}),xt=[{name:"dataverse_publish_customizations",description:'Publishes unpublished Dataverse customizations. \u26A0\uFE0F WITHOUT components \u2192 calls PublishAllXml, an EXCLUSIVE ORG-WIDE LOCK lasting 30-120s. While active, ALL other publish calls (scoped or global) fail with "already in progress". \u2705 ALWAYS prefer specifying components.entities/webResources/optionSets to use the scoped PublishXml action instead \u2014 faster and does NOT block other operations. \u274C DO NOT call without components after each individual change \u2014 batch ALL your schema changes first, then publish ONCE at the end. If you receive a "publish already in progress" error, the MCP will auto-retry every 30s (up to 2 minutes) \u2014 do NOT call the tool again; wait for the previous call to return. WHEN TO USE: Once at the end of a series of schema changes (table/column/relationship/form creation). BEST PRACTICES: Pass components.entities with the list of modified entity logical names. Full publish only if you cannot enumerate the changed components. WORKFLOW: manage_solution.',inputSchema:{type:"object",properties:{components:{type:"object",description:"Specific components. Omit to publish all.",properties:{entities:{type:"array",items:{type:"string"}},webResources:{type:"array",items:{type:"string"}},optionSets:{type:"array",items:{type:"string"}}}}},required:[]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function Dt(a,i,n){switch(a){case"dataverse_publish_customizations":{let{components:e}=Fn.parse(i??{}),t=await n.publishCustomizations(e);return p("Customizations published successfully",t,["Changes are now visible to all users in the environment"])}default:throw new Error(`Unknown solution tool: ${a}`)}}import{z as we}from"zod";var Gn=we.object({callerId:we.string().uuid().describe('Azure AD Object ID (GUID) of the Dataverse system user to impersonate. Requires the executing account to have the "Act on behalf of another user" privilege in Dataverse.'),toolName:we.string().min(1).describe('Name of the MCP tool to execute on behalf of the user (e.g., "dataverse_create", "dataverse_query")'),toolArgs:we.record(we.unknown()).describe("Arguments for the wrapped tool, as an object")}),qe=[{name:"dataverse_impersonate",description:'Executes another Dataverse tool on behalf of a different system user by injecting the MSCRMCallerId header. Requires the executing account to have the "Act on behalf of another user" privilege in Dataverse. The impersonation applies ONLY to the single tool call specified. Use for auditing workflows that must create or update records under a specific user identity. WHEN TO USE: Creating or updating records under a specific user identity for audit-trail purposes. BEST PRACTICES: Impersonation applies to the single wrapped tool call only; cannot impersonate System Administrators. WORKFLOW: update_record.',inputSchema:{type:"object",properties:{callerId:{type:"string",description:"GUID (Azure AD Object ID) of the Dataverse system user to impersonate"},toolName:{type:"string",description:'MCP tool to execute while impersonating (e.g., "dataverse_create")'},toolArgs:{type:"object",description:"Arguments for the wrapped tool"}},required:["callerId","toolName","toolArgs"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function It(a,i,n,e){if(a!=="dataverse_impersonate")throw new Error(`Unknown impersonate tool: ${a}`);let{callerId:t,toolName:r,toolArgs:o}=Gn.parse(i);try{if(((await n.query(`systemusers(${w(t)})/systemuserroles_association`,{select:["name"],filter:"name eq 'System Administrator'",top:1})).value?.length??0)>0)throw new Error("Security policy: impersonation of users with System Administrator role is prohibited to prevent privilege escalation.")}catch(l){throw l instanceof Error&&l.message.includes("Security policy")?l:new Error(`Security policy: cannot verify callerId roles \u2014 impersonation denied. Cause: ${String(l)}`)}let s=n.http,c=s.defaultHeaders.MSCRMCallerId;try{s.defaultHeaders.MSCRMCallerId=t;let l=await e(r,o,n);return p(`Impersonated as ${t}, executed ${r}`,{impersonatedAs:t,tool:r,result:JSON.parse(l.content[0].text)},["Impersonation applies to this single call only"])}finally{c===void 0?delete s.defaultHeaders.MSCRMCallerId:s.defaultHeaders.MSCRMCallerId=c}}import{z as H}from"zod";var Bn={10:"Pre-validation",20:"Pre-operation",40:"Post-operation",45:"Post-operation (deprecated)"},Vn={0:"Synchronous",1:"Asynchronous"},zn=H.object({workflowId:H.string().uuid(),activate:H.boolean()}),Ct=[{name:"dataverse_list_custom_actions",description:"Lists all custom actions (custom API / SDK messages) registered in the environment. Returns the message name, category, bound entity (if any), execute privilege, and whether it is customizable. Useful for discovering available automation entry points and agent-callable actions. WHEN TO USE: Discovering available custom API / SDK messages for automation. BEST PRACTICES: Use nameFilter to search; follow up with dataverse_execute_action to invoke. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 100, max 500)"},nameFilter:{type:"string",description:"Filter by name (substring match)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_plugin_steps",description:"Lists plugin steps (SdkMessageProcessingStep registrations) in the environment. Shows plugin assembly, step name, message (Create/Update/Delete/\u2026), entity, stage (pre/post), mode (sync/async), and state (enabled/disabled). Essential for understanding what custom business logic fires on Dataverse operations. WHEN TO USE: Understanding what custom business logic fires on CRUD operations for a table. BEST PRACTICES: Filter by entityLogicalName; check stage and mode to understand execution order and timing. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 100, max 500)"},activeOnly:{type:"boolean",description:"Return only enabled steps (default: true)"},entityLogicalName:{type:"string",description:"Filter by entity logical name (e.g. 'account')"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_workflow_state",description:"Activates or deactivates a Dataverse workflow (classic workflow / real-time workflow / action). Set activate=true to activate (statecode 1, statuscode 2) or activate=false to deactivate (statecode 0, statuscode 1). Returns the new state. WHEN TO USE: Activating or deactivating a classic workflow, real-time workflow, or action. BEST PRACTICES: Verify workflow ID first; deactivation stops future triggers but does not cancel in-progress runs. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{workflowId:{type:"string",description:"The workflow GUID"},activate:{type:"boolean",description:"true = activate, false = deactivate (draft)"}},required:["workflowId","activate"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_connection_references",description:"Lists all Power Automate connection references defined in the environment, showing their display name, connector ID, connection ID, and active status. Use this before importing a solution containing flows to detect broken or unmapped connections that would cause silent import failures. WHEN TO USE: Pre-deployment solution validation, auditing connection health, identifying broken flow connections. BEST PRACTICES: Look for inactive (isActive=false) references \u2014 these indicate flows that will fail after deployment. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{connectorId:{type:"string",description:"Filter by connector ID substring (e.g. '/providers/Microsoft.PowerApps/apis/shared_sharepointonline')"},activeOnly:{type:"boolean",description:"Return only active connection references (default: false = return all)"},top:{type:"number",description:"Maximum records to return (default 100, max 500)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Kn=H.object({top:H.number().positive().max(500).optional().default(100),nameFilter:H.string().optional()}),Xn=H.object({top:H.number().positive().max(500).optional().default(100),activeOnly:H.boolean().optional().default(!0),entityLogicalName:H.string().optional()}),Jn=H.object({connectorId:H.string().optional(),activeOnly:H.boolean().optional().default(!1),top:H.number().int().positive().max(500).optional().default(100)});async function Ot(a,i,n){switch(a){case"dataverse_list_custom_actions":{let{top:e,nameFilter:t}=Kn.parse(i??{}),r=["isprivate eq false"];t&&r.push(`contains(name,'${w(t)}')`);let s=(await n.query("sdkmessages",{select:["sdkmessageid","name","categoryname","isprivate","isreadonly","isvalidforexecuteasync"],filter:r.join(" and "),top:e})).value.map(c=>({id:c.sdkmessageid,name:c.name,category:c.categoryname??"",isPrivate:c.isprivate,isReadOnly:c.isreadonly,asyncSupported:c.isvalidforexecuteasync}));return p(`${s.length} custom actions found`,{total:s.length,messages:s},["Use dataverse_execute_action to run an action"])}case"dataverse_list_plugin_steps":{let{top:e,activeOnly:t,entityLogicalName:r}=Xn.parse(i??{}),o={select:["sdkmessageprocessingstepid","name","stage","mode","rank","statecode","filteringattributes","asyncautodelete"],expand:"sdkmessageid($select=name,categoryname),plugintypeid($select=name,assemblyname),sdkmessagefilterid($select=primaryobjecttypecode)",top:e};t&&(o.filter="statecode eq 0");let c=(await n.query("sdkmessageprocessingsteps",o)).value;if(r){let d=r.toLowerCase();c=c.filter(u=>u.sdkmessagefilterid?.primaryobjecttypecode?.toLowerCase()===d)}let l=c.map(d=>({id:d.sdkmessageprocessingstepid,name:d.name,message:d.sdkmessageid?.name??"",entity:d.sdkmessagefilterid?.primaryobjecttypecode??"",assembly:d.plugintypeid?.assemblyname??"",pluginType:d.plugintypeid?.name??"",stage:d.stage,stageName:Bn[d.stage]??`Stage ${d.stage}`,mode:d.mode,modeName:Vn[d.mode]??`Mode ${d.mode}`,rank:d.rank,isActive:d.statecode===0,filteringAttributes:d.filteringattributes??null,asyncAutoDelete:d.asyncautodelete}));return p(`${l.length} plugin steps found`,{total:l.length,steps:l},["Use dataverse_get_plugin_trace_logs for debugging plugin issues"])}case"dataverse_set_workflow_state":{let{workflowId:e,activate:t}=zn.parse(i);try{await n.updateRecord("workflows",e,{statecode:t?1:0,statuscode:t?2:1})}catch(r){let o=r instanceof Error?r.message:String(r);throw/0x80040203|does not refer to a valid workflow|404/i.test(o)?new Error(`Workflow '${e}' not found. Use dataverse_list_workflows to retrieve valid workflow GUIDs in this environment. Original error: ${o}`):r}return p(`Workflow state updated for ${e}`,{workflowId:e,newState:t?"Activated":"Draft",statecode:t?1:0,statuscode:t?2:1})}case"dataverse_list_connection_references":{let{connectorId:e,activeOnly:t,top:r}=Jn.parse(i??{}),o=[];t&&o.push("statecode eq 0"),e&&o.push(`contains(connectorid,'${w(e)}')`);let s={select:["connectionreferenceid","connectionreferencelogicalname","connectionreferencedisplayname","connectorid","connectionid","statecode","statuscode"],orderby:"connectionreferencedisplayname asc",top:r};o.length&&(s.filter=o.join(" and "));let l=((await n.query("connectionreferences",s)).value??[]).map(g=>({id:g.connectionreferenceid??"",logicalName:g.connectionreferencelogicalname??"",displayName:g.connectionreferencedisplayname??"",connectorId:g.connectorid??"",connectionId:g.connectionid??null,isActive:g.statecode===0,statusCode:g.statuscode??null})),d=l.filter(g=>!g.isActive).length,u=d>0?`${l.length} connection references found (${d} inactive \u2014 check before deploying)`:`${l.length} connection references found`;return p(u,{connectionReferences:l,count:l.length,inactiveCount:d},["Inactive references (isActive=false) indicate broken connections that will cause flow failures","Use the Power Apps maker portal to reconnect or replace broken connection references before deploying"])}default:throw new Error(`Unknown customization tool: ${a}`)}}import{z as W}from"zod";var Yn={1e8:"String",100000001:"Number",100000002:"Boolean",100000003:"JSON",100000004:"DataSource"},Qn={String:1e8,Integer:100000001,Boolean:100000002,JSON:100000003},Lt=[{name:"dataverse_get_environment_variable",description:"Retrieves an environment variable's definition and current value from Dataverse. Returns the schema name, display name, type, default value, and the current override value (if set). Useful for reading feature flags, configuration values, and integration settings stored in Dataverse environment variables. WHEN TO USE: Reading configuration values, feature flags, or integration settings stored as environment variables. BEST PRACTICES: Check both defaultValue and currentValue; the effective value is currentValue ?? defaultValue. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"The schema name of the environment variable (e.g. 'new_MyConfig')"}},required:["schemaName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_environment_variable",description:"Sets or updates an environment variable value in Dataverse. If a value record already exists for the variable, it is updated; otherwise a new value record is created. The schemaName must match an existing environment variable definition. WHEN TO USE: Updating configuration values or feature flags stored in Dataverse environment variables. BEST PRACTICES: Verify the variable exists first with dataverse_get_environment_variable; schemaName must match exactly. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"The schema name of the environment variable"},value:{type:"string",description:"The new value to set"}},required:["schemaName","value"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create_environment_variable",description:"Creates a new Dataverse environment variable definition and sets its initial value. Use this when the variable does not yet exist \u2014 use dataverse_set_environment_variable to update an existing one. WHEN TO USE: Initial setup of configuration flags, feature toggles, or integration settings. BEST PRACTICES: Use a solution-prefixed schemaName (e.g. 'myprefix_MyConfig'); provide solutionUniqueName to register in a solution. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"Schema name of the new variable (e.g. 'new_MyConfig'). Case-sensitive."},displayName:{type:"string",description:"Human-readable display name"},type:{type:"string",enum:["String","Integer","Boolean","JSON"],description:"Variable type"},value:{type:"string",description:"Initial value to set"},description:{type:"string",description:"Optional description"},defaultValue:{type:"string",description:"Optional default value (fallback when no override value is set)"},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional creation of a new environment variable definition"}},required:["schemaName","displayName","type","value","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_environment_capabilities",description:"Returns a comprehensive snapshot of the Dataverse environment: identity (WhoAmI), organization settings (name, version, language, audit config), unmanaged solution count, and environment variable count. Use at the start of a session to orient yourself to the environment and its configuration. WHEN TO USE: Beginning of any Dataverse automation session, before bulk operations, or when debugging unexpected behavior. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Zn=W.object({schemaName:W.string().min(1)}),er=W.object({schemaName:W.string().min(1),value:W.string()}),tr=W.object({schemaName:W.string().min(1),displayName:W.string().min(1),type:W.enum(["String","Integer","Boolean","JSON"]),value:W.string(),description:W.string().optional(),defaultValue:W.string().optional(),confirm:W.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a new environment variable definition"})})});async function ar(a,i){let{schemaName:n}=Zn.parse(a),t=(await i.query("environmentvariabledefinitions",{filter:`schemaname eq '${w(n)}'`,select:["environmentvariabledefinitionid","schemaname","displayname","description","type","defaultvalue","isrequired"],top:1})).value;if(t.length===0)throw new Error(`Environment variable '${n}' not found. Check the schema name (it is case-sensitive, e.g. 'new_MyConfig'). To browse existing variables: Power Apps maker portal \u2192 Solutions \u2192 your solution \u2192 Environment Variables. To create a new one: open a solution \u2192 New \u2192 More \u2192 Environment Variable.`);let r=t[0],o=r.environmentvariabledefinitionid,c=(await i.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${o}`,select:["environmentvariablevalueid","value"],top:1})).value,l=c.length>0?c[0]:null,d=r.type,u=l?l.value??null:null,g=r.defaultvalue??null,m={schemaName:r.schemaname,displayName:r.displayname??"",description:r.description??"",type:d,typeName:Yn[d]??"Unknown",defaultValue:g,currentValue:u,valueId:l?l.environmentvariablevalueid:null,isRequired:r.isrequired??!1,effectiveValue:u??g};return p(`Environment variable '${m.schemaName}': ${m.typeName} = ${m.effectiveValue??"(not set)"}`,m,["Use dataverse_set_environment_variable to update the value"])}async function nr(a,i){let{schemaName:n,value:e}=er.parse(a),r=(await i.query("environmentvariabledefinitions",{filter:`schemaname eq '${w(n)}'`,select:["environmentvariabledefinitionid","schemaname"],top:1})).value;if(r.length===0)throw new Error(`Environment variable definition '${n}' not found. This tool can only update the value of an existing variable. To create a new environment variable: Power Apps maker portal \u2192 Solutions \u2192 your solution \u2192 New \u2192 More \u2192 Environment variable. Then call this tool to set its value.`);let o=r[0].environmentvariabledefinitionid,c=(await i.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${o}`,select:["environmentvariablevalueid","value"],top:1})).value,l,d;c.length>0?(d=c[0].environmentvariablevalueid,await i.updateRecord("environmentvariablevalues",d,{value:e}),l="updated"):(d=await i.createRecord("environmentvariablevalues",{value:e,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${o})`}),l="created");let u={schemaName:n,operation:l,valueId:d,value:e};return p(`Environment variable '${n}' set to new value`,u,["Use dataverse_get_environment_variable to verify the update"])}async function rr(a,i){let{schemaName:n,displayName:e,type:t,value:r,description:o,defaultValue:s}=tr.parse(a),c=X({toolName:"dataverse_create_environment_variable"}).map(y=>`[${y.severity.toUpperCase()}] ${y.code}: ${y.message}`);if((await i.query("environmentvariabledefinitions",{filter:`schemaname eq '${w(n)}'`,select:["environmentvariabledefinitionid"],top:1})).value.length>0)throw new Error(`Environment variable '${n}' already exists. Use dataverse_set_environment_variable to update its value.`);let d=Qn[t]??1e8,u={schemaname:n,displayname:e,type:d};o&&(u.description=o),s!==void 0&&(u.defaultvalue=s);let g=await i.createRecord("environmentvariabledefinitions",u),m=await i.createRecord("environmentvariablevalues",{value:r,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${g})`});return p(`Environment variable '${n}' created (type: ${t}, value: '${r}').`,{schemaName:n,displayName:e,type:t,typeCode:d,definitionId:g,valueId:m,value:r,...c.length>0&&{warnings:c}},["Use dataverse_get_environment_variable to verify","Use dataverse_set_environment_variable to update the value later"])}async function Ut(a,i,n){switch(a){case"dataverse_get_environment_variable":return ar(i,n);case"dataverse_set_environment_variable":return nr(i,n);case"dataverse_create_environment_variable":return rr(i,n);case"dataverse_environment_capabilities":{let[e,t,r,o]=await Promise.allSettled([n.executeFunction("WhoAmI",{}),n.query("organizations",{select:["uniquename","friendlyname","version","languagecode","isauditenabled"],top:1}),n.query("solutions",{filter:"ismanaged eq false",select:["uniquename"]}),n.query("environmentvariabledefinitions",{select:["schemaname"],top:1})]),s=e.status==="fulfilled"?e.value:null,c=t.status==="fulfilled"?t.value.value:[],l=r.status==="fulfilled"?r.value.value:[],d=o.status==="fulfilled"?o.value.value:[];return p("Environment capabilities retrieved",{identity:s,organization:c[0]??null,unmanagedSolutionsCount:l.length,environmentVariablesCount:d.length},["Use dataverse_list_solutions for full solution list","Use dataverse_get_environment_variable to read specific env vars"])}default:throw new Error(`Unknown environment tool: ${a}`)}}import{z as $}from"zod";var ir={0:"Execute",1:"Create",2:"Retrieve",3:"RetrieveMultiple",4:"GetParent",5:"Update",6:"Delete",7:"Assign"},or={0:"Waiting",10:"WaitingForResources",20:"InProgress",21:"Pausing",22:"Canceling",30:"Succeeded",31:"Failed",32:"Canceled"},sr={0:"ReadyToRun",1:"Suspended",2:"Locked",3:"Completed"},lr=["plugintracelogid","typename","messagename","primaryentity","depth","operationtype","exceptiondetails","messageblock","createdon","performanceexecutionduration","correlationid","requestid"],cr=["asyncoperationid","name","operationtype","statuscode","statecode","message","createdon","startedon","completedon"],Pt=[{name:"dataverse_get_plugin_trace_logs",description:"Retrieves recent plugin and custom workflow activity trace logs from Dataverse. Shows execution details including plugin type name, triggering message, entity, execution duration, trace messages written by the developer, and exception details if the plugin failed. Requires the Plugin Trace Log feature to be enabled in Dataverse settings (Settings > Administration > System Settings > Customization tab > 'Enable logging to plugin trace log'). Essential for debugging plugin failures in production. Returns the most recent <tail> entries (default 50). Set tail higher for more, compact:false for full fields. WHEN TO USE: Debugging plugin failures or performance issues in production. BEST PRACTICES: Enable Plugin Trace Log in Dataverse settings first; filter by plugin name or entity to narrow results. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records to return (default 50, max 200)"},tail:{type:"number",description:"Return only the most recent N entries (sorted by createdon DESC). Default 50, max 500. Set higher only when needed."},compact:{type:"boolean",description:"Strip null/empty fields from each log entry to save tokens. Default true; set false for full payload."},pluginTypeFilter:{type:"string",description:"Filter by plugin type name (substring match, e.g. 'AccountValidation')"},messageFilter:{type:"string",description:"Filter by message name (e.g. 'Create', 'Update')"},entityFilter:{type:"string",description:"Filter by entity logical name (e.g. 'account')"},exceptionsOnly:{type:"boolean",description:"Return only traces where an exception occurred (default false)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_workflow_trace_logs",description:"Retrieves background workflow (Power Automate classic / legacy workflow engine) execution records from Dataverse. These are the AsyncOperation records for workflow-type operations, useful for diagnosing failures in background workflows and real-time workflows running asynchronously. Note: For modern cloud flows (Power Automate), use the Power Automate portal instead. Returns the most recent <tail> entries (default 50). Set tail higher for more, compact:false for full fields. WHEN TO USE: Diagnosing failures in classic/legacy background workflows. BEST PRACTICES: Use failedOnly=true to focus on errors; for modern cloud flows use Power Automate portal. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 50, max 200)"},tail:{type:"number",description:"Return only the most recent N entries (sorted by createdon DESC). Default 50, max 500. Set higher only when needed."},compact:{type:"boolean",description:"Strip null/empty fields from each log entry to save tokens. Default true; set false for full payload."},failedOnly:{type:"boolean",description:"Return only failed workflows (statuscode eq 31, default false)"},entityFilter:{type:"string",description:"Filter by regarding entity type (e.g. 'account')"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],dr=$.object({top:$.number().int().positive().max(200).optional().default(50),tail:$.number().int().positive().max(500).optional().default(50),compact:$.boolean().optional().default(!0),pluginTypeFilter:$.string().optional(),messageFilter:$.string().optional(),entityFilter:$.string().optional(),exceptionsOnly:$.boolean().optional().default(!1)}),ur=$.object({top:$.number().int().positive().max(200).optional().default(50),tail:$.number().int().positive().max(500).optional().default(50),compact:$.boolean().optional().default(!0),failedOnly:$.boolean().optional().default(!1),entityFilter:$.string().optional()});function J(a){return typeof a=="string"?a:""}function pr(a){return typeof a=="number"?a:null}async function Mt(a,i,n){let t=(i&&typeof i=="object"?i:{}).top!==void 0;switch(a){case"dataverse_get_plugin_trace_logs":{let r=dr.parse(i),o=[];r.pluginTypeFilter&&o.push(`contains(typename,'${w(r.pluginTypeFilter)}')`),r.messageFilter&&o.push(`messagename eq '${w(r.messageFilter)}'`),r.entityFilter&&o.push(`primaryentity eq '${w(r.entityFilter)}'`),r.exceptionsOnly&&o.push("exceptiondetails ne null");let s=o.length>0?o.join(" and "):void 0,c=t?r.top:r.tail,u=((await n.query("plugintracelogs",{select:lr,orderby:"createdon desc",top:c,...s!==void 0?{filter:s}:{}})).value??[]).map(m=>{let y=typeof m.operationtype=="number"?m.operationtype:0,R=m.exceptiondetails;return{id:J(m.plugintracelogid),typeName:J(m.typename),message:J(m.messagename),entity:J(m.primaryentity),depth:typeof m.depth=="number"?m.depth:0,operationType:y,operationTypeName:ir[y]??String(y),createdOn:J(m.createdon),durationMs:pr(m.performanceexecutionduration),correlationId:J(m.correlationid),requestId:J(m.requestid),hasException:R!=null&&R!=="",exceptionDetails:typeof R=="string"?R:null,messageBlock:typeof m.messageblock=="string"?m.messageblock:null}});u.length>c&&(u=u.slice(0,c)),r.compact&&(u=v(u));let g={total:u.length,logs:u};return p(`${u.length} plugin trace logs found`,g,["Filter by plugin name or correlation ID for specific traces"])}case"dataverse_get_workflow_trace_logs":{let r=ur.parse(i),o=["operationtype eq 10"];r.failedOnly&&o.push("statuscode eq 31");let s=o.join(" and "),c=t?r.top:r.tail,u=((await n.query("asyncoperations",{select:cr,filter:s,orderby:"createdon desc",top:c})).value??[]).map(m=>{let y=typeof m.statuscode=="number"?m.statuscode:0,R=typeof m.statecode=="number"?m.statecode:0,q=m.message;return{id:J(m.asyncoperationid),name:J(m.name),statusCode:y,statusName:or[y]??String(y),stateCode:R,stateName:sr[R]??String(R),createdOn:J(m.createdon),startedOn:typeof m.startedon=="string"?m.startedon:null,completedOn:typeof m.completedon=="string"?m.completedon:null,errorMessage:typeof q=="string"?q:null}});u.length>c&&(u=u.slice(0,c)),r.compact&&(u=v(u));let g={total:u.length,workflows:u};return p(`${u.length} workflow trace logs found`,g,["Filter by status or entity type for specific workflow traces"])}default:throw new Error(`Unknown trace tool: ${a}`)}}import{z as L}from"zod";var Ht=[{name:"dataverse_search",description:"Full-text Relevance Search across all configured Dataverse tables. Returns ranked results with entity name, record ID, score, and matched fields. Requires Relevance Search to be enabled in Dataverse admin settings. Use when you need to find records without knowing which table they belong to. WHEN TO USE: Cross-table free-text search when you don't know which table contains the data. BEST PRACTICES: Narrow with entities[] and $filter; use searchType=full for Lucene operators (AND, OR, NOT, wildcards). WORKFLOW: search_data.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Full-text search string (supports Lucene syntax with searchType=full)"},entities:{type:"array",items:{type:"string"},description:"Restrict to specific table logical names (omit to search all configured tables). Required when using select."},top:{type:"number",description:"Max results (default 10, max 50)"},searchMode:{type:"string",enum:["any","all"],description:"Match any or all terms (default: any)"},searchType:{type:"string",enum:["simple","full"],description:"Search syntax: simple (default) or full (enables Lucene syntax: AND, OR, NOT, wildcards, fuzzy, proximity, regex)"},filter:{type:"string",description:'OData $filter to apply on search results (e.g., "statecode eq 0")'},facets:{type:"array",items:{type:"string"},description:'Fields to return faceted counts for (e.g., ["entityname,count:100","statecode"])'},orderby:{type:"array",items:{type:"string"},description:'Result sort order (e.g., ["@search.score desc","modifiedon desc"])'},select:{type:"array",items:{type:"string"},description:"Columns to return per result. Only applies when entities is also specified."},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["query"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],mr=L.object({query:L.string().min(1),entities:L.array(L.string()).optional(),top:L.number().int().positive().max(50).optional().default(10),searchMode:L.enum(["any","all"]).optional().default("any"),searchType:L.enum(["simple","full"]).optional(),filter:L.string().optional(),facets:L.array(L.string()).optional(),orderby:L.array(L.string()).optional(),select:L.array(L.string()).optional(),compact:L.boolean().optional().default(!0)});async function $t(a,i,n){switch(a){case"dataverse_search":{let e=mr.parse(i),t={search:e.query,top:e.top};if(e.entities?.length){let c=e.entities.map(l=>({name:l,...e.select?.length?{selectColumns:e.select}:{}}));t.entities=JSON.stringify(c)}e.searchType==="full"&&(t.options=JSON.stringify({querytype:"lucene"})),e.filter&&(t.filter=e.filter),e.facets?.length&&(t.facets=JSON.stringify(e.facets)),e.orderby?.length&&(t.orderby=JSON.stringify(e.orderby));let r;try{r=await n.search(t)}catch(c){let l=c instanceof Error?c.message:String(c);if(/404|not.?found|search.{0,20}disabl|relevance.?search|0x80048[Dd]0[Bb]|search.{0,20}not.{0,20}config|search.{0,20}unavail/i.test(l))return ge({type:"feature_disabled",feature:"Dataverse Search (Relevance Search)",cannotProceedBecause:"Relevance Search is not enabled for this Dataverse environment, so full-text cross-table search is unavailable.",adminPortal:"Power Platform Admin Center",steps:["Open Power Platform Admin Center (admin.powerplatform.microsoft.com)","Select your environment \u2192 Settings","Navigate to Product \u2192 Features",'Under "Search", toggle "Dataverse Search" to On',"Save \u2014 indexing may take a few minutes before search is available"]});throw c}let o=(r.value??[]).map(c=>({entityName:c.entityname??"",objectId:c.objectid??"",score:c.score??0,highlights:c.highlights??{},fields:c.attributes??{}}));e.compact&&(o=v(o));let s={totalRecordCount:r.totalrecordcount??0,results:o};return r.facets&&(s.facets=r.facets),p(`${o.length} search results for '${e.query}'`,s,["Use dataverse_get to retrieve full record details","Narrow results with entities[] filter"])}default:throw new Error(`Unknown search tool: ${a}`)}}import{z as te}from"zod";var je={1:"Create",2:"Update",3:"Delete",4:"Activate",5:"Deactivate",11:"Share",12:"Unshare",13:"Assign",104:"Access"},gr=Object.fromEntries(Object.entries(je).map(([a,i])=>[i,Number(a)])),qt=[{name:"dataverse_get_audit_log",description:"Retrieves audit log entries from Dataverse. Returns operation details, user info, and parsed change data for each entry. At least one filter (recordId, entityLogicalName, userId, fromDate, or operations) is recommended to avoid large result sets. Audit must be enabled on the environment and table \u2014 returns a clear error if auditing is disabled (HTTP 403). WHEN TO USE: Tracking who changed what and when on Dataverse records. BEST PRACTICES: Always provide at least one filter; audit must be enabled on the table. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"GUID of a specific record to retrieve audit entries for"},entityLogicalName:{type:"string",description:'Logical name of the entity to filter audit entries (e.g., "account", "contact")'},userId:{type:"string",description:"GUID of the user who made the changes"},fromDate:{type:"string",description:"ISO 8601 date string \u2014 only return audit entries created on or after this date"},top:{type:"number",description:"Maximum number of audit entries to return (default: 50, max: 500)"},operations:{type:"array",items:{type:"string"},description:'Filter by operation names: "Create", "Update", "Delete", "Activate", "Deactivate", "Share", "Unshare", "Assign", "Access"'},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],fr=te.object({recordId:te.string().uuid().optional(),entityLogicalName:te.string().min(1).optional(),userId:te.string().uuid().optional(),fromDate:te.string().datetime({offset:!0}).optional(),top:te.number().positive().max(500).optional().default(50),operations:te.array(te.string().min(1)).optional(),compact:te.boolean().optional().default(!0)});function yr(a){if(!a)return{};try{return JSON.parse(a)}catch{return a}}function hr(a){return{auditId:a.auditid,operation:a.operation,operationName:je[a.operation]??`Unknown(${a.operation})`,action:a.action,actionName:je[a.action]??`Unknown(${a.action})`,createdOn:a.createdon,userId:a._userid_value,userFullName:a.userid?.fullname??"",userDomainName:a.userid?.domainname??"",objectId:a._objectid_value,objectTypeCode:a.objecttypecode,changes:yr(a.changedata)}}async function jt(a,i,n){switch(a){case"dataverse_get_audit_log":{let e=fr.parse(i),t=[];if(e.recordId&&t.push(`_objectid_value eq ${e.recordId}`),e.entityLogicalName){let r=w(e.entityLogicalName);t.push(`objecttypecode eq '${r}'`)}if(e.userId&&t.push(`_userid_value eq ${e.userId}`),e.fromDate&&t.push(`createdon ge ${e.fromDate}`),e.operations?.length){let r=e.operations.map(o=>gr[o]).filter(o=>o!==void 0);if(r.length>0){let o=r.map(s=>`action eq ${s}`).join(" or ");t.push(`(${o})`)}}try{let o=(await n.query("audits",{select:["auditid","action","operation","createdon","_objectid_value","objecttypecode","changedata","_userid_value"],...t.length>0?{filter:t.join(" and ")}:{},orderby:"createdon desc",top:e.top,expand:"userid($select=fullname,domainname)"})).value.map(hr);return e.compact&&(o=v(o)),p(`${o.length} audit records for ${e.entityLogicalName??e.recordId??"query"}`,{entries:o,count:o.length},["Filter by operation type for specific changes"])}catch(r){let o=r instanceof Error?r.message:String(r);if(o.includes("403")||o.includes("Forbidden"))return{content:[{type:"text",text:JSON.stringify({isError:!0,error:"Audit log access denied (HTTP 403). Ensure auditing is enabled on the Dataverse environment and the target table, and that the authenticated user has sufficient privileges."})}]};throw r}}default:throw new Error(`Unknown audit tool: ${a}`)}}import{z as fe}from"zod";var Wt=new Map,Ft=[{name:"dataverse_detect_duplicates",description:"Checks for potential duplicate records before creating, using a FetchXML query that matches records sharing the same field values as the prospective record (OR conditions on the provided fields). Pass the prospective record fields to check against existing records. WHEN TO USE: Before creating a new record to check if a similar record already exists. BEST PRACTICES: Pass only the key identifying fields (e.g., name, email); too many fields will return fewer matches. WORKFLOW: create_record.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Table to check, e.g., "account"'},record:{type:"object",description:"The prospective record fields to check for duplicates"},top:{type:"number",description:"Maximum number of duplicates to return (default 5, max 20)"}},required:["entityLogicalName","record"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],vr=fe.object({entityLogicalName:fe.string().min(1).regex(/^[a-z_][a-z0-9_]*$/i,"Must be a valid Dataverse logical name"),record:fe.record(fe.string(),fe.unknown()),top:fe.number().int().positive().max(20).optional().default(5)});async function Gt(a,i,n){switch(a){case"dataverse_detect_duplicates":{let r=function(g){return String(g).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")};var e=r;let t=vr.parse(i),o=Object.entries(t.record).filter(([,g])=>g!=null).map(([g,m])=>`<condition attribute="${r(g)}" operator="eq" value="${r(String(m))}" />`).join(`
3
- `);if(!o)return p("No fields provided for duplicate detection",{hasDuplicates:!1,duplicateCount:0,duplicates:[]},["Provide at least one field value to check for duplicates"]);let s=Wt.get(t.entityLogicalName);s||(s=(await n.getTableMetadata(t.entityLogicalName,!1)).EntitySetName,Wt.set(t.entityLogicalName,s));let c=`<fetch top="${t.top}" distinct="true">
4
- <entity name="${r(t.entityLogicalName)}">
2
+ var Ir=Object.defineProperty;var x=(r,a)=>()=>(r&&(a=r(r=0)),a);var me=(r,a)=>{for(var t in a)Ir(r,t,{get:a[t],enumerable:!0})};import{z as X}from"zod";var $t,Lt=x(()=>{"use strict";$t=X.object({environmentUrl:X.string().url("Must be a valid Dataverse environment URL").refine(r=>r.startsWith("https://"),{message:"Dataverse environment URL must use HTTPS"}).refine(r=>{try{return new URL(r).hostname.toLowerCase().endsWith(".dynamics.com")}catch{return!1}},{message:"environmentUrl must be a *.dynamics.com host"}),requestTimeoutMs:X.number().positive().default(3e4),maxRetries:X.number().min(0).max(10).default(3),authMethod:X.enum(["device-code","client-credentials","managed-identity"]).default("device-code"),tenantId:X.string().min(1).optional(),clientId:X.string().min(1).optional(),clientSecret:X.string().min(1).optional(),managedIdentityClientId:X.string().min(1).optional()}).superRefine((r,a)=>{r.authMethod==="client-credentials"&&(r.tenantId||a.addIssue({code:X.ZodIssueCode.custom,path:["tenantId"],message:"tenantId is required when authMethod is 'client-credentials'"}),r.clientId||a.addIssue({code:X.ZodIssueCode.custom,path:["clientId"],message:"clientId is required when authMethod is 'client-credentials'"}),r.clientSecret||a.addIssue({code:X.ZodIssueCode.custom,path:["clientSecret"],message:"clientSecret is required when authMethod is 'client-credentials'. Prefer AZURE_CLIENT_SECRET env var over storing it in config.json."}))})});import{createCipheriv as xr,createDecipheriv as Dr,createHash as Or,randomBytes as Pr}from"crypto";function Mt(){let r=[process.env.COMPUTERNAME??process.env.HOSTNAME??"",process.env.USERNAME??process.env.USER??"","mcp-dataverse-cache-v1"].join(".");return Or("sha256").update(r).digest()}function He(r){let a=Mt(),t=Pr(16),e=xr("aes-256-gcm",a,t),n=Buffer.concat([e.update(r,"utf-8"),e.final()]),i={v:1,iv:t.toString("hex"),tag:e.getAuthTag().toString("hex"),d:n.toString("hex")};return JSON.stringify(i)}function je(r){let a=JSON.parse(r);if(a.v!==1)throw new Error("Unknown encrypted-blob format version");let t=Buffer.from(a.iv,"hex"),e=Buffer.from(a.tag,"hex"),n=Buffer.from(a.d,"hex"),i=Dr("aes-256-gcm",Mt(),t);return i.setAuthTag(e),i.update(n).toString("utf-8")+i.final("utf-8")}function qt(r){try{let a=JSON.parse(r);return a.v===1&&typeof a.iv=="string"&&typeof a.tag=="string"&&typeof a.d=="string"}catch{return!1}}var We=x(()=>{"use strict"});var Fe={};me(Fe,{loadConfig:()=>st});import{readFileSync as Ur,existsSync as Ht}from"fs";import{join as jt}from"path";import{homedir as $r}from"os";function st(){let r=jt($r(),".mcp-dataverse",Wt),a=process.env.MCP_CONFIG_PATH??(Ht(r)?r:jt(process.cwd(),Wt)),t={};if(Ht(a)){let m=Ur(a,"utf-8");try{t=JSON.parse(m)}catch{throw new Error(`Invalid JSON in ${a}. Check for syntax errors (trailing commas, missing quotes).`)}}let e=process.env.DATAVERSE_ENV_URL??process.env.environmentUrl;e&&(t.environmentUrl=e);let n=process.env.REQUEST_TIMEOUT_MS??process.env.requestTimeoutMs;n&&(t.requestTimeoutMs=Number(n));let i=process.env.MAX_RETRIES??process.env.maxRetries;i&&(t.maxRetries=Number(i));let s=process.env.AUTH_METHOD??process.env.authMethod;s&&(t.authMethod=s);let o=process.env.AZURE_TENANT_ID??process.env.tenantId;o&&(t.tenantId=o);let l=process.env.AZURE_CLIENT_ID??process.env.clientId;l&&(t.clientId=l);let c=process.env.AZURE_CLIENT_SECRET??process.env.clientSecret;if(c)t.clientSecret=c;else if(typeof t.clientSecret=="string"&&qt(t.clientSecret))try{t.clientSecret=je(t.clientSecret)}catch{throw new Error("Failed to decrypt clientSecret from config.json. The config may have been created on a different machine or user account. Re-run 'npx mcp-dataverse install' or set AZURE_CLIENT_SECRET env var.")}let d=process.env.AZURE_MANAGED_IDENTITY_CLIENT_ID??process.env.managedIdentityClientId;d&&(t.managedIdentityClientId=d);let u=$t.safeParse(t);if(!u.success)throw new Error(`Invalid configuration:
3
+ ${u.error.issues.map(m=>` - ${m.path.join(".")}: ${m.message}`).join(`
4
+ `)}`);return u.data}var Wt,Ce=x(()=>{"use strict";Lt();We();Wt="config.json"});import{PublicClientApplication as Lr}from"@azure/msal-node";import{existsSync as Mr,mkdirSync as qr,readFileSync as Hr,writeFileSync as jr}from"fs";import{homedir as Wr}from"os";import{join as Gt}from"path";import{exec as Fr}from"child_process";import{platform as Ft}from"process";function Bt(r){let a=Ft==="win32"?`echo|set /p="${r}"| clip`:Ft==="darwin"?`printf '%s' '${r}' | pbcopy`:`printf '%s' '${r}' | xclip -selection clipboard 2>/dev/null || printf '%s' '${r}' | xsel --clipboard 2>/dev/null`;Fr(a,()=>{})}function Vr(){return{beforeCacheAccess:async r=>{if(Mr(ot))try{let a=Hr(ot,"utf-8"),t;try{t=je(a)}catch{t=a}r.tokenCache.deserialize(t)}catch{}},afterCacheAccess:async r=>{r.cacheHasChanged&&(qr(Vt,{recursive:!0}),jr(ot,He(r.tokenCache.serialize()),{encoding:"utf-8",mode:384}))}}}var Br,Vt,ot,Gr,Se,ct=x(()=>{"use strict";We();Br="1950a258-227b-4e31-a9cf-717495945fc2",Vt=Gt(Wr(),".mcp-dataverse"),ot=Gt(Vt,"msal-cache.json"),Gr=300*1e3;Se=class{environmentUrl;pca;cachedToken=null;tokenExpiresAt=0;pendingAuth=null;backgroundAuthFlow=null;activeDeviceCode=null;constructor(a){this.environmentUrl=a.replace(/\/$/,""),this.pca=new Lr({auth:{clientId:Br,authority:"https://login.microsoftonline.com/common"},cache:{cachePlugin:Vr()}})}async getToken(){let a=Date.now();return this.cachedToken!==null&&this.tokenExpiresAt>a+6e4?this.cachedToken:this.pendingAuth!==null?this.pendingAuth:(this.pendingAuth=this.refreshToken().finally(()=>{this.pendingAuth=null}),this.pendingAuth)}invalidateToken(){this.cachedToken=null,this.tokenExpiresAt=0}async isAuthenticated(){try{return await this.getToken(),!0}catch{return!1}}async setupViaDeviceCode(){await this.runDeviceCodeFlowBlocking()}async refreshToken(){let a=await this.pca.getAllAccounts();if(a.length>0)try{return await this.acquireSilently()}catch{process.stderr.write(`
5
+ [mcp-dataverse] Session expired \u2014 initiating re-authentication.
6
+ `)}return this.authenticateViaDeviceCode(a.length===0?"first-time":"session-expired")}async authenticateViaDeviceCode(a){if(this.backgroundAuthFlow)try{return await this.acquireSilently()}catch{}let t=null,e=new Promise(n=>{t=n});if(this.backgroundAuthFlow||(this.activeDeviceCode=null,this.backgroundAuthFlow=this.pca.acquireTokenByDeviceCode({scopes:[`${this.environmentUrl}/.default`],deviceCodeCallback:n=>{Bt(n.userCode),this.activeDeviceCode={userCode:n.userCode,verificationUri:n.verificationUri},t?.(),t=null,process.stderr.write(`
7
+ [mcp-dataverse] Sign in required
8
+
9
+ 1. Open ${n.verificationUri} in your browser
10
+ (use the browser profile linked to your Power Platform account)
11
+ 2. Paste the code: ${n.userCode} (already copied to your clipboard)
12
+ 3. Sign in with your work account
13
+
14
+ `)}}).then(n=>{n&&(this.cacheResult(n),process.stderr.write(`
15
+ [mcp-dataverse] Authenticated \u2713 Token cached.
16
+
17
+ `))}).catch(n=>{let i=n instanceof Error?n.message:String(n);process.stderr.write(`
18
+ [mcp-dataverse] Background auth failed: ${i}
19
+ `)}).finally(()=>{this.backgroundAuthFlow=null,this.activeDeviceCode=null}),await Promise.race([e,new Promise(n=>setTimeout(n,6e3))])),this.activeDeviceCode!==null){let{userCode:n,verificationUri:i}=this.activeDeviceCode,s=a==="first-time"?"First-time sign-in required to connect to Dataverse.":"Your Dataverse session has expired. Please sign in again.";throw new Error(`${s}
20
+
21
+ 1. Open this URL in your browser:
22
+ ${i}
23
+
24
+ 2. Enter the code: ${n} (already copied to your clipboard)
25
+
26
+ 3. Sign in with your organizational (work/school) account
27
+
28
+ Authentication is running in the background. Once you have signed in, retry this command.`)}throw new Error(`Authentication required but could not reach the Microsoft sign-in service.
29
+ Check your network connection and retry.
30
+
31
+ Alternatively, pre-authenticate via the terminal:
32
+ npx mcp-dataverse-auth ${this.environmentUrl}
33
+ Then restart the MCP server.`)}async acquireSilently(){let a=await this.pca.getAllAccounts();if(a.length===0)throw new Error("No account found in cache after authentication.");let t=await this.pca.acquireTokenSilent({scopes:[`${this.environmentUrl}/.default`],account:a[0]});if(!t?.accessToken)throw new Error("Token acquisition returned an empty access token.");return this.cacheResult(t),t.accessToken}async runDeviceCodeFlowBlocking(){let a=await Promise.race([this.pca.acquireTokenByDeviceCode({scopes:[`${this.environmentUrl}/.default`],deviceCodeCallback:t=>{Bt(t.userCode),process.stderr.write(`
34
+ [mcp-dataverse] Sign in required
35
+
36
+ 1. Open ${t.verificationUri} in your browser
37
+ (use the browser profile linked to your Power Platform account)
38
+ 2. Paste the code: ${t.userCode} (already copied to your clipboard)
39
+ 3. Sign in with your work account
40
+
41
+ `)}}),new Promise((t,e)=>setTimeout(()=>e(new Error("Authentication timed out after 5 minutes. Please try again.")),Gr))]);a&&(this.cacheResult(a),process.stderr.write(`
42
+ [mcp-dataverse] Authenticated \u2713 Token cached \u2014 no sign-in needed next time.
43
+
44
+ `))}cacheResult(a){this.cachedToken=a.accessToken,this.tokenExpiresAt=a.expiresOn?.getTime()??Date.now()+3300*1e3}}});import{ConfidentialClientApplication as zr}from"@azure/msal-node";var Be,zt=x(()=>{"use strict";Be=class{environmentUrl;cca;scopes;cachedToken=null;tokenExpiresAt=0;pendingAuth=null;constructor(a,t,e,n){this.environmentUrl=a.replace(/\/$/,""),this.scopes=[`${this.environmentUrl}/.default`],this.cca=new zr({auth:{clientId:e,authority:`https://login.microsoftonline.com/${t}`,clientSecret:n}})}async getToken(){let a=Date.now();return this.cachedToken!==null&&this.tokenExpiresAt>a+6e4?this.cachedToken:this.pendingAuth!==null?this.pendingAuth:(this.pendingAuth=this.acquireToken().finally(()=>{this.pendingAuth=null}),this.pendingAuth)}invalidateToken(){this.cachedToken=null,this.tokenExpiresAt=0}async isAuthenticated(){try{return await this.getToken(),!0}catch{return!1}}async acquireToken(){let a;try{a=await this.cca.acquireTokenByClientCredential({scopes:this.scopes})}catch(t){let e=t instanceof Error?t.message:String(t);throw new Error(`Client credentials token acquisition failed: ${e}
45
+ Verify that tenantId, clientId, and clientSecret are correct and that the app registration has been granted access to Dataverse.`)}if(!a?.accessToken)throw new Error("Client credentials flow returned no access token. Ensure the app registration has the Dataverse Application User configured.");return this.cachedToken=a.accessToken,this.tokenExpiresAt=a.expiresOn?a.expiresOn.getTime():Date.now()+36e5,this.cachedToken}}});import{ManagedIdentityApplication as Kr}from"@azure/msal-node";var Ge,Kt=x(()=>{"use strict";Ge=class{environmentUrl;mia;resource;cachedToken=null;tokenExpiresAt=0;pendingAuth=null;constructor(a,t){this.environmentUrl=a.replace(/\/$/,"");let{protocol:e,hostname:n}=new URL(this.environmentUrl);this.resource=`${e}//${n}`,this.mia=new Kr(t?{managedIdentityIdParams:{userAssignedClientId:t}}:{})}async getToken(){let a=Date.now();return this.cachedToken!==null&&this.tokenExpiresAt>a+6e4?this.cachedToken:this.pendingAuth!==null?this.pendingAuth:(this.pendingAuth=this.acquireToken().finally(()=>{this.pendingAuth=null}),this.pendingAuth)}invalidateToken(){this.cachedToken=null,this.tokenExpiresAt=0}async isAuthenticated(){try{return await this.getToken(),!0}catch{return!1}}async acquireToken(){let a;try{a=await this.mia.acquireToken({resource:this.resource})}catch(t){let e=t instanceof Error?t.message:String(t);throw new Error(`Managed Identity token acquisition failed: ${e}
46
+ This method only works on Azure-hosted environments (App Service, Container Apps, VMs, AKS).
47
+ Ensure the managed identity has been assigned an Application User role in Dataverse.`)}if(!a?.accessToken)throw new Error("Managed Identity flow returned no access token. Verify that the managed identity has been added as a Dataverse Application User.");return this.cachedToken=a.accessToken,this.tokenExpiresAt=a.expiresOn?a.expiresOn.getTime():Date.now()+36e5,this.cachedToken}}});var dt={};me(dt,{createAuthProvider:()=>lt});function lt(r){switch(r.authMethod){case"client-credentials":return new Be(r.environmentUrl,r.tenantId,r.clientId,r.clientSecret);case"managed-identity":return new Ge(r.environmentUrl,r.managedIdentityClientId);case"device-code":default:return new Se(r.environmentUrl)}}var Ve=x(()=>{"use strict";ct();zt();Kt()});var ee,ze,ut=x(()=>{"use strict";ee=class extends Error{constructor(t,e,n,i,s={}){super(t);this.status=e;this.data=n;this.code=i;this.responseHeaders=s;this.name="HttpError"}},ze=class{baseURL;timeoutMs;defaultHeaders;tokenProvider;constructor(a){this.baseURL=a.baseURL.endsWith("/")?a.baseURL:a.baseURL+"/",this.timeoutMs=a.timeout??3e4,this.defaultHeaders={...a.headers},this.tokenProvider=a.tokenProvider??void 0}async get(a,t){return this.request("GET",a,void 0,t)}async post(a,t,e){return this.request("POST",a,t,e)}async patch(a,t,e){return this.request("PATCH",a,t,e)}async put(a,t,e){return this.request("PUT",a,t,e)}async delete(a,t){return this.request("DELETE",a,void 0,t)}resolveUrl(a){if(!a.startsWith("http"))return this.baseURL+a;let t=new URL(a),e=new URL(this.baseURL);if(t.origin!==e.origin)throw new ee(`SSRF protection: request to '${t.origin}' blocked; only '${e.origin}' is permitted`,0,void 0,"SSRF_BLOCKED");return a}async request(a,t,e,n){let i=this.resolveUrl(t),s={...this.defaultHeaders,...n?.headers};this.tokenProvider&&(s.Authorization=`Bearer ${await this.tokenProvider()}`);let o=n?.timeoutMs??this.timeoutMs,l=new AbortController,c=setTimeout(()=>l.abort(),o);try{let d={method:a,headers:s,signal:l.signal};e!==void 0&&(d.body=typeof e=="string"?e:JSON.stringify(e));let u=await fetch(i,d),m={};if(u.headers.forEach((y,w)=>{m[w]=y}),!u.ok){let y=await u.text(),w;try{w=JSON.parse(y)}catch{w=y||void 0}throw new ee(`Request failed with status ${u.status}`,u.status,w,void 0,m)}let p;if(n?.responseType==="text")p=await u.text();else{let y=await u.text();p=y?JSON.parse(y):{}}return{data:p,status:u.status,headers:m}}catch(d){throw d instanceof ee?d:d instanceof DOMException&&d.name==="AbortError"?new ee("Request timed out",0,void 0,"ECONNABORTED"):d}finally{clearTimeout(c)}}}});function Xt(r){let a=r.indexOf(`\r
48
+ \r
49
+ `),t=r.indexOf(`
50
+
51
+ `);return a!==-1&&(t===-1||a<=t)?a+4:t!==-1?t+2:-1}function Jt(r,a){let t=[],e=r.split(`--${a}`);for(let n of e){let i=n.trim();if(!i||i==="--")continue;let s=Xt(n);if(s===-1)continue;let o=n.slice(s),l=Xt(o);if(l===-1)continue;let d=(o.trimStart().split(/\r?\n/)[0]??"").match(/^HTTP\/\d+\.\d+\s+(\d{3})/),u=d?parseInt(d[1],10):0,m=u>=200&&u<300,p=o.slice(l).trim();if(!p){t.push(m?null:{error:"Empty response body",status:u});continue}try{let y=JSON.parse(p);t.push(m?y:{error:y,status:u})}catch{t.push({error:p,status:u})}}return t}function h(r){return r.replace(/'/g,"''")}function Yt(r){if(r instanceof ee){let a=r.data?.error;return a?.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: ${a.message??""}. Tip: verify action names via $metadata or use dataverse_resolve_entity_name for entity names.`):a?new Error(`Dataverse error ${a.code??""}: ${a.message??"Unknown error"}`):r.code==="ECONNABORTED"?new Error("Request timed out. Check your Dataverse environment URL."):r}return r instanceof Error?r:new Error(String(r))}var pt,L=x(()=>{"use strict";ut();pt={opportunities:"opportunityid",territories:"territoryid",categories:"categoryid",activityparties:"activitypartyid",activitymimeattachments:"activitymimeattachmentid",queues:"queueid",queueitems:"queueitemid"}});var mt,Ke,Qt=x(()=>{"use strict";ut();L();mt="9.2",Ke=class{http;authProvider;maxRetries;constructor(a,t=3,e=3e4){this.authProvider=a,this.maxRetries=t,this.http=new ze({baseURL:`${a.environmentUrl}/api/data/v${mt}/`,timeout:e,headers:{"OData-MaxVersion":"4.0","OData-Version":"4.0",Accept:"application/json","Content-Type":"application/json; charset=utf-8"},tokenProvider:()=>a.getToken()})}get serviceRootUrl(){return this.http.baseURL}async requestWithRetry(a,t=0){try{return await a()}catch(e){if(e instanceof ee){if(e.status===401&&t===0)return this.authProvider.invalidateToken(),this.requestWithRetry(a,t+1);if([429,503,504].includes(e.status)&&t<this.maxRetries){let i=e.responseHeaders["retry-after"],s=i?parseInt(i,10)*1e3:Math.pow(2,t)*1e3;return await new Promise(o=>setTimeout(o,s)),this.requestWithRetry(a,t+1)}if(e.status===400){let i=e.data?.error?.code;if(i==="0x80071151"&&t<5){let l=Math.pow(2,t)*5e3;return await new Promise(c=>setTimeout(c,l)),this.requestWithRetry(a,t+1)}if(i==="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 s=e.data?.error?.message?.toLowerCase()??"",o=i==="0x80044181"||s.includes("publish")&&(s.includes("already")||s.includes("in progress")||s.includes("another user"));if(o&&t<4)return await new Promise(l=>setTimeout(l,3e4)),this.requestWithRetry(a,t+1);if(o&&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 Yt(e)}}async whoAmI(){return this.requestWithRetry(async()=>{let a=await this.http.get("WhoAmI"),{UserId:t,BusinessUnitId:e,OrganizationId:n}=a.data,i="";try{i=(await this.http.get(`organizations(${n})?$select=name`)).data.name??""}catch{i=""}let s=this.authProvider.environmentUrl;return{UserId:t,BusinessUnitId:e,OrganizationId:n,OrganizationName:i,EnvironmentUrl:s}})}async listTables(a=!1){let n=["$select=LogicalName,SchemaName,DisplayName,EntitySetName,PrimaryIdAttribute,PrimaryNameAttribute,IsCustomEntity,IconVectorName",a?"$filter=IsCustomEntity eq true":""].filter(Boolean).join("&");return this.requestWithRetry(()=>this.http.get(`EntityDefinitions?${n}`).then(i=>i.data.value))}async getTableMetadata(a,t=!0){let e=t?"$expand=Attributes":"",n=`EntityDefinitions(LogicalName='${h(a)}')${e?"?"+e:""}`;return this.requestWithRetry(()=>this.http.get(n).then(i=>i.data))}async fetchAllPagesOData(a){let t=[],e=a;for(;e;){let n=await this.requestWithRetry(()=>this.http.get(e).then(i=>i.data));t.push(...n.value??[]),e=n["@odata.nextLink"]}return t}async getRelationships(a){let t=h(a),[e,n,i]=await Promise.all([this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${t}')/OneToManyRelationships`),this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${t}')/ManyToOneRelationships`),this.fetchAllPagesOData(`EntityDefinitions(LogicalName='${t}')/ManyToManyRelationships`)]);return[...e,...n,...i]}async query(a,t={}){let e=o=>encodeURIComponent(o).replace(/%28/g,"(").replace(/%29/g,")").replace(/%2C/g,",").replace(/%27/g,"'").replace(/%40/g,"@"),n=[];t.select?.length&&n.push(`$select=${t.select.join(",")}`),t.filter&&n.push(`$filter=${e(t.filter)}`),t.orderby&&n.push(`$orderby=${e(t.orderby)}`),t.top&&n.push(`$top=${t.top}`),t.expand&&n.push(`$expand=${t.expand}`),t.count&&n.push("$count=true"),t.apply&&n.push(`$apply=${e(t.apply)}`);let i=`${a}${n.length?"?"+n.join("&"):""}`,s=t.formattedValues?{headers:{Prefer:'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'}}:void 0;return this.requestWithRetry(()=>this.http.get(i,s).then(o=>o.data))}async executeFetchXml(a,t,e){let n=encodeURIComponent(t),i=e?{headers:{Prefer:'odata.include-annotations="OData.Community.Display.V1.FormattedValue"'}}:void 0;return this.requestWithRetry(()=>this.http.get(`${a}?fetchXml=${n}`,i).then(s=>s.data))}async getRecord(a,t,e,n){let i=[];e?.length&&i.push(`$select=${e.join(",")}`),n&&i.push(`$expand=${n}`);let s=i.length?`?${i.join("&")}`:"";return this.requestWithRetry(async()=>{let o=await this.http.get(`${a}(${t})${s}`,{headers:{Prefer:'odata.include-annotations="*"'}}),l=o.headers["odata-etag"]??o.data["@odata.etag"]??null;return{record:o.data,etag:l}})}async createRecord(a,t){return this.requestWithRetry(async()=>{let e=await this.http.post(a,t,{headers:{Prefer:"return=representation"}}),i=e.headers["odata-entityid"]?.match(/\(([^)]+)\)/)?.[1];if(i)return i;let s=e.data,o=s["@odata.id"]?.match(/\(([^)]+)\)/)?.[1];if(o)return o;let l=pt[a]??a.replace(/s$/,"")+"id",c=s[l];return c||(e.headers.location?.match(/\(([^)]+)\)/)?.[1]??"")})}async createWebResource(a){return this.requestWithRetry(async()=>(await this.http.post("webresourceset",a)).headers["odata-entityid"]?.match(/\(([^)]+)\)$/)?.[1]??"")}async updateRecord(a,t,e,n){await this.requestWithRetry(()=>this.http.patch(`${a}(${t})`,e,{headers:{"If-Match":n??"*"}}))}async deleteRecord(a,t){await this.requestWithRetry(()=>this.http.delete(`${a}(${t})`))}async upsertRecord(a,t,e,n,i="upsert",s){return this.requestWithRetry(async()=>{let o=s?`${a}(${s})`:`${a}(${h(t)}='${h(e)}')`,l={Prefer:"return=representation"};i==="createOnly"&&(l["If-None-Match"]="*"),i==="updateOnly"&&(l["If-Match"]="*");try{let c=await this.http.put(o,n,{headers:l}),d=c.status===201?"created":"updated",m=c.headers["odata-entityid"]?.match(/\(([^)]+)\)/)?.[1],p=c.data,y=pt[a]??a.replace(/s$/,"")+"id",w=m??p?.[y]??e;return{operation:d,id:w}}catch(c){if(c instanceof ee&&c.status===412){if(i==="createOnly")throw new Error("Record already exists");if(i==="updateOnly")throw new Error("Record not found")}throw c}})}async associate(a,t,e,n,i){let s=`${this.authProvider.environmentUrl}/api/data/v${mt}/${n}(${i})`;await this.requestWithRetry(()=>this.http.post(`${a}(${t})/${e}/$ref`,{"@odata.id":s}))}async disassociate(a,t,e,n,i){let s=n?`?$id=${this.authProvider.environmentUrl}/api/data/v${mt}/${i??a}(${n})`:"";await this.requestWithRetry(()=>this.http.delete(`${a}(${t})/${e}/$ref${s}`))}}});var Xe,Zt=x(()=>{"use strict";Qt();L();Xe=class extends Ke{async executeAction(a,t={}){return this.requestWithRetry(()=>this.http.post(a,t).then(e=>e.data))}async executeFunction(a,t={}){let e=[],n=[],i=/^[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(([l,c])=>{if(typeof c=="object"&&c!==null){let d=`@${l}`;n.push(`${h(l)}=${d}`),e.push(`${d}=${encodeURIComponent(JSON.stringify(c))}`)}else typeof c=="string"&&i.test(c)?n.push(`${h(l)}=${c}`):typeof c=="string"?n.push(`${h(l)}='${h(c)}'`):n.push(`${h(l)}=${String(c)}`)});let s=n.length?`${a}(${n.join(",")})`:`${a}()`,o=e.length?`${s}?${e.join("&")}`:s;return this.requestWithRetry(()=>this.http.get(o).then(l=>l.data))}async search(a){let e=`${this.http.baseURL.replace(/\/api\/data\/v[\d.]+\/?$/,"")}/api/search/v2.0/query`;return this.requestWithRetry(()=>this.http.post(e,a).then(n=>n.data))}async executeBoundAction(a,t,e,n={}){return this.requestWithRetry(()=>this.http.post(`${a}(${t})/Microsoft.Dynamics.CRM.${e}`,n).then(i=>i.data))}}});var Je,en=x(()=>{"use strict";Zt();L();Je=class extends Xe{async listDependencies(a,t){return this.requestWithRetry(()=>this.http.get(`RetrieveDependenciesForDelete(ComponentType=${a},ObjectId=${t})`).then(e=>e.data.value))}async listTableDependencies(a,t){let e={0:"Workflow",1:"Dialog",2:"BusinessRule",3:"Action",4:"BusinessProcessFlow",5:"Flow"},n={0:"Draft",1:"Active",2:"Inactive"},s=(await this.requestWithRetry(()=>this.http.get(`workflows?$filter=primaryentity eq '${h(a)}' and statecode ne 2&$select=name,workflowid,statecode,category,triggeroncreate,triggerondelete,triggeronupdateattributelist`).then(d=>d.data))).value.map(d=>{let u=[];return d.triggeroncreate&&u.push("Create"),d.triggerondelete&&u.push("Delete"),d.triggeronupdateattributelist&&u.push("Update"),{componentType:e[d.category]??`Category${d.category}`,name:d.name,id:d.workflowid,state:n[d.statecode]??`State${d.statecode}`,triggerEvent:u.length?u.join(","):null,solutionName:null}}),o=t?.length?s.filter(d=>t.includes(d.componentType)):s,c=t?.some(d=>d==="Plugin"||d==="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:a,dependencies:o,count:o.length,warning:c}}async listGlobalOptionSets(){return this.requestWithRetry(()=>this.http.get("GlobalOptionSetDefinitions").then(a=>a.data.value))}async getOptionSet(a){return this.requestWithRetry(()=>this.http.get(`GlobalOptionSetDefinitions(Name='${h(a)}')`).then(t=>t.data))}async getAttributeOptionSet(a,t){let e=["PicklistAttributeMetadata","MultiSelectPicklistAttributeMetadata","StatusAttributeMetadata","StateAttributeMetadata"];for(let n of e)try{let i=`EntityDefinitions(LogicalName='${h(a)}')/Attributes(LogicalName='${h(t)}')/Microsoft.Dynamics.CRM.${n}?$select=LogicalName,DisplayName&$expand=OptionSet`,c=((await this.requestWithRetry(()=>this.http.get(i).then(d=>d.data))).OptionSet?.Options??[]).map(d=>({label:d.Label?.UserLocalizedLabel?.Label??"",value:d.Value}));return{entityLogicalName:a,attributeLogicalName:t,attributeType:n.replace("AttributeMetadata",""),options:c}}catch{continue}throw new Error(`Attribute '${t}' on entity '${a}' is not a Picklist, MultiSelectPicklist, Status, or State attribute, or does not exist.`)}async getEntityKeys(a){return this.requestWithRetry(async()=>(await this.http.get(`EntityDefinitions(LogicalName='${h(a)}')/Keys?$select=SchemaName,LogicalName,KeyAttributes,IsCustomizable,EntityKeyIndexStatus`)).data.value.map(e=>({schemaName:e.SchemaName,logicalName:e.LogicalName,keyAttributes:e.KeyAttributes,isCustomizable:e.IsCustomizable?.Value??!1,indexStatus:e.EntityKeyIndexStatus})))}async updateEntityDefinition(a,t){await this.requestWithRetry(()=>this.http.patch(`EntityDefinitions(LogicalName='${h(a)}')`,t,{headers:{"MSCRM.MergeLabels":"true"}}))}async replaceEntityDefinition(a,t){await this.requestWithRetry(()=>this.http.put(`EntityDefinitions(LogicalName='${h(a)}')`,t,{headers:{"MSCRM.MergeLabels":"true"}}))}async createEntityDefinition(a){return this.requestWithRetry(async()=>(await this.http.post("EntityDefinitions",a)).headers["odata-entityid"]?.match(/\(([^)]+)\)$/)?.[1]??"")}async createAttribute(a,t){return this.requestWithRetry(async()=>(await this.http.post(`EntityDefinitions(LogicalName='${h(a)}')/Attributes`,t,{headers:{Prefer:"return=representation"}})).data.MetadataId??"")}async updateAttribute(a,t,e){await this.requestWithRetry(()=>this.http.put(`EntityDefinitions(LogicalName='${h(a)}')/Attributes(LogicalName='${h(t)}')`,e,{headers:{"MSCRM.MergeLabels":"true"}}))}async deleteAttribute(a,t){await this.requestWithRetry(()=>this.http.delete(`EntityDefinitions(LogicalName='${h(a)}')/Attributes(LogicalName='${h(t)}')`))}async createRelationship(a){return this.requestWithRetry(async()=>(await this.http.post("RelationshipDefinitions",a)).headers["odata-entityid"]?.match(/\(([^)]+)\)$/)?.[1]??"")}}});var Ye,tn=x(()=>{"use strict";en();L();Ye=class extends Je{async batchExecute(a,t=!1){let e=`batch_${Date.now()}`,n="";if(t){let s=`changeset_${Date.now()+1}`,o=a.filter(c=>c.method==="GET"),l=a.filter(c=>c.method!=="GET");for(let c of o)n+=`--${e}\r
52
+ `,n+=`Content-Type: application/http\r
53
+ `,n+=`Content-Transfer-Encoding: binary\r
54
+ \r
55
+ `,n+=`${c.method} ${this.http.baseURL}${c.url} HTTP/1.1\r
56
+ `,n+=`Accept: application/json\r
57
+ \r
58
+ `;if(l.length>0){n+=`--${e}\r
59
+ `,n+=`Content-Type: multipart/mixed; boundary=${s}\r
60
+ \r
61
+ `;let c=1;for(let d of l)n+=`--${s}\r
62
+ `,n+=`Content-Type: application/http\r
63
+ `,n+=`Content-Transfer-Encoding: binary\r
64
+ `,n+=`Content-ID: ${d.contentId??c++}\r
65
+ \r
66
+ `,n+=`${d.method} ${this.http.baseURL}${d.url} HTTP/1.1\r
67
+ `,n+=`Content-Type: application/json\r
68
+ \r
69
+ `,d.body&&(n+=JSON.stringify(d.body)),n+=`\r
70
+ `;n+=`--${s}--\r
71
+ `}}else a.forEach(s=>{n+=`--${e}\r
72
+ `,n+=`Content-Type: application/http\r
73
+ `,n+=`Content-Transfer-Encoding: binary\r
74
+ \r
75
+ `,n+=`${s.method} ${this.http.baseURL}${s.url} HTTP/1.1\r
76
+ `,n+=`Content-Type: application/json\r
77
+ \r
78
+ `,s.body&&(n+=JSON.stringify(s.body)),n+=`\r
79
+ `});n+=`--${e}--`;let i=await this.requestWithRetry(()=>this.http.post("$batch",n,{headers:{"Content-Type":`multipart/mixed;boundary=${e}`},responseType:"text"}));try{let o=(i.headers["content-type"]??"").match(/boundary=(?:"([^"]+)"|([^;"\s]+))/),l=o?.[1]??o?.[2];return l?Jt(i.data,l):(process.stderr.write(`[batchExecute] No multipart boundary in response Content-Type; returning raw data.
80
+ `),[i.data])}catch(s){return process.stderr.write(`[batchExecute] Failed to parse multipart response; returning raw data. ${String(s)}
81
+ `),[i.data]}}}});var nn={};me(nn,{DataverseAdvancedClient:()=>Ie});function gt(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}var Xr,Ie,ft=x(()=>{"use strict";tn();L();Xr={1:"Entity",2:"Attribute",3:"Relationship",9:"OptionSet",29:"Workflow",61:"SystemForm",71:"SiteMap",90:"PluginAssembly",92:"PluginType",97:"WebResource",95:"ServiceEndpoint",79:"ConnectionRole"};Ie=class extends Ye{async executeBoundFunction(a,t,e,n={}){let i=[],s=[];Object.entries(n).forEach(([d,u])=>{if(typeof u=="object"&&u!==null){let m=`@${d}`;s.push(`${h(d)}=${m}`),i.push(`${m}=${encodeURIComponent(JSON.stringify(u))}`)}else typeof u=="string"?s.push(`${h(d)}='${h(u)}'`):s.push(`${h(d)}=${String(u)}`)});let o=s.join(","),l=`${a}(${t})/Microsoft.Dynamics.CRM.${e}(${o})`,c=i.length?`${l}?${i.join("&")}`:l;return this.requestWithRetry(()=>this.http.get(c).then(d=>d.data))}async executeUnboundFunction(a,t={}){let e=[],n=[];Object.entries(t).forEach(([l,c])=>{if(typeof c=="object"&&c!==null){let d=`@${l}`;n.push(`${h(l)}=${d}`),e.push(`${d}=${encodeURIComponent(JSON.stringify(c))}`)}else typeof c=="string"?n.push(`${h(l)}='${h(c)}'`):n.push(`${h(l)}=${String(c)}`)});let i=n.join(","),s=`${a}(${i})`,o=e.length?`${s}?${e.join("&")}`:s;return this.requestWithRetry(()=>this.http.get(o).then(l=>l.data))}async queryWithPaging(a,t={}){let e=Math.min(t.maxTotal??5e3,5e4),n=[],i=0,s={};t.select!==void 0&&(s.select=t.select),t.filter!==void 0&&(s.filter=t.filter),t.orderby!==void 0&&(s.orderby=t.orderby),t.expand!==void 0&&(s.expand=t.expand);let o=await this.query(a,s);for(n.push(...o.value),i++;o["@odata.nextLink"]&&n.length<e;){let c=o["@odata.nextLink"];o=await this.requestWithRetry(()=>this.http.get(c).then(d=>d.data)),n.push(...o.value),i++}let l=n.slice(0,e);return{records:l,totalRetrieved:l.length,pageCount:i}}async getChangedRecords(a,t,e){let n,i={};if(t===null){let m=e?.length?`?$select=${e.join(",")}`:"";n=`${a}${m}`,i.Prefer="odata.track-changes"}else{let m=e?.length?`&$select=${e.join(",")}`:"";n=`${a}?$deltatoken=${t}${m}`}let s=await this.requestWithRetry(()=>this.http.get(n,{headers:i}).then(m=>m.data)),o=s.value??[],l=[],c=[];for(let m of o)if("@removed"in m){let p=String(m["@id"]??""),y=p.match(/\(([^)]+)\)$/);c.push({id:y?y[1]:p})}else l.push(m);let d=s["@odata.deltaLink"],u=null;if(d){let m=d.match(/\$deltatoken=([^&]+)/);u=m?decodeURIComponent(m[1]):null}return{newAndModified:l,deleted:c,nextDeltaToken:u}}async getSolutionComponents(a,t,e=200){return this.requestWithRetry(async()=>{let i=(await this.http.get(`solutions?$filter=uniquename eq '${h(a)}'&$select=solutionid,uniquename,friendlyname,version&$top=1`)).data.value;if(!i.length)throw new Error(`Solution '${a}' not found`);let s=i[0],o=s.solutionid,l=`_solutionid_value eq ${o}`;t!==void 0&&(l+=` and componenttype eq ${t}`);let d=(await this.http.get(`solutioncomponents?$filter=${l}&$select=componenttype,objectid&$top=${e}&$orderby=componenttype`)).data.value.map(u=>({componentType:u.componenttype,componentTypeName:Xr[u.componenttype]??`Type${u.componenttype}`,objectId:u.objectid}));return{solutionName:s.uniquename,solutionId:o,friendlyName:s.friendlyname,version:s.version,components:d,count:d.length}})}async publishCustomizations(a){try{return await this.requestWithRetry(async()=>{if(!(a&&((a.entities?.length??0)>0||(a.webResources?.length??0)>0||(a.optionSets?.length??0)>0)))return await this.http.post("PublishAllXml",{},{timeoutMs:12e4}),{published:!0,message:"All customizations published successfully"};let e="<importexportxml>";return a.entities?.length&&(e+="<entities>"+a.entities.map(n=>`<entity>${gt(n)}</entity>`).join("")+"</entities>"),a.webResources?.length&&(e+="<webresources>"+a.webResources.map(n=>`<webresource>${gt(n)}</webresource>`).join("")+"</webresources>"),a.optionSets?.length&&(e+="<optionsets>"+a.optionSets.map(n=>`<optionset>${gt(n)}</optionset>`).join("")+"</optionsets>"),e+="</importexportxml>",await this.http.post("PublishXml",{ParameterXml:e},{timeoutMs:12e4}),{published:!0,message:"Selected customizations published successfully"}})}catch(t){if((t instanceof Error?t.message:String(t)).includes("[TRANSIENT_LOCK_EXHAUSTED]")&&(a?.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(a){return this.requestWithRetry(async()=>{let t=[];a.category!==void 0&&t.push(`category eq ${a.category}`),a.nameContains&&t.push(`contains(name,'${h(a.nameContains)}')`);let e=`workflows?$select=workflowid,name,description,category,statecode,statuscode,type,modifiedon&$orderby=name asc&$top=${a.top??50}`;return t.length>0&&(e+=`&$filter=${t.join(" and ")}`),(await this.http.get(e)).data.value??[]})}async getDataverseWorkflow(a){return this.requestWithRetry(()=>this.http.get(`workflows(${a})?$select=workflowid,name,description,category,statecode,statuscode,type,modifiedon`).then(t=>t.data))}async executeFetchXmlAllPages(a,t){let e=[],n=1,i;do{let s=t;n>1&&(s=s.replace(/<fetch\b([^>]*)>/i,(d,u)=>{let m=u.replace(/\s+page="[^"]*"/g,"").replace(/\s+paging-cookie="[^"]*"/g,""),p=i?` paging-cookie="${i.replace(/"/g,"&quot;").replace(/'/g,"&apos;")}"`:"";return`<fetch${m} page="${n}"${p}>`}));let o=await this.executeFetchXml(a,s),l=o.value??[];e.push(...l);let c=o["@Microsoft.Dynamics.CRM.fetchxmlpagingcookie"];if(!c||l.length===0)break;try{let u=decodeURIComponent(decodeURIComponent(String(c))).match(/pagingcookie="([^"]+)"/);i=u?u[1]:void 0}catch{i=String(c)}if(!i||(n++,n>100))break}while(!0);return e}}});var Xa={};me(Xa,{createEntraJwtValidator:()=>Oo});import{createRemoteJWKSet as xo,jwtVerify as Do}from"jose";function Oo(r){let{tenantId:a,audience:t,requiredScope:e}=r,n=`https://login.microsoftonline.com/${a}/v2.0`,i=new URL(`https://login.microsoftonline.com/${a}/discovery/v2.0/keys`),s=xo(i,{cooldownDuration:3e4,cacheMaxAge:10*6e4});return async function(l){let c;try{c=(await Do(l,s,{issuer:n,audience:t,algorithms:["RS256"],clockTolerance:30})).payload}catch(u){let m=u instanceof Error?u.message:String(u);try{let p=l.split(".");if(p.length===3){let y=JSON.parse(Buffer.from(p[1],"base64url").toString("utf8"));process.stderr.write(`[entra-jwt] validation failed: ${m} | aud=${JSON.stringify(y.aud)} iss=${y.iss} scp="${y.scp}" ver=${y.ver}
82
+ `)}}catch{}throw new Error(`Entra JWT validation failed: ${m}`)}if(e){let u=c.scp??"";if(!u.split(" ").includes(e))throw new Error(`Required scope '${e}' not present in token scp claim ('${u}')`)}let d=c;return{oid:d.oid??"",upn:d.upn??d.preferred_username??"",name:d.name??""}}}var Ja=x(()=>{"use strict"});var Qa={};me(Qa,{startHttpTransport:()=>qo});import{createServer as Po}from"http";import{timingSafeEqual as Uo,createHmac as Ya,randomUUID as $o}from"crypto";import{StreamableHTTPServerTransport as Lo}from"@modelcontextprotocol/sdk/server/streamableHttp.js";function Mo(r,a){try{let t=Buffer.from("mcp-token-verify-constant"),e=Ya("sha256",t).update(r).digest(),n=Ya("sha256",t).update(a).digest();return Uo(e,n)}catch{return!1}}async function qo(r,a,t,e){let n=process.env.MCP_HTTP_JSON_RESPONSE!=="false",i=process.env.MCP_HTTP_SECRET??process.env.BEARER_TOKEN,s=process.env.MCP_HTTP_CORS_ORIGIN??"*",o=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,l=null;if(o){let{createEntraJwtValidator:p}=await Promise.resolve().then(()=>(Ja(),Xa));l=p(o),process.stderr.write(`[http-server] Auth mode: entra-jwt (tenant=${o.tenantId}, audience=${o.audience})
83
+ `)}else process.stderr.write(i?`[http-server] Auth mode: static bearer token
84
+ `:`[http-server] WARNING: No auth configured \u2014 all /mcp requests accepted
85
+ `);let c=(process.env.MCP_PUBLIC_URL??`http://localhost:${a}`).replace(/\/$/,""),d=new Map,u=Po(async(p,y)=>{if(y.setHeader("Access-Control-Allow-Origin",s),y.setHeader("Access-Control-Allow-Methods","GET, POST, DELETE, OPTIONS"),y.setHeader("Access-Control-Allow-Headers","Content-Type, mcp-session-id, Authorization"),p.method==="OPTIONS"){y.writeHead(204),y.end();return}let w=new URL(p.url??"/",`http://localhost:${a}`);if(w.pathname==="/health"&&p.method==="GET"){y.writeHead(200,{"Content-Type":"application/json"}),y.end(JSON.stringify({status:"ok",version:t,tools:e}));return}if(w.pathname==="/.well-known/oauth-protected-resource"&&p.method==="GET"){let O=o?.tenantId??"";y.writeHead(200,{"Content-Type":"application/json","Cache-Control":"max-age=3600"});let _=o?.clientId??"";y.end(JSON.stringify({resource:`${c}/mcp`,authorization_servers:O?[`https://login.microsoftonline.com/${O}/v2.0`]:[],bearer_methods_supported:["header"],scopes_supported:_?[`api://${_}/DataverseAccess`]:[],resource_documentation:"https://github.com/aileron-split/mcp-dataverse"}));return}if(w.pathname==="/mcp"){let O=p.headers.authorization??"",_=O.startsWith("Bearer ")?O.slice(7):"",M=`${c}/.well-known/oauth-protected-resource`,pe=o?`Bearer resource_metadata="${M}", realm="MCP Dataverse"`:'Bearer realm="MCP Dataverse"';if(l){if(!_){y.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":pe}),y.end(JSON.stringify({error:"Unauthorized"}));return}try{await l(_)}catch(Z){let b=Z instanceof Error?Z.message:String(Z);process.stderr.write(`[http-server] JWT rejected: ${b}
86
+ `),y.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":pe}),y.end(JSON.stringify({error:"Unauthorized"}));return}}else if(i&&!Mo(_,i)){y.writeHead(401,{"Content-Type":"application/json","WWW-Authenticate":pe}),y.end(JSON.stringify({error:"Unauthorized"}));return}try{let Z=p.headers["mcp-session-id"];if(Z){let b=d.get(Z);if(!b){y.writeHead(404,{"Content-Type":"application/json"}),y.end(JSON.stringify({error:"Session not found. Please reinitialize."}));return}await b.handleRequest(p,y)}else{if(p.method!=="POST"){y.writeHead(400,{"Content-Type":"application/json"}),y.end(JSON.stringify({error:"mcp-session-id header required for GET and DELETE"}));return}let b=new Lo({sessionIdGenerator:()=>$o(),enableJsonResponse:n});b.sessionId&&(d.set(b.sessionId,b),b.onclose=()=>{b.sessionId&&d.delete(b.sessionId)}),await r().connect(b),await b.handleRequest(p,y),b.sessionId&&(d.has(b.sessionId)||(d.set(b.sessionId,b),b.onclose=()=>{b.sessionId&&d.delete(b.sessionId)}))}}catch(Z){process.stderr.write(`[http-server] Unhandled error: ${String(Z)}
87
+ `),y.headersSent||(y.writeHead(500,{"Content-Type":"application/json"}),y.end(JSON.stringify({error:"Internal server error"})))}return}y.writeHead(404,{"Content-Type":"application/json"}),y.end(JSON.stringify({error:"Not found"}))});await new Promise(p=>{u.listen(a,"0.0.0.0",()=>{process.stderr.write(`MCP Dataverse HTTP server listening on http://0.0.0.0:${a}/mcp
88
+ `),p()})});let m=async()=>{process.stderr.write(`Shutting down HTTP server...
89
+ `);for(let p of d.values())await p.close();u.close(),process.exit(0)};process.on("SIGINT",m),process.on("SIGTERM",m),await new Promise(p=>{u.on("close",p)})}var Za=x(()=>{"use strict"});import{execFileSync as tr}from"child_process";import{existsSync as er,readFileSync as Ho,readdirSync as jo,writeFileSync as Wo}from"fs";import{homedir as Fo}from"os";import{join as ue}from"path";function nr(r){let a=JSON.stringify({name:"mcp-dataverse",type:"stdio",command:"npx",args:["-y","mcp-dataverse"],env:{MCP_CONFIG_PATH:r}}),t=[{label:"VS Code Insiders",bin:"code-insiders"},{label:"VS Code Stable",bin:"code"}],e=[];for(let{label:n,bin:i}of t)try{let s=process.platform==="win32"?["/c",i,"--add-mcp",a]:["--add-mcp",a],o=process.platform==="win32"?"cmd.exe":i;tr(o,s,{stdio:"pipe",timeout:15e3}),e.push({label:n,ok:!0})}catch{e.push({label:n,ok:!1})}return e}function ar(r){let a=JSON.stringify({name:"mcp-dataverse",type:"http",url:r}),t=[{label:"VS Code Insiders",bin:"code-insiders"},{label:"VS Code Stable",bin:"code"}],e=[];for(let{label:n,bin:i}of t)try{let s=process.platform==="win32"?["/c",i,"--add-mcp",a]:["--add-mcp",a],o=process.platform==="win32"?"cmd.exe":i;tr(o,s,{stdio:"pipe",timeout:15e3}),e.push({label:n,ok:!0})}catch{e.push({label:n,ok:!1})}return e}function Bo(){let r=Fo();switch(process.platform){case"win32":{let a=process.env.APPDATA??ue(r,"AppData","Roaming");return[{label:"VS Code Insiders",dir:ue(a,"Code - Insiders","User")},{label:"VS Code Stable",dir:ue(a,"Code","User")}]}case"darwin":return[{label:"VS Code Insiders",dir:ue(r,"Library","Application Support","Code - Insiders","User")},{label:"VS Code Stable",dir:ue(r,"Library","Application Support","Code","User")}];default:return[{label:"VS Code Insiders",dir:ue(r,".config","Code - Insiders","User")},{label:"VS Code Stable",dir:ue(r,".config","Code","User")}]}}function Pt(r){let a=[];for(let{dir:t}of Bo()){let e=ue(t,"profiles");if(!er(e))continue;let n;try{n=jo(e)}catch{continue}for(let i of n){let s=ue(e,i,"mcp.json");if(er(s))try{let o=Ho(s,"utf-8"),l=JSON.parse(o);l.servers||(l.servers={}),l.servers["mcp-dataverse"]=r,Wo(s,JSON.stringify(l,null," ")+`
90
+ `,"utf-8"),a.push(s)}catch{}}}return a}function rr(r){if(!r.startsWith("https://"))return"URL must start with https://";try{if(!new URL(r).hostname.toLowerCase().endsWith(".dynamics.com"))return"Must be a *.dynamics.com URL (your Power Platform environment)"}catch{return"Invalid URL format"}return null}function ir(r){g(" Add this to your VS Code mcp.json (Ctrl+Shift+P \u2192 MCP: Open User Configuration):"),g(""),g(' "mcp-dataverse": {'),g(' "type": "http",'),g(` "url": "${r}"`),g(" }")}function sr(r){g(" Add this to your VS Code mcp.json (Ctrl+Shift+P \u2192 MCP: Open User Configuration):"),g(""),g(' "mcp-dataverse": {'),g(' "type": "stdio",'),g(' "command": "npx",'),g(' "args": ["-y", "mcp-dataverse"],'),g(` "env": { "MCP_CONFIG_PATH": "${r.replace(/\\/g,"\\\\")}" }`),g(" }")}var g,se,or=x(()=>{"use strict";g=r=>{process.stdout.write(r+`
91
+ `)},se=()=>{process.stdout.write("\u2500".repeat(58)+`
92
+ `)}});var dr={};me(dr,{runInstall:()=>Xo});import{createInterface as Go}from"readline/promises";import{mkdirSync as Vo,writeFileSync as zo}from"fs";import{homedir as Ko}from"os";import{join as cr}from"path";async function Xo(){g(""),g("\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"),g("\u2551 MCP Dataverse \u2014 Interactive Setup \u2551"),g("\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"),g("");let r=Go({input:process.stdin,output:process.stdout});g("Step 1 / 2 \u2014 Connection Mode"),se(),g(" How will you use the MCP Dataverse server?"),g(""),g(" 1) HTTP (hosted server) The server is already deployed on Azure."),g(" You just need its endpoint URL."),g(" \u2192 Dev connects via HTTPS, VS Code handles auth."),g(""),g(" 2) stdio (local server) The server runs on this machine via npx."),g(" You need a Dataverse URL + credentials."),g("");let a="http";for(;;){let c=(await r.question("Select [1/2] default 1: ")).trim();if(!c||c==="1"){a="http";break}if(c==="2"){a="stdio";break}g(` \u2717 Please enter 1 or 2.
93
+ `)}if(g(` \u2713 ${a==="http"?"HTTP (hosted)":"stdio (local)"}`),g(""),a==="http"){g("Step 2 / 2 \u2014 MCP Server Endpoint"),se(),g(" The HTTPS URL of your hosted MCP Dataverse server."),g(" e.g. https://mcp-dataverse-api.azurewebsites.net/mcp"),g(""),g(" Authentication against the server is handled automatically by VS Code"),g(" using your signed-in Microsoft account (Entra ID)."),g("");let c="";for(;!c;){let y=(await r.question(`Server URL
94
+ \u203A `)).trim().replace(/\/$/,"");if(!y.startsWith("https://")){g(` \u2717 URL must start with https://
95
+ `);continue}try{new URL(y)}catch{g(` \u2717 Invalid URL format.
96
+ `);continue}c=y}g(` \u2713 ${c}`),g(""),r.close(),g("Registering in VS Code\u2026");let d=ar(c),u=d.filter(y=>y.ok);if(u.length>0){for(let{label:y}of u)g(` \u2713 ${y}`);for(let{label:y}of d.filter(w=>!w.ok))g(` \u2013 ${y}: not found on PATH (skipped)`)}else g(" \u26A0 Neither 'code' nor 'code-insiders' found on PATH."),g(" Register manually:"),g(""),ir(c);let p=Pt({type:"http",url:c});p.length>0&&g(` + Patched ${p.length} custom VS Code profile(s)`),se(),g(""),g(" \u2713 Setup complete!"),g(""),g(" Restart VS Code \u2014 the MCP Dataverse tools will be available in Copilot chat."),g(" VS Code will sign you in automatically with your Microsoft account."),g("");return}g("Step 2 / 3 \u2014 Dataverse Environment"),se(),g(" The URL of your Power Platform / Dataverse environment."),g(" Find it in https://admin.powerplatform.microsoft.com \u2192 Environments \u2192 <your env>."),g("");let t="";for(;!t;){let c=(await r.question(`Environment URL (e.g. https://contoso.crm.dynamics.com)
97
+ \u203A `)).trim().replace(/\/$/,""),d=rr(c);if(d){g(` \u2717 ${d}
98
+ `);continue}t=c}g(` \u2713 ${t}`),g(""),g("Step 3 / 3 \u2014 Authentication Method"),se(),g(" 1) Device Code Sign in with your Microsoft account in a browser"),g(" Recommended for individual developers"),g(""),g(" 2) Client Credentials Use an Azure App Registration (service / CI/CD)"),g(" Requires Tenant ID \xB7 Client ID \xB7 Client Secret"),g(""),g(" \u2139 Need Managed Identity? Go back and choose 'On Azure / hosted' (option 1)."),g(" Managed Identity only works when the server runs on Azure \u2014 not locally."),g("");let e="device-code";for(;;){let c=(await r.question("Select [1/2] default 1: ")).trim();if(!c||c==="1"){e="device-code";break}if(c==="2"){e="client-credentials";break}g(` \u2717 Please enter 1 or 2.
99
+ `)}g(` \u2713 ${e}`),g("");let n={environmentUrl:t,authMethod:e};if(e==="client-credentials"){g("Step 4 / 4 \u2014 App Registration Details"),se(),g(" Enter the values from Azure Portal \u2192 App registrations \u2192 your app."),g(" You can also supply these as environment variables (recommended for secrets)."),g("");let c="";for(;!c;)c=(await r.question(`Tenant ID (env: AZURE_TENANT_ID)
100
+ \u203A `)).trim(),c||g(` \u2717 Required.
101
+ `);let d="";for(;!d;)d=(await r.question(`Client ID (env: AZURE_CLIENT_ID)
102
+ \u203A `)).trim(),d||g(` \u2717 Required.
103
+ `);g(""),g(" Client Secret: leave blank to provide it via env var AZURE_CLIENT_SECRET"),g(" (safer \u2014 avoids storing the secret in config.json)."),g("");let u=(await r.question(`Client Secret (env: AZURE_CLIENT_SECRET) [Enter to skip]
104
+ \u203A `)).trim();n.tenantId=c,n.clientId=d,u?(n.clientSecret=He(u),g(""),g(" \u2713 Secret encrypted (AES-256-GCM, machine-bound key) and stored in config.json."),g(" \u26A0 Do not commit config.json to version control.")):(g(""),g(" \u2192 Set AZURE_CLIENT_SECRET in your environment or in the 'env' block of mcp.json.")),g("")}r.close(),g("Saving configuration\u2026"),Vo(lr,{recursive:!0}),zo(Me,JSON.stringify(n,null,2)+`
105
+ `,{encoding:"utf-8",mode:384}),g(` \u2713 ${Me}`),g(""),g("Registering in VS Code\u2026");let i={type:"stdio",command:"npx",args:["-y","mcp-dataverse"],env:{MCP_CONFIG_PATH:Me}},s=nr(Me),o=s.filter(c=>c.ok);if(o.length>0){for(let{label:c}of o)g(` \u2713 ${c}`);for(let{label:c}of s.filter(d=>!d.ok))g(` \u2013 ${c}: not found on PATH (skipped)`)}else g(" \u26A0 Neither 'code' nor 'code-insiders' found on PATH."),g(" Register manually:"),g(""),sr(Me);let l=Pt(i);if(l.length>0&&g(` + Patched ${l.length} custom VS Code profile(s)`),g(""),se(),e!=="device-code"){g(""),g(" \u2713 Setup complete!"),g(""),g(" Restart VS Code to activate the MCP server."),e==="client-credentials"&&!n.clientSecret&&g(" Remember to set AZURE_CLIENT_SECRET before starting the server."),g("");return}g("Authenticating with Microsoft\u2026"),g("(Open the URL shown below and enter the code to complete sign-in)"),g("");try{await new Se(t).setupViaDeviceCode(),se(),g(""),g(" \u2713 Setup complete!"),g(""),o.length>0?(g(" The MCP server is registered in VS Code."),g(" Open VS Code \u2192 Copilot chat \u2014 your Dataverse tools are ready.")):(g(" Config and authentication saved."),g(" Register the server manually (see snippet above), then restart VS Code.")),g("")}catch(c){let d=c instanceof Error?c.message:String(c);se(),g(` \u2717 Authentication failed: ${d}`),g(""),g(" Configuration was saved. Run 'npx mcp-dataverse install' again to retry."),g(""),process.exit(1)}}var lr,Me,ur=x(()=>{"use strict";ct();We();or();lr=cr(Ko(),".mcp-dataverse"),Me=cr(lr,"config.json")});var hr={};me(hr,{runUpdate:()=>Qo});import{existsSync as pr,readFileSync as mr}from"fs";import{join as at}from"path";import{homedir as Jo}from"os";import{fileURLToPath as gr}from"url";function yr(){return"\u2500".repeat(58)}async function Qo(){let r=at(gr(import.meta.url),"..","..","package.json"),t=JSON.parse(mr(r,"utf-8")).version;process.stdout.write(`
106
+ `),process.stdout.write(`\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
107
+ `),process.stdout.write(`\u2551 MCP Dataverse \u2014 Update \u2551
108
+ `),process.stdout.write(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
109
+ `),process.stdout.write(`
110
+ `),process.stdout.write(` Current version : v${t}
111
+ `),process.stdout.write(` Installed via : npx (auto-fetches latest on each run)
112
+ `),process.stdout.write(`
113
+ `),pr(fr)?(process.stdout.write(` Configuration : \u2705 Found
114
+ `),process.stdout.write(` Config path : ${fr}
115
+ `)):(process.stdout.write(` Configuration : \u274C Not found
116
+ `),process.stdout.write(` \u2192 Run 'npx mcp-dataverse install' to set up.
117
+ `),process.stdout.write(`
118
+ `),process.exit(0)),process.stdout.write(`
119
+ ${yr()}
120
+
121
+ `);let e=at(gr(import.meta.url),"..","..","CHANGELOG.md");if(pr(e)){let i=mr(e,"utf-8").match(/## \[([\d.]+)\].*?\n\n([\s\S]*?)(?=\n## \[|$)/);if(i){process.stdout.write(`What's new in v${i[1]}:
122
+
123
+ `);let s=i[2].split(`
124
+ `).filter(o=>o.startsWith("- **"));for(let o of s)process.stdout.write(` ${o}
125
+ `)}}process.stdout.write(`
126
+ ${yr()}
127
+
128
+ `),process.stdout.write(` \u2139 npx always uses the latest published version.
129
+ `),process.stdout.write(` No explicit upgrade needed \u2014 just restart your MCP client.
130
+ `),process.stdout.write(`
131
+ `),process.stdout.write(` For full details: https://codeurali.github.io/mcp-dataverse
132
+ `),process.stdout.write(`
133
+ `)}var Yo,fr,vr=x(()=>{"use strict";Yo=at(Jo(),".mcp-dataverse"),fr=at(Yo,"config.json")});var br={};me(br,{runDoctor:()=>Zo});async function Zo(){let r=s=>process.stdout.write(s+`
134
+ `),a=s=>r(` \u2705 ${s}`),t=s=>r(` \u274C ${s}`);r(""),r("\u{1F3E5} mcp-dataverse doctor"),r("\u2500".repeat(50)),r("");let e=!0;r("\u{1F4CB} Environment");let n=process.version;parseInt(n.slice(1).split(".")[0],10)>=20?a(`Node.js ${n}`):(t(`Node.js ${n} \u2014 requires v20+`),e=!1),r(""),r("\u2699\uFE0F Configuration");try{let{loadConfig:s}=await Promise.resolve().then(()=>(Ce(),Fe)),o=s();a(`Environment URL: ${o.environmentUrl}`),a(`Auth method: ${o.authMethod??"device-code"}`),a(`Timeout: ${o.requestTimeoutMs}ms, Retries: ${o.maxRetries}`)}catch(s){let o=s instanceof Error?s.message:String(s);t(`Configuration error: ${o}`),e=!1,r(""),r(e?"\u2705 All checks passed!":"\u274C Some checks failed."),process.exit(e?0:1)}r(""),r("\u{1F511} Authentication");try{let{loadConfig:s}=await Promise.resolve().then(()=>(Ce(),Fe)),o=s(),l=o.authMethod??"device-code";a(`Auth method: ${l}`),l==="client-credentials"?(a(`Tenant ID : ${o.tenantId}`),a(`Client ID : ${o.clientId}`),o.clientSecret&&!process.env.AZURE_CLIENT_SECRET&&!process.env.clientSecret?r(" \u26A0\uFE0F clientSecret is stored in config file \u2014 prefer AZURE_CLIENT_SECRET env var for production"):a("Client Secret: supplied via env var")):l==="managed-identity"&&(o.managedIdentityClientId?a(`Identity type: user-assigned (clientId: ${o.managedIdentityClientId})`):a("Identity type: system-assigned"));let{createAuthProvider:c}=await Promise.resolve().then(()=>(Ve(),dt)),u=await c(o).getToken();u?(a("Token acquired successfully"),u.split(".").length===3&&a("Valid JWT token format")):(t("No token returned"),e=!1)}catch(s){let o=s instanceof Error?s.message:String(s);t(`Auth failed: ${o}`),e=!1}r(""),r("\u{1F310} Dataverse API");try{let{loadConfig:s}=await Promise.resolve().then(()=>(Ce(),Fe)),o=s(),{createAuthProvider:l}=await Promise.resolve().then(()=>(Ve(),dt)),{DataverseAdvancedClient:c}=await Promise.resolve().then(()=>(ft(),nn)),d=l(o),m=await new c(d,o.maxRetries,o.requestTimeoutMs).whoAmI();a(`Organization: ${m.OrganizationName||"N/A"}`),a(`User ID: ${m.UserId||"N/A"}`),a(`Business Unit: ${m.BusinessUnitId||"N/A"}`),a(`Environment: ${m.EnvironmentUrl||o.environmentUrl}`)}catch(s){let o=s instanceof Error?s.message:String(s);t(`API call failed: ${o}`),e=!1}r(""),r("\u2500".repeat(50)),r(e?"\u2705 All checks passed! Your mcp-dataverse setup is healthy.":"\u274C Some checks failed. Review the errors above."),r(""),process.exit(e?0:1)}var _r=x(()=>{"use strict"});import{Server as ec}from"@modelcontextprotocol/sdk/server/index.js";import{StdioServerTransport as tc}from"@modelcontextprotocol/sdk/server/stdio.js";import{CallToolRequestSchema as nc,ListToolsRequestSchema as ac,ListResourcesRequestSchema as rc,ListResourceTemplatesRequestSchema as ic,ReadResourceRequestSchema as sc,ListPromptsRequestSchema as oc,GetPromptRequestSchema as cc}from"@modelcontextprotocol/sdk/types.js";import{RESOURCE_MIME_TYPE as oe}from"@modelcontextprotocol/ext-apps/server";import{readFileSync as lc,existsSync as H}from"fs";import{readFile as qe}from"node:fs/promises";import{join as dc,dirname as uc,resolve as D}from"path";import{fileURLToPath as pc}from"url";function Ut(){let r=process.argv.slice(2),a="stdio",t=3e3;for(let e=0;e<r.length;e++){if(r[e]==="--transport"&&r[e+1]){let n=r[e+1];(n==="http"||n==="stdio")&&(a=n),e++}if(r[e]==="--port"&&r[e+1]){let n=parseInt(r[e+1],10);!isNaN(n)&&n>0&&n<65536&&(t=n),e++}}return{transport:a,port:t}}Ce();Ve();ft();var yt=class{map=new Map;register(a){for(let t of a.tools){if(this.map.has(t.name))throw new Error(`Duplicate tool name: ${t.name}`);this.map.set(t.name,{definition:t,handler:a.handler})}}getHandler(a){return this.map.get(a)?.handler}getAllDefinitions(){return Array.from(this.map.values()).map(a=>a.definition)}has(a){return this.map.has(a)}get size(){return this.map.size}};function an(r){let a=new yt;for(let t of r)a.register(t);return a}var xe=class{server;token;constructor(a,t){this.server=a??null,this.token=t??null}async report(a,t){if(!(!this.server||this.token==null))try{await this.server.notification({method:"notifications/progress",params:{progressToken:this.token,progress:a,total:t}})}catch{}}},hl=new xe;function j(r){let a=r.errorCategory!==void 0,t=r.data!==null&&r.data!==void 0?vt(r.data):r.data,e=t===r.data?r:{...r,data:t},n=a?{...e,isError:!0,content:[{type:"text",text:e.summary}]}:e,i=JSON.stringify(n);if(Buffer.byteLength(i,"utf8")>9e5){let o={summary:r.summary,data:null,warnings:[...r.warnings??[],`Result exceeded the ${Math.round(900)} KB safety limit and was dropped to avoid hitting the Claude Desktop 1 MB tool-result cap.`],suggestions:[...r.suggestions??[],"Re-run with $select to return only the columns you need.","Reduce the page size: pass $top (or `top`) \u2014 typical safe value is 25\u201350 rows.","For large result sets use dataverse_retrieve_multiple_with_paging (cursor-based) instead of a single big query.","If you need every row, page through results in a loop on the client side."],errorCategory:"ENV_LIMITATION"};return i=JSON.stringify({...o,isError:!0,content:[{type:"text",text:o.summary}]}),{content:[{type:"text",text:i}],isError:!0}}let s={content:[{type:"text",text:i}]};return a&&(s.isError=!0),s}function f(r,a,t,e){return j({summary:r,data:a,suggestions:t,warnings:e})}function ge(r,a,t){return j({summary:`${a.length} ${r} found.`,data:a,suggestions:t})}function Qe(r,a="UNKNOWN",t){return j({summary:r,data:null,errorCategory:a,suggestions:t})}function Ne(r){let a={feature_disabled:"ENV_LIMITATION",permission_required:"PERMISSIONS",schema_missing:"SCHEMA_MISMATCH"};return j({summary:`Blocked: ${r.cannotProceedBecause}`,data:null,prerequisite:r,errorCategory:a[r.type]})}var rn=new Set(["@odata.editLink","@odata.id","@odata.type"]),sn=["@Microsoft.Dynamics.CRM.lookuplogicalname","@Microsoft.Dynamics.CRM.associatednavigationproperty"];function Jr(r,a){if(typeof r!="object"||r===null)return r;let t=new Set(a?.keep??["@odata.etag"]),e=r,n={},i=!1;for(let s of Object.keys(e)){if(t.has(s)){n[s]=e[s];continue}if(rn.has(s)){i=!0;continue}if(sn.some(o=>s.endsWith(o))){i=!0;continue}n[s]=e[s]}return i?n:r}var Yr=new Set(["@odata.context"]);function vt(r){if(Array.isArray(r)){let i=!1,s=[];for(let o of r){let l=vt(o);l!==o&&(i=!0),s.push(l)}return i?s:r}if(r===null||typeof r!="object"||Buffer.isBuffer(r)||r instanceof Date)return r;let a=Object.getPrototypeOf(r);if(a!==Object.prototype&&a!==null)return r;let t=r,e={},n=!1;for(let i of Object.keys(t)){if(i==="@odata.etag"){e[i]=t[i];continue}if(rn.has(i)||Yr.has(i)){n=!0;continue}if(sn.some(o=>i.endsWith(o))){n=!0;continue}let s=vt(t[i]);s!==t[i]&&(n=!0),e[i]=s}return n?e:r}function we(r,a){return r.map(t=>Jr(t,a))}function De(r){if(typeof r!="object"||r===null)return r;let a=r,t={},e=!1;for(let n of Object.keys(a)){let i=a[n];if(i==null||i===""){e=!0;continue}t[n]=i}return e?t:r}function T(r){return r.map(a=>De(a))}function Qr(r){if(r==null||r==="")return!0;if(Array.isArray(r))return r.length===0;if(typeof r=="object"){let a=Object.getPrototypeOf(r);if(a===null||a===Object.prototype)return Object.keys(r).length===0}return!1}function bt(r){if(Array.isArray(r))return r.map(e=>bt(e));if(r===null||typeof r!="object")return r;let a=r;if("UserLocalizedLabel"in a||"LocalizedLabels"in a&&Array.isArray(a.LocalizedLabels)){let e=a.UserLocalizedLabel;if(e&&typeof e.Label=="string")return e.Label;let n=a.LocalizedLabels;return Array.isArray(n)&&n[0]?.Label?n[0].Label:null}let t={};for(let e of Object.keys(a)){let n=bt(a[e]);Qr(n)||(t[e]=n)}return t}function _t(r){return bt(r)}var ht="@OData.Community.Display.V1.FormattedValue";function Te(r){return r.map(a=>{if(typeof a!="object"||a===null)return a;let t=a,e={};for(let i of Object.keys(t))if(i.endsWith(ht)){let s=i.slice(0,-ht.length);e[s]=t[i]}if(Object.keys(e).length===0)return a;let n={};for(let i of Object.keys(t))i.endsWith(ht)||(Object.prototype.hasOwnProperty.call(e,i)?n[i]={value:t[i],label:e[i]}:n[i]=t[i]);return n})}var on=[{name:"dataverse_whoami",description:"Returns the current authenticated user context from Dataverse WhoAmI: userId, businessUnitId, organizationId, organizationName, and environmentUrl. Use this to verify authentication is working, retrieve the current user context, or obtain IDs needed for subsequent operations. WHEN TO USE: Verifying authentication, getting current user/org context, or obtaining IDs for downstream operations. BEST PRACTICES: Call first to confirm connectivity before other tools. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function cn(r,a,t){if(r==="dataverse_whoami"){let e=await t.whoAmI(),n={userId:e.UserId,businessUnitId:e.BusinessUnitId,organizationId:e.OrganizationId,organizationName:e.OrganizationName,environmentUrl:e.EnvironmentUrl};return f(`Authenticated as ${e.OrganizationName}`,n,["Use dataverse_list_tables to discover available tables","Use dataverse_get_table_metadata to inspect a table schema"])}throw new Error(`Unknown auth tool: ${r}`)}import{z as mn}from"zod";import{z as U}from"zod";var wt=[{name:"dataverse_list_tables",description:"Lists all Dataverse tables. By default returns ONLY custom (non-system) tables. Set includeSystemTables=true to include all ~1700+ system tables.",inputSchema:{type:"object",properties:{includeSystemTables:{type:"boolean",description:"Include system tables. Default false = custom tables only."},verbose:{type:"boolean",description:"When true, returns full Dataverse metadata including all localized labels. Default false collapses DisplayName/Description objects to a single string to reduce token usage."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_table_metadata",description:"Returns full schema metadata for a Dataverse table: all attribute (column) logical names, display names, data types, required levels, and lookup target entities. Use this before writing queries or creating/updating records to confirm correct field names and types. Set includeAttributes=false if you only need table-level metadata without column details.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:'The logical name of the table (e.g., "account", "contact", "new_mytable")'},includeAttributes:{type:"boolean",description:"Include attribute (column) definitions. Default: true"},verbose:{type:"boolean",description:"When true, returns full Dataverse metadata including all localized labels. Default false collapses DisplayName/Description objects to a single string to reduce token usage."}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_relationships",description:"Returns all relationship definitions (1:N, N:1, N:N) for a Dataverse table, including relationship schema names, referenced/referencing entity names, and lookup attribute names. Use to determine the correct relationshipName for dataverse_associate/dataverse_disassociate, or to map lookup fields before building FetchXML joins. Use relationshipType to filter results.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:"The logical name of the table"},relationshipType:{type:"string",enum:["OneToMany","ManyToOne","ManyToMany","All"],description:"Filter by relationship type. Default: All"}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_global_option_sets",description:"Lists all global (shared) option sets defined in the Dataverse environment, returning their names and metadata IDs. Use this to discover available option sets before calling dataverse_get_option_set to retrieve their values. Prefer this over dataverse_get_table_metadata when you need to find option sets that are reused across multiple tables.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_option_set",description:"Returns all option labels and their integer values for a named global option set. Use this to look up the numeric code for a picklist value before filtering records (e.g., statecode or statuscode equivalents), or to populate dropdowns with correct option values.",inputSchema:{type:"object",properties:{name:{type:"string",description:"The name of the global option set"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_entity_key",description:"Returns all alternate key definitions for a Dataverse table. Useful before using dataverse_upsert to know which fields serve as alternate keys, their index status (Active/InProgress/Failed), and whether they are customizable.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:'Logical name of the table (e.g., "account", "contact")'}},required:["tableName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_attribute_option_set",description:"Returns all option labels and integer values for a table-specific attribute (Picklist, MultiSelectPicklist, Status, or State field). Use to look up the numeric codes for a column's choices before filtering or updating records.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Logical name of the table (e.g., "account", "contact")'},attributeLogicalName:{type:"string",description:'Logical name of the attribute (e.g., "statuscode", "industrycode", "new_tags")'}},required:["entityLogicalName","attributeLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Zr=U.object({logicalName:U.string().min(1),includeAttributes:U.boolean().optional().default(!0),verbose:U.boolean().optional().default(!1)}),ei=U.object({logicalName:U.string().min(1),relationshipType:U.enum(["OneToMany","ManyToOne","ManyToMany","All"]).optional()}),ti=U.object({includeSystemTables:U.boolean().default(!1).optional().describe("Include system tables. Default false = custom tables only."),verbose:U.boolean().optional().default(!1)}),ni=U.object({name:U.string().min(1)}),ai=U.object({tableName:U.string().min(1).describe('Logical name of the table (e.g., "account", "contact")')}),ln=/^[a-z_][a-z0-9_]*$/,ri=U.object({entityLogicalName:U.string().min(1).regex(ln,"Invalid Dataverse logical name"),attributeLogicalName:U.string().min(1).regex(ln,"Invalid Dataverse logical name")});async function dn(r,a,t){switch(r){case"dataverse_list_tables":{let{includeSystemTables:e=!1,verbose:n=!1}=ti.parse(a??{}),i=!e,s=await t.listTables(i),o=Array.isArray(s)?s:[],l=n?o:_t(o);return ge(`tables (${i?"custom only":"including system"})`,l,["Use dataverse_get_table_metadata to inspect a specific table's columns and types"])}case"dataverse_get_table_metadata":{let{logicalName:e,includeAttributes:n,verbose:i}=Zr.parse(a),s=await t.getTableMetadata(e,n),o=Array.isArray(s?.Attributes)?s.Attributes.length:0,l=i?s:_t(s);return f(`Metadata for ${e}: ${o} attributes`,l,["Use dataverse_query to read records from this table","Use dataverse_get_relationships to see related tables"])}case"dataverse_get_relationships":{let{logicalName:e,relationshipType:n}=ei.parse(a),i=await t.getRelationships(e),s=e.toLowerCase(),o=i.filter(m=>m.RelationshipType==="OneToManyRelationship"&&m.ReferencedEntity?.toLowerCase()===s),l=i.filter(m=>m.RelationshipType==="OneToManyRelationship"&&m.ReferencingEntity?.toLowerCase()===s),c=i.filter(m=>m.RelationshipType==="ManyToManyRelationship"),d=!n||n==="All",u=[...d||n==="OneToMany"?o:[],...d||n==="ManyToOne"?l:[],...d||n==="ManyToMany"?c:[]];return f(`${u.length} relationships found for ${e}`,{tableName:e,oneToMany:d||n==="OneToMany"?o:void 0,manyToOne:d||n==="ManyToOne"?l:void 0,manyToMany:d||n==="ManyToMany"?c:void 0},["Use dataverse_associate or dataverse_disassociate to manage N:N relationships"])}case"dataverse_list_global_option_sets":{let e=await t.listGlobalOptionSets(),n=Array.isArray(e)?e:[];return ge("global option sets",n,["Use dataverse_get_option_set with a name to see the values"])}case"dataverse_get_option_set":{let{name:e}=ni.parse(a),n=await t.getOptionSet(e),i=Array.isArray(n?.Options)?n.Options:[];return f(`Option set '${e}': ${i.length} options`,n,["Use these values in $filter expressions or when creating/updating records"])}case"dataverse_get_entity_key":{let{tableName:e}=ai.parse(a),n=await t.getEntityKeys(e);return f(`${n.length} alternate keys for ${e}`,{tableName:e,keys:n,count:n.length},["Use dataverse_upsert with these keys for create-or-update operations"])}case"dataverse_get_attribute_option_set":{let{entityLogicalName:e,attributeLogicalName:n}=ri.parse(a),i=await t.getAttributeOptionSet(e,n),s=Array.isArray(i?.options)?i.options:[];return f(`Attribute '${n}' on '${e}': ${s.length} options`,i,["Use these integer values in $filter or when creating/updating records"])}default:throw new Error(`Unknown metadata-read tool: ${r}`)}}import{z as W}from"zod";var ii=new Set(["dataverse_delete","dataverse_delete_file","dataverse_update_entity","dataverse_delete_attribute"]);function Tt(r){let a=[];return r.top!==void 0&&r.top>5e3&&a.push({code:"LARGE_RESULT_SET",message:`Large result set requested (top=${r.top}). This can generate very large responses that consume significant context tokens. Reduce $top to \u226450 for exploration, or add a $filter to narrow results before increasing $top.`,severity:"warning"}),(!r.select||r.select.length===0)&&a.push({code:"NO_SELECT",message:"No $select specified \u2014 all 50+ columns are being returned, many of which are null. Add select=['name','statecode','modifiedon'] (or whichever columns you need) to reduce token usage by 60-80% on typical Dataverse tables.",severity:"warning"}),r.filter||a.push({code:"NO_FILTER",message:'No $filter \u2014 returning all accessible records up to the $top limit. For targeted queries, add filter (e.g., "statecode eq 0") to reduce both token usage and API load.',severity:"info"}),a}function te(r){let a=[];return ii.has(r.toolName)&&a.push({code:"DESTRUCTIVE_OP",message:"Destructive operation \u2014 confirm with user before proceeding. This action cannot be undone.",severity:"warning"}),r.toolName==="dataverse_batch_execute"&&a.push({code:"BATCH_OP",message:"Batch operations may modify multiple records in a single transaction.",severity:"info"}),a}var un=[{name:"dataverse_update_entity",description:"Updates configuration flags on an existing Dataverse entity definition \u2014 enables or disables Notes (HasNotes), Change Tracking, and Audit. Requires System Customizer or System Administrator privileges. WHEN TO USE: Enabling notes/attachments support, change tracking, or audit on a table. BEST PRACTICES: Leave autoPublish=false (default) when making multiple changes and call dataverse_publish_customizations once at the end. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the entity to update (e.g. 'account', 'new_mytable')"},hasNotes:{type:"boolean",description:"Enable or disable the Notes/Attachments feature for this entity"},changeTrackingEnabled:{type:"boolean",description:"Enable or disable change tracking (required for delta sync)"},isAuditEnabled:{type:"boolean",description:"Enable or disable auditing on this entity"},autoPublish:{type:"boolean",description:"Publish after update (default: false). BATCH RULE: NEVER set true if you are making multiple schema changes in the same session \u2014 causes Dataverse publish-lock contention (0x80044181). Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the very end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification to Dataverse entity metadata"}},required:["entityLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_table_icon",description:"Sets, changes, or removes the icon for a model-driven app table (entity) by assigning a web resource reference. PREREQUISITE: You MUST call dataverse_create_web_resource FIRST to upload the SVG/PNG \u2014 the name from that step is your iconVectorName here. Supports modern SVG vector icons (IconVectorName, preferred) and legacy PNG icons (IconLargeName, IconMediumName). To REMOVE an icon, pass an empty string '' for that property. Requires System Customizer or System Administrator privileges. WHEN TO USE: After upload \u2014 assigning, changing, or removing a table's icon in the model-driven app navigation. BEST PRACTICES: Use IconVectorName with an SVG web resource for sharp rendering at any size. WORKFLOW: table_icons \u2014 step 1: dataverse_create_web_resource \u2192 step 2: this tool \u2192 step 3: dataverse_publish_customizations.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the entity to update (e.g., 'account', 'new_mytable')"},iconVectorName:{type:"string",description:"SVG vector icon web resource name (e.g., 'new_icon.svg'). This must be the name returned by dataverse_create_web_resource. Pass an empty string '' to remove the icon."},iconLargeName:{type:"string",description:"Legacy 32x32 image web resource name. Must be uploaded first via dataverse_create_web_resource. Pass '' to remove."},iconMediumName:{type:"string",description:"Legacy 16x16 image web resource name. Must be uploaded first via dataverse_create_web_resource. Pass '' to remove."},autoPublish:{type:"boolean",description:"Publish after update (default: false). BATCH RULE: NEVER set true if making multiple schema changes \u2014 call dataverse_publish_customizations ONCE at the very end."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],si=W.object({entityLogicalName:W.string().min(1).regex(/^[a-z_][a-z0-9_]*$/,"Invalid logical name"),confirm:W.literal(!0,{errorMap:()=>({message:"Set confirm: true to modify entity metadata"})}),hasNotes:W.boolean().optional(),changeTrackingEnabled:W.boolean().optional(),isAuditEnabled:W.boolean().optional(),autoPublish:W.boolean().optional().default(!1)}).refine(r=>r.hasNotes!==void 0||r.changeTrackingEnabled!==void 0||r.isAuditEnabled!==void 0,{message:"At least one of hasNotes, changeTrackingEnabled, or isAuditEnabled must be provided"}),oi=W.object({entityLogicalName:W.string().min(1).regex(/^[a-z_][a-z0-9_]*$/,"Invalid logical name"),confirm:W.literal(!0,{errorMap:()=>({message:"Set confirm: true to modify entity metadata"})}),iconVectorName:W.string().optional(),iconLargeName:W.string().optional(),iconMediumName:W.string().optional(),autoPublish:W.boolean().optional().default(!1)}).refine(r=>r.iconVectorName!==void 0||r.iconLargeName!==void 0||r.iconMediumName!==void 0,{message:"At least one of iconVectorName, iconLargeName, or iconMediumName must be provided"});async function pn(r,a,t){switch(r){case"dataverse_update_entity":{let{entityLogicalName:e,hasNotes:n,changeTrackingEnabled:i,isAuditEnabled:s,autoPublish:o}=si.parse(a),l=te({toolName:"dataverse_update_entity",entitySetName:e}).map(p=>`[${p.severity.toUpperCase()}] ${p.code}: ${p.message}`),c={};n!==void 0&&(c.HasNotes=n),i!==void 0&&(c.ChangeTrackingEnabled=i),s!==void 0&&(c.IsAuditEnabled=s);try{await t.getTableMetadata(e,!1)}catch{return j({summary:`Entity '${e}' does not exist in this Dataverse environment.`,data:{error:"ENTITY_NOT_FOUND",entityLogicalName:e},errorCategory:"SCHEMA_MISMATCH",suggestions:["Use dataverse_list_tables to find the correct entity logical name"]})}let d={"@odata.type":"#Microsoft.Dynamics.CRM.EntityMetadata"};n!==void 0&&(d.HasNotes=n),i!==void 0&&(d.ChangeTrackingEnabled=i),s!==void 0&&(d.IsAuditEnabled={Value:s});try{await t.updateEntityDefinition(e,d)}catch(p){if((p instanceof Error?p.message:String(p)).includes("0x80060888")){let w=Object.keys(c),O={IsAuditEnabled:"IsAuditEnabled requires organization-level auditing to be enabled first: Power Platform admin center > Settings > Audit and logs > Start Auditing",ChangeTrackingEnabled:"ChangeTrackingEnabled may be blocked if the entity is part of a managed solution",HasNotes:"HasNotes=true/false should work for custom (unmanaged) entities"},_=w.map(M=>O[M]).filter(M=>M!==void 0);return j({summary:`Cannot update entity '${e}': operation not supported by Dataverse.`,data:{error:"0x80060888",entityLogicalName:e,requestedChanges:c},errorCategory:"SCHEMA_MISMATCH",suggestions:_})}throw p}let u=Object.entries(c).map(([p,y])=>`${p}=${String(y)}`).join(", "),m="autoPublish=false (skipped)";return o&&(await t.publishCustomizations({entities:[e]}),m="published successfully"),f(`Entity '${e}' updated: ${u}. ${m}.`,{entityLogicalName:e,changes:c,published:o,...l.length>0&&{warnings:l}},["Use dataverse_get_table_metadata to verify the changes","Enable HasNotes=true before using dataverse_create_annotation"])}case"dataverse_set_table_icon":{let{entityLogicalName:e,iconVectorName:n,iconLargeName:i,iconMediumName:s,autoPublish:o}=oi.parse(a),l=te({toolName:"dataverse_set_table_icon",entitySetName:e}).map(p=>`[${p.severity.toUpperCase()}] ${p.code}: ${p.message}`),c={};n!==void 0&&(c.IconVectorName=n),i!==void 0&&(c.IconLargeName=i),s!==void 0&&(c.IconMediumName=s);let d;try{d=await t.getTableMetadata(e,!1)}catch{return j({summary:`Entity '${e}' does not exist in this Dataverse environment.`,data:{error:"ENTITY_NOT_FOUND",entityLogicalName:e},errorCategory:"SCHEMA_MISMATCH",suggestions:["Use dataverse_list_tables to find the correct entity logical name"]})}delete d["@odata.context"],n!==void 0&&(d.IconVectorName=n),i!==void 0&&(d.IconLargeName=i),s!==void 0&&(d.IconMediumName=s),await t.replaceEntityDefinition(e,d);let u=Object.entries(c).map(([p,y])=>`${p}=${String(y)}`).join(", "),m="autoPublish=false (skipped)";return o&&(await t.publishCustomizations({entities:[e]}),m="published successfully"),f(`Entity '${e}' icon updated: ${u}. ${m}.`,{entityLogicalName:e,changes:c,published:o,...l.length>0&&{warnings:l}},["Use dataverse_get_table_metadata to verify the icon","Use dataverse_publish_customizations if autoPublish was left false"])}default:throw new Error(`Unknown metadata tool: ${r}`)}}var ci={name:"dataverse_resolve_entity_name",description:"Resolves entity names bidirectionally: input a logicalName (e.g. 'account') or entitySetName (e.g. 'accounts') and get both representations plus metadata. Essential for avoiding 404/0x80060888 errors caused by using logicalName in OData URLs (which require entitySetName). WHEN TO USE: Any time you're unsure whether to use logicalName vs entitySetName, or to look up PrimaryIdAttribute/PrimaryNameAttribute for new entities. EXAMPLES: 'account' \u2192 LogicalName:account, EntitySetName:accounts, PrimaryId:accountid. WORKFLOW: read_record, inspect_audit.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Entity name to resolve \u2014 either logicalName (e.g. 'account') or entitySetName (e.g. 'accounts')"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},li=mn.object({name:mn.string().min(1)}),gn=[...wt,ci,...un];async function fn(r,a,t){if(r==="dataverse_resolve_entity_name"){let{name:n}=li.parse(a),i=`LogicalName eq '${n}' or EntitySetName eq '${n}'`,o=(await t.query("EntityDefinitions",{filter:i,select:["LogicalName","EntitySetName","DisplayName","PrimaryIdAttribute","PrimaryNameAttribute","OwnershipType","IsCustomEntity"]})).value??[];if(o.length===0)return f(`No entity found matching '${n}'`,{name:n,matches:[]},["Try dataverse_list_tables to see available table names"]);let l=o[0],c=l.DisplayName?.UserLocalizedLabel?.Label??null;return f(`Resolved '${n}' \u2192 LogicalName: ${l.LogicalName}, EntitySetName: ${l.EntitySetName}`,{logicalName:l.LogicalName,entitySetName:l.EntitySetName,displayName:c,primaryIdAttribute:l.PrimaryIdAttribute,primaryNameAttribute:l.PrimaryNameAttribute,ownershipType:l.OwnershipType,isCustomEntity:l.IsCustomEntity},["Use entitySetName in OData Web API URLs (dataverse_query, dataverse_read_record)","Use logicalName in FetchXML and metadata API calls"])}return new Set(wt.map(n=>n.name)).has(r)?dn(r,a,t):pn(r,a,t)}import{z as S}from"zod";import{z as yn}from"zod";var hn=/^[a-zA-Z_][a-zA-Z0-9_]*$/,A=yn.string().min(1).regex(hn,"entitySetName must contain only letters, digits, or underscores"),Ze=yn.string().min(1).regex(hn,"relationshipName must contain only letters, digits, or underscores");var di={opportunity:"opportunities",territory:"territories",category:"categories",activityparty:"activityparties",activitymimeattachment:"activitymimeattachments",queue:"queues",queueitem:"queueitems",dependency:"dependencies",salesliteratureitem:"salesliteratureitems",contractdetail:"contractdetails",discounttype:"discounttypes",entitlementtemplate:"entitlementtemplates",pricelevel:"pricelevels"},vn=[{name:"dataverse_query",description:"Queries a Dataverse table using OData ($filter, $select, $orderby, $top, $expand, $count). Use for simple to moderate reads on a single table or with shallow $expand for related fields. TOKEN EFFICIENCY (critical): Always pass $select=['col1','col2'] \u2014 without it, 50+ columns are returned including many nulls. compact defaults to true, automatically dropping null/empty fields. Without $filter, top is auto-capped at 50 to prevent unexpectedly large responses. For complex aggregations (count, sum, avg), multi-entity joins, many-to-many traversal, or advanced FetchXML-only operators, use dataverse_execute_fetchxml. WHEN TO USE: Single-table reads, simple filters, shallow expands, or server-side aggregation via $apply. BEST PRACTICES: $select=['name','statecode'] + compact=true + top\u226450 for exploration. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts", "contacts")'},select:{type:"array",items:{type:"string"},description:"Columns to return. Always specify to minimize payload."},filter:{type:"string",description:'OData $filter expression (e.g., "statecode eq 0 and new_amount gt 1000")'},orderby:{type:"string",description:'OData $orderby expression (e.g., "createdon desc")'},top:{type:"number",description:"Maximum number of records to return (default: 25). Lower defaults reduce token usage; raise explicitly when more rows are needed."},expand:{type:"string",description:'OData $expand for related entities (e.g., "parentaccountid($select=name)")'},count:{type:"boolean",description:"Include total record count in response"},apply:{type:"string",description:'OData $apply for server-side aggregation (e.g., "groupby((statuscode),aggregate($count as count))")'},formattedValues:{type:"boolean",description:"Include human-readable labels for picklist fields alongside raw integer codes."},keepODataAnnotations:{type:"boolean",description:"Preserve verbose OData/CRM annotations. Default false strips them. @odata.etag is always kept."},compact:{type:"boolean",description:"Drops null/empty fields per record (default true). Reduces tokens by 60-80%. Set false only when null values are needed."}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_fetchxml",description:'Executes a FetchXML query against Dataverse \u2014 use for complex scenarios requiring aggregations (count, sum, avg, min, max with grouping), linked-entity joins across multiple tables, many-to-many relationship traversal, or advanced filtering not expressible in OData. Returns a typed array of records. entitySetName is optional \u2014 if omitted it is extracted from the <entity name="..."> element in the FetchXML. Set fetchAll=true to automatically paginate through all pages (up to 100 pages / 50,000 records for safety). Default behavior returns first page only. TOKEN EFFICIENCY: compact defaults to true (drops null/empty fields). Always specify <attribute name="..."> elements to limit columns returned \u2014 omitting them returns all columns including many nulls. WHEN TO USE: Multi-table joins, aggregations with groupby, N:N traversal, or filtering not supported by OData. BEST PRACTICES: Add page/count attributes for large result sets; prefer dataverse_query for simple reads; use fetchAll=true to retrieve all records beyond the first page. WORKFLOW: query_data.',inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name of the root entity (e.g., "accounts"). If omitted, extracted from the <entity name="..."> element in the FetchXML.'},fetchXml:{type:"string",description:"The complete FetchXML query string"},fetchAll:{type:"boolean",description:"When true, automatically paginates through all result pages (up to 100 pages / 50,000 records). Default false returns the first page only."},formattedValues:{type:"boolean",description:"When true, includes human-readable labels for picklist fields alongside raw integer codes."},keepODataAnnotations:{type:"boolean",description:"When true, preserves verbose OData/CRM annotations. Default false strips them to reduce token usage. @odata.etag is always preserved."},compact:{type:"boolean",description:"Drops null, undefined, and empty-string fields from each record (default true). Most Dataverse rows have 30-50+ null columns \u2014 keeping compact=true can reduce token usage by 60-80%."}},required:["fetchXml"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_retrieve_multiple_with_paging",description:"Retrieves ALL records matching a query by automatically following OData nextLink pages. Use instead of dataverse_query when you need more than 5000 records or all records in a table. Returns totalRetrieved count. Set maxTotal to cap retrieval (default 5000, max 50000) to avoid overwhelming the context. TOKEN EFFICIENCY: compact defaults to true (drops null/empty fields). Always pass select=['col1','col2'] \u2014 without $select, all 50+ columns are returned per record. WHEN TO USE: You need all matching records beyond the 5000-row OData page limit or a full table export. BEST PRACTICES: Always set $select; use maxTotal to cap results and avoid context overflow. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},select:{type:"array",items:{type:"string"}},filter:{type:"string"},orderby:{type:"string"},expand:{type:"string"},maxTotal:{type:"number",description:"Maximum records to retrieve (default: 5000, max: 50000)"},formattedValues:{type:"boolean",description:"Include formatted label annotations."},keepODataAnnotations:{type:"boolean",description:"When true, preserves verbose OData/CRM annotations. Default false strips them to reduce token usage. @odata.etag is always preserved."},compact:{type:"boolean",description:"Drops null, undefined, and empty-string fields from each record (default true). Most Dataverse rows have 30-50+ null columns \u2014 keeping compact=true can reduce token usage by 60-80%."}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],ui=S.object({entitySetName:A,select:S.array(S.string()).optional(),filter:S.string().optional(),orderby:S.string().optional(),top:S.number().positive().max(5e3).optional().default(25),expand:S.string().optional(),count:S.boolean().optional(),apply:S.string().optional(),formattedValues:S.boolean().optional(),keepODataAnnotations:S.boolean().optional().default(!1),compact:S.boolean().optional().default(!0)}),pi=S.object({fetchXml:S.string().min(1).describe("Complete FetchXML query string"),entitySetName:S.string().optional().describe('OData entity set name (e.g., "accounts"). If omitted, extracted from the <entity name="..."> element in the FetchXML.'),fetchAll:S.boolean().optional(),formattedValues:S.boolean().optional(),keepODataAnnotations:S.boolean().optional().default(!1),compact:S.boolean().optional().default(!0)}),mi=S.object({entitySetName:A,select:S.array(S.string()).optional(),filter:S.string().optional(),orderby:S.string().optional(),expand:S.string().optional(),maxTotal:S.number().positive().max(5e4).optional(),formattedValues:S.boolean().optional(),keepODataAnnotations:S.boolean().optional().default(!1),compact:S.boolean().optional().default(!0)});async function bn(r,a,t,e){switch(r){case"dataverse_query":{let n=ui.parse(a),i=50,s;!n.filter&&n.top!==void 0&&n.top>i&&(s=i);let o=s??n.top,l=!n.select||n.select.length===0,c=l||n.compact,d={};n.select!==void 0&&(d.select=n.select),n.filter!==void 0&&(d.filter=n.filter),n.orderby!==void 0&&(d.orderby=n.orderby),o!==void 0&&(d.top=o),n.expand!==void 0&&(d.expand=n.expand),n.count!==void 0&&(d.count=n.count),n.apply!==void 0&&(d.apply=n.apply),n.formattedValues!==void 0&&(d.formattedValues=n.formattedValues);let u;try{u=await t.query(n.entitySetName,d)}catch(b){let rt=b,kr=rt?.data?.error?.message,Er=b instanceof Error?b.message:String(b),it=rt?.status,Cr=it===403||it===401?"PERMISSIONS":it===404?"SCHEMA_MISMATCH":"UNKNOWN";return Qe(`dataverse_query failed on '${n.entitySetName}': ${kr??Er}`,Cr,["Use dataverse_list_tables to verify the entity set name","Check OData filter syntax and column names"])}let m=Array.isArray(u?.value)?u.value:[],p=u["@odata.count"],y=p!==void 0?p===m.length?" (showing all results)":` (total in dataset: ${p})`:"",w=(()=>{let b=m;return n.formattedValues&&(b=Te(b)),n.keepODataAnnotations||(b=we(b)),c&&(b=T(b)),b})(),O=Array.isArray(u?.value)?(()=>{let b={...u,value:w};return n.keepODataAnnotations||delete b["@odata.context"],b})():u,_=Tt({...o!==void 0?{top:o}:{},...n.select!==void 0?{select:n.select}:{},...n.filter!==void 0?{filter:n.filter}:{},entitySetName:n.entitySetName}).map(b=>`[${b.severity.toUpperCase()}] ${b.code}: ${b.message}`),M=[];s!==void 0&&M.push(`[INFO] AUTO_TOP_CAP: top was reduced from ${n.top} to ${i} because no $filter was specified. Add a $filter to query specific records, or pass a lower explicit top.`),l&&!n.compact&&M.push("[INFO] AUTO_COMPACT: compact=true was applied automatically because no $select was specified. Pass $select=['col1','col2'] to choose specific columns and reduce token usage further.");let pe=[...M,..._],Z=["Use dataverse_execute_fetchxml for complex joins or aggregations"];return(!n.select||n.select.length===0)&&Z.push("Add $select=['name','statecode',...] to return only the columns you need \u2014 this is the single most effective way to reduce token usage."),f(`${m.length} records returned from ${n.entitySetName}${y}`,O,Z,pe.length>0?pe:void 0)}case"dataverse_execute_fetchxml":{let n=pi.parse(a),i=n.entitySetName,{fetchXml:s}=n;if(!i){let d=s.match(/<entity\s+name=["']([^"']+)["']/i);if(!d)return{content:[{type:"text",text:JSON.stringify({isError:!0,error:"entitySetName is required when not present in FetchXML <entity> element"})}]};let u=d[1];i=di[u]??u+"s"}if(n.fetchAll){let u=await t.executeFetchXmlAllPages(i,s);return n.formattedValues&&(u=Te(u)),n.keepODataAnnotations||(u=we(u)),n.compact&&(u=T(u)),f(`${u.length} records retrieved via FetchXML (all pages)`,u,["Use dataverse_query for simple OData reads","fetchAll paginated through all available pages (up to 100)"])}let o=await t.executeFetchXml(i,s,n.formattedValues),c=Array.isArray(o)?o:Array.isArray(o?.value)?o.value:[];return n.formattedValues&&(c=Te(c)),n.keepODataAnnotations||(c=we(c)),n.compact&&(c=T(c)),f(`${c.length} records returned via FetchXML`,Array.isArray(o?.value)?(()=>{let d={...o,value:c};return n.keepODataAnnotations||delete d["@odata.context"],d})():c,["Use dataverse_query for simple OData reads","Add page/count attributes for large result sets"])}case"dataverse_retrieve_multiple_with_paging":{let n=mi.parse(a),i={};n.select!==void 0&&(i.select=n.select),n.filter!==void 0&&(i.filter=n.filter),n.orderby!==void 0&&(i.orderby=n.orderby),n.expand!==void 0&&(i.expand=n.expand),n.maxTotal!==void 0&&(i.maxTotal=n.maxTotal),await e?.report(0,1);let s=await t.queryWithPaging(n.entitySetName,i),o=s?.totalRetrieved??(Array.isArray(s?.value)?s.value.length:0),l=s?.pageCount??1;await e?.report(1,1);let c=Array.isArray(s?.records)?s.records:[],d=(()=>{let p=c;return n.formattedValues&&p.length>0&&(p=Te(p)),!n.keepODataAnnotations&&p.length>0&&(p=we(p)),n.compact&&p.length>0&&(p=T(p)),p})(),u=d!==c?{...s,records:d}:s,m=Tt({...n.select!==void 0?{select:n.select}:{},...n.filter!==void 0?{filter:n.filter}:{},entitySetName:n.entitySetName}).map(p=>`[${p.severity.toUpperCase()}] ${p.code}: ${p.message}`);return f(`${o} records retrieved across ${l} pages from ${n.entitySetName}`,u,["Set maxTotal to limit retrieval","Use $select to minimize payload size"],m.length>0?m:void 0)}default:throw new Error(`Unknown query tool: ${r}`)}}L();import{z as R}from"zod";var _n=[{name:"dataverse_get",description:"Retrieves a single Dataverse record by its GUID. Use when you already know the exact record ID and want specific fields \u2014 faster and more precise than dataverse_query with a GUID filter. Specify select to limit returned columns and reduce payload size. WHEN TO USE: You have the exact record GUID and want specific fields. BEST PRACTICES: Always specify select; use the returned etag for subsequent optimistic-concurrency updates. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name"},id:{type:"string",description:"Record GUID"},select:{type:"array",items:{type:"string"},description:"Columns to return"},expand:{type:"string",description:'OData $expand for related entities (e.g., "parentaccountid($select=name)")'},formattedValues:{type:"boolean",description:"When true, annotates picklist fields with human-readable labels. Note: dataverse_get always requests all OData annotations."},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["entitySetName","id"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create",description:`Creates a new record in a Dataverse table and returns the new record's GUID. Use dataverse_get_table_metadata first to confirm correct logical field names and required fields. For setting lookup fields, use the format "_fieldname_value" with the related record GUID. For bulk creation of multiple records, consider dataverse_batch_execute to reduce HTTP round-trips. WHEN TO USE: Creating a single new record in a known table. BEST PRACTICES: Validate field names via dataverse_get_table_metadata first; use dataverse_batch_execute for bulk inserts. WORKFLOW: create_record.`,inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name"},data:{type:"object",description:"Record data as key-value pairs using logical names"}},required:["entitySetName","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_update",description:"Updates an existing Dataverse record using PATCH semantics \u2014 only the fields provided in data are changed, all other fields remain unchanged. Requires the record GUID. Use dataverse_upsert instead if you want to create-or-update using an alternate key without knowing the GUID upfront. WHEN TO USE: Modifying specific fields on an existing record with a known GUID. BEST PRACTICES: Pass etag for optimistic concurrency; include only changed fields in data. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},data:{type:"object",description:"Fields to update"},etag:{type:"string",description:"ETag value from a prior dataverse_get response. When provided, the update only succeeds if the record has not been modified since (optimistic concurrency). Prevents lost updates."}},required:["entitySetName","id","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_delete",description:"Permanently deletes a Dataverse record by its GUID. This operation is irreversible \u2014 you MUST set confirm=true to proceed. Use dataverse_list_dependencies to check if the record is referenced by workflows, plugins, or other components before deleting shared or configuration records. WHEN TO USE: Permanently removing a record you are certain should be deleted. BEST PRACTICES: Call dataverse_list_dependencies first for shared records; always set confirm=true. WORKFLOW: delete_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},confirm:{type:"boolean",description:"Must be explicitly true to proceed with deletion"}},required:["entitySetName","id","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_upsert",description:'Creates or updates a Dataverse record using an alternate key (no GUID needed). Returns operation="created" or "updated". Use mode="createOnly" to fail with an error if the record already exists, or mode="updateOnly" to fail if the record does not exist. Default mode="upsert" creates or updates. Supports compound alternate keys via the alternateKeys parameter. WHEN TO USE: Create-or-update when you have an alternate key but no GUID. BEST PRACTICES: Verify alternate keys with dataverse_get_entity_key first; use mode to control behavior. WORKFLOW: create_record.',inputSchema:{type:"object",properties:{entitySetName:{type:"string"},alternateKey:{type:"string",description:"Alternate key attribute name (for single key)"},alternateKeyValue:{type:"string",description:"Alternate key value (for single key)"},alternateKeys:{type:"object",description:'Compound alternate key as key-value map (e.g., {"key1":"val1","key2":"val2"}). Use instead of alternateKey/alternateKeyValue for multi-field keys.'},data:{type:"object",description:"Record data"},mode:{type:"string",enum:["upsert","createOnly","updateOnly"],description:"upsert=create or update (default), createOnly=fail if exists, updateOnly=fail if not found"}},required:["entitySetName","data"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign",description:"Assigns a Dataverse record to a different user or team owner. Sets the ownerid lookup field using the OData bind syntax. WHEN TO USE: Changing the owner of a record to a different user or team. BEST PRACTICES: Use dataverse_list_users or dataverse_list_teams to find valid owner GUIDs first. WORKFLOW: update_record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name of the record to assign"},id:{type:"string",description:"Record GUID"},ownerType:{type:"string",enum:["systemuser","team"],description:'Type of the new owner: "systemuser" for a user, "team" for a team'},ownerId:{type:"string",description:"GUID of the user or team to assign the record to"}},required:["entitySetName","id","ownerType","ownerId"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],gi=R.object({entitySetName:A,id:R.string().uuid(),select:R.array(R.string()).optional(),expand:R.string().optional(),formattedValues:R.boolean().optional(),compact:R.boolean().optional().default(!0)}),fi=R.object({entitySetName:A,data:R.record(R.unknown())}),yi=R.object({entitySetName:A,id:R.string().uuid(),data:R.record(R.unknown()),etag:R.string().optional()}),hi=R.object({entitySetName:A,id:R.string().uuid(),confirm:R.boolean()}),vi=R.object({entitySetName:A,alternateKey:R.string().min(1).optional(),alternateKeyValue:R.string().min(1).optional(),alternateKeys:R.record(R.string()).optional(),data:R.record(R.unknown()),mode:R.enum(["upsert","createOnly","updateOnly"]).default("upsert").optional().describe("upsert=create or update (default), createOnly=fail if exists, updateOnly=fail if not found")}).refine(r=>r.alternateKey&&r.alternateKeyValue||r.alternateKeys&&Object.keys(r.alternateKeys).length>0,{message:"Provide either (alternateKey + alternateKeyValue) or alternateKeys"}),bi=R.object({entitySetName:A,id:R.string().uuid(),ownerType:R.enum(["systemuser","team"]),ownerId:R.string().uuid()});async function wn(r,a,t){switch(r){case"dataverse_get":{let{entitySetName:e,id:n,select:i,expand:s,formattedValues:o,compact:l}=gi.parse(a),c,d;try{let p=await t.getRecord(e,n,i,s);c=p.record,d=p.etag}catch(p){let y=p,O=y?.data?.error?.message,_=p instanceof Error?p.message:String(p),M=y?.status,pe=M===403||M===401?"PERMISSIONS":M===404?"SCHEMA_MISMATCH":"UNKNOWN";return Qe(`dataverse_get failed for '${n}' in '${e}': ${O??_}`,pe,["Use dataverse_list_tables to verify the entity set name","Verify the record ID exists with dataverse_query"])}let u=o?Te([c])[0]??c:c,m=l?De(u):u;return f(`Retrieved record ${n} from ${e}`,{id:n,record:m,etag:d},["Use dataverse_update to modify this record","Use dataverse_get_relationships to explore related records"])}case"dataverse_create":{let{entitySetName:e,data:n}=fi.parse(a),i=await t.createRecord(e,n);return f(`Created record ${i} in ${e}`,{id:i,message:"Record created successfully"},["Use dataverse_get to retrieve the full record","Use dataverse_associate to link related records"])}case"dataverse_update":{let{entitySetName:e,id:n,data:i,etag:s}=yi.parse(a);return await t.updateRecord(e,n,i,s),f(`Updated record ${n} in ${e}`,{message:"Record updated successfully"},["Use dataverse_get to verify the update","Use etag parameter for optimistic concurrency"])}case"dataverse_delete":{let{entitySetName:e,id:n,confirm:i}=hi.parse(a);return i?(await t.deleteRecord(e,n),f(`Deleted record ${n} from ${e}`,{message:"Record deleted successfully"},["This operation is irreversible"])):{content:[{type:"text",text:JSON.stringify({message:`Deletion not performed. Set 'confirm: true' to delete record '${n}' from '${e}'.`})}]}}case"dataverse_upsert":{let e=vi.parse(a),{entitySetName:n,alternateKey:i,alternateKeyValue:s,alternateKeys:o,data:l,mode:c="upsert"}=e,d;o&&Object.keys(o).length>0&&(d=Object.entries(o).map(([m,p])=>`${h(m)}='${h(p)}'`).join(","));let u=await t.upsertRecord(n,i??"",s??"",l,c,d);return f(`Upsert ${u.operation}: record in ${n}`,{operation:u.operation,id:u.id,message:`Record ${u.operation} successfully`},["Use dataverse_get_entity_key to verify alternate key definitions"])}case"dataverse_assign":{let{entitySetName:e,id:n,ownerType:i,ownerId:s}=bi.parse(a),o=i==="systemuser"?"systemusers":"teams";return await t.updateRecord(e,n,{"ownerid@odata.bind":`/${o}(${s})`}),f(`Assigned record ${n} in ${e} to new owner`,{message:"Record assigned successfully"},["Use dataverse_list_users or dataverse_list_teams to find valid owners"])}default:throw new Error(`Unknown CRUD tool: ${r}`)}}import{z as P}from"zod";var Tn=[{name:"dataverse_associate",description:"Creates an association between two Dataverse records via a named N:N or 1:N relationship. Requires the relationship schema name obtainable from dataverse_get_relationships. Use for N:N relationships or to link records without modifying a lookup field directly \u2014 for simple 1:N lookups, setting the lookup field in dataverse_update is simpler.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Source record GUID"},relationshipName:{type:"string",description:"Relationship schema name"},relatedEntitySetName:{type:"string",description:"Related entity set name"},relatedId:{type:"string",description:"Related record GUID"}},required:["entitySetName","id","relationshipName","relatedEntitySetName","relatedId"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_associate_bulk",description:"Associates one source record with multiple related records at once via a named relationship, executing all associations in parallel. Unlike dataverse_associate (one pair at a time), this accepts an array of related IDs and runs them concurrently. Uses Promise.allSettled semantics \u2014 individual failures are reported per item without aborting the others.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"Source entity set name"},id:{type:"string",description:"Source record GUID"},relationshipName:{type:"string",description:"Relationship schema name"},relatedEntitySetName:{type:"string",description:"Related entity set name"},relatedIds:{type:"array",items:{type:"string"},description:"GUIDs of records to associate (1\u2013200)",minItems:1,maxItems:200}},required:["entitySetName","id","relationshipName","relatedEntitySetName","relatedIds"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_disassociate",description:"Removes an existing association between two Dataverse records on a named relationship. For N:N relationships, provide relatedId and relatedEntitySetName to build the correct $id URL. For 1:N relationships, relatedId and relatedEntitySetName are optional. Use dataverse_get_relationships to find the correct relationship schema name.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string"},relationshipName:{type:"string"},relatedId:{type:"string",description:"Required for N:N relationships"},relatedEntitySetName:{type:"string",description:'Entity set name of the related record (required for N:N). E.g., "contacts"'},confirm:{type:"boolean",const:!0,description:"Must be explicitly true to confirm removal of the association"}},required:["entitySetName","id","relationshipName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_query_associations",description:"Reads existing N:N associations for a record by querying through a navigation property. Returns the related records \u2014 use to verify what's already linked before calling dataverse_associate or dataverse_disassociate. Use $select to return only needed columns; set $top to limit results.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"Source entity set name, e.g. 'roles'"},id:{type:"string",description:"Source record GUID"},navigationProperty:{type:"string",description:"Navigation property name, e.g. 'roleprivileges_association'"},select:{type:"string",description:"Comma-separated columns to return on related records"},top:{type:"number",description:"Max records to return. Default 50, max 1000"},filter:{type:"string",description:"OData $filter expression applied to related records"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:["entitySetName","id","navigationProperty"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],_i=P.object({entitySetName:A,id:P.string().uuid(),relationshipName:Ze,relatedEntitySetName:A,relatedIds:P.array(P.string().uuid()).min(1).max(200)}),wi=P.object({entitySetName:A,id:P.string().uuid(),relationshipName:Ze,relatedEntitySetName:A,relatedId:P.string().uuid()}),Ti=P.object({entitySetName:A,id:P.string().uuid(),relationshipName:Ze,relatedId:P.string().uuid().optional(),relatedEntitySetName:A.optional(),confirm:P.literal(!0)}),Ri=/^[A-Za-z_][A-Za-z0-9_]*$/,Si=P.object({entitySetName:A,id:P.string().uuid(),navigationProperty:P.string().min(1).regex(Ri,"Invalid navigation property name"),select:P.string().optional(),top:P.number().int().min(1).max(1e3).optional(),filter:P.string().optional(),compact:P.boolean().optional().default(!0)});async function Rn(r,a,t){switch(r){case"dataverse_associate_bulk":{let{entitySetName:e,id:n,relationshipName:i,relatedEntitySetName:s,relatedIds:o}=_i.parse(a),l=await Promise.allSettled(o.map(u=>t.associate(e,n,i,s,u))),c=[],d=[];return l.forEach((u,m)=>{u.status==="fulfilled"?c.push(o[m]):d.push({id:o[m],error:u.reason instanceof Error?u.reason.message:String(u.reason)})}),f(`${c.length}/${o.length} associations succeeded`,{entitySetName:e,id:n,relationshipName:i,succeeded:c,failed:d,successCount:c.length,failureCount:d.length},d.length>0?["Some associations failed \u2014 check the 'failed' array for details"]:["All associations processed (Dataverse returns 204 for both new and duplicate associations \u2014 use dataverse_query_associations to verify current state)"])}case"dataverse_associate":{let{entitySetName:e,id:n,relationshipName:i,relatedEntitySetName:s,relatedId:o}=wi.parse(a);return await t.associate(e,n,i,s,o),f(`Associated ${e}(${n}) with ${s}(${o}) via ${i}`,{message:"Records associated successfully"},["Use dataverse_get_relationships to verify relationship names"])}case"dataverse_disassociate":{let{entitySetName:e,id:n,relationshipName:i,relatedId:s,relatedEntitySetName:o}=Ti.parse(a);return await t.disassociate(e,n,i,s,o),f(`Disassociated records via ${i}`,{message:"Records disassociated successfully"},["This removes the N:N link but does not delete records"])}case"dataverse_query_associations":{let{entitySetName:e,id:n,navigationProperty:i,select:s,top:o,filter:l,compact:c}=Si.parse(a),d=`${e}(${n})/${i}`,m=(await t.query(d,{...s?{select:s.split(",").map(p=>p.trim())}:{},top:o??50,...l?{filter:l}:{}})).value??[];return c&&(m=T(m)),f(`${m.length} associated records via ${i}`,{entitySetName:e,id:n,navigationProperty:i,count:m.length,records:m},["Use dataverse_associate to add new associations","Use dataverse_disassociate to remove existing ones"])}default:throw new Error(`Unknown relation tool: ${r}`)}}import{z as k}from"zod";var Sn=[{name:"dataverse_execute_action",description:"Executes a global (unbound) Dataverse action that is not tied to a specific record \u2014 for example WinOpportunity, SendEmail, or custom process actions. Use dataverse_execute_bound_action when the action must operate on a particular record. Actions differ from functions in that they are state-changing operations; for read-only operations use dataverse_execute_function.",inputSchema:{type:"object",properties:{actionName:{type:"string",description:'Action logical name (e.g., "WinOpportunity")'},parameters:{type:"object",description:"Action parameters"}},required:["actionName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_execute_function",description:"Executes a global (unbound) Dataverse OData function that is read-only and returns data without side effects \u2014 for example RetrieveTotalRecordCount or InitializeFrom. Use dataverse_execute_action for state-changing operations. Use dataverse_execute_bound_action when the function/action requires a specific record context.",inputSchema:{type:"object",properties:{functionName:{type:"string",description:'Function name (e.g., "WhoAmI", "RetrieveTotalRecordCount")'},parameters:{type:"object",description:"Function parameters: strings are quoted inline, numbers/booleans inlined as-is, objects serialized as aliased OData parameters"}},required:["functionName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_bound_action",description:"Executes a Dataverse action bound to a specific record instance, passing the entity set name and record GUID as context (e.g., QualifyLead on a lead, or a custom action scoped to an account). The actionName should not include the Microsoft.Dynamics.CRM namespace prefix. Use dataverse_execute_action for global unbound actions that do not require a record context.",inputSchema:{type:"object",properties:{entitySetName:{type:"string"},id:{type:"string",description:"Record GUID"},actionName:{type:"string",description:"Action name (without Microsoft.Dynamics.CRM prefix)"},parameters:{type:"object",description:"Action parameters"}},required:["entitySetName","id","actionName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_retrieve_dependencies_for_delete",description:"Checks what solution components would block deletion of a specific component. Provide the Dataverse component type code (1=Entity, 2=Attribute, 26=SavedQuery, 29=Workflow, 92=PluginAssembly) and the component GUID. Use before deleting shared Dataverse customization components.",inputSchema:{type:"object",properties:{componentType:{type:"number",description:"Dataverse component type code (1=Entity, 2=Attribute, 29=Workflow, 92=PluginAssembly)"},objectId:{type:"string",description:"Component GUID"}},required:["componentType","objectId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_execute_bound_function",description:"Executes a Dataverse function bound to a specific record (e.g., CalculateRollupField, GetQuantityAvailable). Use for read-only computed operations on a single record. Unlike bound actions, bound functions do not modify data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name of the table (e.g., "accounts")'},id:{type:"string",description:"GUID of the record"},functionName:{type:"string",description:'Name of the bound function (e.g., "CalculateRollupField")'},parameters:{type:"object",description:"Function parameters as key-value pairs of strings",additionalProperties:{type:"string"}}},required:["entitySetName","id","functionName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_dependencies",description:"Lists workflows, Power Automate flows, Business Rules, and custom actions that reference a given table. Use to detect hidden dependencies before modifying or removing a table. Returns component name, type, state (Active/Draft), trigger event (Create/Update/Delete), and count.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:'Logical name of the table to check (e.g., "account", "contact")'},componentType:{type:"array",items:{type:"string",enum:["Workflow","Flow","BusinessRule","Action","BusinessProcessFlow","Plugin","CustomAPI"]},description:"Filter by component type. Default: all types."}},required:["tableName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],et=/^[a-zA-Z0-9_.]+$/,Ni=k.object({actionName:k.string().min(1).regex(et,"actionName must contain only letters, digits, underscores, or dots"),parameters:k.record(k.unknown()).optional().default({})}),Ai=k.object({functionName:k.string().min(1).regex(et,"functionName must contain only letters, digits, underscores, or dots"),parameters:k.record(k.unknown()).optional().default({})}),ki=k.object({entitySetName:A,id:k.string().uuid(),actionName:k.string().min(1).regex(et,"actionName must contain only letters, digits, underscores, or dots"),parameters:k.record(k.unknown()).optional().default({})}),Ei=k.object({componentType:k.number().int().positive(),objectId:k.string().uuid()}),Ci=["Workflow","Flow","BusinessRule","Action","BusinessProcessFlow","Plugin","CustomAPI"],Ii=k.object({tableName:k.string().min(1),componentType:k.array(k.enum(Ci)).optional()}),xi=k.object({entitySetName:A,id:k.string().uuid(),functionName:k.string().min(1).regex(et,"functionName must contain only letters, digits, underscores, or dots"),parameters:k.record(k.unknown()).optional().default({})});async function Nn(r,a,t){switch(r){case"dataverse_execute_action":{let{actionName:e,parameters:n}=Ni.parse(a),i=await t.executeAction(e,n);return f(`Executed unbound action ${e}`,i,["Use dataverse_list_custom_actions to discover available actions"])}case"dataverse_execute_function":{let{functionName:e,parameters:n}=Ai.parse(a),i=await t.executeFunction(e,n);return f(`Executed function ${e}`,i,["Functions are read-only operations"])}case"dataverse_execute_bound_action":{let{entitySetName:e,id:n,actionName:i,parameters:s}=ki.parse(a),o=await t.executeBoundAction(e,n,i,s);return f(`Executed bound action ${i} on ${e}(${n})`,o)}case"dataverse_list_dependencies":{let{tableName:e,componentType:n}=Ii.parse(a),i=await t.listTableDependencies(e,n);return f(`${i.count} dependencies found for component`,i,["Use this information before deleting or modifying the component"])}case"dataverse_retrieve_dependencies_for_delete":{let{componentType:e,objectId:n}=Ei.parse(a),i=await t.listDependencies(e,n),s=Array.isArray(i)?i:i.value??[i];return f(`${s.length} dependencies found`,i,["Review dependencies before deleting the component"])}case"dataverse_execute_bound_function":{let{entitySetName:e,id:n,functionName:i,parameters:s}=xi.parse(a),o=await t.executeBoundFunction(e,n,i,s);return f(`Executed bound function ${i} on ${e}(${n})`,o)}default:throw new Error(`Unknown action tool: ${r}`)}}import{z as fe}from"zod";var An=[{name:"dataverse_batch_execute",description:`Executes multiple Dataverse operations in a single HTTP $batch request to reduce network round-trips and improve throughput. Accepts up to 1000 individual GET, POST, PATCH, or DELETE requests. Use for bulk creates, updates, or deletes that need to be grouped for performance. Set useChangeset=true to wrap all mutating operations (POST/PATCH/DELETE) in an atomic changeset \u2014 a failure rolls back ALL changeset operations. Individual per-operation results (status, body) are returned as an array in the same order as the input requests. $REF OPERATIONS (N:N/1:N associations): POST to a collection-valued navigation property (N:N): url='roles({id})/roleprivileges_association/$ref', body must contain { "@odata.id": "https://<org_url>/api/data/v9.2/privileges({privilegeId})" } with an ABSOLUTE URL \u2014 a relative URL will cause a 400 error. PUT to a single-valued navigation property (lookup/1:N): url='contacts({id})/account_primary_contact/$ref', body must contain { "@odata.id": "https://<org_url>/api/data/v9.2/accounts({accountId})" } with an ABSOLUTE URL. Example: { method: 'POST', url: 'roles({roleId})/roleprivileges_association/$ref', body: { '@odata.id': 'https://org.crm.dynamics.com/api/data/v9.2/privileges({privilegeId})' } }. WHEN TO USE: Bulk creates, updates, or deletes (up to 1000 ops) needing a single HTTP round-trip; batched $ref associations. BEST PRACTICES: Use useChangeset=true for atomic all-or-nothing mutations; batch GETs for parallel reads; always use absolute URLs in @odata.id for $ref bodies. WORKFLOW: bulk_operations.`,inputSchema:{type:"object",properties:{requests:{type:"array",description:"Array of batch requests to execute",items:{type:"object",properties:{method:{type:"string",enum:["GET","POST","PATCH","DELETE"],description:"HTTP method"},url:{type:"string",description:'Relative URL (e.g., "accounts(guid)" or "contacts"). For $ref operations (e.g. "roles({id})/roleprivileges_association/$ref"), the request body MUST include "@odata.id" with an ABSOLUTE URL \u2014 relative URLs in @odata.id cause 400 errors.'},body:{type:"object",description:"Request body for POST/PATCH operations"}},required:["method","url"]}},useChangeset:{type:"boolean",description:"Wrap mutating operations (POST/PATCH/DELETE) in an atomic changeset. A failure rolls back ALL operations in the changeset. Defaults to false."}},required:["requests"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}],Di=fe.object({method:fe.enum(["GET","POST","PATCH","DELETE"]),url:fe.string().min(1).refine(r=>!/[\r\n]/.test(r),{message:"Batch URL must not contain CR or LF characters"}).refine(r=>!r.startsWith("http"),{message:"Batch URL must be a relative path, not an absolute URL"}).refine(r=>!/(\.\.[\/\\])|(^\.\.$)/.test(r),{message:"Batch URL must not contain path traversal sequences"}),body:fe.record(fe.unknown()).optional()}),Oi=fe.object({requests:fe.array(Di).min(1).max(1e3),useChangeset:fe.boolean().default(!1).describe("Wrap mutating operations (POST/PATCH/DELETE) in an atomic changeset. A failure rolls back ALL operations in the changeset.")});async function kn(r,a,t,e){if(r==="dataverse_batch_execute"){let{requests:n,useChangeset:i}=Oi.parse(a),s=n.map(c=>({method:c.method,url:c.url,body:c.body}));await e?.report(0,n.length);let o=await t.batchExecute(s,i);await e?.report(n.length,n.length);let l=o.filter(c=>c===null||!c.error).length;return f(`Batch executed: ${l}/${o.length} operations succeeded`,{results:o,count:o.length},["Use batch for bulk create/update operations to reduce HTTP round-trips"])}throw new Error(`Unknown batch tool: ${r}`)}import{z as Oe}from"zod";var En=[{name:"dataverse_change_detection",description:"Detects new, modified, and deleted records since a previous sync using Dataverse change tracking (delta queries). On first call, pass deltaToken=null to get an initial snapshot and receive a token. On subsequent calls, pass the returned token to retrieve only changes since last sync. Change tracking must be enabled on the table in Dataverse settings. Returns newAndModified records, deleted record IDs, and the nextDeltaToken for the next call. WHEN TO USE: Incremental sync \u2014 detecting records created, updated, or deleted since a previous snapshot. BEST PRACTICES: Store the deltaToken persistently; always specify $select to minimize payload. WORKFLOW: query_data.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},deltaToken:{anyOf:[{type:"string"},{type:"null"}],description:"Delta token from a previous call, or null for the initial sync"},select:{type:"array",items:{type:"string"},description:"Columns to return (recommended to minimise payload)"},compact:{type:"boolean",description:"Drop null/empty fields from each returned record (default true). Most Dataverse rows have many null columns \u2014 compact=true can reduce token usage by 60-80%."}},required:["entitySetName","deltaToken"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Pi=Oe.object({entitySetName:A,deltaToken:Oe.string().nullable(),select:Oe.array(Oe.string()).optional(),compact:Oe.boolean().optional().default(!0)});async function Cn(r,a,t){if(r==="dataverse_change_detection"){let e=Pi.parse(a),{entitySetName:n,deltaToken:i,select:s}=e,o;try{o=await t.getChangedRecords(n,i,s)}catch(u){let m=u instanceof Error?u.message:String(u);if(/change.?track|0x80072491/i.test(m))return Ne({type:"feature_disabled",feature:"Change Tracking",cannotProceedBecause:`Change tracking is not enabled on '${n}', so delta queries cannot be executed.`,adminPortal:"Power Apps Maker Portal",steps:["Open Power Apps maker portal (make.powerapps.com)",`Navigate to Tables \u2192 search for '${n}'`,"Open the table \u2192 click the Settings (gear) icon",'Enable "Track changes"',"Save the table, then publish customizations"],fixableViaToolName:"dataverse_update_entity"});throw u}let l=o,c=Array.isArray(l?.newAndModified)?l.newAndModified:[];c=we(c),e.compact&&(c=T(c));let d={...l,newAndModified:c};return f(`${c.length} changed records detected`,d,["Store the returned deltaToken for subsequent incremental sync"])}throw new Error(`Unknown tracking tool: ${r}`)}import{z as ye}from"zod";var Ui=ye.object({components:ye.object({entities:ye.array(ye.string()).optional().describe("Entity logical names to publish"),webResources:ye.array(ye.string()).optional().describe("Web resource names to publish"),optionSets:ye.array(ye.string()).optional().describe("Global OptionSet names to publish")}).optional().describe("Specific components to publish. If omitted, ALL unpublished customizations are published.")}),In=[{name:"dataverse_publish_customizations",description:'Publishes unpublished Dataverse customizations. \u26A0\uFE0F WITHOUT components \u2192 calls PublishAllXml, an EXCLUSIVE ORG-WIDE LOCK lasting 30-120s. While active, ALL other publish calls (scoped or global) fail with "already in progress". \u2705 ALWAYS prefer specifying components.entities/webResources/optionSets to use the scoped PublishXml action instead \u2014 faster and does NOT block other operations. \u274C DO NOT call without components after each individual change \u2014 batch ALL your schema changes first, then publish ONCE at the end. If you receive a "publish already in progress" error, the MCP will auto-retry every 30s (up to 2 minutes) \u2014 do NOT call the tool again; wait for the previous call to return. WHEN TO USE: Once at the end of a series of schema changes (table/column/relationship/form creation). BEST PRACTICES: Pass components.entities with the list of modified entity logical names. Full publish only if you cannot enumerate the changed components. WORKFLOW: manage_solution.',inputSchema:{type:"object",properties:{components:{type:"object",description:"Specific components. Omit to publish all.",properties:{entities:{type:"array",items:{type:"string"}},webResources:{type:"array",items:{type:"string"}},optionSets:{type:"array",items:{type:"string"}}}}},required:[]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function xn(r,a,t){switch(r){case"dataverse_publish_customizations":{let{components:e}=Ui.parse(a??{}),n=await t.publishCustomizations(e);return f("Customizations published successfully",n,["Changes are now visible to all users in the environment"])}default:throw new Error(`Unknown solution tool: ${r}`)}}L();import{z as Pe}from"zod";var $i=Pe.object({callerId:Pe.string().uuid().describe('Azure AD Object ID (GUID) of the Dataverse system user to impersonate. Requires the executing account to have the "Act on behalf of another user" privilege in Dataverse.'),toolName:Pe.string().min(1).describe('Name of the MCP tool to execute on behalf of the user (e.g., "dataverse_create", "dataverse_query")'),toolArgs:Pe.record(Pe.unknown()).describe("Arguments for the wrapped tool, as an object")}),Rt=[{name:"dataverse_impersonate",description:'Executes another Dataverse tool on behalf of a different system user by injecting the MSCRMCallerId header. Requires the executing account to have the "Act on behalf of another user" privilege in Dataverse. The impersonation applies ONLY to the single tool call specified. Use for auditing workflows that must create or update records under a specific user identity. WHEN TO USE: Creating or updating records under a specific user identity for audit-trail purposes. BEST PRACTICES: Impersonation applies to the single wrapped tool call only; cannot impersonate System Administrators. WORKFLOW: update_record.',inputSchema:{type:"object",properties:{callerId:{type:"string",description:"GUID (Azure AD Object ID) of the Dataverse system user to impersonate"},toolName:{type:"string",description:'MCP tool to execute while impersonating (e.g., "dataverse_create")'},toolArgs:{type:"object",description:"Arguments for the wrapped tool"}},required:["callerId","toolName","toolArgs"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function Dn(r,a,t,e){if(r!=="dataverse_impersonate")throw new Error(`Unknown impersonate tool: ${r}`);let{callerId:n,toolName:i,toolArgs:s}=$i.parse(a);try{if(((await t.query(`systemusers(${h(n)})/systemuserroles_association`,{select:["name"],filter:"name eq 'System Administrator'",top:1})).value?.length??0)>0)throw new Error("Security policy: impersonation of users with System Administrator role is prohibited to prevent privilege escalation.")}catch(c){throw c instanceof Error&&c.message.includes("Security policy")?c:new Error(`Security policy: cannot verify callerId roles \u2014 impersonation denied. Cause: ${String(c)}`)}let o=t.http,l=o.defaultHeaders.MSCRMCallerId;try{o.defaultHeaders.MSCRMCallerId=n;let c=await e(i,s,t);return f(`Impersonated as ${n}, executed ${i}`,{impersonatedAs:n,tool:i,result:JSON.parse(c.content[0].text)},["Impersonation applies to this single call only"])}finally{l===void 0?delete o.defaultHeaders.MSCRMCallerId:o.defaultHeaders.MSCRMCallerId=l}}L();import{z as F}from"zod";var Li={10:"Pre-validation",20:"Pre-operation",40:"Post-operation",45:"Post-operation (deprecated)"},Mi={0:"Synchronous",1:"Asynchronous"},qi=F.object({workflowId:F.string().uuid(),activate:F.boolean()}),On=[{name:"dataverse_list_custom_actions",description:"Lists all custom actions (custom API / SDK messages) registered in the environment. Returns the message name, category, bound entity (if any), execute privilege, and whether it is customizable. Useful for discovering available automation entry points and agent-callable actions. WHEN TO USE: Discovering available custom API / SDK messages for automation. BEST PRACTICES: Use nameFilter to search; follow up with dataverse_execute_action to invoke. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 100, max 500)"},nameFilter:{type:"string",description:"Filter by name (substring match)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_plugin_steps",description:"Lists plugin steps (SdkMessageProcessingStep registrations) in the environment. Shows plugin assembly, step name, message (Create/Update/Delete/\u2026), entity, stage (pre/post), mode (sync/async), and state (enabled/disabled). Essential for understanding what custom business logic fires on Dataverse operations. WHEN TO USE: Understanding what custom business logic fires on CRUD operations for a table. BEST PRACTICES: Filter by entityLogicalName; check stage and mode to understand execution order and timing. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 100, max 500)"},activeOnly:{type:"boolean",description:"Return only enabled steps (default: true)"},entityLogicalName:{type:"string",description:"Filter by entity logical name (e.g. 'account')"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_workflow_state",description:"Activates or deactivates a Dataverse workflow (classic workflow / real-time workflow / action). Set activate=true to activate (statecode 1, statuscode 2) or activate=false to deactivate (statecode 0, statuscode 1). Returns the new state. WHEN TO USE: Activating or deactivating a classic workflow, real-time workflow, or action. BEST PRACTICES: Verify workflow ID first; deactivation stops future triggers but does not cancel in-progress runs. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{workflowId:{type:"string",description:"The workflow GUID"},activate:{type:"boolean",description:"true = activate, false = deactivate (draft)"}},required:["workflowId","activate"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_connection_references",description:"Lists all Power Automate connection references defined in the environment, showing their display name, connector ID, connection ID, and active status. Use this before importing a solution containing flows to detect broken or unmapped connections that would cause silent import failures. WHEN TO USE: Pre-deployment solution validation, auditing connection health, identifying broken flow connections. BEST PRACTICES: Look for inactive (isActive=false) references \u2014 these indicate flows that will fail after deployment. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{connectorId:{type:"string",description:"Filter by connector ID substring (e.g. '/providers/Microsoft.PowerApps/apis/shared_sharepointonline')"},activeOnly:{type:"boolean",description:"Return only active connection references (default: false = return all)"},top:{type:"number",description:"Maximum records to return (default 100, max 500)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Hi=F.object({top:F.number().positive().max(500).optional().default(100),nameFilter:F.string().optional()}),ji=F.object({top:F.number().positive().max(500).optional().default(100),activeOnly:F.boolean().optional().default(!0),entityLogicalName:F.string().optional()}),Wi=F.object({connectorId:F.string().optional(),activeOnly:F.boolean().optional().default(!1),top:F.number().int().positive().max(500).optional().default(100)});async function Pn(r,a,t){switch(r){case"dataverse_list_custom_actions":{let{top:e,nameFilter:n}=Hi.parse(a??{}),i=["isprivate eq false"];n&&i.push(`contains(name,'${h(n)}')`);let o=(await t.query("sdkmessages",{select:["sdkmessageid","name","categoryname","isprivate","isreadonly","isvalidforexecuteasync"],filter:i.join(" and "),top:e})).value.map(l=>({id:l.sdkmessageid,name:l.name,category:l.categoryname??"",isPrivate:l.isprivate,isReadOnly:l.isreadonly,asyncSupported:l.isvalidforexecuteasync}));return f(`${o.length} custom actions found`,{total:o.length,messages:o},["Use dataverse_execute_action to run an action"])}case"dataverse_list_plugin_steps":{let{top:e,activeOnly:n,entityLogicalName:i}=ji.parse(a??{}),s={select:["sdkmessageprocessingstepid","name","stage","mode","rank","statecode","filteringattributes","asyncautodelete"],expand:"sdkmessageid($select=name,categoryname),plugintypeid($select=name,assemblyname),sdkmessagefilterid($select=primaryobjecttypecode)",top:e};n&&(s.filter="statecode eq 0");let l=(await t.query("sdkmessageprocessingsteps",s)).value;if(i){let d=i.toLowerCase();l=l.filter(u=>u.sdkmessagefilterid?.primaryobjecttypecode?.toLowerCase()===d)}let c=l.map(d=>({id:d.sdkmessageprocessingstepid,name:d.name,message:d.sdkmessageid?.name??"",entity:d.sdkmessagefilterid?.primaryobjecttypecode??"",assembly:d.plugintypeid?.assemblyname??"",pluginType:d.plugintypeid?.name??"",stage:d.stage,stageName:Li[d.stage]??`Stage ${d.stage}`,mode:d.mode,modeName:Mi[d.mode]??`Mode ${d.mode}`,rank:d.rank,isActive:d.statecode===0,filteringAttributes:d.filteringattributes??null,asyncAutoDelete:d.asyncautodelete}));return f(`${c.length} plugin steps found`,{total:c.length,steps:c},["Use dataverse_get_plugin_trace_logs for debugging plugin issues"])}case"dataverse_set_workflow_state":{let{workflowId:e,activate:n}=qi.parse(a);try{await t.updateRecord("workflows",e,{statecode:n?1:0,statuscode:n?2:1})}catch(i){let s=i instanceof Error?i.message:String(i);throw/0x80040203|does not refer to a valid workflow|404/i.test(s)?new Error(`Workflow '${e}' not found. Use dataverse_list_workflows to retrieve valid workflow GUIDs in this environment. Original error: ${s}`):i}return f(`Workflow state updated for ${e}`,{workflowId:e,newState:n?"Activated":"Draft",statecode:n?1:0,statuscode:n?2:1})}case"dataverse_list_connection_references":{let{connectorId:e,activeOnly:n,top:i}=Wi.parse(a??{}),s=[];n&&s.push("statecode eq 0"),e&&s.push(`contains(connectorid,'${h(e)}')`);let o={select:["connectionreferenceid","connectionreferencelogicalname","connectionreferencedisplayname","connectorid","connectionid","statecode","statuscode"],orderby:"connectionreferencedisplayname asc",top:i};s.length&&(o.filter=s.join(" and "));let c=((await t.query("connectionreferences",o)).value??[]).map(m=>({id:m.connectionreferenceid??"",logicalName:m.connectionreferencelogicalname??"",displayName:m.connectionreferencedisplayname??"",connectorId:m.connectorid??"",connectionId:m.connectionid??null,isActive:m.statecode===0,statusCode:m.statuscode??null})),d=c.filter(m=>!m.isActive).length,u=d>0?`${c.length} connection references found (${d} inactive \u2014 check before deploying)`:`${c.length} connection references found`;return f(u,{connectionReferences:c,count:c.length,inactiveCount:d},["Inactive references (isActive=false) indicate broken connections that will cause flow failures","Use the Power Apps maker portal to reconnect or replace broken connection references before deploying"])}default:throw new Error(`Unknown customization tool: ${r}`)}}L();import{z as G}from"zod";var Fi={1e8:"String",100000001:"Number",100000002:"Boolean",100000003:"JSON",100000004:"DataSource"},Bi={String:1e8,Integer:100000001,Boolean:100000002,JSON:100000003},Un=[{name:"dataverse_get_environment_variable",description:"Retrieves an environment variable's definition and current value from Dataverse. Returns the schema name, display name, type, default value, and the current override value (if set). Useful for reading feature flags, configuration values, and integration settings stored in Dataverse environment variables. WHEN TO USE: Reading configuration values, feature flags, or integration settings stored as environment variables. BEST PRACTICES: Check both defaultValue and currentValue; the effective value is currentValue ?? defaultValue. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"The schema name of the environment variable (e.g. 'new_MyConfig')"}},required:["schemaName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_set_environment_variable",description:"Sets or updates an environment variable value in Dataverse. If a value record already exists for the variable, it is updated; otherwise a new value record is created. The schemaName must match an existing environment variable definition. WHEN TO USE: Updating configuration values or feature flags stored in Dataverse environment variables. BEST PRACTICES: Verify the variable exists first with dataverse_get_environment_variable; schemaName must match exactly. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"The schema name of the environment variable"},value:{type:"string",description:"The new value to set"}},required:["schemaName","value"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create_environment_variable",description:"Creates a new Dataverse environment variable definition and sets its initial value. Use this when the variable does not yet exist \u2014 use dataverse_set_environment_variable to update an existing one. WHEN TO USE: Initial setup of configuration flags, feature toggles, or integration settings. BEST PRACTICES: Use a solution-prefixed schemaName (e.g. 'myprefix_MyConfig'); provide solutionUniqueName to register in a solution. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"Schema name of the new variable (e.g. 'new_MyConfig'). Case-sensitive."},displayName:{type:"string",description:"Human-readable display name"},type:{type:"string",enum:["String","Integer","Boolean","JSON"],description:"Variable type"},value:{type:"string",description:"Initial value to set"},description:{type:"string",description:"Optional description"},defaultValue:{type:"string",description:"Optional default value (fallback when no override value is set)"},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional creation of a new environment variable definition"}},required:["schemaName","displayName","type","value","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_environment_capabilities",description:"Returns a comprehensive snapshot of the Dataverse environment: identity (WhoAmI), organization settings (name, version, language, audit config), unmanaged solution count, and environment variable count. Use at the start of a session to orient yourself to the environment and its configuration. WHEN TO USE: Beginning of any Dataverse automation session, before bulk operations, or when debugging unexpected behavior. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],Gi=G.object({schemaName:G.string().min(1)}),Vi=G.object({schemaName:G.string().min(1),value:G.string()}),zi=G.object({schemaName:G.string().min(1),displayName:G.string().min(1),type:G.enum(["String","Integer","Boolean","JSON"]),value:G.string(),description:G.string().optional(),defaultValue:G.string().optional(),confirm:G.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a new environment variable definition"})})});async function Ki(r,a){let{schemaName:t}=Gi.parse(r),n=(await a.query("environmentvariabledefinitions",{filter:`schemaname eq '${h(t)}'`,select:["environmentvariabledefinitionid","schemaname","displayname","description","type","defaultvalue","isrequired"],top:1})).value;if(n.length===0)throw new Error(`Environment variable '${t}' not found. Check the schema name (it is case-sensitive, e.g. 'new_MyConfig'). To browse existing variables: Power Apps maker portal \u2192 Solutions \u2192 your solution \u2192 Environment Variables. To create a new one: open a solution \u2192 New \u2192 More \u2192 Environment Variable.`);let i=n[0],s=i.environmentvariabledefinitionid,l=(await a.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${s}`,select:["environmentvariablevalueid","value"],top:1})).value,c=l.length>0?l[0]:null,d=i.type,u=c?c.value??null:null,m=i.defaultvalue??null,p={schemaName:i.schemaname,displayName:i.displayname??"",description:i.description??"",type:d,typeName:Fi[d]??"Unknown",defaultValue:m,currentValue:u,valueId:c?c.environmentvariablevalueid:null,isRequired:i.isrequired??!1,effectiveValue:u??m};return f(`Environment variable '${p.schemaName}': ${p.typeName} = ${p.effectiveValue??"(not set)"}`,p,["Use dataverse_set_environment_variable to update the value"])}async function Xi(r,a){let{schemaName:t,value:e}=Vi.parse(r),i=(await a.query("environmentvariabledefinitions",{filter:`schemaname eq '${h(t)}'`,select:["environmentvariabledefinitionid","schemaname"],top:1})).value;if(i.length===0)throw new Error(`Environment variable definition '${t}' not found. This tool can only update the value of an existing variable. To create a new environment variable: Power Apps maker portal \u2192 Solutions \u2192 your solution \u2192 New \u2192 More \u2192 Environment variable. Then call this tool to set its value.`);let s=i[0].environmentvariabledefinitionid,l=(await a.query("environmentvariablevalues",{filter:`_environmentvariabledefinitionid_value eq ${s}`,select:["environmentvariablevalueid","value"],top:1})).value,c,d;l.length>0?(d=l[0].environmentvariablevalueid,await a.updateRecord("environmentvariablevalues",d,{value:e}),c="updated"):(d=await a.createRecord("environmentvariablevalues",{value:e,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${s})`}),c="created");let u={schemaName:t,operation:c,valueId:d,value:e};return f(`Environment variable '${t}' set to new value`,u,["Use dataverse_get_environment_variable to verify the update"])}async function Ji(r,a){let{schemaName:t,displayName:e,type:n,value:i,description:s,defaultValue:o}=zi.parse(r),l=te({toolName:"dataverse_create_environment_variable"}).map(y=>`[${y.severity.toUpperCase()}] ${y.code}: ${y.message}`);if((await a.query("environmentvariabledefinitions",{filter:`schemaname eq '${h(t)}'`,select:["environmentvariabledefinitionid"],top:1})).value.length>0)throw new Error(`Environment variable '${t}' already exists. Use dataverse_set_environment_variable to update its value.`);let d=Bi[n]??1e8,u={schemaname:t,displayname:e,type:d};s&&(u.description=s),o!==void 0&&(u.defaultvalue=o);let m=await a.createRecord("environmentvariabledefinitions",u),p=await a.createRecord("environmentvariablevalues",{value:i,"EnvironmentVariableDefinitionId@odata.bind":`/environmentvariabledefinitions(${m})`});return f(`Environment variable '${t}' created (type: ${n}, value: '${i}').`,{schemaName:t,displayName:e,type:n,typeCode:d,definitionId:m,valueId:p,value:i,...l.length>0&&{warnings:l}},["Use dataverse_get_environment_variable to verify","Use dataverse_set_environment_variable to update the value later"])}async function $n(r,a,t){switch(r){case"dataverse_get_environment_variable":return Ki(a,t);case"dataverse_set_environment_variable":return Xi(a,t);case"dataverse_create_environment_variable":return Ji(a,t);case"dataverse_environment_capabilities":{let[e,n,i,s]=await Promise.allSettled([t.executeFunction("WhoAmI",{}),t.query("organizations",{select:["uniquename","friendlyname","version","languagecode","isauditenabled"],top:1}),t.query("solutions",{filter:"ismanaged eq false",select:["uniquename"]}),t.query("environmentvariabledefinitions",{select:["schemaname"],top:1})]),o=e.status==="fulfilled"?e.value:null,l=n.status==="fulfilled"?n.value.value:[],c=i.status==="fulfilled"?i.value.value:[],d=s.status==="fulfilled"?s.value.value:[];return f("Environment capabilities retrieved",{identity:o,organization:l[0]??null,unmanagedSolutionsCount:c.length,environmentVariablesCount:d.length},["Use dataverse_list_solutions for full solution list","Use dataverse_get_environment_variable to read specific env vars"])}default:throw new Error(`Unknown environment tool: ${r}`)}}L();import{z as B}from"zod";var Yi={0:"Execute",1:"Create",2:"Retrieve",3:"RetrieveMultiple",4:"GetParent",5:"Update",6:"Delete",7:"Assign"},Qi={0:"Waiting",10:"WaitingForResources",20:"InProgress",21:"Pausing",22:"Canceling",30:"Succeeded",31:"Failed",32:"Canceled"},Zi={0:"ReadyToRun",1:"Suspended",2:"Locked",3:"Completed"},es=["plugintracelogid","typename","messagename","primaryentity","depth","operationtype","exceptiondetails","messageblock","createdon","performanceexecutionduration","correlationid","requestid"],ts=["asyncoperationid","name","operationtype","statuscode","statecode","message","createdon","startedon","completedon"],Ln=[{name:"dataverse_get_plugin_trace_logs",description:"Retrieves recent plugin and custom workflow activity trace logs from Dataverse. Shows execution details including plugin type name, triggering message, entity, execution duration, trace messages written by the developer, and exception details if the plugin failed. Requires the Plugin Trace Log feature to be enabled in Dataverse settings (Settings > Administration > System Settings > Customization tab > 'Enable logging to plugin trace log'). Essential for debugging plugin failures in production. Returns the most recent <tail> entries (default 50). Set tail higher for more, compact:false for full fields. WHEN TO USE: Debugging plugin failures or performance issues in production. BEST PRACTICES: Enable Plugin Trace Log in Dataverse settings first; filter by plugin name or entity to narrow results. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records to return (default 50, max 200)"},tail:{type:"number",description:"Return only the most recent N entries (sorted by createdon DESC). Default 50, max 500. Set higher only when needed."},compact:{type:"boolean",description:"Strip null/empty fields from each log entry to save tokens. Default true; set false for full payload."},pluginTypeFilter:{type:"string",description:"Filter by plugin type name (substring match, e.g. 'AccountValidation')"},messageFilter:{type:"string",description:"Filter by message name (e.g. 'Create', 'Update')"},entityFilter:{type:"string",description:"Filter by entity logical name (e.g. 'account')"},exceptionsOnly:{type:"boolean",description:"Return only traces where an exception occurred (default false)"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_get_workflow_trace_logs",description:"Retrieves background workflow (Power Automate classic / legacy workflow engine) execution records from Dataverse. These are the AsyncOperation records for workflow-type operations, useful for diagnosing failures in background workflows and real-time workflows running asynchronously. Note: For modern cloud flows (Power Automate), use the Power Automate portal instead. Returns the most recent <tail> entries (default 50). Set tail higher for more, compact:false for full fields. WHEN TO USE: Diagnosing failures in classic/legacy background workflows. BEST PRACTICES: Use failedOnly=true to focus on errors; for modern cloud flows use Power Automate portal. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Max records (default 50, max 200)"},tail:{type:"number",description:"Return only the most recent N entries (sorted by createdon DESC). Default 50, max 500. Set higher only when needed."},compact:{type:"boolean",description:"Strip null/empty fields from each log entry to save tokens. Default true; set false for full payload."},failedOnly:{type:"boolean",description:"Return only failed workflows (statuscode eq 31, default false)"},entityFilter:{type:"string",description:"Filter by regarding entity type (e.g. 'account')"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],ns=B.object({top:B.number().int().positive().max(200).optional().default(50),tail:B.number().int().positive().max(500).optional().default(50),compact:B.boolean().optional().default(!0),pluginTypeFilter:B.string().optional(),messageFilter:B.string().optional(),entityFilter:B.string().optional(),exceptionsOnly:B.boolean().optional().default(!1)}),as=B.object({top:B.number().int().positive().max(200).optional().default(50),tail:B.number().int().positive().max(500).optional().default(50),compact:B.boolean().optional().default(!0),failedOnly:B.boolean().optional().default(!1),entityFilter:B.string().optional()});function ne(r){return typeof r=="string"?r:""}function rs(r){return typeof r=="number"?r:null}async function Mn(r,a,t){let n=(a&&typeof a=="object"?a:{}).top!==void 0;switch(r){case"dataverse_get_plugin_trace_logs":{let i=ns.parse(a),s=[];i.pluginTypeFilter&&s.push(`contains(typename,'${h(i.pluginTypeFilter)}')`),i.messageFilter&&s.push(`messagename eq '${h(i.messageFilter)}'`),i.entityFilter&&s.push(`primaryentity eq '${h(i.entityFilter)}'`),i.exceptionsOnly&&s.push("exceptiondetails ne null");let o=s.length>0?s.join(" and "):void 0,l=n?i.top:i.tail,u=((await t.query("plugintracelogs",{select:es,orderby:"createdon desc",top:l,...o!==void 0?{filter:o}:{}})).value??[]).map(p=>{let y=typeof p.operationtype=="number"?p.operationtype:0,w=p.exceptiondetails;return{id:ne(p.plugintracelogid),typeName:ne(p.typename),message:ne(p.messagename),entity:ne(p.primaryentity),depth:typeof p.depth=="number"?p.depth:0,operationType:y,operationTypeName:Yi[y]??String(y),createdOn:ne(p.createdon),durationMs:rs(p.performanceexecutionduration),correlationId:ne(p.correlationid),requestId:ne(p.requestid),hasException:w!=null&&w!=="",exceptionDetails:typeof w=="string"?w:null,messageBlock:typeof p.messageblock=="string"?p.messageblock:null}});u.length>l&&(u=u.slice(0,l)),i.compact&&(u=T(u));let m={total:u.length,logs:u};return f(`${u.length} plugin trace logs found`,m,["Filter by plugin name or correlation ID for specific traces"])}case"dataverse_get_workflow_trace_logs":{let i=as.parse(a),s=["operationtype eq 10"];i.failedOnly&&s.push("statuscode eq 31");let o=s.join(" and "),l=n?i.top:i.tail,u=((await t.query("asyncoperations",{select:ts,filter:o,orderby:"createdon desc",top:l})).value??[]).map(p=>{let y=typeof p.statuscode=="number"?p.statuscode:0,w=typeof p.statecode=="number"?p.statecode:0,O=p.message;return{id:ne(p.asyncoperationid),name:ne(p.name),statusCode:y,statusName:Qi[y]??String(y),stateCode:w,stateName:Zi[w]??String(w),createdOn:ne(p.createdon),startedOn:typeof p.startedon=="string"?p.startedon:null,completedOn:typeof p.completedon=="string"?p.completedon:null,errorMessage:typeof O=="string"?O:null}});u.length>l&&(u=u.slice(0,l)),i.compact&&(u=T(u));let m={total:u.length,workflows:u};return f(`${u.length} workflow trace logs found`,m,["Filter by status or entity type for specific workflow traces"])}default:throw new Error(`Unknown trace tool: ${r}`)}}import{z as q}from"zod";var qn=[{name:"dataverse_search",description:"Full-text Relevance Search across all configured Dataverse tables. Returns ranked results with entity name, record ID, score, and matched fields. Requires Relevance Search to be enabled in Dataverse admin settings. Use when you need to find records without knowing which table they belong to. WHEN TO USE: Cross-table free-text search when you don't know which table contains the data. BEST PRACTICES: Narrow with entities[] and $filter; use searchType=full for Lucene operators (AND, OR, NOT, wildcards). WORKFLOW: search_data.",inputSchema:{type:"object",properties:{query:{type:"string",description:"Full-text search string (supports Lucene syntax with searchType=full)"},entities:{type:"array",items:{type:"string"},description:"Restrict to specific table logical names (omit to search all configured tables). Required when using select."},top:{type:"number",description:"Max results (default 10, max 50)"},searchMode:{type:"string",enum:["any","all"],description:"Match any or all terms (default: any)"},searchType:{type:"string",enum:["simple","full"],description:"Search syntax: simple (default) or full (enables Lucene syntax: AND, OR, NOT, wildcards, fuzzy, proximity, regex)"},filter:{type:"string",description:'OData $filter to apply on search results (e.g., "statecode eq 0")'},facets:{type:"array",items:{type:"string"},description:'Fields to return faceted counts for (e.g., ["entityname,count:100","statecode"])'},orderby:{type:"array",items:{type:"string"},description:'Result sort order (e.g., ["@search.score desc","modifiedon desc"])'},select:{type:"array",items:{type:"string"},description:"Columns to return per result. Only applies when entities is also specified."},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["query"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],is=q.object({query:q.string().min(1),entities:q.array(q.string()).optional(),top:q.number().int().positive().max(50).optional().default(10),searchMode:q.enum(["any","all"]).optional().default("any"),searchType:q.enum(["simple","full"]).optional(),filter:q.string().optional(),facets:q.array(q.string()).optional(),orderby:q.array(q.string()).optional(),select:q.array(q.string()).optional(),compact:q.boolean().optional().default(!0)});async function Hn(r,a,t){switch(r){case"dataverse_search":{let e=is.parse(a),n={search:e.query,top:e.top};if(e.entities?.length){let l=e.entities.map(c=>({name:c,...e.select?.length?{selectColumns:e.select}:{}}));n.entities=JSON.stringify(l)}e.searchType==="full"&&(n.options=JSON.stringify({querytype:"lucene"})),e.filter&&(n.filter=e.filter),e.facets?.length&&(n.facets=JSON.stringify(e.facets)),e.orderby?.length&&(n.orderby=JSON.stringify(e.orderby));let i;try{i=await t.search(n)}catch(l){let c=l instanceof Error?l.message:String(l);if(/404|not.?found|search.{0,20}disabl|relevance.?search|0x80048[Dd]0[Bb]|search.{0,20}not.{0,20}config|search.{0,20}unavail/i.test(c))return Ne({type:"feature_disabled",feature:"Dataverse Search (Relevance Search)",cannotProceedBecause:"Relevance Search is not enabled for this Dataverse environment, so full-text cross-table search is unavailable.",adminPortal:"Power Platform Admin Center",steps:["Open Power Platform Admin Center (admin.powerplatform.microsoft.com)","Select your environment \u2192 Settings","Navigate to Product \u2192 Features",'Under "Search", toggle "Dataverse Search" to On',"Save \u2014 indexing may take a few minutes before search is available"]});throw l}let s=(i.value??[]).map(l=>({entityName:l.entityname??"",objectId:l.objectid??"",score:l.score??0,highlights:l.highlights??{},fields:l.attributes??{}}));e.compact&&(s=T(s));let o={totalRecordCount:i.totalrecordcount??0,results:s};return i.facets&&(o.facets=i.facets),f(`${s.length} search results for '${e.query}'`,o,["Use dataverse_get to retrieve full record details","Narrow results with entities[] filter"])}default:throw new Error(`Unknown search tool: ${r}`)}}L();import{z as ce}from"zod";var St={1:"Create",2:"Update",3:"Delete",4:"Activate",5:"Deactivate",11:"Share",12:"Unshare",13:"Assign",104:"Access"},ss=Object.fromEntries(Object.entries(St).map(([r,a])=>[a,Number(r)])),jn=[{name:"dataverse_get_audit_log",description:"Retrieves audit log entries from Dataverse. Returns operation details, user info, and parsed change data for each entry. At least one filter (recordId, entityLogicalName, userId, fromDate, or operations) is recommended to avoid large result sets. Audit must be enabled on the environment and table \u2014 returns a clear error if auditing is disabled (HTTP 403). WHEN TO USE: Tracking who changed what and when on Dataverse records. BEST PRACTICES: Always provide at least one filter; audit must be enabled on the table. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"GUID of a specific record to retrieve audit entries for"},entityLogicalName:{type:"string",description:'Logical name of the entity to filter audit entries (e.g., "account", "contact")'},userId:{type:"string",description:"GUID of the user who made the changes"},fromDate:{type:"string",description:"ISO 8601 date string \u2014 only return audit entries created on or after this date"},top:{type:"number",description:"Maximum number of audit entries to return (default: 50, max: 500)"},operations:{type:"array",items:{type:"string"},description:'Filter by operation names: "Create", "Update", "Delete", "Activate", "Deactivate", "Share", "Unshare", "Assign", "Access"'},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],os=ce.object({recordId:ce.string().uuid().optional(),entityLogicalName:ce.string().min(1).optional(),userId:ce.string().uuid().optional(),fromDate:ce.string().datetime({offset:!0}).optional(),top:ce.number().positive().max(500).optional().default(50),operations:ce.array(ce.string().min(1)).optional(),compact:ce.boolean().optional().default(!0)});function cs(r){if(!r)return{};try{return JSON.parse(r)}catch{return r}}function ls(r){return{auditId:r.auditid,operation:r.operation,operationName:St[r.operation]??`Unknown(${r.operation})`,action:r.action,actionName:St[r.action]??`Unknown(${r.action})`,createdOn:r.createdon,userId:r._userid_value,userFullName:r.userid?.fullname??"",userDomainName:r.userid?.domainname??"",objectId:r._objectid_value,objectTypeCode:r.objecttypecode,changes:cs(r.changedata)}}async function Wn(r,a,t){switch(r){case"dataverse_get_audit_log":{let e=os.parse(a),n=[];if(e.recordId&&n.push(`_objectid_value eq ${e.recordId}`),e.entityLogicalName){let i=h(e.entityLogicalName);n.push(`objecttypecode eq '${i}'`)}if(e.userId&&n.push(`_userid_value eq ${e.userId}`),e.fromDate&&n.push(`createdon ge ${e.fromDate}`),e.operations?.length){let i=e.operations.map(s=>ss[s]).filter(s=>s!==void 0);if(i.length>0){let s=i.map(o=>`action eq ${o}`).join(" or ");n.push(`(${s})`)}}try{let s=(await t.query("audits",{select:["auditid","action","operation","createdon","_objectid_value","objecttypecode","changedata","_userid_value"],...n.length>0?{filter:n.join(" and ")}:{},orderby:"createdon desc",top:e.top,expand:"userid($select=fullname,domainname)"})).value.map(ls);return e.compact&&(s=T(s)),f(`${s.length} audit records for ${e.entityLogicalName??e.recordId??"query"}`,{entries:s,count:s.length},["Filter by operation type for specific changes"])}catch(i){let s=i instanceof Error?i.message:String(i);if(s.includes("403")||s.includes("Forbidden"))return{content:[{type:"text",text:JSON.stringify({isError:!0,error:"Audit log access denied (HTTP 403). Ensure auditing is enabled on the Dataverse environment and the target table, and that the authenticated user has sufficient privileges."})}]};throw i}}default:throw new Error(`Unknown audit tool: ${r}`)}}import{z as Ae}from"zod";var Fn=new Map,Bn=[{name:"dataverse_detect_duplicates",description:"Checks for potential duplicate records before creating, using a FetchXML query that matches records sharing the same field values as the prospective record (OR conditions on the provided fields). Pass the prospective record fields to check against existing records. WHEN TO USE: Before creating a new record to check if a similar record already exists. BEST PRACTICES: Pass only the key identifying fields (e.g., name, email); too many fields will return fewer matches. WORKFLOW: create_record.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Table to check, e.g., "account"'},record:{type:"object",description:"The prospective record fields to check for duplicates"},top:{type:"number",description:"Maximum number of duplicates to return (default 5, max 20)"}},required:["entityLogicalName","record"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}],ds=Ae.object({entityLogicalName:Ae.string().min(1).regex(/^[a-z_][a-z0-9_]*$/i,"Must be a valid Dataverse logical name"),record:Ae.record(Ae.string(),Ae.unknown()),top:Ae.number().int().positive().max(20).optional().default(5)});async function Gn(r,a,t){switch(r){case"dataverse_detect_duplicates":{let i=function(m){return String(m).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")};var e=i;let n=ds.parse(a),s=Object.entries(n.record).filter(([,m])=>m!=null).map(([m,p])=>`<condition attribute="${i(m)}" operator="eq" value="${i(String(p))}" />`).join(`
135
+ `);if(!s)return f("No fields provided for duplicate detection",{hasDuplicates:!1,duplicateCount:0,duplicates:[]},["Provide at least one field value to check for duplicates"]);let o=Fn.get(n.entityLogicalName);o||(o=(await t.getTableMetadata(n.entityLogicalName,!1)).EntitySetName,Fn.set(n.entityLogicalName,o));let l=`<fetch top="${n.top}" distinct="true">
136
+ <entity name="${i(n.entityLogicalName)}">
5
137
  <all-attributes />
6
138
  <filter type="or">
7
- ${o}
139
+ ${s}
8
140
  </filter>
9
141
  </entity>
10
- </fetch>`,l=await n.executeFetchXml(s,c),u=(Array.isArray(l.value)?l.value:[]).map(g=>{let m={};for(let[y,R]of Object.entries(g))y.startsWith("@")||(m[y]=R);return m});return p(`Duplicate detection: ${u.length>0?`${u.length} potential duplicate(s) found`:"no duplicates found"}`,{hasDuplicates:u.length>0,duplicateCount:u.length,duplicates:u,note:"Field exact-match candidates (OR across provided fields). Dataverse duplicate detection rules are not applied via REST API."},["Review candidate records before creating","For rule-based duplicate detection, configure rules in Dataverse admin"])}default:throw new Error(`Unknown quality tool: ${a}`)}}import{z as F}from"zod";var br=F.object({recordId:F.string().uuid(),includeContent:F.boolean().optional().default(!1),top:F.number().int().positive().max(100).optional().default(20),mimeTypeFilter:F.string().optional(),compact:F.boolean().optional().default(!0)}),_r=F.object({recordId:F.string().uuid(),entitySetName:S,noteText:F.string().optional(),subject:F.string().optional(),fileName:F.string().optional(),mimeType:F.string().optional(),documentBody:F.string().optional()}).strict().refine(a=>!!a.noteText||!!a.documentBody,{message:"At least one of noteText or documentBody is required"});function wr(a){return{opportunities:"opportunity",territories:"territory",categories:"category",queues:"queue",activities:"activitypointer"}[a]??a.replace(/s$/,"")}var Bt=[{name:"dataverse_get_annotations",description:"Retrieves notes and file attachments (annotations) linked to a Dataverse record. Returns note text, file metadata (name, size, MIME type), owner, and timestamps. Set includeContent=true to also retrieve base64 file content (warning: can be very large). WHEN TO USE: Retrieving notes or file attachments linked to a specific record. BEST PRACTICES: Avoid includeContent=true unless you need file data \u2014 base64 payloads can be very large. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"The parent record's GUID"},includeContent:{type:"boolean",description:"If true, include documentbody (base64). WARNING: can be very large.",default:!1},top:{type:"number",description:"Maximum number of annotations to return (default 20, max 100)",default:20},mimeTypeFilter:{type:"string",description:'Filter by MIME type (e.g. "application/pdf")'},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["recordId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create_annotation",description:"Creates a note or file attachment (annotation) linked to a Dataverse record. Provide notetext for a text note, documentbody (base64) for a file attachment, or both. The parent record is identified by entitySetName and recordId. WHEN TO USE: Adding a text note or file attachment to an existing record. BEST PRACTICES: Provide filename and mimetype when attaching files; at least notetext or documentbody is required. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"The parent record's GUID"},entitySetName:{type:"string",description:'The OData entity set name of the parent record (e.g., "accounts", "contacts")'},noteText:{type:"string",description:"Text content of the note"},subject:{type:"string",description:"Subject/title of the note"},fileName:{type:"string",description:"File name (required when attaching a file)"},mimeType:{type:"string",description:'MIME type of the file (e.g., "application/pdf")'},documentBody:{type:"string",description:"Base64-encoded file content"}},required:["recordId","entitySetName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function Vt(a,i,n){switch(a){case"dataverse_get_annotations":{let e=br.parse(i),t=["annotationid","subject","notetext","filename","filesize","mimetype","isdocument","createdon","modifiedon","_ownerid_value"];e.includeContent&&t.push("documentbody");let r=[`_objectid_value eq ${e.recordId}`];e.mimeTypeFilter&&r.push(`mimetype eq '${w(e.mimeTypeFilter)}'`);let c=((await n.query("annotations",{select:t,filter:r.join(" and "),orderby:"createdon desc",top:e.top})).value??[]).map(l=>{let d={id:l.annotationid??"",subject:l.subject??null,noteText:l.notetext??null,isDocument:l.isdocument===!0,createdOn:l.createdon??"",modifiedOn:l.modifiedon??"",owner:l._ownerid_value??null};return l.isdocument===!0&&(d.fileName=l.filename??null,d.fileSize=l.filesize??null,d.mimeType=l.mimetype??null),e.includeContent&&l.documentbody&&(d.documentBody=l.documentbody),d});return e.compact&&(c=v(c)),p(`${c.length} annotations found for record ${e.recordId}`,{recordId:e.recordId,annotations:c,count:c.length},["Use dataverse_create_annotation to add a note or attachment"])}case"dataverse_create_annotation":{let e=_r.parse(i),t=wr(e.entitySetName),r={[`objectid_${t}@odata.bind`]:`/${e.entitySetName}(${e.recordId})`,objecttypecode:t};e.noteText!==void 0&&(r.notetext=e.noteText),e.subject!==void 0&&(r.subject=e.subject),e.fileName!==void 0&&(r.filename=e.fileName),e.mimeType!==void 0&&(r.mimetype=e.mimeType),e.documentBody!==void 0&&(r.documentbody=e.documentBody,r.isdocument=!0);let o;try{o=await n.createRecord("annotations",r)}catch(s){let c=String(s);if(c.includes("0x80048d19")||c.includes("objectid_")&&c.includes("undeclared"))return ge({type:"feature_disabled",feature:"Notes (HasNotes)",cannotProceedBecause:`The table '${t}' does not have Notes enabled (HasNotes=false), so annotations cannot be created.`,adminPortal:"Power Apps Maker Portal",steps:["Open Power Apps maker portal (make.powerapps.com)",`Navigate to Tables \u2192 search for '${t}'`,"Open the table \u2192 click Properties or Settings",'Enable "Notes (includes file attachments)"',"Save the table, then publish customizations"],fixableViaToolName:"dataverse_update_entity"});throw s}return p(`Created annotation ${o}`,{created:!0,annotationId:o,parentRecordId:e.recordId,entitySetName:e.entitySetName},["Use dataverse_get_annotations to list all notes for this record"])}default:throw new Error(`Unknown annotation tool: ${a}`)}}import{z as A}from"zod";var Nr=A.object({userId:A.string().uuid(),compact:A.boolean().optional().default(!0)}),Sr=A.object({search:A.string().optional(),businessUnitId:A.string().optional(),includeDisabled:A.boolean().optional().default(!1),includeApplicationUsers:A.boolean().optional().default(!1),top:A.number().int().positive().max(100).optional().default(20),compact:A.boolean().optional().default(!0)}),Tr=A.object({nameContains:A.string().optional(),businessUnitId:A.string().optional(),top:A.number().int().positive().max(200).optional().default(50),compact:A.boolean().optional().default(!0)}),Rr=A.object({userId:A.string().uuid(),roleId:A.string().uuid(),confirm:A.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role"})})}),Ar=A.object({userId:A.string().uuid(),roleId:A.string().uuid(),confirm:A.literal(!0,{errorMap:()=>({message:"confirm must be true to remove a role"})})}),zt=[{name:"dataverse_get_user_roles",description:"Returns all security roles assigned to a Dataverse system user. Provide the user GUID to retrieve full name, domain name (UPN), and the list of roles with role ID, name, and managed status. WHEN TO USE: Checking what security roles a user has for permission auditing or troubleshooting. BEST PRACTICES: Use the userId from dataverse_whoami or dataverse_list_users. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"The system user GUID"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:["userId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_users",description:"Searches Dataverse system users by name or email. Returns user ID, full name, domain name (UPN), email, business unit, and disabled status. Excludes application users and disabled users by default. WHEN TO USE: Finding user GUIDs for impersonation, record assignment, or permission review. BEST PRACTICES: Search by name or email; set includeDisabled=true for former employees. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{search:{type:"string",description:"Full-name or email contains-search"},businessUnitId:{type:"string",description:"Restrict to a business unit (GUID)"},includeDisabled:{type:"boolean",description:"Include disabled users (default false)"},includeApplicationUsers:{type:"boolean",description:"Include application/service users (default false)"},top:{type:"number",description:"Maximum number of results (default 20, max 100)"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_roles",description:"Lists security roles in the Dataverse environment. Returns role ID, name, business unit, and managed status. Use this to discover role GUIDs before assigning roles to users or teams via dataverse_assign_role_to_user or dataverse_assign_role_to_team. WHEN TO USE: Looking up security role GUIDs before RBAC assignment. BEST PRACTICES: Filter by businessUnitId or nameContains to narrow results. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{nameContains:{type:"string",description:"Filter roles by name (case-insensitive substring match)"},businessUnitId:{type:"string",description:"Filter roles to a specific business unit (GUID)"},top:{type:"number",description:"Maximum number of roles to return (default 50, max 200)"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign_role_to_user",description:"Assigns a security role to a Dataverse system user. Use dataverse_list_roles to find the role GUID and dataverse_list_users to find the user GUID. Requires System Administrator or System Customizer privileges. WARNING: This operation modifies user permissions. Set confirm=true to proceed. WHEN TO USE: Granting a security role to a user during onboarding or permission changes. BEST PRACTICES: Verify role and user GUIDs first; check existing roles with dataverse_get_user_roles. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"},roleId:{type:"string",description:"GUID of the security role to assign"},confirm:{type:"boolean",description:"Must be true to proceed with role assignment"}},required:["userId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_remove_role_from_user",description:"Removes a security role from a Dataverse system user. Uses the same N:N disassociation pattern as dataverse_disassociate. WARNING: This modifies user permissions. Set confirm=true to proceed. WHEN TO USE: Revoking access by removing a security role from a user. BEST PRACTICES: Verify the role is assigned first with dataverse_get_user_roles. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"},roleId:{type:"string",description:"GUID of the security role to remove"},confirm:{type:"boolean",description:"Must be true to proceed with role removal"}},required:["userId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function Kt(a,i,n){switch(a){case"dataverse_get_user_roles":{let{userId:e,compact:t}=Nr.parse(i),o=(await n.query("systemusers",{filter:`systemuserid eq ${e}`,select:["fullname","domainname"],expand:"systemuserroles_association($select=name,roleid,ismanaged)",top:1})).value??[];if(o.length===0)throw new Error(`User with ID '${e}' not found`);let s=o[0],l=(s.systemuserroles_association??[]).map(d=>({roleId:d.roleid??"",name:d.name??"",isManaged:d.ismanaged===!0}));return t&&(l=v(l)),p(`${l.length} roles assigned to user`,{userId:e,fullname:s.fullname??"",domainname:s.domainname??"",roles:l,roleCount:l.length},["Use dataverse_impersonate to execute operations as this user"])}case"dataverse_list_users":{let e=Sr.parse(i),t=[];if(e.includeDisabled||t.push("isdisabled eq false"),e.includeApplicationUsers||t.push("applicationid eq null"),e.search){let c=w(e.search);t.push(`(contains(fullname,'${c}') or contains(internalemailaddress,'${c}'))`)}e.businessUnitId&&t.push(`_businessunitid_value eq ${e.businessUnitId}`);let s=((await n.query("systemusers",{select:["systemuserid","fullname","domainname","internalemailaddress","applicationid","isdisabled"],filter:t.join(" and "),expand:"businessunitid($select=name)",orderby:"fullname asc",top:e.top})).value??[]).map(c=>({id:c.systemuserid??"",fullName:c.fullname??"",domainName:c.domainname??"",email:c.internalemailaddress??"",businessUnit:c.businessunitid?.name??null,isDisabled:c.isdisabled===!0,isApplicationUser:c.applicationid!=null}));return e.compact&&(s=v(s)),p(`${s.length} users found`,{users:s,count:s.length},["Use dataverse_get_user_roles to inspect specific user permissions"])}case"dataverse_list_roles":{let{nameContains:e,businessUnitId:t,top:r,compact:o}=Tr.parse(i),s=[];e&&s.push(`contains(name,'${w(e)}')`),t&&s.push(`_businessunitid_value eq ${t}`);let c=s.length?s.join(" and "):void 0,d=((await n.query("roles",{select:["roleid","name","description","ismanaged","_businessunitid_value"],...c!==void 0?{filter:c}:{},orderby:"name asc",top:r})).value??[]).map(u=>({id:u.roleid??"",name:u.name??"",businessUnitId:u._businessunitid_value??null,isManaged:u.ismanaged===!0,description:u.description??""}));return o&&(d=v(d)),p(`${d.length} security roles found`,{roles:d,count:d.length},["Use dataverse_assign_role_to_user or dataverse_assign_role_to_team with the role ID"])}case"dataverse_assign_role_to_user":{let{userId:e,roleId:t}=Rr.parse(i);return(((await n.query("systemusers",{filter:`systemuserid eq ${e}`,select:["systemuserid"],expand:`systemuserroles_association($select=roleid;$filter=roleid eq ${t};$top=1)`,top:1})).value??[])[0]?.systemuserroles_association??[]).length>0?p("Role is already assigned to this user.",{userId:e,roleId:t,status:"already_assigned"},["Use dataverse_get_user_roles to see current role assignments"]):(await n.associate("systemusers",e,"systemuserroles_association","roles",t),p("Role assigned to user successfully.",{userId:e,roleId:t,status:"assigned"},["Use dataverse_get_user_roles to verify the new assignment"]))}case"dataverse_remove_role_from_user":{let{userId:e,roleId:t}=Ar.parse(i);return(((await n.query("systemusers",{filter:`systemuserid eq ${e}`,select:["systemuserid"],expand:`systemuserroles_association($select=roleid;$filter=roleid eq ${t};$top=1)`,top:1})).value??[])[0]?.systemuserroles_association??[]).length===0?p("This role is not currently assigned to the user.",{userId:e,roleId:t,status:"not_assigned"},["Use dataverse_get_user_roles to see current role assignments"]):(await n.disassociate("systemusers",e,"systemuserroles_association",t,"roles"),p("Role removed from user successfully.",{userId:e,roleId:t,status:"removed"},["Use dataverse_get_user_roles to verify the updated assignments"]))}default:throw new Error(`Unknown user tool: ${a}`)}}import{z as G}from"zod";var We={ADD_PRIVILEGES_ROLE:"AddPrivilegesRole",REPLACE_PRIVILEGES_ROLE:"ReplacePrivilegesRole"},Xt={RETRIEVE_ROLE_PRIVILEGES:"RetrieveRolePrivilegesRole",WHO_AM_I:"WhoAmI"};var kr=G.enum(["None","Basic","Local","Deep","Global"]),Er=G.object({roleId:G.string().uuid()}),Yt=G.object({privilegeId:G.string().uuid(),depth:kr,businessUnitId:G.string().uuid().optional()}),xr=G.object({roleId:G.string().uuid(),privileges:G.array(Yt).min(1)}),Dr=G.object({roleId:G.string().uuid(),privileges:G.array(Yt).min(1),confirm:G.literal(!0,{errorMap:()=>({message:"confirm must be true to replace all privileges on the role"})})}),Jt={type:"array",description:"Privileges to assign",minItems:1,items:{type:"object",properties:{privilegeId:{type:"string",description:"GUID of the privilege \u2014 use dataverse_query on 'privileges' entity to find by name or entitylogicalname"},depth:{type:"string",enum:["None","Basic","Local","Deep","Global"],description:"Depth level: Basic=user, Local=BU, Deep=parent BU, Global=org. Use Global for org-owned entities (Basic/Local/Deep fail with error 0x8004140b)."},businessUnitId:{type:"string",description:"Business unit GUID (optional; defaults to root BU if omitted)"}},required:["privilegeId","depth"]}},Qt=[{name:"dataverse_get_role_privileges",description:`Retrieves all privileges assigned to a security role with their depth levels (None/Basic/Local/Deep/Global). Use to audit a role's current permissions before modifying them, or to verify depth assignments. Combine with dataverse_query on 'privileges' entity to resolve privilege names and entity associations.
142
+ </fetch>`,c=await t.executeFetchXml(o,l),u=(Array.isArray(c.value)?c.value:[]).map(m=>{let p={};for(let[y,w]of Object.entries(m))y.startsWith("@")||(p[y]=w);return p});return f(`Duplicate detection: ${u.length>0?`${u.length} potential duplicate(s) found`:"no duplicates found"}`,{hasDuplicates:u.length>0,duplicateCount:u.length,duplicates:u,note:"Field exact-match candidates (OR across provided fields). Dataverse duplicate detection rules are not applied via REST API."},["Review candidate records before creating","For rule-based duplicate detection, configure rules in Dataverse admin"])}default:throw new Error(`Unknown quality tool: ${r}`)}}L();import{z as V}from"zod";var us=V.object({recordId:V.string().uuid(),includeContent:V.boolean().optional().default(!1),top:V.number().int().positive().max(100).optional().default(20),mimeTypeFilter:V.string().optional(),compact:V.boolean().optional().default(!0)}),ps=V.object({recordId:V.string().uuid(),entitySetName:A,noteText:V.string().optional(),subject:V.string().optional(),fileName:V.string().optional(),mimeType:V.string().optional(),documentBody:V.string().optional()}).strict().refine(r=>!!r.noteText||!!r.documentBody,{message:"At least one of noteText or documentBody is required"});function ms(r){return{opportunities:"opportunity",territories:"territory",categories:"category",queues:"queue",activities:"activitypointer"}[r]??r.replace(/s$/,"")}var Vn=[{name:"dataverse_get_annotations",description:"Retrieves notes and file attachments (annotations) linked to a Dataverse record. Returns note text, file metadata (name, size, MIME type), owner, and timestamps. Set includeContent=true to also retrieve base64 file content (warning: can be very large). WHEN TO USE: Retrieving notes or file attachments linked to a specific record. BEST PRACTICES: Avoid includeContent=true unless you need file data \u2014 base64 payloads can be very large. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"The parent record's GUID"},includeContent:{type:"boolean",description:"If true, include documentbody (base64). WARNING: can be very large.",default:!1},top:{type:"number",description:"Maximum number of annotations to return (default 20, max 100)",default:20},mimeTypeFilter:{type:"string",description:'Filter by MIME type (e.g. "application/pdf")'},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["recordId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_create_annotation",description:"Creates a note or file attachment (annotation) linked to a Dataverse record. Provide notetext for a text note, documentbody (base64) for a file attachment, or both. The parent record is identified by entitySetName and recordId. WHEN TO USE: Adding a text note or file attachment to an existing record. BEST PRACTICES: Provide filename and mimetype when attaching files; at least notetext or documentbody is required. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{recordId:{type:"string",description:"The parent record's GUID"},entitySetName:{type:"string",description:'The OData entity set name of the parent record (e.g., "accounts", "contacts")'},noteText:{type:"string",description:"Text content of the note"},subject:{type:"string",description:"Subject/title of the note"},fileName:{type:"string",description:"File name (required when attaching a file)"},mimeType:{type:"string",description:'MIME type of the file (e.g., "application/pdf")'},documentBody:{type:"string",description:"Base64-encoded file content"}},required:["recordId","entitySetName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function zn(r,a,t){switch(r){case"dataverse_get_annotations":{let e=us.parse(a),n=["annotationid","subject","notetext","filename","filesize","mimetype","isdocument","createdon","modifiedon","_ownerid_value"];e.includeContent&&n.push("documentbody");let i=[`_objectid_value eq ${e.recordId}`];e.mimeTypeFilter&&i.push(`mimetype eq '${h(e.mimeTypeFilter)}'`);let l=((await t.query("annotations",{select:n,filter:i.join(" and "),orderby:"createdon desc",top:e.top})).value??[]).map(c=>{let d={id:c.annotationid??"",subject:c.subject??null,noteText:c.notetext??null,isDocument:c.isdocument===!0,createdOn:c.createdon??"",modifiedOn:c.modifiedon??"",owner:c._ownerid_value??null};return c.isdocument===!0&&(d.fileName=c.filename??null,d.fileSize=c.filesize??null,d.mimeType=c.mimetype??null),e.includeContent&&c.documentbody&&(d.documentBody=c.documentbody),d});return e.compact&&(l=T(l)),f(`${l.length} annotations found for record ${e.recordId}`,{recordId:e.recordId,annotations:l,count:l.length},["Use dataverse_create_annotation to add a note or attachment"])}case"dataverse_create_annotation":{let e=ps.parse(a),n=ms(e.entitySetName),i={[`objectid_${n}@odata.bind`]:`/${e.entitySetName}(${e.recordId})`,objecttypecode:n};e.noteText!==void 0&&(i.notetext=e.noteText),e.subject!==void 0&&(i.subject=e.subject),e.fileName!==void 0&&(i.filename=e.fileName),e.mimeType!==void 0&&(i.mimetype=e.mimeType),e.documentBody!==void 0&&(i.documentbody=e.documentBody,i.isdocument=!0);let s;try{s=await t.createRecord("annotations",i)}catch(o){let l=String(o);if(l.includes("0x80048d19")||l.includes("objectid_")&&l.includes("undeclared"))return Ne({type:"feature_disabled",feature:"Notes (HasNotes)",cannotProceedBecause:`The table '${n}' does not have Notes enabled (HasNotes=false), so annotations cannot be created.`,adminPortal:"Power Apps Maker Portal",steps:["Open Power Apps maker portal (make.powerapps.com)",`Navigate to Tables \u2192 search for '${n}'`,"Open the table \u2192 click Properties or Settings",'Enable "Notes (includes file attachments)"',"Save the table, then publish customizations"],fixableViaToolName:"dataverse_update_entity"});throw o}return f(`Created annotation ${s}`,{created:!0,annotationId:s,parentRecordId:e.recordId,entitySetName:e.entitySetName},["Use dataverse_get_annotations to list all notes for this record"])}default:throw new Error(`Unknown annotation tool: ${r}`)}}L();import{z as E}from"zod";var gs=E.object({userId:E.string().uuid(),compact:E.boolean().optional().default(!0)}),fs=E.object({search:E.string().optional(),businessUnitId:E.string().optional(),includeDisabled:E.boolean().optional().default(!1),includeApplicationUsers:E.boolean().optional().default(!1),top:E.number().int().positive().max(100).optional().default(20),compact:E.boolean().optional().default(!0)}),ys=E.object({nameContains:E.string().optional(),businessUnitId:E.string().optional(),top:E.number().int().positive().max(200).optional().default(50),compact:E.boolean().optional().default(!0)}),hs=E.object({userId:E.string().uuid(),roleId:E.string().uuid(),confirm:E.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role"})})}),vs=E.object({userId:E.string().uuid(),roleId:E.string().uuid(),confirm:E.literal(!0,{errorMap:()=>({message:"confirm must be true to remove a role"})})}),Kn=[{name:"dataverse_get_user_roles",description:"Returns all security roles assigned to a Dataverse system user. Provide the user GUID to retrieve full name, domain name (UPN), and the list of roles with role ID, name, and managed status. WHEN TO USE: Checking what security roles a user has for permission auditing or troubleshooting. BEST PRACTICES: Use the userId from dataverse_whoami or dataverse_list_users. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"The system user GUID"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:["userId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_users",description:"Searches Dataverse system users by name or email. Returns user ID, full name, domain name (UPN), email, business unit, and disabled status. Excludes application users and disabled users by default. WHEN TO USE: Finding user GUIDs for impersonation, record assignment, or permission review. BEST PRACTICES: Search by name or email; set includeDisabled=true for former employees. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{search:{type:"string",description:"Full-name or email contains-search"},businessUnitId:{type:"string",description:"Restrict to a business unit (GUID)"},includeDisabled:{type:"boolean",description:"Include disabled users (default false)"},includeApplicationUsers:{type:"boolean",description:"Include application/service users (default false)"},top:{type:"number",description:"Maximum number of results (default 20, max 100)"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_list_roles",description:"Lists security roles in the Dataverse environment. Returns role ID, name, business unit, and managed status. Use this to discover role GUIDs before assigning roles to users or teams via dataverse_assign_role_to_user or dataverse_assign_role_to_team. WHEN TO USE: Looking up security role GUIDs before RBAC assignment. BEST PRACTICES: Filter by businessUnitId or nameContains to narrow results. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{nameContains:{type:"string",description:"Filter roles by name (case-insensitive substring match)"},businessUnitId:{type:"string",description:"Filter roles to a specific business unit (GUID)"},top:{type:"number",description:"Maximum number of roles to return (default 50, max 200)"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign_role_to_user",description:"Assigns a security role to a Dataverse system user. Use dataverse_list_roles to find the role GUID and dataverse_list_users to find the user GUID. Requires System Administrator or System Customizer privileges. WARNING: This operation modifies user permissions. Set confirm=true to proceed. WHEN TO USE: Granting a security role to a user during onboarding or permission changes. BEST PRACTICES: Verify role and user GUIDs first; check existing roles with dataverse_get_user_roles. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"},roleId:{type:"string",description:"GUID of the security role to assign"},confirm:{type:"boolean",description:"Must be true to proceed with role assignment"}},required:["userId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_remove_role_from_user",description:"Removes a security role from a Dataverse system user. Uses the same N:N disassociation pattern as dataverse_disassociate. WARNING: This modifies user permissions. Set confirm=true to proceed. WHEN TO USE: Revoking access by removing a security role from a user. BEST PRACTICES: Verify the role is assigned first with dataverse_get_user_roles. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"},roleId:{type:"string",description:"GUID of the security role to remove"},confirm:{type:"boolean",description:"Must be true to proceed with role removal"}},required:["userId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function Xn(r,a,t){switch(r){case"dataverse_get_user_roles":{let{userId:e,compact:n}=gs.parse(a),s=(await t.query("systemusers",{filter:`systemuserid eq ${e}`,select:["fullname","domainname"],expand:"systemuserroles_association($select=name,roleid,ismanaged)",top:1})).value??[];if(s.length===0)throw new Error(`User with ID '${e}' not found`);let o=s[0],c=(o.systemuserroles_association??[]).map(d=>({roleId:d.roleid??"",name:d.name??"",isManaged:d.ismanaged===!0}));return n&&(c=T(c)),f(`${c.length} roles assigned to user`,{userId:e,fullname:o.fullname??"",domainname:o.domainname??"",roles:c,roleCount:c.length},["Use dataverse_impersonate to execute operations as this user"])}case"dataverse_list_users":{let e=fs.parse(a),n=[];if(e.includeDisabled||n.push("isdisabled eq false"),e.includeApplicationUsers||n.push("applicationid eq null"),e.search){let l=h(e.search);n.push(`(contains(fullname,'${l}') or contains(internalemailaddress,'${l}'))`)}e.businessUnitId&&n.push(`_businessunitid_value eq ${e.businessUnitId}`);let o=((await t.query("systemusers",{select:["systemuserid","fullname","domainname","internalemailaddress","applicationid","isdisabled"],filter:n.join(" and "),expand:"businessunitid($select=name)",orderby:"fullname asc",top:e.top})).value??[]).map(l=>({id:l.systemuserid??"",fullName:l.fullname??"",domainName:l.domainname??"",email:l.internalemailaddress??"",businessUnit:l.businessunitid?.name??null,isDisabled:l.isdisabled===!0,isApplicationUser:l.applicationid!=null}));return e.compact&&(o=T(o)),f(`${o.length} users found`,{users:o,count:o.length},["Use dataverse_get_user_roles to inspect specific user permissions"])}case"dataverse_list_roles":{let{nameContains:e,businessUnitId:n,top:i,compact:s}=ys.parse(a),o=[];e&&o.push(`contains(name,'${h(e)}')`),n&&o.push(`_businessunitid_value eq ${n}`);let l=o.length?o.join(" and "):void 0,d=((await t.query("roles",{select:["roleid","name","description","ismanaged","_businessunitid_value"],...l!==void 0?{filter:l}:{},orderby:"name asc",top:i})).value??[]).map(u=>({id:u.roleid??"",name:u.name??"",businessUnitId:u._businessunitid_value??null,isManaged:u.ismanaged===!0,description:u.description??""}));return s&&(d=T(d)),f(`${d.length} security roles found`,{roles:d,count:d.length},["Use dataverse_assign_role_to_user or dataverse_assign_role_to_team with the role ID"])}case"dataverse_assign_role_to_user":{let{userId:e,roleId:n}=hs.parse(a);return(((await t.query("systemusers",{filter:`systemuserid eq ${e}`,select:["systemuserid"],expand:`systemuserroles_association($select=roleid;$filter=roleid eq ${n};$top=1)`,top:1})).value??[])[0]?.systemuserroles_association??[]).length>0?f("Role is already assigned to this user.",{userId:e,roleId:n,status:"already_assigned"},["Use dataverse_get_user_roles to see current role assignments"]):(await t.associate("systemusers",e,"systemuserroles_association","roles",n),f("Role assigned to user successfully.",{userId:e,roleId:n,status:"assigned"},["Use dataverse_get_user_roles to verify the new assignment"]))}case"dataverse_remove_role_from_user":{let{userId:e,roleId:n}=vs.parse(a);return(((await t.query("systemusers",{filter:`systemuserid eq ${e}`,select:["systemuserid"],expand:`systemuserroles_association($select=roleid;$filter=roleid eq ${n};$top=1)`,top:1})).value??[])[0]?.systemuserroles_association??[]).length===0?f("This role is not currently assigned to the user.",{userId:e,roleId:n,status:"not_assigned"},["Use dataverse_get_user_roles to see current role assignments"]):(await t.disassociate("systemusers",e,"systemuserroles_association",n,"roles"),f("Role removed from user successfully.",{userId:e,roleId:n,status:"removed"},["Use dataverse_get_user_roles to verify the updated assignments"]))}default:throw new Error(`Unknown user tool: ${r}`)}}import{z}from"zod";var Nt={ADD_PRIVILEGES_ROLE:"AddPrivilegesRole",REPLACE_PRIVILEGES_ROLE:"ReplacePrivilegesRole"},Jn={RETRIEVE_ROLE_PRIVILEGES:"RetrieveRolePrivilegesRole",WHO_AM_I:"WhoAmI"};var bs=z.enum(["None","Basic","Local","Deep","Global"]),_s=z.object({roleId:z.string().uuid()}),Qn=z.object({privilegeId:z.string().uuid(),depth:bs,businessUnitId:z.string().uuid().optional()}),ws=z.object({roleId:z.string().uuid(),privileges:z.array(Qn).min(1)}),Ts=z.object({roleId:z.string().uuid(),privileges:z.array(Qn).min(1),confirm:z.literal(!0,{errorMap:()=>({message:"confirm must be true to replace all privileges on the role"})})}),Yn={type:"array",description:"Privileges to assign",minItems:1,items:{type:"object",properties:{privilegeId:{type:"string",description:"GUID of the privilege \u2014 use dataverse_query on 'privileges' entity to find by name or entitylogicalname"},depth:{type:"string",enum:["None","Basic","Local","Deep","Global"],description:"Depth level: Basic=user, Local=BU, Deep=parent BU, Global=org. Use Global for org-owned entities (Basic/Local/Deep fail with error 0x8004140b)."},businessUnitId:{type:"string",description:"Business unit GUID (optional; defaults to root BU if omitted)"}},required:["privilegeId","depth"]}},Zn=[{name:"dataverse_get_role_privileges",description:`Retrieves all privileges assigned to a security role with their depth levels (None/Basic/Local/Deep/Global). Use to audit a role's current permissions before modifying them, or to verify depth assignments. Combine with dataverse_query on 'privileges' entity to resolve privilege names and entity associations.
11
143
 
12
- \u26A0\uFE0F NOTE \u2014 Direct OData query on 'roleprivileges': The entity set name for roleprivileges is 'roleprivilegess' (double 's'). This means OData queries must target /api/data/v9.2/roleprivilegess. For direct query use: dataverse_query with entitySetName='roleprivilegess'. For FetchXML join pattern (role \u2192 privilege depths), use: <fetch><entity name='role'><attribute name='name'/><link-entity name='roleprivileges' from='roleid' to='roleid'><attribute name='privilegedepthmask'/><link-entity name='privilege' from='privilegeid' to='privilegeid'><attribute name='name'/></link-entity></link-entity></entity></fetch>. WHEN TO USE: Auditing which privileges and depths a security role has. BEST PRACTICES: Use dataverse_list_roles to find the role GUID first. Use this tool (RetrieveRolePrivilegesRole) for the clean structured result; use FetchXML for joins with privilege names in one shot. WORKFLOW: inspect_audit.`,inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"}},required:["roleId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_add_role_privileges",description:"Adds one or more privileges to a security role with specified depth levels using the AddPrivilegesRole action. Supports all depths: None, Basic (user-level), Local (BU-level), Deep (parent-BU), Global (org-level). For org-owned entities only Global is valid \u2014 Basic/Local/Deep return error 0x8004140b. WHEN TO USE: Setting up or extending a role's permissions. BEST PRACTICES: Get privilege GUIDs from dataverse_query on 'privileges' entity; use dataverse_replace_role_privileges to atomically reset all at once. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"},privileges:Jt},required:["roleId","privileges"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_replace_role_privileges",description:"Atomically replaces ALL privileges on a security role using the ReplacePrivilegesRole action. The new set completely overrides the existing privileges \u2014 any privilege not in the list is removed. Use for initial setup or full reconfiguration of a role. WHEN TO USE: Complete reconfiguration of a role's privilege set. BEST PRACTICES: Read current privileges with dataverse_get_role_privileges first to avoid accidental removal. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"},privileges:Jt,confirm:{type:"boolean",description:"Must be true \u2014 replaces ALL existing privileges on the role"}},required:["roleId","privileges","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}}];async function Zt(a,i,n){switch(a){case"dataverse_get_role_privileges":{let{roleId:e}=Er.parse(i),r=(await n.executeFunction(Xt.RETRIEVE_ROLE_PRIVILEGES,{RoleId:e}))?.RolePrivileges??[];return p(`${r.length} privileges on role`,{roleId:e,privileges:r,count:r.length},["Use dataverse_add_role_privileges to modify privileges or depths"])}case"dataverse_add_role_privileges":{let e=xr.parse(i),t=e.privileges.map(r=>({"@odata.type":"Microsoft.Dynamics.CRM.RolePrivilege",Depth:r.depth,PrivilegeId:r.privilegeId,...r.businessUnitId?{BusinessUnitId:r.businessUnitId}:{}}));return await n.executeBoundAction("roles",e.roleId,We.ADD_PRIVILEGES_ROLE,{Privileges:t}),p(`${e.privileges.length} privileges added to role`,{roleId:e.roleId,count:e.privileges.length},["Use dataverse_get_role_privileges to verify the assignment"])}case"dataverse_replace_role_privileges":{let e=Dr.parse(i),t=e.privileges.map(r=>({"@odata.type":"Microsoft.Dynamics.CRM.RolePrivilege",Depth:r.depth,PrivilegeId:r.privilegeId,...r.businessUnitId?{BusinessUnitId:r.businessUnitId}:{}}));return await n.executeBoundAction("roles",e.roleId,We.REPLACE_PRIVILEGES_ROLE,{Privileges:t}),p(`All privileges replaced on role (${e.privileges.length} total)`,{roleId:e.roleId,count:e.privileges.length},["Use dataverse_get_role_privileges to verify the new privilege set"])}default:throw new Error(`Unknown role privilege tool: ${a}`)}}import{z as Ne}from"zod";var Ir=Ne.object({entityLogicalName:Ne.string().min(1),includePersonal:Ne.boolean().optional().default(!1),top:Ne.number().int().positive().max(100).optional().default(20),compact:Ne.boolean().optional().default(!0)}),ea=[{name:"dataverse_list_views",description:"Lists saved views (system and optionally personal) for a Dataverse table. System views come from savedqueries; personal views come from userqueries. Returns view name, ID, default flag, query type, and description. WHEN TO USE: Discovering saved system or personal views for a table. BEST PRACTICES: Use the view's FetchXML with dataverse_execute_fetchxml to run it. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Logical name of the entity to list views for (e.g., "account")'},includePersonal:{type:"boolean",description:"Include personal (user) views in addition to system views (default false)"},top:{type:"number",description:"Maximum number of results per category (default 20, max 100)"},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["entityLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function ta(a,i,n){switch(a){case"dataverse_list_views":{let{entityLogicalName:e,includePersonal:t,top:r,compact:o}=Ir.parse(i),s=w(e),c=await n.query("savedqueries",{filter:`returnedtypecode eq '${s}' and statecode eq 0`,select:["savedqueryid","name","isdefault","querytype","description"],orderby:"name asc",top:r}),l=o?v(c.value.map(g=>({id:g.savedqueryid,name:g.name,isDefault:g.isdefault,queryType:g.querytype,description:g.description??null,viewType:"system"}))):c.value.map(g=>({id:g.savedqueryid,name:g.name,isDefault:g.isdefault,queryType:g.querytype,description:g.description??null,viewType:"system"})),d=[];t&&(d=(await n.query("userqueries",{filter:`returnedtypecode eq '${s}'`,select:["userqueryid","name","description"],orderby:"name asc",top:r})).value.map(m=>({id:m.userqueryid,name:m.name,description:m.description??null,viewType:"personal"})),o&&(d=v(d)));let u=l.length+d.length;return p(`${u} views found for ${e}`,{entityLogicalName:e,systemViews:l,systemViewCount:l.length,personalViews:t?d:void 0,personalViewCount:t?d.length:void 0},["Use the view's fetchxml with dataverse_execute_fetchxml to run it"])}default:throw new Error(`Unknown view tool: ${a}`)}}import{z as De}from"zod";var Cr=De.object({top:De.number().int().positive().max(200).optional().default(50),includeDisabled:De.boolean().optional().default(!1),compact:De.boolean().optional().default(!0)}),aa=[{name:"dataverse_list_business_units",description:"Lists business units in the Dataverse environment. Returns name, ID, parent business unit ID, disabled status, and creation date. By default only active business units are returned. WHEN TO USE: Finding business unit IDs for user management, team setup, or security role scope. BEST PRACTICES: The root business unit is the parent of all others; filter disabled units by default. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Maximum number of results (default 50, max 200)"},includeDisabled:{type:"boolean",description:"Include disabled business units (default false)"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function na(a,i,n){switch(a){case"dataverse_list_business_units":{let{top:e,includeDisabled:t,compact:r}=Cr.parse(i??{}),o=t?void 0:"isdisabled eq false",c=(await n.query("businessunits",{select:["businessunitid","name","parentbusinessunitid","isdisabled","createdon"],...o?{filter:o}:{},orderby:"name asc",top:e})).value.map(l=>({id:l.businessunitid,name:l.name,parentBusinessUnitId:l.parentbusinessunitid??null,isDisabled:l.isdisabled,createdOn:l.createdon}));return r&&(c=v(c)),p(`${c.length} business units found`,{businessUnits:c,count:c.length},["Use business unit IDs for user management or security role assignment"])}default:throw new Error(`Unknown org tool: ${a}`)}}import{z as Y}from"zod";var ra=/^[a-zA-Z0-9_]+$/,ia=/\.\.|[/\\]/,Or=Y.object({entitySetName:Y.string().min(1).refine(a=>!ia.test(a),{message:"entitySetName must not contain path traversal characters"}),recordId:Y.string().uuid(),columnName:Y.string().min(1).refine(a=>ra.test(a),{message:"columnName must be alphanumeric/underscore only"}),fileContent:Y.string().min(1).describe("Base64-encoded file content"),fileName:Y.string().min(1)}),Lr=Y.object({entitySetName:Y.string().min(1).refine(a=>!ia.test(a),{message:"entitySetName must not contain path traversal characters"}),recordId:Y.string().uuid(),columnName:Y.string().min(1).refine(a=>ra.test(a),{message:"columnName must be alphanumeric/underscore only"})}),oa=[{name:"dataverse_upload_file_column",description:"Uploads a file to a Dataverse file column. Provide the entity set name, record GUID, column name, base64-encoded file content, and file name. The file is stored in the specified file-type column on the record. WHEN TO USE: Uploading a file to a file-type column on a Dataverse record. BEST PRACTICES: Base64-encode the file content; verify the column is a file-type column via dataverse_get_table_metadata. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},recordId:{type:"string",description:"Record GUID"},columnName:{type:"string",description:"File column logical name"},fileContent:{type:"string",description:"Base64-encoded file content"},fileName:{type:"string",description:'File name including extension (e.g., "report.pdf")'}},required:["entitySetName","recordId","columnName","fileContent","fileName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_download_file_column",description:"Downloads a file from a Dataverse file column. Returns the file content as a base64-encoded string along with the file name and size. WHEN TO USE: Downloading a file stored in a file-type column on a Dataverse record. BEST PRACTICES: The response is base64-encoded; decode before saving or processing. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},recordId:{type:"string",description:"Record GUID"},columnName:{type:"string",description:"File column logical name"}},required:["entitySetName","recordId","columnName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function sa(a,i,n){let e=n.http;switch(a){case"dataverse_upload_file_column":{let{entitySetName:t,recordId:r,columnName:o,fileContent:s,fileName:c}=Or.parse(i),l=Buffer.from(s,"base64"),d=`${t}(${r})/${o}`;return await e.patch(d,l,{headers:{"Content-Type":"application/octet-stream","x-ms-file-name":c}}),p(`Uploaded file to ${o} on ${t}(${r})`,{success:!0,entitySetName:t,recordId:r,columnName:o,fileName:c,sizeBytes:l.length},["Use dataverse_download_file_column to retrieve the file"])}case"dataverse_download_file_column":{let{entitySetName:t,recordId:r,columnName:o}=Lr.parse(i),s=`${t}(${r})/${o}/$value`,c=await e.get(s,{responseType:"text"}),l=c.data,d=Buffer.from(l,"binary").toString("base64"),u=c.headers["x-ms-file-name"]??c.headers["content-disposition"]?.match(/filename="?([^";\n]+)"?/)?.[1]??"download",g=parseInt(c.headers["content-length"]??"0",10)||d.length;return p(`Downloaded file from ${o} on ${t}(${r})`,{entitySetName:t,recordId:r,columnName:o,fileName:u,sizeBytes:g,contentBase64:d})}default:throw new Error(`Unknown file tool: ${a}`)}}import{z as ye}from"zod";var Ur={SVG:11,PNG:5,JPG:6,GIF:7,ICO:10},Pr=ye.object({name:ye.string().min(1).describe("Unique name with publisher prefix (e.g., 'new_myicon.svg'). This is the value you pass as iconVectorName in dataverse_set_table_icon or dataverse_create_table."),displayName:ye.string().min(1).describe("Human-friendly display name."),webResourceType:ye.enum(["SVG","PNG","JPG","GIF","ICO"]).describe("Web resource type. Use 'SVG' for IconVectorName table icons."),contentBase64:ye.string().min(1).describe("Base64-encoded file content."),description:ye.string().optional().describe("Optional description of the web resource.")}),la=[{name:"dataverse_create_web_resource",description:"Creates a web resource (SVG, PNG, JPG, GIF, ICO) in Dataverse. REQUIRED PREREQUISITE for table icons \u2014 you MUST call this first before dataverse_set_table_icon or dataverse_create_table. The name you choose (e.g. 'new_icon.svg') becomes the iconVectorName value in the next step. Provide the base64-encoded file content. For table icons, use webResourceType='SVG' (type code 11) \u2014 SVG icons scale sharply at any size. WHEN TO USE: Uploading a custom icon image that will be assigned to a model-driven app table. NEXT STEP: After creation, use dataverse_set_table_icon with the returned name as iconVectorName. WORKFLOW: table_icons.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Unique name with publisher prefix (e.g., 'new_myicon.svg'). This name becomes your iconVectorName for the next step."},displayName:{type:"string",description:"Human-friendly display name."},webResourceType:{type:"string",enum:["SVG","PNG","JPG","GIF","ICO"],description:"Web resource type. Use 'SVG' for IconVectorName table icons."},contentBase64:{type:"string",description:"Base64-encoded file content."},description:{type:"string",description:"Optional description of the web resource."}},required:["name","displayName","webResourceType","contentBase64"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function ca(a,i,n){switch(a){case"dataverse_create_web_resource":{let e=Pr.parse(i),t=Ur[e.webResourceType],r={name:e.name,displayname:e.displayName,webresourcetype:t,content:e.contentBase64};e.description&&(r.description=e.description);let o;try{o=await n.createWebResource(r)}catch(s){let c=s instanceof Error?s.message:String(s);if(c.includes("duplicate")||c.includes("0x80040237"))return P({summary:`Web resource '${e.name}' already exists. Use a unique name or remove the existing one first.`,data:{error:"DUPLICATE_NAME",name:e.name},errorCategory:"VALIDATION",suggestions:["Choose a different name for the new web resource"]});throw s}return p(`Web resource '${e.name}' created (${e.webResourceType}, id: ${o}). Use this name as iconVectorName when creating or updating a table.`,{webResourceId:o,name:e.name,displayName:e.displayName,webResourceType:e.webResourceType,typeCode:t},[`Use dataverse_create_table with iconVectorName: '${e.name}'`,`Use dataverse_set_table_icon with iconVectorName: '${e.name}'`])}default:throw new Error(`Unknown webresource tool: ${a}`)}}import{z as V}from"zod";var Mr={0:"Owner",1:"Access",2:"Office",3:"Security"},Hr=V.object({top:V.number().int().positive().max(200).optional().default(50),teamType:V.union([V.literal(0),V.literal(1),V.literal(2),V.literal(3)]).optional(),compact:V.boolean().optional().default(!0)}),$r=V.object({teamId:V.string().uuid(),roleId:V.string().uuid(),confirm:V.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role to a team"})})}),da=[{name:"dataverse_list_teams",description:"Lists Dataverse teams in the environment. Useful for finding team owners for record assignment and sharing. teamtype: 0=Owner, 1=Access, 2=AAD Office Group, 3=AAD Security Group. WHEN TO USE: Finding team IDs for record assignment or sharing. BEST PRACTICES: Filter by teamType to distinguish owner, access, and AAD group teams. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Maximum number of teams to return (1\u2013200, default 50)"},teamType:{type:"number",enum:[0,1,2,3],description:"Filter by team type: 0=Owner, 1=Access, 2=Office Group, 3=Security Group"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign_role_to_team",description:"Assigns a security role to a Dataverse team. All team members inherit the role permissions. Use dataverse_list_roles to find the role GUID and dataverse_list_teams to find the team GUID. WARNING: This modifies team permissions for ALL members. Set confirm=true to proceed. WHEN TO USE: Providing role-based permissions to an entire team during configuration. BEST PRACTICES: Prefer team-based RBAC over individual user assignments for maintainability. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{teamId:{type:"string",description:"GUID of the team"},roleId:{type:"string",description:"GUID of the security role to assign"},confirm:{type:"boolean",description:"Must be true to proceed with role assignment"}},required:["teamId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function ua(a,i,n){switch(a){case"dataverse_list_teams":{let{top:e,teamType:t,compact:r}=Hr.parse(i),s={select:["teamid","name","teamtype","description","isdefault","createdon","_businessunitid_value"],top:e,orderby:"name asc"};t!==void 0&&(s.filter=`teamtype eq ${t}`);let l=(await n.query("teams",s)).value.map(d=>({...d,teamTypeName:Mr[d.teamtype]??"Unknown"}));return r&&(l=v(l)),p(`${l.length} teams found`,{teams:l,count:l.length},["Use dataverse_assign with a team ID to assign records to a team"])}case"dataverse_assign_role_to_team":{let{teamId:e,roleId:t}=$r.parse(i);return(((await n.query("teams",{filter:`teamid eq ${e}`,select:["teamid"],expand:`teamroles_association($select=roleid;$filter=roleid eq ${t};$top=1)`,top:1})).value??[])[0]?.teamroles_association??[]).length>0?p("Role is already assigned to this team.",{teamId:e,roleId:t,status:"already_assigned"},["Use dataverse_list_teams to view current team configuration"]):(await n.associate("teams",e,"teamroles_association","roles",t),p("Role assigned to team successfully.",{teamId:e,roleId:t,status:"assigned"},["All members of this team now inherit the assigned role"]))}default:throw new Error(`Unknown team tool: ${a}`)}}var Se={dataverse_query:["query","read","odata"],dataverse_execute_fetchxml:["query","read","fetchxml","aggregate"],dataverse_create:["write","crud","create"],dataverse_update:["write","crud","update"],dataverse_delete:["write","crud","delete","destructive"],dataverse_get:["read","crud","get"],dataverse_upsert:["write","crud","upsert"],dataverse_retrieve_multiple_with_paging:["query","read","paging"],dataverse_search:["search","read","fulltext"],dataverse_list_tables:["metadata","schema","read"],dataverse_get_table_metadata:["metadata","schema","read"],dataverse_get_relationships:["metadata","schema","relations","read"],dataverse_get_option_set:["metadata","schema","read"],dataverse_get_attribute_option_set:["metadata","schema","read"],dataverse_list_global_option_sets:["metadata","schema","read"],dataverse_get_entity_key:["metadata","schema","read"],dataverse_resolve_entity_name:["metadata","schema","read"],dataverse_create_attribute:["metadata","schema","write","attribute"],dataverse_update_attribute:["metadata","schema","write","attribute"],dataverse_delete_attribute:["metadata","schema","write","attribute","destructive"],dataverse_create_lookup_attribute:["metadata","schema","write","attribute","relations"],dataverse_update_entity:["metadata","schema","write"],dataverse_publish_customizations:["customization","write"],dataverse_create_sitemap:["customization","sitemap","write","manage_solution"],dataverse_list_views:["metadata","views","read"],dataverse_execute_action:["actions","write","custom"],dataverse_execute_bound_action:["actions","write","custom"],dataverse_execute_function:["actions","read","custom"],dataverse_execute_bound_function:["actions","read","custom"],dataverse_batch_execute:["write","bulk","batch"],dataverse_whoami:["auth","identity","read"],dataverse_impersonate:["security","admin"],dataverse_associate:["relations","write"],dataverse_disassociate:["relations","write"],dataverse_associate_bulk:["relations","write","bulk"],dataverse_query_associations:["relations","read"],dataverse_list_roles:["admin","security","roles","read"],dataverse_get_role_privileges:["admin","security","roles","read"],dataverse_add_role_privileges:["admin","security","roles","write"],dataverse_replace_role_privileges:["admin","security","roles","write","destructive"],dataverse_assign_role_to_user:["admin","security","users","write"],dataverse_remove_role_from_user:["admin","security","users","write"],dataverse_assign_role_to_team:["admin","security","teams","write"],dataverse_assign:["write","crud"],dataverse_list_users:["admin","users","read"],dataverse_get_user_roles:["admin","users","security","read"],dataverse_list_teams:["admin","teams","read"],dataverse_list_business_units:["admin","teams","read"],dataverse_list_workflows:["workflows","read"],dataverse_get_workflow:["workflows","read"],dataverse_set_workflow_state:["workflows","write"],dataverse_environment_capabilities:["admin","environments","read"],dataverse_get_environment_variable:["admin","environments","read"],dataverse_set_environment_variable:["admin","environments","write"],dataverse_create_environment_variable:["admin","environments","write"],dataverse_get_audit_log:["audit","read"],dataverse_get_plugin_trace_logs:["debug","trace","read"],dataverse_get_workflow_trace_logs:["debug","trace","read"],dataverse_list_plugin_steps:["debug","plugins","read"],dataverse_get_annotations:["notes","read"],dataverse_create_annotation:["notes","write"],dataverse_download_file_column:["files","read","download"],dataverse_upload_file_column:["files","write","upload"],dataverse_change_detection:["sync","delta","read"],dataverse_detect_duplicates:["quality","read","validation"],dataverse_list_dependencies:["metadata","schema","read"],dataverse_retrieve_dependencies_for_delete:["metadata","schema","read"],dataverse_list_connection_references:["metadata","read"],dataverse_list_custom_actions:["actions","metadata","read"],dataverse_get_guide:["guides","help","read"],dataverse_list_guides:["guides","help","read"],dataverse_suggest_tools:["router","help","read"],dataverse_list_tool_tags:["router","help","read"],dataverse_create_table:["metadata","schema","write","entity","table"],dataverse_create_relationship:["metadata","schema","write","relationship"],dataverse_check_record_access:["security","rbac","sharing","read","record"],dataverse_grant_access:["security","rbac","sharing","write","record"],dataverse_revoke_access:["security","rbac","sharing","write","record"],dataverse_merge_records:["write","crud","deduplication","merge","destructive"]},Fe={dataverse_query:"Query Dataverse tables using OData syntax with filters, select, expand",dataverse_execute_fetchxml:"Execute FetchXML queries with aggregation, groupby, and joins",dataverse_create:"Create a new record in a Dataverse table",dataverse_update:"Update an existing Dataverse record",dataverse_delete:"Delete a Dataverse record permanently",dataverse_get:"Retrieve a single record by GUID",dataverse_upsert:"Create or update a record by alternate key",dataverse_retrieve_multiple_with_paging:"Query with automatic paging support for large datasets",dataverse_search:"Full-text search across multiple Dataverse tables using Relevance Search",dataverse_list_tables:"List available Dataverse tables (entities)",dataverse_get_table_metadata:"Get column definitions and schema for a table",dataverse_get_relationships:"Get one-to-many and many-to-many relationships for a table",dataverse_get_option_set:"Get a global or local option set definition",dataverse_get_attribute_option_set:"Get option values for a specific column (OptionSet/MultiSelectPicklist)",dataverse_list_global_option_sets:"List all global option sets in the environment",dataverse_get_entity_key:"Get alternate key definitions for a table",dataverse_resolve_entity_name:"Resolve a table logical name to its entity set name (OData collection name)",dataverse_create_attribute:"Create a new column on a Dataverse table",dataverse_update_attribute:"Update mutable properties of an existing column",dataverse_delete_attribute:"Delete a column and all its data from a table",dataverse_create_lookup_attribute:"Create a Lookup column via a One-to-Many relationship",dataverse_update_entity:"Update mutable entity-level properties (display name, description, etc.)",dataverse_publish_customizations:"Publish unpublished schema customizations to make them available to users",dataverse_create_sitemap:"Create a model-driven app sitemap (navigation structure) by generating XML and posting to /sitemaps; optionally links it to an app module",dataverse_list_views:"List saved views (saved queries) for a table",dataverse_execute_action:"Execute an unbound custom action",dataverse_execute_bound_action:"Execute a bound custom action on a specific record",dataverse_execute_function:"Execute an unbound function (read-only)",dataverse_execute_bound_function:"Execute a bound function on a specific record (read-only)",dataverse_batch_execute:"Execute multiple read/write operations in a single HTTP batch request",dataverse_whoami:"Get current authenticated user identity and organization context",dataverse_impersonate:"Set impersonation (run subsequent calls on behalf of another user)",dataverse_associate:"Associate two records via a relationship",dataverse_disassociate:"Remove an association between two records",dataverse_associate_bulk:"Associate multiple records in a single batch",dataverse_query_associations:"Query the associated records through a relationship",dataverse_list_roles:"List security roles in the environment",dataverse_get_role_privileges:"Get all privileges and their depth levels assigned to a security role",dataverse_add_role_privileges:"Add privileges to a security role with specified depth",dataverse_replace_role_privileges:"Atomically replace all privileges on a security role",dataverse_assign_role_to_user:"Assign a security role to a user",dataverse_remove_role_from_user:"Remove a security role from a user",dataverse_assign_role_to_team:"Assign a security role to a team",dataverse_assign:"Assign a record to a different owner (user or team)",dataverse_list_users:"List system users in the environment",dataverse_get_user_roles:"Get security roles assigned to a specific user",dataverse_list_teams:"List teams in the environment",dataverse_list_business_units:"List business units in the organization hierarchy",dataverse_list_workflows:"List workflows and Power Automate flows",dataverse_get_workflow:"Get the definition and status of a specific workflow",dataverse_set_workflow_state:"Activate or deactivate a workflow",dataverse_environment_capabilities:"Get environment capabilities: identity, settings, solutions, variable counts",dataverse_get_environment_variable:"Get the value and schema of an environment variable",dataverse_set_environment_variable:"Set the current value of an environment variable",dataverse_create_environment_variable:"Create a new environment variable definition and optionally its value",dataverse_get_audit_log:"Get the audit history for a specific record",dataverse_get_plugin_trace_logs:"Get plugin trace logs for debugging plugin execution errors",dataverse_get_workflow_trace_logs:"Get execution trace logs for a Power Automate workflow run",dataverse_list_plugin_steps:"List registered plugin steps (SDK message processing steps)",dataverse_get_annotations:"Get notes/annotations attached to a record",dataverse_create_annotation:"Create a note (annotation) on a record",dataverse_download_file_column:"Download the binary content of a file-type column",dataverse_upload_file_column:"Upload base64-encoded content to a file-type column",dataverse_change_detection:"Detect new or changed records since a delta token (change tracking)",dataverse_detect_duplicates:"Detect potential duplicate records before creating a new one",dataverse_list_dependencies:"List workflows, flows, and plugins that reference a table",dataverse_retrieve_dependencies_for_delete:"Check which components block deletion of a specific component",dataverse_list_connection_references:"List Power Automate connection references in the environment",dataverse_list_custom_actions:"List registered custom API actions",dataverse_get_guide:"Get a contextual MCP usage guide for a specific workflow",dataverse_list_guides:"List all available MCP usage guides",dataverse_suggest_tools:"Get top-5 tool suggestions for a natural-language intent",dataverse_list_tool_tags:"List all tool tags with tool counts per category",dataverse_create_table:"Create a new custom table (entity) in Dataverse via POST /EntityDefinitions",dataverse_create_relationship:"Create a One-to-Many or Many-to-Many relationship between two Dataverse tables",dataverse_check_record_access:"Check which access rights a user or team has on a specific Dataverse record",dataverse_grant_access:"Grant record-level sharing access to a user or team via the GrantAccess action",dataverse_revoke_access:"Revoke record-level sharing access from a user or team via the RevokeAccess action",dataverse_merge_records:"Merge two Dataverse records: the subordinate is deactivated and data merged into the target"};function qr(a){return a.toLowerCase().replace(/[^a-z0-9_]/g," ").split(/\s+/).filter(i=>i.length>1)}function jr(a,i){let n=Se[a]??[],e=(Fe[a]??"").toLowerCase(),t=a.toLowerCase(),r=0;for(let o of i)n.includes(o)&&(r+=3),t.includes(o)&&(r+=2),e.includes(o)&&(r+=1);return r}function Wr(a){let i=qr(a);if(i.length===0)return["dataverse_whoami","dataverse_list_tables","dataverse_query","dataverse_search","dataverse_get_table_metadata"].map(t=>({name:t,description:Fe[t]??"",tags:Se[t]??[],score:1}));let n=[];for(let e of Object.keys(Se)){let t=jr(e,i);t>0&&n.push({name:e,description:Fe[e]??"",tags:Se[e]??[],score:t})}return n.sort((e,t)=>t.score-e.score),n.slice(0,5)}function Fr(){let a=new Map;for(let i of Object.values(Se))for(let n of i)a.set(n,(a.get(n)??0)+1);return Array.from(a.entries()).map(([i,n])=>({tag:i,toolCount:n})).sort((i,n)=>n.toolCount-i.toolCount)}var pa=[{name:"dataverse_suggest_tools",description:"Suggests the most relevant Dataverse tools for a given intent. Provide a natural-language description of what you want to do and this tool returns the top 5 matching tools with descriptions and tags.",inputSchema:{type:"object",properties:{intent:{type:"string",description:"Natural language description of the desired action"}},required:["intent"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},{name:"dataverse_list_tool_tags",description:"Lists all available tool tags with the number of tools in each category. Use this to discover what kinds of operations are available.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}}];async function ma(a,i,n){if(a==="dataverse_suggest_tools"){let{intent:e}=i??{},t=Wr(e??""),r=t.length>0?[`Try: ${t[0].name}`]:["Use dataverse_list_tool_tags to explore categories"];return p(`${t.length} tools match intent "${e??""}"`,t,r)}if(a==="dataverse_list_tool_tags"){let e=Fr();return re("tags",e,["Use dataverse_suggest_tools with an intent to find specific tools"])}throw new Error(`Unknown router tool: ${a}`)}var Gr=[{uriTemplate:"dataverse://tables/{tableName}/schema",name:"Table Schema",description:"Returns the full schema (columns, types, requirements) for a Dataverse table",mimeType:"application/json"},{uriTemplate:"dataverse://tables/{tableName}/relationships",name:"Table Relationships",description:"Returns all 1:N and N:N relationships for a Dataverse table",mimeType:"application/json"}],Br=[{uri:"dataverse://tables",name:"Available Tables",description:"Lists all custom tables in the connected Dataverse environment",mimeType:"application/json"},{uri:"dataverse://server/instructions",name:"Server Instructions",description:"Usage guidelines and best practices for interacting with this Dataverse MCP server",mimeType:"text/plain"}];function Vr(a){let i="dataverse://";if(!a.startsWith(i))throw new Error(`Unknown resource URI: ${a}`);let n=a.slice(i.length);if(n==="tables")return{type:"tables"};if(n==="server/instructions")return{type:"instructions"};let e=/^tables\/([^/]+)\/schema$/.exec(n);if(e)return{type:"schema",tableName:e[1]};let t=/^tables\/([^/]+)\/relationships$/.exec(n);if(t)return{type:"relationships",tableName:t[1]};throw new Error(`Unknown resource URI: ${a}`)}function ga(){return Br}function fa(){return Gr}async function ya(a,i,n){let e=Vr(a);switch(e.type){case"tables":{let t=await i.listTables(!0);return{uri:a,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"schema":{let t=await i.getTableMetadata(e.tableName);return{uri:a,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"relationships":{let t=await i.getRelationships(e.tableName);return{uri:a,mimeType:"application/json",text:JSON.stringify(t,null,2)}}case"instructions":return{uri:a,mimeType:"text/plain",text:n}}}var zr=[{name:"analyze-org-health",description:"Run a complete Dataverse org health check: tables, security roles, workflows, plugins, and audit settings.",arguments:[]},{name:"data-quality-check",description:"Analyze data quality for a Dataverse table: null rates, duplicates, format anomalies, and referential integrity.",arguments:[{name:"tableName",description:"Logical name of the table to analyse (e.g. account, contact, cr123_order)",required:!0},{name:"sampleSize",description:"Number of rows to sample (default: 50, maximum: 500)",required:!1}]},{name:"schema-review",description:"Perform an expert schema review of a Dataverse table: columns, types, naming, relationships, keys, and views.",arguments:[{name:"tableName",description:"Logical name of the table to review (e.g. account, contact, cr123_order)",required:!0}]},{name:"security-audit",description:"Audit the Dataverse security model: roles, over-privileged users, teams, business units, and field-level security.",arguments:[]},{name:"analyze-workflow",description:"Analyse Dataverse workflows and Power Automate flows for ownership, error rates, and optimisation opportunities.",arguments:[{name:"workflowName",description:"Optional: filter by workflow/flow name substring",required:!1},{name:"statusFilter",description:"Which workflows to include: active | inactive | all (default: active)",required:!1}]}];function ha(){return zr}function va(a,i={}){switch(a){case"analyze-org-health":return Kr();case"data-quality-check":return Xr(i);case"schema-review":return Jr(i);case"security-audit":return Yr();case"analyze-workflow":return Qr(i);default:throw new Error(`Unknown prompt: ${a}`)}}function Kr(){return{description:"Complete Dataverse org health check",messages:[{role:"user",content:{type:"text",text:`Perform a comprehensive health check of this Dataverse environment. Follow these steps in order and report findings after each one.
144
+ \u26A0\uFE0F NOTE \u2014 Direct OData query on 'roleprivileges': The entity set name for roleprivileges is 'roleprivilegess' (double 's'). This means OData queries must target /api/data/v9.2/roleprivilegess. For direct query use: dataverse_query with entitySetName='roleprivilegess'. For FetchXML join pattern (role \u2192 privilege depths), use: <fetch><entity name='role'><attribute name='name'/><link-entity name='roleprivileges' from='roleid' to='roleid'><attribute name='privilegedepthmask'/><link-entity name='privilege' from='privilegeid' to='privilegeid'><attribute name='name'/></link-entity></link-entity></entity></fetch>. WHEN TO USE: Auditing which privileges and depths a security role has. BEST PRACTICES: Use dataverse_list_roles to find the role GUID first. Use this tool (RetrieveRolePrivilegesRole) for the clean structured result; use FetchXML for joins with privilege names in one shot. WORKFLOW: inspect_audit.`,inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"}},required:["roleId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_add_role_privileges",description:"Adds one or more privileges to a security role with specified depth levels using the AddPrivilegesRole action. Supports all depths: None, Basic (user-level), Local (BU-level), Deep (parent-BU), Global (org-level). For org-owned entities only Global is valid \u2014 Basic/Local/Deep return error 0x8004140b. WHEN TO USE: Setting up or extending a role's permissions. BEST PRACTICES: Get privilege GUIDs from dataverse_query on 'privileges' entity; use dataverse_replace_role_privileges to atomically reset all at once. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"},privileges:Yn},required:["roleId","privileges"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_replace_role_privileges",description:"Atomically replaces ALL privileges on a security role using the ReplacePrivilegesRole action. The new set completely overrides the existing privileges \u2014 any privilege not in the list is removed. Use for initial setup or full reconfiguration of a role. WHEN TO USE: Complete reconfiguration of a role's privilege set. BEST PRACTICES: Read current privileges with dataverse_get_role_privileges first to avoid accidental removal. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"},privileges:Yn,confirm:{type:"boolean",description:"Must be true \u2014 replaces ALL existing privileges on the role"}},required:["roleId","privileges","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!0,openWorldHint:!0}}];async function ea(r,a,t){switch(r){case"dataverse_get_role_privileges":{let{roleId:e}=_s.parse(a),i=(await t.executeFunction(Jn.RETRIEVE_ROLE_PRIVILEGES,{RoleId:e}))?.RolePrivileges??[];return f(`${i.length} privileges on role`,{roleId:e,privileges:i,count:i.length},["Use dataverse_add_role_privileges to modify privileges or depths"])}case"dataverse_add_role_privileges":{let e=ws.parse(a),n=e.privileges.map(i=>({"@odata.type":"Microsoft.Dynamics.CRM.RolePrivilege",Depth:i.depth,PrivilegeId:i.privilegeId,...i.businessUnitId?{BusinessUnitId:i.businessUnitId}:{}}));return await t.executeBoundAction("roles",e.roleId,Nt.ADD_PRIVILEGES_ROLE,{Privileges:n}),f(`${e.privileges.length} privileges added to role`,{roleId:e.roleId,count:e.privileges.length},["Use dataverse_get_role_privileges to verify the assignment"])}case"dataverse_replace_role_privileges":{let e=Ts.parse(a),n=e.privileges.map(i=>({"@odata.type":"Microsoft.Dynamics.CRM.RolePrivilege",Depth:i.depth,PrivilegeId:i.privilegeId,...i.businessUnitId?{BusinessUnitId:i.businessUnitId}:{}}));return await t.executeBoundAction("roles",e.roleId,Nt.REPLACE_PRIVILEGES_ROLE,{Privileges:n}),f(`All privileges replaced on role (${e.privileges.length} total)`,{roleId:e.roleId,count:e.privileges.length},["Use dataverse_get_role_privileges to verify the new privilege set"])}default:throw new Error(`Unknown role privilege tool: ${r}`)}}L();import{z as Ue}from"zod";var Rs=Ue.object({entityLogicalName:Ue.string().min(1),includePersonal:Ue.boolean().optional().default(!1),top:Ue.number().int().positive().max(100).optional().default(20),compact:Ue.boolean().optional().default(!0)}),ta=[{name:"dataverse_list_views",description:"Lists saved views (system and optionally personal) for a Dataverse table. System views come from savedqueries; personal views come from userqueries. Returns view name, ID, default flag, query type, and description. WHEN TO USE: Discovering saved system or personal views for a table. BEST PRACTICES: Use the view's FetchXML with dataverse_execute_fetchxml to run it. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:'Logical name of the entity to list views for (e.g., "account")'},includePersonal:{type:"boolean",description:"Include personal (user) views in addition to system views (default false)"},top:{type:"number",description:"Maximum number of results per category (default 20, max 100)"},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["entityLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function na(r,a,t){switch(r){case"dataverse_list_views":{let{entityLogicalName:e,includePersonal:n,top:i,compact:s}=Rs.parse(a),o=h(e),l=await t.query("savedqueries",{filter:`returnedtypecode eq '${o}' and statecode eq 0`,select:["savedqueryid","name","isdefault","querytype","description"],orderby:"name asc",top:i}),c=s?T(l.value.map(m=>({id:m.savedqueryid,name:m.name,isDefault:m.isdefault,queryType:m.querytype,description:m.description??null,viewType:"system"}))):l.value.map(m=>({id:m.savedqueryid,name:m.name,isDefault:m.isdefault,queryType:m.querytype,description:m.description??null,viewType:"system"})),d=[];n&&(d=(await t.query("userqueries",{filter:`returnedtypecode eq '${o}'`,select:["userqueryid","name","description"],orderby:"name asc",top:i})).value.map(p=>({id:p.userqueryid,name:p.name,description:p.description??null,viewType:"personal"})),s&&(d=T(d)));let u=c.length+d.length;return f(`${u} views found for ${e}`,{entityLogicalName:e,systemViews:c,systemViewCount:c.length,personalViews:n?d:void 0,personalViewCount:n?d.length:void 0},["Use the view's fetchxml with dataverse_execute_fetchxml to run it"])}default:throw new Error(`Unknown view tool: ${r}`)}}import{z as tt}from"zod";var Ss=tt.object({top:tt.number().int().positive().max(200).optional().default(50),includeDisabled:tt.boolean().optional().default(!1),compact:tt.boolean().optional().default(!0)}),aa=[{name:"dataverse_list_business_units",description:"Lists business units in the Dataverse environment. Returns name, ID, parent business unit ID, disabled status, and creation date. By default only active business units are returned. WHEN TO USE: Finding business unit IDs for user management, team setup, or security role scope. BEST PRACTICES: The root business unit is the parent of all others; filter disabled units by default. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Maximum number of results (default 50, max 200)"},includeDisabled:{type:"boolean",description:"Include disabled business units (default false)"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function ra(r,a,t){switch(r){case"dataverse_list_business_units":{let{top:e,includeDisabled:n,compact:i}=Ss.parse(a??{}),s=n?void 0:"isdisabled eq false",l=(await t.query("businessunits",{select:["businessunitid","name","parentbusinessunitid","isdisabled","createdon"],...s?{filter:s}:{},orderby:"name asc",top:e})).value.map(c=>({id:c.businessunitid,name:c.name,parentBusinessUnitId:c.parentbusinessunitid??null,isDisabled:c.isdisabled,createdOn:c.createdon}));return i&&(l=T(l)),f(`${l.length} business units found`,{businessUnits:l,count:l.length},["Use business unit IDs for user management or security role assignment"])}default:throw new Error(`Unknown org tool: ${r}`)}}import{z as ae}from"zod";var ia=/^[a-zA-Z0-9_]+$/,sa=/\.\.|[/\\]/,Ns=ae.object({entitySetName:ae.string().min(1).refine(r=>!sa.test(r),{message:"entitySetName must not contain path traversal characters"}),recordId:ae.string().uuid(),columnName:ae.string().min(1).refine(r=>ia.test(r),{message:"columnName must be alphanumeric/underscore only"}),fileContent:ae.string().min(1).describe("Base64-encoded file content"),fileName:ae.string().min(1)}),As=ae.object({entitySetName:ae.string().min(1).refine(r=>!sa.test(r),{message:"entitySetName must not contain path traversal characters"}),recordId:ae.string().uuid(),columnName:ae.string().min(1).refine(r=>ia.test(r),{message:"columnName must be alphanumeric/underscore only"})}),oa=[{name:"dataverse_upload_file_column",description:"Uploads a file to a Dataverse file column. Provide the entity set name, record GUID, column name, base64-encoded file content, and file name. The file is stored in the specified file-type column on the record. WHEN TO USE: Uploading a file to a file-type column on a Dataverse record. BEST PRACTICES: Base64-encode the file content; verify the column is a file-type column via dataverse_get_table_metadata. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},recordId:{type:"string",description:"Record GUID"},columnName:{type:"string",description:"File column logical name"},fileContent:{type:"string",description:"Base64-encoded file content"},fileName:{type:"string",description:'File name including extension (e.g., "report.pdf")'}},required:["entitySetName","recordId","columnName","fileContent","fileName"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_download_file_column",description:"Downloads a file from a Dataverse file column. Returns the file content as a base64-encoded string along with the file name and size. WHEN TO USE: Downloading a file stored in a file-type column on a Dataverse record. BEST PRACTICES: The response is base64-encoded; decode before saving or processing. WORKFLOW: file_operations.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'OData entity set name (e.g., "accounts")'},recordId:{type:"string",description:"Record GUID"},columnName:{type:"string",description:"File column logical name"}},required:["entitySetName","recordId","columnName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}}];async function ca(r,a,t){let e=t.http;switch(r){case"dataverse_upload_file_column":{let{entitySetName:n,recordId:i,columnName:s,fileContent:o,fileName:l}=Ns.parse(a),c=Buffer.from(o,"base64"),d=`${n}(${i})/${s}`;return await e.patch(d,c,{headers:{"Content-Type":"application/octet-stream","x-ms-file-name":l}}),f(`Uploaded file to ${s} on ${n}(${i})`,{success:!0,entitySetName:n,recordId:i,columnName:s,fileName:l,sizeBytes:c.length},["Use dataverse_download_file_column to retrieve the file"])}case"dataverse_download_file_column":{let{entitySetName:n,recordId:i,columnName:s}=As.parse(a),o=`${n}(${i})/${s}/$value`,l=await e.get(o,{responseType:"text"}),c=l.data,d=Buffer.from(c,"binary").toString("base64"),u=l.headers["x-ms-file-name"]??l.headers["content-disposition"]?.match(/filename="?([^";\n]+)"?/)?.[1]??"download",m=parseInt(l.headers["content-length"]??"0",10)||d.length;return f(`Downloaded file from ${s} on ${n}(${i})`,{entitySetName:n,recordId:i,columnName:s,fileName:u,sizeBytes:m,contentBase64:d})}default:throw new Error(`Unknown file tool: ${r}`)}}import{z as ke}from"zod";var ks={SVG:11,PNG:5,JPG:6,GIF:7,ICO:10},Es=ke.object({name:ke.string().min(1).describe("Unique name with publisher prefix (e.g., 'new_myicon.svg'). This is the value you pass as iconVectorName in dataverse_set_table_icon or dataverse_create_table."),displayName:ke.string().min(1).describe("Human-friendly display name."),webResourceType:ke.enum(["SVG","PNG","JPG","GIF","ICO"]).describe("Web resource type. Use 'SVG' for IconVectorName table icons."),contentBase64:ke.string().min(1).describe("Base64-encoded file content."),description:ke.string().optional().describe("Optional description of the web resource.")}),la=[{name:"dataverse_create_web_resource",description:"Creates a web resource (SVG, PNG, JPG, GIF, ICO) in Dataverse. REQUIRED PREREQUISITE for table icons \u2014 you MUST call this first before dataverse_set_table_icon or dataverse_create_table. The name you choose (e.g. 'new_icon.svg') becomes the iconVectorName value in the next step. Provide the base64-encoded file content. For table icons, use webResourceType='SVG' (type code 11) \u2014 SVG icons scale sharply at any size. WHEN TO USE: Uploading a custom icon image that will be assigned to a model-driven app table. NEXT STEP: After creation, use dataverse_set_table_icon with the returned name as iconVectorName. WORKFLOW: table_icons.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Unique name with publisher prefix (e.g., 'new_myicon.svg'). This name becomes your iconVectorName for the next step."},displayName:{type:"string",description:"Human-friendly display name."},webResourceType:{type:"string",enum:["SVG","PNG","JPG","GIF","ICO"],description:"Web resource type. Use 'SVG' for IconVectorName table icons."},contentBase64:{type:"string",description:"Base64-encoded file content."},description:{type:"string",description:"Optional description of the web resource."}},required:["name","displayName","webResourceType","contentBase64"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function da(r,a,t){switch(r){case"dataverse_create_web_resource":{let e=Es.parse(a),n=ks[e.webResourceType],i={name:e.name,displayname:e.displayName,webresourcetype:n,content:e.contentBase64};e.description&&(i.description=e.description);let s;try{s=await t.createWebResource(i)}catch(o){let l=o instanceof Error?o.message:String(o);if(l.includes("duplicate")||l.includes("0x80040237"))return j({summary:`Web resource '${e.name}' already exists. Use a unique name or remove the existing one first.`,data:{error:"DUPLICATE_NAME",name:e.name},errorCategory:"VALIDATION",suggestions:["Choose a different name for the new web resource"]});throw o}return f(`Web resource '${e.name}' created (${e.webResourceType}, id: ${s}). Use this name as iconVectorName when creating or updating a table.`,{webResourceId:s,name:e.name,displayName:e.displayName,webResourceType:e.webResourceType,typeCode:n},[`Use dataverse_create_table with iconVectorName: '${e.name}'`,`Use dataverse_set_table_icon with iconVectorName: '${e.name}'`])}default:throw new Error(`Unknown webresource tool: ${r}`)}}import{z as J}from"zod";var Cs={0:"Owner",1:"Access",2:"Office",3:"Security"},Is=J.object({top:J.number().int().positive().max(200).optional().default(50),teamType:J.union([J.literal(0),J.literal(1),J.literal(2),J.literal(3)]).optional(),compact:J.boolean().optional().default(!0)}),xs=J.object({teamId:J.string().uuid(),roleId:J.string().uuid(),confirm:J.literal(!0,{errorMap:()=>({message:"confirm must be true to assign a role to a team"})})}),ua=[{name:"dataverse_list_teams",description:"Lists Dataverse teams in the environment. Useful for finding team owners for record assignment and sharing. teamtype: 0=Owner, 1=Access, 2=AAD Office Group, 3=AAD Security Group. WHEN TO USE: Finding team IDs for record assignment or sharing. BEST PRACTICES: Filter by teamType to distinguish owner, access, and AAD group teams. WORKFLOW: explore_schema.",inputSchema:{type:"object",properties:{top:{type:"number",description:"Maximum number of teams to return (1\u2013200, default 50)"},teamType:{type:"number",enum:[0,1,2,3],description:"Filter by team type: 0=Owner, 1=Access, 2=Office Group, 3=Security Group"},compact:{type:"boolean",description:"Strip null/empty fields from each record to save tokens. Default true."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_assign_role_to_team",description:"Assigns a security role to a Dataverse team. All team members inherit the role permissions. Use dataverse_list_roles to find the role GUID and dataverse_list_teams to find the team GUID. WARNING: This modifies team permissions for ALL members. Set confirm=true to proceed. WHEN TO USE: Providing role-based permissions to an entire team during configuration. BEST PRACTICES: Prefer team-based RBAC over individual user assignments for maintainability. WORKFLOW: inspect_audit.",inputSchema:{type:"object",properties:{teamId:{type:"string",description:"GUID of the team"},roleId:{type:"string",description:"GUID of the security role to assign"},confirm:{type:"boolean",description:"Must be true to proceed with role assignment"}},required:["teamId","roleId","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function pa(r,a,t){switch(r){case"dataverse_list_teams":{let{top:e,teamType:n,compact:i}=Is.parse(a),o={select:["teamid","name","teamtype","description","isdefault","createdon","_businessunitid_value"],top:e,orderby:"name asc"};n!==void 0&&(o.filter=`teamtype eq ${n}`);let c=(await t.query("teams",o)).value.map(d=>({...d,teamTypeName:Cs[d.teamtype]??"Unknown"}));return i&&(c=T(c)),f(`${c.length} teams found`,{teams:c,count:c.length},["Use dataverse_assign with a team ID to assign records to a team"])}case"dataverse_assign_role_to_team":{let{teamId:e,roleId:n}=xs.parse(a);return(((await t.query("teams",{filter:`teamid eq ${e}`,select:["teamid"],expand:`teamroles_association($select=roleid;$filter=roleid eq ${n};$top=1)`,top:1})).value??[])[0]?.teamroles_association??[]).length>0?f("Role is already assigned to this team.",{teamId:e,roleId:n,status:"already_assigned"},["Use dataverse_list_teams to view current team configuration"]):(await t.associate("teams",e,"teamroles_association","roles",n),f("Role assigned to team successfully.",{teamId:e,roleId:n,status:"assigned"},["All members of this team now inherit the assigned role"]))}default:throw new Error(`Unknown team tool: ${r}`)}}var $e={dataverse_query:["query","read","odata"],dataverse_execute_fetchxml:["query","read","fetchxml","aggregate"],dataverse_create:["write","crud","create"],dataverse_update:["write","crud","update"],dataverse_delete:["write","crud","delete","destructive"],dataverse_get:["read","crud","get"],dataverse_upsert:["write","crud","upsert"],dataverse_retrieve_multiple_with_paging:["query","read","paging"],dataverse_search:["search","read","fulltext"],dataverse_list_tables:["metadata","schema","read"],dataverse_get_table_metadata:["metadata","schema","read"],dataverse_get_relationships:["metadata","schema","relations","read"],dataverse_get_option_set:["metadata","schema","read"],dataverse_get_attribute_option_set:["metadata","schema","read"],dataverse_list_global_option_sets:["metadata","schema","read"],dataverse_get_entity_key:["metadata","schema","read"],dataverse_resolve_entity_name:["metadata","schema","read"],dataverse_create_attribute:["metadata","schema","write","attribute"],dataverse_update_attribute:["metadata","schema","write","attribute"],dataverse_delete_attribute:["metadata","schema","write","attribute","destructive"],dataverse_create_lookup_attribute:["metadata","schema","write","attribute","relations"],dataverse_update_entity:["metadata","schema","write"],dataverse_publish_customizations:["customization","write"],dataverse_create_sitemap:["customization","sitemap","write","manage_solution"],dataverse_list_views:["metadata","views","read"],dataverse_execute_action:["actions","write","custom"],dataverse_execute_bound_action:["actions","write","custom"],dataverse_execute_function:["actions","read","custom"],dataverse_execute_bound_function:["actions","read","custom"],dataverse_batch_execute:["write","bulk","batch"],dataverse_whoami:["auth","identity","read"],dataverse_impersonate:["security","admin"],dataverse_associate:["relations","write"],dataverse_disassociate:["relations","write"],dataverse_associate_bulk:["relations","write","bulk"],dataverse_query_associations:["relations","read"],dataverse_list_roles:["admin","security","roles","read"],dataverse_get_role_privileges:["admin","security","roles","read"],dataverse_add_role_privileges:["admin","security","roles","write"],dataverse_replace_role_privileges:["admin","security","roles","write","destructive"],dataverse_assign_role_to_user:["admin","security","users","write"],dataverse_remove_role_from_user:["admin","security","users","write"],dataverse_assign_role_to_team:["admin","security","teams","write"],dataverse_assign:["write","crud"],dataverse_list_users:["admin","users","read"],dataverse_get_user_roles:["admin","users","security","read"],dataverse_list_teams:["admin","teams","read"],dataverse_list_business_units:["admin","teams","read"],dataverse_list_workflows:["workflows","read"],dataverse_get_workflow:["workflows","read"],dataverse_set_workflow_state:["workflows","write"],dataverse_environment_capabilities:["admin","environments","read"],dataverse_get_environment_variable:["admin","environments","read"],dataverse_set_environment_variable:["admin","environments","write"],dataverse_create_environment_variable:["admin","environments","write"],dataverse_get_audit_log:["audit","read"],dataverse_get_plugin_trace_logs:["debug","trace","read"],dataverse_get_workflow_trace_logs:["debug","trace","read"],dataverse_list_plugin_steps:["debug","plugins","read"],dataverse_get_annotations:["notes","read"],dataverse_create_annotation:["notes","write"],dataverse_download_file_column:["files","read","download"],dataverse_upload_file_column:["files","write","upload"],dataverse_change_detection:["sync","delta","read"],dataverse_detect_duplicates:["quality","read","validation"],dataverse_list_dependencies:["metadata","schema","read"],dataverse_retrieve_dependencies_for_delete:["metadata","schema","read"],dataverse_list_connection_references:["metadata","read"],dataverse_list_custom_actions:["actions","metadata","read"],dataverse_get_guide:["guides","help","read"],dataverse_list_guides:["guides","help","read"],dataverse_suggest_tools:["router","help","read"],dataverse_list_tool_tags:["router","help","read"],dataverse_create_table:["metadata","schema","write","entity","table"],dataverse_create_relationship:["metadata","schema","write","relationship"],dataverse_check_record_access:["security","rbac","sharing","read","record"],dataverse_grant_access:["security","rbac","sharing","write","record"],dataverse_revoke_access:["security","rbac","sharing","write","record"],dataverse_merge_records:["write","crud","deduplication","merge","destructive"]},At={dataverse_query:"Query Dataverse tables using OData syntax with filters, select, expand",dataverse_execute_fetchxml:"Execute FetchXML queries with aggregation, groupby, and joins",dataverse_create:"Create a new record in a Dataverse table",dataverse_update:"Update an existing Dataverse record",dataverse_delete:"Delete a Dataverse record permanently",dataverse_get:"Retrieve a single record by GUID",dataverse_upsert:"Create or update a record by alternate key",dataverse_retrieve_multiple_with_paging:"Query with automatic paging support for large datasets",dataverse_search:"Full-text search across multiple Dataverse tables using Relevance Search",dataverse_list_tables:"List available Dataverse tables (entities)",dataverse_get_table_metadata:"Get column definitions and schema for a table",dataverse_get_relationships:"Get one-to-many and many-to-many relationships for a table",dataverse_get_option_set:"Get a global or local option set definition",dataverse_get_attribute_option_set:"Get option values for a specific column (OptionSet/MultiSelectPicklist)",dataverse_list_global_option_sets:"List all global option sets in the environment",dataverse_get_entity_key:"Get alternate key definitions for a table",dataverse_resolve_entity_name:"Resolve a table logical name to its entity set name (OData collection name)",dataverse_create_attribute:"Create a new column on a Dataverse table",dataverse_update_attribute:"Update mutable properties of an existing column",dataverse_delete_attribute:"Delete a column and all its data from a table",dataverse_create_lookup_attribute:"Create a Lookup column via a One-to-Many relationship",dataverse_update_entity:"Update mutable entity-level properties (display name, description, etc.)",dataverse_publish_customizations:"Publish unpublished schema customizations to make them available to users",dataverse_create_sitemap:"Create a model-driven app sitemap (navigation structure) by generating XML and posting to /sitemaps; optionally links it to an app module",dataverse_list_views:"List saved views (saved queries) for a table",dataverse_execute_action:"Execute an unbound custom action",dataverse_execute_bound_action:"Execute a bound custom action on a specific record",dataverse_execute_function:"Execute an unbound function (read-only)",dataverse_execute_bound_function:"Execute a bound function on a specific record (read-only)",dataverse_batch_execute:"Execute multiple read/write operations in a single HTTP batch request",dataverse_whoami:"Get current authenticated user identity and organization context",dataverse_impersonate:"Set impersonation (run subsequent calls on behalf of another user)",dataverse_associate:"Associate two records via a relationship",dataverse_disassociate:"Remove an association between two records",dataverse_associate_bulk:"Associate multiple records in a single batch",dataverse_query_associations:"Query the associated records through a relationship",dataverse_list_roles:"List security roles in the environment",dataverse_get_role_privileges:"Get all privileges and their depth levels assigned to a security role",dataverse_add_role_privileges:"Add privileges to a security role with specified depth",dataverse_replace_role_privileges:"Atomically replace all privileges on a security role",dataverse_assign_role_to_user:"Assign a security role to a user",dataverse_remove_role_from_user:"Remove a security role from a user",dataverse_assign_role_to_team:"Assign a security role to a team",dataverse_assign:"Assign a record to a different owner (user or team)",dataverse_list_users:"List system users in the environment",dataverse_get_user_roles:"Get security roles assigned to a specific user",dataverse_list_teams:"List teams in the environment",dataverse_list_business_units:"List business units in the organization hierarchy",dataverse_list_workflows:"List workflows and Power Automate flows",dataverse_get_workflow:"Get the definition and status of a specific workflow",dataverse_set_workflow_state:"Activate or deactivate a workflow",dataverse_environment_capabilities:"Get environment capabilities: identity, settings, solutions, variable counts",dataverse_get_environment_variable:"Get the value and schema of an environment variable",dataverse_set_environment_variable:"Set the current value of an environment variable",dataverse_create_environment_variable:"Create a new environment variable definition and optionally its value",dataverse_get_audit_log:"Get the audit history for a specific record",dataverse_get_plugin_trace_logs:"Get plugin trace logs for debugging plugin execution errors",dataverse_get_workflow_trace_logs:"Get execution trace logs for a Power Automate workflow run",dataverse_list_plugin_steps:"List registered plugin steps (SDK message processing steps)",dataverse_get_annotations:"Get notes/annotations attached to a record",dataverse_create_annotation:"Create a note (annotation) on a record",dataverse_download_file_column:"Download the binary content of a file-type column",dataverse_upload_file_column:"Upload base64-encoded content to a file-type column",dataverse_change_detection:"Detect new or changed records since a delta token (change tracking)",dataverse_detect_duplicates:"Detect potential duplicate records before creating a new one",dataverse_list_dependencies:"List workflows, flows, and plugins that reference a table",dataverse_retrieve_dependencies_for_delete:"Check which components block deletion of a specific component",dataverse_list_connection_references:"List Power Automate connection references in the environment",dataverse_list_custom_actions:"List registered custom API actions",dataverse_get_guide:"Get a contextual MCP usage guide for a specific workflow",dataverse_list_guides:"List all available MCP usage guides",dataverse_suggest_tools:"Get top-5 tool suggestions for a natural-language intent",dataverse_list_tool_tags:"List all tool tags with tool counts per category",dataverse_create_table:"Create a new custom table (entity) in Dataverse via POST /EntityDefinitions",dataverse_create_relationship:"Create a One-to-Many or Many-to-Many relationship between two Dataverse tables",dataverse_check_record_access:"Check which access rights a user or team has on a specific Dataverse record",dataverse_grant_access:"Grant record-level sharing access to a user or team via the GrantAccess action",dataverse_revoke_access:"Revoke record-level sharing access from a user or team via the RevokeAccess action",dataverse_merge_records:"Merge two Dataverse records: the subordinate is deactivated and data merged into the target"};function Ds(r){return r.toLowerCase().replace(/[^a-z0-9_]/g," ").split(/\s+/).filter(a=>a.length>1)}function Os(r,a){let t=$e[r]??[],e=(At[r]??"").toLowerCase(),n=r.toLowerCase(),i=0;for(let s of a)t.includes(s)&&(i+=3),n.includes(s)&&(i+=2),e.includes(s)&&(i+=1);return i}function Ps(r){let a=Ds(r);if(a.length===0)return["dataverse_whoami","dataverse_list_tables","dataverse_query","dataverse_search","dataverse_get_table_metadata"].map(n=>({name:n,description:At[n]??"",tags:$e[n]??[],score:1}));let t=[];for(let e of Object.keys($e)){let n=Os(e,a);n>0&&t.push({name:e,description:At[e]??"",tags:$e[e]??[],score:n})}return t.sort((e,n)=>n.score-e.score),t.slice(0,5)}function Us(){let r=new Map;for(let a of Object.values($e))for(let t of a)r.set(t,(r.get(t)??0)+1);return Array.from(r.entries()).map(([a,t])=>({tag:a,toolCount:t})).sort((a,t)=>t.toolCount-a.toolCount)}var ma=[{name:"dataverse_suggest_tools",description:"Suggests the most relevant Dataverse tools for a given intent. Provide a natural-language description of what you want to do and this tool returns the top 5 matching tools with descriptions and tags.",inputSchema:{type:"object",properties:{intent:{type:"string",description:"Natural language description of the desired action"}},required:["intent"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}},{name:"dataverse_list_tool_tags",description:"Lists all available tool tags with the number of tools in each category. Use this to discover what kinds of operations are available.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1}}];async function ga(r,a,t){if(r==="dataverse_suggest_tools"){let{intent:e}=a??{},n=Ps(e??""),i=n.length>0?[`Try: ${n[0].name}`]:["Use dataverse_list_tool_tags to explore categories"];return f(`${n.length} tools match intent "${e??""}"`,n,i)}if(r==="dataverse_list_tool_tags"){let e=Us();return ge("tags",e,["Use dataverse_suggest_tools with an intent to find specific tools"])}throw new Error(`Unknown router tool: ${r}`)}var $s=[{uriTemplate:"dataverse://tables/{tableName}/schema",name:"Table Schema",description:"Returns the full schema (columns, types, requirements) for a Dataverse table",mimeType:"application/json"},{uriTemplate:"dataverse://tables/{tableName}/relationships",name:"Table Relationships",description:"Returns all 1:N and N:N relationships for a Dataverse table",mimeType:"application/json"}],Ls=[{uri:"dataverse://tables",name:"Available Tables",description:"Lists all custom tables in the connected Dataverse environment",mimeType:"application/json"},{uri:"dataverse://server/instructions",name:"Server Instructions",description:"Usage guidelines and best practices for interacting with this Dataverse MCP server",mimeType:"text/plain"}];function Ms(r){let a="dataverse://";if(!r.startsWith(a))throw new Error(`Unknown resource URI: ${r}`);let t=r.slice(a.length);if(t==="tables")return{type:"tables"};if(t==="server/instructions")return{type:"instructions"};let e=/^tables\/([^/]+)\/schema$/.exec(t);if(e)return{type:"schema",tableName:e[1]};let n=/^tables\/([^/]+)\/relationships$/.exec(t);if(n)return{type:"relationships",tableName:n[1]};throw new Error(`Unknown resource URI: ${r}`)}function fa(){return Ls}function ya(){return $s}async function ha(r,a,t){let e=Ms(r);switch(e.type){case"tables":{let n=await a.listTables(!0);return{uri:r,mimeType:"application/json",text:JSON.stringify(n,null,2)}}case"schema":{let n=await a.getTableMetadata(e.tableName);return{uri:r,mimeType:"application/json",text:JSON.stringify(n,null,2)}}case"relationships":{let n=await a.getRelationships(e.tableName);return{uri:r,mimeType:"application/json",text:JSON.stringify(n,null,2)}}case"instructions":return{uri:r,mimeType:"text/plain",text:t}}}var qs=[{name:"analyze-org-health",description:"Run a complete Dataverse org health check: tables, security roles, workflows, plugins, and audit settings.",arguments:[]},{name:"data-quality-check",description:"Analyze data quality for a Dataverse table: null rates, duplicates, format anomalies, and referential integrity.",arguments:[{name:"tableName",description:"Logical name of the table to analyse (e.g. account, contact, cr123_order)",required:!0},{name:"sampleSize",description:"Number of rows to sample (default: 50, maximum: 500)",required:!1}]},{name:"schema-review",description:"Perform an expert schema review of a Dataverse table: columns, types, naming, relationships, keys, and views.",arguments:[{name:"tableName",description:"Logical name of the table to review (e.g. account, contact, cr123_order)",required:!0}]},{name:"security-audit",description:"Audit the Dataverse security model: roles, over-privileged users, teams, business units, and field-level security.",arguments:[]},{name:"analyze-workflow",description:"Analyse Dataverse workflows and Power Automate flows for ownership, error rates, and optimisation opportunities.",arguments:[{name:"workflowName",description:"Optional: filter by workflow/flow name substring",required:!1},{name:"statusFilter",description:"Which workflows to include: active | inactive | all (default: active)",required:!1}]}];function va(){return qs}function ba(r,a={}){switch(r){case"analyze-org-health":return Hs();case"data-quality-check":return js(a);case"schema-review":return Ws(a);case"security-audit":return Fs();case"analyze-workflow":return Bs(a);default:throw new Error(`Unknown prompt: ${r}`)}}function Hs(){return{description:"Complete Dataverse org health check",messages:[{role:"user",content:{type:"text",text:`Perform a comprehensive health check of this Dataverse environment. Follow these steps in order and report findings after each one.
13
145
 
14
146
  **Step 1 \u2014 Identity**
15
147
  Call \`dataverse_whoami\` to confirm the connected user, org ID, and environment URL.
@@ -35,13 +167,13 @@ Produce a structured report with these sections:
35
167
  - \u2705 Healthy items
36
168
  - \u26A0\uFE0F Warnings (non-blocking but should be addressed)
37
169
  - \u{1F534} Issues (blocking or high-risk)
38
- - \u{1F4A1} Top 5 recommended actions, prioritised by impact`}}]}}function Xr(a){let i=a.tableName;if(!i)throw new Error("Argument 'tableName' is required");let n=a.sampleSize??"50";return{description:`Data quality check \u2014 ${i}`,messages:[{role:"user",content:{type:"text",text:`Perform a data quality analysis on the Dataverse table **${i}**. Sample size: ${n} rows.
170
+ - \u{1F4A1} Top 5 recommended actions, prioritised by impact`}}]}}function js(r){let a=r.tableName;if(!a)throw new Error("Argument 'tableName' is required");let t=r.sampleSize??"50";return{description:`Data quality check \u2014 ${a}`,messages:[{role:"user",content:{type:"text",text:`Perform a data quality analysis on the Dataverse table **${a}**. Sample size: ${t} rows.
39
171
 
40
172
  **Step 1 \u2014 Schema baseline**
41
- Call \`dataverse_get_table_metadata\` for \`${i}\`. Identify: required vs optional columns, column types (text/lookup/optionset/datetime), and any deprecated column types.
173
+ Call \`dataverse_get_table_metadata\` for \`${a}\`. Identify: required vs optional columns, column types (text/lookup/optionset/datetime), and any deprecated column types.
42
174
 
43
175
  **Step 2 \u2014 Sample data**
44
- Call \`dataverse_query\` on \`${i}\` with \`$top=${n}\` and \`$orderby=modifiedon desc\`. Select all non-binary columns.
176
+ Call \`dataverse_query\` on \`${a}\` with \`$top=${t}\` and \`$orderby=modifiedon desc\`. Select all non-binary columns.
45
177
 
46
178
  **Step 3 \u2014 Null analysis**
47
179
  For each optional column in the sample: calculate the null/empty rate. Flag columns where > 30% of sampled records have no value.
@@ -59,10 +191,10 @@ For each Lookup column in the schema: spot-check 5 records to verify the referen
59
191
  Produce:
60
192
  - \u{1F4CA} Overall quality score (0\u2013100)
61
193
  - Issues grouped by: Completeness | Uniqueness | Freshness | Integrity
62
- - Top 5 action items with effort estimate (Low / Medium / High)`}}]}}function Jr(a){let i=a.tableName;if(!i)throw new Error("Argument 'tableName' is required");return{description:`Schema review \u2014 ${i}`,messages:[{role:"user",content:{type:"text",text:`Perform an expert schema review of the Dataverse table **${i}**.
194
+ - Top 5 action items with effort estimate (Low / Medium / High)`}}]}}function Ws(r){let a=r.tableName;if(!a)throw new Error("Argument 'tableName' is required");return{description:`Schema review \u2014 ${a}`,messages:[{role:"user",content:{type:"text",text:`Perform an expert schema review of the Dataverse table **${a}**.
63
195
 
64
196
  **Step 1 \u2014 Table metadata**
65
- Call \`dataverse_get_table_metadata\` for \`${i}\`. Evaluate: display name, description presence, ownership type (User/Organization), change tracking, notes/attachments settings, and audit enabled flag.
197
+ Call \`dataverse_get_table_metadata\` for \`${a}\`. Evaluate: display name, description presence, ownership type (User/Organization), change tracking, notes/attachments settings, and audit enabled flag.
66
198
 
67
199
  **Step 2 \u2014 Column review**
68
200
  For each attribute note: type, requirement level, valid-for-create, valid-for-update. Flag:
@@ -75,23 +207,23 @@ For each attribute note: type, requirement level, valid-for-create, valid-for-up
75
207
  Verify column logical names follow a consistent pattern (e.g. \`prefix_fieldname\`). Flag deviations and columns using reserved keywords.
76
208
 
77
209
  **Step 4 \u2014 Relationships**
78
- Call \`dataverse_get_relationships\` for \`${i}\`. Flag:
210
+ Call \`dataverse_get_relationships\` for \`${a}\`. Flag:
79
211
  - Relationships with \`CascadeConfiguration\` all set to \`NoCascade\` (potential orphan risk)
80
212
  - More than 15 total relationships (complexity risk)
81
213
  - Any self-referential (hierarchy) relationship \u2014 document it explicitly
82
214
 
83
215
  **Step 5 \u2014 Alternate keys**
84
- Call \`dataverse_get_entity_key\` for \`${i}\`. If no alternate key is defined, flag as integration risk (no natural deduplication key for external systems).
216
+ Call \`dataverse_get_entity_key\` for \`${a}\`. If no alternate key is defined, flag as integration risk (no natural deduplication key for external systems).
85
217
 
86
218
  **Step 6 \u2014 Views**
87
- Call \`dataverse_list_views\` for \`${i}\`. Flag: more than 15 public views (UI clutter), views with no filter criteria (full-table scans), views sharing identical columns and filters (duplicates).
219
+ Call \`dataverse_list_views\` for \`${a}\`. Flag: more than 15 public views (UI clutter), views with no filter criteria (full-table scans), views sharing identical columns and filters (duplicates).
88
220
 
89
221
  **Step 7 \u2014 Review report**
90
222
  Produce:
91
223
  - Schema Maturity Score (0\u2013100)
92
224
  - Critical issues (must fix before going live)
93
225
  - Recommendations (best-practice improvements)
94
- - Quick wins (< 1 hour, low risk)`}}]}}function Yr(){return{description:"Dataverse security model audit",messages:[{role:"user",content:{type:"text",text:`Perform a security audit of this Dataverse environment.
226
+ - Quick wins (< 1 hour, low risk)`}}]}}function Fs(){return{description:"Dataverse security model audit",messages:[{role:"user",content:{type:"text",text:`Perform a security audit of this Dataverse environment.
95
227
 
96
228
  **Step 1 \u2014 Users**
97
229
  Call \`dataverse_list_users\` (top: 200). Identify:
@@ -118,10 +250,10 @@ Produce:
118
250
  - \u{1F534} Critical issues (immediate action required)
119
251
  - \u{1F7E1} Moderate findings (plan remediation)
120
252
  - \u2705 Good practices observed
121
- - Priority action list (top 5, sorted by risk)`}}]}}function Qr(a){let i=a.workflowName,n=a.statusFilter??"active",e=i?` \u2014 filtering by name containing "${i}"`:"";return{description:`Workflow analysis${e}`,messages:[{role:"user",content:{type:"text",text:`Analyse Dataverse workflows and Power Automate flows${e}. Status filter: **${n}**.
253
+ - Priority action list (top 5, sorted by risk)`}}]}}function Bs(r){let a=r.workflowName,t=r.statusFilter??"active",e=a?` \u2014 filtering by name containing "${a}"`:"";return{description:`Workflow analysis${e}`,messages:[{role:"user",content:{type:"text",text:`Analyse Dataverse workflows and Power Automate flows${e}. Status filter: **${t}**.
122
254
 
123
255
  **Step 1 \u2014 Inventory**
124
- Call \`dataverse_list_workflows\` with status: "${n}"${i?` and name filter containing "${i}"`:""}. Report total count and break down by type: Classic Workflow | Real-time Workflow | Power Automate Flow | Business Rule | Action.
256
+ Call \`dataverse_list_workflows\` with status: "${t}"${a?` and name filter containing "${a}"`:""}. Report total count and break down by type: Classic Workflow | Real-time Workflow | Power Automate Flow | Business Rule | Action.
125
257
 
126
258
  **Step 2 \u2014 Ownership check**
127
259
  For each workflow, note the owner. Call \`dataverse_list_users\` to cross-check that owners are still active users. Flag orphaned workflows (owner disabled or deleted).
@@ -145,16 +277,16 @@ Identify and report:
145
277
  - Duplicate workflows performing near-identical logic
146
278
  - Workflows that run synchronously and could be made asynchronous to improve user responsiveness
147
279
 
148
- Produce: Workflow Health Matrix | Issues by severity | Migration candidates prioritised by effort`}}]}}import{z as se}from"zod";var Ie=new Map([["query_data",{name:"query_data",description:"Query data from Dataverse tables. Authenticate, discover tables, inspect schema, then query with OData or FetchXML.",tags:["query","data","fetchxml","odata","read"],steps:[{order:1,tool:"dataverse_whoami",description:"Verify authentication and get user context",required:!0},{order:2,tool:"dataverse_list_tables",description:"Discover available tables",required:!0,tips:"Use customOnly=true to focus on custom tables"},{order:3,tool:"dataverse_get_table_metadata",description:"Inspect column names, types, and required fields",required:!0,tips:"Note logical names for use in $select and $filter"},{order:4,tool:"dataverse_query",description:"Execute OData query or use dataverse_execute_fetchxml for complex joins",required:!0,tips:"Always use $select to limit columns; use $top to limit rows"}]}],["explore_schema",{name:"explore_schema",description:"Explore the Dataverse schema: tables, columns, relationships, and saved views.",tags:["schema","metadata","tables","relationships","views"],steps:[{order:1,tool:"dataverse_whoami",description:"Verify authentication",required:!0},{order:2,tool:"dataverse_list_tables",description:"List all available tables",required:!0},{order:3,tool:"dataverse_get_table_metadata",description:"Get detailed column metadata for a table",required:!0},{order:4,tool:"dataverse_get_relationships",description:"Discover one-to-many and many-to-many relationships",required:!1,tips:"Useful before building FetchXML joins"},{order:5,tool:"dataverse_list_views",description:"List saved views for the table",required:!1}]}],["create_record",{name:"create_record",description:"Create a new record in Dataverse. Inspect schema first to know required fields, then create and verify.",tags:["create","record","write","insert"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Check required fields and column types before creating",required:!0,tips:"Pay attention to required fields and lookup columns"},{order:2,tool:"dataverse_create",description:"Create the new record",required:!0},{order:3,tool:"dataverse_query",description:"Verify the record was created with correct values",required:!1,tips:"Query by the returned ID to confirm field values"}]}],["update_record",{name:"update_record",description:"Update an existing record. Read current values, apply changes, then verify the update.",tags:["update","record","write","modify"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Confirm column names and types",required:!0},{order:2,tool:"dataverse_get",description:"Read current record values and etag",required:!0,tips:"Capture @odata.etag for optimistic concurrency"},{order:3,tool:"dataverse_update",description:"Apply the update with etag for concurrency control",required:!0},{order:4,tool:"dataverse_get",description:"Verify the update was applied correctly",required:!1}]}],["delete_record",{name:"delete_record",description:"Delete a record from Dataverse. Read the record first to confirm identity, then delete.",tags:["delete","record","remove"],steps:[{order:1,tool:"dataverse_get",description:"Read the record to confirm it is the correct one",required:!0,tips:"Show record details to the user before deletion"},{order:2,tool:"dataverse_delete",description:"Delete the record (irreversible)",required:!0,tips:"Always confirm with the user before executing this step"}]}],["bulk_operations",{name:"bulk_operations",description:"Perform bulk create/update/delete using Dataverse batch API for efficiency.",tags:["bulk","batch","mass","multiple"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Inspect schema to build correct request payloads",required:!0},{order:2,tool:"dataverse_batch_execute",description:"Execute batch of operations in a single request",required:!0,tips:"Group up to 1000 operations per batch; use changesets for transactional groups"}]}],["search_data",{name:"search_data",description:"Full-text search across Dataverse tables, then refine results with targeted queries.",tags:["search","find","fulltext","relevance"],steps:[{order:1,tool:"dataverse_search",description:"Execute full-text search across multiple tables",required:!0,tips:"Use entities filter to narrow which tables to search"},{order:2,tool:"dataverse_query",description:"Refine results with a targeted OData query on the relevant table",required:!1}]}],["manage_solution",{name:"manage_solution",description:"Inspect and manage Dataverse solutions and their components.",tags:["solution","customization","deployment","components"],steps:[{order:1,tool:"dataverse_query",description:"List all solutions: query the 'solutions' table ($select=uniquename,friendlyname,version,ismanaged)",required:!0,tips:"Filter with ismanaged eq false to see only unmanaged solutions"},{order:2,tool:"dataverse_query",description:"List components in a solution: query 'solutioncomponents' filtered by solutionid",required:!1,tips:"Join with the solution GUID from step 1; componenttype indicates the kind of component"},{order:3,tool:"dataverse_create_sitemap",description:"Create or update a sitemap entry within a solution",required:!1,tips:"Pass appModuleUniqueName to link the sitemap to a model-driven app"},{order:4,tool:"dataverse_publish_customizations",description:"Publish all pending customizations to make changes live",required:!1,tips:"Required after modifying solution components or sitemaps"}]}],["inspect_audit",{name:"inspect_audit",description:"Review audit history for records or tables to track changes and user activity.",tags:["audit","history","changes","tracking"],steps:[{order:1,tool:"dataverse_get_audit_log",description:"Retrieve audit entries filtered by record, table, user, or date range",required:!0,tips:"Use at least one filter to avoid large result sets; audit must be enabled on the table"}]}],["file_operations",{name:"file_operations",description:"Upload or download files from Dataverse file/image columns.",tags:["file","upload","download","image","attachment"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Identify file or image columns on the table",required:!0,tips:"Look for columns of type File or Image"},{order:2,tool:"dataverse_upload_file_column",description:"Upload a file to a file/image column",required:!1},{order:3,tool:"dataverse_download_file_column",description:"Download a file from a file/image column",required:!1,tips:"Returns base64-encoded content"}]}],["batch_schema_workflow",{name:"batch_schema_workflow",description:"Batch Schema Workflow \u2014 create multiple tables/columns/relationships then publish once. NEVER publish between individual schema operations. Collect all affected entity logical names and call dataverse_publish_customizations ONCE at the end.",tags:["schema","batch","table","column","relationship","publish"],steps:[{order:1,tool:"(planning)",description:"Plan all schema changes upfront: list every table to create, columns per table, and relationships. Collect all entity logical names (lowercased schemaName values) \u2014 you will need them for the final publish call. IMPORTANT: Creating NEW entities, columns, and relationships via POST auto-publishes (immediately queryable via API). An explicit publish is only required to regenerate model-driven app forms/views. PATCH/PUT updates to existing entities always require explicit publish.",required:!0,tips:"Entity logical name = schemaName.toLowerCase(). Do this before touching Dataverse."},{order:2,tool:"dataverse_create_table",description:"Create each table. Do NOT set autoPublish. Do NOT call dataverse_publish_customizations. NEW entity creation auto-publishes (API queryable immediately). Explicit publish is only needed to make the table visible in model-driven app forms/views.",required:!0,tips:"RULE: autoPublish must be omitted or false for every call in this workflow."},{order:3,tool:"dataverse_create_attribute",description:"Create every column on every table. Set autoPublish: false on every call. Also applies to dataverse_create_lookup_attribute. NEW column creation auto-publishes for API access \u2014 explicit publish only refreshes forms/views.",required:!1,tips:"RULE: NEVER set autoPublish: true during a batch workflow \u2014 causes publish-lock contention."},{order:4,tool:"dataverse_create_lookup_attribute",description:"Create lookup (relationship) columns. Set autoPublish: false on every call.",required:!1,tips:"Collect both entityLogicalName and referencedEntity \u2014 both must appear in the final publish list."},{order:5,tool:"dataverse_update_entity",description:"Update entity metadata if needed (enable notes, change tracking, audit). Set autoPublish: false. PATCH/PUT updates to existing entities require explicit publish \u2014 this is the main reason to publish at the end.",required:!1,tips:"RULE: autoPublish: false for all intermediate operations."},{order:6,tool:"dataverse_publish_customizations",description:"ONE single publish at the END. Pass components: { entities: [array of ALL affected entity logical names] }. This calls scoped PublishXml. NOTE: Both PublishXml and PublishAllXml share the same org-wide lock \u2014 the scope only controls what metadata is regenerated, NOT the lock granularity. Using components.entities is still preferred: it minimises regeneration time and reduces the lock hold duration. If a publish-lock error occurs, the MCP server auto-retries every 30s up to 4 times. If retries are exhausted and components.entities was provided, the server returns a soft success because a concurrent PublishAllXml will have covered all pending changes org-wide.",required:!0,tips:"Always provide components.entities in batch workflows \u2014 shorter lock hold = less contention."},{order:7,tool:"dataverse_get_table_metadata",description:"Verify each created table to confirm columns, relationships and publish are complete.",required:!1,tips:"Call once per created table to validate the full schema."}]}]]);var _a=[{name:"dataverse_list_guides",description:"Lists all available step-by-step guided workflows for common Dataverse tasks (query_data, create_record, manage_solution, etc.). Use this to discover recommended MCP patterns. NOTE: these are MCP-internal task guides, NOT Dataverse automation processes \u2014 use dataverse_list_workflows to list actual Dataverse flows/workflows.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_get_guide",description:"Returns the full definition of an MCP task guide including ordered steps, tool names, tips, and required flags. Use this to get a step-by-step plan for a common Dataverse task. NOTE: these are MCP-internal guides \u2014 use dataverse_get_workflow to retrieve an actual Dataverse process.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Guide name (e.g. query_data, explore_schema, create_record, update_record, delete_record, bulk_operations, search_data, manage_solution, inspect_audit, file_operations)"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_list_workflows",description:"Lists automation processes stored in the Dataverse environment: Power Automate cloud flows, classic workflows, business rules, business process flows, and actions. Optionally filter by category or partial name.",inputSchema:{type:"object",properties:{category:{type:"number",description:"Filter by process category: 0=Classic Workflow, 1=Dialog, 2=Business Rule, 3=Action, 4=Business Process Flow, 6=Power Automate (Cloud Flow). Omit to list all categories."},nameContains:{type:"string",description:"Filter by partial name match (case-insensitive contains)."},top:{type:"number",description:"Maximum number of results to return (default 50)."},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_get_workflow",description:"Returns full details of a Dataverse automation process (workflow, cloud flow, business rule, or action) by its GUID.",inputSchema:{type:"object",properties:{workflowId:{type:"string",description:"GUID of the workflow/process record"},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["workflowId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}}],ba={0:"Classic Workflow",1:"Dialog",2:"Business Rule",3:"Action",4:"Business Process Flow",6:"Power Automate (Cloud Flow)"},Zr=se.object({category:se.number().int().optional(),nameContains:se.string().optional(),top:se.number().int().positive().max(200).optional().default(50),compact:se.boolean().optional().default(!0)}),ei=se.object({workflowId:se.string().min(1),compact:se.boolean().optional().default(!0)});async function wa(a,i,n){if(a==="dataverse_list_guides"){let e=Array.from(Ie.values()).map(t=>({name:t.name,description:t.description,tags:t.tags,stepCount:t.steps.length}));return re("guides",e,["Use dataverse_get_guide with a guide name to see detailed steps"])}if(a==="dataverse_get_guide"){let{name:e}=i,t=Ie.get(e);if(!t){let r=Array.from(Ie.keys()).join(", ");throw new Error(`Unknown guide: "${e}". Available guides: ${r}`)}return p(`Guide "${t.name}": ${t.description}`,t,t.steps.map(r=>`Step ${r.order}: ${r.tool} \u2014 ${r.description}`))}if(a==="dataverse_list_workflows"){let e=Zr.parse(i),r=(await n.listDataverseWorkflows({...e.category!==void 0?{category:e.category}:{},...e.nameContains!==void 0?{nameContains:e.nameContains}:{},top:e.top})).map(o=>({workflowId:o.workflowid,name:o.name,category:ba[o.category]??o.category,state:o.statecode===1?"Active":"Inactive",modifiedOn:o.modifiedon}));return e.compact&&(r=v(r)),re(`Dataverse workflows (${r.length} found)`,r,["Use dataverse_get_workflow with a workflowId GUID for full details","Filter with category: 0=Classic, 2=Business Rule, 4=BPF, 6=Cloud Flow"])}if(a==="dataverse_get_workflow"){let{workflowId:e,compact:t}=ei.parse(i),r=await n.getDataverseWorkflow(e),o=r.category,s={...r,categoryLabel:ba[o]??`Category ${o}`,stateLabel:r.statecode===1?"Active":"Inactive"};return t&&(s=be(s)),p(`Workflow: ${r.name}`,s,[])}throw new Error(`Unknown workflow tool: ${a}`)}import{z as f}from"zod";var me=/^[a-z_][a-z0-9_]*$/,Ge=["String","Memo","Integer","Decimal","Money","DateTime","Boolean","Picklist","MultiSelectPicklist","AutoNumber","Image"],ti={String:"Microsoft.Dynamics.CRM.StringAttributeMetadata",Memo:"Microsoft.Dynamics.CRM.MemoAttributeMetadata",Integer:"Microsoft.Dynamics.CRM.IntegerAttributeMetadata",Decimal:"Microsoft.Dynamics.CRM.DecimalAttributeMetadata",Money:"Microsoft.Dynamics.CRM.MoneyAttributeMetadata",DateTime:"Microsoft.Dynamics.CRM.DateTimeAttributeMetadata",Boolean:"Microsoft.Dynamics.CRM.BooleanAttributeMetadata",Picklist:"Microsoft.Dynamics.CRM.PicklistAttributeMetadata",MultiSelectPicklist:"Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata",AutoNumber:"Microsoft.Dynamics.CRM.StringAttributeMetadata",Image:"Microsoft.Dynamics.CRM.ImageAttributeMetadata"},Na=f.object({entityLogicalName:f.string().min(1).regex(me,"Invalid Dataverse logical name"),schemaName:f.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/,"Invalid schema name"),attributeType:f.enum(Ge),displayName:f.string().min(1),description:f.string().optional(),requiredLevel:f.enum(["None","ApplicationRequired","Recommended"]).optional().default("None"),maxLength:f.number().int().positive().optional(),minValue:f.number().optional(),maxValue:f.number().optional(),precision:f.number().int().min(0).max(10).optional(),dateTimeFormat:f.enum(["DateOnly","DateAndTime"]).optional().default("DateAndTime"),defaultBooleanValue:f.boolean().optional().default(!1),picklistOptions:f.array(f.object({value:f.number().int(),label:f.string().min(1)})).optional(),autoNumberFormat:f.string().optional(),languageCode:f.number().int().optional().default(1033),autoPublish:f.boolean().optional().default(!1),confirm:f.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a column"})})}).refine(a=>a.attributeType!=="Picklist"&&a.attributeType!=="MultiSelectPicklist"||a.picklistOptions&&a.picklistOptions.length>0,{message:"picklistOptions is required for Picklist and MultiSelectPicklist types and must have at least one option"}),Sa=f.object({entityLogicalName:f.string().min(1).regex(me,"Invalid Dataverse logical name"),attributeLogicalName:f.string().min(1).regex(me,"Invalid Dataverse logical name"),displayName:f.string().min(1).optional(),description:f.string().optional(),requiredLevel:f.enum(["None","ApplicationRequired","Recommended"]).optional(),maxLength:f.number().int().positive().optional(),isSearchable:f.boolean().optional(),languageCode:f.number().int().optional().default(1033),autoPublish:f.boolean().optional().default(!1),confirm:f.literal(!0,{errorMap:()=>({message:"Set confirm: true to update column metadata"})})}).refine(a=>a.displayName!==void 0||a.description!==void 0||a.requiredLevel!==void 0||a.maxLength!==void 0||a.isSearchable!==void 0,{message:"At least one mutable property (displayName, description, requiredLevel, maxLength, isSearchable) must be provided"}),Ta=f.object({entityLogicalName:f.string().min(1).regex(me,"Invalid Dataverse logical name"),attributeLogicalName:f.string().min(1).regex(me,"Invalid Dataverse logical name"),autoPublish:f.boolean().optional().default(!1),confirm:f.literal(!0,{errorMap:()=>({message:"Set confirm: true to delete a column \u2014 this is irreversible"})})});function z(a,i){return{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:a,LanguageCode:i}]}}function Ra(a){let i=a.languageCode,n={"@odata.type":ti[a.attributeType],SchemaName:a.schemaName,DisplayName:z(a.displayName,i),RequiredLevel:{Value:a.requiredLevel}};switch(a.description&&(n.Description=z(a.description,i)),a.attributeType){case"String":n.MaxLength=a.maxLength??100;break;case"Memo":n.MaxLength=a.maxLength??4e3;break;case"Integer":n.Format="None",a.minValue!==void 0&&(n.MinValue=a.minValue),a.maxValue!==void 0&&(n.MaxValue=a.maxValue);break;case"Decimal":a.precision!==void 0&&(n.Precision=a.precision),a.minValue!==void 0&&(n.MinValue=a.minValue),a.maxValue!==void 0&&(n.MaxValue=a.maxValue);break;case"Money":n.PrecisionSource=a.precision!==void 0?0:2,a.precision!==void 0&&(n.Precision=a.precision);break;case"DateTime":n.Format=a.dateTimeFormat;break;case"Boolean":n.DefaultValue=a.defaultBooleanValue,n.OptionSet={TrueOption:{Value:1,Label:z("Yes",i)},FalseOption:{Value:0,Label:z("No",i)}};break;case"Picklist":n.OptionSet={"@odata.type":"Microsoft.Dynamics.CRM.OptionSetMetadata",IsGlobal:!1,OptionSetType:"Picklist",Options:a.picklistOptions.map(e=>({Value:e.value,Label:z(e.label,i)}))};break;case"MultiSelectPicklist":n.OptionSet={"@odata.type":"Microsoft.Dynamics.CRM.OptionSetMetadata",IsGlobal:!1,Options:a.picklistOptions.map(e=>({Value:e.value,Label:z(e.label,i)}))};break;case"AutoNumber":n.MaxLength=a.maxLength??100,a.autoNumberFormat&&(n.AutoNumberFormat=a.autoNumberFormat);break;case"Image":break}return n}var Aa=f.object({entityLogicalName:f.string().min(1).regex(me),schemaName:f.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/),displayName:f.string().min(1),description:f.string().optional(),referencedEntity:f.string().min(1).regex(me),requiredLevel:f.enum(["None","ApplicationRequired","Recommended"]).optional().default("None"),cascadeConfiguration:f.enum(["Referential","Parental"]).optional().default("Referential"),languageCode:f.number().int().optional().default(1033),autoPublish:f.boolean().optional().default(!1),confirm:f.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a lookup column"})})});function ka(a){let i=a.languageCode,n=a.schemaName.includes("_")?a.schemaName.slice(0,a.schemaName.indexOf("_")):a.schemaName,e=a.schemaName.includes("_")?a.schemaName.slice(a.schemaName.indexOf("_")+1):a.schemaName,t=`${n}_${a.referencedEntity}_${a.entityLogicalName}_${e}`;if(t.length>100)throw new Error(`Auto-generated relationship SchemaName '${t}' is ${t.length} chars, exceeding Dataverse's 100-char limit. Use shorter entity logical names or column schema names.`);let r={"@odata.type":"Microsoft.Dynamics.CRM.LookupAttributeMetadata",AttributeType:"Lookup",AttributeTypeName:{Value:"LookupType"},SchemaName:a.schemaName,DisplayName:z(a.displayName,i),RequiredLevel:{Value:a.requiredLevel}};return a.description&&(r.Description=z(a.description,i)),{"@odata.type":"Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",SchemaName:t,ReferencedEntity:a.referencedEntity,ReferencedAttribute:`${a.referencedEntity}id`,ReferencingEntity:a.entityLogicalName,Lookup:r,CascadeConfiguration:a.cascadeConfiguration==="Parental"?{Assign:"Cascade",Delete:"Cascade",Merge:"Cascade",Reparent:"Cascade",Share:"Cascade",Unshare:"Cascade"}:{Assign:"NoCascade",Delete:"RemoveLink",Merge:"NoCascade",Reparent:"NoCascade",Share:"NoCascade",Unshare:"NoCascade"},AssociatedMenuConfiguration:{Behavior:"UseCollectionName",Group:"Details",Label:z(a.displayName,i),Order:1e4}}}var Be=[{name:"dataverse_create_attribute",description:"Creates a new column (attribute) on an existing Dataverse table. Supports String, Memo, Integer, Decimal, Money, DateTime, Boolean, Picklist, MultiSelectPicklist, AutoNumber, and Image types. IMPORTANT: Column type CANNOT be changed after creation \u2014 choose carefully. Requires System Customizer or System Administrator privileges. WHEN TO USE: Adding a new column to a table. BEST PRACTICES: Use dataverse_get_table_metadata first to check existing columns. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the target table (e.g. 'account', 'new_mytable')"},schemaName:{type:"string",description:"Schema name for the new column (e.g. 'new_mycolumn'). Must include publisher prefix."},attributeType:{type:"string",enum:Ge,description:"Column data type. CANNOT be changed after creation. String=single-line text, Memo=multi-line, Integer=whole number, Decimal=decimal number, Money=currency, DateTime=date/time, Boolean=yes/no, Picklist=choice/option set, MultiSelectPicklist=multi-choice, AutoNumber=auto-generated sequence, Image=image file."},displayName:{type:"string",description:"Display label for the column"},description:{type:"string",description:"Optional description of the column"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"Required level. Default: None"},maxLength:{type:"number",description:"Max length for String (default 100, max 4000) or Memo (default 4000, max 1048576) types"},minValue:{type:"number",description:"Minimum value for Integer or Decimal types"},maxValue:{type:"number",description:"Maximum value for Integer or Decimal types"},precision:{type:"number",description:"Decimal precision for Decimal (0-10) or Money types"},dateTimeFormat:{type:"string",enum:["DateOnly","DateAndTime"],description:"Format for DateTime type. CANNOT be changed after creation. Default: DateAndTime"},defaultBooleanValue:{type:"boolean",description:"Default value for Boolean type. Default: false"},picklistOptions:{type:"array",items:{type:"object",properties:{value:{type:"number",description:"Integer value for the option"},label:{type:"string",description:"Display label for the option"}},required:["value","label"]},description:"Options for Picklist and MultiSelectPicklist types. Each option needs a value (integer) and label (text)."},autoNumberFormat:{type:"string",description:"Auto-number format string for AutoNumber type (e.g. 'INV-{SEQNUM:5}'). Only used when attributeType=AutoNumber."},languageCode:{type:"number",description:"Language code for labels. Default: 1033 (English)"},autoPublish:{type:"boolean",description:"Publish after creation (default: false). BATCH RULE: NEVER set true if you are creating/modifying multiple tables, columns, or relationships in the same session \u2014 setting true between operations causes Dataverse publish-lock contention (0x80044181). Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","schemaName","attributeType","displayName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_update_attribute",description:"Updates mutable properties of an existing column (attribute): display name, description, required level, max length (increase only for String/Memo), and search indexing. CANNOT change: column type, logical name, DateTime format, or lookup targets \u2014 these are immutable after creation. Requires System Customizer or System Administrator privileges. WHEN TO USE: Changing a column's label, description, or required level. BEST PRACTICES: Use dataverse_get_table_metadata first to see current values. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the table"},attributeLogicalName:{type:"string",description:"Logical name of the column to update"},displayName:{type:"string",description:"New display label for the column"},description:{type:"string",description:"New description for the column"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"New required level"},maxLength:{type:"number",description:"New max length for String/Memo columns. Can only INCREASE, never decrease."},isSearchable:{type:"boolean",description:"Enable or disable Dataverse Search indexing on this column"},languageCode:{type:"number",description:"Language code for labels. Default: 1033 (English)"},autoPublish:{type:"boolean",description:"Publish after update (default: false). BATCH RULE: NEVER set true if you are updating multiple columns or other schema objects in the same session \u2014 setting true between operations causes Dataverse publish-lock contention (0x80044181). Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","attributeLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_delete_attribute",description:"Deletes a column (attribute) from a Dataverse table. WARNING: This permanently deletes the column AND all its data. Automatically checks dependencies (views, workflows, forms) before deletion \u2014 refuses if dependencies exist unless force=true. Managed solution columns cannot be deleted. WHEN TO USE: Removing an obsolete custom column. BEST PRACTICES: Use dataverse_get_table_metadata to verify the column exists; check dependencies first. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the table"},attributeLogicalName:{type:"string",description:"Logical name of the column to delete"},autoPublish:{type:"boolean",description:"Publish after deletion (default: false). BATCH RULE: NEVER set true if you are deleting multiple columns or performing other schema changes in the same session. Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional deletion of column and ALL its data"}},required:["entityLogicalName","attributeLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_create_lookup_attribute",description:"Creates a Lookup column on a Dataverse table by creating a One-to-Many relationship. Use this for typed relationship columns (e.g. 'Account' field on a custom table pointing to the account table). IMPORTANT: Creates both the relationship AND the lookup column in one operation. cascadeConfiguration defaults to 'Referential' (NoCascade on Assign/Reparent/Share/Unshare, RemoveLink on Delete). Use 'Parental' only when the source table has no existing parental relationship with the target. Requires System Customizer or System Administrator privileges.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Source table logical name (referencing)"},schemaName:{type:"string",description:"Schema name for the lookup column (e.g. 'new_accountid')"},displayName:{type:"string",description:"Display label"},description:{type:"string",description:"Optional description"},referencedEntity:{type:"string",description:"Target table logical name (e.g. 'account')"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"Required level. Default: None"},languageCode:{type:"number",description:"Language code. Default: 1033"},cascadeConfiguration:{type:"string",enum:["Referential","Parental"],description:"Cascade behavior. 'Referential' (default): NoCascade on most actions, RemoveLink on Delete \u2014 works on any table pair. 'Parental': full Cascade \u2014 only if no parental relation exists yet with the target."},autoPublish:{type:"boolean",description:"Publish after creation (default: false). BATCH RULE: NEVER set true if you are creating/modifying multiple tables, columns, or relationships in the same session \u2014 causes Dataverse publish-lock contention. Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true"}},required:["entityLogicalName","schemaName","displayName","referencedEntity","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function Ea(a,i,n){switch(a){case"dataverse_create_attribute":{let e=Na.parse(i),t=X({toolName:"dataverse_create_attribute",entitySetName:e.entityLogicalName}).map(c=>`[${c.severity.toUpperCase()}] ${c.code}: ${c.message}`),r=Ra(e),o=await n.createAttribute(e.entityLogicalName,r),s="autoPublish=false (skipped)";return e.autoPublish&&(await n.publishCustomizations({entities:[e.entityLogicalName]}),s="published successfully"),p(`Column '${e.schemaName}' (${e.attributeType}) created on '${e.entityLogicalName}'. ${s}.`,{entityLogicalName:e.entityLogicalName,schemaName:e.schemaName,attributeType:e.attributeType,metadataId:o,published:e.autoPublish,...t.length>0&&{warnings:t}},["Use dataverse_get_table_metadata to verify the new column","Column type CANNOT be changed after creation \u2014 to change type, create a new column and migrate data"])}case"dataverse_update_attribute":{let e=Sa.parse(i),t=X({toolName:"dataverse_update_attribute",entitySetName:e.entityLogicalName}).map(d=>`[${d.severity.toUpperCase()}] ${d.code}: ${d.message}`),r=e.languageCode,o={},s={};e.displayName!==void 0&&(o.DisplayName=z(e.displayName,r),s.DisplayName=e.displayName),e.description!==void 0&&(o.Description=z(e.description,r),s.Description=e.description),e.requiredLevel!==void 0&&(o.RequiredLevel={Value:e.requiredLevel},s.RequiredLevel=e.requiredLevel),e.maxLength!==void 0&&(o.MaxLength=e.maxLength,s.MaxLength=e.maxLength),e.isSearchable!==void 0&&(o.IsSearchable=e.isSearchable,s.IsSearchable=e.isSearchable),await n.updateAttribute(e.entityLogicalName,e.attributeLogicalName,o);let c=Object.entries(s).map(([d,u])=>`${d}=${String(u)}`).join(", "),l="autoPublish=false (skipped)";return e.autoPublish&&(await n.publishCustomizations({entities:[e.entityLogicalName]}),l="published successfully"),p(`Column '${e.attributeLogicalName}' on '${e.entityLogicalName}' updated: ${c}. ${l}.`,{entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,changes:s,published:e.autoPublish,...t.length>0&&{warnings:t}},["Use dataverse_get_table_metadata to verify the changes","Column type, logical name, and DateTime format cannot be changed \u2014 these are immutable after creation"])}case"dataverse_delete_attribute":{let e=Ta.parse(i),t=X({toolName:"dataverse_delete_attribute",entitySetName:e.entityLogicalName}).map(o=>`[${o.severity.toUpperCase()}] ${o.code}: ${o.message}`);try{await n.deleteAttribute(e.entityLogicalName,e.attributeLogicalName)}catch(o){let s=o instanceof Error?o.message:String(o);if(s.includes("0x80048405"))return P({summary:`Cannot delete column '${e.attributeLogicalName}': it belongs to a managed solution and cannot be deleted.`,data:{error:"0x80048405",entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName},errorCategory:"SCHEMA_MISMATCH",suggestions:["Managed solution columns can only be removed by uninstalling the solution"]});if(s.includes("dependency")||s.includes("dependencies")||s.includes("DependencyList"))return P({summary:`Cannot delete column '${e.attributeLogicalName}': it has dependencies (views, workflows, forms, or other components reference it).`,data:{error:"HAS_DEPENDENCIES",entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,rawError:s},errorCategory:"SCHEMA_MISMATCH",suggestions:["Use dataverse_list_dependencies to identify what references this column","Remove all references before retrying deletion"]});let c=s.match(/0x[0-9a-fA-F]{8}/),l=c?c[0]:"UNKNOWN";return P({summary:`Cannot delete column '${e.attributeLogicalName}': deletion rejected by Dataverse (${l}).`,data:{error:l,entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,rawError:s},errorCategory:"SCHEMA_MISMATCH",suggestions:["Verify this is a custom (non-managed, non-system) column","Check that no solution dependencies or forms reference this column","Standard system attributes (e.g. accountnumber) cannot be deleted"]})}let r="autoPublish=false (skipped)";return e.autoPublish&&(await n.publishCustomizations({entities:[e.entityLogicalName]}),r="published successfully"),p(`Column '${e.attributeLogicalName}' deleted from '${e.entityLogicalName}'. ${r}. All data in this column has been permanently removed.`,{entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,deleted:!0,published:e.autoPublish,...t.length>0&&{warnings:t}},["This action is irreversible \u2014 the column and its data cannot be recovered","The column logical name is tombstoned and cannot be reused for ~90 days"])}case"dataverse_create_lookup_attribute":{let e=Aa.parse(i),t=X({toolName:"dataverse_create_lookup_attribute",entitySetName:e.entityLogicalName}).map(l=>`[${l.severity.toUpperCase()}] ${l.code}: ${l.message}`),r=ka(e),o,s=!1;try{o=await n.createRelationship(r)}catch(l){if((l instanceof Error?l.message:String(l)).includes("Request timed out")){let u=!1;try{u=((await n.getTableMetadata(e.entityLogicalName,!0)).Attributes??[]).some(y=>y.LogicalName===e.schemaName.toLowerCase())}catch{}if(u)s=!0,o="";else throw l}else throw l}let c="autoPublish=false (skipped)";return e.autoPublish&&!s?(await n.publishCustomizations({entities:[e.entityLogicalName,e.referencedEntity]}),c="published successfully"):s&&(c="publish skipped (request timed out \u2014 use dataverse_publish_customizations to publish)"),s&&t.push("REQUEST TIMED OUT but lookup was found on the table \u2014 creation succeeded. MetadataId unavailable."),p(`Lookup column '${e.schemaName}' created on '${e.entityLogicalName}' \u2192 '${e.referencedEntity}'. ${c}.`,{entityLogicalName:e.entityLogicalName,schemaName:e.schemaName,referencedEntity:e.referencedEntity,...o&&{metadataId:o},published:e.autoPublish&&!s,...t.length>0&&{warnings:t}},["Use dataverse_get_table_metadata to verify the new lookup column","Lookup columns cannot be created via the standard attribute API \u2014 this uses the RelationshipDefinitions endpoint"])}default:throw new Error(`Unknown attribute tool: ${a}`)}}import{z as O}from"zod";function le(a){return a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function ai(a){let i=0,n=0,e=0;return`<SiteMap>
149
- ${a.map(r=>{let o=r.id??`Area_${++i}`,s=r.groups.map(c=>{let l=c.id??`Group_${++n}`,d=c.subareas.map(u=>{let g=u.id??`Sub_${++e}`,m=[`Id="${le(g)}"`];return u.entityLogicalName&&m.push(`Entity="${le(u.entityLogicalName)}"`),u.url&&m.push(`Url="${le(u.url)}"`),u.title&&m.push(`Title="${le(u.title)}"`),` <SubArea ${m.join(" ")}/>`});return` <Group Id="${le(l)}" Title="${le(c.title)}">
280
+ Produce: Workflow Health Matrix | Issues by severity | Migration candidates prioritised by effort`}}]}}import{z as he}from"zod";var nt=new Map([["query_data",{name:"query_data",description:"Query data from Dataverse tables. Authenticate, discover tables, inspect schema, then query with OData or FetchXML.",tags:["query","data","fetchxml","odata","read"],steps:[{order:1,tool:"dataverse_whoami",description:"Verify authentication and get user context",required:!0},{order:2,tool:"dataverse_list_tables",description:"Discover available tables",required:!0,tips:"Use customOnly=true to focus on custom tables"},{order:3,tool:"dataverse_get_table_metadata",description:"Inspect column names, types, and required fields",required:!0,tips:"Note logical names for use in $select and $filter"},{order:4,tool:"dataverse_query",description:"Execute OData query or use dataverse_execute_fetchxml for complex joins",required:!0,tips:"Always use $select to limit columns; use $top to limit rows"}]}],["explore_schema",{name:"explore_schema",description:"Explore the Dataverse schema: tables, columns, relationships, and saved views.",tags:["schema","metadata","tables","relationships","views"],steps:[{order:1,tool:"dataverse_whoami",description:"Verify authentication",required:!0},{order:2,tool:"dataverse_list_tables",description:"List all available tables",required:!0},{order:3,tool:"dataverse_get_table_metadata",description:"Get detailed column metadata for a table",required:!0},{order:4,tool:"dataverse_get_relationships",description:"Discover one-to-many and many-to-many relationships",required:!1,tips:"Useful before building FetchXML joins"},{order:5,tool:"dataverse_list_views",description:"List saved views for the table",required:!1}]}],["create_record",{name:"create_record",description:"Create a new record in Dataverse. Inspect schema first to know required fields, then create and verify.",tags:["create","record","write","insert"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Check required fields and column types before creating",required:!0,tips:"Pay attention to required fields and lookup columns"},{order:2,tool:"dataverse_create",description:"Create the new record",required:!0},{order:3,tool:"dataverse_query",description:"Verify the record was created with correct values",required:!1,tips:"Query by the returned ID to confirm field values"}]}],["update_record",{name:"update_record",description:"Update an existing record. Read current values, apply changes, then verify the update.",tags:["update","record","write","modify"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Confirm column names and types",required:!0},{order:2,tool:"dataverse_get",description:"Read current record values and etag",required:!0,tips:"Capture @odata.etag for optimistic concurrency"},{order:3,tool:"dataverse_update",description:"Apply the update with etag for concurrency control",required:!0},{order:4,tool:"dataverse_get",description:"Verify the update was applied correctly",required:!1}]}],["delete_record",{name:"delete_record",description:"Delete a record from Dataverse. Read the record first to confirm identity, then delete.",tags:["delete","record","remove"],steps:[{order:1,tool:"dataverse_get",description:"Read the record to confirm it is the correct one",required:!0,tips:"Show record details to the user before deletion"},{order:2,tool:"dataverse_delete",description:"Delete the record (irreversible)",required:!0,tips:"Always confirm with the user before executing this step"}]}],["bulk_operations",{name:"bulk_operations",description:"Perform bulk create/update/delete using Dataverse batch API for efficiency.",tags:["bulk","batch","mass","multiple"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Inspect schema to build correct request payloads",required:!0},{order:2,tool:"dataverse_batch_execute",description:"Execute batch of operations in a single request",required:!0,tips:"Group up to 1000 operations per batch; use changesets for transactional groups"}]}],["search_data",{name:"search_data",description:"Full-text search across Dataverse tables, then refine results with targeted queries.",tags:["search","find","fulltext","relevance"],steps:[{order:1,tool:"dataverse_search",description:"Execute full-text search across multiple tables",required:!0,tips:"Use entities filter to narrow which tables to search"},{order:2,tool:"dataverse_query",description:"Refine results with a targeted OData query on the relevant table",required:!1}]}],["manage_solution",{name:"manage_solution",description:"Inspect and manage Dataverse solutions and their components.",tags:["solution","customization","deployment","components"],steps:[{order:1,tool:"dataverse_query",description:"List all solutions: query the 'solutions' table ($select=uniquename,friendlyname,version,ismanaged)",required:!0,tips:"Filter with ismanaged eq false to see only unmanaged solutions"},{order:2,tool:"dataverse_query",description:"List components in a solution: query 'solutioncomponents' filtered by solutionid",required:!1,tips:"Join with the solution GUID from step 1; componenttype indicates the kind of component"},{order:3,tool:"dataverse_create_sitemap",description:"Create or update a sitemap entry within a solution",required:!1,tips:"Pass appModuleUniqueName to link the sitemap to a model-driven app"},{order:4,tool:"dataverse_publish_customizations",description:"Publish all pending customizations to make changes live",required:!1,tips:"Required after modifying solution components or sitemaps"}]}],["inspect_audit",{name:"inspect_audit",description:"Review audit history for records or tables to track changes and user activity.",tags:["audit","history","changes","tracking"],steps:[{order:1,tool:"dataverse_get_audit_log",description:"Retrieve audit entries filtered by record, table, user, or date range",required:!0,tips:"Use at least one filter to avoid large result sets; audit must be enabled on the table"}]}],["file_operations",{name:"file_operations",description:"Upload or download files from Dataverse file/image columns.",tags:["file","upload","download","image","attachment"],steps:[{order:1,tool:"dataverse_get_table_metadata",description:"Identify file or image columns on the table",required:!0,tips:"Look for columns of type File or Image"},{order:2,tool:"dataverse_upload_file_column",description:"Upload a file to a file/image column",required:!1},{order:3,tool:"dataverse_download_file_column",description:"Download a file from a file/image column",required:!1,tips:"Returns base64-encoded content"}]}],["batch_schema_workflow",{name:"batch_schema_workflow",description:"Batch Schema Workflow \u2014 create multiple tables/columns/relationships then publish once. NEVER publish between individual schema operations. Collect all affected entity logical names and call dataverse_publish_customizations ONCE at the end.",tags:["schema","batch","table","column","relationship","publish"],steps:[{order:1,tool:"(planning)",description:"Plan all schema changes upfront: list every table to create, columns per table, and relationships. Collect all entity logical names (lowercased schemaName values) \u2014 you will need them for the final publish call. IMPORTANT: Creating NEW entities, columns, and relationships via POST auto-publishes (immediately queryable via API). An explicit publish is only required to regenerate model-driven app forms/views. PATCH/PUT updates to existing entities always require explicit publish.",required:!0,tips:"Entity logical name = schemaName.toLowerCase(). Do this before touching Dataverse."},{order:2,tool:"dataverse_create_table",description:"Create each table. Do NOT set autoPublish. Do NOT call dataverse_publish_customizations. NEW entity creation auto-publishes (API queryable immediately). Explicit publish is only needed to make the table visible in model-driven app forms/views.",required:!0,tips:"RULE: autoPublish must be omitted or false for every call in this workflow."},{order:3,tool:"dataverse_create_attribute",description:"Create every column on every table. Set autoPublish: false on every call. Also applies to dataverse_create_lookup_attribute. NEW column creation auto-publishes for API access \u2014 explicit publish only refreshes forms/views.",required:!1,tips:"RULE: NEVER set autoPublish: true during a batch workflow \u2014 causes publish-lock contention."},{order:4,tool:"dataverse_create_lookup_attribute",description:"Create lookup (relationship) columns. Set autoPublish: false on every call.",required:!1,tips:"Collect both entityLogicalName and referencedEntity \u2014 both must appear in the final publish list."},{order:5,tool:"dataverse_update_entity",description:"Update entity metadata if needed (enable notes, change tracking, audit). Set autoPublish: false. PATCH/PUT updates to existing entities require explicit publish \u2014 this is the main reason to publish at the end.",required:!1,tips:"RULE: autoPublish: false for all intermediate operations."},{order:6,tool:"dataverse_publish_customizations",description:"ONE single publish at the END. Pass components: { entities: [array of ALL affected entity logical names] }. This calls scoped PublishXml. NOTE: Both PublishXml and PublishAllXml share the same org-wide lock \u2014 the scope only controls what metadata is regenerated, NOT the lock granularity. Using components.entities is still preferred: it minimises regeneration time and reduces the lock hold duration. If a publish-lock error occurs, the MCP server auto-retries every 30s up to 4 times. If retries are exhausted and components.entities was provided, the server returns a soft success because a concurrent PublishAllXml will have covered all pending changes org-wide.",required:!0,tips:"Always provide components.entities in batch workflows \u2014 shorter lock hold = less contention."},{order:7,tool:"dataverse_get_table_metadata",description:"Verify each created table to confirm columns, relationships and publish are complete.",required:!1,tips:"Call once per created table to validate the full schema."}]}]]);var wa=[{name:"dataverse_list_guides",description:"Lists all available step-by-step guided workflows for common Dataverse tasks (query_data, create_record, manage_solution, etc.). Use this to discover recommended MCP patterns. NOTE: these are MCP-internal task guides, NOT Dataverse automation processes \u2014 use dataverse_list_workflows to list actual Dataverse flows/workflows.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_get_guide",description:"Returns the full definition of an MCP task guide including ordered steps, tool names, tips, and required flags. Use this to get a step-by-step plan for a common Dataverse task. NOTE: these are MCP-internal guides \u2014 use dataverse_get_workflow to retrieve an actual Dataverse process.",inputSchema:{type:"object",properties:{name:{type:"string",description:"Guide name (e.g. query_data, explore_schema, create_record, update_record, delete_record, bulk_operations, search_data, manage_solution, inspect_audit, file_operations)"}},required:["name"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_list_workflows",description:"Lists automation processes stored in the Dataverse environment: Power Automate cloud flows, classic workflows, business rules, business process flows, and actions. Optionally filter by category or partial name.",inputSchema:{type:"object",properties:{category:{type:"number",description:"Filter by process category: 0=Classic Workflow, 1=Dialog, 2=Business Rule, 3=Action, 4=Business Process Flow, 6=Power Automate (Cloud Flow). Omit to list all categories."},nameContains:{type:"string",description:"Filter by partial name match (case-insensitive contains)."},top:{type:"number",description:"Maximum number of results to return (default 50)."},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}},{name:"dataverse_get_workflow",description:"Returns full details of a Dataverse automation process (workflow, cloud flow, business rule, or action) by its GUID.",inputSchema:{type:"object",properties:{workflowId:{type:"string",description:"GUID of the workflow/process record"},compact:{type:"boolean",description:"When true (default), strips null/undefined/empty fields from results to reduce token usage."}},required:["workflowId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0}}],_a={0:"Classic Workflow",1:"Dialog",2:"Business Rule",3:"Action",4:"Business Process Flow",6:"Power Automate (Cloud Flow)"},Gs=he.object({category:he.number().int().optional(),nameContains:he.string().optional(),top:he.number().int().positive().max(200).optional().default(50),compact:he.boolean().optional().default(!0)}),Vs=he.object({workflowId:he.string().min(1),compact:he.boolean().optional().default(!0)});async function Ta(r,a,t){if(r==="dataverse_list_guides"){let e=Array.from(nt.values()).map(n=>({name:n.name,description:n.description,tags:n.tags,stepCount:n.steps.length}));return ge("guides",e,["Use dataverse_get_guide with a guide name to see detailed steps"])}if(r==="dataverse_get_guide"){let{name:e}=a,n=nt.get(e);if(!n){let i=Array.from(nt.keys()).join(", ");throw new Error(`Unknown guide: "${e}". Available guides: ${i}`)}return f(`Guide "${n.name}": ${n.description}`,n,n.steps.map(i=>`Step ${i.order}: ${i.tool} \u2014 ${i.description}`))}if(r==="dataverse_list_workflows"){let e=Gs.parse(a),i=(await t.listDataverseWorkflows({...e.category!==void 0?{category:e.category}:{},...e.nameContains!==void 0?{nameContains:e.nameContains}:{},top:e.top})).map(s=>({workflowId:s.workflowid,name:s.name,category:_a[s.category]??s.category,state:s.statecode===1?"Active":"Inactive",modifiedOn:s.modifiedon}));return e.compact&&(i=T(i)),ge(`Dataverse workflows (${i.length} found)`,i,["Use dataverse_get_workflow with a workflowId GUID for full details","Filter with category: 0=Classic, 2=Business Rule, 4=BPF, 6=Cloud Flow"])}if(r==="dataverse_get_workflow"){let{workflowId:e,compact:n}=Vs.parse(a),i=await t.getDataverseWorkflow(e),s=i.category,o={...i,categoryLabel:_a[s]??`Category ${s}`,stateLabel:i.statecode===1?"Active":"Inactive"};return n&&(o=De(o)),f(`Workflow: ${i.name}`,o,[])}throw new Error(`Unknown workflow tool: ${r}`)}import{z as v}from"zod";var Re=/^[a-z_][a-z0-9_]*$/,kt=["String","Memo","Integer","Decimal","Money","DateTime","Boolean","Picklist","MultiSelectPicklist","AutoNumber","Image"],zs={String:"Microsoft.Dynamics.CRM.StringAttributeMetadata",Memo:"Microsoft.Dynamics.CRM.MemoAttributeMetadata",Integer:"Microsoft.Dynamics.CRM.IntegerAttributeMetadata",Decimal:"Microsoft.Dynamics.CRM.DecimalAttributeMetadata",Money:"Microsoft.Dynamics.CRM.MoneyAttributeMetadata",DateTime:"Microsoft.Dynamics.CRM.DateTimeAttributeMetadata",Boolean:"Microsoft.Dynamics.CRM.BooleanAttributeMetadata",Picklist:"Microsoft.Dynamics.CRM.PicklistAttributeMetadata",MultiSelectPicklist:"Microsoft.Dynamics.CRM.MultiSelectPicklistAttributeMetadata",AutoNumber:"Microsoft.Dynamics.CRM.StringAttributeMetadata",Image:"Microsoft.Dynamics.CRM.ImageAttributeMetadata"},Ra=v.object({entityLogicalName:v.string().min(1).regex(Re,"Invalid Dataverse logical name"),schemaName:v.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/,"Invalid schema name"),attributeType:v.enum(kt),displayName:v.string().min(1),description:v.string().optional(),requiredLevel:v.enum(["None","ApplicationRequired","Recommended"]).optional().default("None"),maxLength:v.number().int().positive().optional(),minValue:v.number().optional(),maxValue:v.number().optional(),precision:v.number().int().min(0).max(10).optional(),dateTimeFormat:v.enum(["DateOnly","DateAndTime"]).optional().default("DateAndTime"),defaultBooleanValue:v.boolean().optional().default(!1),picklistOptions:v.array(v.object({value:v.number().int(),label:v.string().min(1)})).optional(),autoNumberFormat:v.string().optional(),languageCode:v.number().int().optional().default(1033),autoPublish:v.boolean().optional().default(!1),confirm:v.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a column"})})}).refine(r=>r.attributeType!=="Picklist"&&r.attributeType!=="MultiSelectPicklist"||r.picklistOptions&&r.picklistOptions.length>0,{message:"picklistOptions is required for Picklist and MultiSelectPicklist types and must have at least one option"}),Sa=v.object({entityLogicalName:v.string().min(1).regex(Re,"Invalid Dataverse logical name"),attributeLogicalName:v.string().min(1).regex(Re,"Invalid Dataverse logical name"),displayName:v.string().min(1).optional(),description:v.string().optional(),requiredLevel:v.enum(["None","ApplicationRequired","Recommended"]).optional(),maxLength:v.number().int().positive().optional(),isSearchable:v.boolean().optional(),languageCode:v.number().int().optional().default(1033),autoPublish:v.boolean().optional().default(!1),confirm:v.literal(!0,{errorMap:()=>({message:"Set confirm: true to update column metadata"})})}).refine(r=>r.displayName!==void 0||r.description!==void 0||r.requiredLevel!==void 0||r.maxLength!==void 0||r.isSearchable!==void 0,{message:"At least one mutable property (displayName, description, requiredLevel, maxLength, isSearchable) must be provided"}),Na=v.object({entityLogicalName:v.string().min(1).regex(Re,"Invalid Dataverse logical name"),attributeLogicalName:v.string().min(1).regex(Re,"Invalid Dataverse logical name"),autoPublish:v.boolean().optional().default(!1),confirm:v.literal(!0,{errorMap:()=>({message:"Set confirm: true to delete a column \u2014 this is irreversible"})})});function Y(r,a){return{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:r,LanguageCode:a}]}}function Aa(r){let a=r.languageCode,t={"@odata.type":zs[r.attributeType],SchemaName:r.schemaName,DisplayName:Y(r.displayName,a),RequiredLevel:{Value:r.requiredLevel}};switch(r.description&&(t.Description=Y(r.description,a)),r.attributeType){case"String":t.MaxLength=r.maxLength??100;break;case"Memo":t.MaxLength=r.maxLength??4e3;break;case"Integer":t.Format="None",r.minValue!==void 0&&(t.MinValue=r.minValue),r.maxValue!==void 0&&(t.MaxValue=r.maxValue);break;case"Decimal":r.precision!==void 0&&(t.Precision=r.precision),r.minValue!==void 0&&(t.MinValue=r.minValue),r.maxValue!==void 0&&(t.MaxValue=r.maxValue);break;case"Money":t.PrecisionSource=r.precision!==void 0?0:2,r.precision!==void 0&&(t.Precision=r.precision);break;case"DateTime":t.Format=r.dateTimeFormat;break;case"Boolean":t.DefaultValue=r.defaultBooleanValue,t.OptionSet={TrueOption:{Value:1,Label:Y("Yes",a)},FalseOption:{Value:0,Label:Y("No",a)}};break;case"Picklist":t.OptionSet={"@odata.type":"Microsoft.Dynamics.CRM.OptionSetMetadata",IsGlobal:!1,OptionSetType:"Picklist",Options:r.picklistOptions.map(e=>({Value:e.value,Label:Y(e.label,a)}))};break;case"MultiSelectPicklist":t.OptionSet={"@odata.type":"Microsoft.Dynamics.CRM.OptionSetMetadata",IsGlobal:!1,Options:r.picklistOptions.map(e=>({Value:e.value,Label:Y(e.label,a)}))};break;case"AutoNumber":t.MaxLength=r.maxLength??100,r.autoNumberFormat&&(t.AutoNumberFormat=r.autoNumberFormat);break;case"Image":break}return t}var ka=v.object({entityLogicalName:v.string().min(1).regex(Re),schemaName:v.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_]*$/),displayName:v.string().min(1),description:v.string().optional(),referencedEntity:v.string().min(1).regex(Re),requiredLevel:v.enum(["None","ApplicationRequired","Recommended"]).optional().default("None"),cascadeConfiguration:v.enum(["Referential","Parental"]).optional().default("Referential"),languageCode:v.number().int().optional().default(1033),autoPublish:v.boolean().optional().default(!1),confirm:v.literal(!0,{errorMap:()=>({message:"Set confirm: true to create a lookup column"})})});function Ea(r){let a=r.languageCode,t=r.schemaName.includes("_")?r.schemaName.slice(0,r.schemaName.indexOf("_")):r.schemaName,e=r.schemaName.includes("_")?r.schemaName.slice(r.schemaName.indexOf("_")+1):r.schemaName,n=`${t}_${r.referencedEntity}_${r.entityLogicalName}_${e}`;if(n.length>100)throw new Error(`Auto-generated relationship SchemaName '${n}' is ${n.length} chars, exceeding Dataverse's 100-char limit. Use shorter entity logical names or column schema names.`);let i={"@odata.type":"Microsoft.Dynamics.CRM.LookupAttributeMetadata",AttributeType:"Lookup",AttributeTypeName:{Value:"LookupType"},SchemaName:r.schemaName,DisplayName:Y(r.displayName,a),RequiredLevel:{Value:r.requiredLevel}};return r.description&&(i.Description=Y(r.description,a)),{"@odata.type":"Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",SchemaName:n,ReferencedEntity:r.referencedEntity,ReferencedAttribute:`${r.referencedEntity}id`,ReferencingEntity:r.entityLogicalName,Lookup:i,CascadeConfiguration:r.cascadeConfiguration==="Parental"?{Assign:"Cascade",Delete:"Cascade",Merge:"Cascade",Reparent:"Cascade",Share:"Cascade",Unshare:"Cascade"}:{Assign:"NoCascade",Delete:"RemoveLink",Merge:"NoCascade",Reparent:"NoCascade",Share:"NoCascade",Unshare:"NoCascade"},AssociatedMenuConfiguration:{Behavior:"UseCollectionName",Group:"Details",Label:Y(r.displayName,a),Order:1e4}}}var Et=[{name:"dataverse_create_attribute",description:"Creates a new column (attribute) on an existing Dataverse table. Supports String, Memo, Integer, Decimal, Money, DateTime, Boolean, Picklist, MultiSelectPicklist, AutoNumber, and Image types. IMPORTANT: Column type CANNOT be changed after creation \u2014 choose carefully. Requires System Customizer or System Administrator privileges. WHEN TO USE: Adding a new column to a table. BEST PRACTICES: Use dataverse_get_table_metadata first to check existing columns. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the target table (e.g. 'account', 'new_mytable')"},schemaName:{type:"string",description:"Schema name for the new column (e.g. 'new_mycolumn'). Must include publisher prefix."},attributeType:{type:"string",enum:kt,description:"Column data type. CANNOT be changed after creation. String=single-line text, Memo=multi-line, Integer=whole number, Decimal=decimal number, Money=currency, DateTime=date/time, Boolean=yes/no, Picklist=choice/option set, MultiSelectPicklist=multi-choice, AutoNumber=auto-generated sequence, Image=image file."},displayName:{type:"string",description:"Display label for the column"},description:{type:"string",description:"Optional description of the column"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"Required level. Default: None"},maxLength:{type:"number",description:"Max length for String (default 100, max 4000) or Memo (default 4000, max 1048576) types"},minValue:{type:"number",description:"Minimum value for Integer or Decimal types"},maxValue:{type:"number",description:"Maximum value for Integer or Decimal types"},precision:{type:"number",description:"Decimal precision for Decimal (0-10) or Money types"},dateTimeFormat:{type:"string",enum:["DateOnly","DateAndTime"],description:"Format for DateTime type. CANNOT be changed after creation. Default: DateAndTime"},defaultBooleanValue:{type:"boolean",description:"Default value for Boolean type. Default: false"},picklistOptions:{type:"array",items:{type:"object",properties:{value:{type:"number",description:"Integer value for the option"},label:{type:"string",description:"Display label for the option"}},required:["value","label"]},description:"Options for Picklist and MultiSelectPicklist types. Each option needs a value (integer) and label (text)."},autoNumberFormat:{type:"string",description:"Auto-number format string for AutoNumber type (e.g. 'INV-{SEQNUM:5}'). Only used when attributeType=AutoNumber."},languageCode:{type:"number",description:"Language code for labels. Default: 1033 (English)"},autoPublish:{type:"boolean",description:"Publish after creation (default: false). BATCH RULE: NEVER set true if you are creating/modifying multiple tables, columns, or relationships in the same session \u2014 setting true between operations causes Dataverse publish-lock contention (0x80044181). Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","schemaName","attributeType","displayName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_update_attribute",description:"Updates mutable properties of an existing column (attribute): display name, description, required level, max length (increase only for String/Memo), and search indexing. CANNOT change: column type, logical name, DateTime format, or lookup targets \u2014 these are immutable after creation. Requires System Customizer or System Administrator privileges. WHEN TO USE: Changing a column's label, description, or required level. BEST PRACTICES: Use dataverse_get_table_metadata first to see current values. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the table"},attributeLogicalName:{type:"string",description:"Logical name of the column to update"},displayName:{type:"string",description:"New display label for the column"},description:{type:"string",description:"New description for the column"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"New required level"},maxLength:{type:"number",description:"New max length for String/Memo columns. Can only INCREASE, never decrease."},isSearchable:{type:"boolean",description:"Enable or disable Dataverse Search indexing on this column"},languageCode:{type:"number",description:"Language code for labels. Default: 1033 (English)"},autoPublish:{type:"boolean",description:"Publish after update (default: false). BATCH RULE: NEVER set true if you are updating multiple columns or other schema objects in the same session \u2014 setting true between operations causes Dataverse publish-lock contention (0x80044181). Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional schema modification"}},required:["entityLogicalName","attributeLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0}},{name:"dataverse_delete_attribute",description:"Deletes a column (attribute) from a Dataverse table. WARNING: This permanently deletes the column AND all its data. Automatically checks dependencies (views, workflows, forms) before deletion \u2014 refuses if dependencies exist unless force=true. Managed solution columns cannot be deleted. WHEN TO USE: Removing an obsolete custom column. BEST PRACTICES: Use dataverse_get_table_metadata to verify the column exists; check dependencies first. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the table"},attributeLogicalName:{type:"string",description:"Logical name of the column to delete"},autoPublish:{type:"boolean",description:"Publish after deletion (default: false). BATCH RULE: NEVER set true if you are deleting multiple columns or performing other schema changes in the same session. Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true \u2014 confirms intentional deletion of column and ALL its data"}},required:["entityLogicalName","attributeLogicalName","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!0,idempotentHint:!1,openWorldHint:!0}},{name:"dataverse_create_lookup_attribute",description:"Creates a Lookup column on a Dataverse table by creating a One-to-Many relationship. Use this for typed relationship columns (e.g. 'Account' field on a custom table pointing to the account table). IMPORTANT: Creates both the relationship AND the lookup column in one operation. cascadeConfiguration defaults to 'Referential' (NoCascade on Assign/Reparent/Share/Unshare, RemoveLink on Delete). Use 'Parental' only when the source table has no existing parental relationship with the target. Requires System Customizer or System Administrator privileges.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Source table logical name (referencing)"},schemaName:{type:"string",description:"Schema name for the lookup column (e.g. 'new_accountid')"},displayName:{type:"string",description:"Display label"},description:{type:"string",description:"Optional description"},referencedEntity:{type:"string",description:"Target table logical name (e.g. 'account')"},requiredLevel:{type:"string",enum:["None","ApplicationRequired","Recommended"],description:"Required level. Default: None"},languageCode:{type:"number",description:"Language code. Default: 1033"},cascadeConfiguration:{type:"string",enum:["Referential","Parental"],description:"Cascade behavior. 'Referential' (default): NoCascade on most actions, RemoveLink on Delete \u2014 works on any table pair. 'Parental': full Cascade \u2014 only if no parental relation exists yet with the target."},autoPublish:{type:"boolean",description:"Publish after creation (default: false). BATCH RULE: NEVER set true if you are creating/modifying multiple tables, columns, or relationships in the same session \u2014 causes Dataverse publish-lock contention. Leave false for ALL intermediate operations and call dataverse_publish_customizations ONCE at the end with components.entities=[all affected entity logical names]. Only set true if this is the LAST and ONLY schema change in the session."},confirm:{type:"boolean",description:"Must be true"}},required:["entityLogicalName","schemaName","displayName","referencedEntity","confirm"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function Ca(r,a,t){switch(r){case"dataverse_create_attribute":{let e=Ra.parse(a),n=te({toolName:"dataverse_create_attribute",entitySetName:e.entityLogicalName}).map(l=>`[${l.severity.toUpperCase()}] ${l.code}: ${l.message}`),i=Aa(e),s=await t.createAttribute(e.entityLogicalName,i),o="autoPublish=false (skipped)";return e.autoPublish&&(await t.publishCustomizations({entities:[e.entityLogicalName]}),o="published successfully"),f(`Column '${e.schemaName}' (${e.attributeType}) created on '${e.entityLogicalName}'. ${o}.`,{entityLogicalName:e.entityLogicalName,schemaName:e.schemaName,attributeType:e.attributeType,metadataId:s,published:e.autoPublish,...n.length>0&&{warnings:n}},["Use dataverse_get_table_metadata to verify the new column","Column type CANNOT be changed after creation \u2014 to change type, create a new column and migrate data"])}case"dataverse_update_attribute":{let e=Sa.parse(a),n=te({toolName:"dataverse_update_attribute",entitySetName:e.entityLogicalName}).map(d=>`[${d.severity.toUpperCase()}] ${d.code}: ${d.message}`),i=e.languageCode,s={},o={};e.displayName!==void 0&&(s.DisplayName=Y(e.displayName,i),o.DisplayName=e.displayName),e.description!==void 0&&(s.Description=Y(e.description,i),o.Description=e.description),e.requiredLevel!==void 0&&(s.RequiredLevel={Value:e.requiredLevel},o.RequiredLevel=e.requiredLevel),e.maxLength!==void 0&&(s.MaxLength=e.maxLength,o.MaxLength=e.maxLength),e.isSearchable!==void 0&&(s.IsSearchable=e.isSearchable,o.IsSearchable=e.isSearchable),await t.updateAttribute(e.entityLogicalName,e.attributeLogicalName,s);let l=Object.entries(o).map(([d,u])=>`${d}=${String(u)}`).join(", "),c="autoPublish=false (skipped)";return e.autoPublish&&(await t.publishCustomizations({entities:[e.entityLogicalName]}),c="published successfully"),f(`Column '${e.attributeLogicalName}' on '${e.entityLogicalName}' updated: ${l}. ${c}.`,{entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,changes:o,published:e.autoPublish,...n.length>0&&{warnings:n}},["Use dataverse_get_table_metadata to verify the changes","Column type, logical name, and DateTime format cannot be changed \u2014 these are immutable after creation"])}case"dataverse_delete_attribute":{let e=Na.parse(a),n=te({toolName:"dataverse_delete_attribute",entitySetName:e.entityLogicalName}).map(s=>`[${s.severity.toUpperCase()}] ${s.code}: ${s.message}`);try{await t.deleteAttribute(e.entityLogicalName,e.attributeLogicalName)}catch(s){let o=s instanceof Error?s.message:String(s);if(o.includes("0x80048405"))return j({summary:`Cannot delete column '${e.attributeLogicalName}': it belongs to a managed solution and cannot be deleted.`,data:{error:"0x80048405",entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName},errorCategory:"SCHEMA_MISMATCH",suggestions:["Managed solution columns can only be removed by uninstalling the solution"]});if(o.includes("dependency")||o.includes("dependencies")||o.includes("DependencyList"))return j({summary:`Cannot delete column '${e.attributeLogicalName}': it has dependencies (views, workflows, forms, or other components reference it).`,data:{error:"HAS_DEPENDENCIES",entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,rawError:o},errorCategory:"SCHEMA_MISMATCH",suggestions:["Use dataverse_list_dependencies to identify what references this column","Remove all references before retrying deletion"]});let l=o.match(/0x[0-9a-fA-F]{8}/),c=l?l[0]:"UNKNOWN";return j({summary:`Cannot delete column '${e.attributeLogicalName}': deletion rejected by Dataverse (${c}).`,data:{error:c,entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,rawError:o},errorCategory:"SCHEMA_MISMATCH",suggestions:["Verify this is a custom (non-managed, non-system) column","Check that no solution dependencies or forms reference this column","Standard system attributes (e.g. accountnumber) cannot be deleted"]})}let i="autoPublish=false (skipped)";return e.autoPublish&&(await t.publishCustomizations({entities:[e.entityLogicalName]}),i="published successfully"),f(`Column '${e.attributeLogicalName}' deleted from '${e.entityLogicalName}'. ${i}. All data in this column has been permanently removed.`,{entityLogicalName:e.entityLogicalName,attributeLogicalName:e.attributeLogicalName,deleted:!0,published:e.autoPublish,...n.length>0&&{warnings:n}},["This action is irreversible \u2014 the column and its data cannot be recovered","The column logical name is tombstoned and cannot be reused for ~90 days"])}case"dataverse_create_lookup_attribute":{let e=ka.parse(a),n=te({toolName:"dataverse_create_lookup_attribute",entitySetName:e.entityLogicalName}).map(c=>`[${c.severity.toUpperCase()}] ${c.code}: ${c.message}`),i=Ea(e),s,o=!1;try{s=await t.createRelationship(i)}catch(c){if((c instanceof Error?c.message:String(c)).includes("Request timed out")){let u=!1;try{u=((await t.getTableMetadata(e.entityLogicalName,!0)).Attributes??[]).some(y=>y.LogicalName===e.schemaName.toLowerCase())}catch{}if(u)o=!0,s="";else throw c}else throw c}let l="autoPublish=false (skipped)";return e.autoPublish&&!o?(await t.publishCustomizations({entities:[e.entityLogicalName,e.referencedEntity]}),l="published successfully"):o&&(l="publish skipped (request timed out \u2014 use dataverse_publish_customizations to publish)"),o&&n.push("REQUEST TIMED OUT but lookup was found on the table \u2014 creation succeeded. MetadataId unavailable."),f(`Lookup column '${e.schemaName}' created on '${e.entityLogicalName}' \u2192 '${e.referencedEntity}'. ${l}.`,{entityLogicalName:e.entityLogicalName,schemaName:e.schemaName,referencedEntity:e.referencedEntity,...s&&{metadataId:s},published:e.autoPublish&&!o,...n.length>0&&{warnings:n}},["Use dataverse_get_table_metadata to verify the new lookup column","Lookup columns cannot be created via the standard attribute API \u2014 this uses the RelationshipDefinitions endpoint"])}default:throw new Error(`Unknown attribute tool: ${r}`)}}import{z as $}from"zod";function ve(r){return r.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function Ks(r){let a=0,t=0,e=0;return`<SiteMap>
281
+ ${r.map(i=>{let s=i.id??`Area_${++a}`,o=i.groups.map(l=>{let c=l.id??`Group_${++t}`,d=l.subareas.map(u=>{let m=u.id??`Sub_${++e}`,p=[`Id="${ve(m)}"`];return u.entityLogicalName&&p.push(`Entity="${ve(u.entityLogicalName)}"`),u.url&&p.push(`Url="${ve(u.url)}"`),u.title&&p.push(`Title="${ve(u.title)}"`),` <SubArea ${p.join(" ")}/>`});return` <Group Id="${ve(c)}" Title="${ve(l.title)}">
150
282
  `+d.join(`
151
283
  `)+`
152
- </Group>`});return` <Area Id="${le(o)}" Title="${le(r.title)}" ShowGroups="true">
153
- `+s.join(`
284
+ </Group>`});return` <Area Id="${ve(s)}" Title="${ve(i.title)}" ShowGroups="true">
285
+ `+o.join(`
154
286
  `)+`
155
287
  </Area>`}).join(`
156
288
  `)}
157
- </SiteMap>`}var ni=O.object({id:O.string().optional(),entityLogicalName:O.string().optional(),url:O.string().optional(),title:O.string().optional()}),ri=O.object({id:O.string().optional(),title:O.string(),subareas:O.array(ni).min(1)}),ii=O.object({id:O.string().optional(),title:O.string(),groups:O.array(ri).min(1)}),oi=O.object({sitemapName:O.string().min(1),appModuleUniqueName:O.string().optional(),areas:O.array(ii).min(1)}),xa=[{name:"dataverse_create_sitemap",description:"Creates a model-driven app sitemap (navigation structure) by generating valid sitemap XML and posting it to the Dataverse sitemaps entity set (POST /api/data/v9.2/sitemaps with field sitemapxml). Optionally links the sitemap to an existing model-driven app module by patching the appmodule's sitemapid_sitemap navigation property. WHEN TO USE: Scaffolding a model-driven app that needs a navigation structure (areas, groups, subareas). BEST PRACTICES: Keep area/group/subarea IDs short and URL-safe (e.g. 'nav_accounts', 'grp_sales'). Specify entityLogicalName OR url per SubArea, not both. Call dataverse_publish_customizations after creation to publish the sitemap. To find appModuleUniqueName, query appmodules with $select=uniquename,name. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{sitemapName:{type:"string",description:"Display name of the sitemap (stored in sitemapname field)"},appModuleUniqueName:{type:"string",description:"Unique name of the model-driven app to link this sitemap to (optional). Query appmodules entity set for uniquename values."},areas:{type:"array",description:"One or more navigation areas (min 1)",items:{type:"object",properties:{id:{type:"string",description:"Area element ID (auto-generated if omitted, e.g. 'nav_area1')"},title:{type:"string",description:"Area display title"},groups:{type:"array",description:"Groups inside this area (min 1)",items:{type:"object",properties:{id:{type:"string",description:"Group element ID (auto-generated if omitted)"},title:{type:"string",description:"Group display title"},subareas:{type:"array",description:"SubAreas (navigation items) inside this group (min 1)",items:{type:"object",properties:{id:{type:"string",description:"SubArea element ID (auto-generated if omitted)"},entityLogicalName:{type:"string",description:"Entity logical name to navigate to (e.g. 'account')"},url:{type:"string",description:"Custom URL target (alternative to entityLogicalName)"},title:{type:"string",description:"SubArea display title"}},required:[]}}},required:["title","subareas"]}}},required:["title","groups"]}}},required:["sitemapName","areas"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function Da(a,i,n){switch(a){case"dataverse_create_sitemap":{let{sitemapName:e,appModuleUniqueName:t,areas:r}=oi.parse(i),o=ai(r),s=await n.createRecord("sitemaps",{sitemapname:e,sitemapxml:o}),c=!1,l=null;if(t){let d=await n.query("appmodules",{select:["appmoduleid"],filter:`uniquename eq '${t}'`,top:1});if(d.value.length===0)return p(`Sitemap '${e}' created (id: ${s}) \u2014 app module not found`,{sitemapId:s,sitemapName:e,linked:!1,warning:`App module '${t}' not found. Sitemap was created but not linked.`,sitemapxml:o},["Run dataverse_publish_customizations to publish the new sitemap","Query appmodules ($select=uniquename,name) to verify the app module unique name"]);l=d.value[0].appmoduleid,await n.updateRecord("appmodules",l,{"sitemapid_sitemap@odata.bind":`/sitemaps(${s})`}),c=!0}return p(`Sitemap '${e}' created successfully`,{sitemapId:s,sitemapName:e,linked:c,appModuleUniqueName:t??null,appModuleId:l,areaCount:r.length,sitemapxml:o},["Run dataverse_publish_customizations to publish the sitemap",c?`Sitemap linked to app '${t}' (appmoduleid: ${l})`:"Provide appModuleUniqueName to link this sitemap to a model-driven app"])}default:throw new Error(`Unknown sitemap tool: ${a}`)}}import{z as k}from"zod";var Ve=[{name:"dataverse_create_table",description:"Create a new custom table (entity) in Dataverse via POST /EntityDefinitions. Provisions the table with a primary name column. Schema changes persist immediately \u2014 publish is NOT required between individual operations. BATCH RULE: if you plan to create more tables, add columns, or create relationships, do NOT publish now \u2014 accumulate all entity names and call dataverse_publish_customizations ONCE at the very end with components.entities=[list of all modified entities]. Use dataverse_create_attribute to add more columns afterwards.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"Schema name (e.g., 'new_order'). Must include a publisher prefix."},displayName:{type:"string",description:"Singular display name (e.g., 'Order')."},displayCollectionName:{type:"string",description:"Plural display name (e.g., 'Orders')."},description:{type:"string",description:"Optional table description."},ownershipType:{type:"string",enum:["UserOwned","OrganizationOwned"],description:"Ownership model. Default: UserOwned."},hasNotes:{type:"boolean",description:"Allow notes/annotations. Default: false."},hasActivities:{type:"boolean",description:"Allow activities (email, task). Default: false."},isAuditEnabled:{type:"boolean",description:"Enable audit logging. Default: false."},primaryNameColumnSchemaName:{type:"string",description:"Schema name for the primary name column. Defaults to '{schemaName}_name'."},primaryNameColumnDisplayName:{type:"string",description:"Display name for the primary name column. Default: 'Name'."},iconVectorName:{type:"string",description:"Optional SVG web resource name for the table icon (e.g., 'new_myicon.svg'). PREREQUISITE: call dataverse_create_web_resource first to upload the SVG \u2014 the name you pick there becomes this value."}},required:["schemaName","displayName","displayCollectionName"]}},{name:"dataverse_create_relationship",description:"Create a One-to-Many or Many-to-Many relationship between two Dataverse tables via POST /RelationshipDefinitions. OneToMany: provide referencedEntity (the 'one' side) and referencingEntity (the 'many' side). ManyToMany: provide entity1LogicalName and entity2LogicalName. Schema changes persist immediately \u2014 publish is NOT required between individual operations. BATCH RULE: if more schema changes are planned, do NOT publish now \u2014 call dataverse_publish_customizations ONCE at the very end with all affected entities.",inputSchema:{type:"object",properties:{type:{type:"string",enum:["OneToMany","ManyToMany"],description:"Relationship type."},schemaName:{type:"string",description:"Schema name (e.g., 'new_account_order')."},referencedEntity:{type:"string",description:"(OneToMany) Logical name of the 'one' side entity (e.g., 'account')."},referencingEntity:{type:"string",description:"(OneToMany) Logical name of the 'many' side entity."},referencingAttributeSchemaName:{type:"string",description:"(OneToMany) Schema name of the lookup column on the 'many' entity."},referencingAttributeDisplayName:{type:"string",description:"(OneToMany) Display name for the lookup column."},entity1LogicalName:{type:"string",description:"(ManyToMany) First entity logical name."},entity2LogicalName:{type:"string",description:"(ManyToMany) Second entity logical name."},intersectEntitySchemaName:{type:"string",description:"(ManyToMany) Schema name for the intersect table."},cascadeDelete:{type:"string",enum:["Cascade","RemoveLink","Restrict","NoCascade"],description:"(OneToMany) Delete cascade behavior on referencing side. Default: RemoveLink."}},required:["type","schemaName"]}}];var si=k.object({schemaName:k.string().min(1).describe("Schema name of the new table (e.g., 'new_order'). Must include a publisher prefix."),displayName:k.string().min(1).describe("Singular display name (e.g., 'Order')."),displayCollectionName:k.string().min(1).describe("Plural display name (e.g., 'Orders')."),description:k.string().optional().describe("Optional description of the table's purpose."),ownershipType:k.enum(["UserOwned","OrganizationOwned"]).optional().default("UserOwned").describe("Ownership model. 'UserOwned' supports record-level sharing."),hasNotes:k.boolean().optional().default(!1).describe("Allow notes/annotations on this table."),hasActivities:k.boolean().optional().default(!1).describe("Allow activities (email, task, appointment) on this table."),isAuditEnabled:k.boolean().optional().default(!1).describe("Enable audit logging for this table."),primaryNameColumnSchemaName:k.string().optional().describe("Schema name for the primary name column. Defaults to '{schemaName}_name'."),primaryNameColumnDisplayName:k.string().optional().default("Name").describe("Display name for the primary name column. Default: 'Name'."),iconVectorName:k.string().optional().describe("Optional SVG web resource name for the table icon (e.g., 'new_myicon.svg'). PREREQUISITE: call dataverse_create_web_resource first.")}),li=k.object({type:k.enum(["OneToMany","ManyToMany"]).describe("Relationship type."),schemaName:k.string().min(1).describe("Schema name for the relationship (e.g., 'new_account_order')."),referencedEntity:k.string().optional().describe("(OneToMany) Logical name of the 'one' side entity (e.g., 'account')."),referencingEntity:k.string().optional().describe("(OneToMany) Logical name of the 'many' side entity (e.g., 'new_order')."),referencingAttributeSchemaName:k.string().optional().describe("(OneToMany) Schema name of the lookup column on the referencing entity."),referencingAttributeDisplayName:k.string().optional().describe("(OneToMany) Display name for the lookup column."),entity1LogicalName:k.string().optional().describe("(ManyToMany) First entity logical name."),entity2LogicalName:k.string().optional().describe("(ManyToMany) Second entity logical name."),intersectEntitySchemaName:k.string().optional().describe("(ManyToMany) Schema name for the auto-generated intersect table."),cascadeDelete:k.enum(["Cascade","RemoveLink","Restrict","NoCascade"]).optional().default("RemoveLink").describe("(OneToMany) Delete cascade behavior on the referencing side. Default: RemoveLink.")});async function Ia(a,i,n){switch(a){case"dataverse_create_table":return ci(i,n);case"dataverse_create_relationship":return di(i,n);default:throw new Error(`Unknown schema tool: ${a}`)}}async function ci(a,i){let n=si.parse(a),e=n.primaryNameColumnSchemaName??`${n.schemaName.toLowerCase()}_name`,t={"@odata.type":"Microsoft.Dynamics.CRM.EntityMetadata",SchemaName:n.schemaName,DisplayName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:n.displayName,LanguageCode:1033}]},DisplayCollectionName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:n.displayCollectionName,LanguageCode:1033}]},OwnershipType:n.ownershipType,HasNotes:n.hasNotes,HasActivities:n.hasActivities,IsAuditEnabled:{Value:n.isAuditEnabled,CanBeChanged:!0,ManagedPropertyLogicalName:"canauditentity"},PrimaryNameAttribute:e.toLowerCase(),Attributes:[{"@odata.type":"Microsoft.Dynamics.CRM.StringAttributeMetadata",SchemaName:e,IsPrimaryName:!0,RequiredLevel:{Value:"None",CanBeChanged:!0,ManagedPropertyLogicalName:"canmodifyrequirementlevelsettings"},DisplayName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:n.primaryNameColumnDisplayName??"Name",LanguageCode:1033}]},MaxLength:100}]};n.description&&(t.Description={"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:n.description,LanguageCode:1033}]}),n.iconVectorName&&(t.IconVectorName=n.iconVectorName);let r,o=!1;try{r=await i.createEntityDefinition(t)}catch(c){let l=c instanceof Error?c.message:String(c);if(l.includes("Request timed out")||l.includes("TRANSIENT_LOCK")){let d=!1;try{let u=await i.getTableMetadata(n.schemaName.toLowerCase(),!1);u?.MetadataId&&(r=u.MetadataId,d=!0,o=!0)}catch{}if(!d)throw c}else throw c}let s=o?" (NOTE: the request timed out but the table was confirmed created server-side \u2014 [TRANSIENT_TIMEOUT_RECOVERED])":"";return p(`Table '${n.displayName}' (${n.schemaName}) created. MetadataId: ${r}. Table is immediately queryable via the API. BATCH: if more schema changes are planned (columns, relationships, other tables), do NOT publish yet \u2014 call dataverse_publish_customizations ONCE at the very end with all affected entity names.${s}`,{metadataId:r,schemaName:n.schemaName,displayName:n.displayName,displayCollectionName:n.displayCollectionName,ownershipType:n.ownershipType,hasNotes:n.hasNotes,hasActivities:n.hasActivities,isAuditEnabled:n.isAuditEnabled},["BATCH: do NOT publish now if more schema changes are planned. Add columns/relationships first, then call dataverse_publish_customizations ONCE with components.entities=[all affected entity logical names]",`Use dataverse_create_attribute to add columns to '${n.schemaName.toLowerCase()}'`,`Use dataverse_create_relationship to link '${n.schemaName.toLowerCase()}' to other tables`,`When ALL schema work is done: dataverse_publish_customizations({ components: { entities: ['${n.schemaName.toLowerCase()}', ...otherEntities] } })`])}async function di(a,i){let n=li.parse(a),e,t;if(n.type==="OneToMany"){if(!n.referencedEntity||!n.referencingEntity)throw new Error("OneToMany relationship requires 'referencedEntity' (the 'one' side) and 'referencingEntity' (the 'many' side).");let s=`${n.referencedEntity}id`,c=(()=>{if(n.referencingAttributeSchemaName)return null;let u=n.referencingEntity.split("_");return u.length>=2?u[0]:null})(),l=n.referencingAttributeSchemaName??(c?`${c}_${n.referencedEntity}id`:`${n.referencedEntity}id`),d=n.referencingAttributeDisplayName??n.referencedEntity.charAt(0).toUpperCase()+n.referencedEntity.slice(1);e={"@odata.type":"Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",SchemaName:n.schemaName,ReferencedEntity:n.referencedEntity,ReferencedAttribute:s,ReferencingEntity:n.referencingEntity,CascadeConfiguration:{"@odata.type":"Microsoft.Dynamics.CRM.CascadeConfiguration",Assign:"NoCascade",Delete:n.cascadeDelete,Merge:"NoCascade",Reparent:"NoCascade",Share:"NoCascade",Unshare:"NoCascade",RollupView:"NoCascade"},Lookup:{"@odata.type":"Microsoft.Dynamics.CRM.LookupAttributeMetadata",SchemaName:l,DisplayName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:d,LanguageCode:1033}]},RequiredLevel:{Value:"None",CanBeChanged:!0,ManagedPropertyLogicalName:"canmodifyrequirementlevelsettings"}}},t=`One-to-Many relationship '${n.schemaName}' created: ${n.referencedEntity} \u2192 ${n.referencingEntity}.`}else{if(!n.entity1LogicalName||!n.entity2LogicalName)throw new Error("ManyToMany relationship requires 'entity1LogicalName' and 'entity2LogicalName'.");let s=n.intersectEntitySchemaName??`${n.entity1LogicalName}_${n.entity2LogicalName}`;e={"@odata.type":"Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata",SchemaName:n.schemaName,Entity1LogicalName:n.entity1LogicalName,Entity2LogicalName:n.entity2LogicalName,IntersectEntityName:s,Entity1AssociatedMenuConfiguration:{"@odata.type":"Microsoft.Dynamics.CRM.AssociatedMenuConfiguration",Behavior:"UseCollectionName",Group:"Details",Order:null,IsCustomizable:!0},Entity2AssociatedMenuConfiguration:{"@odata.type":"Microsoft.Dynamics.CRM.AssociatedMenuConfiguration",Behavior:"UseCollectionName",Group:"Details",Order:null,IsCustomizable:!0}},t=`Many-to-Many relationship '${n.schemaName}' created: ${n.entity1LogicalName} \u2194 ${n.entity2LogicalName}.`}let r=await i.createRelationship(e),o={metadataId:r,schemaName:n.schemaName,type:n.type};return n.type==="OneToMany"&&(o.cascadeDelete=n.cascadeDelete),p(`${t} MetadataId: ${r}.`,o,["BATCH: do NOT publish now if more schema changes are planned \u2014 call dataverse_publish_customizations ONCE at the very end with components.entities=[all affected entity logical names]"])}import{z as N}from"zod";var ze=[{name:"dataverse_check_record_access",description:"Check which access rights a specific user or team has on a Dataverse record. Calls the RetrievePrincipalAccess function. Returns a list of rights: ReadAccess, WriteAccess, AppendAccess, AppendToAccess, AssignAccess, ShareAccess, DeleteAccess. Use dataverse_whoami to find the current user ID.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts')."},recordId:{type:"string",description:"GUID of the record."},principalId:{type:"string",description:"GUID of the user or team."},principalType:{type:"string",enum:["systemuser","team"],description:"Principal type. Default: systemuser."}},required:["entitySetName","recordId","principalId"]}},{name:"dataverse_grant_access",description:"Grant record-level sharing access to a user or team using the GrantAccess Dataverse action. The caller must have ShareAccess on the record. Use dataverse_check_record_access to verify rights were granted.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts')."},recordId:{type:"string",description:"GUID of the record to share."},principalId:{type:"string",description:"GUID of the user or team receiving access."},principalType:{type:"string",enum:["systemuser","team"],description:"Principal type. Default: systemuser."},accessRights:{type:"array",items:{type:"string",enum:["ReadAccess","WriteAccess","AppendAccess","AppendToAccess","AssignAccess","ShareAccess","DeleteAccess"]},description:"Rights to grant."}},required:["entitySetName","recordId","principalId","accessRights"]}},{name:"dataverse_revoke_access",description:"Revoke record-level sharing access from a user or team using the RevokeAccess Dataverse action. Removes all shared access previously granted by GrantAccess. The caller must have ShareAccess on the record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts')."},recordId:{type:"string",description:"GUID of the record."},principalId:{type:"string",description:"GUID of the user or team."},principalType:{type:"string",enum:["systemuser","team"],description:"Principal type. Default: systemuser."}},required:["entitySetName","recordId","principalId"]}},{name:"dataverse_merge_records",description:"Merge two Dataverse records of the same entity type. The subordinate record is deactivated and its data merged into the target. Supports account, contact, and lead entities. This is IRREVERSIBLE \u2014 the subordinate record cannot be restored. Requires confirm: true.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name (e.g., 'account', 'contact', 'lead')."},entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts', 'contacts', 'leads')."},targetId:{type:"string",description:"GUID of the master record (kept after merge)."},subordinateId:{type:"string",description:"GUID of the subordinate record (deactivated after merge)."},updateContent:{type:"object",description:"Optional field values to update on the target from the subordinate."},performParentingChecks:{type:"boolean",description:"Check for parenting conflicts before merge. Default: false."},confirm:{type:"boolean",enum:[!0],description:"Must be true to execute the merge."}},required:["entityLogicalName","entitySetName","targetId","subordinateId","confirm"]}}];var ui=["ReadAccess","WriteAccess","AppendAccess","AppendToAccess","AssignAccess","ShareAccess","DeleteAccess"],Ke=["systemuser","team"],pi=N.object({entitySetName:N.string().min(1).describe("OData entity set name (plural, e.g., 'accounts')."),recordId:N.string().uuid().describe("GUID of the record to check access on."),principalId:N.string().uuid().describe("GUID of the user or team to check access for."),principalType:N.enum(Ke).optional().default("systemuser").describe("Type of principal. Default: systemuser.")}),mi=N.object({entitySetName:N.string().min(1).describe("OData entity set name for the target record (e.g., 'accounts')."),recordId:N.string().uuid().describe("GUID of the record to share."),principalId:N.string().uuid().describe("GUID of the user or team receiving access."),principalType:N.enum(Ke).optional().default("systemuser").describe("Type of principal. Default: systemuser."),accessRights:N.array(N.enum(ui)).min(1).describe("Rights to grant. Options: ReadAccess, WriteAccess, AppendAccess, AppendToAccess, AssignAccess, ShareAccess, DeleteAccess.")}),gi=N.object({entitySetName:N.string().min(1).describe("OData entity set name for the target record (e.g., 'accounts')."),recordId:N.string().uuid().describe("GUID of the record to revoke access from."),principalId:N.string().uuid().describe("GUID of the user or team losing access."),principalType:N.enum(Ke).optional().default("systemuser").describe("Type of principal. Default: systemuser.")}),fi=N.object({entityLogicalName:N.string().min(1).describe("Logical name of the entity (e.g., 'account', 'contact', 'lead')."),entitySetName:N.string().min(1).describe("OData entity set name (e.g., 'accounts', 'contacts')."),targetId:N.string().uuid().describe("GUID of the master record (kept after merge)."),subordinateId:N.string().uuid().describe("GUID of the subordinate record (deactivated after merge)."),updateContent:N.record(N.unknown()).optional().describe("Fields from the subordinate to copy into the target record."),performParentingChecks:N.boolean().optional().default(!1).describe("Check for parenting conflicts before merge. Default: false."),confirm:N.literal(!0).describe("Must be true to confirm the merge. The subordinate record is deactivated permanently.")});async function Oa(a,i,n){switch(a){case"dataverse_check_record_access":return yi(i,n);case"dataverse_grant_access":return hi(i,n);case"dataverse_revoke_access":return vi(i,n);case"dataverse_merge_records":return bi(i,n);default:throw new Error(`Unknown record-access tool: ${a}`)}}function Xe(a){return a==="systemuser"?"systemusers":"teams"}function La(a){return a==="systemuser"?"systemuserid":"teamid"}function Ua(a){return a==="systemuser"?"Microsoft.Dynamics.CRM.systemuser":"Microsoft.Dynamics.CRM.team"}async function yi(a,i){let n=pi.parse(a),e=Xe(n.principalType),r=(await i.executeBoundFunction(e,n.principalId,"RetrievePrincipalAccess",{Target:{"@odata.id":`${n.entitySetName}(${n.recordId})`}}))?.AccessRights??"None",o=r==="None"?[]:r.split(", ").filter(Boolean);return p(`${n.principalType} ${n.principalId} has [${o.join(", ")||"no access"}] on ${n.entitySetName}(${n.recordId}).`,{accessRights:r,rights:o,principalId:n.principalId,principalType:n.principalType,entitySetName:n.entitySetName,recordId:n.recordId},o.length===0?["Use dataverse_grant_access to grant access to this principal"]:["Use dataverse_revoke_access to remove all shared access from this principal"])}async function hi(a,i){let n=mi.parse(a),e=Xe(n.principalType),t=La(n.principalType),r=Ua(n.principalType);return await i.executeAction("GrantAccess",{Target:{"@odata.id":`${n.entitySetName}(${n.recordId})`},PrincipalAccess:{Principal:{"@odata.type":r,[t]:n.principalId,"@odata.id":`${e}(${n.principalId})`},AccessMask:n.accessRights.join(", ")}}),p(`Access granted: ${n.principalType} ${n.principalId} has [${n.accessRights.join(", ")}] on ${n.entitySetName}(${n.recordId}).`,{entitySetName:n.entitySetName,recordId:n.recordId,principalId:n.principalId,principalType:n.principalType,accessRights:n.accessRights},["Use dataverse_check_record_access to verify the granted rights","Use dataverse_revoke_access to remove access if needed"])}async function vi(a,i){let n=gi.parse(a),e=Xe(n.principalType),t=La(n.principalType),r=Ua(n.principalType);return await i.executeAction("RevokeAccess",{Target:{"@odata.id":`${n.entitySetName}(${n.recordId})`},Revokee:{"@odata.type":r,[t]:n.principalId,"@odata.id":`${e}(${n.principalId})`}}),p(`Access revoked: ${n.principalType} ${n.principalId} no longer has shared access to ${n.entitySetName}(${n.recordId}).`,{entitySetName:n.entitySetName,recordId:n.recordId,principalId:n.principalId,principalType:n.principalType},["Use dataverse_check_record_access to verify access has been removed"])}var Ca=new Set(["account","contact","lead","incident","opportunity"]);async function bi(a,i){let n=fi.parse(a);if(!Ca.has(n.entityLogicalName))throw new Error(`dataverse_merge_records only supports: ${[...Ca].join(", ")}. '${n.entityLogicalName}' is not supported by the Dataverse Merge API. For custom entities, manually copy fields with dataverse_update then deactivate the subordinate record.`);let e=`Microsoft.Dynamics.CRM.${n.entityLogicalName}`,t=`${n.entityLogicalName}id`;return await i.executeAction("Merge",{Target:{"@odata.type":e,[t]:n.targetId,"@odata.id":`${n.entitySetName}(${n.targetId})`},Subordinate:{"@odata.type":e,[t]:n.subordinateId,"@odata.id":`${n.entitySetName}(${n.subordinateId})`},UpdateContent:{"@odata.type":`#Microsoft.Dynamics.CRM.${n.entityLogicalName}`,...n.updateContent??{}},PerformParentingChecks:n.performParentingChecks??!1}),p(`Merge complete: ${n.entityLogicalName}(${n.subordinateId}) has been deactivated and merged into ${n.entityLogicalName}(${n.targetId}).`,{entityLogicalName:n.entityLogicalName,targetId:n.targetId,subordinateId:n.subordinateId},[`Use dataverse_get to verify the merged record: entitySetName='${n.entitySetName}', recordId='${n.targetId}'`,"The subordinate record is now deactivated and cannot be restored"])}import{z as he}from"zod";var Q="ui://dataverse/table-browser.html",Pa=[{name:"dataverse_open_table_browser",description:"Opens an interactive MCP App to browse Dataverse table rows with sorting and paging.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:"Optional initial entity set name to open (e.g., accounts)"},top:{type:"number",description:"Initial row count to load (default 50, max 200)"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:Q,visibility:["model","app"]},"ui/resourceUri":Q}},{name:"dataverse_table_browser_list_tables",description:"App-only helper: lists available Dataverse tables for the table browser UI.",inputSchema:{type:"object",properties:{}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:Q,visibility:["app"]},"ui/resourceUri":Q}},{name:"dataverse_table_browser_get_rows",description:"App-only helper: retrieves rows from one table for UI rendering with compact output.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'Entity set name (e.g., "accounts")'},top:{type:"number",description:"Maximum number of rows to fetch (default 50, max 200)"}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:Q,visibility:["app"]},"ui/resourceUri":Q}}],_i=he.object({tableName:he.string().optional(),top:he.number().int().min(5).max(200).optional().default(50)}),wi=he.object({entitySetName:he.string().min(1),top:he.number().int().min(5).max(200).optional().default(50)}),Ni=[{EntitySetName:"accounts",LogicalName:"account",DisplayName:{UserLocalizedLabel:{Label:"Account"}}},{EntitySetName:"contacts",LogicalName:"contact",DisplayName:{UserLocalizedLabel:{Label:"Contact"}}},{EntitySetName:"leads",LogicalName:"lead",DisplayName:{UserLocalizedLabel:{Label:"Lead"}}},{EntitySetName:"opportunities",LogicalName:"opportunity",DisplayName:{UserLocalizedLabel:{Label:"Opportunity"}}}];async function Ma(a,i,n){switch(a){case"dataverse_open_table_browser":{let e=_i.parse(i??{});return{content:[{type:"text",text:JSON.stringify({message:"Table Browser opened",initial:{tableName:e.tableName??null,top:e.top}},null,2)}]}}case"dataverse_table_browser_list_tables":{let e=[],t=[];try{t=await n.listTables(!1)}catch(r){e.push(`listTables(false) failed: ${String(r)}`)}if(!t.length)try{t=await n.listTables(!0),t.length&&e.push("Fell back to custom tables only.")}catch(r){e.push(`listTables(true) failed: ${String(r)}`)}return t.length||(t=Ni,e.push("Using built-in fallback table candidates.")),{content:[{type:"text",text:JSON.stringify({tables:t,warnings:e})}]}}case"dataverse_table_browser_get_rows":{let e=wi.parse(i??{}),r=(await n.query(e.entitySetName,{top:e.top})).value.map(o=>{let s={};for(let[c,l]of Object.entries(o))l!=null&&l!==""&&!c.startsWith("@odata")&&!c.includes("@odata")&&(s[c]=l);return s});return{content:[{type:"text",text:JSON.stringify({entitySetName:e.entitySetName,rows:r},null,2)}]}}default:throw new Error(`Unknown table browser tool: ${a}`)}}import{z as Z}from"zod";var K="ui://dataverse/fetchxml-workbench.html",$a=[{name:"dataverse_open_fetchxml_workbench",description:"Opens an interactive MCP App to run FetchXML queries with sortable results.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"Optional initial entity set name to target (e.g., accounts)"},fetchXml:{type:"string",description:"Optional initial FetchXML query to prefill editor"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["model","app"]},"ui/resourceUri":K}},{name:"dataverse_fetchxml_workbench_list_tables",description:"App-only helper: lists available Dataverse tables for FetchXML workbench hints.",inputSchema:{type:"object",properties:{}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]},"ui/resourceUri":K}},{name:"dataverse_fetchxml_workbench_list_attributes",description:"App-only helper: returns attribute logical names and types for a Dataverse entity to populate the attribute picker.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the entity (e.g., account, contact)"}},required:["entityLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]},"ui/resourceUri":K}},{name:"dataverse_fetchxml_workbench_execute",description:"App-only helper: executes FetchXML and returns compact rows for UI rendering.",inputSchema:{type:"object",properties:{fetchXml:{type:"string",description:"Complete FetchXML query string"},entitySetName:{type:"string",description:"Optional entity set name (e.g., accounts). If omitted, extracted from <entity name=...>."},fetchAll:{type:"boolean",description:"Whether to auto-paginate all pages (default false)"},compact:{type:"boolean",description:"Drop null/empty and @odata fields to reduce payload (default true)"}},required:["fetchXml"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]},"ui/resourceUri":K}}],Si=Z.object({entitySetName:Z.string().optional(),fetchXml:Z.string().optional()}),Ti=Z.object({fetchXml:Z.string().min(1),entitySetName:Z.string().optional(),fetchAll:Z.boolean().optional().default(!1),compact:Z.boolean().optional().default(!0)}),Ri=[{EntitySetName:"accounts",LogicalName:"account",DisplayName:{UserLocalizedLabel:{Label:"Account"}}},{EntitySetName:"contacts",LogicalName:"contact",DisplayName:{UserLocalizedLabel:{Label:"Contact"}}},{EntitySetName:"leads",LogicalName:"lead",DisplayName:{UserLocalizedLabel:{Label:"Lead"}}},{EntitySetName:"opportunities",LogicalName:"opportunity",DisplayName:{UserLocalizedLabel:{Label:"Opportunity"}}}];function Ha(a){let i={};for(let[n,e]of Object.entries(a))e!=null&&e!==""&&!n.startsWith("@odata")&&!n.includes("@odata")&&(i[n]=e);return i}function Ai(a){let n=a.match(/<entity\s+name=["']([^"']+)["']/i)?.[1]?.trim();return n||void 0}async function qa(a,i,n){switch(a){case"dataverse_open_fetchxml_workbench":{let e=Si.parse(i??{});return{content:[{type:"text",text:JSON.stringify({message:"FetchXML Workbench opened",initial:{entitySetName:e.entitySetName??null,fetchXml:e.fetchXml??null}},null,2)}]}}case"dataverse_fetchxml_workbench_list_attributes":{let e=Z.object({entityLogicalName:Z.string()}).parse(i??{}),t=[],r=[];try{t=((await n.getTableMetadata(e.entityLogicalName,!0)).Attributes??[]).map(s=>({logicalName:s.LogicalName,attributeType:s.AttributeType,displayName:s.DisplayName?.UserLocalizedLabel?.Label??s.LogicalName})),t.sort((s,c)=>s.logicalName.localeCompare(c.logicalName))}catch(o){r.push(`getTableMetadata failed: ${String(o)}`)}return{content:[{type:"text",text:JSON.stringify({attributes:t,warnings:r})}]}}case"dataverse_fetchxml_workbench_list_tables":{let e=[],t=[];try{t=await n.listTables(!1)}catch(r){e.push(`listTables(false) failed: ${String(r)}`)}if(!t.length)try{t=await n.listTables(!0),t.length&&e.push("Fell back to custom tables only.")}catch(r){e.push(`listTables(true) failed: ${String(r)}`)}return t.length||(t=Ri,e.push("Using built-in fallback table candidates.")),{content:[{type:"text",text:JSON.stringify({tables:t,warnings:e})}]}}case"dataverse_fetchxml_workbench_execute":{let e=Ti.parse(i??{}),t=e.entitySetName??Ai(e.fetchXml);if(!t)return{content:[{type:"text",text:JSON.stringify({error:"entitySetName is required when not present in FetchXML <entity name=...> element"})}],isError:!0};if(e.fetchAll){let c=await n.executeFetchXmlAllPages(t,e.fetchXml),l=e.compact?c.map(Ha):c;return{content:[{type:"text",text:JSON.stringify({entitySetName:t,rows:l,fetchAll:!0})}]}}let r=await n.executeFetchXml(t,e.fetchXml),o=Array.isArray(r.value)?r.value:[],s=e.compact?o.map(Ha):o;return{content:[{type:"text",text:JSON.stringify({entitySetName:t,rows:s,fetchAll:!1})}]}}default:throw new Error(`Unknown FetchXML workbench tool: ${a}`)}}import{z as ae}from"zod";var ce="ui://dataverse/plugin-trace-viewer.html",ja=[{name:"dataverse_open_plugin_trace_viewer",description:"Opens an interactive MCP App to browse, filter, and inspect Dataverse plugin trace logs. Shows plugin name, entity, message, execution duration, and exception details. Requires Plugin Trace Log to be enabled in Dataverse settings (Settings > Administration > System Settings > Customization tab).",inputSchema:{type:"object",properties:{exceptionsOnly:{type:"boolean",description:"Pre-filter to show only logs with exceptions (default false)"},entityFilter:{type:"string",description:"Pre-filter by entity logical name (e.g. 'account')"},pluginFilter:{type:"string",description:"Pre-filter by plugin type name substring (e.g. 'AccountValidation')"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:ce,visibility:["model","app"]},"ui/resourceUri":ce}},{name:"dataverse_plugin_trace_viewer_get_logs",description:"App-only helper: retrieves plugin trace logs for the Plugin Trace Viewer UI.",inputSchema:{type:"object",properties:{tail:{type:"number",description:"Max recent logs to return (default 100, max 500)"},exceptionsOnly:{type:"boolean",description:"Return only logs with exceptions (default false)"},entityFilter:{type:"string",description:"Server-side filter by entity logical name"},pluginFilter:{type:"string",description:"Server-side filter by plugin type name substring"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:ce,visibility:["app"]},"ui/resourceUri":ce}}],ki=["plugintracelogid","typename","messagename","primaryentity","exceptiondetails","messageblock","createdon","performanceexecutionduration"],Ei=ae.object({exceptionsOnly:ae.boolean().optional().default(!1),entityFilter:ae.string().optional(),pluginFilter:ae.string().optional()}),xi=ae.object({tail:ae.number().int().positive().max(500).optional().default(100),exceptionsOnly:ae.boolean().optional().default(!1),entityFilter:ae.string().optional(),pluginFilter:ae.string().optional()});function Te(a){return typeof a=="string"?a:""}async function Wa(a,i,n){switch(a){case"dataverse_open_plugin_trace_viewer":{let e=Ei.parse(i??{});return{content:[{type:"text",text:JSON.stringify({message:"Plugin Trace Viewer opened",initial:e},null,2)}]}}case"dataverse_plugin_trace_viewer_get_logs":{let e=xi.parse(i??{}),t=[];e.exceptionsOnly&&t.push("exceptiondetails ne null"),e.entityFilter&&t.push(`primaryentity eq '${w(e.entityFilter)}'`),e.pluginFilter&&t.push(`contains(typename,'${w(e.pluginFilter)}')`);let r=t.length>0?t.join(" and "):void 0,c=((await n.query("plugintracelogs",{select:ki,orderby:"createdon desc",top:e.tail,...r!==void 0?{filter:r}:{}})).value??[]).map(l=>({id:Te(l.plugintracelogid),typeName:Te(l.typename),message:Te(l.messagename),entity:Te(l.primaryentity),createdOn:Te(l.createdon),durationMs:typeof l.performanceexecutionduration=="number"?l.performanceexecutionduration:null,hasException:l.exceptiondetails!=null&&l.exceptiondetails!=="",exceptionDetails:typeof l.exceptiondetails=="string"?l.exceptiondetails:null,messageBlock:typeof l.messageblock=="string"?l.messageblock:null}));return{content:[{type:"text",text:JSON.stringify({total:c.length,logs:c})}]}}default:return{content:[{type:"text",text:`Unknown tool: ${a}`}],isError:!0}}}import{z as Fa}from"zod";var ne="ui://dataverse/schema-explorer.html",Ga=[{name:"dataverse_open_schema_explorer",description:"Opens the Dataverse Schema Explorer \u2014 an interactive UI for browsing table definitions, column types, relationships, and alternate keys. Renders an in-context app panel so you can explore any Dataverse entity visually.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},_meta:{ui:{resourceUri:ne,visibility:["model","app"]}}},{name:"dataverse_schema_explorer_list_tables",description:"App-only helper: lists all Dataverse tables (including system tables) for the Schema Explorer UI. Returns logical name, display name, entity set name, custom flag, and ownership type.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:ne,visibility:["app"]}}},{name:"dataverse_schema_explorer_get_entity",description:"App-only helper: returns the full entity card for the Schema Explorer \u2014 columns, relationship counts (1:N / N:1 / N:N), and alternate keys for a given table.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:'Logical name of the table (e.g. "account", "contact")'}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:ne,visibility:["app"]}}}],Di=Fa.object({logicalName:Fa.string().min(1)});function Je(a){if(typeof a=="string")return a;let i=a;return i?.UserLocalizedLabel?.Label??i?.LocalizedLabels?.[0]?.Label??""}async function Ba(a,i,n){switch(a){case"dataverse_open_schema_explorer":return{content:[{type:"text",text:JSON.stringify({resourceUri:ne})}],_meta:{ui:{resourceUri:ne}}};case"dataverse_schema_explorer_list_tables":{let e=await n.listTables(!1),t=(Array.isArray(e)?e:[]).map(r=>({logicalName:r.LogicalName??"",displayName:Je(r.DisplayName)||r.LogicalName,entitySetName:r.EntitySetName??"",isCustomEntity:r.IsCustomEntity??!1,ownershipType:r.OwnershipType??"Unknown"}));return{content:[{type:"text",text:JSON.stringify({tables:t})}]}}case"dataverse_schema_explorer_get_entity":{let{logicalName:e}=Di.parse(i),[t,r,o]=await Promise.all([n.getTableMetadata(e,!0),n.getRelationships(e),n.getEntityKeys(e)]),c=(Array.isArray(t?.Attributes)?t.Attributes:[]).map(h=>({logicalName:h.LogicalName??"",displayName:Je(h.DisplayName)||h.LogicalName,dataType:h.AttributeType??"Unknown",isRequired:h.RequiredLevel?.Value==="ApplicationRequired"||h.RequiredLevel?.Value==="SystemRequired",lookupTarget:Array.isArray(h.Targets)&&h.Targets.length>0?h.Targets[0]:null})),l=e.toLowerCase(),d=Array.isArray(r)?r:[],u=d.filter(h=>h.RelationshipType==="OneToManyRelationship"&&h.ReferencedEntity?.toLowerCase()===l).length,g=d.filter(h=>h.RelationshipType==="OneToManyRelationship"&&h.ReferencingEntity?.toLowerCase()===l).length,m=d.filter(h=>h.RelationshipType==="ManyToManyRelationship").length,y=(Array.isArray(o)?o:[]).map(h=>({name:h.schemaName??h.logicalName??"",keyAttributes:Array.isArray(h.keyAttributes)?h.keyAttributes:[]})),R=t,q={logicalName:R.LogicalName??e,displayName:Je(R.DisplayName)||e,entitySetName:R.EntitySetName??"",ownershipType:R.OwnershipType??"Unknown",isAuditEnabled:R.IsAuditEnabled??!1,isCustomEntity:R.IsCustomEntity??!1,columns:c,relationships:{oneToMany:u,manyToOne:g,manyToMany:m},alternateKeys:y};return{content:[{type:"text",text:JSON.stringify(q)}]}}default:throw new Error(`Unknown schema explorer tool: ${a}`)}}import{z as de}from"zod";var B="ui://dataverse/rbac-inspector.html",Va=[{name:"dataverse_open_rbac_inspector",description:"Opens the Dataverse RBAC Inspector \u2014 an interactive UI for listing security roles, browsing privilege matrices (entity \xD7 operation \xD7 depth), and auditing user role assignments.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},_meta:{ui:{resourceUri:B,visibility:["model","app"]}}},{name:"dataverse_rbac_inspector_list_roles",description:"App-only helper: lists all Dataverse security roles with name and associated business unit.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:B,visibility:["app"]}}},{name:"dataverse_rbac_inspector_get_role_privileges",description:"App-only helper: returns the full privilege matrix (entity \xD7 operation \xD7 depth) for a security role. Uses FetchXML to join privileges with role assignments in a single call.",inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"}},required:["roleId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:B,visibility:["app"]}}},{name:"dataverse_rbac_inspector_list_users",description:"App-only helper: lists active Dataverse system users, optionally filtered by name or email.",inputSchema:{type:"object",properties:{search:{type:"string",description:"Optional name or email search term"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:B,visibility:["app"]}}},{name:"dataverse_rbac_inspector_get_user_roles",description:"App-only helper: returns all security roles assigned to a Dataverse system user.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"}},required:["userId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:B,visibility:["app"]}}},{name:"dataverse_rbac_inspector_list_field_profiles",description:"App-only helper: lists all Dataverse Field Security Profiles (column-level security, independent of entity-level Security Roles).",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:B,visibility:["app"]}}},{name:"dataverse_rbac_inspector_get_profile_permissions",description:"App-only helper: returns all field-level permissions for a Field Security Profile, grouped by entity. Each permission has canRead, canUpdate, canCreate boolean flags.",inputSchema:{type:"object",properties:{profileId:{type:"string",description:"GUID of the field security profile"}},required:["profileId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:B,visibility:["app"]}}}],Ii=de.object({roleId:de.string().min(1)}),Ci=de.object({userId:de.string().min(1)}),Oi=de.object({search:de.string().optional()}),Li=de.object({profileId:de.string().min(1)}),Ui={Create:"create",Read:"read",Write:"write",Delete:"delete",AppendTo:"appendTo",Append:"append",Assign:"assign",Share:"share"};function Pi(a){let i=a.match(/^prv(Create|Read|Write|Delete|AppendTo|Append|Assign|Share)(.+)$/);if(!i)return null;let n=Ui[i[1]];return n?{action:n,entity:i[2].toLowerCase()}:null}function Mi(a){return a&8?"Global":a&4?"Deep":a&2?"Local":a&1?"Basic":"None"}async function za(a,i,n){switch(a){case"dataverse_open_rbac_inspector":return{content:[{type:"text",text:JSON.stringify({resourceUri:B})}],_meta:{ui:{resourceUri:B}}};case"dataverse_rbac_inspector_list_roles":{let t=((await n.query("roles",{select:["roleid","name"],expand:"businessunitid($select=name)",orderby:"name asc",top:200})).value??[]).map(r=>({id:r.roleid??"",name:r.name??"",businessUnit:r.businessunitid?.name??""}));return{content:[{type:"text",text:JSON.stringify({roles:t})}]}}case"dataverse_rbac_inspector_get_role_privileges":{let{roleId:e}=Ii.parse(i),[t,r]=await Promise.all([n.query("roles",{filter:`roleid eq ${e}`,select:["name"],expand:"businessunitid($select=name)",top:1}),n.executeFetchXmlAllPages("privileges",`<fetch count="5000">
289
+ </SiteMap>`}var Xs=$.object({id:$.string().optional(),entityLogicalName:$.string().optional(),url:$.string().optional(),title:$.string().optional()}),Js=$.object({id:$.string().optional(),title:$.string(),subareas:$.array(Xs).min(1)}),Ys=$.object({id:$.string().optional(),title:$.string(),groups:$.array(Js).min(1)}),Qs=$.object({sitemapName:$.string().min(1),appModuleUniqueName:$.string().optional(),areas:$.array(Ys).min(1)}),Ia=[{name:"dataverse_create_sitemap",description:"Creates a model-driven app sitemap (navigation structure) by generating valid sitemap XML and posting it to the Dataverse sitemaps entity set (POST /api/data/v9.2/sitemaps with field sitemapxml). Optionally links the sitemap to an existing model-driven app module by patching the appmodule's sitemapid_sitemap navigation property. WHEN TO USE: Scaffolding a model-driven app that needs a navigation structure (areas, groups, subareas). BEST PRACTICES: Keep area/group/subarea IDs short and URL-safe (e.g. 'nav_accounts', 'grp_sales'). Specify entityLogicalName OR url per SubArea, not both. Call dataverse_publish_customizations after creation to publish the sitemap. To find appModuleUniqueName, query appmodules with $select=uniquename,name. WORKFLOW: manage_solution.",inputSchema:{type:"object",properties:{sitemapName:{type:"string",description:"Display name of the sitemap (stored in sitemapname field)"},appModuleUniqueName:{type:"string",description:"Unique name of the model-driven app to link this sitemap to (optional). Query appmodules entity set for uniquename values."},areas:{type:"array",description:"One or more navigation areas (min 1)",items:{type:"object",properties:{id:{type:"string",description:"Area element ID (auto-generated if omitted, e.g. 'nav_area1')"},title:{type:"string",description:"Area display title"},groups:{type:"array",description:"Groups inside this area (min 1)",items:{type:"object",properties:{id:{type:"string",description:"Group element ID (auto-generated if omitted)"},title:{type:"string",description:"Group display title"},subareas:{type:"array",description:"SubAreas (navigation items) inside this group (min 1)",items:{type:"object",properties:{id:{type:"string",description:"SubArea element ID (auto-generated if omitted)"},entityLogicalName:{type:"string",description:"Entity logical name to navigate to (e.g. 'account')"},url:{type:"string",description:"Custom URL target (alternative to entityLogicalName)"},title:{type:"string",description:"SubArea display title"}},required:[]}}},required:["title","subareas"]}}},required:["title","groups"]}}},required:["sitemapName","areas"]},annotations:{readOnlyHint:!1,destructiveHint:!1,idempotentHint:!1,openWorldHint:!0}}];async function xa(r,a,t){switch(r){case"dataverse_create_sitemap":{let{sitemapName:e,appModuleUniqueName:n,areas:i}=Qs.parse(a),s=Ks(i),o=await t.createRecord("sitemaps",{sitemapname:e,sitemapxml:s}),l=!1,c=null;if(n){let d=await t.query("appmodules",{select:["appmoduleid"],filter:`uniquename eq '${n}'`,top:1});if(d.value.length===0)return f(`Sitemap '${e}' created (id: ${o}) \u2014 app module not found`,{sitemapId:o,sitemapName:e,linked:!1,warning:`App module '${n}' not found. Sitemap was created but not linked.`,sitemapxml:s},["Run dataverse_publish_customizations to publish the new sitemap","Query appmodules ($select=uniquename,name) to verify the app module unique name"]);c=d.value[0].appmoduleid,await t.updateRecord("appmodules",c,{"sitemapid_sitemap@odata.bind":`/sitemaps(${o})`}),l=!0}return f(`Sitemap '${e}' created successfully`,{sitemapId:o,sitemapName:e,linked:l,appModuleUniqueName:n??null,appModuleId:c,areaCount:i.length,sitemapxml:s},["Run dataverse_publish_customizations to publish the sitemap",l?`Sitemap linked to app '${n}' (appmoduleid: ${c})`:"Provide appModuleUniqueName to link this sitemap to a model-driven app"])}default:throw new Error(`Unknown sitemap tool: ${r}`)}}import{z as C}from"zod";var Ct=[{name:"dataverse_create_table",description:"Create a new custom table (entity) in Dataverse via POST /EntityDefinitions. Provisions the table with a primary name column. Schema changes persist immediately \u2014 publish is NOT required between individual operations. BATCH RULE: if you plan to create more tables, add columns, or create relationships, do NOT publish now \u2014 accumulate all entity names and call dataverse_publish_customizations ONCE at the very end with components.entities=[list of all modified entities]. Use dataverse_create_attribute to add more columns afterwards.",inputSchema:{type:"object",properties:{schemaName:{type:"string",description:"Schema name (e.g., 'new_order'). Must include a publisher prefix."},displayName:{type:"string",description:"Singular display name (e.g., 'Order')."},displayCollectionName:{type:"string",description:"Plural display name (e.g., 'Orders')."},description:{type:"string",description:"Optional table description."},ownershipType:{type:"string",enum:["UserOwned","OrganizationOwned"],description:"Ownership model. Default: UserOwned."},hasNotes:{type:"boolean",description:"Allow notes/annotations. Default: false."},hasActivities:{type:"boolean",description:"Allow activities (email, task). Default: false."},isAuditEnabled:{type:"boolean",description:"Enable audit logging. Default: false."},primaryNameColumnSchemaName:{type:"string",description:"Schema name for the primary name column. Defaults to '{schemaName}_name'."},primaryNameColumnDisplayName:{type:"string",description:"Display name for the primary name column. Default: 'Name'."},iconVectorName:{type:"string",description:"Optional SVG web resource name for the table icon (e.g., 'new_myicon.svg'). PREREQUISITE: call dataverse_create_web_resource first to upload the SVG \u2014 the name you pick there becomes this value."}},required:["schemaName","displayName","displayCollectionName"]}},{name:"dataverse_create_relationship",description:"Create a One-to-Many or Many-to-Many relationship between two Dataverse tables via POST /RelationshipDefinitions. OneToMany: provide referencedEntity (the 'one' side) and referencingEntity (the 'many' side). ManyToMany: provide entity1LogicalName and entity2LogicalName. Schema changes persist immediately \u2014 publish is NOT required between individual operations. BATCH RULE: if more schema changes are planned, do NOT publish now \u2014 call dataverse_publish_customizations ONCE at the very end with all affected entities.",inputSchema:{type:"object",properties:{type:{type:"string",enum:["OneToMany","ManyToMany"],description:"Relationship type."},schemaName:{type:"string",description:"Schema name (e.g., 'new_account_order')."},referencedEntity:{type:"string",description:"(OneToMany) Logical name of the 'one' side entity (e.g., 'account')."},referencingEntity:{type:"string",description:"(OneToMany) Logical name of the 'many' side entity."},referencingAttributeSchemaName:{type:"string",description:"(OneToMany) Schema name of the lookup column on the 'many' entity."},referencingAttributeDisplayName:{type:"string",description:"(OneToMany) Display name for the lookup column."},entity1LogicalName:{type:"string",description:"(ManyToMany) First entity logical name."},entity2LogicalName:{type:"string",description:"(ManyToMany) Second entity logical name."},intersectEntitySchemaName:{type:"string",description:"(ManyToMany) Schema name for the intersect table."},cascadeDelete:{type:"string",enum:["Cascade","RemoveLink","Restrict","NoCascade"],description:"(OneToMany) Delete cascade behavior on referencing side. Default: RemoveLink."}},required:["type","schemaName"]}}];var Zs=C.object({schemaName:C.string().min(1).describe("Schema name of the new table (e.g., 'new_order'). Must include a publisher prefix."),displayName:C.string().min(1).describe("Singular display name (e.g., 'Order')."),displayCollectionName:C.string().min(1).describe("Plural display name (e.g., 'Orders')."),description:C.string().optional().describe("Optional description of the table's purpose."),ownershipType:C.enum(["UserOwned","OrganizationOwned"]).optional().default("UserOwned").describe("Ownership model. 'UserOwned' supports record-level sharing."),hasNotes:C.boolean().optional().default(!1).describe("Allow notes/annotations on this table."),hasActivities:C.boolean().optional().default(!1).describe("Allow activities (email, task, appointment) on this table."),isAuditEnabled:C.boolean().optional().default(!1).describe("Enable audit logging for this table."),primaryNameColumnSchemaName:C.string().optional().describe("Schema name for the primary name column. Defaults to '{schemaName}_name'."),primaryNameColumnDisplayName:C.string().optional().default("Name").describe("Display name for the primary name column. Default: 'Name'."),iconVectorName:C.string().optional().describe("Optional SVG web resource name for the table icon (e.g., 'new_myicon.svg'). PREREQUISITE: call dataverse_create_web_resource first.")}),eo=C.object({type:C.enum(["OneToMany","ManyToMany"]).describe("Relationship type."),schemaName:C.string().min(1).describe("Schema name for the relationship (e.g., 'new_account_order')."),referencedEntity:C.string().optional().describe("(OneToMany) Logical name of the 'one' side entity (e.g., 'account')."),referencingEntity:C.string().optional().describe("(OneToMany) Logical name of the 'many' side entity (e.g., 'new_order')."),referencingAttributeSchemaName:C.string().optional().describe("(OneToMany) Schema name of the lookup column on the referencing entity."),referencingAttributeDisplayName:C.string().optional().describe("(OneToMany) Display name for the lookup column."),entity1LogicalName:C.string().optional().describe("(ManyToMany) First entity logical name."),entity2LogicalName:C.string().optional().describe("(ManyToMany) Second entity logical name."),intersectEntitySchemaName:C.string().optional().describe("(ManyToMany) Schema name for the auto-generated intersect table."),cascadeDelete:C.enum(["Cascade","RemoveLink","Restrict","NoCascade"]).optional().default("RemoveLink").describe("(OneToMany) Delete cascade behavior on the referencing side. Default: RemoveLink.")});async function Da(r,a,t){switch(r){case"dataverse_create_table":return to(a,t);case"dataverse_create_relationship":return no(a,t);default:throw new Error(`Unknown schema tool: ${r}`)}}async function to(r,a){let t=Zs.parse(r),e=t.primaryNameColumnSchemaName??`${t.schemaName.toLowerCase()}_name`,n={"@odata.type":"Microsoft.Dynamics.CRM.EntityMetadata",SchemaName:t.schemaName,DisplayName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:t.displayName,LanguageCode:1033}]},DisplayCollectionName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:t.displayCollectionName,LanguageCode:1033}]},OwnershipType:t.ownershipType,HasNotes:t.hasNotes,HasActivities:t.hasActivities,IsAuditEnabled:{Value:t.isAuditEnabled,CanBeChanged:!0,ManagedPropertyLogicalName:"canauditentity"},PrimaryNameAttribute:e.toLowerCase(),Attributes:[{"@odata.type":"Microsoft.Dynamics.CRM.StringAttributeMetadata",SchemaName:e,IsPrimaryName:!0,RequiredLevel:{Value:"None",CanBeChanged:!0,ManagedPropertyLogicalName:"canmodifyrequirementlevelsettings"},DisplayName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:t.primaryNameColumnDisplayName??"Name",LanguageCode:1033}]},MaxLength:100}]};t.description&&(n.Description={"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:t.description,LanguageCode:1033}]}),t.iconVectorName&&(n.IconVectorName=t.iconVectorName);let i,s=!1;try{i=await a.createEntityDefinition(n)}catch(l){let c=l instanceof Error?l.message:String(l);if(c.includes("Request timed out")||c.includes("TRANSIENT_LOCK")){let d=!1;try{let u=await a.getTableMetadata(t.schemaName.toLowerCase(),!1);u?.MetadataId&&(i=u.MetadataId,d=!0,s=!0)}catch{}if(!d)throw l}else throw l}let o=s?" (NOTE: the request timed out but the table was confirmed created server-side \u2014 [TRANSIENT_TIMEOUT_RECOVERED])":"";return f(`Table '${t.displayName}' (${t.schemaName}) created. MetadataId: ${i}. Table is immediately queryable via the API. BATCH: if more schema changes are planned (columns, relationships, other tables), do NOT publish yet \u2014 call dataverse_publish_customizations ONCE at the very end with all affected entity names.${o}`,{metadataId:i,schemaName:t.schemaName,displayName:t.displayName,displayCollectionName:t.displayCollectionName,ownershipType:t.ownershipType,hasNotes:t.hasNotes,hasActivities:t.hasActivities,isAuditEnabled:t.isAuditEnabled},["BATCH: do NOT publish now if more schema changes are planned. Add columns/relationships first, then call dataverse_publish_customizations ONCE with components.entities=[all affected entity logical names]",`Use dataverse_create_attribute to add columns to '${t.schemaName.toLowerCase()}'`,`Use dataverse_create_relationship to link '${t.schemaName.toLowerCase()}' to other tables`,`When ALL schema work is done: dataverse_publish_customizations({ components: { entities: ['${t.schemaName.toLowerCase()}', ...otherEntities] } })`])}async function no(r,a){let t=eo.parse(r),e,n;if(t.type==="OneToMany"){if(!t.referencedEntity||!t.referencingEntity)throw new Error("OneToMany relationship requires 'referencedEntity' (the 'one' side) and 'referencingEntity' (the 'many' side).");let o=`${t.referencedEntity}id`,l=(()=>{if(t.referencingAttributeSchemaName)return null;let u=t.referencingEntity.split("_");return u.length>=2?u[0]:null})(),c=t.referencingAttributeSchemaName??(l?`${l}_${t.referencedEntity}id`:`${t.referencedEntity}id`),d=t.referencingAttributeDisplayName??t.referencedEntity.charAt(0).toUpperCase()+t.referencedEntity.slice(1);e={"@odata.type":"Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",SchemaName:t.schemaName,ReferencedEntity:t.referencedEntity,ReferencedAttribute:o,ReferencingEntity:t.referencingEntity,CascadeConfiguration:{"@odata.type":"Microsoft.Dynamics.CRM.CascadeConfiguration",Assign:"NoCascade",Delete:t.cascadeDelete,Merge:"NoCascade",Reparent:"NoCascade",Share:"NoCascade",Unshare:"NoCascade",RollupView:"NoCascade"},Lookup:{"@odata.type":"Microsoft.Dynamics.CRM.LookupAttributeMetadata",SchemaName:c,DisplayName:{"@odata.type":"Microsoft.Dynamics.CRM.Label",LocalizedLabels:[{"@odata.type":"Microsoft.Dynamics.CRM.LocalizedLabel",Label:d,LanguageCode:1033}]},RequiredLevel:{Value:"None",CanBeChanged:!0,ManagedPropertyLogicalName:"canmodifyrequirementlevelsettings"}}},n=`One-to-Many relationship '${t.schemaName}' created: ${t.referencedEntity} \u2192 ${t.referencingEntity}.`}else{if(!t.entity1LogicalName||!t.entity2LogicalName)throw new Error("ManyToMany relationship requires 'entity1LogicalName' and 'entity2LogicalName'.");let o=t.intersectEntitySchemaName??`${t.entity1LogicalName}_${t.entity2LogicalName}`;e={"@odata.type":"Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata",SchemaName:t.schemaName,Entity1LogicalName:t.entity1LogicalName,Entity2LogicalName:t.entity2LogicalName,IntersectEntityName:o,Entity1AssociatedMenuConfiguration:{"@odata.type":"Microsoft.Dynamics.CRM.AssociatedMenuConfiguration",Behavior:"UseCollectionName",Group:"Details",Order:null,IsCustomizable:!0},Entity2AssociatedMenuConfiguration:{"@odata.type":"Microsoft.Dynamics.CRM.AssociatedMenuConfiguration",Behavior:"UseCollectionName",Group:"Details",Order:null,IsCustomizable:!0}},n=`Many-to-Many relationship '${t.schemaName}' created: ${t.entity1LogicalName} \u2194 ${t.entity2LogicalName}.`}let i=await a.createRelationship(e),s={metadataId:i,schemaName:t.schemaName,type:t.type};return t.type==="OneToMany"&&(s.cascadeDelete=t.cascadeDelete),f(`${n} MetadataId: ${i}.`,s,["BATCH: do NOT publish now if more schema changes are planned \u2014 call dataverse_publish_customizations ONCE at the very end with components.entities=[all affected entity logical names]"])}import{z as N}from"zod";var It=[{name:"dataverse_check_record_access",description:"Check which access rights a specific user or team has on a Dataverse record. Calls the RetrievePrincipalAccess function. Returns a list of rights: ReadAccess, WriteAccess, AppendAccess, AppendToAccess, AssignAccess, ShareAccess, DeleteAccess. Use dataverse_whoami to find the current user ID.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts')."},recordId:{type:"string",description:"GUID of the record."},principalId:{type:"string",description:"GUID of the user or team."},principalType:{type:"string",enum:["systemuser","team"],description:"Principal type. Default: systemuser."}},required:["entitySetName","recordId","principalId"]}},{name:"dataverse_grant_access",description:"Grant record-level sharing access to a user or team using the GrantAccess Dataverse action. The caller must have ShareAccess on the record. Use dataverse_check_record_access to verify rights were granted.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts')."},recordId:{type:"string",description:"GUID of the record to share."},principalId:{type:"string",description:"GUID of the user or team receiving access."},principalType:{type:"string",enum:["systemuser","team"],description:"Principal type. Default: systemuser."},accessRights:{type:"array",items:{type:"string",enum:["ReadAccess","WriteAccess","AppendAccess","AppendToAccess","AssignAccess","ShareAccess","DeleteAccess"]},description:"Rights to grant."}},required:["entitySetName","recordId","principalId","accessRights"]}},{name:"dataverse_revoke_access",description:"Revoke record-level sharing access from a user or team using the RevokeAccess Dataverse action. Removes all shared access previously granted by GrantAccess. The caller must have ShareAccess on the record.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts')."},recordId:{type:"string",description:"GUID of the record."},principalId:{type:"string",description:"GUID of the user or team."},principalType:{type:"string",enum:["systemuser","team"],description:"Principal type. Default: systemuser."}},required:["entitySetName","recordId","principalId"]}},{name:"dataverse_merge_records",description:"Merge two Dataverse records of the same entity type. The subordinate record is deactivated and its data merged into the target. Supports account, contact, and lead entities. This is IRREVERSIBLE \u2014 the subordinate record cannot be restored. Requires confirm: true.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name (e.g., 'account', 'contact', 'lead')."},entitySetName:{type:"string",description:"OData entity set name (e.g., 'accounts', 'contacts', 'leads')."},targetId:{type:"string",description:"GUID of the master record (kept after merge)."},subordinateId:{type:"string",description:"GUID of the subordinate record (deactivated after merge)."},updateContent:{type:"object",description:"Optional field values to update on the target from the subordinate."},performParentingChecks:{type:"boolean",description:"Check for parenting conflicts before merge. Default: false."},confirm:{type:"boolean",enum:[!0],description:"Must be true to execute the merge."}},required:["entityLogicalName","entitySetName","targetId","subordinateId","confirm"]}}];var ao=["ReadAccess","WriteAccess","AppendAccess","AppendToAccess","AssignAccess","ShareAccess","DeleteAccess"],xt=["systemuser","team"],ro=N.object({entitySetName:N.string().min(1).describe("OData entity set name (plural, e.g., 'accounts')."),recordId:N.string().uuid().describe("GUID of the record to check access on."),principalId:N.string().uuid().describe("GUID of the user or team to check access for."),principalType:N.enum(xt).optional().default("systemuser").describe("Type of principal. Default: systemuser.")}),io=N.object({entitySetName:N.string().min(1).describe("OData entity set name for the target record (e.g., 'accounts')."),recordId:N.string().uuid().describe("GUID of the record to share."),principalId:N.string().uuid().describe("GUID of the user or team receiving access."),principalType:N.enum(xt).optional().default("systemuser").describe("Type of principal. Default: systemuser."),accessRights:N.array(N.enum(ao)).min(1).describe("Rights to grant. Options: ReadAccess, WriteAccess, AppendAccess, AppendToAccess, AssignAccess, ShareAccess, DeleteAccess.")}),so=N.object({entitySetName:N.string().min(1).describe("OData entity set name for the target record (e.g., 'accounts')."),recordId:N.string().uuid().describe("GUID of the record to revoke access from."),principalId:N.string().uuid().describe("GUID of the user or team losing access."),principalType:N.enum(xt).optional().default("systemuser").describe("Type of principal. Default: systemuser.")}),oo=N.object({entityLogicalName:N.string().min(1).describe("Logical name of the entity (e.g., 'account', 'contact', 'lead')."),entitySetName:N.string().min(1).describe("OData entity set name (e.g., 'accounts', 'contacts')."),targetId:N.string().uuid().describe("GUID of the master record (kept after merge)."),subordinateId:N.string().uuid().describe("GUID of the subordinate record (deactivated after merge)."),updateContent:N.record(N.unknown()).optional().describe("Fields from the subordinate to copy into the target record."),performParentingChecks:N.boolean().optional().default(!1).describe("Check for parenting conflicts before merge. Default: false."),confirm:N.literal(!0).describe("Must be true to confirm the merge. The subordinate record is deactivated permanently.")});async function Pa(r,a,t){switch(r){case"dataverse_check_record_access":return co(a,t);case"dataverse_grant_access":return lo(a,t);case"dataverse_revoke_access":return uo(a,t);case"dataverse_merge_records":return po(a,t);default:throw new Error(`Unknown record-access tool: ${r}`)}}function Dt(r){return r==="systemuser"?"systemusers":"teams"}function Ua(r){return r==="systemuser"?"systemuserid":"teamid"}function $a(r){return r==="systemuser"?"Microsoft.Dynamics.CRM.systemuser":"Microsoft.Dynamics.CRM.team"}async function co(r,a){let t=ro.parse(r),e=Dt(t.principalType),i=(await a.executeBoundFunction(e,t.principalId,"RetrievePrincipalAccess",{Target:{"@odata.id":`${t.entitySetName}(${t.recordId})`}}))?.AccessRights??"None",s=i==="None"?[]:i.split(", ").filter(Boolean);return f(`${t.principalType} ${t.principalId} has [${s.join(", ")||"no access"}] on ${t.entitySetName}(${t.recordId}).`,{accessRights:i,rights:s,principalId:t.principalId,principalType:t.principalType,entitySetName:t.entitySetName,recordId:t.recordId},s.length===0?["Use dataverse_grant_access to grant access to this principal"]:["Use dataverse_revoke_access to remove all shared access from this principal"])}async function lo(r,a){let t=io.parse(r),e=Dt(t.principalType),n=Ua(t.principalType),i=$a(t.principalType);return await a.executeAction("GrantAccess",{Target:{"@odata.id":`${t.entitySetName}(${t.recordId})`},PrincipalAccess:{Principal:{"@odata.type":i,[n]:t.principalId,"@odata.id":`${e}(${t.principalId})`},AccessMask:t.accessRights.join(", ")}}),f(`Access granted: ${t.principalType} ${t.principalId} has [${t.accessRights.join(", ")}] on ${t.entitySetName}(${t.recordId}).`,{entitySetName:t.entitySetName,recordId:t.recordId,principalId:t.principalId,principalType:t.principalType,accessRights:t.accessRights},["Use dataverse_check_record_access to verify the granted rights","Use dataverse_revoke_access to remove access if needed"])}async function uo(r,a){let t=so.parse(r),e=Dt(t.principalType),n=Ua(t.principalType),i=$a(t.principalType);return await a.executeAction("RevokeAccess",{Target:{"@odata.id":`${t.entitySetName}(${t.recordId})`},Revokee:{"@odata.type":i,[n]:t.principalId,"@odata.id":`${e}(${t.principalId})`}}),f(`Access revoked: ${t.principalType} ${t.principalId} no longer has shared access to ${t.entitySetName}(${t.recordId}).`,{entitySetName:t.entitySetName,recordId:t.recordId,principalId:t.principalId,principalType:t.principalType},["Use dataverse_check_record_access to verify access has been removed"])}var Oa=new Set(["account","contact","lead","incident","opportunity"]);async function po(r,a){let t=oo.parse(r);if(!Oa.has(t.entityLogicalName))throw new Error(`dataverse_merge_records only supports: ${[...Oa].join(", ")}. '${t.entityLogicalName}' is not supported by the Dataverse Merge API. For custom entities, manually copy fields with dataverse_update then deactivate the subordinate record.`);let e=`Microsoft.Dynamics.CRM.${t.entityLogicalName}`,n=`${t.entityLogicalName}id`;return await a.executeAction("Merge",{Target:{"@odata.type":e,[n]:t.targetId,"@odata.id":`${t.entitySetName}(${t.targetId})`},Subordinate:{"@odata.type":e,[n]:t.subordinateId,"@odata.id":`${t.entitySetName}(${t.subordinateId})`},UpdateContent:{"@odata.type":`#Microsoft.Dynamics.CRM.${t.entityLogicalName}`,...t.updateContent??{}},PerformParentingChecks:t.performParentingChecks??!1}),f(`Merge complete: ${t.entityLogicalName}(${t.subordinateId}) has been deactivated and merged into ${t.entityLogicalName}(${t.targetId}).`,{entityLogicalName:t.entityLogicalName,targetId:t.targetId,subordinateId:t.subordinateId},[`Use dataverse_get to verify the merged record: entitySetName='${t.entitySetName}', recordId='${t.targetId}'`,"The subordinate record is now deactivated and cannot be restored"])}import{z as Ee}from"zod";var re="ui://dataverse/table-browser.html",La=[{name:"dataverse_open_table_browser",description:"Opens an interactive MCP App to browse Dataverse table rows with sorting and paging.",inputSchema:{type:"object",properties:{tableName:{type:"string",description:"Optional initial entity set name to open (e.g., accounts)"},top:{type:"number",description:"Initial row count to load (default 50, max 200)"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:re,visibility:["model","app"]},"ui/resourceUri":re}},{name:"dataverse_table_browser_list_tables",description:"App-only helper: lists available Dataverse tables for the table browser UI.",inputSchema:{type:"object",properties:{}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:re,visibility:["app"]},"ui/resourceUri":re}},{name:"dataverse_table_browser_get_rows",description:"App-only helper: retrieves rows from one table for UI rendering with compact output.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:'Entity set name (e.g., "accounts")'},top:{type:"number",description:"Maximum number of rows to fetch (default 50, max 200)"}},required:["entitySetName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:re,visibility:["app"]},"ui/resourceUri":re}}],mo=Ee.object({tableName:Ee.string().optional(),top:Ee.number().int().min(5).max(200).optional().default(50)}),go=Ee.object({entitySetName:Ee.string().min(1),top:Ee.number().int().min(5).max(200).optional().default(50)}),fo=[{EntitySetName:"accounts",LogicalName:"account",DisplayName:{UserLocalizedLabel:{Label:"Account"}}},{EntitySetName:"contacts",LogicalName:"contact",DisplayName:{UserLocalizedLabel:{Label:"Contact"}}},{EntitySetName:"leads",LogicalName:"lead",DisplayName:{UserLocalizedLabel:{Label:"Lead"}}},{EntitySetName:"opportunities",LogicalName:"opportunity",DisplayName:{UserLocalizedLabel:{Label:"Opportunity"}}}];async function Ma(r,a,t){switch(r){case"dataverse_open_table_browser":{let e=mo.parse(a??{});return{content:[{type:"text",text:JSON.stringify({message:"Table Browser opened",initial:{tableName:e.tableName??null,top:e.top}},null,2)}]}}case"dataverse_table_browser_list_tables":{let e=[],n=[];try{n=await t.listTables(!1)}catch(i){e.push(`listTables(false) failed: ${String(i)}`)}if(!n.length)try{n=await t.listTables(!0),n.length&&e.push("Fell back to custom tables only.")}catch(i){e.push(`listTables(true) failed: ${String(i)}`)}return n.length||(n=fo,e.push("Using built-in fallback table candidates.")),{content:[{type:"text",text:JSON.stringify({tables:n,warnings:e})}]}}case"dataverse_table_browser_get_rows":{let e=go.parse(a??{}),i=(await t.query(e.entitySetName,{top:e.top})).value.map(s=>{let o={};for(let[l,c]of Object.entries(s))c!=null&&c!==""&&!l.startsWith("@odata")&&!l.includes("@odata")&&(o[l]=c);return o});return{content:[{type:"text",text:JSON.stringify({entitySetName:e.entitySetName,rows:i},null,2)}]}}default:throw new Error(`Unknown table browser tool: ${r}`)}}import{z as ie}from"zod";var Q="ui://dataverse/fetchxml-workbench.html",Ha=[{name:"dataverse_open_fetchxml_workbench",description:"Opens an interactive MCP App to run FetchXML queries with sortable results.",inputSchema:{type:"object",properties:{entitySetName:{type:"string",description:"Optional initial entity set name to target (e.g., accounts)"},fetchXml:{type:"string",description:"Optional initial FetchXML query to prefill editor"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:Q,visibility:["model","app"]},"ui/resourceUri":Q}},{name:"dataverse_fetchxml_workbench_list_tables",description:"App-only helper: lists available Dataverse tables for FetchXML workbench hints.",inputSchema:{type:"object",properties:{}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:Q,visibility:["app"]},"ui/resourceUri":Q}},{name:"dataverse_fetchxml_workbench_list_attributes",description:"App-only helper: returns attribute logical names and types for a Dataverse entity to populate the attribute picker.",inputSchema:{type:"object",properties:{entityLogicalName:{type:"string",description:"Logical name of the entity (e.g., account, contact)"}},required:["entityLogicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:Q,visibility:["app"]},"ui/resourceUri":Q}},{name:"dataverse_fetchxml_workbench_execute",description:"App-only helper: executes FetchXML and returns compact rows for UI rendering.",inputSchema:{type:"object",properties:{fetchXml:{type:"string",description:"Complete FetchXML query string"},entitySetName:{type:"string",description:"Optional entity set name (e.g., accounts). If omitted, extracted from <entity name=...>."},fetchAll:{type:"boolean",description:"Whether to auto-paginate all pages (default false)"},compact:{type:"boolean",description:"Drop null/empty and @odata fields to reduce payload (default true)"}},required:["fetchXml"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:Q,visibility:["app"]},"ui/resourceUri":Q}}],yo=ie.object({entitySetName:ie.string().optional(),fetchXml:ie.string().optional()}),ho=ie.object({fetchXml:ie.string().min(1),entitySetName:ie.string().optional(),fetchAll:ie.boolean().optional().default(!1),compact:ie.boolean().optional().default(!0)}),vo=[{EntitySetName:"accounts",LogicalName:"account",DisplayName:{UserLocalizedLabel:{Label:"Account"}}},{EntitySetName:"contacts",LogicalName:"contact",DisplayName:{UserLocalizedLabel:{Label:"Contact"}}},{EntitySetName:"leads",LogicalName:"lead",DisplayName:{UserLocalizedLabel:{Label:"Lead"}}},{EntitySetName:"opportunities",LogicalName:"opportunity",DisplayName:{UserLocalizedLabel:{Label:"Opportunity"}}}];function qa(r){let a={};for(let[t,e]of Object.entries(r))e!=null&&e!==""&&!t.startsWith("@odata")&&!t.includes("@odata")&&(a[t]=e);return a}function bo(r){let t=r.match(/<entity\s+name=["']([^"']+)["']/i)?.[1]?.trim();return t||void 0}async function ja(r,a,t){switch(r){case"dataverse_open_fetchxml_workbench":{let e=yo.parse(a??{});return{content:[{type:"text",text:JSON.stringify({message:"FetchXML Workbench opened",initial:{entitySetName:e.entitySetName??null,fetchXml:e.fetchXml??null}},null,2)}]}}case"dataverse_fetchxml_workbench_list_attributes":{let e=ie.object({entityLogicalName:ie.string()}).parse(a??{}),n=[],i=[];try{n=((await t.getTableMetadata(e.entityLogicalName,!0)).Attributes??[]).map(o=>({logicalName:o.LogicalName,attributeType:o.AttributeType,displayName:o.DisplayName?.UserLocalizedLabel?.Label??o.LogicalName})),n.sort((o,l)=>o.logicalName.localeCompare(l.logicalName))}catch(s){i.push(`getTableMetadata failed: ${String(s)}`)}return{content:[{type:"text",text:JSON.stringify({attributes:n,warnings:i})}]}}case"dataverse_fetchxml_workbench_list_tables":{let e=[],n=[];try{n=await t.listTables(!1)}catch(i){e.push(`listTables(false) failed: ${String(i)}`)}if(!n.length)try{n=await t.listTables(!0),n.length&&e.push("Fell back to custom tables only.")}catch(i){e.push(`listTables(true) failed: ${String(i)}`)}return n.length||(n=vo,e.push("Using built-in fallback table candidates.")),{content:[{type:"text",text:JSON.stringify({tables:n,warnings:e})}]}}case"dataverse_fetchxml_workbench_execute":{let e=ho.parse(a??{}),n=e.entitySetName??bo(e.fetchXml);if(!n)return{content:[{type:"text",text:JSON.stringify({error:"entitySetName is required when not present in FetchXML <entity name=...> element"})}],isError:!0};if(e.fetchAll){let l=await t.executeFetchXmlAllPages(n,e.fetchXml),c=e.compact?l.map(qa):l;return{content:[{type:"text",text:JSON.stringify({entitySetName:n,rows:c,fetchAll:!0})}]}}let i=await t.executeFetchXml(n,e.fetchXml),s=Array.isArray(i.value)?i.value:[],o=e.compact?s.map(qa):s;return{content:[{type:"text",text:JSON.stringify({entitySetName:n,rows:o,fetchAll:!1})}]}}default:throw new Error(`Unknown FetchXML workbench tool: ${r}`)}}L();import{z as le}from"zod";var be="ui://dataverse/plugin-trace-viewer.html",Wa=[{name:"dataverse_open_plugin_trace_viewer",description:"Opens an interactive MCP App to browse, filter, and inspect Dataverse plugin trace logs. Shows plugin name, entity, message, execution duration, and exception details. Requires Plugin Trace Log to be enabled in Dataverse settings (Settings > Administration > System Settings > Customization tab).",inputSchema:{type:"object",properties:{exceptionsOnly:{type:"boolean",description:"Pre-filter to show only logs with exceptions (default false)"},entityFilter:{type:"string",description:"Pre-filter by entity logical name (e.g. 'account')"},pluginFilter:{type:"string",description:"Pre-filter by plugin type name substring (e.g. 'AccountValidation')"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:be,visibility:["model","app"]},"ui/resourceUri":be}},{name:"dataverse_plugin_trace_viewer_get_logs",description:"App-only helper: retrieves plugin trace logs for the Plugin Trace Viewer UI.",inputSchema:{type:"object",properties:{tail:{type:"number",description:"Max recent logs to return (default 100, max 500)"},exceptionsOnly:{type:"boolean",description:"Return only logs with exceptions (default false)"},entityFilter:{type:"string",description:"Server-side filter by entity logical name"},pluginFilter:{type:"string",description:"Server-side filter by plugin type name substring"}}},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:be,visibility:["app"]},"ui/resourceUri":be}}],_o=["plugintracelogid","typename","messagename","primaryentity","exceptiondetails","messageblock","createdon","performanceexecutionduration"],wo=le.object({exceptionsOnly:le.boolean().optional().default(!1),entityFilter:le.string().optional(),pluginFilter:le.string().optional()}),To=le.object({tail:le.number().int().positive().max(500).optional().default(100),exceptionsOnly:le.boolean().optional().default(!1),entityFilter:le.string().optional(),pluginFilter:le.string().optional()});function Le(r){return typeof r=="string"?r:""}async function Fa(r,a,t){switch(r){case"dataverse_open_plugin_trace_viewer":{let e=wo.parse(a??{});return{content:[{type:"text",text:JSON.stringify({message:"Plugin Trace Viewer opened",initial:e},null,2)}]}}case"dataverse_plugin_trace_viewer_get_logs":{let e=To.parse(a??{}),n=[];e.exceptionsOnly&&n.push("exceptiondetails ne null"),e.entityFilter&&n.push(`primaryentity eq '${h(e.entityFilter)}'`),e.pluginFilter&&n.push(`contains(typename,'${h(e.pluginFilter)}')`);let i=n.length>0?n.join(" and "):void 0,l=((await t.query("plugintracelogs",{select:_o,orderby:"createdon desc",top:e.tail,...i!==void 0?{filter:i}:{}})).value??[]).map(c=>({id:Le(c.plugintracelogid),typeName:Le(c.typename),message:Le(c.messagename),entity:Le(c.primaryentity),createdOn:Le(c.createdon),durationMs:typeof c.performanceexecutionduration=="number"?c.performanceexecutionduration:null,hasException:c.exceptiondetails!=null&&c.exceptiondetails!=="",exceptionDetails:typeof c.exceptiondetails=="string"?c.exceptiondetails:null,messageBlock:typeof c.messageblock=="string"?c.messageblock:null}));return{content:[{type:"text",text:JSON.stringify({total:l.length,logs:l})}]}}default:return{content:[{type:"text",text:`Unknown tool: ${r}`}],isError:!0}}}import{z as Ba}from"zod";var de="ui://dataverse/schema-explorer.html",Ga=[{name:"dataverse_open_schema_explorer",description:"Opens the Dataverse Schema Explorer \u2014 an interactive UI for browsing table definitions, column types, relationships, and alternate keys. Renders an in-context app panel so you can explore any Dataverse entity visually.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},_meta:{ui:{resourceUri:de,visibility:["model","app"]}}},{name:"dataverse_schema_explorer_list_tables",description:"App-only helper: lists all Dataverse tables (including system tables) for the Schema Explorer UI. Returns logical name, display name, entity set name, custom flag, and ownership type.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:de,visibility:["app"]}}},{name:"dataverse_schema_explorer_get_entity",description:"App-only helper: returns the full entity card for the Schema Explorer \u2014 columns, relationship counts (1:N / N:1 / N:N), and alternate keys for a given table.",inputSchema:{type:"object",properties:{logicalName:{type:"string",description:'Logical name of the table (e.g. "account", "contact")'}},required:["logicalName"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:de,visibility:["app"]}}}],Ro=Ba.object({logicalName:Ba.string().min(1)});function Ot(r){if(typeof r=="string")return r;let a=r;return a?.UserLocalizedLabel?.Label??a?.LocalizedLabels?.[0]?.Label??""}async function Va(r,a,t){switch(r){case"dataverse_open_schema_explorer":return{content:[{type:"text",text:JSON.stringify({resourceUri:de})}],_meta:{ui:{resourceUri:de}}};case"dataverse_schema_explorer_list_tables":{let e=await t.listTables(!1),n=(Array.isArray(e)?e:[]).map(i=>({logicalName:i.LogicalName??"",displayName:Ot(i.DisplayName)||i.LogicalName,entitySetName:i.EntitySetName??"",isCustomEntity:i.IsCustomEntity??!1,ownershipType:i.OwnershipType??"Unknown"}));return{content:[{type:"text",text:JSON.stringify({tables:n})}]}}case"dataverse_schema_explorer_get_entity":{let{logicalName:e}=Ro.parse(a),[n,i,s]=await Promise.all([t.getTableMetadata(e,!0),t.getRelationships(e),t.getEntityKeys(e)]),l=(Array.isArray(n?.Attributes)?n.Attributes:[]).map(_=>({logicalName:_.LogicalName??"",displayName:Ot(_.DisplayName)||_.LogicalName,dataType:_.AttributeType??"Unknown",isRequired:_.RequiredLevel?.Value==="ApplicationRequired"||_.RequiredLevel?.Value==="SystemRequired",lookupTarget:Array.isArray(_.Targets)&&_.Targets.length>0?_.Targets[0]:null})),c=e.toLowerCase(),d=Array.isArray(i)?i:[],u=d.filter(_=>_.RelationshipType==="OneToManyRelationship"&&_.ReferencedEntity?.toLowerCase()===c).length,m=d.filter(_=>_.RelationshipType==="OneToManyRelationship"&&_.ReferencingEntity?.toLowerCase()===c).length,p=d.filter(_=>_.RelationshipType==="ManyToManyRelationship").length,y=(Array.isArray(s)?s:[]).map(_=>({name:_.schemaName??_.logicalName??"",keyAttributes:Array.isArray(_.keyAttributes)?_.keyAttributes:[]})),w=n,O={logicalName:w.LogicalName??e,displayName:Ot(w.DisplayName)||e,entitySetName:w.EntitySetName??"",ownershipType:w.OwnershipType??"Unknown",isAuditEnabled:w.IsAuditEnabled??!1,isCustomEntity:w.IsCustomEntity??!1,columns:l,relationships:{oneToMany:u,manyToOne:m,manyToMany:p},alternateKeys:y};return{content:[{type:"text",text:JSON.stringify(O)}]}}default:throw new Error(`Unknown schema explorer tool: ${r}`)}}import{z as _e}from"zod";var K="ui://dataverse/rbac-inspector.html",za=[{name:"dataverse_open_rbac_inspector",description:"Opens the Dataverse RBAC Inspector \u2014 an interactive UI for listing security roles, browsing privilege matrices (entity \xD7 operation \xD7 depth), and auditing user role assignments.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!1},_meta:{ui:{resourceUri:K,visibility:["model","app"]}}},{name:"dataverse_rbac_inspector_list_roles",description:"App-only helper: lists all Dataverse security roles with name and associated business unit.",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]}}},{name:"dataverse_rbac_inspector_get_role_privileges",description:"App-only helper: returns the full privilege matrix (entity \xD7 operation \xD7 depth) for a security role. Uses FetchXML to join privileges with role assignments in a single call.",inputSchema:{type:"object",properties:{roleId:{type:"string",description:"GUID of the security role"}},required:["roleId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]}}},{name:"dataverse_rbac_inspector_list_users",description:"App-only helper: lists active Dataverse system users, optionally filtered by name or email.",inputSchema:{type:"object",properties:{search:{type:"string",description:"Optional name or email search term"}},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]}}},{name:"dataverse_rbac_inspector_get_user_roles",description:"App-only helper: returns all security roles assigned to a Dataverse system user.",inputSchema:{type:"object",properties:{userId:{type:"string",description:"GUID of the system user"}},required:["userId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]}}},{name:"dataverse_rbac_inspector_list_field_profiles",description:"App-only helper: lists all Dataverse Field Security Profiles (column-level security, independent of entity-level Security Roles).",inputSchema:{type:"object",properties:{},required:[]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]}}},{name:"dataverse_rbac_inspector_get_profile_permissions",description:"App-only helper: returns all field-level permissions for a Field Security Profile, grouped by entity. Each permission has canRead, canUpdate, canCreate boolean flags.",inputSchema:{type:"object",properties:{profileId:{type:"string",description:"GUID of the field security profile"}},required:["profileId"]},annotations:{readOnlyHint:!0,destructiveHint:!1,idempotentHint:!0,openWorldHint:!0},_meta:{ui:{resourceUri:K,visibility:["app"]}}}],So=_e.object({roleId:_e.string().min(1)}),No=_e.object({userId:_e.string().min(1)}),Ao=_e.object({search:_e.string().optional()}),ko=_e.object({profileId:_e.string().min(1)}),Eo={Create:"create",Read:"read",Write:"write",Delete:"delete",AppendTo:"appendTo",Append:"append",Assign:"assign",Share:"share"};function Co(r){let a=r.match(/^prv(Create|Read|Write|Delete|AppendTo|Append|Assign|Share)(.+)$/);if(!a)return null;let t=Eo[a[1]];return t?{action:t,entity:a[2].toLowerCase()}:null}function Io(r){return r&8?"Global":r&4?"Deep":r&2?"Local":r&1?"Basic":"None"}async function Ka(r,a,t){switch(r){case"dataverse_open_rbac_inspector":return{content:[{type:"text",text:JSON.stringify({resourceUri:K})}],_meta:{ui:{resourceUri:K}}};case"dataverse_rbac_inspector_list_roles":{let n=((await t.query("roles",{select:["roleid","name"],expand:"businessunitid($select=name)",orderby:"name asc",top:200})).value??[]).map(i=>({id:i.roleid??"",name:i.name??"",businessUnit:i.businessunitid?.name??""}));return{content:[{type:"text",text:JSON.stringify({roles:n})}]}}case"dataverse_rbac_inspector_get_role_privileges":{let{roleId:e}=So.parse(a),[n,i]=await Promise.all([t.query("roles",{filter:`roleid eq ${e}`,select:["name"],expand:"businessunitid($select=name)",top:1}),t.executeFetchXmlAllPages("privileges",`<fetch count="5000">
158
290
  <entity name="privilege">
159
291
  <attribute name="name"/>
160
292
  <link-entity name="roleprivileges" from="privilegeid" to="privilegeid" alias="rp">
@@ -164,7 +296,7 @@ ${a.map(r=>{let o=r.id??`Area_${++i}`,s=r.groups.map(c=>{let l=c.id??`Group_${++
164
296
  </filter>
165
297
  </link-entity>
166
298
  </entity>
167
- </fetch>`)]),o=(t.value??[])[0]??{},s=o.name??"",c=o.businessunitid?.name??"",l=new Map;for(let u of r){let g=u.name??"",m=u["rp.privilegedepthmask"]??u.rp_privilegedepthmask??0,y=typeof m=="string"?parseInt(m,10):Number(m),R=Mi(y),q=Pi(g);if(!q)continue;let{entity:h,action:j}=q;l.has(h)||l.set(h,{create:"None",read:"None",write:"None",delete:"None",appendTo:"None",append:"None",assign:"None",share:"None"}),l.get(h)[j]=R}let d=Array.from(l.entries()).filter(([,u])=>Object.values(u).some(g=>g!=="None")).map(([u,g])=>({entity:u,...g})).sort((u,g)=>u.entity.localeCompare(g.entity));return{content:[{type:"text",text:JSON.stringify({roleId:e,roleName:s,businessUnit:c,matrix:d,totalPrivileges:r.length,totalEntities:d.length})}]}}case"dataverse_rbac_inspector_list_users":{let{search:e}=Oi.parse(i),t=["isdisabled eq false","applicationid eq null"];if(e){let s=e.replace(/'/g,"''");t.push(`(contains(fullname,'${s}') or contains(internalemailaddress,'${s}'))`)}let o=((await n.query("systemusers",{select:["systemuserid","fullname","internalemailaddress"],filter:t.join(" and "),expand:"businessunitid($select=name)",orderby:"fullname asc",top:50})).value??[]).map(s=>({id:s.systemuserid??"",fullName:s.fullname??"",email:s.internalemailaddress??"",businessUnit:s.businessunitid?.name??""}));return{content:[{type:"text",text:JSON.stringify({users:o})}]}}case"dataverse_rbac_inspector_get_user_roles":{let{userId:e}=Ci.parse(i),r=(await n.query("systemusers",{filter:`systemuserid eq ${e}`,select:["fullname","internalemailaddress"],expand:"systemuserroles_association($select=name,roleid)",top:1})).value??[];if(r.length===0)throw new Error(`User '${e}' not found`);let o=r[0],c=(o.systemuserroles_association??[]).map(l=>({id:l.roleid??"",name:l.name??""}));return{content:[{type:"text",text:JSON.stringify({userId:e,fullName:o.fullname??"",email:o.internalemailaddress??"",roles:c})}]}}case"dataverse_rbac_inspector_list_field_profiles":{let t=((await n.query("fieldsecurityprofiles",{select:["fieldsecurityprofileid","name","description"],orderby:"name asc",top:500})).value??[]).map(r=>({id:r.fieldsecurityprofileid??"",name:r.name??"",description:r.description??""}));return{content:[{type:"text",text:JSON.stringify({profiles:t})}]}}case"dataverse_rbac_inspector_get_profile_permissions":{let{profileId:e}=Li.parse(i),[t,r]=await Promise.all([n.query("fieldsecurityprofiles",{filter:`fieldsecurityprofileid eq ${e}`,select:["name","description"],top:1}),n.executeFetchXmlAllPages("fieldpermissions",`<fetch count="5000">
299
+ </fetch>`)]),s=(n.value??[])[0]??{},o=s.name??"",l=s.businessunitid?.name??"",c=new Map;for(let u of i){let m=u.name??"",p=u["rp.privilegedepthmask"]??u.rp_privilegedepthmask??0,y=typeof p=="string"?parseInt(p,10):Number(p),w=Io(y),O=Co(m);if(!O)continue;let{entity:_,action:M}=O;c.has(_)||c.set(_,{create:"None",read:"None",write:"None",delete:"None",appendTo:"None",append:"None",assign:"None",share:"None"}),c.get(_)[M]=w}let d=Array.from(c.entries()).filter(([,u])=>Object.values(u).some(m=>m!=="None")).map(([u,m])=>({entity:u,...m})).sort((u,m)=>u.entity.localeCompare(m.entity));return{content:[{type:"text",text:JSON.stringify({roleId:e,roleName:o,businessUnit:l,matrix:d,totalPrivileges:i.length,totalEntities:d.length})}]}}case"dataverse_rbac_inspector_list_users":{let{search:e}=Ao.parse(a),n=["isdisabled eq false","applicationid eq null"];if(e){let o=e.replace(/'/g,"''");n.push(`(contains(fullname,'${o}') or contains(internalemailaddress,'${o}'))`)}let s=((await t.query("systemusers",{select:["systemuserid","fullname","internalemailaddress"],filter:n.join(" and "),expand:"businessunitid($select=name)",orderby:"fullname asc",top:50})).value??[]).map(o=>({id:o.systemuserid??"",fullName:o.fullname??"",email:o.internalemailaddress??"",businessUnit:o.businessunitid?.name??""}));return{content:[{type:"text",text:JSON.stringify({users:s})}]}}case"dataverse_rbac_inspector_get_user_roles":{let{userId:e}=No.parse(a),i=(await t.query("systemusers",{filter:`systemuserid eq ${e}`,select:["fullname","internalemailaddress"],expand:"systemuserroles_association($select=name,roleid)",top:1})).value??[];if(i.length===0)throw new Error(`User '${e}' not found`);let s=i[0],l=(s.systemuserroles_association??[]).map(c=>({id:c.roleid??"",name:c.name??""}));return{content:[{type:"text",text:JSON.stringify({userId:e,fullName:s.fullname??"",email:s.internalemailaddress??"",roles:l})}]}}case"dataverse_rbac_inspector_list_field_profiles":{let n=((await t.query("fieldsecurityprofiles",{select:["fieldsecurityprofileid","name","description"],orderby:"name asc",top:500})).value??[]).map(i=>({id:i.fieldsecurityprofileid??"",name:i.name??"",description:i.description??""}));return{content:[{type:"text",text:JSON.stringify({profiles:n})}]}}case"dataverse_rbac_inspector_get_profile_permissions":{let{profileId:e}=ko.parse(a),[n,i]=await Promise.all([t.query("fieldsecurityprofiles",{filter:`fieldsecurityprofileid eq ${e}`,select:["name","description"],top:1}),t.executeFetchXmlAllPages("fieldpermissions",`<fetch count="5000">
168
300
  <entity name="fieldpermission">
169
301
  <attribute name="entityname"/>
170
302
  <attribute name="attributelogicalname"/>
@@ -177,7 +309,7 @@ ${a.map(r=>{let o=r.id??`Area_${++i}`,s=r.groups.map(c=>{let l=c.id??`Group_${++
177
309
  <order attribute="entityname" descending="false"/>
178
310
  <order attribute="attributelogicalname" descending="false"/>
179
311
  </entity>
180
- </fetch>`)]),o=(t.value??[])[0]??{},s=new Map;for(let l of r){let d=l.entityname??"",u=l.attributelogicalname??"",g=Number(l.canread)!==0,m=Number(l.canupdate)!==0,y=Number(l.cancreate)!==0;s.has(d)||s.set(d,[]),s.get(d).push({field:u,canRead:g,canUpdate:m,canCreate:y})}let c=Array.from(s.entries()).map(([l,d])=>({entity:l,fields:d})).sort((l,d)=>l.entity.localeCompare(d.entity));return{content:[{type:"text",text:JSON.stringify({profileId:e,profileName:o.name??"",description:o.description??"",entities:c,totalFields:r.length,totalEntities:c.length})}]}}default:throw new Error(`Unknown RBAC inspector tool: ${a}`)}}var Ya=nt([{tools:ot,handler:st},{tools:mt,handler:gt},{tools:ht,handler:vt},{tools:bt,handler:_t},{tools:wt,handler:Nt},{tools:St,handler:Tt},{tools:Rt,handler:At},{tools:kt,handler:Et},{tools:xt,handler:Dt},{tools:Ct,handler:Ot},{tools:Lt,handler:Ut},{tools:Pt,handler:Mt},{tools:Ht,handler:$t},{tools:qt,handler:jt},{tools:Ft,handler:Gt},{tools:Bt,handler:Vt},{tools:zt,handler:Kt},{tools:Qt,handler:Zt},{tools:ea,handler:ta},{tools:aa,handler:na},{tools:oa,handler:sa},{tools:da,handler:ua},{tools:la,handler:ca},{tools:_a,handler:wa},{tools:Be,handler:Ea},{tools:xa,handler:Da},{tools:Ve,handler:Ia},{tools:ze,handler:Oa},{tools:Pa,handler:Ma},{tools:$a,handler:qa},{tools:ja,handler:Wa},{tools:Ga,handler:Ba},{tools:Va,handler:za},{tools:pa,handler:ma}]),Yi=new Set(qe.map(a=>a.name)),Qa=[...Ya.getAllDefinitions(),...qe],E=Xi(Ji(import.meta.url)),Za=JSON.parse(zi(Ki(E,"../package.json"),"utf-8")).version,Qi={uri:Q,name:"Dataverse Table Browser",description:"Interactive MCP App UI for browsing Dataverse tables and rows",mimeType:ee},Zi={uri:K,name:"Dataverse FetchXML Workbench",description:"Interactive MCP App UI for composing and executing FetchXML queries",mimeType:ee},eo={uri:ce,name:"Dataverse Plugin Trace Viewer",description:"Interactive MCP App UI for browsing and filtering Dataverse plugin trace logs",mimeType:ee},to={uri:ne,name:"Dataverse Schema Explorer",description:"Interactive MCP App UI for exploring Dataverse table schemas, columns, relationships, and alternate keys",mimeType:ee},ao={uri:B,name:"Dataverse RBAC Inspector",description:"Interactive MCP App UI for inspecting security roles, privilege matrices, and user role assignments",mimeType:ee};function no(){let a=x(E,"apps","table-browser","index.html");if(U(a))return a;let i=x(E,"apps","table-browser.html");if(U(i))return i;let n=x(E,"..","dist","apps","table-browser","index.html");return U(n)?n:x(E,"..","dist","apps","table-browser.html")}function ro(){let a=x(E,"apps","fetchxml-workbench","index.html");if(U(a))return a;let i=x(E,"apps","fetchxml-workbench.html");if(U(i))return i;let n=x(E,"..","dist","apps","fetchxml-workbench","index.html");return U(n)?n:x(E,"..","dist","apps","fetchxml-workbench.html")}function io(){let a=x(E,"apps","plugin-trace-viewer","index.html");if(U(a))return a;let i=x(E,"apps","plugin-trace-viewer.html");if(U(i))return i;let n=x(E,"..","dist","apps","plugin-trace-viewer","index.html");return U(n)?n:x(E,"..","dist","apps","plugin-trace-viewer.html")}function oo(){let a=x(E,"apps","schema-explorer","index.html");if(U(a))return a;let i=x(E,"apps","schema-explorer.html");if(U(i))return i;let n=x(E,"..","dist","apps","schema-explorer","index.html");return U(n)?n:x(E,"..","dist","apps","schema-explorer.html")}function so(){let a=x(E,"apps","rbac-inspector","index.html");if(U(a))return a;let i=x(E,"apps","rbac-inspector.html");if(U(i))return i;let n=x(E,"..","dist","apps","rbac-inspector","index.html");return U(n)?n:x(E,"..","dist","apps","rbac-inspector.html")}var Ka=`You are connected to a Microsoft Dataverse environment via the mcp-dataverse server.
312
+ </fetch>`)]),s=(n.value??[])[0]??{},o=new Map;for(let c of i){let d=c.entityname??"",u=c.attributelogicalname??"",m=Number(c.canread)!==0,p=Number(c.canupdate)!==0,y=Number(c.cancreate)!==0;o.has(d)||o.set(d,[]),o.get(d).push({field:u,canRead:m,canUpdate:p,canCreate:y})}let l=Array.from(o.entries()).map(([c,d])=>({entity:c,fields:d})).sort((c,d)=>c.entity.localeCompare(d.entity));return{content:[{type:"text",text:JSON.stringify({profileId:e,profileName:s.name??"",description:s.description??"",entities:l,totalFields:i.length,totalEntities:l.length})}]}}default:throw new Error(`Unknown RBAC inspector tool: ${r}`)}}var Sr=an([{tools:on,handler:cn},{tools:gn,handler:fn},{tools:vn,handler:bn},{tools:_n,handler:wn},{tools:Tn,handler:Rn},{tools:Sn,handler:Nn},{tools:An,handler:kn},{tools:En,handler:Cn},{tools:In,handler:xn},{tools:On,handler:Pn},{tools:Un,handler:$n},{tools:Ln,handler:Mn},{tools:qn,handler:Hn},{tools:jn,handler:Wn},{tools:Bn,handler:Gn},{tools:Vn,handler:zn},{tools:Kn,handler:Xn},{tools:Zn,handler:ea},{tools:ta,handler:na},{tools:aa,handler:ra},{tools:oa,handler:ca},{tools:ua,handler:pa},{tools:la,handler:da},{tools:wa,handler:Ta},{tools:Et,handler:Ca},{tools:Ia,handler:xa},{tools:Ct,handler:Da},{tools:It,handler:Pa},{tools:La,handler:Ma},{tools:Ha,handler:ja},{tools:Wa,handler:Fa},{tools:Ga,handler:Va},{tools:za,handler:Ka},{tools:ma,handler:ga}]),mc=new Set(Rt.map(r=>r.name)),Nr=[...Sr.getAllDefinitions(),...Rt],I=uc(pc(import.meta.url)),Ar=JSON.parse(lc(dc(I,"../package.json"),"utf-8")).version,gc={uri:re,name:"Dataverse Table Browser",description:"Interactive MCP App UI for browsing Dataverse tables and rows",mimeType:oe},fc={uri:Q,name:"Dataverse FetchXML Workbench",description:"Interactive MCP App UI for composing and executing FetchXML queries",mimeType:oe},yc={uri:be,name:"Dataverse Plugin Trace Viewer",description:"Interactive MCP App UI for browsing and filtering Dataverse plugin trace logs",mimeType:oe},hc={uri:de,name:"Dataverse Schema Explorer",description:"Interactive MCP App UI for exploring Dataverse table schemas, columns, relationships, and alternate keys",mimeType:oe},vc={uri:K,name:"Dataverse RBAC Inspector",description:"Interactive MCP App UI for inspecting security roles, privilege matrices, and user role assignments",mimeType:oe};function bc(){let r=D(I,"apps","table-browser","index.html");if(H(r))return r;let a=D(I,"apps","table-browser.html");if(H(a))return a;let t=D(I,"..","dist","apps","table-browser","index.html");return H(t)?t:D(I,"..","dist","apps","table-browser.html")}function _c(){let r=D(I,"apps","fetchxml-workbench","index.html");if(H(r))return r;let a=D(I,"apps","fetchxml-workbench.html");if(H(a))return a;let t=D(I,"..","dist","apps","fetchxml-workbench","index.html");return H(t)?t:D(I,"..","dist","apps","fetchxml-workbench.html")}function wc(){let r=D(I,"apps","plugin-trace-viewer","index.html");if(H(r))return r;let a=D(I,"apps","plugin-trace-viewer.html");if(H(a))return a;let t=D(I,"..","dist","apps","plugin-trace-viewer","index.html");return H(t)?t:D(I,"..","dist","apps","plugin-trace-viewer.html")}function Tc(){let r=D(I,"apps","schema-explorer","index.html");if(H(r))return r;let a=D(I,"apps","schema-explorer.html");if(H(a))return a;let t=D(I,"..","dist","apps","schema-explorer","index.html");return H(t)?t:D(I,"..","dist","apps","schema-explorer.html")}function Rc(){let r=D(I,"apps","rbac-inspector","index.html");if(H(r))return r;let a=D(I,"apps","rbac-inspector.html");if(H(a))return a;let t=D(I,"..","dist","apps","rbac-inspector","index.html");return H(t)?t:D(I,"..","dist","apps","rbac-inspector.html")}var wr=`You are connected to a Microsoft Dataverse environment via the mcp-dataverse server.
181
313
 
182
314
  ## Recommended Workflow
183
315
  1. Start with dataverse_whoami to verify authentication.
@@ -204,11 +336,11 @@ ${a.map(r=>{let o=r.id??`Area_${++i}`,s=r.groups.map(c=>{let l=c.id??`Group_${++
204
336
  - Avoid retrieving more than 5000 records unless explicitly needed (use dataverse_retrieve_multiple_with_paging with maxTotal).
205
337
  - Use server-side filtering ($filter / FetchXML conditions) instead of client-side filtering.
206
338
  - Cache table metadata \u2014 it rarely changes during a session.
207
- `;function Xa(a,i,n,e){let t=Ya.getHandler(a);if(!t)throw new Error(`Unknown tool: ${a}`);return t(a,i,n,e)}function Ja(a){let i=new Hi({name:"mcp-dataverse",version:Za},{capabilities:{tools:{},resources:{},prompts:{}},instructions:Ka});return i.setRequestHandler(ji,async()=>({tools:Qa})),i.setRequestHandler(qi,async n=>{let{name:e,arguments:t}=n.params,r=n.params._meta?.progressToken,o=new ve(r!=null?i:void 0,r);try{return Yi.has(e)?It(e,t,a,Xa):await Xa(e,t,a,o)}catch(s){return{content:[{type:"text",text:`Error: ${s instanceof Error?s.message:String(s)}`}],isError:!0}}}),i.setRequestHandler(Wi,async()=>({resources:[...ga(),Qi,Zi,eo,to,ao]})),i.setRequestHandler(Fi,async()=>({resourceTemplates:fa()})),i.setRequestHandler(Gi,async n=>{if(n.params.uri===Q){let t=no(),r=await Re(t,"utf-8");return{contents:[{uri:Q,mimeType:ee,text:r}]}}if(n.params.uri===K){let t=ro(),r=await Re(t,"utf-8");return{contents:[{uri:K,mimeType:ee,text:r}]}}if(n.params.uri===ce){let t=io(),r=await Re(t,"utf-8");return{contents:[{uri:ce,mimeType:ee,text:r}]}}if(n.params.uri===ne){let t=oo(),r=await Re(t,"utf-8");return{contents:[{uri:ne,mimeType:ee,text:r}]}}if(n.params.uri===B){let t=so(),r=await Re(t,"utf-8");return{contents:[{uri:B,mimeType:ee,text:r}]}}return{contents:[await ya(n.params.uri,a,Ka)]}}),i.setRequestHandler(Bi,async()=>({prompts:ha()})),i.setRequestHandler(Vi,async n=>{let{name:e,arguments:t}=n.params;return va(e,t??{})}),i}async function lo(){let a=Ze(),i=et(a),n=new tt(i,a.maxRetries,a.requestTimeoutMs),e=at();if(e.transport==="http"){let{startHttpTransport:t}=await import("./http-server.js");process.stderr.write(`Starting HTTP transport on port ${e.port}...
208
- `),i.getToken().then(()=>{process.stderr.write(`[mcp-dataverse] Authenticated \u2713
209
- `)}).catch(r=>{let o=r instanceof Error?r.message:String(r);process.stderr.write(`[mcp-dataverse] Authentication failed: ${o}
210
- `)}),await t(()=>Ja(n),e.port,Za,Qa.length)}else{let t=Ja(n),r=new $i;await t.connect(r),process.stderr.write(`MCP Dataverse server started
211
- `),i.getToken().then(()=>{process.stderr.write(`[mcp-dataverse] Authenticated \u2713
212
- `)}).catch(o=>{let s=o instanceof Error?o.message:String(o);process.stderr.write(`[mcp-dataverse] Authentication failed: ${s}
213
- `)})}}async function co(){if(process.argv.includes("install")){let{runInstall:a}=await import("./install.js");await a(),process.exit(0)}if(process.argv.includes("doctor")){let{runDoctor:a}=await import("./doctor.js");await a()}await lo()}co().catch(a=>{process.stderr.write(`Fatal error: ${a instanceof Error?a.message:String(a)}
339
+ `;function Tr(r,a,t,e){let n=Sr.getHandler(r);if(!n)throw new Error(`Unknown tool: ${r}`);return n(r,a,t,e)}function Rr(r){let a=new ec({name:"mcp-dataverse",version:Ar},{capabilities:{tools:{},resources:{},prompts:{}},instructions:wr});return a.setRequestHandler(ac,async()=>({tools:Nr})),a.setRequestHandler(nc,async t=>{let{name:e,arguments:n}=t.params,i=t.params._meta?.progressToken,s=new xe(i!=null?a:void 0,i);try{return mc.has(e)?Dn(e,n,r,Tr):await Tr(e,n,r,s)}catch(o){return{content:[{type:"text",text:`Error: ${o instanceof Error?o.message:String(o)}`}],isError:!0}}}),a.setRequestHandler(rc,async()=>({resources:[...fa(),gc,fc,yc,hc,vc]})),a.setRequestHandler(ic,async()=>({resourceTemplates:ya()})),a.setRequestHandler(sc,async t=>{if(t.params.uri===re){let n=bc(),i=await qe(n,"utf-8");return{contents:[{uri:re,mimeType:oe,text:i}]}}if(t.params.uri===Q){let n=_c(),i=await qe(n,"utf-8");return{contents:[{uri:Q,mimeType:oe,text:i}]}}if(t.params.uri===be){let n=wc(),i=await qe(n,"utf-8");return{contents:[{uri:be,mimeType:oe,text:i}]}}if(t.params.uri===de){let n=Tc(),i=await qe(n,"utf-8");return{contents:[{uri:de,mimeType:oe,text:i}]}}if(t.params.uri===K){let n=Rc(),i=await qe(n,"utf-8");return{contents:[{uri:K,mimeType:oe,text:i}]}}return{contents:[await ha(t.params.uri,r,wr)]}}),a.setRequestHandler(oc,async()=>({prompts:va()})),a.setRequestHandler(cc,async t=>{let{name:e,arguments:n}=t.params;return ba(e,n??{})}),a}async function Sc(){let r=st(),a=lt(r),t=new Ie(a,r.maxRetries,r.requestTimeoutMs),e=Ut();if(e.transport==="http"){let{startHttpTransport:n}=await Promise.resolve().then(()=>(Za(),Qa));process.stderr.write(`Starting HTTP transport on port ${e.port}...
340
+ `),a.getToken().then(()=>{process.stderr.write(`[mcp-dataverse] Authenticated \u2713
341
+ `)}).catch(i=>{let s=i instanceof Error?i.message:String(i);process.stderr.write(`[mcp-dataverse] Authentication failed: ${s}
342
+ `)}),await n(()=>Rr(t),e.port,Ar,Nr.length)}else{let n=Rr(t),i=new tc;await n.connect(i),process.stderr.write(`MCP Dataverse server started
343
+ `),a.getToken().then(()=>{process.stderr.write(`[mcp-dataverse] Authenticated \u2713
344
+ `)}).catch(s=>{let o=s instanceof Error?s.message:String(s);process.stderr.write(`[mcp-dataverse] Authentication failed: ${o}
345
+ `)})}}async function Nc(){if(process.argv.includes("install")){let{runInstall:r}=await Promise.resolve().then(()=>(ur(),dr));await r(),process.exit(0)}if(process.argv.includes("update")){let{runUpdate:r}=await Promise.resolve().then(()=>(vr(),hr));await r(),process.exit(0)}if(process.argv.includes("doctor")){let{runDoctor:r}=await Promise.resolve().then(()=>(_r(),br));await r()}await Sc()}Nc().catch(r=>{process.stderr.write(`Fatal error: ${r instanceof Error?r.message:String(r)}
214
346
  `),process.exit(1)});